VirtualBox

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

Last change on this file since 25724 was 25724, checked in by vboxsync, 15 years ago

iprt: Use RTMSINTERVAL for timeouts. Fixed missing timeout underflow checks in two RTFileAioCtxWait implementations.

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