VirtualBox

source: vbox/trunk/src/VBox/Runtime/nt/semeventmulti-nt.cpp@ 106877

Last change on this file since 106877 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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