VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/sems-linux.cpp@ 822

Last change on this file since 822 was 822, checked in by vboxsync, 18 years ago

A quick futex based RTSEMEVENT and RTSEMEVENTMULTI implementation, only for AMD64 were we can safely require 2.6.x. Still some rough edges to fix, but it seems to work now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 26.2 KB
Line 
1/* $Id: sems-linux.cpp 822 2007-02-10 04:30:51Z vboxsync $ */
2/** @file
3 * InnoTek Portable Runtime - Semaphores, Linux (AMD64 only ATM).
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung 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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#include <iprt/semaphore.h>
26#include <iprt/assert.h>
27#include <iprt/alloc.h>
28#include <iprt/asm.h>
29#include <iprt/err.h>
30
31#include <errno.h>
32#include <limits.h>
33#include <pthread.h>
34#include <unistd.h>
35#include <sys/time.h>
36#include <sys/syscall.h>
37#include <linux/futex.h>
38
39
40/*******************************************************************************
41* Structures and Typedefs *
42*******************************************************************************/
43
44/**
45 * Linux (single wakup) event semaphore.
46 */
47struct RTSEMEVENTINTERNAL
48{
49 /** Magic value. */
50 intptr_t volatile iMagic;
51 /** The futex state variable.
52 * <0 means signaled.
53 * 0 means not signaled, no waiters.
54 * >0 means not signaled, and the value gives the number of waiters.
55 */
56 int32_t volatile cWaiters;
57};
58
59#define RTSEMEVENT_MAGIC ((intptr_t)-16)
60
61
62/**
63 * Linux multiple wakup event semaphore.
64 */
65struct RTSEMEVENTMULTIINTERNAL
66{
67 /** Magic value. */
68 intptr_t volatile iMagic;
69 /** The futex state variable.
70 * -1 means signaled.
71 * 0 means not signaled, no waiters.
72 * >0 means not signaled, and the value gives the number of waiters.
73 */
74 int32_t volatile iState;
75};
76
77#define RTSEMEVENTMULTI_MAGIC ((intptr_t)-128)
78
79/**
80 * Posix internal representation of a Mutex semaphore.
81 */
82struct RTSEMMUTEXINTERNAL
83{
84 /** pthread mutex. */
85 pthread_mutex_t Mutex;
86 /** The owner of the mutex. */
87 volatile pthread_t Owner;
88 /** Nesting count. */
89 volatile uint32_t cNesting;
90};
91
92
93/**
94 * Posix internal representation of a read-write semaphore.
95 */
96struct RTSEMRWINTERNAL
97{
98 /** pthread rwlock. */
99 pthread_rwlock_t RWLock;
100 /** Variable to check if initialized.
101 * 0 is uninitialized, ~0 is inititialized. */
102 volatile unsigned uCheck;
103 /** The write owner of the lock. */
104 volatile pthread_t WROwner;
105};
106
107
108/**
109 * Wrapper for the futex syscall.
110 */
111static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
112{
113 errno = 0;
114 long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
115 if (rc < 0)
116 {
117 Assert(rc == -1);
118 rc = -errno;
119 }
120 return rc;
121}
122
123
124
125RTDECL(int) RTSemEventCreate(PRTSEMEVENT pEventSem)
126{
127 /*
128 * Allocate semaphore handle.
129 */
130 struct RTSEMEVENTINTERNAL *pIntEventSem = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
131 if (pIntEventSem)
132 {
133 pIntEventSem->iMagic = RTSEMEVENT_MAGIC;
134 pIntEventSem->cWaiters = 0;
135 *pEventSem = pIntEventSem;
136 return VINF_SUCCESS;
137 }
138 return VERR_NO_MEMORY;
139}
140
141
142RTDECL(int) RTSemEventDestroy(RTSEMEVENT EventSem)
143{
144 /*
145 * Validate input.
146 */
147 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
148 AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
149 VERR_INVALID_HANDLE);
150
151 /*
152 * Invalidate the semaphore and wake up anyone waiting on it.
153 */
154 ASMAtomicXchgSize(&pIntEventSem->iMagic, RTSEMEVENT_MAGIC + 1);
155 if (ASMAtomicXchgS32(&pIntEventSem->cWaiters, INT32_MIN / 2) > 0)
156 {
157 sys_futex(&pIntEventSem->cWaiters, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
158 usleep(1000);
159 }
160
161 /*
162 * Free the semaphore memory and be gone.
163 */
164 RTMemFree(pIntEventSem);
165 return VINF_SUCCESS;
166}
167
168
169RTDECL(int) RTSemEventSignal(RTSEMEVENT EventSem)
170{
171 /*
172 * Validate input.
173 */
174 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
175 AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
176 VERR_INVALID_HANDLE);
177 /*
178 * Try signal it.
179 */
180 for (unsigned i = 0;; i++)
181 {
182 int32_t iCur = pIntEventSem->cWaiters;
183 if (iCur < 0)
184 break; /* already signaled */
185 if (iCur == 0)
186 {
187 if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, -1, 0))
188 break; /* nobody is waiting */
189 }
190 else if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, iCur - 1, iCur))
191 {
192 /* somebody is waiting, wake up one. */
193 long cWoken = sys_futex(&pIntEventSem->cWaiters, FUTEX_WAKE, 1, NULL, NULL, 0);
194 if (cWoken == 1)
195 break;
196 ASMAtomicIncS32(&pIntEventSem->cWaiters);
197 AssertMsg(cWoken == 0, ("%ld\n", cWoken));
198
199 /*
200 * We're waiting for the threads to start executing, that's kind of
201 * silly really. But for now, take care that we don't prevent them
202 * from executing.
203 */
204 if ((i % 128) == 127)
205 usleep(1000);
206 else if ((i % 16) == 15)
207 pthread_yield();
208 else
209 Assert(i < 1024);
210 }
211 }
212 return VINF_SUCCESS;
213}
214
215
216static int rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
217{
218 /*
219 * Validate input.
220 */
221 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
222 AssertReturn(VALID_PTR(pIntEventSem) && pIntEventSem->iMagic == RTSEMEVENT_MAGIC,
223 VERR_INVALID_HANDLE);
224
225 /*
226 * Quickly check whether it's signaled.
227 */
228 if (ASMAtomicCmpXchgS32(&pIntEventSem->cWaiters, 0, -1))
229 return VINF_SUCCESS;
230
231 /*
232 * Convert timeout value.
233 */
234 struct timespec ts;
235 struct timespec *pTimeout = 0;
236 if (cMillies != RT_INDEFINITE_WAIT)
237 {
238 ts.tv_sec = cMillies / 1000;
239 ts.tv_nsec = (cMillies % 1000) * 1000000;
240 pTimeout = &ts;
241 }
242
243 /*
244 * The wait loop.
245 */
246 for (unsigned i = 0;; i++)
247 {
248 /*
249 * Announce that we're among the waiters.
250 */
251 int32_t iNew = ASMAtomicIncS32(&pIntEventSem->cWaiters);
252 if (iNew == 0)
253 return VINF_SUCCESS;
254 if (RT_LIKELY(iNew > 0))
255 {
256 /*
257 * Go to sleep.
258 */
259 long rc = sys_futex(&pIntEventSem->cWaiters, FUTEX_WAIT, iNew, pTimeout, NULL, 0);
260 if (RT_UNLIKELY(pIntEventSem->iMagic != RTSEMEVENT_MAGIC))
261 return VERR_SEM_DESTROYED;
262 if (rc == 0)
263 return VINF_SUCCESS;
264
265 /* don't count us among the waiters. */
266 iNew = ASMAtomicDecS32(&pIntEventSem->cWaiters);
267 Assert(iNew >= 0);
268
269 /*
270 * Act on the wakup code.
271 */
272 if (rc == -ETIMEDOUT)
273 {
274 Assert(pTimeout);
275 return VERR_TIMEOUT;
276 }
277 if (rc == -EWOULDBLOCK)
278 /* retry with new value. */;
279 else if (rc == -EINTR)
280 {
281 if (!fAutoResume)
282 return VERR_INTERRUPTED;
283 }
284 else
285 {
286 /* this shouldn't happen! */
287 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
288 return RTErrConvertFromErrno(rc);
289 }
290 }
291 else
292 {
293 /*
294 * Somebody is signaling the semaphore, let's try again.
295 * But take care to yield and sleep after a while like always.
296 * (Could probably change this to string only and return right away...)
297 */
298 ASMAtomicDecS32(&pIntEventSem->cWaiters);
299 if ((i % 128) == 127)
300 usleep(1000);
301 else if ((i % 16) == 15)
302 pthread_yield();
303 else
304 AssertReleaseMsg(i < 4096, ("iNew=%d\n", iNew));
305 if (RT_UNLIKELY(pIntEventSem->iMagic != RTSEMEVENT_MAGIC))
306 return VERR_SEM_DESTROYED;
307 }
308 }
309}
310
311
312RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, unsigned cMillies)
313{
314 int rc = rtSemEventWait(EventSem, cMillies, true);
315 Assert(rc != VERR_INTERRUPTED);
316 return rc;
317}
318
319
320RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT EventSem, unsigned cMillies)
321{
322 return rtSemEventWait(EventSem, cMillies, false);
323}
324
325
326
327
328
329RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI pEventMultiSem)
330{
331 /*
332 * Allocate semaphore handle.
333 */
334 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
335 if (pIntEventMultiSem)
336 {
337 pIntEventMultiSem->iMagic = RTSEMEVENTMULTI_MAGIC;
338 pIntEventMultiSem->iState = 0;
339 *pEventMultiSem = pIntEventMultiSem;
340 return VINF_SUCCESS;
341 }
342 return VERR_NO_MEMORY;
343}
344
345
346RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI EventMultiSem)
347{
348 /*
349 * Validate input.
350 */
351 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
352 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
353 VERR_INVALID_HANDLE);
354
355 /*
356 * Invalidate the semaphore and wake up anyone waiting on it.
357 */
358 ASMAtomicXchgSize(&pIntEventMultiSem->iMagic, RTSEMEVENTMULTI_MAGIC + 1);
359 if (ASMAtomicXchgS32(&pIntEventMultiSem->iState, -1) == 1)
360 {
361 sys_futex(&pIntEventMultiSem->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
362 usleep(1000);
363 }
364
365 /*
366 * Free the semaphore memory and be gone.
367 */
368 RTMemFree(pIntEventMultiSem);
369 return VINF_SUCCESS;
370}
371
372
373RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI EventMultiSem)
374{
375 /*
376 * Validate input.
377 */
378 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
379 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
380 VERR_INVALID_HANDLE);
381 /*
382 * Signal it.
383 */
384 int32_t iOld = ASMAtomicXchgS32(&pIntEventMultiSem->iState, -1);
385 if (iOld > 0)
386 {
387 /* wake up sleeping threads. */
388 long cWoken = sys_futex(&pIntEventMultiSem->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
389 AssertMsg(cWoken >= 0, ("%ld\n", cWoken)); NOREF(cWoken);
390 }
391 Assert(iOld == 0 || iOld == -1 || iOld == 1);
392 return VINF_SUCCESS;
393}
394
395
396RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI EventMultiSem)
397{
398 /*
399 * Validate input.
400 */
401 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
402 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
403 VERR_INVALID_HANDLE);
404#ifdef RT_STRICT
405 int32_t i = pIntEventMultiSem->iState;
406 Assert(i == 0 || i == -1 || i == 1);
407#endif
408
409 /*
410 * Reset it.
411 */
412 ASMAtomicCmpXchgS32(&pIntEventMultiSem->iState, 0, -1);
413 return VINF_SUCCESS;
414}
415
416
417static int rtSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies, bool fAutoResume)
418{
419 /*
420 * Validate input.
421 */
422 struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = EventMultiSem;
423 AssertReturn(VALID_PTR(pIntEventMultiSem) && pIntEventMultiSem->iMagic == RTSEMEVENTMULTI_MAGIC,
424 VERR_INVALID_HANDLE);
425
426 /*
427 * Quickly check whether it's signaled.
428 */
429 int32_t iCur = pIntEventMultiSem->iState;
430 Assert(iCur == 0 || iCur == -1 || iCur == 1);
431 if (iCur == -1)
432 return VINF_SUCCESS;
433 if (!cMillies)
434 return VERR_TIMEOUT;
435
436 /*
437 * Convert timeout value.
438 */
439 struct timespec ts;
440 struct timespec *pTimeout = NULL;
441 if (cMillies != RT_INDEFINITE_WAIT)
442 {
443 ts.tv_sec = cMillies / 1000;
444 ts.tv_nsec = (cMillies % 1000) * 1000000;
445 pTimeout = &ts;
446 }
447
448 /*
449 * The wait loop.
450 */
451 for (unsigned i = 0;; i++)
452 {
453 /*
454 * Start waiting. We only account for there being or having been
455 * threads waiting on the semaphore to keep things simple.
456 */
457 iCur = pIntEventMultiSem->iState;
458 Assert(iCur == 0 || iCur == -1 || iCur == 1);
459 if ( iCur == 1
460 || ASMAtomicCmpXchgS32(&pIntEventMultiSem->iState, 1, 0))
461 {
462 long rc = sys_futex(&pIntEventMultiSem->iState, FUTEX_WAIT, 1, pTimeout, NULL, 0);
463 if (RT_UNLIKELY(pIntEventMultiSem->iMagic != RTSEMEVENTMULTI_MAGIC))
464 return VERR_SEM_DESTROYED;
465 if (rc == 0)
466 return VINF_SUCCESS;
467
468 /*
469 * Act on the wakup code.
470 */
471 if (rc == -ETIMEDOUT)
472 {
473 Assert(pTimeout);
474 return VERR_TIMEOUT;
475 }
476 if (rc == -EWOULDBLOCK)
477 /* retry, the value changed. */;
478 else if (rc == -EINTR)
479 {
480 if (!fAutoResume)
481 return VERR_INTERRUPTED;
482 }
483 else
484 {
485 /* this shouldn't happen! */
486 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
487 return RTErrConvertFromErrno(rc);
488 }
489 }
490 else if (iCur == -1)
491 return VINF_SUCCESS;
492 }
493}
494
495
496RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
497{
498 int rc = rtSemEventMultiWait(EventMultiSem, cMillies, true);
499 Assert(rc != VERR_INTERRUPTED);
500 return rc;
501}
502
503
504RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
505{
506 return rtSemEventMultiWait(EventMultiSem, cMillies, false);
507}
508
509
510
511
512
513/**
514 * Validate a Mutex semaphore handle passed to one of the interface.
515 *
516 * @returns true if valid.
517 * @returns false if invalid.
518 * @param pIntMutexSem Pointer to the mutex semaphore to validate.
519 */
520inline bool rtsemMutexValid(struct RTSEMMUTEXINTERNAL *pIntMutexSem)
521{
522 if ((uintptr_t)pIntMutexSem < 0x10000)
523 return false;
524
525 if (pIntMutexSem->cNesting == (uint32_t)~0)
526 return false;
527
528 return true;
529}
530
531
532RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX pMutexSem)
533{
534 int rc;
535
536 /*
537 * Allocate semaphore handle.
538 */
539 struct RTSEMMUTEXINTERNAL *pIntMutexSem = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
540 if (pIntMutexSem)
541 {
542 /*
543 * Create the semaphore.
544 */
545 pthread_mutexattr_t MutexAttr;
546 rc = pthread_mutexattr_init(&MutexAttr);
547 if (!rc)
548 {
549 rc = pthread_mutex_init(&pIntMutexSem->Mutex, &MutexAttr);
550 if (!rc)
551 {
552 pthread_mutexattr_destroy(&MutexAttr);
553
554 pIntMutexSem->Owner = (pthread_t)~0;
555 pIntMutexSem->cNesting = 0;
556
557 *pMutexSem = pIntMutexSem;
558 return VINF_SUCCESS;
559 }
560 pthread_mutexattr_destroy(&MutexAttr);
561 }
562 RTMemFree(pIntMutexSem);
563 }
564 else
565 rc = VERR_NO_MEMORY;
566
567 return rc;
568}
569
570
571RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX MutexSem)
572{
573 /*
574 * Validate input.
575 */
576 if (!rtsemMutexValid(MutexSem))
577 {
578 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
579 return VERR_INVALID_HANDLE;
580 }
581
582 /*
583 * Try destroy it.
584 */
585 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
586 int rc = pthread_mutex_destroy(&pIntMutexSem->Mutex);
587 if (rc)
588 {
589 AssertMsgFailed(("Failed to destroy mutex sem %p, rc=%d.\n", MutexSem, rc));
590 return RTErrConvertFromErrno(rc);
591 }
592
593 /*
594 * Free the memory and be gone.
595 */
596 pIntMutexSem->Owner = (pthread_t)~0;
597 pIntMutexSem->cNesting = ~0;
598 RTMemTmpFree(pIntMutexSem);
599
600 return VINF_SUCCESS;
601}
602
603
604RTDECL(int) RTSemMutexRequest(RTSEMMUTEX MutexSem, unsigned cMillies)
605{
606 /*
607 * Validate input.
608 */
609 if (!rtsemMutexValid(MutexSem))
610 {
611 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
612 return VERR_INVALID_HANDLE;
613 }
614
615 /*
616 * Check if nested request.
617 */
618 pthread_t Self = pthread_self();
619 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
620 if ( pIntMutexSem->Owner == Self
621 && pIntMutexSem->cNesting > 0)
622 {
623 pIntMutexSem->cNesting++;
624 return VINF_SUCCESS;
625 }
626
627 /*
628 * Lock it.
629 */
630 if (cMillies == RT_INDEFINITE_WAIT)
631 {
632 /* take mutex */
633 int rc = pthread_mutex_lock(&pIntMutexSem->Mutex);
634 if (rc)
635 {
636 AssertMsgFailed(("Failed to lock mutex sem %p, rc=%d.\n", MutexSem, rc)); NOREF(rc);
637 return RTErrConvertFromErrno(rc);
638 }
639 }
640 else
641 {
642 /*
643 * Get current time and calc end of wait time.
644 */
645 struct timespec ts = {0,0};
646 clock_gettime(CLOCK_REALTIME, &ts);
647 if (cMillies != 0)
648 {
649 ts.tv_nsec += (cMillies % 1000) * 1000000;
650 ts.tv_sec += cMillies / 1000;
651 if (ts.tv_nsec >= 1000000000)
652 {
653 ts.tv_nsec -= 1000000000;
654 ts.tv_sec++;
655 }
656 }
657
658 /* take mutex */
659 int rc = pthread_mutex_timedlock(&pIntMutexSem->Mutex, &ts);
660 if (rc)
661 {
662 AssertMsg(rc == ETIMEDOUT, ("Failed to lock mutex sem %p, rc=%d.\n", MutexSem, rc)); NOREF(rc);
663 return RTErrConvertFromErrno(rc);
664 }
665 }
666
667 /*
668 * Set the owner and nesting.
669 */
670 pIntMutexSem->Owner = Self;
671 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 1);
672
673 return VINF_SUCCESS;
674}
675
676
677RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX MutexSem, unsigned cMillies)
678{
679 /* EINTR isn't returned by the wait functions we're using. */
680 return RTSemMutexRequest(MutexSem, cMillies);
681}
682
683
684RTDECL(int) RTSemMutexRelease(RTSEMMUTEX MutexSem)
685{
686 /*
687 * Validate input.
688 */
689 if (!rtsemMutexValid(MutexSem))
690 {
691 AssertMsgFailed(("Invalid handle %p!\n", MutexSem));
692 return VERR_INVALID_HANDLE;
693 }
694
695 /*
696 * Check if nested.
697 */
698 pthread_t Self = pthread_self();
699 struct RTSEMMUTEXINTERNAL *pIntMutexSem = MutexSem;
700 if ( pIntMutexSem->Owner != Self
701 || pIntMutexSem->cNesting == (uint32_t)~0)
702 {
703 AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
704 pIntMutexSem, Self, pIntMutexSem->Owner, pIntMutexSem->cNesting));
705 return VERR_NOT_OWNER;
706 }
707
708 /*
709 * If nested we'll just pop a nesting.
710 */
711 if (pIntMutexSem->cNesting > 1)
712 {
713 pIntMutexSem->cNesting--;
714 return VINF_SUCCESS;
715 }
716
717 /*
718 * Clear the state. (cNesting == 1)
719 */
720 pIntMutexSem->Owner = (pthread_t)~0;
721 ASMAtomicXchgU32(&pIntMutexSem->cNesting, 0);
722
723 /*
724 * Unlock mutex semaphore.
725 */
726 int rc = pthread_mutex_unlock(&pIntMutexSem->Mutex);
727 if (rc)
728 {
729 AssertMsgFailed(("Failed to unlock mutex sem %p, rc=%d.\n", MutexSem, rc)); NOREF(rc);
730 return RTErrConvertFromErrno(rc);
731 }
732
733 return VINF_SUCCESS;
734}
735
736
737
738
739
740/**
741 * Validate a read-write semaphore handle passed to one of the interface.
742 *
743 * @returns true if valid.
744 * @returns false if invalid.
745 * @param pIntRWSem Pointer to the read-write semaphore to validate.
746 */
747inline bool rtsemRWValid(struct RTSEMRWINTERNAL *pIntRWSem)
748{
749 if ((uintptr_t)pIntRWSem < 0x10000)
750 return false;
751
752 if (pIntRWSem->uCheck != (unsigned)~0)
753 return false;
754
755 return true;
756}
757
758
759RTDECL(int) RTSemRWCreate(PRTSEMRW pRWSem)
760{
761 int rc;
762
763 /*
764 * Allocate handle.
765 */
766 struct RTSEMRWINTERNAL *pIntRWSem = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
767 if (pIntRWSem)
768 {
769 /*
770 * Create the rwlock.
771 */
772 pthread_rwlockattr_t Attr;
773 rc = pthread_rwlockattr_init(&Attr);
774 if (!rc)
775 {
776 rc = pthread_rwlock_init(&pIntRWSem->RWLock, &Attr);
777 if (!rc)
778 {
779 pIntRWSem->uCheck = ~0;
780 pIntRWSem->WROwner = (pthread_t)~0;
781 *pRWSem = pIntRWSem;
782 return VINF_SUCCESS;
783 }
784 }
785
786 rc = RTErrConvertFromErrno(rc);
787 RTMemFree(pIntRWSem);
788 }
789 else
790 rc = VERR_NO_MEMORY;
791
792 return rc;
793}
794
795
796RTDECL(int) RTSemRWDestroy(RTSEMRW RWSem)
797{
798 /*
799 * Validate input.
800 */
801 if (!rtsemRWValid(RWSem))
802 {
803 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
804 return VERR_INVALID_HANDLE;
805 }
806
807 /*
808 * Try destroy it.
809 */
810 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
811 int rc = pthread_rwlock_destroy(&pIntRWSem->RWLock);
812 if (!rc)
813 {
814 pIntRWSem->uCheck = 0;
815 RTMemFree(pIntRWSem);
816 rc = VINF_SUCCESS;
817 }
818 else
819 {
820 AssertMsgFailed(("Failed to destroy read-write sem %p, rc=%d.\n", RWSem, rc));
821 rc = RTErrConvertFromErrno(rc);
822 }
823
824 return rc;
825}
826
827
828RTDECL(int) RTSemRWRequestRead(RTSEMRW RWSem, unsigned cMillies)
829{
830 /*
831 * Validate input.
832 */
833 if (!rtsemRWValid(RWSem))
834 {
835 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
836 return VERR_INVALID_HANDLE;
837 }
838
839 /*
840 * Try lock it.
841 */
842 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
843 if (cMillies == RT_INDEFINITE_WAIT)
844 {
845 /* take rwlock */
846 int rc = pthread_rwlock_rdlock(&pIntRWSem->RWLock);
847 if (rc)
848 {
849 AssertMsgFailed(("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
850 return RTErrConvertFromErrno(rc);
851 }
852 }
853 else
854 {
855 /*
856 * Get current time and calc end of wait time.
857 */
858 struct timespec ts = {0,0};
859 clock_gettime(CLOCK_REALTIME, &ts);
860 if (cMillies != 0)
861 {
862 ts.tv_nsec += (cMillies % 1000) * 1000000;
863 ts.tv_sec += cMillies / 1000;
864 if (ts.tv_nsec >= 1000000000)
865 {
866 ts.tv_nsec -= 1000000000;
867 ts.tv_sec++;
868 }
869 }
870
871 /* take rwlock */
872 int rc = pthread_rwlock_timedrdlock(&pIntRWSem->RWLock, &ts);
873 if (rc)
874 {
875 AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
876 return RTErrConvertFromErrno(rc);
877 }
878 }
879
880 return VINF_SUCCESS;
881}
882
883
884RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW RWSem, unsigned cMillies)
885{
886 /* EINTR isn't returned by the wait functions we're using. */
887 return RTSemRWRequestRead(RWSem, cMillies);
888}
889
890
891RTDECL(int) RTSemRWReleaseRead(RTSEMRW RWSem)
892{
893 /*
894 * Validate input.
895 */
896 if (!rtsemRWValid(RWSem))
897 {
898 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
899 return VERR_INVALID_HANDLE;
900 }
901
902 /*
903 * Try unlock it.
904 */
905 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
906 if (pIntRWSem->WROwner == pthread_self())
907 {
908 AssertMsgFailed(("Tried to read unlock when write owner - read-write sem %p.\n", RWSem));
909 return VERR_NOT_OWNER;
910 }
911 int rc = pthread_rwlock_unlock(&pIntRWSem->RWLock);
912 if (rc)
913 {
914 AssertMsgFailed(("Failed read unlock read-write sem %p, rc=%d.\n", RWSem, rc));
915 return RTErrConvertFromErrno(rc);
916 }
917
918 return VINF_SUCCESS;
919}
920
921
922RTDECL(int) RTSemRWRequestWrite(RTSEMRW RWSem, unsigned cMillies)
923{
924 /*
925 * Validate input.
926 */
927 if (!rtsemRWValid(RWSem))
928 {
929 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
930 return VERR_INVALID_HANDLE;
931 }
932
933 /*
934 * Try lock it.
935 */
936 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
937 if (cMillies == RT_INDEFINITE_WAIT)
938 {
939 /* take rwlock */
940 int rc = pthread_rwlock_wrlock(&pIntRWSem->RWLock);
941 if (rc)
942 {
943 AssertMsgFailed(("Failed write lock read-write sem %p, rc=%d.\n", RWSem, rc));
944 return RTErrConvertFromErrno(rc);
945 }
946 }
947 else
948 {
949 /*
950 * Get current time and calc end of wait time.
951 */
952 struct timespec ts = {0,0};
953 clock_gettime(CLOCK_REALTIME, &ts);
954 if (cMillies != 0)
955 {
956 ts.tv_nsec += (cMillies % 1000) * 1000000;
957 ts.tv_sec += cMillies / 1000;
958 if (ts.tv_nsec >= 1000000000)
959 {
960 ts.tv_nsec -= 1000000000;
961 ts.tv_sec++;
962 }
963 }
964
965 /* take rwlock */
966 int rc = pthread_rwlock_timedwrlock(&pIntRWSem->RWLock, &ts);
967 if (rc)
968 {
969 AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", RWSem, rc));
970 return RTErrConvertFromErrno(rc);
971 }
972 }
973
974 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)pthread_self());
975
976 return VINF_SUCCESS;
977}
978
979
980RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW RWSem, unsigned cMillies)
981{
982 /* EINTR isn't returned by the wait functions we're using. */
983 return RTSemRWRequestWrite(RWSem, cMillies);
984}
985
986
987RTDECL(int) RTSemRWReleaseWrite(RTSEMRW RWSem)
988{
989 /*
990 * Validate input.
991 */
992 if (!rtsemRWValid(RWSem))
993 {
994 AssertMsgFailed(("Invalid handle %p!\n", RWSem));
995 return VERR_INVALID_HANDLE;
996 }
997
998 /*
999 * Try unlock it.
1000 */
1001 pthread_t Self = pthread_self();
1002 struct RTSEMRWINTERNAL *pIntRWSem = RWSem;
1003 if (pIntRWSem->WROwner != Self)
1004 {
1005 AssertMsgFailed(("Not Write owner!\n"));
1006 return VERR_NOT_OWNER;
1007 }
1008
1009 /*
1010 * Try unlock it.
1011 */
1012 AssertMsg(sizeof(pthread_t) == sizeof(void *), ("pthread_t is not the size of a pointer but %d bytes\n", sizeof(pthread_t)));
1013 ASMAtomicXchgPtr((void * volatile *)&pIntRWSem->WROwner, (void *)(uintptr_t)~0);
1014 int rc = pthread_rwlock_unlock(&pIntRWSem->RWLock);
1015 if (rc)
1016 {
1017 AssertMsgFailed(("Failed write unlock read-write sem %p, rc=%d.\n", RWSem, rc));
1018 return RTErrConvertFromErrno(rc);
1019 }
1020
1021 return VINF_SUCCESS;
1022}
1023
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