VirtualBox

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

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

Use ASMAtomicCmpXchgS32

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