VirtualBox

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

Last change on this file since 62514 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

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