VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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