VirtualBox

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

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

iprt,pdmcritsect: Added RTSemEvent[Set|Add|Remove]Signaller so that we can validate who is signalling an event if we like and, more importantly, detect deadlocks involving event semaphores. More attempts at dealing with the races (and bugs) in the all-other-threads-blocking detection in tstRTLockValidator.cpp, adding RTThreadGetReallySleeping and RTThreadGetNativeState in the process.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.8 KB
Line 
1/* $Id: semevent-linux.cpp 25638 2010-01-04 16:08:04Z vboxsync $ */
2/** @file
3 * IPRT - Event Semaphore, Linux (2.6.x+).
4 */
5
6/*
7 * Copyright (C) 2006-2010 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#include <features.h>
32#if __GLIBC_PREREQ(2,6) && !defined(IPRT_WITH_FUTEX_BASED_SEMS) && !defined(DEBUG_bird) //// testing 1 2 3
33
34/*
35 * glibc 2.6 fixed a serious bug in the mutex implementation. We wrote this
36 * linux specific event semaphores code in order to work around the bug. We
37 * will fall back on the pthread-based implementation if glibc is known to
38 * contain the bug fix.
39 *
40 * The external refernce to epoll_pwait is a hack which prevents that we link
41 * against glibc < 2.6.
42 */
43#include "../posix/semevent-posix.cpp"
44asm volatile (".global epoll_pwait");
45
46#else /* glibc < 2.6 */
47
48/*******************************************************************************
49* Header Files *
50*******************************************************************************/
51#include <iprt/semaphore.h>
52#include "internal/iprt.h"
53
54#include <iprt/asm.h>
55#include <iprt/assert.h>
56#include <iprt/err.h>
57#include <iprt/lockvalidator.h>
58#include <iprt/mem.h>
59#include <iprt/time.h>
60#include "internal/magics.h"
61#include "internal/strict.h"
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 (single wakup) event semaphore.
82 */
83struct RTSEMEVENTINTERNAL
84{
85 /** Magic value. */
86 intptr_t volatile iMagic;
87 /** The futex state variable.
88 * 0 means not signalled.
89 1 means signalled. */
90 uint32_t volatile fSignalled;
91 /** The number of waiting threads */
92 int32_t volatile cWaiters;
93#ifdef RTSEMEVENT_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(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 pEventSem)
120{
121 /*
122 * Allocate semaphore handle.
123 */
124 struct RTSEMEVENTINTERNAL *pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
125 if (pThis)
126 {
127 pThis->iMagic = RTSEMEVENT_MAGIC;
128 pThis->cWaiters = 0;
129 pThis->fSignalled = 0;
130#ifdef RTSEMEVENT_STRICT
131 RTLockValidatorRecSharedInit(&pThis->Signallers,
132 NIL_RTLOCKVALIDATORCLASS, RTLOCKVALIDATOR_SUB_CLASS_ANY,
133 "RTSemEvent", pThis, true /*fSignaller*/);
134 pThis->fEverHadSignallers = false;
135#endif
136 *pEventSem = pThis;
137 return VINF_SUCCESS;
138 }
139 return VERR_NO_MEMORY;
140}
141
142
143RTDECL(int) RTSemEventDestroy(RTSEMEVENT EventSem)
144{
145 /*
146 * Validate input.
147 */
148 if (EventSem == NIL_RTSEMEVENT) /* don't bitch */
149 return VERR_INVALID_HANDLE;
150 struct RTSEMEVENTINTERNAL *pThis = EventSem;
151 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
152 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
153
154 /*
155 * Invalidate the semaphore and wake up anyone waiting on it.
156 */
157 ASMAtomicXchgSize(&pThis->iMagic, RTSEMEVENT_MAGIC | UINT32_C(0x80000000));
158 if (ASMAtomicXchgS32(&pThis->cWaiters, INT32_MIN / 2) > 0)
159 {
160 sys_futex(&pThis->fSignalled, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
161 usleep(1000);
162 }
163
164 /*
165 * Free the semaphore memory and be gone.
166 */
167#ifdef RTSEMEVENT_STRICT
168 RTLockValidatorRecSharedDelete(&pThis->Signallers);
169#endif
170 RTMemFree(pThis);
171 return VINF_SUCCESS;
172}
173
174
175RTDECL(int) RTSemEventSignal(RTSEMEVENT EventSem)
176{
177 /*
178 * Validate input.
179 */
180 struct RTSEMEVENTINTERNAL *pThis = EventSem;
181 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
182 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
183
184#ifdef RTSEMEVENT_STRICT
185 if (pThis->fEverHadSignallers)
186 {
187 int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
188 if (RT_FAILURE(rc9))
189 return rc9;
190 }
191#endif
192
193 ASMAtomicWriteU32(&pThis->fSignalled, 1);
194 if (ASMAtomicReadS32(&pThis->cWaiters) < 1)
195 return VINF_SUCCESS;
196
197 /* somebody is waiting, try wake up one of them. */
198 long cWoken = sys_futex(&pThis->fSignalled, FUTEX_WAKE, 1, NULL, NULL, 0);
199 if (RT_LIKELY(cWoken >= 0))
200 return VINF_SUCCESS;
201
202 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
203 return VERR_SEM_DESTROYED;
204
205 return VERR_INVALID_PARAMETER;
206}
207
208
209static int rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
210{
211 PCRTLOCKVALSRCPOS pSrcPos = NULL;
212
213 /*
214 * Validate input.
215 */
216 struct RTSEMEVENTINTERNAL *pThis = EventSem;
217 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
218 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
219
220 /*
221 * Quickly check whether it's signaled.
222 */
223 /** @todo this isn't fair if someone is already waiting on it. They should
224 * have the first go at it!
225 * (ASMAtomicReadS32(&pThis->cWaiters) == 0 || !cMillies) && ... */
226 if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
227 return VINF_SUCCESS;
228
229 /*
230 * Convert the timeout value.
231 */
232 struct timespec ts;
233 struct timespec *pTimeout = NULL;
234 uint64_t u64End = 0; /* shut up gcc */
235 if (cMillies != RT_INDEFINITE_WAIT)
236 {
237 if (!cMillies)
238 return VERR_TIMEOUT;
239 ts.tv_sec = cMillies / 1000;
240 ts.tv_nsec = (cMillies % 1000) * 1000000;
241 u64End = RTTimeSystemNanoTS() + cMillies * 1000000;
242 pTimeout = &ts;
243 }
244
245 ASMAtomicIncS32(&pThis->cWaiters);
246
247 /*
248 * The wait loop.
249 */
250#ifdef RTSEMEVENT_STRICT
251 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
252#else
253 RTTHREAD hThreadSelf = RTThreadSelf();
254#endif
255 int rc = VINF_SUCCESS;
256 for (;;)
257 {
258#ifdef RTSEMEVENT_STRICT
259 if (pThis->fEverHadSignallers)
260 {
261 rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
262 RTTHREADSTATE_EVENT, true);
263 if (RT_FAILURE(rc))
264 break;
265 }
266#endif
267 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
268 long lrc = sys_futex(&pThis->fSignalled, FUTEX_WAIT, 0, pTimeout, NULL, 0);
269 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
270 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
271 {
272 rc = VERR_SEM_DESTROYED;
273 break;
274 }
275
276 if (RT_LIKELY(lrc == 0 || lrc == -EWOULDBLOCK))
277 {
278 /* successful wakeup or fSignalled > 0 in the meantime */
279 if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
280 break;
281 }
282 else if (lrc == -ETIMEDOUT)
283 {
284 rc = VERR_TIMEOUT;
285 break;
286 }
287 else if (lrc == -EINTR)
288 {
289 if (!fAutoResume)
290 {
291 rc = VERR_INTERRUPTED;
292 break;
293 }
294 }
295 else
296 {
297 /* this shouldn't happen! */
298 AssertMsgFailed(("rc=%ld errno=%d\n", lrc, errno));
299 rc = RTErrConvertFromErrno(lrc);
300 break;
301 }
302 /* adjust the relative timeout */
303 if (pTimeout)
304 {
305 int64_t i64Diff = u64End - RTTimeSystemNanoTS();
306 if (i64Diff < 1000)
307 {
308 rc = VERR_TIMEOUT;
309 break;
310 }
311 ts.tv_sec = i64Diff / 1000000000;
312 ts.tv_nsec = i64Diff % 1000000000;
313 }
314 }
315
316 ASMAtomicDecS32(&pThis->cWaiters);
317 return rc;
318}
319
320
321RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, unsigned cMillies)
322{
323 int rc = rtSemEventWait(EventSem, cMillies, true);
324 Assert(rc != VERR_INTERRUPTED);
325 Assert(rc != VERR_TIMEOUT || cMillies != RT_INDEFINITE_WAIT);
326 return rc;
327}
328
329
330RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT EventSem, unsigned cMillies)
331{
332 return rtSemEventWait(EventSem, cMillies, false);
333}
334
335
336RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
337{
338#ifdef RTSEMEVENT_STRICT
339 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
340 AssertPtrReturnVoid(pThis);
341 AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
342
343 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
344 RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
345#endif
346}
347
348
349RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
350{
351#ifdef RTSEMEVENT_STRICT
352 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
353 AssertPtrReturnVoid(pThis);
354 AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
355
356 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
357 RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
358#endif
359}
360
361
362RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
363{
364#ifdef RTSEMEVENT_STRICT
365 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
366 AssertPtrReturnVoid(pThis);
367 AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
368
369 RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
370#endif
371}
372
373#endif /* glibc < 2.6 || IPRT_WITH_FUTEX_BASED_SEMS */
374
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