VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.2 KB
Line 
1/* $Id: semmutex-linux.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Mutex Semaphore, Linux (2.6.x+).
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/semaphore.h>
42#include "internal/iprt.h"
43
44#include <iprt/alloc.h>
45#include <iprt/asm.h>
46#include <iprt/assert.h>
47#include <iprt/err.h>
48#include <iprt/lockvalidator.h>
49#include <iprt/thread.h>
50#include <iprt/time.h>
51#include "internal/magics.h"
52#include "internal/strict.h"
53
54#include <errno.h>
55#include <limits.h>
56#include <pthread.h>
57#include <unistd.h>
58#include <sys/time.h>
59#include <sys/syscall.h>
60#if 0 /* With 2.6.17 futex.h has become C++ unfriendly. */
61# include <linux/futex.h>
62#else
63# define FUTEX_WAIT 0
64# define FUTEX_WAKE 1
65#endif
66
67
68/*********************************************************************************************************************************
69* Structures and Typedefs *
70*********************************************************************************************************************************/
71/**
72 * Linux internal representation of a Mutex semaphore.
73 */
74struct RTSEMMUTEXINTERNAL
75{
76 /** The futex state variable.
77 * 0 means unlocked.
78 * 1 means locked, no waiters.
79 * 2 means locked, one or more waiters.
80 */
81 int32_t volatile iState;
82 /** Nesting count. */
83 uint32_t volatile cNestings;
84 /** The owner of the mutex. */
85 pthread_t volatile Owner;
86 /** Magic value (RTSEMMUTEX_MAGIC). */
87 uint32_t volatile u32Magic;
88#ifdef RTSEMMUTEX_STRICT
89 /** Lock validator record associated with this mutex. */
90 RTLOCKVALRECEXCL ValidatorRec;
91#endif
92};
93
94
95
96/**
97 * Wrapper for the futex syscall.
98 */
99static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
100{
101 errno = 0;
102 long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
103 if (rc < 0)
104 {
105 Assert(rc == -1);
106 rc = -errno;
107 }
108 return rc;
109}
110
111
112#undef RTSemMutexCreate
113RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
114{
115 return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
116}
117
118
119RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
120 RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
121{
122 AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
123
124 /*
125 * Allocate semaphore handle.
126 */
127 struct RTSEMMUTEXINTERNAL *pThis = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
128 if (pThis)
129 {
130 pThis->u32Magic = RTSEMMUTEX_MAGIC;
131 pThis->iState = 0;
132 pThis->Owner = (pthread_t)~0;
133 pThis->cNestings = 0;
134#ifdef RTSEMMUTEX_STRICT
135 if (!pszNameFmt)
136 {
137 static uint32_t volatile s_iMutexAnon = 0;
138 RTLockValidatorRecExclInit(&pThis->ValidatorRec, hClass, uSubClass, pThis,
139 !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL),
140 "RTSemMutex-%u", ASMAtomicIncU32(&s_iMutexAnon) - 1);
141 }
142 else
143 {
144 va_list va;
145 va_start(va, pszNameFmt);
146 RTLockValidatorRecExclInitV(&pThis->ValidatorRec, hClass, uSubClass, pThis,
147 !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL), pszNameFmt, va);
148 va_end(va);
149 }
150#else
151 RT_NOREF(hClass, uSubClass, pszNameFmt);
152#endif
153
154 *phMutexSem = pThis;
155 return VINF_SUCCESS;
156 }
157
158 return VERR_NO_MEMORY;
159}
160
161
162RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
163{
164 /*
165 * Validate input.
166 */
167 if (hMutexSem == NIL_RTSEMMUTEX)
168 return VINF_SUCCESS;
169 struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
170 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
171 AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC,
172 ("hMutexSem=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
173 VERR_INVALID_HANDLE);
174
175 /*
176 * Invalidate the semaphore and wake up anyone waiting on it.
177 */
178 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD);
179 if (ASMAtomicXchgS32(&pThis->iState, 0) > 0)
180 {
181 sys_futex(&pThis->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
182 usleep(1000);
183 }
184 pThis->Owner = (pthread_t)~0;
185 pThis->cNestings = 0;
186#ifdef RTSEMMUTEX_STRICT
187 RTLockValidatorRecExclDelete(&pThis->ValidatorRec);
188#endif
189
190 /*
191 * Free the semaphore memory and be gone.
192 */
193 RTMemFree(pThis);
194 return VINF_SUCCESS;
195}
196
197
198RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass)
199{
200#ifdef RTSEMMUTEX_STRICT
201 /*
202 * Validate.
203 */
204 RTSEMMUTEXINTERNAL *pThis = hMutexSem;
205 AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
206 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
207
208 return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass);
209#else
210 RT_NOREF(hMutexSem, uSubClass);
211 return RTLOCKVAL_SUB_CLASS_INVALID;
212#endif
213}
214
215
216DECL_FORCE_INLINE(int) rtSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, bool fAutoResume, PCRTLOCKVALSRCPOS pSrcPos)
217{
218 RT_NOREF(pSrcPos);
219
220 /*
221 * Validate input.
222 */
223 struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
224 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
225 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
226
227 /*
228 * Check if nested request.
229 */
230 pthread_t Self = pthread_self();
231 if ( pThis->Owner == Self
232 && pThis->cNestings > 0)
233 {
234#ifdef RTSEMMUTEX_STRICT
235 int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorRec, pSrcPos);
236 if (RT_FAILURE(rc9))
237 return rc9;
238#endif
239 ASMAtomicIncU32(&pThis->cNestings);
240 return VINF_SUCCESS;
241 }
242
243#ifdef RTSEMMUTEX_STRICT
244 RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
245 if (cMillies)
246 {
247 int rc9 = RTLockValidatorRecExclCheckOrder(&pThis->ValidatorRec, hThreadSelf, pSrcPos, cMillies);
248 if (RT_FAILURE(rc9))
249 return rc9;
250 }
251#else
252 RTTHREAD hThreadSelf = RTThreadSelf();
253#endif
254
255 /*
256 * Convert timeout value.
257 */
258 struct timespec ts;
259 struct timespec *pTimeout = NULL;
260 uint64_t u64End = 0; /* shut up gcc */
261 if (cMillies != RT_INDEFINITE_WAIT)
262 {
263 ts.tv_sec = cMillies / 1000;
264 ts.tv_nsec = (cMillies % 1000) * UINT32_C(1000000);
265 u64End = RTTimeSystemNanoTS() + cMillies * UINT64_C(1000000);
266 pTimeout = &ts;
267 }
268
269 /*
270 * Lock the mutex.
271 * Optimize for the uncontended case (makes 1-2 ns difference).
272 */
273 if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&pThis->iState, 1, 0)))
274 {
275 for (;;)
276 {
277 int32_t iOld = ASMAtomicXchgS32(&pThis->iState, 2);
278
279 /*
280 * Was the lock released in the meantime? This is unlikely (but possible)
281 */
282 if (RT_UNLIKELY(iOld == 0))
283 break;
284
285 /*
286 * Go to sleep.
287 */
288 if (pTimeout && ( pTimeout->tv_sec || pTimeout->tv_nsec ))
289 {
290#ifdef RTSEMMUTEX_STRICT
291 int rc9 = RTLockValidatorRecExclCheckBlocking(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true,
292 cMillies, RTTHREADSTATE_MUTEX, true);
293 if (RT_FAILURE(rc9))
294 return rc9;
295#else
296 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_MUTEX, true);
297#endif
298 }
299
300 long rc = sys_futex(&pThis->iState, FUTEX_WAIT, 2, pTimeout, NULL, 0);
301
302 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
303 if (RT_UNLIKELY(pThis->u32Magic != RTSEMMUTEX_MAGIC))
304 return VERR_SEM_DESTROYED;
305
306 /*
307 * Act on the wakup code.
308 */
309 if (rc == -ETIMEDOUT)
310 {
311 Assert(pTimeout);
312 return VERR_TIMEOUT;
313 }
314 if (rc == 0)
315 /* we'll leave the loop now unless another thread is faster */;
316 else if (rc == -EWOULDBLOCK)
317 /* retry with new value. */;
318 else if (rc == -EINTR)
319 {
320 if (!fAutoResume)
321 return VERR_INTERRUPTED;
322 }
323 else
324 {
325 /* this shouldn't happen! */
326 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
327 return RTErrConvertFromErrno(rc);
328 }
329
330 /* adjust the relative timeout */
331 if (pTimeout)
332 {
333 int64_t i64Diff = u64End - RTTimeSystemNanoTS();
334 if (i64Diff < 1000)
335 {
336 rc = VERR_TIMEOUT;
337 break;
338 }
339 ts.tv_sec = (uint64_t)i64Diff / UINT32_C(1000000000);
340 ts.tv_nsec = (uint64_t)i64Diff % UINT32_C(1000000000);
341 }
342 }
343
344 /*
345 * When leaving this loop, iState is set to 2. This means that we gained the
346 * lock and there are _possibly_ some waiters. We don't know exactly as another
347 * thread might entered this loop at nearly the same time. Therefore we will
348 * call futex_wakeup once too often (if _no_ other thread entered this loop).
349 * The key problem is the simple futex_wait test for x != y (iState != 2) in
350 * our case).
351 */
352 }
353
354 /*
355 * Set the owner and nesting.
356 */
357 pThis->Owner = Self;
358 ASMAtomicWriteU32(&pThis->cNestings, 1);
359#ifdef RTSEMMUTEX_STRICT
360 RTLockValidatorRecExclSetOwner(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true);
361#endif
362 return VINF_SUCCESS;
363}
364
365
366#undef RTSemMutexRequest
367RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
368{
369#ifndef RTSEMMUTEX_STRICT
370 int rc = rtSemMutexRequest(hMutexSem, cMillies, true, NULL);
371#else
372 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
373 int rc = rtSemMutexRequest(hMutexSem, cMillies, true, &SrcPos);
374#endif
375 Assert(rc != VERR_INTERRUPTED);
376 return rc;
377}
378
379
380RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
381{
382 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
383 int rc = rtSemMutexRequest(hMutexSem, cMillies, true, &SrcPos);
384 Assert(rc != VERR_INTERRUPTED);
385 return rc;
386}
387
388
389#undef RTSemMutexRequestNoResume
390RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
391{
392#ifndef RTSEMMUTEX_STRICT
393 return rtSemMutexRequest(hMutexSem, cMillies, false, NULL);
394#else
395 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
396 return rtSemMutexRequest(hMutexSem, cMillies, false, &SrcPos);
397#endif
398}
399
400
401RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
402{
403 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
404 return rtSemMutexRequest(hMutexSem, cMillies, false, &SrcPos);
405}
406
407
408RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
409{
410 /*
411 * Validate input.
412 */
413 struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
414 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
415 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
416
417#ifdef RTSEMMUTEX_STRICT
418 int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorRec, pThis->cNestings == 1);
419 if (RT_FAILURE(rc9))
420 return rc9;
421#endif
422
423 /*
424 * Check if nested.
425 */
426 pthread_t Self = pthread_self();
427 if (RT_UNLIKELY( pThis->Owner != Self
428 || pThis->cNestings == 0))
429 {
430 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNestings=%d\n",
431 pThis, Self, pThis->Owner, pThis->cNestings));
432 return VERR_NOT_OWNER;
433 }
434
435 /*
436 * If nested we'll just pop a nesting.
437 */
438 if (pThis->cNestings > 1)
439 {
440 ASMAtomicDecU32(&pThis->cNestings);
441 return VINF_SUCCESS;
442 }
443
444 /*
445 * Clear the state. (cNestings == 1)
446 */
447 pThis->Owner = (pthread_t)~0;
448 ASMAtomicWriteU32(&pThis->cNestings, 0);
449
450 /*
451 * Release the mutex.
452 */
453 int32_t iNew = ASMAtomicDecS32(&pThis->iState);
454 if (RT_UNLIKELY(iNew != 0))
455 {
456 /* somebody is waiting, try wake up one of them. */
457 ASMAtomicXchgS32(&pThis->iState, 0);
458 (void)sys_futex(&pThis->iState, FUTEX_WAKE, 1, NULL, NULL, 0);
459 }
460 return VINF_SUCCESS;
461}
462
463
464RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
465{
466 /*
467 * Validate.
468 */
469 RTSEMMUTEXINTERNAL *pThis = hMutexSem;
470 AssertPtrReturn(pThis, false);
471 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false);
472
473 return pThis->Owner != (pthread_t)~0;
474}
475
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use