VirtualBox

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

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

Runtime/semmutex: fix timeout handling like in semevent

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