VirtualBox

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

Last change on this file since 68198 was 63417, checked in by vboxsync, 8 years ago

IPRT: Fixed for IPRT_WITH_FUTEX_BASED_SEMS=1.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.5 KB
Line 
1/* $Id: semeventmulti-linux.cpp 63417 2016-08-13 16:42:19Z vboxsync $ */
2/** @file
3 * IPRT - Multiple Release Event Semaphore, Linux (2.6.x+).
4 */
5
6/*
7 * Copyright (C) 2006-2016 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"
42asm volatile (".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#if 0 /* With 2.6.17 futex.h has become C++ unfriendly. */
70# include <linux/futex.h>
71#else
72# define FUTEX_WAIT 0
73# define FUTEX_WAKE 1
74#endif
75
76
77/*********************************************************************************************************************************
78* Structures and Typedefs *
79*********************************************************************************************************************************/
80/**
81 * Linux multiple wakup event semaphore.
82 */
83struct RTSEMEVENTMULTIINTERNAL
84{
85 /** Magic value. */
86 uint32_t volatile u32Magic;
87 /** The futex state variable.
88 * -1 means signaled.
89 * 0 means not signaled, no waiters.
90 * 1 means not signaled and that someone is waiting.
91 */
92 int32_t volatile iState;
93#ifdef RTSEMEVENTMULTI_STRICT
94 /** Signallers. */
95 RTLOCKVALRECSHRD Signallers;
96 /** Indicates that lock validation should be performed. */
97 bool volatile fEverHadSignallers;
98#endif
99};
100
101
102/**
103 * Wrapper for the futex syscall.
104 */
105static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
106{
107 errno = 0;
108 long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
109 if (rc < 0)
110 {
111 Assert(rc == -1);
112 rc = -errno;
113 }
114 return rc;
115}
116
117
118RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
119{
120 return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
121}
122
123
124RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
125 const char *pszNameFmt, ...)
126{
127 AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
128
129 /*
130 * Allocate semaphore handle.
131 */
132 struct RTSEMEVENTMULTIINTERNAL *pThis = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
133 if (pThis)
134 {
135 pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
136 pThis->iState = 0;
137#ifdef RTSEMEVENTMULTI_STRICT
138 if (!pszNameFmt)
139 {
140 static uint32_t volatile s_iSemEventMultiAnon = 0;
141 RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
142 true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
143 "RTSemEventMulti-%u", ASMAtomicIncU32(&s_iSemEventMultiAnon) - 1);
144 }
145 else
146 {
147 va_list va;
148 va_start(va, pszNameFmt);
149 RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
150 true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
151 pszNameFmt, va);
152 va_end(va);
153 }
154 pThis->fEverHadSignallers = false;
155#else
156 RT_NOREF(hClass, pszNameFmt);
157#endif
158
159 *phEventMultiSem = pThis;
160 return VINF_SUCCESS;
161 }
162 return VERR_NO_MEMORY;
163}
164
165
166RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
167{
168 /*
169 * Validate input.
170 */
171 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
172 if (pThis == NIL_RTSEMEVENTMULTI)
173 return VINF_SUCCESS;
174 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
175 AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
176
177 /*
178 * Invalidate the semaphore and wake up anyone waiting on it.
179 */
180 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMEVENTMULTI_MAGIC + 1);
181 if (ASMAtomicXchgS32(&pThis->iState, -1) == 1)
182 {
183 sys_futex(&pThis->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
184 usleep(1000);
185 }
186
187 /*
188 * Free the semaphore memory and be gone.
189 */
190#ifdef RTSEMEVENTMULTI_STRICT
191 RTLockValidatorRecSharedDelete(&pThis->Signallers);
192#endif
193 RTMemFree(pThis);
194 return VINF_SUCCESS;
195}
196
197
198RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
199{
200 /*
201 * Validate input.
202 */
203 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
204 AssertReturn(VALID_PTR(pThis) && pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
205 VERR_INVALID_HANDLE);
206
207#ifdef RTSEMEVENTMULTI_STRICT
208 if (pThis->fEverHadSignallers)
209 {
210 int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
211 if (RT_FAILURE(rc9))
212 return rc9;
213 }
214#endif
215
216
217 /*
218 * Signal it.
219 */
220 int32_t iOld = ASMAtomicXchgS32(&pThis->iState, -1);
221 if (iOld > 0)
222 {
223 /* wake up sleeping threads. */
224 long cWoken = sys_futex(&pThis->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
225 AssertMsg(cWoken >= 0, ("%ld\n", cWoken)); NOREF(cWoken);
226 }
227 Assert(iOld == 0 || iOld == -1 || iOld == 1);
228 return VINF_SUCCESS;
229}
230
231
232RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
233{
234 /*
235 * Validate input.
236 */
237 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
238 AssertReturn(VALID_PTR(pThis) && pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
239 VERR_INVALID_HANDLE);
240#ifdef RT_STRICT
241 int32_t i = pThis->iState;
242 Assert(i == 0 || i == -1 || i == 1);
243#endif
244
245 /*
246 * Reset it.
247 */
248 ASMAtomicCmpXchgS32(&pThis->iState, 0, -1);
249 return VINF_SUCCESS;
250}
251
252
253
254DECLINLINE(int) rtSemEventLnxMultiWait(struct RTSEMEVENTMULTIINTERNAL *pThis, uint32_t fFlags, uint64_t uTimeout,
255 PCRTLOCKVALSRCPOS pSrcPos)
256{
257 RT_NOREF(pSrcPos);
258
259 /*
260 * Validate input.
261 */
262 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
263 AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
264 AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
265
266 /*
267 * Quickly check whether it's signaled.
268 */
269 int32_t iCur = ASMAtomicUoReadS32(&pThis->iState);
270 Assert(iCur == 0 || iCur == -1 || iCur == 1);
271 if (iCur == -1)
272 return VINF_SUCCESS;
273
274 /*
275 * Check and convert the timeout value.
276 */
277 struct timespec ts;
278 struct timespec *pTimeout = NULL;
279 uint64_t u64Deadline = 0; /* shut up gcc */
280 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
281 {
282 /* If the timeout is zero, then we're done. */
283 if (!uTimeout)
284 return VERR_TIMEOUT;
285
286 /* Convert it to a deadline + interval timespec. */
287 if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
288 uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000)
289 ? uTimeout * UINT32_C(1000000)
290 : UINT64_MAX;
291 if (uTimeout != UINT64_MAX) /* unofficial way of indicating an indefinite wait */
292 {
293 if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
294 u64Deadline = RTTimeSystemNanoTS() + uTimeout;
295 else
296 {
297 uint64_t u64Now = RTTimeSystemNanoTS();
298 if (uTimeout <= u64Now)
299 return VERR_TIMEOUT;
300 u64Deadline = uTimeout;
301 uTimeout -= u64Now;
302 }
303 if ( sizeof(ts.tv_sec) >= sizeof(uint64_t)
304 || uTimeout <= UINT64_C(1000000000) * UINT32_MAX)
305 {
306 ts.tv_nsec = uTimeout % UINT32_C(1000000000);
307 ts.tv_sec = uTimeout / UINT32_C(1000000000);
308 pTimeout = &ts;
309 }
310 }
311 }
312
313 /*
314 * The wait loop.
315 */
316#ifdef RTSEMEVENTMULTI_STRICT
317 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
318#else
319 RTTHREAD hThreadSelf = RTThreadSelf();
320#endif
321 for (unsigned i = 0;; i++)
322 {
323 /*
324 * Start waiting. We only account for there being or having been
325 * threads waiting on the semaphore to keep things simple.
326 */
327 iCur = ASMAtomicUoReadS32(&pThis->iState);
328 Assert(iCur == 0 || iCur == -1 || iCur == 1);
329 if ( iCur == 1
330 || ASMAtomicCmpXchgS32(&pThis->iState, 1, 0))
331 {
332 /* adjust the relative timeout */
333 if (pTimeout)
334 {
335 int64_t i64Diff = u64Deadline - RTTimeSystemNanoTS();
336 if (i64Diff < 1000)
337 return VERR_TIMEOUT;
338 ts.tv_sec = (uint64_t)i64Diff / UINT32_C(1000000000);
339 ts.tv_nsec = (uint64_t)i64Diff % UINT32_C(1000000000);
340 }
341#ifdef RTSEMEVENTMULTI_STRICT
342 if (pThis->fEverHadSignallers)
343 {
344 int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
345 uTimeout / UINT32_C(1000000), RTTHREADSTATE_EVENT_MULTI, true);
346 if (RT_FAILURE(rc9))
347 return rc9;
348 }
349#endif
350 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true);
351 long rc = sys_futex(&pThis->iState, FUTEX_WAIT, 1, pTimeout, NULL, 0);
352 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI);
353 if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
354 return VERR_SEM_DESTROYED;
355 if (rc == 0)
356 return VINF_SUCCESS;
357
358 /*
359 * Act on the wakup code.
360 */
361 if (rc == -ETIMEDOUT)
362 {
363/** @todo something is broken here. shows up every now and again in the ata
364 * code. Should try to run the timeout against RTTimeMilliTS to
365 * check that it's doing the right thing... */
366 Assert(pTimeout);
367 return VERR_TIMEOUT;
368 }
369 if (rc == -EWOULDBLOCK)
370 /* retry, the value changed. */;
371 else if (rc == -EINTR)
372 {
373 if (fFlags & RTSEMWAIT_FLAGS_NORESUME)
374 return VERR_INTERRUPTED;
375 }
376 else
377 {
378 /* this shouldn't happen! */
379 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
380 return RTErrConvertFromErrno(rc);
381 }
382 }
383 else if (iCur == -1)
384 return VINF_SUCCESS;
385 }
386}
387
388
389#undef RTSemEventMultiWaitEx
390RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
391{
392#ifndef RTSEMEVENT_STRICT
393 return rtSemEventLnxMultiWait(hEventMultiSem, fFlags, uTimeout, NULL);
394#else
395 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
396 return rtSemEventLnxMultiWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
397#endif
398}
399
400
401RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
402 RTHCUINTPTR uId, RT_SRC_POS_DECL)
403{
404 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
405 return rtSemEventLnxMultiWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
406}
407
408
409RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
410{
411#ifdef RTSEMEVENTMULTI_STRICT
412 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
413 AssertPtrReturnVoid(pThis);
414 AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
415
416 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
417 RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
418#else
419 RT_NOREF(hEventMultiSem, hThread);
420#endif
421}
422
423
424RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
425{
426#ifdef RTSEMEVENTMULTI_STRICT
427 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
428 AssertPtrReturnVoid(pThis);
429 AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
430
431 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
432 RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
433#else
434 RT_NOREF(hEventMultiSem, hThread);
435#endif
436}
437
438
439RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
440{
441#ifdef RTSEMEVENTMULTI_STRICT
442 struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
443 AssertPtrReturnVoid(pThis);
444 AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
445
446 RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
447#else
448 RT_NOREF(hEventMultiSem, hThread);
449#endif
450}
451
452#endif /* glibc < 2.6 || IPRT_WITH_FUTEX_BASED_SEMS */
453
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