VirtualBox

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

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

Runtime/semevent: Fix part II for the Linux-specific RTSemEvent* implementation (adjusting the relative timeout)

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