spinlock_rtm.hpp 4.7 KB

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