VirtualBox

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

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

IPRT,PDMCritSect: More validation changes. Validate posix and linux mutexes. Always update the thread state with critsects.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.4 KB
Line 
1/* $Id: semmutex-linux.cpp 25373 2009-12-14 19:20:27Z 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.215389.xyz. 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 RTLOCKVALIDATORREC ValidatorRec;
84#endif
85};
86
87
88/**
89 * Wrapper for the futex syscall.
90 */
91static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
92{
93 errno = 0;
94 long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
95 if (rc < 0)
96 {
97 Assert(rc == -1);
98 rc = -errno;
99 }
100 return rc;
101}
102
103
104RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX pMutexSem)
105{
106 /*
107 * Allocate semaphore handle.
108 */
109 struct RTSEMMUTEXINTERNAL *pThis = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
110 if (pThis)
111 {
112 pThis->u32Magic = RTSEMMUTEX_MAGIC;
113 pThis->iState = 0;
114 pThis->Owner = (pthread_t)~0;
115 pThis->cNesting = 0;
116#ifdef RTSEMMUTEX_STRICT
117 RTLockValidatorInit(&pThis->ValidatorRec, NIL_RTLOCKVALIDATORCLASS, RTLOCKVALIDATOR_SUB_CLASS_NONE, NULL, pThis);
118#endif
119
120 *pMutexSem = pThis;
121 return VINF_SUCCESS;
122 }
123
124 return VERR_NO_MEMORY;
125}
126
127
128RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX MutexSem)
129{
130 /*
131 * Validate input.
132 */
133 if (MutexSem == NIL_RTSEMMUTEX)
134 return VINF_SUCCESS;
135 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
136 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
137 AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC,
138 ("MutexSem=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
139 VERR_INVALID_HANDLE);
140
141 /*
142 * Invalidate the semaphore and wake up anyone waiting on it.
143 */
144 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD);
145 if (ASMAtomicXchgS32(&pThis->iState, 0) > 0)
146 {
147 sys_futex(&pThis->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
148 usleep(1000);
149 }
150 pThis->Owner = (pthread_t)~0;
151 pThis->cNesting = 0;
152#ifdef RTSEMMUTEX_STRICT
153 RTLockValidatorDelete(&pThis->ValidatorRec);
154#endif
155
156 /*
157 * Free the semaphore memory and be gone.
158 */
159 RTMemFree(pThis);
160 return VINF_SUCCESS;
161}
162
163
164DECL_FORCE_INLINE(int) rtSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies, bool fAutoResume, RTSEMMUTEX_STRICT_POS_DECL)
165{
166 /*
167 * Validate input.
168 */
169 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
170 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
171 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
172
173#ifdef RTSEMMUTEX_STRICT
174 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
175 RTLockValidatorCheckOrder(&pThis->ValidatorRec, hThreadSelf, RTSEMMUTEX_STRICT_POS_ARGS);
176#endif
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 ASMAtomicIncU32(&pThis->cNesting);
186 return VINF_SUCCESS;
187 }
188#ifndef RTSEMMUTEX_STRICT
189 RTTHREAD hThreadSelf = RTThreadSelf();
190#endif
191
192 /*
193 * Convert timeout value.
194 */
195 struct timespec ts;
196 struct timespec *pTimeout = NULL;
197 uint64_t u64End = 0; /* shut up gcc */
198 if (cMillies != RT_INDEFINITE_WAIT)
199 {
200 ts.tv_sec = cMillies / 1000;
201 ts.tv_nsec = (cMillies % 1000) * 1000000;
202 u64End = RTTimeSystemNanoTS() + cMillies * 1000000;
203 pTimeout = &ts;
204 }
205
206 /*
207 * Lock the mutex.
208 * Optimize for the uncontended case (makes 1-2 ns difference).
209 */
210 if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&pThis->iState, 1, 0)))
211 {
212 for (;;)
213 {
214 int32_t iOld = ASMAtomicXchgS32(&pThis->iState, 2);
215
216 /*
217 * Was the lock released in the meantime? This is unlikely (but possible)
218 */
219 if (RT_UNLIKELY(iOld == 0))
220 break;
221
222 /*
223 * Go to sleep.
224 */
225 if (pTimeout && ( pTimeout->tv_sec || pTimeout->tv_nsec ))
226 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_MUTEX, RTSEMMUTEX_STRICT_BLOCK_ARGS(&pThis->ValidatorRec));
227 long rc = sys_futex(&pThis->iState, FUTEX_WAIT, 2, pTimeout, NULL, 0);
228 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
229 if (RT_UNLIKELY(pThis->u32Magic != RTSEMMUTEX_MAGIC))
230 return VERR_SEM_DESTROYED;
231
232 /*
233 * Act on the wakup code.
234 */
235 if (rc == -ETIMEDOUT)
236 {
237 Assert(pTimeout);
238 return VERR_TIMEOUT;
239 }
240 if (rc == 0)
241 /* we'll leave the loop now unless another thread is faster */;
242 else if (rc == -EWOULDBLOCK)
243 /* retry with new value. */;
244 else if (rc == -EINTR)
245 {
246 if (!fAutoResume)
247 return VERR_INTERRUPTED;
248 }
249 else
250 {
251 /* this shouldn't happen! */
252 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
253 return RTErrConvertFromErrno(rc);
254 }
255
256 /* adjust the relative timeout */
257 if (pTimeout)
258 {
259 int64_t i64Diff = u64End - RTTimeSystemNanoTS();
260 if (i64Diff < 1000)
261 {
262 rc = VERR_TIMEOUT;
263 break;
264 }
265 ts.tv_sec = i64Diff / 1000000000;
266 ts.tv_nsec = i64Diff % 1000000000;
267 }
268 }
269
270 /*
271 * When leaving this loop, iState is set to 2. This means that we gained the
272 * lock and there are _possibly_ some waiters. We don't know exactly as another
273 * thread might entered this loop at nearly the same time. Therefore we will
274 * call futex_wakeup once too often (if _no_ other thread entered this loop).
275 * The key problem is the simple futex_wait test for x != y (iState != 2) in
276 * our case).
277 */
278 }
279
280 /*
281 * Set the owner and nesting.
282 */
283 pThis->Owner = Self;
284 ASMAtomicWriteU32(&pThis->cNesting, 1);
285#ifdef RTSEMMUTEX_STRICT
286 RTThreadWriteLockInc(RTLockValidatorSetOwner(&pThis->ValidatorRec, hThreadSelf, RTSEMMUTEX_STRICT_POS_ARGS));
287#endif
288 return VINF_SUCCESS;
289}
290
291
292RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies)
293{
294#ifndef RTSEMMUTEX_STRICT
295 int rc = rtSemMutexRequest(MutexSem, cMillies, true, RTSEMMUTEX_STRICT_POS_ARGS);
296 Assert(rc != VERR_INTERRUPTED);
297 return rc;
298#else
299 return RTSemMutexRequestDebug(MutexSem, cMillies, (uintptr_t)ASMReturnAddress(), RT_SRC_POS);
300#endif
301}
302
303
304RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX MutexSem, unsigned cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
305{
306#ifdef RTSEMMUTEX_STRICT
307 int rc = rtSemMutexRequest(MutexSem, cMillies, true, RTSEMMUTEX_STRICT_POS_ARGS);
308 Assert(rc != VERR_INTERRUPTED);
309 return rc;
310#else
311 return RTSemMutexRequest(MutexSem, cMillies);
312#endif
313}
314
315
316RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies)
317{
318#ifndef RTSEMMUTEX_STRICT
319 return rtSemMutexRequest(MutexSem, cMillies, false, RTSEMMUTEX_STRICT_POS_ARGS);
320#else
321 return RTSemMutexRequestNoResumeDebug(MutexSem, cMillies, (uintptr_t)ASMReturnAddress(), RT_SRC_POS);
322#endif
323}
324
325
326RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX MutexSem, unsigned cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
327{
328#ifdef RTSEMMUTEX_STRICT
329 return rtSemMutexRequest(MutexSem, cMillies, false, RTSEMMUTEX_STRICT_POS_ARGS);
330#else
331 return RTSemMutexRequest(MutexSem, cMillies);
332#endif
333}
334
335
336RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem)
337{
338 /*
339 * Validate input.
340 */
341 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
342 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
343 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
344
345 /*
346 * Check if nested.
347 */
348 pthread_t Self = pthread_self();
349 if (RT_UNLIKELY( pThis->Owner != Self
350 || pThis->cNesting == 0))
351 {
352 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
353 pThis, Self, pThis->Owner, pThis->cNesting));
354 return VERR_NOT_OWNER;
355 }
356
357 /*
358 * If nested we'll just pop a nesting.
359 */
360 if (pThis->cNesting > 1)
361 {
362 ASMAtomicDecU32(&pThis->cNesting);
363 return VINF_SUCCESS;
364 }
365
366 /*
367 * Clear the state. (cNesting == 1)
368 */
369#ifdef RTSEMMUTEX_STRICT
370 RTThreadWriteLockDec(RTLockValidatorUnsetOwner(&pThis->ValidatorRec));
371#endif
372 pThis->Owner = (pthread_t)~0;
373 ASMAtomicWriteU32(&pThis->cNesting, 0);
374
375 /*
376 * Release the mutex.
377 */
378 int32_t iNew = ASMAtomicDecS32(&pThis->iState);
379 if (RT_UNLIKELY(iNew != 0))
380 {
381 /* somebody is waiting, try wake up one of them. */
382 ASMAtomicXchgS32(&pThis->iState, 0);
383 (void)sys_futex(&pThis->iState, FUTEX_WAKE, 1, NULL, NULL, 0);
384 }
385 return VINF_SUCCESS;
386}
387
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette