@@ -120,6 +120,14 @@ class AtomicWaitQueue {
120
120
// / An RAII helper class for signalling that the current thread is a
121
121
// / worker thread which has acquired the lock.
122
122
// /
123
+ // / `AtomicWaitQueue` does not require the global lock to be held
124
+ // / while creating or publishing the queue. Clients taking advantage
125
+ // / of this should inform the Worker class that a created queue has
126
+ // / been published by calling `flagQueueIsPublished`. Clients who
127
+ // / wish to publish the queue while holding the global lock, perhaps
128
+ // / to get a rule that all stores are done under the lock, may instead
129
+ // / use `tryPublishQueue`.
130
+ // /
123
131
// / The expected use pattern is something like:
124
132
// /
125
133
// / ```
@@ -128,26 +136,47 @@ class AtomicWaitQueue {
128
136
// / while (true) {
129
137
// / if (oldStatus.isDone()) return;
130
138
// /
131
- // / // Try to publish the wait queue. If this succeeds, we've become
132
- // / // the worker thread.
133
- // / bool published = worker.tryPublishQueue([&] {
134
- // / auto newStatus = oldStatus.withLock(worker.createQueue());
135
- // / return myAtomic.compare_exchange_weak(oldStatus, newStatus,
136
- // / /*success*/ std::memory_order_release,
137
- // / /*failure*/ std::memory_order_acquire);
138
- // / });
139
- // / if (!published) continue;
139
+ // / if (oldStatus.hasWaitQueue()) {
140
+ // / bool waited = worker.tryReloadAndWait([&] {
141
+ // / oldStatus = myAtomic.load(std::memory_order_acquire);
142
+ // / return (oldStatus.hasWaitQueue() ? oldStatus.getWaitQueue()
143
+ // / : nullptr);
144
+ // / });
140
145
// /
141
- // / // Do the actual work here.
146
+ // / // If we waited, `oldStatus` will be out of date; reload it.
147
+ // / //
148
+ // / // (For the pattern in this example, where the worker thread
149
+ // / // always transitions the status to done, this is actually
150
+ // / // unnecessary: by virtue of having successfully waited, we've
151
+ // / // synchronized with the worker thread and know that the status
152
+ // / // is done, so we could just return. But in general, this
153
+ // / // reload is necessary.)
154
+ // / if (waited)
155
+ // / oldStatus = myAtomic.load(std::memory_order_acquire);
156
+ // /
157
+ // / // Go back and reconsider the updated status.
158
+ // / continue;
159
+ // / }
142
160
// /
143
- // / // "Unpublish" the queue from the the atomic.
144
- // / while (true) {
145
- // / auto newStatus = oldStatus.withDone(true);
146
- // / if (myAtomic.compare_exchange_weak(oldStatus, newStatus,
161
+ // / // Create a queue and try to publish it. If this succeeds,
162
+ // / // we've become the worker thread. We don't have to worry
163
+ // / // about the queue leaking if we don't use it; that's managed
164
+ // / // by the Worker class.
165
+ // / {
166
+ // / auto queue = worker.createQueue();
167
+ // / auto newStatus = oldStatus.withWaitQueue(queue);
168
+ // / if (!myAtomic.compare_exchange_weak(oldStatus, newStatus,
147
169
// / /*success*/ std::memory_order_release,
148
170
// / /*failure*/ std::memory_order_acquire))
149
- // / break;
171
+ // / continue;
172
+ // / worker.flagQueueIsPublished(queue);
150
173
// / }
174
+ // /
175
+ // / // Do the actual work here.
176
+ // /
177
+ // / // Report that the work is done and "unpublish" the queue from
178
+ // / // the atomic.
179
+ // / myAtomic.store(oldStatus.withDone(true), std::memory_order_release);
151
180
// / worker.finishAndUnpublishQueue([]{});
152
181
// / return;
153
182
// / }
@@ -193,6 +222,19 @@ class AtomicWaitQueue {
193
222
return Published;
194
223
}
195
224
225
+ // / Given that this thread is not the worker thread and there seems
226
+ // / to be a wait queue in place, try to wait on it.
227
+ // /
228
+ // / Acquire the global lock and call the given function. If it
229
+ // / returns a wait queue, wait on that queue and return true;
230
+ // / otherwise, return false.
231
+ template <class Fn >
232
+ bool tryReloadAndWait (Fn &&fn) {
233
+ assert (!isWorkerThread ());
234
+ typename Impl::Waiter waiter (GlobalLock);
235
+ return waiter.tryReloadAndWait (std::forward<Fn>(fn));
236
+ }
237
+
196
238
// / Given that this thread is the worker thread, return the queue
197
239
// / that's been created and published for it.
198
240
Impl *getPublishedQueue () const {
0 commit comments