VirtualBox

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

Last change on this file since 40654 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: 12.1 KB
Line 
1/* $Id: semevent-linux.cpp 33540 2010-10-28 09:27:05Z vboxsync $ */
2/** @file
3 * IPRT - 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#include <features.h>
28#if __GLIBC_PREREQ(2,6) && !defined(IPRT_WITH_FUTEX_BASED_SEMS)
29
30/*
31 * glibc 2.6 fixed a serious bug in the mutex implementation. We wrote this
32 * linux specific event semaphores code in order to work around the bug. We
33 * will fall back on the pthread-based implementation if glibc is known to
34 * contain the bug fix.
35 *
36 * The external reference to epoll_pwait is a hack which prevents that we link
37 * against glibc < 2.6.
38 */
39#include "../posix/semevent-posix.cpp"
40asm volatile (".global epoll_pwait");
41
42#else /* glibc < 2.6 */
43
44/*******************************************************************************
45* Header Files *
46*******************************************************************************/
47#include <iprt/semaphore.h>
48#include "internal/iprt.h"
49
50#include <iprt/asm.h>
51#include <iprt/assert.h>
52#include <iprt/err.h>
53#include <iprt/lockvalidator.h>
54#include <iprt/mem.h>
55#include <iprt/time.h>
56#include "internal/magics.h"
57#include "internal/mem.h"
58#include "internal/strict.h"
59
60#include <errno.h>
61#include <limits.h>
62#include <pthread.h>
63#include <unistd.h>
64#include <sys/time.h>
65#include <sys/syscall.h>
66#if 0 /* With 2.6.17 futex.h has become C++ unfriendly. */
67# include <linux/futex.h>
68#else
69# define FUTEX_WAIT 0
70# define FUTEX_WAKE 1
71#endif
72
73
74/*******************************************************************************
75* Structures and Typedefs *
76*******************************************************************************/
77/**
78 * Linux (single wakup) event semaphore.
79 */
80struct RTSEMEVENTINTERNAL
81{
82 /** Magic value. */
83 intptr_t volatile iMagic;
84 /** The futex state variable.
85 * 0 means not signalled.
86 1 means signalled. */
87 uint32_t volatile fSignalled;
88 /** The number of waiting threads */
89 int32_t volatile cWaiters;
90#ifdef RTSEMEVENT_STRICT
91 /** Signallers. */
92 RTLOCKVALRECSHRD Signallers;
93 /** Indicates that lock validation should be performed. */
94 bool volatile fEverHadSignallers;
95#endif
96 /** The creation flags. */
97 uint32_t fFlags;
98};
99
100
101/**
102 * Wrapper for the futex syscall.
103 */
104static long sys_futex(uint32_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
117
118RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
119{
120 return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
121}
122
123
124RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
125{
126 AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
127 Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
128
129 /*
130 * Allocate semaphore handle.
131 */
132 struct RTSEMEVENTINTERNAL *pThis;
133 if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
134 pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
135 else
136 pThis = (struct RTSEMEVENTINTERNAL *)rtMemBaseAlloc(sizeof(struct RTSEMEVENTINTERNAL));
137 if (pThis)
138 {
139 pThis->iMagic = RTSEMEVENT_MAGIC;
140 pThis->cWaiters = 0;
141 pThis->fSignalled = 0;
142 pThis->fFlags = fFlags;
143#ifdef RTSEMEVENT_STRICT
144 if (!pszNameFmt)
145 {
146 static uint32_t volatile s_iSemEventAnon = 0;
147 RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
148 true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
149 "RTSemEvent-%u", ASMAtomicIncU32(&s_iSemEventAnon) - 1);
150 }
151 else
152 {
153 va_list va;
154 va_start(va, pszNameFmt);
155 RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
156 true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
157 pszNameFmt, va);
158 va_end(va);
159 }
160 pThis->fEverHadSignallers = false;
161#endif
162
163 *phEventSem = pThis;
164 return VINF_SUCCESS;
165 }
166 return VERR_NO_MEMORY;
167}
168
169
170RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
171{
172 /*
173 * Validate input.
174 */
175 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
176 if (pThis == NIL_RTSEMEVENT)
177 return VINF_SUCCESS;
178 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
179 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
180
181 /*
182 * Invalidate the semaphore and wake up anyone waiting on it.
183 */
184 ASMAtomicXchgSize(&pThis->iMagic, RTSEMEVENT_MAGIC | UINT32_C(0x80000000));
185 if (ASMAtomicXchgS32(&pThis->cWaiters, INT32_MIN / 2) > 0)
186 {
187 sys_futex(&pThis->fSignalled, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
188 usleep(1000);
189 }
190
191 /*
192 * Free the semaphore memory and be gone.
193 */
194#ifdef RTSEMEVENT_STRICT
195 RTLockValidatorRecSharedDelete(&pThis->Signallers);
196#endif
197 if (!(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
198 RTMemFree(pThis);
199 else
200 rtMemBaseFree(pThis);
201 return VINF_SUCCESS;
202}
203
204
205RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
206{
207 /*
208 * Validate input.
209 */
210 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
211 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
212 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
213
214#ifdef RTSEMEVENT_STRICT
215 if (pThis->fEverHadSignallers)
216 {
217 int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
218 if (RT_FAILURE(rc9))
219 return rc9;
220 }
221#endif
222
223 ASMAtomicWriteU32(&pThis->fSignalled, 1);
224 if (ASMAtomicReadS32(&pThis->cWaiters) < 1)
225 return VINF_SUCCESS;
226
227 /* somebody is waiting, try wake up one of them. */
228 long cWoken = sys_futex(&pThis->fSignalled, FUTEX_WAKE, 1, NULL, NULL, 0);
229 if (RT_LIKELY(cWoken >= 0))
230 return VINF_SUCCESS;
231
232 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
233 return VERR_SEM_DESTROYED;
234
235 return VERR_INVALID_PARAMETER;
236}
237
238
239static int rtSemEventWait(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies, bool fAutoResume)
240{
241 PCRTLOCKVALSRCPOS pSrcPos = NULL;
242
243 /*
244 * Validate input.
245 */
246 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
247 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
248 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
249
250 /*
251 * Quickly check whether it's signaled.
252 */
253 /** @todo this isn't fair if someone is already waiting on it. They should
254 * have the first go at it!
255 * (ASMAtomicReadS32(&pThis->cWaiters) == 0 || !cMillies) && ... */
256 if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
257 return VINF_SUCCESS;
258
259 /*
260 * Convert the timeout value.
261 */
262 struct timespec ts;
263 struct timespec *pTimeout = NULL;
264 uint64_t u64End = 0; /* shut up gcc */
265 if (cMillies != RT_INDEFINITE_WAIT)
266 {
267 if (!cMillies)
268 return VERR_TIMEOUT;
269 ts.tv_sec = cMillies / 1000;
270 ts.tv_nsec = (cMillies % 1000) * UINT32_C(1000000);
271 u64End = RTTimeSystemNanoTS() + cMillies * UINT64_C(1000000);
272 pTimeout = &ts;
273 }
274
275 ASMAtomicIncS32(&pThis->cWaiters);
276
277 /*
278 * The wait loop.
279 */
280#ifdef RTSEMEVENT_STRICT
281 RTTHREAD hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)
282 ? RTThreadSelfAutoAdopt()
283 : RTThreadSelf();
284#else
285 RTTHREAD hThreadSelf = RTThreadSelf();
286#endif
287 int rc = VINF_SUCCESS;
288 for (;;)
289 {
290#ifdef RTSEMEVENT_STRICT
291 if (pThis->fEverHadSignallers)
292 {
293 rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
294 cMillies, RTTHREADSTATE_EVENT, true);
295 if (RT_FAILURE(rc))
296 break;
297 }
298#endif
299 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
300 long lrc = sys_futex(&pThis->fSignalled, FUTEX_WAIT, 0, pTimeout, NULL, 0);
301 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
302 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
303 {
304 rc = VERR_SEM_DESTROYED;
305 break;
306 }
307
308 if (RT_LIKELY(lrc == 0 || lrc == -EWOULDBLOCK))
309 {
310 /* successful wakeup or fSignalled > 0 in the meantime */
311 if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
312 break;
313 }
314 else if (lrc == -ETIMEDOUT)
315 {
316 rc = VERR_TIMEOUT;
317 break;
318 }
319 else if (lrc == -EINTR)
320 {
321 if (!fAutoResume)
322 {
323 rc = VERR_INTERRUPTED;
324 break;
325 }
326 }
327 else
328 {
329 /* this shouldn't happen! */
330 AssertMsgFailed(("rc=%ld errno=%d\n", lrc, errno));
331 rc = RTErrConvertFromErrno(lrc);
332 break;
333 }
334 /* adjust the relative timeout */
335 if (pTimeout)
336 {
337 int64_t i64Diff = u64End - RTTimeSystemNanoTS();
338 if (i64Diff < 1000)
339 {
340 rc = VERR_TIMEOUT;
341 break;
342 }
343 ts.tv_sec = (uint64_t)i64Diff / UINT32_C(1000000000);
344 ts.tv_nsec = (uint64_t)i64Diff % UINT32_C(1000000000);
345 }
346 }
347
348 ASMAtomicDecS32(&pThis->cWaiters);
349 return rc;
350}
351
352
353RTDECL(int) RTSemEventWait(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
354{
355 int rc = rtSemEventWait(hEventSem, cMillies, true);
356 Assert(rc != VERR_INTERRUPTED);
357 Assert(rc != VERR_TIMEOUT || cMillies != RT_INDEFINITE_WAIT);
358 return rc;
359}
360
361
362RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
363{
364 return rtSemEventWait(hEventSem, cMillies, false);
365}
366
367
368RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
369{
370#ifdef RTSEMEVENT_STRICT
371 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
372 AssertPtrReturnVoid(pThis);
373 AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
374
375 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
376 RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
377#endif
378}
379
380
381RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
382{
383#ifdef RTSEMEVENT_STRICT
384 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
385 AssertPtrReturnVoid(pThis);
386 AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
387
388 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
389 RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
390#endif
391}
392
393
394RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
395{
396#ifdef RTSEMEVENT_STRICT
397 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
398 AssertPtrReturnVoid(pThis);
399 AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
400
401 RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
402#endif
403}
404
405#endif /* glibc < 2.6 || IPRT_WITH_FUTEX_BASED_SEMS */
406
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