VirtualBox

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

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

rebranding: IPRT files again.

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