VirtualBox

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

Last change on this file since 24872 was 22959, checked in by vboxsync, 16 years ago

sem*-linux.cpp,Makefile.kmk: Added check for cMillies==0 in RTSemEvent and introduced a build config variable IPRT_WITH_FUTEX_BASED_SEMS. Corrected comment in semeventmulti about the futex variable values.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.4 KB
Line 
1/* $Id: semmutex-linux.cpp 22959 2009-09-11 13:45:44Z 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 <iprt/assert.h>
36#include <iprt/alloc.h>
37#include <iprt/thread.h>
38#include <iprt/asm.h>
39#include <iprt/err.h>
40#include <iprt/time.h>
41#include "internal/magics.h"
42#include "internal/strict.h"
43
44#include <errno.h>
45#include <limits.h>
46#include <pthread.h>
47#include <unistd.h>
48#include <sys/time.h>
49#include <sys/syscall.h>
50#if 0 /* With 2.6.17 futex.h has become C++ unfriendly. */
51# include <linux/futex.h>
52#else
53# define FUTEX_WAIT 0
54# define FUTEX_WAKE 1
55#endif
56
57
58/*******************************************************************************
59* Structures and Typedefs *
60*******************************************************************************/
61/**
62 * Linux internal representation of a Mutex semaphore.
63 */
64struct RTSEMMUTEXINTERNAL
65{
66 /** The futex state variable.
67 * 0 means unlocked.
68 * 1 means locked, no waiters.
69 * 2 means locked, one or more waiters.
70 */
71 int32_t volatile iState;
72 /** Nesting count. */
73 uint32_t volatile cNesting;
74 /** The owner of the mutex. */
75 pthread_t volatile Owner;
76 /** Magic value. */
77 intptr_t volatile iMagic;
78};
79
80
81/**
82 * Wrapper for the futex syscall.
83 */
84static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
85{
86 errno = 0;
87 long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
88 if (rc < 0)
89 {
90 Assert(rc == -1);
91 rc = -errno;
92 }
93 return rc;
94}
95
96
97RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX pMutexSem)
98{
99 /*
100 * Allocate semaphore handle.
101 */
102 struct RTSEMMUTEXINTERNAL *pThis = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
103 if (pThis)
104 {
105 pThis->iMagic = RTSEMMUTEX_MAGIC;
106 pThis->iState = 0;
107 pThis->Owner = (pthread_t)~0;
108 pThis->cNesting = 0;
109
110 *pMutexSem = pThis;
111 return VINF_SUCCESS;
112 }
113
114 return VERR_NO_MEMORY;
115}
116
117
118RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX MutexSem)
119{
120 /*
121 * Validate input.
122 */
123 if (MutexSem == NIL_RTSEMMUTEX)
124 return VERR_INVALID_HANDLE;
125 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
126 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
127 AssertMsgReturn(pThis->iMagic == RTSEMMUTEX_MAGIC,
128 ("MutexSem=%p iMagic=%#x\n", pThis, pThis->iMagic),
129 VERR_INVALID_HANDLE);
130
131 /*
132 * Invalidate the semaphore and wake up anyone waiting on it.
133 */
134 ASMAtomicXchgSize(&pThis->iMagic, RTSEMMUTEX_MAGIC + 1);
135 if (ASMAtomicXchgS32(&pThis->iState, 0) > 0)
136 {
137 sys_futex(&pThis->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
138 usleep(1000);
139 }
140 pThis->Owner = (pthread_t)~0;
141 pThis->cNesting = 0;
142
143 /*
144 * Free the semaphore memory and be gone.
145 */
146 RTMemFree(pThis);
147 return VINF_SUCCESS;
148}
149
150
151static int rtsemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies, bool fAutoResume)
152{
153 /*
154 * Validate input.
155 */
156 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
157 AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMMUTEX_MAGIC,
158 VERR_INVALID_HANDLE);
159
160 /*
161 * Check if nested request.
162 */
163 pthread_t Self = pthread_self();
164 if ( pThis->Owner == Self
165 && pThis->cNesting > 0)
166 {
167 pThis->cNesting++;
168 return VINF_SUCCESS;
169 }
170
171 /*
172 * Convert timeout value.
173 */
174 struct timespec ts;
175 struct timespec *pTimeout = NULL;
176 uint64_t u64End = 0; /* shut up gcc */
177 if (cMillies != RT_INDEFINITE_WAIT)
178 {
179 ts.tv_sec = cMillies / 1000;
180 ts.tv_nsec = (cMillies % 1000) * 1000000;
181 u64End = RTTimeSystemNanoTS() + cMillies * 1000000;
182 pTimeout = &ts;
183 }
184
185 /*
186 * Lock the mutex.
187 * Optimize for the uncontended case (makes 1-2 ns difference).
188 */
189 if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&pThis->iState, 1, 0)))
190 {
191 for (;;)
192 {
193 int32_t iOld = ASMAtomicXchgS32(&pThis->iState, 2);
194
195 /*
196 * Was the lock released in the meantime? This is unlikely (but possible)
197 */
198 if (RT_UNLIKELY(iOld == 0))
199 break;
200
201 /*
202 * Go to sleep.
203 */
204 long rc = sys_futex(&pThis->iState, FUTEX_WAIT, 2, pTimeout, NULL, 0);
205 if (RT_UNLIKELY(pThis->iMagic != RTSEMMUTEX_MAGIC))
206 return VERR_SEM_DESTROYED;
207
208 /*
209 * Act on the wakup code.
210 */
211 if (rc == -ETIMEDOUT)
212 {
213 Assert(pTimeout);
214 return VERR_TIMEOUT;
215 }
216 if (rc == 0)
217 /* we'll leave the loop now unless another thread is faster */;
218 else if (rc == -EWOULDBLOCK)
219 /* retry with new value. */;
220 else if (rc == -EINTR)
221 {
222 if (!fAutoResume)
223 return VERR_INTERRUPTED;
224 }
225 else
226 {
227 /* this shouldn't happen! */
228 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
229 return RTErrConvertFromErrno(rc);
230 }
231
232 /* adjust the relative timeout */
233 if (pTimeout)
234 {
235 int64_t i64Diff = u64End - RTTimeSystemNanoTS();
236 if (i64Diff < 1000)
237 {
238 rc = VERR_TIMEOUT;
239 break;
240 }
241 ts.tv_sec = i64Diff / 1000000000;
242 ts.tv_nsec = i64Diff % 1000000000;
243 }
244 }
245
246 /*
247 * When leaving this loop, iState is set to 2. This means that we gained the
248 * lock and there are _possibly_ some waiters. We don't know exactly as another
249 * thread might entered this loop at nearly the same time. Therefore we will
250 * call futex_wakeup once too often (if _no_ other thread entered this loop).
251 * The key problem is the simple futex_wait test for x != y (iState != 2) in
252 * our case).
253 */
254 }
255
256 /*
257 * Set the owner and nesting.
258 */
259 pThis->Owner = Self;
260 ASMAtomicXchgU32(&pThis->cNesting, 1);
261#ifdef RTSEMMUTEX_STRICT
262 RTTHREAD Thread = RTThreadSelf();
263 if (Thread != NIL_RTTHREAD)
264 RTThreadWriteLockInc(Thread);
265#endif
266 return VINF_SUCCESS;
267}
268
269
270RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies)
271{
272 int rc = rtsemMutexRequest(MutexSem, cMillies, true);
273 Assert(rc != VERR_INTERRUPTED);
274 return rc;
275}
276
277
278RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies)
279{
280 return rtsemMutexRequest(MutexSem, cMillies, false);
281}
282
283
284RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem)
285{
286 /*
287 * Validate input.
288 */
289 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
290 AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMMUTEX_MAGIC,
291 VERR_INVALID_HANDLE);
292
293 /*
294 * Check if nested.
295 */
296 pthread_t Self = pthread_self();
297 if (RT_UNLIKELY( pThis->Owner != Self
298 || pThis->cNesting == 0))
299 {
300 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
301 pThis, Self, pThis->Owner, pThis->cNesting));
302 return VERR_NOT_OWNER;
303 }
304
305 /*
306 * If nested we'll just pop a nesting.
307 */
308 if (pThis->cNesting > 1)
309 {
310 pThis->cNesting--;
311 return VINF_SUCCESS;
312 }
313
314 /*
315 * Clear the state. (cNesting == 1)
316 */
317#ifdef RTSEMMUTEX_STRICT
318 RTTHREAD Thread = RTThreadSelf();
319 if (Thread != NIL_RTTHREAD)
320 RTThreadWriteLockDec(Thread);
321#endif
322 pThis->Owner = (pthread_t)~0;
323 ASMAtomicXchgU32(&pThis->cNesting, 0);
324
325 /*
326 * Release the mutex.
327 */
328 int32_t iNew = ASMAtomicDecS32(&pThis->iState);
329 if (RT_UNLIKELY(iNew != 0))
330 {
331 /* somebody is waiting, try wake up one of them. */
332 ASMAtomicXchgS32(&pThis->iState, 0);
333 (void)sys_futex(&pThis->iState, FUTEX_WAKE, 1, NULL, NULL, 0);
334 }
335 return VINF_SUCCESS;
336}
337
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