VirtualBox

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

Last change on this file since 34147 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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