VirtualBox

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

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 15.5 KB
Line 
1/* $Id: semevent-nt.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT - Single Release Event Semaphores, Ring-0 Driver & Ring-3 Userland, NT.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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
46#include "internal/magics.h"
47
48
49/*********************************************************************************************************************************
50* Structures and Typedefs *
51*********************************************************************************************************************************/
52/**
53 * NT event semaphore.
54 */
55typedef struct RTSEMEVENTINTERNAL
56{
57 /** Magic value (RTSEMEVENT_MAGIC). */
58 uint32_t volatile u32Magic;
59 /** Reference counter. */
60 uint32_t volatile cRefs;
61#ifdef IN_RING0
62 /** The NT event object. */
63 KEVENT Event;
64#elif defined(IN_RING3)
65 /** Handle to the NT event object. */
66 HANDLE hEvent;
67#else
68# error "Unknown context"
69#endif
70#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
71 /** Signallers. */
72 RTLOCKVALRECSHRD Signallers;
73 /** Indicates that lock validation should be performed. */
74 bool volatile fEverHadSignallers;
75#endif
76
77} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
78
79
80RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
81{
82 return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
83}
84
85
86RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
87{
88 AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
89 Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
90 AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
91
92 PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis));
93 if (pThis)
94 {
95 pThis->u32Magic = RTSEMEVENT_MAGIC;
96 pThis->cRefs = 1;
97#ifdef IN_RING0
98 KeInitializeEvent(&pThis->Event, SynchronizationEvent, FALSE /* not signalled */);
99#else
100 NTSTATUS rcNt = NtCreateEvent(&pThis->hEvent, EVENT_ALL_ACCESS, NULL /*pObjAttr*/,
101 SynchronizationEvent, FALSE /*not signalled*/);
102 if (NT_SUCCESS(rcNt))
103#endif
104 {
105#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
106 if (!pszNameFmt)
107 {
108 static uint32_t volatile s_iSemEventAnon = 0;
109 RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
110 true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
111 "RTSemEvent-%u", ASMAtomicIncU32(&s_iSemEventAnon) - 1);
112 }
113 else
114 {
115 va_list va;
116 va_start(va, pszNameFmt);
117 RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
118 true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
119 pszNameFmt, va);
120 va_end(va);
121 }
122 pThis->fEverHadSignallers = false;
123#else
124 RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
125#endif
126 *phEventSem = pThis;
127 return VINF_SUCCESS;
128 }
129#ifdef IN_RING3
130 RTMemFree(pThis);
131 return RTErrConvertFromNtStatus(rcNt);
132#endif
133 }
134 return VERR_NO_MEMORY;
135}
136
137
138/**
139 * Retains a reference to the semaphore.
140 *
141 * @param pThis The semaphore to retain.
142 */
143DECLINLINE(void) rtR0SemEventNtRetain(PRTSEMEVENTINTERNAL pThis)
144{
145 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
146 Assert(cRefs < 100000); NOREF(cRefs);
147}
148
149
150/**
151 * Releases a reference to the semaphore.
152 *
153 * @param pThis The semaphore to release
154 */
155DECLINLINE(void) rtR0SemEventNtRelease(PRTSEMEVENTINTERNAL pThis)
156{
157 if (ASMAtomicDecU32(&pThis->cRefs) == 0)
158 {
159#ifdef IN_RING3
160 NTSTATUS rcNt = NtClose(pThis->hEvent);
161 AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt)); RT_NOREF(rcNt);
162 pThis->hEvent = NULL;
163#endif
164#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
165 RTLockValidatorRecSharedDelete(&pThis->Signallers);
166#endif
167 RTMemFree(pThis);
168 }
169}
170
171
172RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
173{
174 /*
175 * Validate input.
176 */
177 PRTSEMEVENTINTERNAL pThis = hEventSem;
178 if (pThis == NIL_RTSEMEVENT)
179 return VINF_SUCCESS;
180 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
181 AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
182
183 /*
184 * Invalidate it and signal the object just in case.
185 */
186 ASMAtomicIncU32(&pThis->u32Magic);
187#ifdef IN_RING0
188 KeSetEvent(&pThis->Event, 0xfff, FALSE);
189#else
190 NtSetEvent(pThis->hEvent, NULL);
191#endif
192
193 rtR0SemEventNtRelease(pThis);
194 return VINF_SUCCESS;
195}
196
197
198RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
199{
200 /*
201 * Validate input.
202 */
203 PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
204 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
205 AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
206 rtR0SemEventNtRetain(pThis);
207
208#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
209 if (pThis->fEverHadSignallers)
210 {
211 int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
212 if (RT_FAILURE(rc9))
213 return rc9;
214 }
215#endif
216
217 /*
218 * Signal the event object.
219 */
220#ifdef IN_RING0
221 KeSetEvent(&pThis->Event, 1, FALSE);
222#else
223 NTSTATUS rcNt = NtSetEvent(pThis->hEvent, NULL);
224#endif
225
226 rtR0SemEventNtRelease(pThis);
227#ifdef IN_RING3
228 AssertMsgReturn(NT_SUCCESS(rcNt), ("Signaling hEventSem %p failed: %#x\n", pThis, rcNt), RTErrConvertFromNtStatus(rcNt));
229#endif
230 return VINF_SUCCESS;
231}
232
233
234
235/**
236 * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
237 *
238 * @returns VBox status code.
239 * @param pThis The event semaphore.
240 * @param fFlags See RTSemEventWaitEx.
241 * @param uTimeout See RTSemEventWaitEx.
242 * @param pSrcPos The source code position of the wait.
243 */
244DECLINLINE(int) rtR0SemEventNtWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
245 PCRTLOCKVALSRCPOS pSrcPos)
246{
247 /*
248 * Validate input.
249 */
250 if (!pThis)
251 return VERR_INVALID_PARAMETER;
252 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
253 AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
254 AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_FLAGS);
255 NOREF(pSrcPos);
256
257 rtR0SemEventNtRetain(pThis);
258
259 /*
260 * Lock validation needs to be done only when not polling.
261 */
262#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
263 RTTHREAD const hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) ? RTThreadSelfAutoAdopt() : RTThreadSelf();
264 if ( pThis->fEverHadSignallers
265 && ( uTimeout != 0
266 || (fFlags & (RTSEMWAIT_FLAGS_INDEFINITE | RTSEMWAIT_FLAGS_ABSOLUTE))) )
267 {
268 int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, NULL /*pSrcPos*/, false,
269 fFlags & RTSEMWAIT_FLAGS_INDEFINITE
270 ? RT_INDEFINITE_WAIT : RT_MS_30SEC /*whatever*/,
271 RTTHREADSTATE_EVENT, true);
272 if (RT_FAILURE(rc9))
273 return rc9;
274 }
275#elif defined(IN_RING3)
276 RTTHREAD const hThreadSelf = RTThreadSelf();
277#endif
278
279 /*
280 * Convert the timeout to a relative one because KeWaitForSingleObject
281 * takes system time instead of interrupt time as input for absolute
282 * timeout specifications. So, we're best off by giving it relative time.
283 *
284 * Lazy bird converts uTimeout to relative nanoseconds and then to Nt time.
285 */
286#ifdef IN_RING3
287 uint64_t nsStartNow = 0;
288#endif
289 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
290 {
291 if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
292 uTimeout = uTimeout < UINT64_MAX / RT_NS_1MS
293 ? uTimeout * RT_NS_1MS
294 : UINT64_MAX;
295 if (uTimeout == UINT64_MAX)
296 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
297 else
298 {
299#ifdef IN_RING3
300 if (fFlags & (RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_ABSOLUTE))
301 nsStartNow = RTTimeSystemNanoTS();
302#endif
303 if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE)
304 {
305#ifdef IN_RING0
306 uint64_t const nsStartNow = RTTimeSystemNanoTS();
307#endif
308 uTimeout = nsStartNow < uTimeout
309 ? uTimeout - nsStartNow
310 : 0;
311 }
312 }
313 }
314
315 /*
316 * Wait for it.
317 * We're assuming interruptible waits should happen at UserMode level.
318 */
319 int rc;
320#ifdef IN_RING3
321 for (;;)
322#endif
323 {
324#ifdef IN_RING0
325 BOOLEAN fInterruptible = !!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE);
326 KPROCESSOR_MODE WaitMode = fInterruptible ? UserMode : KernelMode;
327#endif
328 NTSTATUS rcNt;
329#ifdef IN_RING3
330 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
331#endif
332 if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
333#ifdef IN_RING0
334 rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, NULL);
335#else
336 rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, NULL);
337#endif
338 else
339 {
340 LARGE_INTEGER Timeout;
341 Timeout.QuadPart = -(int64_t)(uTimeout / 100);
342#ifdef IN_RING0
343 rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, &Timeout);
344#else
345 rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, &Timeout);
346#endif
347 }
348#ifdef IN_RING3
349 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
350#endif
351 if (pThis->u32Magic == RTSEMEVENT_MAGIC)
352 {
353 switch (rcNt)
354 {
355 case STATUS_SUCCESS:
356 rc = VINF_SUCCESS;
357 break;
358
359 case STATUS_TIMEOUT:
360 Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE));
361 rc = VERR_TIMEOUT;
362 break;
363
364 case STATUS_USER_APC:
365 case STATUS_ALERTED:
366 rc = VERR_INTERRUPTED;
367#ifdef IN_RING3
368 /* Loop if when automatically resuming on interruption, adjusting the timeout. */
369 if (fFlags & RTSEMWAIT_FLAGS_RESUME)
370 {
371 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE) && uTimeout > 0)
372 {
373 uint64_t const nsNewNow = RTTimeSystemNanoTS();
374 uint64_t const cNsElapsed = nsNewNow - nsStartNow;
375 if (cNsElapsed < uTimeout)
376 uTimeout -= cNsElapsed;
377 else
378 uTimeout = 0;
379 nsStartNow = nsNewNow;
380 }
381 continue;
382 }
383#endif
384 break;
385
386#ifdef IN_RING3
387 case STATUS_ABANDONED_WAIT_0:
388 rc = VERR_SEM_OWNER_DIED;
389 break;
390#endif
391 default:
392 AssertMsgFailed(("pThis->u32Magic=%RX32 pThis=%p: wait returned %x!\n", pThis->u32Magic, pThis, rcNt));
393 rc = VERR_INTERNAL_ERROR_4;
394 break;
395 }
396 }
397 else
398 rc = VERR_SEM_DESTROYED;
399#ifdef IN_RING3
400 break;
401#endif
402 }
403
404 rtR0SemEventNtRelease(pThis);
405 return rc;
406}
407
408
409RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
410{
411#ifndef RTSEMEVENT_STRICT
412 return rtR0SemEventNtWait(hEventSem, fFlags, uTimeout, NULL);
413#else
414 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
415 return rtR0SemEventNtWait(hEventSem, fFlags, uTimeout, &SrcPos);
416#endif
417}
418
419
420RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
421 RTHCUINTPTR uId, RT_SRC_POS_DECL)
422{
423 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
424 return rtR0SemEventNtWait(hEventSem, fFlags, uTimeout, &SrcPos);
425}
426
427
428#ifdef IN_RING0
429RTR0DECL(bool) RTSemEventIsSignalSafe(void)
430{
431 return KeGetCurrentIrql() <= DISPATCH_LEVEL;
432}
433#endif
434
435#ifdef IN_RING3
436
437RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
438{
439# ifdef RTSEMEVENT_STRICT
440 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
441 AssertPtrReturnVoid(pThis);
442 AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
443
444 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
445 RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
446# else
447 RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
448# endif
449}
450
451
452RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
453{
454# ifdef RTSEMEVENT_STRICT
455 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
456 AssertPtrReturnVoid(pThis);
457 AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
458
459 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
460 RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
461# else
462 RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
463# endif
464}
465
466
467RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
468{
469# ifdef RTSEMEVENT_STRICT
470 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
471 AssertPtrReturnVoid(pThis);
472 AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
473
474 RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
475# else
476 RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
477# endif
478}
479
480#endif /* IN_RING3 */
Note: See TracBrowser for help on using the repository browser.

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