VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/semevent-posix.cpp@ 25659

Last change on this file since 25659 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: 16.4 KB
Line 
1/* $Id: semevent-posix.cpp 25638 2010-01-04 16:08:04Z vboxsync $ */
2/** @file
3 * IPRT - Event Semaphore, POSIX.
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/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#include <iprt/semaphore.h>
35#include "internal/iprt.h"
36
37#include <iprt/asm.h>
38#include <iprt/assert.h>
39#include <iprt/err.h>
40#include <iprt/mem.h>
41#include <iprt/lockvalidator.h>
42
43#include "internal/strict.h"
44
45#include <errno.h>
46#include <pthread.h>
47#include <unistd.h>
48#include <sys/time.h>
49
50#ifdef RT_OS_DARWIN
51# define pthread_yield() pthread_yield_np()
52#endif
53
54#ifdef RT_OS_SOLARIS
55# include <sched.h>
56# define pthread_yield() sched_yield()
57#endif
58
59
60/*******************************************************************************
61* Structures and Typedefs *
62*******************************************************************************/
63
64/** Internal representation of the POSIX implementation of an Event semaphore.
65 * The POSIX implementation uses a mutex and a condition variable to implement
66 * the automatic reset event semaphore semantics.
67 */
68struct RTSEMEVENTINTERNAL
69{
70 /** pthread condition. */
71 pthread_cond_t Cond;
72 /** pthread mutex which protects the condition and the event state. */
73 pthread_mutex_t Mutex;
74 /** The state of the semaphore.
75 * This is operated while owning mutex and using atomic updating. */
76 volatile uint32_t u32State;
77 /** Number of waiters. */
78 volatile uint32_t cWaiters;
79#ifdef RTSEMEVENT_STRICT
80 /** Signallers. */
81 RTLOCKVALRECSHRD Signallers;
82 /** Indicates that lock validation should be performed. */
83 bool volatile fEverHadSignallers;
84#endif
85};
86
87/** The valus of the u32State variable in a RTSEMEVENTINTERNAL.
88 * @{ */
89/** The object isn't initialized. */
90#define EVENT_STATE_UNINITIALIZED 0
91/** The semaphore is signaled. */
92#define EVENT_STATE_SIGNALED 0xff00ff00
93/** The semaphore is not signaled. */
94#define EVENT_STATE_NOT_SIGNALED 0x00ff00ff
95/** @} */
96
97
98RTDECL(int) RTSemEventCreate(PRTSEMEVENT pEventSem)
99{
100 int rc;
101
102 /*
103 * Allocate semaphore handle.
104 */
105 struct RTSEMEVENTINTERNAL *pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
106 if (pThis)
107 {
108 /*
109 * Create the condition variable.
110 */
111 pthread_condattr_t CondAttr;
112 rc = pthread_condattr_init(&CondAttr);
113 if (!rc)
114 {
115 rc = pthread_cond_init(&pThis->Cond, &CondAttr);
116 if (!rc)
117 {
118 /*
119 * Create the semaphore.
120 */
121 pthread_mutexattr_t MutexAttr;
122 rc = pthread_mutexattr_init(&MutexAttr);
123 if (!rc)
124 {
125 rc = pthread_mutex_init(&pThis->Mutex, &MutexAttr);
126 if (!rc)
127 {
128 pthread_mutexattr_destroy(&MutexAttr);
129 pthread_condattr_destroy(&CondAttr);
130
131 ASMAtomicXchgU32(&pThis->u32State, EVENT_STATE_NOT_SIGNALED);
132 ASMAtomicXchgU32(&pThis->cWaiters, 0);
133#ifdef RTSEMEVENT_STRICT
134 RTLockValidatorRecSharedInit(&pThis->Signallers,
135 NIL_RTLOCKVALIDATORCLASS, RTLOCKVALIDATOR_SUB_CLASS_ANY,
136 "RTSemEvent", pThis, true /*fSignaller*/);
137 pThis->fEverHadSignallers = false;
138#endif
139
140 *pEventSem = pThis;
141 return VINF_SUCCESS;
142 }
143 pthread_mutexattr_destroy(&MutexAttr);
144 }
145 pthread_cond_destroy(&pThis->Cond);
146 }
147 pthread_condattr_destroy(&CondAttr);
148 }
149
150 rc = RTErrConvertFromErrno(rc);
151 RTMemFree(pThis);
152 }
153 else
154 rc = VERR_NO_MEMORY;
155
156 return rc;
157}
158
159
160RTDECL(int) RTSemEventDestroy(RTSEMEVENT EventSem)
161{
162 /*
163 * Validate handle.
164 */
165 struct RTSEMEVENTINTERNAL *pThis = EventSem;
166 if (pThis == NIL_RTSEMEVENT) /* don't bitch */
167 return VERR_INVALID_HANDLE;
168 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
169 uint32_t u32 = pThis->u32State;
170 AssertReturn(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED, VERR_INVALID_HANDLE);
171
172 /*
173 * Abort all waiters forcing them to return failure.
174 */
175 int rc;
176 for (int i = 30; i > 0; i--)
177 {
178 ASMAtomicXchgU32(&pThis->u32State, EVENT_STATE_UNINITIALIZED);
179 rc = pthread_cond_destroy(&pThis->Cond);
180 if (rc != EBUSY)
181 break;
182 pthread_cond_broadcast(&pThis->Cond);
183 usleep(1000);
184 }
185 if (rc)
186 {
187 AssertMsgFailed(("Failed to destroy event sem %p, rc=%d.\n", EventSem, rc));
188 return RTErrConvertFromErrno(rc);
189 }
190
191 /*
192 * Destroy the semaphore
193 * If it's busy we'll wait a bit to give the threads a chance to be scheduled.
194 */
195 for (int i = 30; i > 0; i--)
196 {
197 rc = pthread_mutex_destroy(&pThis->Mutex);
198 if (rc != EBUSY)
199 break;
200 usleep(1000);
201 }
202 if (rc)
203 {
204 AssertMsgFailed(("Failed to destroy event sem %p, rc=%d. (mutex)\n", EventSem, rc));
205 return RTErrConvertFromErrno(rc);
206 }
207
208 /*
209 * Free the semaphore memory and be gone.
210 */
211#ifdef RTSEMEVENT_STRICT
212 RTLockValidatorRecSharedDelete(&pThis->Signallers);
213#endif
214 RTMemFree(pThis);
215 return VINF_SUCCESS;
216}
217
218
219RTDECL(int) RTSemEventSignal(RTSEMEVENT EventSem)
220{
221 /*
222 * Validate input.
223 */
224 struct RTSEMEVENTINTERNAL *pThis = EventSem;
225 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
226 uint32_t u32 = pThis->u32State;
227 AssertReturn(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED, VERR_INVALID_HANDLE);
228
229#ifdef RTSEMEVENT_STRICT
230 if (pThis->fEverHadSignallers)
231 {
232 int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
233 if (RT_FAILURE(rc9))
234 return rc9;
235 }
236#endif
237
238 /*
239 * Lock the mutex semaphore.
240 */
241 int rc = pthread_mutex_lock(&pThis->Mutex);
242 if (rc)
243 {
244 AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", EventSem, rc));
245 return RTErrConvertFromErrno(rc);
246 }
247
248 /*
249 * Check the state.
250 */
251 if (pThis->u32State == EVENT_STATE_NOT_SIGNALED)
252 {
253 ASMAtomicXchgU32(&pThis->u32State, EVENT_STATE_SIGNALED);
254 rc = pthread_cond_signal(&pThis->Cond);
255 AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d.\n", EventSem, rc));
256 }
257 else if (pThis->u32State == EVENT_STATE_SIGNALED)
258 {
259 rc = pthread_cond_signal(&pThis->Cond); /* give'm another kick... */
260 AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d. (2)\n", EventSem, rc));
261 }
262 else
263 rc = VERR_SEM_DESTROYED;
264
265 /*
266 * Release the mutex and return.
267 */
268 int rc2 = pthread_mutex_unlock(&pThis->Mutex);
269 AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc));
270 if (rc)
271 return RTErrConvertFromErrno(rc);
272 if (rc2)
273 return RTErrConvertFromErrno(rc2);
274
275 return VINF_SUCCESS;
276}
277
278
279DECL_FORCE_INLINE(int) rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
280{
281 PCRTLOCKVALSRCPOS pSrcPos = NULL;
282
283 /*
284 * Validate input.
285 */
286 struct RTSEMEVENTINTERNAL *pThis = EventSem;
287 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
288 uint32_t u32 = pThis->u32State;
289 AssertReturn(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED, VERR_INVALID_HANDLE);
290
291 /*
292 * Timed or indefinite wait?
293 */
294 if (cMillies == RT_INDEFINITE_WAIT)
295 {
296 /* for fairness, yield before going to sleep. */
297 if ( ASMAtomicIncU32(&pThis->cWaiters) > 1
298 && pThis->u32State == EVENT_STATE_SIGNALED)
299 pthread_yield();
300
301 /* take mutex */
302 int rc = pthread_mutex_lock(&pThis->Mutex);
303 if (rc)
304 {
305 ASMAtomicDecU32(&pThis->cWaiters);
306 AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", EventSem, rc));
307 return RTErrConvertFromErrno(rc);
308 }
309
310 for (;;)
311 {
312 /* check state. */
313 if (pThis->u32State == EVENT_STATE_SIGNALED)
314 {
315 ASMAtomicXchgU32(&pThis->u32State, EVENT_STATE_NOT_SIGNALED);
316 ASMAtomicDecU32(&pThis->cWaiters);
317 rc = pthread_mutex_unlock(&pThis->Mutex);
318 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
319 return VINF_SUCCESS;
320 }
321 if (pThis->u32State == EVENT_STATE_UNINITIALIZED)
322 {
323 rc = pthread_mutex_unlock(&pThis->Mutex);
324 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
325 return VERR_SEM_DESTROYED;
326 }
327
328 /* wait */
329#ifdef RTSEMEVENT_STRICT
330 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
331 if (pThis->fEverHadSignallers)
332 {
333 rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
334 RTTHREADSTATE_EVENT, true);
335 if (RT_FAILURE(rc))
336 {
337 ASMAtomicDecU32(&pThis->cWaiters);
338 pthread_mutex_unlock(&pThis->Mutex);
339 return rc;
340 }
341 }
342#else
343 RTTHREAD hThreadSelf = RTThreadSelf();
344#endif
345 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
346 rc = pthread_cond_wait(&pThis->Cond, &pThis->Mutex);
347 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
348 if (rc)
349 {
350 AssertMsgFailed(("Failed to wait on event sem %p, rc=%d.\n", EventSem, rc));
351 ASMAtomicDecU32(&pThis->cWaiters);
352 int rc2 = pthread_mutex_unlock(&pThis->Mutex);
353 AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc2)); NOREF(rc2);
354 return RTErrConvertFromErrno(rc);
355 }
356 }
357 }
358 else
359 {
360 /*
361 * Get current time and calc end of wait time.
362 */
363 struct timespec ts = {0,0};
364#ifdef RT_OS_DARWIN
365 struct timeval tv = {0,0};
366 gettimeofday(&tv, NULL);
367 ts.tv_sec = tv.tv_sec;
368 ts.tv_nsec = tv.tv_usec * 1000;
369#else
370 clock_gettime(CLOCK_REALTIME, &ts);
371#endif
372 if (cMillies != 0)
373 {
374 ts.tv_nsec += (cMillies % 1000) * 1000000;
375 ts.tv_sec += cMillies / 1000;
376 if (ts.tv_nsec >= 1000000000)
377 {
378 ts.tv_nsec -= 1000000000;
379 ts.tv_sec++;
380 }
381 }
382
383 /* for fairness, yield before going to sleep. */
384 if (ASMAtomicIncU32(&pThis->cWaiters) > 1 && cMillies)
385 pthread_yield();
386
387 /* take mutex */
388 int rc = pthread_mutex_lock(&pThis->Mutex);
389 if (rc)
390 {
391 ASMAtomicDecU32(&pThis->cWaiters);
392 AssertMsg(rc == ETIMEDOUT, ("Failed to lock event sem %p, rc=%d.\n", EventSem, rc));
393 return RTErrConvertFromErrno(rc);
394 }
395
396 for (;;)
397 {
398 /* check state. */
399 if (pThis->u32State == EVENT_STATE_SIGNALED)
400 {
401 ASMAtomicXchgU32(&pThis->u32State, EVENT_STATE_NOT_SIGNALED);
402 ASMAtomicDecU32(&pThis->cWaiters);
403 rc = pthread_mutex_unlock(&pThis->Mutex);
404 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
405 return VINF_SUCCESS;
406 }
407 if (pThis->u32State == EVENT_STATE_UNINITIALIZED)
408 {
409 rc = pthread_mutex_unlock(&pThis->Mutex);
410 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
411 return VERR_SEM_DESTROYED;
412 }
413
414 /* we're done if the timeout is 0. */
415 if (!cMillies)
416 {
417 ASMAtomicDecU32(&pThis->cWaiters);
418 rc = pthread_mutex_unlock(&pThis->Mutex);
419 return VERR_SEM_BUSY;
420 }
421
422 /* wait */
423#ifdef RTSEMEVENT_STRICT
424 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
425 if (pThis->fEverHadSignallers)
426 {
427 rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
428 RTTHREADSTATE_EVENT, true);
429 if (RT_FAILURE(rc))
430 {
431 ASMAtomicDecU32(&pThis->cWaiters);
432 pthread_mutex_unlock(&pThis->Mutex);
433 return rc;
434 }
435 }
436#else
437 RTTHREAD hThreadSelf = RTThreadSelf();
438#endif
439 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
440 rc = pthread_cond_timedwait(&pThis->Cond, &pThis->Mutex, &ts);
441 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
442 if (rc && (rc != EINTR || !fAutoResume)) /* according to SuS this function shall not return EINTR, but linux man page says differently. */
443 {
444 AssertMsg(rc == ETIMEDOUT, ("Failed to wait on event sem %p, rc=%d.\n", EventSem, rc));
445 ASMAtomicDecU32(&pThis->cWaiters);
446 int rc2 = pthread_mutex_unlock(&pThis->Mutex);
447 AssertMsg(!rc2, ("Failed to unlock event sem %p, rc2=%d.\n", EventSem, rc2)); NOREF(rc2);
448 return RTErrConvertFromErrno(rc);
449 }
450 } /* for (;;) */
451 }
452}
453
454
455RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, unsigned cMillies)
456{
457 int rc = rtSemEventWait(EventSem, cMillies, true);
458 Assert(rc != VERR_INTERRUPTED);
459 return rc;
460}
461
462
463RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT EventSem, unsigned cMillies)
464{
465 return rtSemEventWait(EventSem, cMillies, false);
466}
467
468
469RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
470{
471#ifdef RTSEMEVENT_STRICT
472 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
473 AssertPtrReturnVoid(pThis);
474 uint32_t u32 = pThis->u32State;
475 AssertReturnVoid(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED);
476
477 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
478 RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
479#endif
480}
481
482
483RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
484{
485#ifdef RTSEMEVENT_STRICT
486 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
487 AssertPtrReturnVoid(pThis);
488 uint32_t u32 = pThis->u32State;
489 AssertReturnVoid(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED);
490
491 ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
492 RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
493#endif
494}
495
496
497RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
498{
499#ifdef RTSEMEVENT_STRICT
500 struct RTSEMEVENTINTERNAL *pThis = hEventSem;
501 AssertPtrReturnVoid(pThis);
502 uint32_t u32 = pThis->u32State;
503 AssertReturnVoid(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED);
504
505 RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
506#endif
507}
508
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