7 std::atomic<mcs_node *> next;
19 // tail is null when lock is not held
20 std::atomic<mcs_node *> m_tail;
31 ASSERT( m_tail.load() == NULL );
34 // Each thread will have their own guard.
38 mcs_node m_node; // node held on the stack
40 guard(mcs_mutex * t) : m_t(t) { t->lock(this); }
41 ~guard() { m_t->unlock(this); }
53 _lock_acquired = false;
62 @Commit_point_set: Lock_Enqueue_Point1 | Lock_Enqueue_Point2
64 _lock_acquired == false;
66 _lock_acquired = true;
69 void lock(guard * I) {
70 mcs_node * me = &(I->m_node);
73 // not published yet so relaxed :
74 me->next.store(NULL, std::mo_relaxed );
75 me->gate.store(1, std::mo_relaxed );
77 // publish my node as the new tail :
78 mcs_node * pred = m_tail.exchange(me, std::mo_acq_rel);
81 @Commit_point_define_check: pred == NULL
82 @Label: Lock_Enqueue_Point1
87 // unlock of pred can see me in the tail before I fill next
89 // publish me to previous lock-holder :
90 pred->next.store(me, std::mo_release );
92 // (*2) pred not touched any more
94 // now this is the spin -
95 // wait on predecessor setting my flag -
96 rl::linear_backoff bo;
99 my_gate = me->gate.load(std::mo_acquire);
102 @Commit_point_define_check: my_gate == 0
103 @Label: Lock_Enqueue_Point2
115 Unlock_Point_Success_1 | Unlock_Point_Success_2
117 _lock_acquired == true
119 _lock_acquired = false;
122 void unlock(guard * I) {
123 mcs_node * me = &(I->m_node);
125 mcs_node * next = me->next.load(std::mo_acquire);
128 mcs_node * tail_was_me = me;
130 success = m_tail.compare_exchange_strong(
131 tail_was_me,NULL,std::mo_acq_rel);
134 @Commit_point_define_check: success == true
135 @Label: Unlock_Point_Success_1
140 // got null in tail, mutex is unlocked
144 // (*1) catch the race :
145 rl::linear_backoff bo;
147 next = me->next.load(std::mo_acquire);
154 // (*2) - store to next must be done,
155 // so no locker can be viewing my node any more
158 next->gate.store( 0, std::mo_release );
161 @Commit_point_define_check: true
162 @Label: Unlock_Point_Success_2