VirtualBox

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

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

sem*-linux.cpp,Makefile.kmk: Added check for cMillies==0 in RTSemEvent and introduced a build config variable IPRT_WITH_FUTEX_BASED_SEMS. Corrected comment in semeventmulti about the futex variable values.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.9 KB
Line 
1/* $Id: semeventmulti-linux.cpp 22959 2009-09-11 13:45:44Z 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 <iprt/assert.h>
55#include <iprt/alloc.h>
56#include <iprt/asm.h>
57#include <iprt/err.h>
58#include <iprt/time.h>
59#include "internal/magics.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 multiple wakup event semaphore.
80 */
81struct RTSEMEVENTMULTIINTERNAL
82{
83 /** Magic value. */
84 intptr_t volatile iMagic;
85 /** The futex state variable.
86 * -1 means signaled.
87 * 0 means not signaled, no waiters.
88 * 1 means not signaled and that someone is waiting.
89 */
90 int32_t volatile iState;
91};
92
93
94/**
95 * Wrapper for the futex syscall.
96 */
97static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
98{
99 errno = 0;
100 long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
101 if (rc < 0)
102 {
103 Assert(rc == -1);
104 rc = -errno;
105 }
106 return rc;
107}
108
109
110RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI pEventMultiSem)
111{
112 /*
113 * Allocate semaphore handle.
114 */
115 struct RTSEMEVENTMULTIINTERNAL *pThis = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
116 if (pThis)
117 {
118 pThis->iMagic = RTSEMEVENTMULTI_MAGIC;
119 pThis->iState = 0;
120 *pEventMultiSem = pThis;
121 return VINF_SUCCESS;
122 }
123 return VERR_NO_MEMORY;
124}
125
126
127RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI EventMultiSem)
128{
129 /*
130 * Validate input.
131 */
132 struct RTSEMEVENTMULTIINTERNAL *pThis = EventMultiSem;
133 AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMEVENTMULTI_MAGIC,
134 VERR_INVALID_HANDLE);
135
136 /*
137 * Invalidate the semaphore and wake up anyone waiting on it.
138 */
139 ASMAtomicWriteSize(&pThis->iMagic, RTSEMEVENTMULTI_MAGIC + 1);
140 if (ASMAtomicXchgS32(&pThis->iState, -1) == 1)
141 {
142 sys_futex(&pThis->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
143 usleep(1000);
144 }
145
146 /*
147 * Free the semaphore memory and be gone.
148 */
149 RTMemFree(pThis);
150 return VINF_SUCCESS;
151}
152
153
154RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI EventMultiSem)
155{
156 /*
157 * Validate input.
158 */
159 struct RTSEMEVENTMULTIINTERNAL *pThis = EventMultiSem;
160 AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMEVENTMULTI_MAGIC,
161 VERR_INVALID_HANDLE);
162 /*
163 * Signal it.
164 */
165 int32_t iOld = ASMAtomicXchgS32(&pThis->iState, -1);
166 if (iOld > 0)
167 {
168 /* wake up sleeping threads. */
169 long cWoken = sys_futex(&pThis->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
170 AssertMsg(cWoken >= 0, ("%ld\n", cWoken)); NOREF(cWoken);
171 }
172 Assert(iOld == 0 || iOld == -1 || iOld == 1);
173 return VINF_SUCCESS;
174}
175
176
177RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI EventMultiSem)
178{
179 /*
180 * Validate input.
181 */
182 struct RTSEMEVENTMULTIINTERNAL *pThis = EventMultiSem;
183 AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMEVENTMULTI_MAGIC,
184 VERR_INVALID_HANDLE);
185#ifdef RT_STRICT
186 int32_t i = pThis->iState;
187 Assert(i == 0 || i == -1 || i == 1);
188#endif
189
190 /*
191 * Reset it.
192 */
193 ASMAtomicCmpXchgS32(&pThis->iState, 0, -1);
194 return VINF_SUCCESS;
195}
196
197
198static int rtSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies, bool fAutoResume)
199{
200 /*
201 * Validate input.
202 */
203 struct RTSEMEVENTMULTIINTERNAL *pThis = EventMultiSem;
204 AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMEVENTMULTI_MAGIC,
205 VERR_INVALID_HANDLE);
206
207 /*
208 * Quickly check whether it's signaled.
209 */
210 int32_t iCur = ASMAtomicUoReadS32(&pThis->iState);
211 Assert(iCur == 0 || iCur == -1 || iCur == 1);
212 if (iCur == -1)
213 return VINF_SUCCESS;
214
215 /*
216 * Convert the timeout value.
217 */
218 struct timespec ts;
219 struct timespec *pTimeout = NULL;
220 uint64_t u64End = 0; /* shut up gcc */
221 if (cMillies != RT_INDEFINITE_WAIT)
222 {
223 if (!cMillies)
224 return VERR_TIMEOUT;
225 ts.tv_sec = cMillies / 1000;
226 ts.tv_nsec = (cMillies % 1000) * 1000000;
227 u64End = RTTimeSystemNanoTS() + cMillies * 1000000;
228 pTimeout = &ts;
229 }
230
231 /*
232 * The wait loop.
233 */
234 for (unsigned i = 0;; i++)
235 {
236 /*
237 * Start waiting. We only account for there being or having been
238 * threads waiting on the semaphore to keep things simple.
239 */
240 iCur = ASMAtomicUoReadS32(&pThis->iState);
241 Assert(iCur == 0 || iCur == -1 || iCur == 1);
242 if ( iCur == 1
243 || ASMAtomicCmpXchgS32(&pThis->iState, 1, 0))
244 {
245 /* adjust the relative timeout */
246 if (pTimeout)
247 {
248 int64_t i64Diff = u64End - RTTimeSystemNanoTS();
249 if (i64Diff < 1000)
250 return VERR_TIMEOUT;
251 ts.tv_sec = i64Diff / 1000000000;
252 ts.tv_nsec = i64Diff % 1000000000;
253 }
254 long rc = sys_futex(&pThis->iState, FUTEX_WAIT, 1, pTimeout, NULL, 0);
255 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENTMULTI_MAGIC))
256 return VERR_SEM_DESTROYED;
257 if (rc == 0)
258 return VINF_SUCCESS;
259
260 /*
261 * Act on the wakup code.
262 */
263 if (rc == -ETIMEDOUT)
264 {
265/** @something is broken here. shows up every now and again in the ata code. Should try to run the timeout against RTTimeMilliTS to check that it's doing the right thing... */
266 Assert(pTimeout);
267 return VERR_TIMEOUT;
268 }
269 if (rc == -EWOULDBLOCK)
270 /* retry, the value changed. */;
271 else if (rc == -EINTR)
272 {
273 if (!fAutoResume)
274 return VERR_INTERRUPTED;
275 }
276 else
277 {
278 /* this shouldn't happen! */
279 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
280 return RTErrConvertFromErrno(rc);
281 }
282 }
283 else if (iCur == -1)
284 return VINF_SUCCESS;
285 }
286}
287
288
289RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
290{
291 int rc = rtSemEventMultiWait(EventMultiSem, cMillies, true);
292 Assert(rc != VERR_INTERRUPTED);
293 return rc;
294}
295
296
297RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
298{
299 return rtSemEventMultiWait(EventMultiSem, cMillies, false);
300}
301
302#endif /* glibc < 2.6 || IPRT_WITH_FUTEX_BASED_SEMS */
303
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