ReadWriteLock.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 54 Interprocess Communication */
10 
11 #include "squid.h"
12 #include "ipc/ReadWriteLock.h"
13 #include "Store.h"
14 
15 void Ipc::AssertFlagIsSet(std::atomic_flag &flag)
16 {
17  // If the flag was false, then we set it to true and assert. A true flag
18  // may help keep other processes away from this broken entry.
19  // Otherwise, we just set an already set flag, which is probably a no-op.
20  assert(flag.test_and_set(std::memory_order_relaxed));
21 }
22 
27 bool
29 {
30  assert(writeLevel); // "new" readers are locked out by the caller
31  assert(!appending); // nobody can be appending without an exclusive lock
32  if (!readLevel) { // no old readers and nobody is becoming a reader
33  writing = true;
34  return true;
35  }
36  --writeLevel;
37  return false;
38 }
39 
40 bool
42 {
43  ++readLevel; // this locks "new" writers out
44  if (!writeLevel || appending) { // nobody is writing, or sharing is OK
45  ++readers;
46  return true;
47  }
48  --readLevel;
49  return false;
50 }
51 
52 bool
54 {
55  if (!writeLevel++) // we are the first writer + lock "new" readers out
56  return finalizeExclusive(); // decrements writeLevel on failures
57 
58  --writeLevel;
59  return false;
60 }
61 
62 bool
64 {
65  if (lockShared()) {
66  if (!updating.test_and_set(std::memory_order_acquire))
67  return true; // we got here first
68  // the updating lock was already set by somebody else
69  unlockShared();
70  }
71  return false;
72 }
73 
74 void
76 {
77  assert(readers > 0);
78  --readers;
79  --readLevel;
80 }
81 
82 void
84 {
85  assert(writing);
86  appending = false;
87  writing = false;
88  --writeLevel;
89 }
90 
91 void
93 {
94  AssertFlagIsSet(updating);
95  updating.clear(std::memory_order_release);
96  unlockShared();
97 }
98 
99 void
101 {
102  assert(writing);
103  ++readLevel; // must be done before we release exclusive control
104  ++readers;
105  unlockExclusive();
106 }
107 
108 bool
110 {
111  assert(readers > 0);
112  if (!writeLevel++) { // we are the first writer + lock "new" readers out
113  unlockShared();
114  return finalizeExclusive(); // decrements writeLevel on failures
115  }
116 
117  // somebody is still writing, so we just stop reading
118  unlockShared();
119  --writeLevel;
120  return false;
121 }
122 
123 void
125 {
126  assert(writing);
127  appending = true;
128 }
129 
130 bool
132 {
133  assert(writing);
134  assert(appending);
135 
136  appending = false;
137 
138  // Checking `readers` here would mishandle a lockShared() call that started
139  // before we banned appending above, saw still true `appending`, got on a
140  // "success" code path, but had not incremented the `readers` counter yet.
141  // Checking `readLevel` mishandles lockShared() that saw false `appending`,
142  // got on a "failure" code path, but had not decremented `readLevel` yet.
143  // Our callers prefer the wrong "false" to the wrong "true" result.
144  return !readLevel;
145 }
146 
147 void
149 {
150  if (readers) {
151  ++stats.readable;
152  stats.readers += readers;
153  } else if (writing) {
154  ++stats.writeable;
155  ++stats.writers;
156  stats.appenders += appending;
157  } else {
158  ++stats.idle;
159  }
160  ++stats.count;
161 }
162 
163 /* Ipc::ReadWriteLockStats */
164 
166 {
167  memset(this, 0, sizeof(*this));
168 }
169 
170 void
172 {
173  storeAppendPrintf(&e, "Available locks: %9d\n", count);
174 
175  if (!count)
176  return;
177 
178  storeAppendPrintf(&e, "Reading: %9d %6.2f%%\n",
179  readable, (100.0 * readable / count));
180  storeAppendPrintf(&e, "Writing: %9d %6.2f%%\n",
181  writeable, (100.0 * writeable / count));
182  storeAppendPrintf(&e, "Idle: %9d %6.2f%%\n",
183  idle, (100.0 * idle / count));
184 
185  if (readers || writers) {
186  const int locked = readers + writers;
187  storeAppendPrintf(&e, "Readers: %9d %6.2f%%\n",
188  readers, (100.0 * readers / locked));
189  const double appPerc = writers ? (100.0 * appenders / writers) : 0.0;
190  storeAppendPrintf(&e, "Writers: %9d %6.2f%% including Appenders: %9d %6.2f%%\n",
191  writers, (100.0 * writers / locked),
192  appenders, appPerc);
193  }
194 }
195 
196 std::ostream &
197 Ipc::operator <<(std::ostream &os, const Ipc::ReadWriteLock &lock)
198 {
199  return os << lock.readers << 'R' <<
200  (lock.writing ? "W" : "") <<
201  (lock.appending ? "A" : "");
202  // impossible to report lock.updating without setting/clearing that flag
203 }
204 
approximate stats of a set of ReadWriteLocks
Definition: ReadWriteLock.h:70
std::atomic< bool > writing
there is a writing user (there can be at most 1)
Definition: ReadWriteLock.h:55
bool lockShared()
lock for reading or return false
bool unlockSharedAndSwitchToExclusive()
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:855
void updateStats(ReadWriteLockStats &stats) const
adds approximate current stats to the supplied ones
bool lockExclusive()
lock for modification or return false
int writers
sum of lock.writers
Definition: ReadWriteLock.h:82
std::atomic< bool > appending
the writer has promised to only append
Definition: ReadWriteLock.h:56
void unlockShared()
undo successful sharedLock()
std::atomic< uint32_t > readers
number of reading users
Definition: ReadWriteLock.h:54
void unlockExclusive()
undo successful exclusiveLock()
int readers
sum of lock.readers
Definition: ReadWriteLock.h:81
#define assert(EX)
Definition: assert.h:17
bool lockHeaders()
lock for [readable] metadata update or return false
int appenders
number of appending writers
Definition: ReadWriteLock.h:83
void dump(StoreEntry &e) const
int idle
number of unlocked locks
Definition: ReadWriteLock.h:80
void unlockHeaders()
undo successful lockHeaders()
void AssertFlagIsSet(std::atomic_flag &flag)
bool stopAppendingAndRestoreExclusive()
std::atomic< uint32_t > writeLevel
number of users writing (or trying to write)
Definition: ReadWriteLock.h:63
int writeable
number of locks locked for writing
Definition: ReadWriteLock.h:79
std::ostream & operator<<(std::ostream &os, const QuestionerId &qid)
Definition: QuestionerId.h:63
int readable
number of locks locked for reading
Definition: ReadWriteLock.h:78
void switchExclusiveToShared()
void startAppending()
writer keeps its lock but also allows reading
std::atomic< uint32_t > readLevel
number of users reading (or trying to)
Definition: ReadWriteLock.h:62
int count
the total number of locks
Definition: ReadWriteLock.h:77

 

Introduction

Documentation

Support

Miscellaneous