4 std::atomic<mcs_node *> next;
15 // tail is null when lock is not held
16 std::atomic<mcs_node *> m_tail;
19 m_tail($).store( NULL );
22 ASSERT( m_tail($).load() == NULL );
28 mcs_node m_node; // node held on the stack
30 guard(mcs_mutex * t) : m_t(t) { t->lock(this); }
31 ~guard() { m_t->unlock(this); }
34 void lock(guard * I) {
35 mcs_node * me = &(I->m_node);
38 // not published yet so relaxed :
39 me->next($).store(NULL, std::mo_relaxed );
40 me->gate($).store(1, std::mo_relaxed );
42 // publish my node as the new tail :
43 mcs_node * pred = m_tail($).exchange(me, std::mo_acq_rel);
46 // unlock of pred can see me in the tail before I fill next
48 // publish me to previous lock-holder :
49 pred->next($).store(me, std::mo_release );
51 // (*2) pred not touched any more
53 // now this is the spin -
54 // wait on predecessor setting my flag -
55 rl::linear_backoff bo;
56 while ( me->gate($).load(std::mo_acquire) ) {
62 void unlock(guard * I) {
63 mcs_node * me = &(I->m_node);
65 mcs_node * next = me->next($).load(std::mo_acquire);
68 mcs_node * tail_was_me = me;
69 if ( m_tail($).compare_exchange_strong( tail_was_me,NULL,std::mo_acq_rel) ) {
70 // got null in tail, mutex is unlocked
74 // (*1) catch the race :
75 rl::linear_backoff bo;
77 next = me->next($).load(std::mo_acquire);
84 // (*2) - store to next must be done,
85 // so no locker can be viewing my node any more
88 next->gate($).store( 0, std::mo_release );