Futex.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. /*
  2. * Copyright 2013-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <folly/detail/Futex.h>
  17. #include <folly/ScopeGuard.h>
  18. #include <folly/hash/Hash.h>
  19. #include <folly/portability/SysSyscall.h>
  20. #include <stdint.h>
  21. #include <string.h>
  22. #include <array>
  23. #include <cerrno>
  24. #include <folly/synchronization/ParkingLot.h>
  25. #ifdef __linux__
  26. #include <linux/futex.h>
  27. #endif
  28. using namespace std::chrono;
  29. namespace folly {
  30. namespace detail {
  31. namespace {
  32. ////////////////////////////////////////////////////
  33. // native implementation using the futex() syscall
  34. #ifdef __linux__
  35. /// Certain toolchains (like Android's) don't include the full futex API in
  36. /// their headers even though they support it. Make sure we have our constants
  37. /// even if the headers don't have them.
  38. #ifndef FUTEX_WAIT_BITSET
  39. #define FUTEX_WAIT_BITSET 9
  40. #endif
  41. #ifndef FUTEX_WAKE_BITSET
  42. #define FUTEX_WAKE_BITSET 10
  43. #endif
  44. #ifndef FUTEX_PRIVATE_FLAG
  45. #define FUTEX_PRIVATE_FLAG 128
  46. #endif
  47. #ifndef FUTEX_CLOCK_REALTIME
  48. #define FUTEX_CLOCK_REALTIME 256
  49. #endif
  50. int nativeFutexWake(const void* addr, int count, uint32_t wakeMask) {
  51. int rv = syscall(
  52. __NR_futex,
  53. addr, /* addr1 */
  54. FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, /* op */
  55. count, /* val */
  56. nullptr, /* timeout */
  57. nullptr, /* addr2 */
  58. wakeMask); /* val3 */
  59. /* NOTE: we ignore errors on wake for the case of a futex
  60. guarding its own destruction, similar to this
  61. glibc bug with sem_post/sem_wait:
  62. https://sourceware.org/bugzilla/show_bug.cgi?id=12674 */
  63. if (rv < 0) {
  64. return 0;
  65. }
  66. return rv;
  67. }
  68. template <class Clock>
  69. struct timespec timeSpecFromTimePoint(time_point<Clock> absTime) {
  70. auto epoch = absTime.time_since_epoch();
  71. if (epoch.count() < 0) {
  72. // kernel timespec_valid requires non-negative seconds and nanos in [0,1G)
  73. epoch = Clock::duration::zero();
  74. }
  75. // timespec-safe seconds and nanoseconds;
  76. // chrono::{nano,}seconds are `long long int`
  77. // whereas timespec uses smaller types
  78. using time_t_seconds = duration<std::time_t, seconds::period>;
  79. using long_nanos = duration<long int, nanoseconds::period>;
  80. auto secs = duration_cast<time_t_seconds>(epoch);
  81. auto nanos = duration_cast<long_nanos>(epoch - secs);
  82. struct timespec result = {secs.count(), nanos.count()};
  83. return result;
  84. }
  85. FutexResult nativeFutexWaitImpl(
  86. const void* addr,
  87. uint32_t expected,
  88. system_clock::time_point const* absSystemTime,
  89. steady_clock::time_point const* absSteadyTime,
  90. uint32_t waitMask) {
  91. assert(absSystemTime == nullptr || absSteadyTime == nullptr);
  92. int op = FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG;
  93. struct timespec ts;
  94. struct timespec* timeout = nullptr;
  95. if (absSystemTime != nullptr) {
  96. op |= FUTEX_CLOCK_REALTIME;
  97. ts = timeSpecFromTimePoint(*absSystemTime);
  98. timeout = &ts;
  99. } else if (absSteadyTime != nullptr) {
  100. ts = timeSpecFromTimePoint(*absSteadyTime);
  101. timeout = &ts;
  102. }
  103. // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET requires an absolute timeout
  104. // value - http://locklessinc.com/articles/futex_cheat_sheet/
  105. int rv = syscall(
  106. __NR_futex,
  107. addr, /* addr1 */
  108. op, /* op */
  109. expected, /* val */
  110. timeout, /* timeout */
  111. nullptr, /* addr2 */
  112. waitMask); /* val3 */
  113. if (rv == 0) {
  114. return FutexResult::AWOKEN;
  115. } else {
  116. switch (errno) {
  117. case ETIMEDOUT:
  118. assert(timeout != nullptr);
  119. return FutexResult::TIMEDOUT;
  120. case EINTR:
  121. return FutexResult::INTERRUPTED;
  122. case EWOULDBLOCK:
  123. return FutexResult::VALUE_CHANGED;
  124. default:
  125. assert(false);
  126. // EINVAL, EACCESS, or EFAULT. EINVAL means there was an invalid
  127. // op (should be impossible) or an invalid timeout (should have
  128. // been sanitized by timeSpecFromTimePoint). EACCESS or EFAULT
  129. // means *addr points to invalid memory, which is unlikely because
  130. // the caller should have segfaulted already. We can either
  131. // crash, or return a value that lets the process continue for
  132. // a bit. We choose the latter. VALUE_CHANGED probably turns the
  133. // caller into a spin lock.
  134. return FutexResult::VALUE_CHANGED;
  135. }
  136. }
  137. }
  138. #endif // __linux__
  139. ///////////////////////////////////////////////////////
  140. // compatibility implementation using standard C++ API
  141. using Lot = ParkingLot<uint32_t>;
  142. Lot parkingLot;
  143. int emulatedFutexWake(const void* addr, int count, uint32_t waitMask) {
  144. int woken = 0;
  145. parkingLot.unpark(addr, [&](const uint32_t& mask) {
  146. if ((mask & waitMask) == 0) {
  147. return UnparkControl::RetainContinue;
  148. }
  149. assert(count > 0);
  150. count--;
  151. woken++;
  152. return count > 0 ? UnparkControl::RemoveContinue
  153. : UnparkControl::RemoveBreak;
  154. });
  155. return woken;
  156. }
  157. template <typename F>
  158. FutexResult emulatedFutexWaitImpl(
  159. F* futex,
  160. uint32_t expected,
  161. system_clock::time_point const* absSystemTime,
  162. steady_clock::time_point const* absSteadyTime,
  163. uint32_t waitMask) {
  164. static_assert(
  165. std::is_same<F, const Futex<std::atomic>>::value ||
  166. std::is_same<F, const Futex<EmulatedFutexAtomic>>::value,
  167. "Type F must be either Futex<std::atomic> or Futex<EmulatedFutexAtomic>");
  168. ParkResult res;
  169. if (absSystemTime) {
  170. res = parkingLot.park_until(
  171. futex,
  172. waitMask,
  173. [&] { return *futex == expected; },
  174. [] {},
  175. *absSystemTime);
  176. } else if (absSteadyTime) {
  177. res = parkingLot.park_until(
  178. futex,
  179. waitMask,
  180. [&] { return *futex == expected; },
  181. [] {},
  182. *absSteadyTime);
  183. } else {
  184. res = parkingLot.park(
  185. futex, waitMask, [&] { return *futex == expected; }, [] {});
  186. }
  187. switch (res) {
  188. case ParkResult::Skip:
  189. return FutexResult::VALUE_CHANGED;
  190. case ParkResult::Unpark:
  191. return FutexResult::AWOKEN;
  192. case ParkResult::Timeout:
  193. return FutexResult::TIMEDOUT;
  194. }
  195. return FutexResult::INTERRUPTED;
  196. }
  197. } // namespace
  198. /////////////////////////////////
  199. // Futex<> overloads
  200. int futexWakeImpl(
  201. const Futex<std::atomic>* futex,
  202. int count,
  203. uint32_t wakeMask) {
  204. #ifdef __linux__
  205. return nativeFutexWake(futex, count, wakeMask);
  206. #else
  207. return emulatedFutexWake(futex, count, wakeMask);
  208. #endif
  209. }
  210. int futexWakeImpl(
  211. const Futex<EmulatedFutexAtomic>* futex,
  212. int count,
  213. uint32_t wakeMask) {
  214. return emulatedFutexWake(futex, count, wakeMask);
  215. }
  216. FutexResult futexWaitImpl(
  217. const Futex<std::atomic>* futex,
  218. uint32_t expected,
  219. system_clock::time_point const* absSystemTime,
  220. steady_clock::time_point const* absSteadyTime,
  221. uint32_t waitMask) {
  222. #ifdef __linux__
  223. return nativeFutexWaitImpl(
  224. futex, expected, absSystemTime, absSteadyTime, waitMask);
  225. #else
  226. return emulatedFutexWaitImpl(
  227. futex, expected, absSystemTime, absSteadyTime, waitMask);
  228. #endif
  229. }
  230. FutexResult futexWaitImpl(
  231. const Futex<EmulatedFutexAtomic>* futex,
  232. uint32_t expected,
  233. system_clock::time_point const* absSystemTime,
  234. steady_clock::time_point const* absSteadyTime,
  235. uint32_t waitMask) {
  236. return emulatedFutexWaitImpl(
  237. futex, expected, absSystemTime, absSteadyTime, waitMask);
  238. }
  239. } // namespace detail
  240. } // namespace folly