HPCToolkit
mcs-lock.c
Go to the documentation of this file.
1 // * BeginRiceCopyright *****************************************************
2 //
3 // $HeadURL$
4 // $Id$
5 //
6 // --------------------------------------------------------------------------
7 // Part of HPCToolkit (hpctoolkit.org)
8 //
9 // Information about sources of support for research and development of
10 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
11 // --------------------------------------------------------------------------
12 //
13 // Copyright ((c)) 2002-2019, Rice University
14 // All rights reserved.
15 //
16 // Redistribution and use in source and binary forms, with or without
17 // modification, are permitted provided that the following conditions are
18 // met:
19 //
20 // * Redistributions of source code must retain the above copyright
21 // notice, this list of conditions and the following disclaimer.
22 //
23 // * Redistributions in binary form must reproduce the above copyright
24 // notice, this list of conditions and the following disclaimer in the
25 // documentation and/or other materials provided with the distribution.
26 //
27 // * Neither the name of Rice University (RICE) nor the names of its
28 // contributors may be used to endorse or promote products derived from
29 // this software without specific prior written permission.
30 //
31 // This software is provided by RICE and contributors "as is" and any
32 // express or implied warranties, including, but not limited to, the
33 // implied warranties of merchantability and fitness for a particular
34 // purpose are disclaimed. In no event shall RICE or contributors be
35 // liable for any direct, indirect, incidental, special, exemplary, or
36 // consequential damages (including, but not limited to, procurement of
37 // substitute goods or services; loss of use, data, or profits; or
38 // business interruption) however caused and on any theory of liability,
39 // whether in contract, strict liability, or tort (including negligence
40 // or otherwise) arising in any way out of the use of this software, even
41 // if advised of the possibility of such damage.
42 //
43 // ******************************************************* EndRiceCopyright *
44 
45 //******************************************************************************
46 //
47 // File:
48 // $HeadURL$
49 //
50 // Purpose:
51 // Implement an API for the MCS lock: a fair queue-based lock.
52 //
53 // Reference:
54 // John M. Mellor-Crummey and Michael L. Scott. 1991. Algorithms for scalable
55 // synchronization on shared-memory multiprocessors. ACM Transactions on
56 // Computing Systems 9, 1 (February 1991), 21-65.
57 // http://doi.acm.org/10.1145/103727.103729
58 //******************************************************************************
59 
60 
61 
62 //******************************************************************************
63 // local includes
64 //******************************************************************************
65 
66 #include "mcs-lock.h"
67 
68 //******************************************************************************
69 // private operations
70 //******************************************************************************
71 
72 //******************************************************************************
73 // interface operations
74 //******************************************************************************
75 
76 void
78 {
79  //--------------------------------------------------------------------
80  // initialize my queue node
81  //--------------------------------------------------------------------
82  atomic_init(&me->next, mcs_nil);
83 
84  //--------------------------------------------------------------------
85  // install my node at the tail of the lock queue.
86  // determine my predecessor, if any.
87  //
88  // note: the rel aspect of the ordering below ensures that
89  // initialization of me->next completes before anyone sees my node
90  //--------------------------------------------------------------------
91  mcs_node_t *predecessor =
93 
94  //--------------------------------------------------------------------
95  // if I have a predecessor, wait until it signals me
96  //--------------------------------------------------------------------
97  if (predecessor != mcs_nil) {
98  //------------------------------------------------------------------
99  // prepare to block until signaled by my predecessor
100  //------------------------------------------------------------------
101  atomic_init(&me->blocked, true);
102 
103  //------------------------------------------------------------------
104  // link behind my predecessor
105  // note: use release to ensure that prior assignment to blocked
106  // occurs first
107  //------------------------------------------------------------------
108  atomic_store_explicit(&predecessor->next, me, memory_order_release);
109 
110  //------------------------------------------------------------------
111  // wait for my predecessor to clear my flag
112  // note: use acquire order to ensure that reads or writes in the
113  // critical section will not occur until after blocked is
114  // cleared
115  //------------------------------------------------------------------
117  }
118 }
119 
120 
121 bool
123 {
124  //--------------------------------------------------------------------
125  // initialize my queue node
126  //--------------------------------------------------------------------
128 
129  //--------------------------------------------------------------------
130  // if the tail pointer is nil, swap it with a pointer to me, which
131  // acquires the lock and installs myself at the tail of the queue.
132  // note: the acq_rel ordering ensures that
133  // (1) rel: my store of me->next above completes before the exchange
134  // (2) acq: any accesses after the exchange can't begin until after
135  // the exchange completes.
136  //--------------------------------------------------------------------
137  mcs_node_t *oldme = mcs_nil;
138  return
139  atomic_compare_exchange_strong_explicit(&l->tail, &oldme, me,
142 }
143 
144 
145 void
147 {
148  mcs_node_t *successor = atomic_load_explicit(&me->next, memory_order_acquire);
149 
150  if (successor == mcs_nil) {
151  //--------------------------------------------------------------------
152  // I don't currently have a successor, so I may be at the tail
153  //--------------------------------------------------------------------
154 
155  //--------------------------------------------------------------------
156  // if my node is at the tail of the queue, attempt to remove myself
157  // note: release order below on success guarantees that all accesses
158  // above the exchange must complete before the exchange if the
159  // exchange unlinks me from the tail of the queue
160  //--------------------------------------------------------------------
161  mcs_node_t *oldme = me;
162 
163  if (atomic_compare_exchange_strong_explicit(&l->tail, &oldme, mcs_nil,
166  //------------------------------------------------------------------
167  // I removed myself from the queue; I will never have a
168  // successor, so I'm done
169  //------------------------------------------------------------------
170  return;
171  }
172 
173  //------------------------------------------------------------------
174  // another thread is writing me->next to define itself as our successor;
175  // wait for it to finish that
176  //------------------------------------------------------------------
177  while (mcs_nil == (successor = atomic_load_explicit(&me->next, memory_order_acquire)));
178  }
179 
181 }
void mcs_lock(mcs_lock_t *l, mcs_node_t *me)
Definition: mcs-lock.c:77
#define atomic_exchange_explicit(object, desired, order)
Definition: stdatomic.h:357
#define mcs_nil
Definition: mcs-lock.h:93
#define atomic_load_explicit(object, order)
Definition: stdatomic.h:378
#define atomic_store_explicit(object, desired, order)
Definition: stdatomic.h:380
bool mcs_trylock(mcs_lock_t *l, mcs_node_t *me)
Definition: mcs-lock.c:122
void mcs_unlock(mcs_lock_t *l, mcs_node_t *me)
Definition: mcs-lock.c:146
#define atomic_compare_exchange_strong_explicit(object, expected, desired, success, failure)
Definition: stdatomic.h:335
#define atomic_init(obj, value)
Definition: stdatomic.h:133
atomic_bool blocked
Definition: mcs-lock.h:79