db_mutex.c 5.4 KB

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