VirtualBox

source: vbox/trunk/src/VBox/Runtime/nt/semeventmulti-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: 16.7 KB
Line 
1/* $Id: semeventmulti-nt.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, 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 RTSEMEVENTMULTI_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
47#include "internal/magics.h"
48
49
50/*********************************************************************************************************************************
51* Structures and Typedefs *
52*********************************************************************************************************************************/
53/**
54 * NT event semaphore.
55 */
56typedef struct RTSEMEVENTMULTIINTERNAL
57{
58 /** Magic value (RTSEMEVENTMULTI_MAGIC). */
59 uint32_t volatile u32Magic;
60 /** Reference counter. */
61 uint32_t volatile cRefs;
62#ifdef IN_RING0
63 /** The NT event object. */
64 KEVENT Event;
65#elif defined(IN_RING3)
66 /** Handle to the NT event object. */
67 HANDLE hEvent;
68#endif
69#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
70 /** Signallers. */
71 RTLOCKVALRECSHRD Signallers;
72 /** Indicates that lock validation should be performed. */
73 bool volatile fEverHadSignallers;
74#endif
75} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
76
77
78RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
79{
80 return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
81}
82
83
84RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
85 const char *pszNameFmt, ...)
86{
87 AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
88 RT_NOREF2(hClass, pszNameFmt);
89
90 AssertCompile(sizeof(RTSEMEVENTMULTIINTERNAL) > sizeof(void *));
91 PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis));
92 if (pThis)
93 {
94 pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
95 pThis->cRefs = 1;
96#ifdef IN_RING0
97 KeInitializeEvent(&pThis->Event, NotificationEvent, FALSE /* not signalled */);
98#else
99 NTSTATUS rcNt = NtCreateEvent(&pThis->hEvent, EVENT_ALL_ACCESS, NULL /*pObjAttr*/,
100 NotificationEvent, FALSE /*not signalled*/);
101 if (NT_SUCCESS(rcNt))
102#endif
103 {
104#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
105 if (!pszNameFmt)
106 {
107 static uint32_t volatile s_iSemEventMultiAnon = 0;
108 RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
109 true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
110 "RTSemEventMulti-%u", ASMAtomicIncU32(&s_iSemEventMultiAnon) - 1);
111 }
112 else
113 {
114 va_list va;
115 va_start(va, pszNameFmt);
116 RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
117 true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
118 pszNameFmt, va);
119 va_end(va);
120 }
121 pThis->fEverHadSignallers = false;
122#else
123 RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
124#endif
125
126 *phEventMultiSem = 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) rtR0SemEventMultiNtRetain(PRTSEMEVENTMULTIINTERNAL 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) rtR0SemEventMultiNtRelease(PRTSEMEVENTMULTIINTERNAL 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) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
173{
174 /*
175 * Validate input.
176 */
177 PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
178 if (pThis == NIL_RTSEMEVENTMULTI)
179 return VINF_SUCCESS;
180 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
181 AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
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 rtR0SemEventMultiNtRelease(pThis);
194 return VINF_SUCCESS;
195}
196
197
198RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
199{
200 /*
201 * Validate input.
202 */
203 PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
204 if (!pThis)
205 return VERR_INVALID_PARAMETER;
206 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
207 AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
208 rtR0SemEventMultiNtRetain(pThis);
209
210#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
211 if (pThis->fEverHadSignallers)
212 {
213 int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
214 if (RT_FAILURE(rc9))
215 return rc9;
216 }
217#endif
218
219 /*
220 * Signal the event object.
221 */
222#ifdef IN_RING0
223 KeSetEvent(&pThis->Event, 1, FALSE);
224#else
225 NTSTATUS rcNt = NtSetEvent(pThis->hEvent, NULL);
226#endif
227
228 rtR0SemEventMultiNtRelease(pThis);
229#ifdef IN_RING3
230 AssertMsgReturn(NT_SUCCESS(rcNt), ("Signaling hEventMultiSem %p failed: %#x\n", pThis, rcNt), RTErrConvertFromNtStatus(rcNt));
231#endif
232 return VINF_SUCCESS;
233}
234
235
236RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
237{
238 /*
239 * Validate input.
240 */
241 PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
242 if (!pThis)
243 return VERR_INVALID_PARAMETER;
244 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
245 AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
246 rtR0SemEventMultiNtRetain(pThis);
247
248 /*
249 * Reset the event object.
250 */
251#ifdef IN_RING0
252 KeResetEvent(&pThis->Event);
253#else
254 NTSTATUS rcNt = NtResetEvent(pThis->hEvent, NULL);
255#endif
256
257 rtR0SemEventMultiNtRelease(pThis);
258#ifdef IN_RING3
259 AssertMsgReturn(NT_SUCCESS(rcNt), ("Resetting hEventMultiSem %p failed: %#x\n", pThis, rcNt), RTErrConvertFromNtStatus(rcNt));
260#endif
261 return VINF_SUCCESS;
262}
263
264
265/**
266 * Worker for RTSemEventMultiWaitEx and RTSemEventMultiWaitExDebug.
267 *
268 * @returns VBox status code.
269 * @param pThis The event semaphore.
270 * @param fFlags See RTSemEventMultiWaitEx.
271 * @param uTimeout See RTSemEventMultiWaitEx.
272 * @param pSrcPos The source code position of the wait.
273 */
274DECLINLINE(int) rtR0SemEventMultiNtWait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
275 PCRTLOCKVALSRCPOS pSrcPos)
276{
277 /*
278 * Validate input.
279 */
280 if (!pThis)
281 return VERR_INVALID_PARAMETER;
282 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
283 AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
284 AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
285 RT_NOREF1(pSrcPos);
286
287 rtR0SemEventMultiNtRetain(pThis);
288
289 /*
290 * Lock validation needs to be done only when not polling.
291 */
292#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3)
293 RTTHREAD const hThreadSelf = RTThreadSelfAutoAdopt();
294 if ( pThis->fEverHadSignallers
295 && ( uTimeout != 0
296 || (fFlags & (RTSEMWAIT_FLAGS_INDEFINITE | RTSEMWAIT_FLAGS_ABSOLUTE))) )
297 {
298 int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, NULL /*pSrcPos*/, false,
299 fFlags & RTSEMWAIT_FLAGS_INDEFINITE
300 ? RT_INDEFINITE_WAIT : RT_MS_30SEC /*whatever*/,
301 RTTHREADSTATE_EVENT_MULTI, true);
302 if (RT_FAILURE(rc9))
303 return rc9;
304 }
305#elif defined(IN_RING3)
306 RTTHREAD const hThreadSelf = RTThreadSelf();
307#endif
308
309 /*
310 * Convert the timeout to a relative one because KeWaitForSingleObject
311 * takes system time instead of interrupt time as input for absolute
312 * timeout specifications. So, we're best of by giving it relative time.
313 *
314 * Lazy bird converts uTimeout to relative nanoseconds and then to Nt time.
315 */
316#ifdef IN_RING3
317 uint64_t nsStartNow = 0;
318#endif
319 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
320 {
321 if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
322 uTimeout = uTimeout < UINT64_MAX / RT_NS_1MS
323 ? uTimeout * RT_NS_1MS
324 : UINT64_MAX;
325 if (uTimeout == UINT64_MAX)
326 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
327 else
328 {
329#ifdef IN_RING3
330 if (fFlags & (RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_ABSOLUTE))
331 nsStartNow = RTTimeSystemNanoTS();
332#endif
333 if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE)
334 {
335#ifdef IN_RING0
336 uint64_t const nsStartNow = RTTimeSystemNanoTS();
337#endif
338 uTimeout = nsStartNow < uTimeout
339 ? uTimeout - nsStartNow
340 : 0;
341 }
342 }
343 }
344
345 /*
346 * Wait for it.
347 * We're assuming interruptible waits should happen at UserMode level.
348 */
349 int rc;
350#ifdef IN_RING3
351 for (;;)
352#endif
353 {
354#ifdef IN_RING0
355 BOOLEAN fInterruptible = !!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE);
356 KPROCESSOR_MODE WaitMode = fInterruptible ? UserMode : KernelMode;
357#endif
358 NTSTATUS rcNt;
359#ifdef IN_RING3
360 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true);
361#endif
362 if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
363#ifdef IN_RING0
364 rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, NULL);
365#else
366 rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, NULL);
367#endif
368 else
369 {
370 LARGE_INTEGER Timeout;
371 Timeout.QuadPart = -(int64_t)(uTimeout / 100);
372#ifdef IN_RING0
373 rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, &Timeout);
374#else
375 rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, &Timeout);
376#endif
377 }
378#ifdef IN_RING3
379 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI);
380#endif
381 if (pThis->u32Magic == RTSEMEVENTMULTI_MAGIC)
382 {
383 switch (rcNt)
384 {
385 case STATUS_SUCCESS:
386 rc = VINF_SUCCESS;
387 break;
388
389 case STATUS_TIMEOUT:
390 Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE));
391 rc = VERR_TIMEOUT;
392 break;
393
394 case STATUS_USER_APC:
395 case STATUS_ALERTED:
396 rc = VERR_INTERRUPTED;
397#ifdef IN_RING3
398 /* Loop if when automatically resuming on interruption, adjusting the timeout. */
399 if (fFlags & RTSEMWAIT_FLAGS_RESUME)
400 {
401 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE) && uTimeout > 0)
402 {
403 uint64_t const nsNewNow = RTTimeSystemNanoTS();
404 uint64_t const cNsElapsed = nsNewNow - nsStartNow;
405 if (cNsElapsed < uTimeout)
406 uTimeout -= cNsElapsed;
407 else
408 uTimeout = 0;
409 nsStartNow = nsNewNow;
410 }
411 continue;
412 }
413#endif
414 break;
415
416#ifdef IN_RING3
417 case STATUS_ABANDONED_WAIT_0:
418 rc = VERR_SEM_OWNER_DIED;
419 break;
420#endif
421 default:
422 AssertMsgFailed(("pThis->u32Magic=%RX32 pThis=%p: wait returned %x!\n", pThis->u32Magic, pThis, rcNt));
423 rc = VERR_INTERNAL_ERROR_4;
424 break;
425 }
426 }
427 else
428 rc = VERR_SEM_DESTROYED;
429#ifdef IN_RING3
430 break;
431#endif
432 }
433
434 rtR0SemEventMultiNtRelease(pThis);
435 return rc;
436}
437
438
439RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
440{
441#ifndef RTSEMEVENT_STRICT
442 return rtR0SemEventMultiNtWait(hEventMultiSem, fFlags, uTimeout, NULL);
443#else
444 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
445 return rtR0SemEventMultiNtWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
446#endif
447}
448
449
450RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
451 RTHCUINTPTR uId, RT_SRC_POS_DECL)
452{
453 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
454 return rtR0SemEventMultiNtWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
455}
456
457
458#ifdef IN_RING0
459RTR0DECL(bool) RTSemEventMultiIsSignalSafe(void)
460{
461 return KeGetCurrentIrql() <= DISPATCH_LEVEL;
462}
463#endif
464
465#ifdef IN_RING3
466
467RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
468{
469# ifdef RTSEMEVENT_STRICT
470 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
471 AssertPtrReturnVoid(pThis);
472 AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
473
474 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
475 RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
476# else
477 RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
478# endif
479}
480
481
482RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
483{
484# ifdef RTSEMEVENT_STRICT
485 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
486 AssertPtrReturnVoid(pThis);
487 AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
488
489 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
490 RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
491# else
492 RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
493# endif
494}
495
496
497RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
498{
499# ifdef RTSEMEVENT_STRICT
500 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
501 AssertPtrReturnVoid(pThis);
502 AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
503
504 RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
505# else
506 RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
507# endif
508}
509
510#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