VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/semeventmulti-linux.cpp@ 92779

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

IPRT/semevent*-linux: Share some common bits between the single-release and multiple-release linux event sempahore code. Freshened up the latter a bit. bugref:10138

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.6 KB
Line 
1/* $Id: semeventmulti-linux.cpp 92779 2021-12-07 09:45:53Z vboxsync $ */
2/** @file
3 * IPRT - Multiple Release Event Semaphore, Linux (2.6.x+).
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#include <features.h>
29#if __GLIBC_PREREQ(2,6) && !defined(IPRT_WITH_FUTEX_BASED_SEMS)
30
31/*
32 * glibc 2.6 fixed a serious bug in the mutex implementation. We wrote this
33 * linux specific event semaphores code in order to work around the bug. As it
34 * turns out, this code seems to have an unresolved issue (@bugref{2599}), so we'll
35 * fall back on the pthread based implementation if glibc is known to contain
36 * the bug fix.
37 *
38 * The external reference to epoll_pwait is a hack which prevents that we link
39 * against glibc < 2.6.
40 */
41#include "../posix/semeventmulti-posix.cpp"
42__asm__ (".global epoll_pwait");
43
44#else /* glibc < 2.6 */
45
46
47/*********************************************************************************************************************************
48* Header Files *
49*********************************************************************************************************************************/
50#include <iprt/semaphore.h>
51#include "internal/iprt.h"
52
53#include <iprt/assert.h>
54#include <iprt/asm.h>
55#include <iprt/err.h>
56#include <iprt/lockvalidator.h>
57#include <iprt/mem.h>
58#include <iprt/time.h>
59#include "internal/magics.h"
60#include "internal/strict.h"
61
62
63#include <errno.h>
64#include <limits.h>
65#include <pthread.h>
66#include <unistd.h>
67#include <sys/time.h>
68#include <sys/syscall.h>
69
70#include "semwait-linux.h"
71
72
73/*********************************************************************************************************************************
74* Structures and Typedefs *
75*********************************************************************************************************************************/
76/**
77 * Linux multiple wakup event semaphore.
78 */
79struct RTSEMEVENTMULTIINTERNAL
80{
81 /** Magic value. */
82 uint32_t volatile u32Magic;
83 /** The futex state variable, see RTSEMEVENTMULTI_LNX_XXX. */
84 uint32_t volatile uState;
85#ifdef RT_STRICT
86 /** Increased on every signalling call. */
87 uint32_t volatile uSignalSerialNo;
88#endif
89#ifdef RTSEMEVENTMULTI_STRICT
90 /** Signallers. */
91 RTLOCKVALRECSHRD Signallers;
92 /** Indicates that lock validation should be performed. */
93 bool volatile fEverHadSignallers;
94#endif
95};
96
97
98/*********************************************************************************************************************************
99* Defined Constants And Macros *
100*********************************************************************************************************************************/
101/** @name RTSEMEVENTMULTI_LNX_XXX - state
102 * @{ */
103#define RTSEMEVENTMULTI_LNX_NOT_SIGNALED UINT32_C(0x00000000)
104#define RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS UINT32_C(0x00000001)
105#define RTSEMEVENTMULTI_LNX_SIGNALED UINT32_C(0x00000003)
106/** @} */
107
108#define ASSERT_VALID_STATE(a_uState) \
109 AssertMsg( (a_uState) == RTSEMEVENTMULTI_LNX_NOT_SIGNALED \
110 || (a_uState) == RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS \
111 || (a_uState) == RTSEMEVENTMULTI_LNX_SIGNALED, \
112 (#a_uState "=%s\n", a_uState))
113
114
115/*********************************************************************************************************************************
116* Global Variables *
117*********************************************************************************************************************************/
118/** Whether we can use FUTEX_WAIT_BITSET. */
119static int volatile g_fCanUseWaitBitSet = -1;
120
121
122RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
123{
124 return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
125}
126
127
128RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
129 const char *pszNameFmt, ...)
130{
131 AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
132
133 /*
134 * Make sure we know whether FUTEX_WAIT_BITSET works.
135 */
136 rtSemLinuxCheckForFutexWaitBitSet(&g_fCanUseWaitBitSet);
137#if defined(DEBUG_bird) && !defined(IN_GUEST)
138 Assert(g_fCanUseWaitBitSet == true);
139#endif
140
141 /*
142 * Allocate semaphore handle.
143 */
144 struct RTSEMEVENTMULTIINTERNAL *pThis = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
145 if (pThis)
146 {
147 pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
148 pThis->uState = RTSEMEVENTMULTI_LNX_NOT_SIGNALED;
149#ifdef RT_STRICT
150 pThis->uSignalSerialNo = 0;
151#endif
152#ifdef RTSEMEVENTMULTI_STRICT
153 if (!pszNameFmt)
154 {
155 static uint32_t volatile s_iSemEventMultiAnon = 0;
156 RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
157 true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
158 "RTSemEventMulti-%u", ASMAtomicIncU32(&s_iSemEventMultiAnon) - 1);
159 }
160 else
161 {
162 va_list va;
163 va_start(va, pszNameFmt);
164 RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
165 true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
166 pszNameFmt, va);
167 va_end(va);
168 }
169 pThis->fEverHadSignallers = false;
170#else
171 RT_NOREF(hClass, pszNameFmt);
172#endif
173
174 *phEventMultiSem = pThis;
175 return VINF_SUCCESS;
176 }
177 return VERR_NO_MEMORY;
178}
179
180
181RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
182{
183 /*
184 * Validate input.
185 */
186 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
187 if (pThis == NIL_RTSEMEVENTMULTI)
188 return VINF_SUCCESS;
189 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
190 AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
191
192 /*
193 * Invalidate the semaphore and wake up anyone waiting on it.
194 */
195 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMEVENTMULTI_MAGIC + 1);
196 if (ASMAtomicXchgU32(&pThis->uState, RTSEMEVENTMULTI_LNX_SIGNALED) == RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS)
197 {
198 sys_futex(&pThis->uState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
199 usleep(1000);
200 }
201
202 /*
203 * Free the semaphore memory and be gone.
204 */
205#ifdef RTSEMEVENTMULTI_STRICT
206 RTLockValidatorRecSharedDelete(&pThis->Signallers);
207#endif
208 RTMemFree(pThis);
209 return VINF_SUCCESS;
210}
211
212
213RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
214{
215 /*
216 * Validate input.
217 */
218 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
219 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
220 AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
221
222#ifdef RTSEMEVENTMULTI_STRICT
223 if (pThis->fEverHadSignallers)
224 {
225 int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
226 if (RT_FAILURE(rc9))
227 return rc9;
228 }
229#endif
230
231 /*
232 * Signal it.
233 */
234#ifdef RT_STRICT
235 ASMAtomicIncU32(&pThis->uSignalSerialNo);
236#endif
237 uint32_t uOld = ASMAtomicXchgU32(&pThis->uState, RTSEMEVENTMULTI_LNX_SIGNALED);
238 if (uOld == RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS)
239 {
240 /* wake up sleeping threads. */
241 long cWoken = sys_futex(&pThis->uState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
242 AssertMsg(cWoken >= 0, ("%ld\n", cWoken)); NOREF(cWoken);
243 }
244 ASSERT_VALID_STATE(uOld);
245 return VINF_SUCCESS;
246}
247
248
249RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
250{
251 /*
252 * Validate input.
253 */
254 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
255 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
256 AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
257#ifdef RT_STRICT
258 uint32_t const uState = pThis->uState;
259 ASSERT_VALID_STATE(uState);
260#endif
261
262 /*
263 * Reset it.
264 */
265 ASMAtomicCmpXchgU32(&pThis->uState, RTSEMEVENTMULTI_LNX_NOT_SIGNALED, RTSEMEVENTMULTI_LNX_SIGNALED);
266 return VINF_SUCCESS;
267}
268
269
270
271DECLINLINE(int) rtSemEventLnxMultiWait(struct RTSEMEVENTMULTIINTERNAL *pThis, uint32_t fFlags, uint64_t uTimeout,
272 PCRTLOCKVALSRCPOS pSrcPos)
273{
274 RT_NOREF(pSrcPos);
275
276 /*
277 * Validate input.
278 */
279 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
280 AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
281 AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
282
283 /*
284 * Quickly check whether it's signaled.
285 */
286 uint32_t uState = ASMAtomicUoReadU32(&pThis->uState);
287 if (uState == RTSEMEVENTMULTI_LNX_SIGNALED)
288 return VINF_SUCCESS;
289 ASSERT_VALID_STATE(uState);
290
291 /*
292 * Check and convert the timeout value.
293 */
294 struct timespec ts;
295 struct timespec *pTimeout = NULL;
296 uint64_t u64Deadline = 0; /* shut up gcc */
297 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
298 {
299 /* If the timeout is zero, then we're done. */
300 if (!uTimeout)
301 return VERR_TIMEOUT;
302
303 /* Convert it to a deadline + interval timespec. */
304 if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
305 uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000)
306 ? uTimeout * UINT32_C(1000000)
307 : UINT64_MAX;
308 if (uTimeout != UINT64_MAX) /* unofficial way of indicating an indefinite wait */
309 {
310 if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
311 u64Deadline = RTTimeSystemNanoTS() + uTimeout;
312 else
313 {
314 uint64_t u64Now = RTTimeSystemNanoTS();
315 if (uTimeout <= u64Now)
316 return VERR_TIMEOUT;
317 u64Deadline = uTimeout;
318 uTimeout -= u64Now;
319 }
320 if ( sizeof(ts.tv_sec) >= sizeof(uint64_t)
321 || uTimeout <= UINT64_C(1000000000) * UINT32_MAX)
322 {
323 ts.tv_nsec = uTimeout % UINT32_C(1000000000);
324 ts.tv_sec = uTimeout / UINT32_C(1000000000);
325 pTimeout = &ts;
326 }
327 }
328 }
329
330 /*
331 * The wait loop.
332 */
333#ifdef RTSEMEVENTMULTI_STRICT
334 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
335#else
336 RTTHREAD hThreadSelf = RTThreadSelf();
337#endif
338 for (unsigned i = 0;; i++)
339 {
340 /*
341 * Start waiting. We only account for there being or having been
342 * threads waiting on the semaphore to keep things simple.
343 */
344 uState = ASMAtomicUoReadU32(&pThis->uState);
345 if ( uState == RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS
346 || ( uState == RTSEMEVENTMULTI_LNX_NOT_SIGNALED
347 && ASMAtomicCmpXchgU32(&pThis->uState, RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS,
348 RTSEMEVENTMULTI_LNX_NOT_SIGNALED)))
349 {
350 /* adjust the relative timeout */
351 if (pTimeout)
352 {
353 int64_t i64Diff = u64Deadline - RTTimeSystemNanoTS();
354 if (i64Diff < 1000)
355 return VERR_TIMEOUT;
356 ts.tv_sec = (uint64_t)i64Diff / UINT32_C(1000000000);
357 ts.tv_nsec = (uint64_t)i64Diff % UINT32_C(1000000000);
358 }
359#ifdef RTSEMEVENTMULTI_STRICT
360 if (pThis->fEverHadSignallers)
361 {
362 int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
363 uTimeout / UINT32_C(1000000), RTTHREADSTATE_EVENT_MULTI, true);
364 if (RT_FAILURE(rc9))
365 return rc9;
366 }
367#endif
368#ifdef RT_STRICT
369 uint32_t const uPrevSignalSerialNo = ASMAtomicReadU32(&pThis->uSignalSerialNo);
370#endif
371 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true);
372 long rc = sys_futex(&pThis->uState, FUTEX_WAIT, 1, pTimeout, NULL, 0);
373 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI);
374 if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
375 return VERR_SEM_DESTROYED;
376 if (rc == 0)
377 {
378 Assert(uPrevSignalSerialNo != ASMAtomicReadU32(&pThis->uSignalSerialNo));
379 return VINF_SUCCESS;
380 }
381
382 /*
383 * Act on the wakup code.
384 */
385 if (rc == -ETIMEDOUT)
386 {
387/** @todo something is broken here. shows up every now and again in the ata
388 * code. Should try to run the timeout against RTTimeMilliTS to
389 * check that it's doing the right thing... */
390 Assert(pTimeout);
391 return VERR_TIMEOUT;
392 }
393 if (rc == -EWOULDBLOCK)
394 /* retry, the value changed. */;
395 else if (rc == -EINTR)
396 {
397 if (fFlags & RTSEMWAIT_FLAGS_NORESUME)
398 return VERR_INTERRUPTED;
399 }
400 else
401 {
402 /* this shouldn't happen! */
403 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
404 return RTErrConvertFromErrno(rc);
405 }
406 }
407 else if (uState == RTSEMEVENTMULTI_LNX_SIGNALED)
408 return VINF_SUCCESS;
409 else
410 ASSERT_VALID_STATE(uState);
411 }
412}
413
414
415#undef RTSemEventMultiWaitEx
416RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
417{
418#ifndef RTSEMEVENT_STRICT
419 return rtSemEventLnxMultiWait(hEventMultiSem, fFlags, uTimeout, NULL);
420#else
421 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
422 return rtSemEventLnxMultiWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
423#endif
424}
425
426
427RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
428 RTHCUINTPTR uId, RT_SRC_POS_DECL)
429{
430 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
431 return rtSemEventLnxMultiWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
432}
433
434
435RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
436{
437#ifdef RTSEMEVENTMULTI_STRICT
438 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
439 AssertPtrReturnVoid(pThis);
440 AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
441
442 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
443 RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
444#else
445 RT_NOREF(hEventMultiSem, hThread);
446#endif
447}
448
449
450RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
451{
452#ifdef RTSEMEVENTMULTI_STRICT
453 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
454 AssertPtrReturnVoid(pThis);
455 AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
456
457 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
458 RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
459#else
460 RT_NOREF(hEventMultiSem, hThread);
461#endif
462}
463
464
465RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
466{
467#ifdef RTSEMEVENTMULTI_STRICT
468 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
469 AssertPtrReturnVoid(pThis);
470 AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
471
472 RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
473#else
474 RT_NOREF(hEventMultiSem, hThread);
475#endif
476}
477
478#endif /* glibc < 2.6 || IPRT_WITH_FUTEX_BASED_SEMS */
479
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