Skip to content

Commit 5f5b65a

Browse files
authored
Avoid blocking access to the AsyncQueue during Dispose (#5635)
1 parent ff5bb56 commit 5f5b65a

File tree

2 files changed

+42
-4
lines changed

2 files changed

+42
-4
lines changed

Firestore/core/src/util/async_queue.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ void AsyncQueue::EnterRestrictedMode() {
5252
}
5353

5454
void AsyncQueue::Dispose() {
55-
std::lock_guard<std::mutex> lock(mutex_);
55+
{
56+
std::lock_guard<std::mutex> lock(mutex_);
57+
mode_ = Mode::kDisposed;
58+
}
5659

57-
mode_ = Mode::kDisposed;
5860
executor_->Dispose();
5961
}
6062

Firestore/core/test/unit/util/async_queue_test.cc

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ TEST_P(AsyncQueueTest, CanScheduleOprationsWithRespectsToShutdownState) {
224224
EXPECT_EQ(steps, "124");
225225
}
226226

227-
TEST_P(AsyncQueueTest, RestrictedModeBlocksEnqueue) {
227+
TEST_P(AsyncQueueTest, RestrictedModePreventsEnqueue) {
228228
ASSERT_TRUE(queue->Enqueue([&] {}));
229229
ASSERT_TRUE(queue->EnqueueEvenWhileRestricted([&] {}));
230230

@@ -233,7 +233,7 @@ TEST_P(AsyncQueueTest, RestrictedModeBlocksEnqueue) {
233233
ASSERT_TRUE(queue->EnqueueEvenWhileRestricted([&] {}));
234234
}
235235

236-
TEST_P(AsyncQueueTest, DisposeBlocksAllEnqueues) {
236+
TEST_P(AsyncQueueTest, DisposePreventsAllEnqueues) {
237237
ASSERT_TRUE(queue->Enqueue([&] {}));
238238
ASSERT_TRUE(queue->EnqueueEvenWhileRestricted([&] {}));
239239

@@ -242,6 +242,42 @@ TEST_P(AsyncQueueTest, DisposeBlocksAllEnqueues) {
242242
ASSERT_FALSE(queue->EnqueueEvenWhileRestricted([&] {}));
243243
}
244244

245+
TEST_P(AsyncQueueTest, DisposeDoesNotBlockEnqueueWhileWaiting) {
246+
// Start a task that will block the queue. AsyncQueue::Dispose will block
247+
// until this completes.
248+
Expectation blocking_started;
249+
Expectation blocking_complete;
250+
queue->Enqueue([&] {
251+
blocking_started.Fulfill();
252+
Await(blocking_complete);
253+
});
254+
255+
// Kick off Dispose--this will block while the task above is still running.
256+
Await(blocking_started);
257+
Expectation dispose_started;
258+
Expectation dispose_complete;
259+
Async([&] {
260+
dispose_started.Fulfill();
261+
queue->Dispose();
262+
dispose_complete.Fulfill();
263+
});
264+
265+
// Finally, try to enqueue while Dispose is blocked waiting for the first
266+
// task to complete. This should not block.
267+
Expectation enqueue_completed;
268+
Expectation post_dispose;
269+
Async([&] {
270+
Await(dispose_started);
271+
bool enqueued = queue->Enqueue(post_dispose.AsCallback());
272+
ASSERT_FALSE(enqueued);
273+
enqueue_completed.Fulfill();
274+
});
275+
276+
Await(enqueue_completed);
277+
blocking_complete.Fulfill();
278+
Await(dispose_complete);
279+
}
280+
245281
} // namespace util
246282
} // namespace firestore
247283
} // namespace firebase

0 commit comments

Comments
 (0)