HPCToolkit
pfq-rwlock.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 the API for a fair, phased reader-writer lock with local spinning
52 //
53 // Reference:
54 // Björn B. Brandenburg and James H. Anderson. 2010. Spin-based reader-writer
55 // synchronization for multiprocessor real-time systems. Real-Time Systems
56 // 46(1):25-87 (September 2010). http://dx.doi.org/10.1007/s11241-010-9097-2
57 //
58 // Notes:
59 // the reference uses a queue for arriving readers. on a cache coherent
60 // machine, the local spinning property for waiting readers can be achieved
61 // by simply using a cacheable flag. the implementation here uses that
62 // simplification.
63 //
64 //******************************************************************************
65 
66 
67 
68 //******************************************************************************
69 // local includes
70 //******************************************************************************
71 
72 #include "hpctoolkit-config.h"
73 #include "pfq-rwlock.h"
74 
75 //******************************************************************************
76 // macros
77 //******************************************************************************
78 
79 #define READER_INCREMENT 0x100
80 
81 #define PHASE_BIT 0x001
82 #define WRITER_PRESENT 0x002
83 
84 #define WRITER_MASK (PHASE_BIT | WRITER_PRESENT)
85 #define TICKET_MASK ~(WRITER_MASK)
86 
87 //------------------------------------------------------------------
88 // define a macro to point to the low-order byte of an integer type
89 // in a way that will work on both big-endian and little-endian
90 // processors
91 //------------------------------------------------------------------
92 #ifdef HOST_BIG_ENDIAN
93 #define LSB_PTR(p) (((unsigned char *) p) + (sizeof(*p) - 1))
94 #endif
95 
96 #ifdef HOST_LITTLE_ENDIAN
97 #define LSB_PTR(p) ((unsigned char *) p)
98 #endif
99 
100 #ifndef LSB_PTR
101 #error "endianness must be configured. " \
102  "use --enable-endian to force configuration"
103 #endif
104 
105 //******************************************************************************
106 // interface operations
107 //******************************************************************************
108 
109 void
111 {
112  atomic_init(&l->rin, 0);
113  atomic_init(&l->rout, 0);
114  atomic_init(&l->last, 0);
115  atomic_init(&l->writer_blocking_readers[0].bit, false);
116  atomic_init(&l->writer_blocking_readers[1].bit, false);
117  mcs_init(&l->wtail);
118  l->whead = mcs_nil;
119 }
120 
121 void
123 {
125 
126  if (ticket & WRITER_PRESENT) {
127  uint32_t phase = ticket & PHASE_BIT;
129  }
130 }
131 
132 
133 void
135 {
137 
138  if (ticket & WRITER_PRESENT) {
139  //----------------------------------------------------------------------------
140  // finish reading counter before reading last
141  //----------------------------------------------------------------------------
142  if (ticket == atomic_load_explicit(&l->last, memory_order_acquire))
143  atomic_store_explicit(&l->whead->blocked, false, memory_order_release);
144  }
145 }
146 
147 
148 void
150 {
151  //--------------------------------------------------------------------
152  // use MCS lock to enforce mutual exclusion with other writers
153  //--------------------------------------------------------------------
154  mcs_lock(&l->wtail, me);
155 
156  //--------------------------------------------------------------------
157  // this may be false when at the head of the mcs queue
158  //--------------------------------------------------------------------
160 
161  //--------------------------------------------------------------------
162  // announce myself as next writer
163  //--------------------------------------------------------------------
164  l->whead = me;
165 
166  //--------------------------------------------------------------------
167  // set writer_blocking_readers to block any readers in the next batch
168  //--------------------------------------------------------------------
169  uint32_t phase = atomic_load_explicit(&l->rin, memory_order_relaxed) & PHASE_BIT;
171 
172  //----------------------------------------------------------------------------
173  // store to writer_blocking_headers bit must complete before incrementing rin
174  //----------------------------------------------------------------------------
175 
176  //--------------------------------------------------------------------
177  // acquire an "in" sequence number to see how many readers arrived
178  // set the WRITER_PRESENT bit so subsequent readers will wait
179  //--------------------------------------------------------------------
181 
182  //--------------------------------------------------------------------
183  // save the ticket that the last reader will see
184  //--------------------------------------------------------------------
186 
187  //-------------------------------------------------------------
188  // update to 'last' must complete before others see changed value of rout.
189  // acquire an "out" sequence number to see how many readers left
190  // set the WRITER_PRESENT bit so the last reader will know to signal
191  // it is responsible for signaling the waiting writer
192  //-------------------------------------------------------------
194 
195  //--------------------------------------------------------------------
196  // if any reads are active, wait for last reader to signal me
197  //--------------------------------------------------------------------
198  if (in != out) {
200  // wait for active reads to drain
201 
202  //--------------------------------------------------------------------------
203  // store to writer_blocking headers bit must complete before notifying
204  // readers of writer
205  //--------------------------------------------------------------------------
206  }
207 }
208 
209 
210 void
212 {
213  //--------------------------------------------------------------------
214  // toggle phase and clear WRITER_PRESENT in rin. No synch issues
215  // since there are no concurrent updates of the low-order byte
216  //--------------------------------------------------------------------
217  unsigned char *lsb = LSB_PTR(&l->rin);
218  uint32_t phase = *lsb & PHASE_BIT;
219  *lsb ^= WRITER_MASK;
220 
221  //--------------------------------------------------------------------
222  // toggle phase and clear WRITER_PRESENT in rout. No synch issues
223  // since the low-order byte modified here isn't modified again until
224  // another writer has the mcs_lock.
225  //--------------------------------------------------------------------
226  lsb = LSB_PTR(&l->rout);
227  *lsb ^= WRITER_MASK;
228 
229  //----------------------------------------------------------------------------
230  // clearing writer present in rin can be reordered with writer_blocking_readers set below
231  // because any arriving reader will see the cleared writer_blocking_readers and proceed.
232  //----------------------------------------------------------------------------
233 
234  //--------------------------------------------------------------------
235  // clear writer_blocking_readers to release waiting readers in the current read phase
236  //--------------------------------------------------------------------
238 
239  //--------------------------------------------------------------------
240  // pass writer lock to next writer
241  //--------------------------------------------------------------------
242  mcs_unlock(&l->wtail, me);
243 }
#define READER_INCREMENT
Definition: pfq-rwlock.c:79
void mcs_lock(mcs_lock_t *l, mcs_node_t *me)
Definition: mcs-lock.c:77
#define atomic_fetch_add_explicit(object, operand, order)
Definition: stdatomic.h:366
#define mcs_nil
Definition: mcs-lock.h:93
bigbool writer_blocking_readers[2]
Definition: pfq-rwlock.h:113
#define atomic_load_explicit(object, order)
Definition: stdatomic.h:378
void pfq_rwlock_init(pfq_rwlock_t *l)
Definition: pfq-rwlock.c:110
#define atomic_store_explicit(object, desired, order)
Definition: stdatomic.h:380
void mcs_unlock(mcs_lock_t *l, mcs_node_t *me)
Definition: mcs-lock.c:146
void pfq_rwlock_write_lock(pfq_rwlock_t *l, pfq_rwlock_node_t *me)
Definition: pfq-rwlock.c:149
void pfq_rwlock_read_lock(pfq_rwlock_t *l)
Definition: pfq-rwlock.c:122
#define WRITER_MASK
Definition: pfq-rwlock.c:84
void pfq_rwlock_write_unlock(pfq_rwlock_t *l, pfq_rwlock_node_t *me)
Definition: pfq-rwlock.c:211
void pfq_rwlock_read_unlock(pfq_rwlock_t *l)
Definition: pfq-rwlock.c:134
#define atomic_init(obj, value)
Definition: stdatomic.h:133
atomic_bool blocked
Definition: mcs-lock.h:79
#define PHASE_BIT
Definition: pfq-rwlock.c:81
#define atomic_fetch_or_explicit(object, operand, order)
Definition: stdatomic.h:371
static void mcs_init(mcs_lock_t *l)
Definition: mcs-lock.h:100
#define WRITER_PRESENT
Definition: pfq-rwlock.c:82