VirtualBox

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