db_mutex.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /* Copyright (C) 2014 Stony Brook University
  2. This file is part of Graphene Library OS.
  3. Graphene Library OS is free software: you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public License
  5. as published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. Graphene Library OS is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. /*
  14. * db_mutex.c
  15. *
  16. * This file contains APIs that provide operations of (futex based) mutexes.
  17. * Based on "Mutexes and Condition Variables using Futexes"
  18. * (http://locklessinc.com/articles/mutex_cv_futex)
  19. */
  20. #include <asm/errno.h>
  21. #include <atomic.h>
  22. #include <limits.h>
  23. #include <linux/futex.h>
  24. #include <linux/time.h>
  25. #include <unistd.h>
  26. #include "api.h"
  27. #include "pal.h"
  28. #include "pal_defs.h"
  29. #include "pal_error.h"
  30. #include "pal_internal.h"
  31. #include "pal_linux.h"
  32. #include "pal_linux_defs.h"
  33. #ifdef __x86_64__
  34. #include <unistd.h>
  35. #endif
  36. #define MUTEX_SPINLOCK_TIMES 100
  37. #define MUTEX_UNLOCKED 0
  38. #define MUTEX_LOCKED 1
  39. /* Interplay between locked and nwaiters:
  40. *
  41. * If lock is unlocked and uncontended, just set the locked state.
  42. *
  43. * Important possible interleavings of lock and unlock:
  44. *
  45. * Case 1:
  46. *
  47. * Owner: Locker:
  48. * Try lock and fail; increment nwaiters; sleep
  49. * Set state to unlocked
  50. * Read nwaiters; wake
  51. * Try again and succeed.
  52. *
  53. * ***************************************************
  54. *
  55. * Case 2:
  56. *
  57. * Owner: Locker:
  58. * Try lock and fail
  59. * Set state to unlocked
  60. * Read nwaiters (=0)
  61. * Increment nwaiters.
  62. * Can't go to sleep here; will cmpxchg locked and succeed
  63. * Don't wake anyone
  64. */
  65. int _DkMutexCreate(PAL_HANDLE* handle, int initialCount) {
  66. PAL_HANDLE mut = malloc(HANDLE_SIZE(mutex));
  67. SET_HANDLE_TYPE(mut, mutex);
  68. atomic_set(&mut->mutex.mut.nwaiters, 0);
  69. mut->mutex.mut.locked = initialCount;
  70. *handle = mut;
  71. return 0;
  72. }
  73. int _DkMutexLockTimeout(struct mutex_handle* m, int64_t timeout_us) {
  74. int i, ret = 0;
  75. #ifdef DEBUG_MUTEX
  76. int tid = INLINE_SYSCALL(gettid, 0);
  77. #endif
  78. /* If this is a trylock-style call, break more quickly. */
  79. int iterations = (timeout_us == 0) ? 1 : MUTEX_SPINLOCK_TIMES;
  80. /* Spin and try to take lock. Ignore any contribution this makes toward
  81. * the timeout.*/
  82. for (i = 0; i < iterations; i++) {
  83. if (MUTEX_UNLOCKED == cmpxchg(&m->locked, MUTEX_UNLOCKED, MUTEX_LOCKED))
  84. goto success;
  85. CPU_RELAX();
  86. }
  87. if (timeout_us == 0) {
  88. ret = -PAL_ERROR_TRYAGAIN;
  89. goto out;
  90. }
  91. // Bump up the waiters count; we are probably going to block
  92. atomic_inc(&m->nwaiters);
  93. while (MUTEX_LOCKED == cmpxchg(&m->locked, MUTEX_UNLOCKED, MUTEX_LOCKED)) {
  94. struct timespec waittime, *waittimep = NULL;
  95. if (timeout_us >= 0) {
  96. int64_t sec = timeout_us / 1000000;
  97. int64_t microsec = timeout_us - (sec * 1000000);
  98. waittime.tv_sec = sec;
  99. waittime.tv_nsec = microsec * 1000;
  100. waittimep = &waittime;
  101. }
  102. ret = INLINE_SYSCALL(futex, 6, m, FUTEX_WAIT, MUTEX_LOCKED, waittimep, NULL, 0);
  103. if (IS_ERR(ret)) {
  104. if (ERRNO(ret) == EWOULDBLOCK) {
  105. if (timeout_us >= 0) {
  106. ret = -PAL_ERROR_TRYAGAIN;
  107. atomic_dec(&m->nwaiters);
  108. goto out;
  109. }
  110. } else {
  111. #ifdef DEBUG_MUTEX
  112. printf("futex failed (err = %d)\n", ERRNO(ret));
  113. #endif
  114. ret = unix_to_pal_error(ERRNO(ret));
  115. atomic_dec(&m->nwaiters);
  116. goto out;
  117. }
  118. }
  119. }
  120. atomic_dec(&m->nwaiters);
  121. success:
  122. #ifdef DEBUG_MUTEX
  123. m->owner = tid;
  124. #endif
  125. ret = 0;
  126. out:
  127. #ifdef DEBUG_MUTEX
  128. if (ret < 0)
  129. printf("mutex failed (%s, tid = %d)\n", PAL_STRERROR(ret), tid);
  130. #endif
  131. return ret;
  132. }
  133. int _DkMutexLock(struct mutex_handle* m) {
  134. return _DkMutexLockTimeout(m, -1);
  135. }
  136. int _DkMutexAcquireTimeout(PAL_HANDLE handle, int64_t timeout_us) {
  137. return _DkMutexLockTimeout(&handle->mutex.mut, timeout_us);
  138. }
  139. int _DkMutexUnlock(struct mutex_handle* m) {
  140. int ret = 0;
  141. int need_wake;
  142. #ifdef DEBUG_MUTEX
  143. m->owner = 0;
  144. #endif
  145. /* Unlock */
  146. m->locked = 0;
  147. /* We need to make sure the write to locked is visible to lock-ers
  148. * before we read the waiter count. */
  149. MB();
  150. need_wake = atomic_read(&m->nwaiters);
  151. /* If we need to wake someone up... */
  152. if (need_wake)
  153. INLINE_SYSCALL(futex, 6, m, FUTEX_WAKE, 1, NULL, NULL, 0);
  154. return ret;
  155. }
  156. void _DkMutexRelease(PAL_HANDLE handle) {
  157. _DkMutexUnlock(&handle->mutex.mut);
  158. return;
  159. }
  160. bool _DkMutexIsLocked(struct mutex_handle* m) {
  161. if (!m->locked) {
  162. return false;
  163. }
  164. #ifdef DEBUG_MUTEX
  165. if (m->owner != INLINE_SYSCALL(gettid, 0)) {
  166. return false;
  167. }
  168. #endif
  169. return true;
  170. }
  171. void _DkInternalLock(PAL_LOCK* lock) {
  172. // Retry the lock if being interrupted by signals
  173. while (_DkMutexLock(lock) < 0)
  174. ;
  175. }
  176. void _DkInternalUnlock(PAL_LOCK* lock) {
  177. _DkMutexUnlock(lock);
  178. }
  179. bool _DkInternalIsLocked(PAL_LOCK* lock) {
  180. return _DkMutexIsLocked(lock);
  181. }
  182. static int mutex_wait(PAL_HANDLE handle, int64_t timeout_us) {
  183. return _DkMutexAcquireTimeout(handle, timeout_us);
  184. }
  185. struct handle_ops mutex_ops = {
  186. .wait = &mutex_wait,
  187. };