VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 15.0 KB
Line 
1/* $Id: semevent-r0drv-darwin.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Single Release Event Semaphores, Ring-0 Driver, Darwin.
4 */
5
6/*
7 * Copyright (C) 2006-2023 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 RTSEMEVENT_WITHOUT_REMAPPING
42#include "the-darwin-kernel.h"
43#include "internal/iprt.h"
44#include <iprt/semaphore.h>
45
46#include <iprt/assert.h>
47#include <iprt/asm.h>
48#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
49# include <iprt/asm-amd64-x86.h>
50#endif
51#include <iprt/err.h>
52#include <iprt/list.h>
53#include <iprt/lockvalidator.h>
54#include <iprt/mem.h>
55#include <iprt/mp.h>
56#include <iprt/thread.h>
57#include <iprt/time.h>
58
59#include "internal/magics.h"
60
61
62/*********************************************************************************************************************************
63* Structures and Typedefs *
64*********************************************************************************************************************************/
65/**
66 * Waiter entry. Lives on the stack.
67 */
68typedef struct RTSEMEVENTDARWINENTRY
69{
70 /** The list node. */
71 RTLISTNODE Node;
72 /** Flag set when waking up the thread by signal or destroy. */
73 bool volatile fWokenUp;
74} RTSEMEVENTDARWINENTRY;
75/** Pointer to waiter entry. */
76typedef RTSEMEVENTDARWINENTRY *PRTSEMEVENTDARWINENTRY;
77
78
79/**
80 * Darwin event semaphore.
81 */
82typedef struct RTSEMEVENTINTERNAL
83{
84 /** Magic value (RTSEMEVENT_MAGIC). */
85 uint32_t volatile u32Magic;
86 /** Reference counter. */
87 uint32_t volatile cRefs;
88 /** Set if there are blocked threads. */
89 bool volatile fHaveBlockedThreads;
90 /** Set if the event object is signaled. */
91 bool volatile fSignaled;
92 /** List of waiting and woken up threads. */
93 RTLISTANCHOR WaitList;
94 /** The spinlock protecting us. */
95 lck_spin_t *pSpinlock;
96} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
97
98
99
100RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
101{
102 return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
103}
104
105
106RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
107{
108 RT_NOREF(hClass, pszNameFmt);
109 AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
110 AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
111 Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
112 AssertPtrReturn(phEventSem, VERR_INVALID_POINTER);
113 RT_ASSERT_PREEMPTIBLE();
114 IPRT_DARWIN_SAVE_EFL_AC();
115
116 PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis));
117 if (pThis)
118 {
119 pThis->u32Magic = RTSEMEVENT_MAGIC;
120 pThis->cRefs = 1;
121 pThis->fHaveBlockedThreads = false;
122 pThis->fSignaled = false;
123 RTListInit(&pThis->WaitList);
124 Assert(g_pDarwinLockGroup);
125 pThis->pSpinlock = lck_spin_alloc_init(g_pDarwinLockGroup, LCK_ATTR_NULL);
126 if (pThis->pSpinlock)
127 {
128 *phEventSem = pThis;
129 IPRT_DARWIN_RESTORE_EFL_AC();
130 return VINF_SUCCESS;
131 }
132
133 pThis->u32Magic = 0;
134 RTMemFree(pThis);
135 }
136 IPRT_DARWIN_RESTORE_EFL_AC();
137 return VERR_NO_MEMORY;
138}
139
140
141/**
142 * Retain a reference to the semaphore.
143 *
144 * @param pThis The semaphore.
145 */
146DECLINLINE(void) rtR0SemEventDarwinRetain(PRTSEMEVENTINTERNAL pThis)
147{
148 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
149 Assert(cRefs && cRefs < 100000); RT_NOREF_PV(cRefs);
150}
151
152
153/**
154 * Release a reference, destroy the thing if necessary.
155 *
156 * @param pThis The semaphore.
157 */
158DECLINLINE(void) rtR0SemEventDarwinRelease(PRTSEMEVENTINTERNAL pThis)
159{
160 if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
161 {
162 Assert(pThis->u32Magic != RTSEMEVENT_MAGIC);
163 IPRT_DARWIN_SAVE_EFL_AC();
164
165 lck_spin_destroy(pThis->pSpinlock, g_pDarwinLockGroup);
166 RTMemFree(pThis);
167
168 IPRT_DARWIN_RESTORE_EFL_AC();
169 }
170}
171
172RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
173{
174 PRTSEMEVENTINTERNAL pThis = hEventSem;
175 if (pThis == NIL_RTSEMEVENT)
176 return VINF_SUCCESS;
177 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
178 AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
179 RT_ASSERT_INTS_ON();
180 IPRT_DARWIN_SAVE_EFL_AC();
181
182 RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
183 lck_spin_lock(pThis->pSpinlock);
184
185 ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC); /* make the handle invalid */
186 ASMAtomicWriteBool(&pThis->fSignaled, false);
187
188 /* abort waiting threads. */
189 PRTSEMEVENTDARWINENTRY pWaiter;
190 RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTDARWINENTRY, Node)
191 {
192 pWaiter->fWokenUp = true;
193 thread_wakeup_prim((event_t)pWaiter, FALSE /* all threads */, THREAD_RESTART);
194 }
195
196 lck_spin_unlock(pThis->pSpinlock);
197 ASMSetFlags(fIntSaved);
198 rtR0SemEventDarwinRelease(pThis);
199
200 IPRT_DARWIN_RESTORE_EFL_AC();
201 return VINF_SUCCESS;
202}
203
204
205RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
206{
207 PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
208 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
209 AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC,
210 ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
211 VERR_INVALID_HANDLE);
212 RT_ASSERT_PREEMPT_CPUID_VAR();
213
214 /*
215 * Coming here with interrupts disabled should be okay. The thread_wakeup_prim KPI is used
216 * by the interrupt handler IOFilterInterruptEventSource::disableInterruptOccurred() via
217 * signalWorkAvailable(). The only problem is if we have to destroy the event structure,
218 * as RTMemFree does not work with interrupts disabled (IOFree/kfree takes zone mutex).
219 */
220 //RT_ASSERT_INTS_ON(); - we may be called from interrupt context, which seems to be perfectly fine.
221 IPRT_DARWIN_SAVE_EFL_AC();
222
223 RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
224 rtR0SemEventDarwinRetain(pThis);
225 lck_spin_lock(pThis->pSpinlock);
226
227 /*
228 * Wake up one thread.
229 */
230 ASMAtomicWriteBool(&pThis->fSignaled, true);
231
232 PRTSEMEVENTDARWINENTRY pWaiter;
233 RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTDARWINENTRY, Node)
234 {
235 if (!pWaiter->fWokenUp)
236 {
237 pWaiter->fWokenUp = true;
238 thread_wakeup_prim((event_t)pWaiter, FALSE /* all threads */, THREAD_AWAKENED);
239 ASMAtomicWriteBool(&pThis->fSignaled, false);
240 break;
241 }
242 }
243
244 lck_spin_unlock(pThis->pSpinlock);
245 ASMSetFlags(fIntSaved);
246 rtR0SemEventDarwinRelease(pThis);
247
248 RT_ASSERT_PREEMPT_CPUID();
249 AssertMsg((fSavedEfl & X86_EFL_IF) == (ASMGetFlags() & X86_EFL_IF), ("fSavedEfl=%#x cur=%#x\n",(uint32_t)fSavedEfl, ASMGetFlags()));
250 IPRT_DARWIN_RESTORE_EFL_AC();
251 return VINF_SUCCESS;
252}
253
254
255/**
256 * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
257 *
258 * @returns VBox status code.
259 * @param pThis The event semaphore.
260 * @param fFlags See RTSemEventWaitEx.
261 * @param uTimeout See RTSemEventWaitEx.
262 * @param pSrcPos The source code position of the wait.
263 */
264static int rtR0SemEventDarwinWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
265 PCRTLOCKVALSRCPOS pSrcPos)
266{
267 RT_NOREF(pSrcPos);
268
269 /*
270 * Validate the input.
271 */
272 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
273 AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
274 AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
275 IPRT_DARWIN_SAVE_EFL_AC();
276
277 RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
278 rtR0SemEventDarwinRetain(pThis);
279 lck_spin_lock(pThis->pSpinlock);
280
281 /*
282 * In the signaled state?
283 */
284 int rc;
285 if (ASMAtomicCmpXchgBool(&pThis->fSignaled, false, true))
286 rc = VINF_SUCCESS;
287 else
288 {
289 /*
290 * We have to wait. So, we'll need to convert the timeout and figure
291 * out if it's indefinite or not.
292 */
293 uint64_t uNsAbsTimeout = 1;
294 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
295 {
296 if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
297 uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000)
298 ? uTimeout * UINT32_C(1000000)
299 : UINT64_MAX;
300 if (uTimeout == UINT64_MAX)
301 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
302 else
303 {
304 uint64_t u64Now;
305 if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
306 {
307 if (uTimeout != 0)
308 {
309 u64Now = RTTimeSystemNanoTS();
310 uNsAbsTimeout = u64Now + uTimeout;
311 if (uNsAbsTimeout < u64Now) /* overflow */
312 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
313 }
314 }
315 else
316 {
317 uNsAbsTimeout = uTimeout;
318 u64Now = RTTimeSystemNanoTS();
319 uTimeout = u64Now < uTimeout ? uTimeout - u64Now : 0;
320 }
321 }
322 }
323
324 if ( !(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
325 && uTimeout == 0)
326 {
327 /*
328 * Poll call, we already checked the condition above so no need to
329 * wait for anything.
330 */
331 rc = VERR_TIMEOUT;
332 }
333 else
334 {
335 RTSEMEVENTDARWINENTRY Waiter;
336 Waiter.fWokenUp = false;
337 RTListAppend(&pThis->WaitList, &Waiter.Node);
338
339 for (;;)
340 {
341 /*
342 * Do the actual waiting.
343 */
344 ASMAtomicWriteBool(&pThis->fHaveBlockedThreads, true);
345 wait_interrupt_t fInterruptible = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE ? THREAD_ABORTSAFE : THREAD_UNINT;
346 wait_result_t rcWait;
347 if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
348 rcWait = lck_spin_sleep(pThis->pSpinlock, LCK_SLEEP_DEFAULT, (event_t)&Waiter, fInterruptible);
349 else
350 {
351 uint64_t u64AbsTime;
352 nanoseconds_to_absolutetime(uNsAbsTimeout, &u64AbsTime);
353 rcWait = lck_spin_sleep_deadline(pThis->pSpinlock, LCK_SLEEP_DEFAULT,
354 (event_t)&Waiter, fInterruptible, u64AbsTime);
355 }
356
357 /*
358 * Deal with the wait result.
359 */
360 if (RT_LIKELY(pThis->u32Magic == RTSEMEVENT_MAGIC))
361 {
362 switch (rcWait)
363 {
364 case THREAD_AWAKENED:
365 if (RT_LIKELY(Waiter.fWokenUp))
366 rc = VINF_SUCCESS;
367 else if (fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE)
368 rc = VERR_INTERRUPTED;
369 else
370 continue; /* Seen this happen after fork/exec/something. */
371 break;
372
373 case THREAD_TIMED_OUT:
374 Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE));
375 rc = !Waiter.fWokenUp ? VERR_TIMEOUT : VINF_SUCCESS;
376 break;
377
378 case THREAD_INTERRUPTED:
379 Assert(fInterruptible != THREAD_UNINT);
380 rc = !Waiter.fWokenUp ? VERR_INTERRUPTED : VINF_SUCCESS;
381 break;
382
383 case THREAD_RESTART:
384 AssertMsg(pThis->u32Magic == ~RTSEMEVENT_MAGIC, ("%#x\n", pThis->u32Magic));
385 rc = VERR_SEM_DESTROYED;
386 break;
387
388 default:
389 AssertMsgFailed(("rcWait=%d\n", rcWait));
390 rc = VERR_INTERNAL_ERROR_3;
391 break;
392 }
393 }
394 else
395 rc = VERR_SEM_DESTROYED;
396 break;
397 }
398
399 RTListNodeRemove(&Waiter.Node);
400 }
401 }
402
403 lck_spin_unlock(pThis->pSpinlock);
404 ASMSetFlags(fIntSaved);
405 rtR0SemEventDarwinRelease(pThis);
406
407 IPRT_DARWIN_RESTORE_EFL_AC();
408 return rc;
409}
410
411
412RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
413{
414#ifndef RTSEMEVENT_STRICT
415 return rtR0SemEventDarwinWait(hEventSem, fFlags, uTimeout, NULL);
416#else
417 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
418 return rtR0SemEventDarwinWait(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 rtR0SemEventDarwinWait(hEventSem, fFlags, uTimeout, &SrcPos);
428}
429
430
431RTDECL(uint32_t) RTSemEventGetResolution(void)
432{
433 uint64_t cNs;
434 absolutetime_to_nanoseconds(1, &cNs);
435 return (uint32_t)cNs ? (uint32_t)cNs : 0;
436}
437
438
439RTR0DECL(bool) RTSemEventIsSignalSafe(void)
440{
441 /** @todo check the code... */
442 return false;
443}
444RT_EXPORT_SYMBOL(RTSemEventIsSignalSafe);
445
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use