VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/semmutex-linux.cpp@ 25648

Last change on this file since 25648 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: 12.2 KB
Line 
1/* $Id: semmutex-linux.cpp 25638 2010-01-04 16:08:04Z vboxsync $ */
2/** @file
3 * IPRT - Mutex 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* Header Files *
33*******************************************************************************/
34#include <iprt/semaphore.h>
35#include "internal/iprt.h"
36
37#include <iprt/alloc.h>
38#include <iprt/asm.h>
39#include <iprt/assert.h>
40#include <iprt/err.h>
41#include <iprt/lockvalidator.h>
42#include <iprt/thread.h>
43#include <iprt/time.h>
44#include "internal/magics.h"
45#include "internal/strict.h"
46
47#include <errno.h>
48#include <limits.h>
49#include <pthread.h>
50#include <unistd.h>
51#include <sys/time.h>
52#include <sys/syscall.h>
53#if 0 /* With 2.6.17 futex.h has become C++ unfriendly. */
54# include <linux/futex.h>
55#else
56# define FUTEX_WAIT 0
57# define FUTEX_WAKE 1
58#endif
59
60
61/*******************************************************************************
62* Structures and Typedefs *
63*******************************************************************************/
64/**
65 * Linux internal representation of a Mutex semaphore.
66 */
67struct RTSEMMUTEXINTERNAL
68{
69 /** The futex state variable.
70 * 0 means unlocked.
71 * 1 means locked, no waiters.
72 * 2 means locked, one or more waiters.
73 */
74 int32_t volatile iState;
75 /** Nesting count. */
76 uint32_t volatile cNesting;
77 /** The owner of the mutex. */
78 pthread_t volatile Owner;
79 /** Magic value (RTSEMMUTEX_MAGIC). */
80 uint32_t volatile u32Magic;
81#ifdef RTSEMMUTEX_STRICT
82 /** Lock validator record associated with this mutex. */
83 RTLOCKVALRECEXCL ValidatorRec;
84#endif
85};
86
87
88/* Undefine debug mappings. */
89#undef RTSemMutexRequest
90#undef RTSemMutexRequestNoResume
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) RTSemMutexCreate(PRTSEMMUTEX pMutexSem)
110{
111 /*
112 * Allocate semaphore handle.
113 */
114 struct RTSEMMUTEXINTERNAL *pThis = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
115 if (pThis)
116 {
117 pThis->u32Magic = RTSEMMUTEX_MAGIC;
118 pThis->iState = 0;
119 pThis->Owner = (pthread_t)~0;
120 pThis->cNesting = 0;
121#ifdef RTSEMMUTEX_STRICT
122 RTLockValidatorRecExclInit(&pThis->ValidatorRec, NIL_RTLOCKVALIDATORCLASS, RTLOCKVALIDATOR_SUB_CLASS_NONE, "RTSemMutex", pThis);
123#endif
124
125 *pMutexSem = pThis;
126 return VINF_SUCCESS;
127 }
128
129 return VERR_NO_MEMORY;
130}
131
132
133RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX MutexSem)
134{
135 /*
136 * Validate input.
137 */
138 if (MutexSem == NIL_RTSEMMUTEX)
139 return VINF_SUCCESS;
140 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
141 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
142 AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC,
143 ("MutexSem=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
144 VERR_INVALID_HANDLE);
145
146 /*
147 * Invalidate the semaphore and wake up anyone waiting on it.
148 */
149 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD);
150 if (ASMAtomicXchgS32(&pThis->iState, 0) > 0)
151 {
152 sys_futex(&pThis->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
153 usleep(1000);
154 }
155 pThis->Owner = (pthread_t)~0;
156 pThis->cNesting = 0;
157#ifdef RTSEMMUTEX_STRICT
158 RTLockValidatorRecExclDelete(&pThis->ValidatorRec);
159#endif
160
161 /*
162 * Free the semaphore memory and be gone.
163 */
164 RTMemFree(pThis);
165 return VINF_SUCCESS;
166}
167
168
169DECL_FORCE_INLINE(int) rtSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies, bool fAutoResume, PCRTLOCKVALSRCPOS pSrcPos)
170{
171 /*
172 * Validate input.
173 */
174 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
175 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
176 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
177
178 /*
179 * Check if nested request.
180 */
181 pthread_t Self = pthread_self();
182 if ( pThis->Owner == Self
183 && pThis->cNesting > 0)
184 {
185#ifdef RTSEMMUTEX_STRICT
186 int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorRec, pSrcPos);
187 if (RT_FAILURE(rc9))
188 return rc9;
189#endif
190 ASMAtomicIncU32(&pThis->cNesting);
191 return VINF_SUCCESS;
192 }
193
194#ifdef RTSEMMUTEX_STRICT
195 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
196 if (cMillies)
197 {
198 int rc9 = RTLockValidatorRecExclCheckOrder(&pThis->ValidatorRec, hThreadSelf, pSrcPos);
199 if (RT_FAILURE(rc9))
200 return rc9;
201 }
202#else
203 RTTHREAD hThreadSelf = RTThreadSelf();
204#endif
205
206 /*
207 * Convert timeout value.
208 */
209 struct timespec ts;
210 struct timespec *pTimeout = NULL;
211 uint64_t u64End = 0; /* shut up gcc */
212 if (cMillies != RT_INDEFINITE_WAIT)
213 {
214 ts.tv_sec = cMillies / 1000;
215 ts.tv_nsec = (cMillies % 1000) * 1000000;
216 u64End = RTTimeSystemNanoTS() + cMillies * 1000000;
217 pTimeout = &ts;
218 }
219
220 /*
221 * Lock the mutex.
222 * Optimize for the uncontended case (makes 1-2 ns difference).
223 */
224 if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&pThis->iState, 1, 0)))
225 {
226 for (;;)
227 {
228 int32_t iOld = ASMAtomicXchgS32(&pThis->iState, 2);
229
230 /*
231 * Was the lock released in the meantime? This is unlikely (but possible)
232 */
233 if (RT_UNLIKELY(iOld == 0))
234 break;
235
236 /*
237 * Go to sleep.
238 */
239 if (pTimeout && ( pTimeout->tv_sec || pTimeout->tv_nsec ))
240 {
241#ifdef RTSEMMUTEX_STRICT
242 int rc9 = RTLockValidatorRecExclCheckBlocking(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true,
243 RTTHREADSTATE_MUTEX, true);
244 if (RT_FAILURE(rc9))
245 return rc9;
246#else
247 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_MUTEX, true);
248#endif
249 }
250
251 long rc = sys_futex(&pThis->iState, FUTEX_WAIT, 2, pTimeout, NULL, 0);
252
253 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
254 if (RT_UNLIKELY(pThis->u32Magic != RTSEMMUTEX_MAGIC))
255 return VERR_SEM_DESTROYED;
256
257 /*
258 * Act on the wakup code.
259 */
260 if (rc == -ETIMEDOUT)
261 {
262 Assert(pTimeout);
263 return VERR_TIMEOUT;
264 }
265 if (rc == 0)
266 /* we'll leave the loop now unless another thread is faster */;
267 else if (rc == -EWOULDBLOCK)
268 /* retry with new value. */;
269 else if (rc == -EINTR)
270 {
271 if (!fAutoResume)
272 return VERR_INTERRUPTED;
273 }
274 else
275 {
276 /* this shouldn't happen! */
277 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
278 return RTErrConvertFromErrno(rc);
279 }
280
281 /* adjust the relative timeout */
282 if (pTimeout)
283 {
284 int64_t i64Diff = u64End - RTTimeSystemNanoTS();
285 if (i64Diff < 1000)
286 {
287 rc = VERR_TIMEOUT;
288 break;
289 }
290 ts.tv_sec = i64Diff / 1000000000;
291 ts.tv_nsec = i64Diff % 1000000000;
292 }
293 }
294
295 /*
296 * When leaving this loop, iState is set to 2. This means that we gained the
297 * lock and there are _possibly_ some waiters. We don't know exactly as another
298 * thread might entered this loop at nearly the same time. Therefore we will
299 * call futex_wakeup once too often (if _no_ other thread entered this loop).
300 * The key problem is the simple futex_wait test for x != y (iState != 2) in
301 * our case).
302 */
303 }
304
305 /*
306 * Set the owner and nesting.
307 */
308 pThis->Owner = Self;
309 ASMAtomicWriteU32(&pThis->cNesting, 1);
310#ifdef RTSEMMUTEX_STRICT
311 RTLockValidatorRecExclSetOwner(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true);
312#endif
313 return VINF_SUCCESS;
314}
315
316
317RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies)
318{
319#ifndef RTSEMMUTEX_STRICT
320 int rc = rtSemMutexRequest(MutexSem, cMillies, true, NULL);
321#else
322 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
323 int rc = rtSemMutexRequest(MutexSem, cMillies, true, &SrcPos);
324#endif
325 Assert(rc != VERR_INTERRUPTED);
326 return rc;
327}
328
329
330RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX MutexSem, unsigned cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
331{
332 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
333 int rc = rtSemMutexRequest(MutexSem, cMillies, true, &SrcPos);
334 Assert(rc != VERR_INTERRUPTED);
335 return rc;
336}
337
338
339RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies)
340{
341#ifndef RTSEMMUTEX_STRICT
342 return rtSemMutexRequest(MutexSem, cMillies, false, NULL);
343#else
344 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
345 return rtSemMutexRequest(MutexSem, cMillies, false, &SrcPos);
346#endif
347}
348
349
350RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX MutexSem, unsigned cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
351{
352 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
353 return rtSemMutexRequest(MutexSem, cMillies, false, &SrcPos);
354}
355
356
357RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem)
358{
359 /*
360 * Validate input.
361 */
362 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
363 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
364 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
365
366#ifdef RTSEMMUTEX_STRICT
367 int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorRec, pThis->cNestings != 1);
368 if (RT_FAILURE(rc9))
369 return rc9;
370#endif
371
372 /*
373 * Check if nested.
374 */
375 pthread_t Self = pthread_self();
376 if (RT_UNLIKELY( pThis->Owner != Self
377 || pThis->cNesting == 0))
378 {
379 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
380 pThis, Self, pThis->Owner, pThis->cNesting));
381 return VERR_NOT_OWNER;
382 }
383
384 /*
385 * If nested we'll just pop a nesting.
386 */
387 if (pThis->cNesting > 1)
388 {
389 ASMAtomicDecU32(&pThis->cNesting);
390 return VINF_SUCCESS;
391 }
392
393 /*
394 * Clear the state. (cNesting == 1)
395 */
396 pThis->Owner = (pthread_t)~0;
397 ASMAtomicWriteU32(&pThis->cNesting, 0);
398
399 /*
400 * Release the mutex.
401 */
402 int32_t iNew = ASMAtomicDecS32(&pThis->iState);
403 if (RT_UNLIKELY(iNew != 0))
404 {
405 /* somebody is waiting, try wake up one of them. */
406 ASMAtomicXchgS32(&pThis->iState, 0);
407 (void)sys_futex(&pThis->iState, FUTEX_WAKE, 1, NULL, NULL, 0);
408 }
409 return VINF_SUCCESS;
410}
411
412
413RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutex)
414{
415 /*
416 * Validate.
417 */
418 RTSEMMUTEXINTERNAL *pThis = hMutex;
419 AssertPtrReturn(pThis, false);
420 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false);
421
422 return pThis->Owner != (pthread_t)~0;
423}
424
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