spinlock_rtm.hpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // Copyright Oliver Kowalke 2017.
  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. #ifndef BOOST_FIBERS_SPINLOCK_RTM_H
  6. #define BOOST_FIBERS_SPINLOCK_RTM_H
  7. #include <algorithm>
  8. #include <atomic>
  9. #include <chrono>
  10. #include <cmath>
  11. #include <random>
  12. #include <thread>
  13. #include <boost/fiber/detail/config.hpp>
  14. #include <boost/fiber/detail/cpu_relax.hpp>
  15. #include <boost/fiber/detail/rtm.hpp>
  16. #include <boost/fiber/detail/spinlock_status.hpp>
  17. namespace boost {
  18. namespace fibers {
  19. namespace detail {
  20. template< typename FBSplk >
  21. class spinlock_rtm {
  22. private:
  23. FBSplk splk_{};
  24. public:
  25. spinlock_rtm() = default;
  26. spinlock_rtm( spinlock_rtm const&) = delete;
  27. spinlock_rtm & operator=( spinlock_rtm const&) = delete;
  28. void lock() noexcept {
  29. static thread_local std::minstd_rand generator{ std::random_device{}() };
  30. std::size_t collisions = 0 ;
  31. for ( std::size_t retries = 0; retries < BOOST_FIBERS_RETRY_THRESHOLD; ++retries) {
  32. std::uint32_t status;
  33. if ( rtm_status::success == ( status = rtm_begin() ) ) {
  34. // add lock to read-set
  35. if ( spinlock_status::unlocked == splk_.state_.load( std::memory_order_relaxed) ) {
  36. // lock is free, enter critical section
  37. return;
  38. }
  39. // lock was acquired by another thread
  40. // explicit abort of transaction with abort argument 'lock not free'
  41. rtm_abort_lock_not_free();
  42. }
  43. // transaction aborted
  44. if ( rtm_status::none != (status & rtm_status::may_retry) ||
  45. rtm_status::none != (status & rtm_status::memory_conflict) ) {
  46. // another logical processor conflicted with a memory address that was
  47. // part or the read-/write-set
  48. if ( BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD > collisions) {
  49. std::uniform_int_distribution< std::size_t > distribution{
  50. 0, static_cast< std::size_t >( 1) << (std::min)(collisions, static_cast< std::size_t >( BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD)) };
  51. const std::size_t z = distribution( generator);
  52. ++collisions;
  53. for ( std::size_t i = 0; i < z; ++i) {
  54. cpu_relax();
  55. }
  56. } else {
  57. std::this_thread::yield();
  58. }
  59. } else if ( rtm_status::none != (status & rtm_status::explicit_abort) &&
  60. rtm_status::none == (status & rtm_status::nested_abort) ) {
  61. // another logical processor has acquired the lock and
  62. // abort was not caused by a nested transaction
  63. // wait till lock becomes free again
  64. std::size_t count = 0;
  65. while ( spinlock_status::locked == splk_.state_.load( std::memory_order_relaxed) ) {
  66. if ( BOOST_FIBERS_SPIN_BEFORE_SLEEP0 > count) {
  67. ++count;
  68. cpu_relax();
  69. } else if ( BOOST_FIBERS_SPIN_BEFORE_YIELD > count) {
  70. ++count;
  71. static constexpr std::chrono::microseconds us0{ 0 };
  72. std::this_thread::sleep_for( us0);
  73. #if 0
  74. using namespace std::chrono_literals;
  75. std::this_thread::sleep_for( 0ms);
  76. #endif
  77. } else {
  78. std::this_thread::yield();
  79. }
  80. }
  81. } else {
  82. // transaction aborted due:
  83. // - internal buffer to track transactional state overflowed
  84. // - debug exception or breakpoint exception was hit
  85. // - abort during execution of nested transactions (max nesting limit exceeded)
  86. // -> use fallback path
  87. break;
  88. }
  89. }
  90. splk_.lock();
  91. }
  92. bool try_lock() noexcept {
  93. if ( rtm_status::success != rtm_begin() ) {
  94. return false;
  95. }
  96. // add lock to read-set
  97. if ( spinlock_status::unlocked != splk_.state_.load( std::memory_order_relaxed) ) {
  98. // lock was acquired by another thread
  99. // explicit abort of transaction with abort argument 'lock not free'
  100. rtm_abort_lock_not_free();
  101. }
  102. return true;
  103. }
  104. void unlock() noexcept {
  105. if ( spinlock_status::unlocked == splk_.state_.load( std::memory_order_acquire) ) {
  106. rtm_end();
  107. } else {
  108. splk_.unlock();
  109. }
  110. }
  111. };
  112. }}}
  113. #endif // BOOST_FIBERS_SPINLOCK_RTM_H