9 1. At any point, only one thread can acquire the mutex; when any thread
10 nlock the mutex, he must have it in his hand.
11 2. The sequence of the lock is guaranteed, which means there is a queue for
12 all the lock operations.
14 3. Should establish the happens-before relationship between unlock and lock
20 # The invariant is that the thread that has a guard at the head of the
21 # queue should be the one who currently has the lock.
23 int __lock_acquired = 0;
24 queue<guard*> __lock_waiting_queue;
27 # Without any specifying, this means the beginning of a successful Unlock()
28 # happens before the end of the next successful Lock().
34 std::atomic<mcs_node *> next;
35 std::atomic<int> gate;
45 // tail is null when lock is not held
46 std::atomic<mcs_node *> m_tail;
52 ASSERT( m_tail.load() == NULL );
55 // Each thread will have their own guard.
59 mcs_node m_node; // node held on the stack
61 guard(mcs_mutex * t) : m_t(t) { t->lock(this); }
62 ~guard() { m_t->unlock(this); }
68 # This function will soon enqueue the current guard to the queue to make
69 # sure it will get fair lock successfully.
71 @Commit_point_set: Lock_Enqueue_Point
74 __lock_waiting_queue.enqueue(I);
78 # Make sure when it successfully locks, the lock is not acquired yet
79 # and the locking is in a FIFO order
80 __lock_acquired == 1 && I == __lock_waiting_queue.peak()
83 void lock(guard * I) {
84 mcs_node * me = &(I->m_node);
87 // not published yet so relaxed :
88 me->next.store(NULL, std::mo_relaxed );
89 me->gate.store(1, std::mo_relaxed );
91 // publish my node as the new tail :
92 mcs_node * pred = m_tail.exchange(me, std::mo_acq_rel);
94 # Once the exchange completes, the thread already claimed the next
95 # available slot for the lock
97 @Commit_point_check_define: true
98 @Label: Lock_Enqueue_Point
101 if ( pred != NULL ) {
103 // unlock of pred can see me in the tail before I fill next
105 // publish me to previous lock-holder :
106 pred->next.store(me, std::mo_release );
108 // (*2) pred not touched any more
110 // now this is the spin -
111 // wait on predecessor setting my flag -
112 rl::linear_backoff bo;
114 while ( (my_gate = me->gate.load(std::mo_acquire)) ) {
124 Unlock = Unlock_Point_Success_1 | Unlock_Point_Success_2
126 lock_acquired == 1 && I == lock_waiting_queue.peak()
130 lock_waiting_queue.dequeue();
133 void unlock(guard * I) {
134 mcs_node * me = &(I->m_node);
136 mcs_node * next = me->next.load(std::mo_acquire);
139 mcs_node * tail_was_me = me;
141 if ( (success = m_tail.compare_exchange_strong(
142 tail_was_me,NULL,std::mo_acq_rel)) ) {
145 @Commit_point_check_define: __ATOMIC_RET__ == true
146 @Label: Unlock_Point_Success_1
149 // got null in tail, mutex is unlocked
153 // (*1) catch the race :
154 rl::linear_backoff bo;
156 next = me->next.load(std::mo_acquire);
163 // (*2) - store to next must be done,
164 // so no locker can be viewing my node any more
167 next->gate.store( 0, std::mo_release );
170 @Commit_point_check_define: true
171 @Label: Unlock_Point_Success_2