VirtualBox

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

Last change on this file was 103141, checked in by vboxsync, 4 months ago

Runtime: Some warning fixes about externally visible functions which should be static, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.5 KB
Line 
1/* $Id: semmutex-posix.cpp 103141 2024-01-31 15:03:29Z vboxsync $ */
2/** @file
3 * IPRT - Mutex Semaphore, POSIX.
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 "internal/magics.h"
51#include "internal/strict.h"
52
53#include <errno.h>
54#include <pthread.h>
55#include <unistd.h>
56#include <sys/time.h>
57
58
59/*********************************************************************************************************************************
60* Structures and Typedefs *
61*********************************************************************************************************************************/
62/** Posix internal representation of a Mutex semaphore. */
63struct RTSEMMUTEXINTERNAL
64{
65 /** pthread mutex. */
66 pthread_mutex_t Mutex;
67 /** The owner of the mutex. */
68 volatile pthread_t Owner;
69 /** Nesting count. */
70 volatile uint32_t cNesting;
71 /** Magic value (RTSEMMUTEX_MAGIC). */
72 uint32_t u32Magic;
73#ifdef RTSEMMUTEX_STRICT
74 /** Lock validator record associated with this mutex. */
75 RTLOCKVALRECEXCL ValidatorRec;
76#endif
77};
78
79#if defined(RT_OS_DARWIN) || defined(RT_OS_NETBSD)
80/**
81 * This function is a crude approximation of pthread_mutex_timedlock.
82 */
83static int rtSemFallbackPthreadMutexTimedlock(pthread_mutex_t *mutex, RTMSINTERVAL cMillies)
84{
85 struct timespec ts;
86 int rc;
87
88 rc = pthread_mutex_trylock(mutex);
89 if (rc != EBUSY)
90 return rc;
91
92 ts.tv_sec = cMillies / 1000;
93 ts.tv_nsec = (cMillies % 1000) * 1000000;
94
95 while (ts.tv_sec > 0 || ts.tv_nsec > 0)
96 {
97 struct timespec delta, remaining;
98
99 if (ts.tv_sec > 0)
100 {
101 delta.tv_sec = 1;
102 delta.tv_nsec = 0;
103 ts.tv_sec--;
104 }
105 else
106 {
107 delta.tv_sec = 0;
108 delta.tv_nsec = ts.tv_nsec;
109 ts.tv_nsec = 0;
110 }
111
112 nanosleep(&delta, &remaining);
113
114 rc = pthread_mutex_trylock(mutex);
115 if (rc != EBUSY)
116 return rc;
117
118 if (RT_UNLIKELY(remaining.tv_nsec > 0 || remaining.tv_sec > 0))
119 {
120 ts.tv_sec += remaining.tv_sec;
121 ts.tv_nsec += remaining.tv_nsec;
122 if (ts.tv_nsec >= 1000000000)
123 {
124 ts.tv_nsec -= 1000000000;
125 ts.tv_sec++;
126 }
127 }
128 }
129
130 return ETIMEDOUT;
131}
132#endif
133
134
135#undef RTSemMutexCreate
136RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
137{
138 return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
139}
140
141
142RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
143 RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
144{
145 AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
146
147 /*
148 * Allocate semaphore handle.
149 */
150 int rc;
151 struct RTSEMMUTEXINTERNAL *pThis = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
152 if (pThis)
153 {
154 /*
155 * Create the semaphore.
156 */
157 rc = pthread_mutex_init(&pThis->Mutex, NULL);
158 if (!rc)
159 {
160 pThis->Owner = (pthread_t)-1;
161 pThis->cNesting = 0;
162 pThis->u32Magic = RTSEMMUTEX_MAGIC;
163#ifdef RTSEMMUTEX_STRICT
164 if (!pszNameFmt)
165 {
166 static uint32_t volatile s_iMutexAnon = 0;
167 RTLockValidatorRecExclInit(&pThis->ValidatorRec, hClass, uSubClass, pThis,
168 !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL),
169 "RTSemMutex-%u", ASMAtomicIncU32(&s_iMutexAnon) - 1);
170 }
171 else
172 {
173 va_list va;
174 va_start(va, pszNameFmt);
175 RTLockValidatorRecExclInitV(&pThis->ValidatorRec, hClass, uSubClass, pThis,
176 !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL), pszNameFmt, va);
177 va_end(va);
178 }
179#else
180 RT_NOREF_PV(hClass); RT_NOREF_PV(uSubClass); RT_NOREF_PV(pszNameFmt);
181#endif
182
183 *phMutexSem = pThis;
184 return VINF_SUCCESS;
185 }
186 RTMemFree(pThis);
187 }
188 else
189 rc = VERR_NO_MEMORY;
190
191 return rc;
192}
193
194
195RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
196{
197 /*
198 * Validate input.
199 */
200 if (hMutexSem == NIL_RTSEMMUTEX)
201 return VINF_SUCCESS;
202 struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
203 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
204 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
205
206 /*
207 * Try destroy it.
208 */
209 int rc = pthread_mutex_destroy(&pThis->Mutex);
210 if (rc)
211 {
212 AssertMsgFailed(("Failed to destroy mutex sem %p, rc=%d.\n", hMutexSem, rc));
213 return RTErrConvertFromErrno(rc);
214 }
215
216 /*
217 * Free the memory and be gone.
218 */
219 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD);
220 pThis->Owner = (pthread_t)-1;
221 pThis->cNesting = UINT32_MAX;
222#ifdef RTSEMMUTEX_STRICT
223 RTLockValidatorRecExclDelete(&pThis->ValidatorRec);
224#endif
225 RTMemTmpFree(pThis);
226
227 return VINF_SUCCESS;
228}
229
230
231RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass)
232{
233#ifdef RTSEMMUTEX_STRICT
234 /*
235 * Validate.
236 */
237 RTSEMMUTEXINTERNAL *pThis = hMutexSem;
238 AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
239 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
240
241 return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass);
242#else
243 RT_NOREF_PV(hMutexSem); RT_NOREF_PV(uSubClass);
244 return RTLOCKVAL_SUB_CLASS_INVALID;
245#endif
246}
247
248
249DECL_FORCE_INLINE(int) rtSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, PCRTLOCKVALSRCPOS pSrcPos)
250{
251 /*
252 * Validate input.
253 */
254 struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
255 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
256 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
257
258 /*
259 * Check if nested request.
260 */
261 pthread_t Self = pthread_self();
262 if ( pThis->Owner == Self
263 && pThis->cNesting > 0)
264 {
265#ifdef RTSEMMUTEX_STRICT
266 int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorRec, pSrcPos);
267 if (RT_FAILURE(rc9))
268 return rc9;
269#endif
270 ASMAtomicIncU32(&pThis->cNesting);
271 return VINF_SUCCESS;
272 }
273
274 /*
275 * Lock it.
276 */
277 RTTHREAD hThreadSelf = NIL_RTTHREAD;
278 if (cMillies != 0)
279 {
280#ifdef RTSEMMUTEX_STRICT
281 hThreadSelf = RTThreadSelfAutoAdopt();
282 int rc9 = RTLockValidatorRecExclCheckOrderAndBlocking(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true,
283 cMillies, RTTHREADSTATE_MUTEX, true);
284 if (RT_FAILURE(rc9))
285 return rc9;
286#else
287 hThreadSelf = RTThreadSelf();
288 RTThreadBlocking(hThreadSelf, RTTHREADSTATE_MUTEX, true);
289 RT_NOREF_PV(pSrcPos);
290#endif
291 }
292
293 if (cMillies == RT_INDEFINITE_WAIT)
294 {
295 /* take mutex */
296 int rc = pthread_mutex_lock(&pThis->Mutex);
297 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
298 if (rc)
299 {
300 AssertMsgFailed(("Failed to lock mutex sem %p, rc=%d.\n", hMutexSem, rc)); NOREF(rc);
301 return RTErrConvertFromErrno(rc);
302 }
303 }
304 else
305 {
306 int rc;
307#if !defined(RT_OS_DARWIN) && !defined(RT_OS_NETBSD)
308 struct timespec ts = {0,0};
309# if defined(RT_OS_HAIKU)
310 struct timeval tv = {0,0};
311 gettimeofday(&tv, NULL);
312 ts.tv_sec = tv.tv_sec;
313 ts.tv_nsec = tv.tv_usec * 1000;
314# else
315 clock_gettime(CLOCK_REALTIME, &ts);
316# endif
317 if (cMillies != 0)
318 {
319 ts.tv_nsec += (cMillies % 1000) * 1000000;
320 ts.tv_sec += cMillies / 1000;
321 if (ts.tv_nsec >= 1000000000)
322 {
323 ts.tv_nsec -= 1000000000;
324 ts.tv_sec++;
325 }
326 }
327
328 /* take mutex */
329 rc = pthread_mutex_timedlock(&pThis->Mutex, &ts);
330#else
331 /*
332 * When there's no pthread_mutex_timedlock() use a crude sleep
333 * and retry approximation. Since the sleep interval is
334 * relative, we don't need to convert to the absolute time
335 * here only to convert back to relative in the fallback
336 * function.
337 */
338 rc = rtSemFallbackPthreadMutexTimedlock(&pThis->Mutex, cMillies);
339#endif
340 RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
341 if (rc)
342 {
343 AssertMsg(rc == ETIMEDOUT, ("Failed to lock mutex sem %p, rc=%d.\n", hMutexSem, rc)); NOREF(rc);
344 return RTErrConvertFromErrno(rc);
345 }
346 }
347
348 /*
349 * Set the owner and nesting.
350 */
351 pThis->Owner = Self;
352 ASMAtomicWriteU32(&pThis->cNesting, 1);
353#ifdef RTSEMMUTEX_STRICT
354 RTLockValidatorRecExclSetOwner(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true);
355#endif
356
357 return VINF_SUCCESS;
358}
359
360
361#undef RTSemMutexRequest
362RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
363{
364#ifndef RTSEMMUTEX_STRICT
365 return rtSemMutexRequest(hMutexSem, cMillies, NULL);
366#else
367 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
368 return rtSemMutexRequest(hMutexSem, cMillies, &SrcPos);
369#endif
370}
371
372
373RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
374{
375 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
376 return rtSemMutexRequest(hMutexSem, cMillies, &SrcPos);
377}
378
379
380#undef RTSemMutexRequestNoResume
381RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
382{
383 /* (EINTR isn't returned by the wait functions we're using.) */
384#ifndef RTSEMMUTEX_STRICT
385 return rtSemMutexRequest(hMutexSem, cMillies, NULL);
386#else
387 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
388 return rtSemMutexRequest(hMutexSem, cMillies, &SrcPos);
389#endif
390}
391
392
393RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
394{
395 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
396 return rtSemMutexRequest(hMutexSem, cMillies, &SrcPos);
397}
398
399
400RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
401{
402 /*
403 * Validate input.
404 */
405 struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
406 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
407 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
408
409#ifdef RTSEMMUTEX_STRICT
410 int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorRec, pThis->cNesting == 1);
411 if (RT_FAILURE(rc9))
412 return rc9;
413#endif
414
415 /*
416 * Check if nested.
417 */
418 pthread_t Self = pthread_self();
419 if (RT_UNLIKELY( pThis->Owner != Self
420 || pThis->cNesting == 0))
421 {
422 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
423 pThis, Self, pThis->Owner, pThis->cNesting));
424 return VERR_NOT_OWNER;
425 }
426
427 /*
428 * If nested we'll just pop a nesting.
429 */
430 if (pThis->cNesting > 1)
431 {
432 ASMAtomicDecU32(&pThis->cNesting);
433 return VINF_SUCCESS;
434 }
435
436 /*
437 * Clear the state. (cNesting == 1)
438 */
439 pThis->Owner = (pthread_t)-1;
440 ASMAtomicWriteU32(&pThis->cNesting, 0);
441
442 /*
443 * Unlock mutex semaphore.
444 */
445 int rc = pthread_mutex_unlock(&pThis->Mutex);
446 if (RT_UNLIKELY(rc))
447 {
448 AssertMsgFailed(("Failed to unlock mutex sem %p, rc=%d.\n", hMutexSem, rc)); NOREF(rc);
449 return RTErrConvertFromErrno(rc);
450 }
451
452 return VINF_SUCCESS;
453}
454
455
456RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
457{
458 /*
459 * Validate.
460 */
461 RTSEMMUTEXINTERNAL *pThis = hMutexSem;
462 AssertPtrReturn(pThis, false);
463 AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false);
464
465 return pThis->Owner != (pthread_t)-1;
466}
467
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use