wait_ops_darwin_ulock.hpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /*
  2. * Distributed under the Boost Software License, Version 1.0.
  3. * (See accompanying file LICENSE_1_0.txt or copy at
  4. * http://www.boost.org/LICENSE_1_0.txt)
  5. *
  6. * Copyright (c) 2021-2025 Andrey Semashev
  7. */
  8. /*!
  9. * \file atomic/detail/wait_ops_darwin_ulock.hpp
  10. *
  11. * This header contains implementation of the waiting/notifying atomic operations based on Darwin systems using ulock syscalls.
  12. *
  13. * https://github.com/apple/darwin-xnu/blob/master/bsd/sys/ulock.h
  14. * https://github.com/apple/darwin-xnu/blob/master/bsd/kern/sys_ulock.c
  15. */
  16. #ifndef BOOST_ATOMIC_DETAIL_WAIT_OPS_DARWIN_ULOCK_HPP_INCLUDED_
  17. #define BOOST_ATOMIC_DETAIL_WAIT_OPS_DARWIN_ULOCK_HPP_INCLUDED_
  18. #include <stdint.h>
  19. #include <cerrno>
  20. #include <chrono>
  21. #include <boost/memory_order.hpp>
  22. #include <boost/atomic/detail/config.hpp>
  23. #include <boost/atomic/detail/chrono.hpp>
  24. #include <boost/atomic/detail/wait_capabilities.hpp>
  25. #include <boost/atomic/detail/wait_operations_fwd.hpp>
  26. #include <boost/atomic/detail/header.hpp>
  27. #ifdef BOOST_HAS_PRAGMA_ONCE
  28. #pragma once
  29. #endif
  30. namespace boost {
  31. namespace atomics {
  32. namespace detail {
  33. extern "C" {
  34. // Timeout is in microseconds with zero meaning no timeout
  35. int __ulock_wait(std::uint32_t operation, void* addr, std::uint64_t value, std::uint32_t timeout);
  36. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_WAIT2)
  37. // Timeout is in nanoseconds with zero meaning no timeout
  38. int __ulock_wait2(std::uint32_t operation, void* addr, std::uint64_t value, std::uint64_t timeout, std::uint64_t value2);
  39. #endif // defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_WAIT2)
  40. int __ulock_wake(std::uint32_t operation, void* addr, std::uint64_t wake_value);
  41. } // extern "C"
  42. enum ulock_op
  43. {
  44. ulock_op_compare_and_wait = 1,
  45. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_SHARED)
  46. ulock_op_compare_and_wait_shared = 3,
  47. #endif // defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_SHARED)
  48. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK64)
  49. ulock_op_compare_and_wait64 = 5,
  50. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_SHARED)
  51. ulock_op_compare_and_wait64_shared = 6,
  52. #endif // defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_SHARED)
  53. #endif // defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK64)
  54. // Flags for __ulock_wake
  55. ulock_flag_wake_all = 0x00000100,
  56. // Generic flags
  57. ulock_flag_no_errno = 0x01000000
  58. };
  59. template< typename Base, std::uint32_t Opcode >
  60. struct wait_operations_darwin_ulock_common :
  61. public Base
  62. {
  63. using base_type = Base;
  64. using storage_type = typename base_type::storage_type;
  65. static constexpr bool always_has_native_wait_notify = true;
  66. static BOOST_FORCEINLINE bool has_native_wait_notify(storage_type const volatile&) noexcept
  67. {
  68. return true;
  69. }
  70. static BOOST_FORCEINLINE storage_type wait(storage_type const volatile& storage, storage_type old_val, memory_order order) noexcept
  71. {
  72. storage_type new_val = base_type::load(storage, order);
  73. while (new_val == old_val)
  74. {
  75. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_WAIT2)
  76. __ulock_wait2(Opcode | ulock_flag_no_errno, const_cast< storage_type* >(&storage), old_val, 0u, 0u);
  77. #else
  78. __ulock_wait(Opcode | ulock_flag_no_errno, const_cast< storage_type* >(&storage), old_val, 0u);
  79. #endif
  80. new_val = base_type::load(storage, order);
  81. }
  82. return new_val;
  83. }
  84. private:
  85. template< typename Clock >
  86. static BOOST_FORCEINLINE storage_type wait_until_impl
  87. (
  88. storage_type const volatile& storage,
  89. storage_type old_val,
  90. typename Clock::time_point timeout,
  91. typename Clock::time_point now,
  92. memory_order order,
  93. bool& timed_out
  94. ) noexcept(noexcept(Clock::now()))
  95. {
  96. storage_type new_val = base_type::load(storage, order);
  97. while (new_val == old_val)
  98. {
  99. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_WAIT2)
  100. const std::int64_t rel_timeout = atomics::detail::chrono::ceil< std::chrono::nanoseconds >(timeout - now).count();
  101. #else
  102. const std::int64_t rel_timeout = atomics::detail::chrono::ceil< std::chrono::microseconds >(timeout - now).count();
  103. #endif
  104. if (rel_timeout <= 0)
  105. {
  106. timed_out = true;
  107. break;
  108. }
  109. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_WAIT2)
  110. __ulock_wait2(Opcode | ulock_flag_no_errno, const_cast< storage_type* >(&storage), old_val, static_cast< std::uint64_t >(rel_timeout), 0u);
  111. #else
  112. __ulock_wait
  113. (
  114. Opcode | ulock_flag_no_errno,
  115. const_cast< storage_type* >(&storage),
  116. old_val,
  117. rel_timeout <= static_cast< std::int64_t >(~static_cast< std::uint32_t >(0u)) ? static_cast< std::uint32_t >(rel_timeout) : ~static_cast< std::uint32_t >(0u)
  118. );
  119. #endif
  120. now = Clock::now();
  121. new_val = base_type::load(storage, order);
  122. }
  123. return new_val;
  124. }
  125. public:
  126. template< typename Clock, typename Duration >
  127. static BOOST_FORCEINLINE storage_type wait_until
  128. (
  129. storage_type const volatile& storage,
  130. storage_type old_val,
  131. std::chrono::time_point< Clock, Duration > timeout,
  132. memory_order order,
  133. bool& timed_out
  134. ) noexcept(noexcept(Clock::now()))
  135. {
  136. return wait_until_impl< Clock >(storage, old_val, timeout, Clock::now(), order, timed_out);
  137. }
  138. template< typename Rep, typename Period >
  139. static BOOST_FORCEINLINE storage_type wait_for
  140. (
  141. storage_type const volatile& storage,
  142. storage_type old_val,
  143. std::chrono::duration< Rep, Period > timeout,
  144. memory_order order,
  145. bool& timed_out
  146. ) noexcept
  147. {
  148. const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
  149. return wait_until_impl< std::chrono::steady_clock >(storage, old_val, now + timeout, now, order, timed_out);
  150. }
  151. static BOOST_FORCEINLINE void notify_one(storage_type volatile& storage) noexcept
  152. {
  153. while (true)
  154. {
  155. const int res = __ulock_wake(Opcode | ulock_flag_no_errno, const_cast< storage_type* >(&storage), 0u);
  156. if (BOOST_LIKELY(res != -EINTR))
  157. break;
  158. }
  159. }
  160. static BOOST_FORCEINLINE void notify_all(storage_type volatile& storage) noexcept
  161. {
  162. while (true)
  163. {
  164. const int res = __ulock_wake(Opcode | ulock_flag_wake_all | ulock_flag_no_errno, const_cast< storage_type* >(&storage), 0u);
  165. if (BOOST_LIKELY(res != -EINTR))
  166. break;
  167. }
  168. }
  169. };
  170. template< typename Base >
  171. struct wait_operations< Base, sizeof(std::uint32_t), true, false > :
  172. public wait_operations_darwin_ulock_common< Base, ulock_op_compare_and_wait >
  173. {
  174. };
  175. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_SHARED)
  176. template< typename Base >
  177. struct wait_operations< Base, sizeof(std::uint32_t), true, true > :
  178. public wait_operations_darwin_ulock_common< Base, ulock_op_compare_and_wait_shared >
  179. {
  180. };
  181. #endif // defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_SHARED)
  182. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK64)
  183. template< typename Base >
  184. struct wait_operations< Base, sizeof(std::uint64_t), true, false > :
  185. public wait_operations_darwin_ulock_common< Base, ulock_op_compare_and_wait64 >
  186. {
  187. };
  188. #if defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_SHARED)
  189. template< typename Base >
  190. struct wait_operations< Base, sizeof(std::uint64_t), true, true > :
  191. public wait_operations_darwin_ulock_common< Base, ulock_op_compare_and_wait64_shared >
  192. {
  193. };
  194. #endif // defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK_SHARED)
  195. #endif // defined(BOOST_ATOMIC_DETAIL_HAS_DARWIN_ULOCK64)
  196. } // namespace detail
  197. } // namespace atomics
  198. } // namespace boost
  199. #include <boost/atomic/detail/footer.hpp>
  200. #endif // BOOST_ATOMIC_DETAIL_WAIT_OPS_DARWIN_ULOCK_HPP_INCLUDED_