VirtualBox

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

Last change on this file since 19167 was 8651, checked in by vboxsync, 17 years ago

Moved the strictness indicators into internal/strict.h.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.9 KB
Line 
1/* $Id: semmutex-linux.cpp 8651 2008-05-07 12:16:29Z 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 if (cMillies != RT_INDEFINITE_WAIT)
176 {
177 ts.tv_sec = cMillies / 1000;
178 ts.tv_nsec = (cMillies % 1000) * 1000000;
179 pTimeout = &ts;
180 }
181
182 /*
183 * Lock the mutex.
184 * Optimize for the uncontended case (makes 1-2 ns difference).
185 */
186 if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&pThis->iState, 1, 0)))
187 {
188 for (;;)
189 {
190 int32_t iOld = ASMAtomicXchgS32(&pThis->iState, 2);
191
192 /*
193 * Was the lock released in the meantime? This is unlikely (but possible)
194 */
195 if (RT_UNLIKELY(iOld == 0))
196 break;
197
198 /*
199 * Go to sleep.
200 */
201 long rc = sys_futex(&pThis->iState, FUTEX_WAIT, 2, pTimeout, NULL, 0);
202 if (RT_UNLIKELY(pThis->iMagic != RTSEMMUTEX_MAGIC))
203 return VERR_SEM_DESTROYED;
204
205 /*
206 * Act on the wakup code.
207 */
208 if (rc == -ETIMEDOUT)
209 {
210 Assert(pTimeout);
211 return VERR_TIMEOUT;
212 }
213 if (rc == 0)
214 /* we'll leave the loop now unless another thread is faster */;
215 else if (rc == -EWOULDBLOCK)
216 /* retry with new value. */;
217 else if (rc == -EINTR)
218 {
219 if (!fAutoResume)
220 return VERR_INTERRUPTED;
221 }
222 else
223 {
224 /* this shouldn't happen! */
225 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
226 return RTErrConvertFromErrno(rc);
227 }
228 }
229
230 /*
231 * When leaving this loop, iState is set to 2. This means that we gained the
232 * lock and there are _possibly_ some waiters. We don't know exactly as another
233 * thread might entered this loop at nearly the same time. Therefore we will
234 * call futex_wakeup once too often (if _no_ other thread entered this loop).
235 * The key problem is the simple futex_wait test for x != y (iState != 2) in
236 * our case).
237 */
238 }
239
240 /*
241 * Set the owner and nesting.
242 */
243 pThis->Owner = Self;
244 ASMAtomicXchgU32(&pThis->cNesting, 1);
245#ifdef RTSEMMUTEX_STRICT
246 RTTHREAD Thread = RTThreadSelf();
247 if (Thread != NIL_RTTHREAD)
248 RTThreadWriteLockInc(Thread);
249#endif
250 return VINF_SUCCESS;
251}
252
253
254RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies)
255{
256 int rc = rtsemMutexRequest(MutexSem, cMillies, true);
257 Assert(rc != VERR_INTERRUPTED);
258 return rc;
259}
260
261
262RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies)
263{
264 return rtsemMutexRequest(MutexSem, cMillies, false);
265}
266
267
268RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem)
269{
270 /*
271 * Validate input.
272 */
273 struct RTSEMMUTEXINTERNAL *pThis = MutexSem;
274 AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMMUTEX_MAGIC,
275 VERR_INVALID_HANDLE);
276
277 /*
278 * Check if nested.
279 */
280 pthread_t Self = pthread_self();
281 if (RT_UNLIKELY( pThis->Owner != Self
282 || pThis->cNesting == 0))
283 {
284 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
285 pThis, Self, pThis->Owner, pThis->cNesting));
286 return VERR_NOT_OWNER;
287 }
288
289 /*
290 * If nested we'll just pop a nesting.
291 */
292 if (pThis->cNesting > 1)
293 {
294 pThis->cNesting--;
295 return VINF_SUCCESS;
296 }
297
298 /*
299 * Clear the state. (cNesting == 1)
300 */
301#ifdef RTSEMMUTEX_STRICT
302 RTTHREAD Thread = RTThreadSelf();
303 if (Thread != NIL_RTTHREAD)
304 RTThreadWriteLockDec(Thread);
305#endif
306 pThis->Owner = (pthread_t)~0;
307 ASMAtomicXchgU32(&pThis->cNesting, 0);
308
309 /*
310 * Release the mutex.
311 */
312 int32_t iNew = ASMAtomicDecS32(&pThis->iState);
313 if (RT_UNLIKELY(iNew != 0))
314 {
315 /* somebody is waiting, try wake up one of them. */
316 ASMAtomicXchgS32(&pThis->iState, 0);
317 (void)sys_futex(&pThis->iState, FUTEX_WAKE, 1, NULL, NULL, 0);
318 }
319 return VINF_SUCCESS;
320}
321
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