VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/semevent-linux.cpp@ 25651

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

-DEBUG_bird

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.8 KB
Line 
1/* $Id: semevent-linux.cpp 25651 2010-01-05 14:44:52Z vboxsync $ */
2/** @file
3 * IPRT - Event Semaphore, Linux (2.6.x+).
4 */
5
6/*
7 * Copyright (C) 2006-2010 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#include <features.h>
32#if __GLIBC_PREREQ(2,6) && !defined(IPRT_WITH_FUTEX_BASED_SEMS)
33
34/*
35 * glibc 2.6 fixed a serious bug in the mutex implementation. We wrote this
36 * linux specific event semaphores code in order to work around the bug. We
37 * will fall back on the pthread-based implementation if glibc is known to
38 * contain the bug fix.
39 *
40 * The external refernce to epoll_pwait is a hack which prevents that we link
41 * against glibc < 2.6.
42 */
43#include "../posix/semevent-posix.cpp"
44asm volatile (".global epoll_pwait");
45
46#else /* glibc < 2.6 */
47
48/*******************************************************************************
49* Header Files *
50*******************************************************************************/
51#include <iprt/semaphore.h>
52#include "internal/iprt.h"
53
54#include <iprt/asm.h>
55#include <iprt/assert.h>
56#include <iprt/err.h>
57#include <iprt/lockvalidator.h>
58#include <iprt/mem.h>
59#include <iprt/time.h>
60#include "internal/magics.h"
61#include "internal/strict.h"
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 (single wakup) event semaphore.
82 */
83struct RTSEMEVENTINTERNAL
84{
85 /** Magic value. */
86 intptr_t volatile iMagic;
87 /** The futex state variable.
88 * 0 means not signalled.
89 1 means signalled. */
90 uint32_t volatile fSignalled;
91 /** The number of waiting threads */
92 int32_t volatile cWaiters;
93#ifdef RTSEMEVENT_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(uint32_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
118
119RTDECL(int) RTSemEventCreate(PRTSEMEVENT pEventSem)
120{
121 /*
122 * Allocate semaphore handle.
123 */
124 struct RTSEMEVENTINTERNAL *pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
125 if (pThis)
126 {
127 pThis->iMagic = RTSEMEVENT_MAGIC;
128 pThis->cWaiters = 0;
129 pThis->fSignalled = 0;
130#ifdef RTSEMEVENT_STRICT
131 RTLockValidatorRecSharedInit(&pThis->Signallers,
132 NIL_RTLOCKVALIDATORCLASS, RTLOCKVALIDATOR_SUB_CLASS_ANY,
133 "RTSemEvent", pThis, true /*fSignaller*/);
134 pThis->fEverHadSignallers = false;
135#endif
136 *pEventSem = pThis;
137 return VINF_SUCCESS;
138 }
139 return VERR_NO_MEMORY;
140}
141
142
143RTDECL(int) RTSemEventDestroy(RTSEMEVENT EventSem)
144{
145 /*
146 * Validate input.
147 */
148 if (EventSem == NIL_RTSEMEVENT) /* don't bitch */
149 return VERR_INVALID_HANDLE;
150 struct RTSEMEVENTINTERNAL *pThis = EventSem;
151 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
152 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
153
154 /*
155 * Invalidate the semaphore and wake up anyone waiting on it.
156 */
157 ASMAtomicXchgSize(&pThis->iMagic, RTSEMEVENT_MAGIC | UINT32_C(0x80000000));
158 if (ASMAtomicXchgS32(&pThis->cWaiters, INT32_MIN / 2) > 0)
159 {
160 sys_futex(&pThis->fSignalled, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
161 usleep(1000);
162 }
163
164 /*
165 * Free the semaphore memory and be gone.
166 */
167#ifdef RTSEMEVENT_STRICT
168 RTLockValidatorRecSharedDelete(&pThis->Signallers);
169#endif
170 RTMemFree(pThis);
171 return VINF_SUCCESS;
172}
173
174
175RTDECL(int) RTSemEventSignal(RTSEMEVENT EventSem)
176{
177 /*
178 * Validate input.
179 */
180 struct RTSEMEVENTINTERNAL *pThis = EventSem;
181 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
182 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
183
184#ifdef RTSEMEVENT_STRICT
185 if (pThis->fEverHadSignallers)
186 {
187 int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
188 if (RT_FAILURE(rc9))
189 return rc9;
190 }
191#endif
192
193 ASMAtomicWriteU32(&pThis->fSignalled, 1);
194 if (ASMAtomicReadS32(&pThis->cWaiters) < 1)
195 return VINF_SUCCESS;
196
197 /* somebody is waiting, try wake up one of them. */
198 long cWoken = sys_futex(&pThis->fSignalled, FUTEX_WAKE, 1, NULL, NULL, 0);
199 if (RT_LIKELY(cWoken >= 0))
200 return VINF_SUCCESS;
201
202 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
203 return VERR_SEM_DESTROYED;
204
205 return VERR_INVALID_PARAMETER;
206}
207
208
209static int rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
210{
211 PCRTLOCKVALSRCPOS pSrcPos = NULL;
212
213 /*
214 * Validate input.
215 */
216 struct RTSEMEVENTINTERNAL *pThis = EventSem;
217 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
218 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
219
220 /*
221 * Quickly check whether it's signaled.
222 */
223 /** @todo this isn't fair if someone is already waiting on it. They should
224 * have the first go at it!
225 * (ASMAtomicReadS32(&pThis->cWaiters) == 0 || !cMillies) && ... */
226 if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
227 return VINF_SUCCESS;
228
229 /*
230 * Convert the timeout value.
231 */
232 struct timespec ts;
233 struct timespec *pTimeout = NULL;
234 uint64_t u64End = 0; /* shut up gcc */
235 if (cMillies != RT_INDEFINITE_WAIT)
236 {
237 if (!cMillies)
238 return VERR_TIMEOUT;
239 ts.tv_sec = cMillies / 1000;
240 ts.tv_nsec = (cMillies % 1000) * UINT32_C(1000000);
241 u64End = RTTimeSystemNanoTS() + cMillies * UINT64_C(1000000);
242 pTimeout = &ts;
243 }
244
245 ASMAtomicIncS32(&pThis->cWaiters);
246
247 /*
248 * The wait loop.
249 */
250#ifdef RTSEMEVENT_STRICT
251 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
252#else
253 RTTHREAD hThreadSelf = RTThreadSelf();
254#endif
255 int rc = VINF_SUCCESS;
256 for (;;)
257 {
258#ifdef RTSEMEVENT_STRICT
259 if (pThis->fEverHadSignallers)
260 {
261 rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
262 RTTHREADSTATE_EVENT, true);
263 if (RT_FAILURE(rc))
264 break;
265 }
266#endif
267 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
268 long lrc = sys_futex(&pThis->fSignalled, FUTEX_WAIT, 0, pTimeout, NULL, 0);
269 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
270 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
271 {
272 rc = VERR_SEM_DESTROYED;
273 break;
274 }
275
276 if (RT_LIKELY(lrc == 0 || lrc == -EWOULDBLOCK))
277 {
278 /* successful wakeup or fSignalled > 0 in the meantime */
279 if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
280 break;
281 }
282 else if (lrc == -ETIMEDOUT)
283 {
284 rc = VERR_TIMEOUT;
285 break;
286 }
287 else if (lrc == -EINTR)
288 {
289 if (!fAutoResume)
290 {
291 rc = VERR_INTERRUPTED;
292 break;
293 }
294 }
295 else
296 {
297 /* this shouldn't happen! */
298 AssertMsgFailed(("rc=%ld errno=%d\n", lrc, errno));
299 rc = RTErrConvertFromErrno(lrc);
300 break;
301 }
302 /* adjust the relative timeout */
303 if (pTimeout)
304 {
305 int64_t i64Diff = u64End - RTTimeSystemNanoTS();
306 if (i64Diff < 1000)
307 {
308 rc = VERR_TIMEOUT;
309 break;
310 }
311 ts.tv_sec = (uint64_t)i64Diff / UINT32_C(1000000000);
312 ts.tv_nsec = (uint64_t)i64Diff % UINT32_C(1000000000);
313 }
314 }
315
316 ASMAtomicDecS32(&pThis->cWaiters);
317 return rc;
318}
319
320
321RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, unsigned cMillies)
322{
323 int rc = rtSemEventWait(EventSem, cMillies, true);
324 Assert(rc != VERR_INTERRUPTED);
325 Assert(rc != VERR_TIMEOUT || cMillies != RT_INDEFINITE_WAIT);
326 return rc;
327}
328
329
330RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT EventSem, unsigned cMillies)
331{
332 return rtSemEventWait(EventSem, cMillies, false);
333}
334
335
336RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
337{
338#ifdef RTSEMEVENT_STRICT
339 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
340 AssertPtrReturnVoid(pThis);
341 AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
342
343 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
344 RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
345#endif
346}
347
348
349RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
350{
351#ifdef RTSEMEVENT_STRICT
352 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
353 AssertPtrReturnVoid(pThis);
354 AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
355
356 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
357 RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
358#endif
359}
360
361
362RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
363{
364#ifdef RTSEMEVENT_STRICT
365 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
366 AssertPtrReturnVoid(pThis);
367 AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
368
369 RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
370#endif
371}
372
373#endif /* glibc < 2.6 || IPRT_WITH_FUTEX_BASED_SEMS */
374
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