Skip to content

Commit 6c6ef3b

Browse files
author
Brian Chen
authored
Throw error in subsequent function calls after the client is shutdown (#2978)
1 parent 4caf99c commit 6c6ef3b

File tree

3 files changed

+49
-18
lines changed

3 files changed

+49
-18
lines changed

Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,4 +1223,21 @@ - (void)testCanDisableNetwork {
12231223
[self awaitExpectations];
12241224
}
12251225

1226+
- (void)testClientCallsAfterShutdownFail {
1227+
FIRDocumentReference *doc = [self documentRef];
1228+
FIRFirestore *firestore = doc.firestore;
1229+
1230+
[firestore enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network"]];
1231+
[self awaitExpectations];
1232+
[firestore shutdownWithCompletion:[self completionForExpectationWithName:@"Shutdown"]];
1233+
[self awaitExpectations];
1234+
1235+
XCTAssertThrowsSpecific(
1236+
{
1237+
[firestore disableNetworkWithCompletion:^(NSError *error){
1238+
}];
1239+
},
1240+
NSException, @"The client has already been shutdown.");
1241+
}
1242+
12261243
@end

Firestore/Source/Core/FSTFirestoreClient.mm

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
using firebase::firestore::api::DocumentSnapshot;
6363
using firebase::firestore::api::Settings;
6464
using firebase::firestore::api::SnapshotMetadata;
65+
using firebase::firestore::api::ThrowIllegalState;
6566
using firebase::firestore::auth::CredentialsProvider;
6667
using firebase::firestore::auth::User;
6768
using firebase::firestore::core::DatabaseInfo;
@@ -128,7 +129,8 @@ @implementation FSTFirestoreClient {
128129
std::unique_ptr<Executor> _userExecutor;
129130
std::chrono::milliseconds _initialGcDelay;
130131
std::chrono::milliseconds _regularGcDelay;
131-
BOOL _gcHasRun;
132+
bool _gcHasRun;
133+
std::atomic<bool> _isShutdown;
132134
_Nullable id<FSTLRUDelegate> _lruDelegate;
133135
DelayedOperation _lruCallback;
134136
}
@@ -165,7 +167,8 @@ - (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo
165167
_credentialsProvider = credentialsProvider;
166168
_userExecutor = std::move(userExecutor);
167169
_workerQueue = std::move(workerQueue);
168-
_gcHasRun = NO;
170+
_gcHasRun = false;
171+
_isShutdown = false;
169172
_initialGcDelay = FSTLruGcInitialDelay;
170173
_regularGcDelay = FSTLruGcRegularDelay;
171174

@@ -270,7 +273,7 @@ - (void)scheduleLruGarbageCollection {
270273
std::chrono::milliseconds delay = _gcHasRun ? _regularGcDelay : _initialGcDelay;
271274
_lruCallback = _workerQueue->EnqueueAfterDelay(delay, TimerId::GarbageCollectionDelay, [self]() {
272275
[self->_localStore collectGarbage:self->_lruDelegate.gc];
273-
self->_gcHasRun = YES;
276+
self->_gcHasRun = true;
274277
[self scheduleLruGarbageCollection];
275278
});
276279
}
@@ -283,6 +286,7 @@ - (void)credentialDidChangeWithUser:(const User &)user {
283286
}
284287

285288
- (void)disableNetworkWithCallback:(util::StatusCallback)callback {
289+
[self verifyNotShutdown];
286290
_workerQueue->Enqueue([self, callback] {
287291
_remoteStore->DisableNetwork();
288292
if (callback) {
@@ -292,6 +296,7 @@ - (void)disableNetworkWithCallback:(util::StatusCallback)callback {
292296
}
293297

294298
- (void)enableNetworkWithCallback:(util::StatusCallback)callback {
299+
[self verifyNotShutdown];
295300
_workerQueue->Enqueue([self, callback] {
296301
_remoteStore->EnableNetwork();
297302
if (callback) {
@@ -302,20 +307,29 @@ - (void)enableNetworkWithCallback:(util::StatusCallback)callback {
302307

303308
- (void)shutdownWithCallback:(util::StatusCallback)callback {
304309
_workerQueue->Enqueue([self, callback] {
305-
self->_credentialsProvider->SetCredentialChangeListener(nullptr);
310+
if (!_isShutdown) {
311+
self->_credentialsProvider->SetCredentialChangeListener(nullptr);
306312

307-
// If we've scheduled LRU garbage collection, cancel it.
308-
if (self->_lruCallback) {
309-
self->_lruCallback.Cancel();
313+
// If we've scheduled LRU garbage collection, cancel it.
314+
if (self->_lruCallback) {
315+
self->_lruCallback.Cancel();
316+
}
317+
_remoteStore->Shutdown();
318+
[self.persistence shutdown];
319+
self->_isShutdown = true;
310320
}
311-
_remoteStore->Shutdown();
312-
[self.persistence shutdown];
313321
if (callback) {
314322
self->_userExecutor->Execute([=] { callback(Status::OK()); });
315323
}
316324
});
317325
}
318326

327+
- (void)verifyNotShutdown {
328+
if (_isShutdown) {
329+
ThrowIllegalState("The client has already been shutdown.");
330+
}
331+
}
332+
319333
- (std::shared_ptr<QueryListener>)listenToQuery:(FSTQuery *)query
320334
options:(core::ListenOptions)options
321335
listener:(ViewSnapshot::SharedListener &&)listener {
@@ -327,11 +341,13 @@ - (void)shutdownWithCallback:(util::StatusCallback)callback {
327341
}
328342

329343
- (void)removeListener:(const std::shared_ptr<QueryListener> &)listener {
344+
[self verifyNotShutdown];
330345
_workerQueue->Enqueue([self, listener] { [self.eventManager removeListener:listener]; });
331346
}
332347

333348
- (void)getDocumentFromLocalCache:(const DocumentReference &)doc
334349
callback:(DocumentSnapshot::Listener &&)callback {
350+
[self verifyNotShutdown];
335351
auto shared_callback = absl::ShareUniquePtr(std::move(callback));
336352
_workerQueue->Enqueue([self, doc, shared_callback] {
337353
FSTMaybeDocument *maybeDoc = [self.localStore readDocument:doc.key()];
@@ -362,6 +378,7 @@ - (void)getDocumentFromLocalCache:(const DocumentReference &)doc
362378
- (void)getDocumentsFromLocalCache:(FIRQuery *)query
363379
completion:(void (^)(FIRQuerySnapshot *_Nullable query,
364380
NSError *_Nullable error))completion {
381+
[self verifyNotShutdown];
365382
_workerQueue->Enqueue([self, query, completion] {
366383
DocumentMap docs = [self.localStore executeQuery:query.query];
367384

@@ -391,6 +408,7 @@ - (void)writeMutations:(std::vector<FSTMutation *> &&)mutations
391408
callback:(util::StatusCallback)callback {
392409
// TODO(c++14): move `mutations` into lambda (C++14).
393410
_workerQueue->Enqueue([self, mutations, callback]() mutable {
411+
[self verifyNotShutdown];
394412
if (mutations.empty()) {
395413
if (callback) {
396414
self->_userExecutor->Execute([=] { callback(Status::OK()); });
@@ -413,6 +431,7 @@ - (void)transactionWithRetries:(int)retries
413431
resultCallback:(core::TransactionResultCallback)resultCallback {
414432
// Dispatch the result back onto the user dispatch queue.
415433
auto async_callback = [self, resultCallback](util::StatusOr<absl::any> maybe_value) {
434+
[self verifyNotShutdown];
416435
if (resultCallback) {
417436
self->_userExecutor->Execute([=] { resultCallback(std::move(maybe_value)); });
418437
}

Firestore/core/src/firebase/firestore/api/firestore.mm

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,10 @@
140140
}
141141

142142
void Firestore::Shutdown(util::StatusCallback callback) {
143-
if (!client_) {
144-
if (callback) {
145-
// We should be dispatching the callback on the user dispatch queue
146-
// but if the client is nil here that queue was never created.
147-
callback(Status::OK());
148-
}
149-
} else {
150-
[client_ shutdownWithCallback:std::move(callback)];
151-
}
143+
// The client must be initialized to ensure that all subsequent API usage
144+
// throws an exception.
145+
EnsureClientConfigured();
146+
[client_ shutdownWithCallback:std::move(callback)];
152147
}
153148

154149
void Firestore::EnableNetwork(util::StatusCallback callback) {

0 commit comments

Comments
 (0)