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