Skip to content

Commit e546421

Browse files
committed
testing_hooks.cc: make callbacks synchronous, for simplicity
1 parent 8cf3e8e commit e546421

File tree

2 files changed

+32
-11
lines changed

2 files changed

+32
-11
lines changed

Firestore/core/src/util/testing_hooks.cc

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616

1717
#include "Firestore/core/src/util/testing_hooks.h"
1818

19-
#include <future>
19+
#include <functional>
2020
#include <mutex>
2121
#include <type_traits>
2222
#include <unordered_map>
23+
#include <utility>
2324

2425
#include "Firestore/core/src/util/no_destructor.h"
2526

@@ -88,10 +89,27 @@ TestingHooks::OnExistenceFilterMismatch(
8889

8990
void TestingHooks::NotifyOnExistenceFilterMismatch(
9091
const ExistenceFilterMismatchInfo& info) {
91-
std::lock_guard<std::mutex> lock(mutex_);
92+
std::unique_lock<std::mutex> lock(mutex_);
93+
94+
// Short-circuit to avoid any unnecessary work if there is nothing to do.
95+
if (existence_filter_mismatch_callbacks_.empty()) {
96+
return;
97+
}
98+
99+
// Copy the callbacks into a vector so that they can be invoked after
100+
// releasing the lock.
101+
std::vector<std::shared_ptr<ExistenceFilterMismatchCallback>> callbacks;
92102
for (auto&& entry : existence_filter_mismatch_callbacks_) {
93-
std::async(
94-
[info, callback = entry.second]() { callback->operator()(info); });
103+
callbacks.push_back(entry.second);
104+
}
105+
106+
// Release the lock so that the callback invocations are done _without_
107+
// holding the lock. This avoids deadlock in the case that invocations are
108+
// re-entrant.
109+
lock.unlock();
110+
111+
for (std::shared_ptr<ExistenceFilterMismatchCallback> callback : callbacks) {
112+
callback->operator()(info);
95113
}
96114
}
97115

Firestore/core/src/util/testing_hooks.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ class TestingHooks final {
5959
* rely on any particular ordering. If a given callback is registered multiple
6060
* times then it will be notified multiple times, once per registration.
6161
*
62-
* The thread on which the callback occurs is unspecified; listeners should
63-
* perform their work as quickly as possible and return to avoid blocking any
64-
* critical work. In particular, the listener callbacks should *not* block or
65-
* perform long-running operations. Listener callbacks can occur concurrently
66-
* with other callbacks on the same and other listeners.
62+
* The listener callbacks are performed synchronously in
63+
* `NotifyOnExistenceFilterMismatch()`; therefore, listeners should perform
64+
* their work as quickly as possible and return to avoid blocking any critical
65+
* work. In particular, the listener callbacks should *not* block or perform
66+
* long-running operations.
6767
*
6868
* The `ExistenceFilterMismatchInfo` reference specified to the callback is
6969
* only valid during the lifetime of the callback. Once the callback returns
@@ -74,13 +74,16 @@ class TestingHooks final {
7474
*
7575
* @return an object whose `Remove()` member function unregisters the given
7676
* callback; only the first invocation of `Remove()` does anything; all
77-
* subsequent invocations do nothing.
77+
* subsequent invocations do nothing. Note that due to inherent race
78+
* conditions it is technically possible, although unlikely, that callbacks
79+
* could still occur _after_ unregistering.
7880
*/
7981
std::shared_ptr<api::ListenerRegistration> OnExistenceFilterMismatch(
8082
ExistenceFilterMismatchCallback);
8183

8284
/**
83-
* Invokes all currently-registered `OnExistenceFilterMismatch` callbacks.
85+
* Invokes all currently-registered `OnExistenceFilterMismatch` callbacks
86+
* synchronously.
8487
* @param info Information about the existence filter mismatch.
8588
*/
8689
void NotifyOnExistenceFilterMismatch(const ExistenceFilterMismatchInfo&);

0 commit comments

Comments
 (0)