VirtualBox

source: vbox/trunk/src/VBox/Runtime/nt/semevent-nt.cpp@ 92792

Last change on this file since 92792 was 92792, checked in by vboxsync, 3 years ago

IPRT/semevent-nt: Converted semevent-r0drv-nt.cpp into something that works both in ring-3 and ring-0. Enabled the tstRTSemEvent testcase. bugref:10138

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.2 KB
Line 
1/* $Id: semevent-nt.cpp 92792 2021-12-07 21:49:10Z vboxsync $ */
2/** @file
3 * IPRT - Single Release Event Semaphores, Ring-0 Driver & Ring-3 Userland, NT.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define RTSEMEVENT_WITHOUT_REMAPPING
32#ifdef IN_RING0
33# include "../r0drv/nt/the-nt-kernel.h"
34#else
35# include <iprt/nt/nt.h>
36#endif
37#include <iprt/semaphore.h>
38
39#include <iprt/asm.h>
40#include <iprt/assert.h>
41#include <iprt/err.h>
42#include <iprt/lockvalidator.h>
43#include <iprt/mem.h>
44#include <iprt/time.h>
45#include <iprt/timer.h>
46#ifdef IN_RING3
47# include <iprt/system.h>
48#endif
49#include "internal/magics.h"
50
51
52/*********************************************************************************************************************************
53* Structures and Typedefs *
54*********************************************************************************************************************************/
55/**
56 * NT event semaphore.
57 */
58typedef struct RTSEMEVENTINTERNAL
59{
60 /** Magic value (RTSEMEVENT_MAGIC). */
61 uint32_t volatile u32Magic;
62 /** Reference counter. */
63 uint32_t volatile cRefs;
64#ifdef IN_RING0
65 /** The NT event object. */
66 KEVENT Event;
67#elif defined(IN_RING3)
68 /** Handle to the NT event object. */
69 HANDLE hEvent;
70#else
71# error "Unknown context"
72#endif
73#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
74 /** Signallers. */
75 RTLOCKVALRECSHRD Signallers;
76 /** Indicates that lock validation should be performed. */
77 bool volatile fEverHadSignallers;
78#endif
79
80} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
81
82
83RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
84{
85 return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
86}
87
88
89RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
90{
91 AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
92 Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
93 AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
94
95 PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis));
96 if (pThis)
97 {
98 pThis->u32Magic = RTSEMEVENT_MAGIC;
99 pThis->cRefs = 1;
100#ifdef IN_RING0
101 KeInitializeEvent(&pThis->Event, SynchronizationEvent, FALSE /* not signalled */);
102#else
103 NTSTATUS rcNt = NtCreateEvent(&pThis->hEvent, EVENT_ALL_ACCESS, NULL /*pObjAttr*/,
104 SynchronizationEvent, FALSE /*not signalled*/);
105 if (NT_SUCCESS(rcNt))
106#endif
107 {
108#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
109 if (!pszNameFmt)
110 {
111 static uint32_t volatile s_iSemEventAnon = 0;
112 RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
113 true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
114 "RTSemEvent-%u", ASMAtomicIncU32(&s_iSemEventAnon) - 1);
115 }
116 else
117 {
118 va_list va;
119 va_start(va, pszNameFmt);
120 RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
121 true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
122 pszNameFmt, va);
123 va_end(va);
124 }
125 pThis->fEverHadSignallers = false;
126#else
127 RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
128#endif
129 *phEventSem = pThis;
130 return VINF_SUCCESS;
131 }
132#ifdef IN_RING3
133 RTMemFree(pThis);
134 return RTErrConvertFromNtStatus(rcNt);
135#endif
136 }
137 return VERR_NO_MEMORY;
138}
139
140
141/**
142 * Retains a reference to the semaphore.
143 *
144 * @param pThis The semaphore to retain.
145 */
146DECLINLINE(void) rtR0SemEventNtRetain(PRTSEMEVENTINTERNAL pThis)
147{
148 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
149 Assert(cRefs < 100000); NOREF(cRefs);
150}
151
152
153/**
154 * Releases a reference to the semaphore.
155 *
156 * @param pThis The semaphore to release
157 */
158DECLINLINE(void) rtR0SemEventNtRelease(PRTSEMEVENTINTERNAL pThis)
159{
160 if (ASMAtomicDecU32(&pThis->cRefs) == 0)
161 {
162#ifdef IN_RING3
163 NTSTATUS rcNt = NtClose(pThis->hEvent);
164 AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt)); RT_NOREF(rcNt);
165 pThis->hEvent = NULL;
166#endif
167#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
168 RTLockValidatorRecSharedDelete(&pThis->Signallers);
169#endif
170 RTMemFree(pThis);
171 }
172}
173
174
175RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
176{
177 /*
178 * Validate input.
179 */
180 PRTSEMEVENTINTERNAL pThis = hEventSem;
181 if (pThis == NIL_RTSEMEVENT)
182 return VINF_SUCCESS;
183 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
184 AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
185
186 /*
187 * Invalidate it and signal the object just in case.
188 */
189 ASMAtomicIncU32(&pThis->u32Magic);
190#ifdef IN_RING0
191 KeSetEvent(&pThis->Event, 0xfff, FALSE);
192#else
193 NtSetEvent(pThis->hEvent, NULL);
194#endif
195
196 rtR0SemEventNtRelease(pThis);
197 return VINF_SUCCESS;
198}
199
200
201RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
202{
203 /*
204 * Validate input.
205 */
206 PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
207 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
208 AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
209 rtR0SemEventNtRetain(pThis);
210
211#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
212 if (pThis->fEverHadSignallers)
213 {
214 int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
215 if (RT_FAILURE(rc9))
216 return rc9;
217 }
218#endif
219
220 /*
221 * Signal the event object.
222 */
223#ifdef IN_RING0
224 KeSetEvent(&pThis->Event, 1, FALSE);
225#else
226 NTSTATUS rcNt = NtSetEvent(pThis->hEvent, NULL);
227#endif
228
229 rtR0SemEventNtRelease(pThis);
230#ifdef IN_RING3
231 AssertMsgReturn(NT_SUCCESS(rcNt), ("Signaling hEventSem %p failed: %#x\n", pThis, rcNt), RTErrConvertFromNtStatus(rcNt));
232#endif
233 return VINF_SUCCESS;
234}
235
236
237
238/**
239 * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
240 *
241 * @returns VBox status code.
242 * @param pThis The event semaphore.
243 * @param fFlags See RTSemEventWaitEx.
244 * @param uTimeout See RTSemEventWaitEx.
245 * @param pSrcPos The source code position of the wait.
246 */
247DECLINLINE(int) rtR0SemEventNtWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
248 PCRTLOCKVALSRCPOS pSrcPos)
249{
250 /*
251 * Validate input.
252 */
253 if (!pThis)
254 return VERR_INVALID_PARAMETER;
255 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
256 AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
257 AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_FLAGS);
258 NOREF(pSrcPos);
259
260 rtR0SemEventNtRetain(pThis);
261
262 /*
263 * Lock validation needs to be done only when not polling.
264 */
265#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
266 RTTHREAD const hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) ? RTThreadSelfAutoAdopt() : RTThreadSelf();
267 if ( pThis->fEverHadSignallers
268 && ( uTimeout != 0
269 || (fFlags & (RTSEMWAIT_FLAGS_INDEFINITE | RTSEMWAIT_FLAGS_ABSOLUTE))) )
270 {
271 int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, NULL /*pSrcPos*/, false,
272 fFlags & RTSEMWAIT_FLAGS_INDEFINITE
273 ? RT_INDEFINITE_WAIT : RT_MS_30SEC /*whatever*/,
274 RTTHREADSTATE_EVENT, true);
275 if (RT_FAILURE(rc9))
276 return rc9;
277 }
278#elif defined(IN_RING3)
279 RTTHREAD const hThreadSelf = RTThreadSelf();
280#endif
281
282 /*
283 * Convert the timeout to a relative one because KeWaitForSingleObject
284 * takes system time instead of interrupt time as input for absolute
285 * timeout specifications. So, we're best off by giving it relative time.
286 *
287 * Lazy bird converts uTimeout to relative nanoseconds and then to Nt time.
288 */
289#ifdef IN_RING3
290 uint64_t nsStartNow = 0;
291#endif
292 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
293 {
294 if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
295 uTimeout = uTimeout < UINT64_MAX / RT_NS_1MS
296 ? uTimeout * RT_NS_1MS
297 : UINT64_MAX;
298 if (uTimeout == UINT64_MAX)
299 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
300 else
301 {
302#ifdef IN_RING3
303 if (fFlags & (RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_ABSOLUTE))
304 nsStartNow = RTTimeSystemNanoTS();
305#endif
306 if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE)
307 {
308#ifdef IN_RING0
309 uint64_t const nsStartNow = RTTimeSystemNanoTS();
310#endif
311 uTimeout = nsStartNow < uTimeout
312 ? uTimeout - nsStartNow
313 : 0;
314 }
315 }
316 }
317
318 /*
319 * Wait for it.
320 * We're assuming interruptible waits should happen at UserMode level.
321 */
322 int rc;
323#ifdef IN_RING3
324 for (;;)
325#endif
326 {
327#ifdef IN_RING0
328 BOOLEAN fInterruptible = !!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE);
329 KPROCESSOR_MODE WaitMode = fInterruptible ? UserMode : KernelMode;
330#endif
331 NTSTATUS rcNt;
332#ifdef IN_RING3
333 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
334#endif
335 if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
336#ifdef IN_RING0
337 rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, NULL);
338#else
339 rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, NULL);
340#endif
341 else
342 {
343 LARGE_INTEGER Timeout;
344 Timeout.QuadPart = -(int64_t)(uTimeout / 100);
345#ifdef IN_RING0
346 rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, &Timeout);
347#else
348 rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, &Timeout);
349#endif
350 }
351#ifdef IN_RING3
352 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
353#endif
354 if (pThis->u32Magic == RTSEMEVENT_MAGIC)
355 {
356 switch (rcNt)
357 {
358 case STATUS_SUCCESS:
359 rc = VINF_SUCCESS;
360 break;
361
362 case STATUS_TIMEOUT:
363 Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE));
364 rc = VERR_TIMEOUT;
365 break;
366
367 case STATUS_USER_APC:
368 case STATUS_ALERTED:
369 rc = VERR_INTERRUPTED;
370#ifdef IN_RING3
371 /* Loop if when automatically resuming on interruption, adjusting the timeout. */
372 if (fFlags & RTSEMWAIT_FLAGS_RESUME)
373 {
374 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE) && uTimeout > 0)
375 {
376 uint64_t const nsNewNow = RTTimeSystemNanoTS();
377 uint64_t const cNsElapsed = nsNewNow - nsStartNow;
378 if (cNsElapsed < uTimeout)
379 uTimeout -= cNsElapsed;
380 else
381 uTimeout = 0;
382 nsStartNow = nsNewNow;
383 }
384 continue;
385 }
386#endif
387 break;
388
389#ifdef IN_RING3
390 case STATUS_ABANDONED_WAIT_0:
391 rc = VERR_SEM_OWNER_DIED;
392 break;
393#endif
394 default:
395 AssertMsgFailed(("pThis->u32Magic=%RX32 pThis=%p: wait returned %x!\n", pThis->u32Magic, pThis, rcNt));
396 rc = VERR_INTERNAL_ERROR_4;
397 break;
398 }
399 }
400 else
401 rc = VERR_SEM_DESTROYED;
402#ifdef IN_RING3
403 break;
404#endif
405 }
406
407 rtR0SemEventNtRelease(pThis);
408 return rc;
409}
410
411
412RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
413{
414#ifndef RTSEMEVENT_STRICT
415 return rtR0SemEventNtWait(hEventSem, fFlags, uTimeout, NULL);
416#else
417 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
418 return rtR0SemEventNtWait(hEventSem, fFlags, uTimeout, &SrcPos);
419#endif
420}
421
422
423RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
424 RTHCUINTPTR uId, RT_SRC_POS_DECL)
425{
426 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
427 return rtR0SemEventNtWait(hEventSem, fFlags, uTimeout, &SrcPos);
428}
429
430
431RTDECL(uint32_t) RTSemEventGetResolution(void)
432{
433 /*
434 * We need to figure the KeWaitForSingleObject / NtWaitForSingleObject timeout
435 * resolution, i.e. if we wish to wait for 1000ns how long are we likely to
436 * actually wait before woken up.
437 *
438 * In older versions of NT, these timeout were implemented using KTIMERs and
439 * have the same resolution as what them. This should be found using
440 * ExSetTimerResolution or NtQueryTimerResolution.
441 *
442 * Probably since windows 8.1 the value returned by NtQueryTimerResolution (and
443 * set NtSetTimerResolution) have been virtualized and no longer reflects the
444 * timer wheel resolution, at least from what I can tell. ExSetTimerResolution
445 * still works as before, but it accesses variable that I cannot find out how
446 * to access from user land. So, kernel will get (and be able to set) the right
447 * granularity, while in user land we'll be forced to reporting the max value.
448 *
449 * (The reason why I suspect it's since 8.1 is because the high resolution
450 * ExSetTimer APIs were introduced back then.)
451 */
452#ifdef IN_RING0
453 return RTTimerGetSystemGranularity();
454#else
455 ULONG cNtTicksMin = 0;
456 ULONG cNtTicksMax = 0;
457 ULONG cNtTicksCur = 0;
458 NTSTATUS rcNt = NtQueryTimerResolution(&cNtTicksMin, &cNtTicksMax, &cNtTicksCur);
459 if (NT_SUCCESS(rcNt))
460 {
461 Assert(cNtTicksMin >= cNtTicksMax);
462 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6,3,9600)) /** @todo check when the switch happened, might be much later... */
463 return cNtTicksMin * 100;
464 return cNtTicksCur * 100;
465 }
466 AssertFailed();
467 return 16 * RT_NS_1MS; /* the default on 64-bit windows 10 */
468#endif
469}
470
471
472#ifdef IN_RING0
473RTR0DECL(bool) RTSemEventIsSignalSafe(void)
474{
475 return KeGetCurrentIrql() <= DISPATCH_LEVEL;
476}
477#endif
478
479#ifdef IN_RING3
480
481RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
482{
483# ifdef RTSEMEVENT_STRICT
484 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
485 AssertPtrReturnVoid(pThis);
486 AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
487
488 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
489 RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
490# else
491 RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
492# endif
493}
494
495
496RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
497{
498# ifdef RTSEMEVENT_STRICT
499 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
500 AssertPtrReturnVoid(pThis);
501 AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
502
503 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
504 RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
505# else
506 RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
507# endif
508}
509
510
511RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
512{
513# ifdef RTSEMEVENT_STRICT
514 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
515 AssertPtrReturnVoid(pThis);
516 AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
517
518 RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
519# else
520 RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
521# endif
522}
523
524#endif /* IN_RING3 */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette