@@ -120,6 +120,14 @@ class AtomicWaitQueue {
120120 // / An RAII helper class for signalling that the current thread is a
121121 // / worker thread which has acquired the lock.
122122 // /
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+ // /
123131 // / The expected use pattern is something like:
124132 // /
125133 // / ```
@@ -128,26 +136,47 @@ class AtomicWaitQueue {
128136 // / while (true) {
129137 // / if (oldStatus.isDone()) return;
130138 // /
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+ // / });
140145 // /
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+ // / }
142160 // /
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,
147169 // / /*success*/ std::memory_order_release,
148170 // / /*failure*/ std::memory_order_acquire))
149- // / break;
171+ // / continue;
172+ // / worker.flagQueueIsPublished(queue);
150173 // / }
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);
151180 // / worker.finishAndUnpublishQueue([]{});
152181 // / return;
153182 // / }
@@ -193,6 +222,19 @@ class AtomicWaitQueue {
193222 return Published;
194223 }
195224
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+
196238 // / Given that this thread is the worker thread, return the queue
197239 // / that's been created and published for it.
198240 Impl *getPublishedQueue () const {
0 commit comments