Files
firebase-ios-sdk/Firestore/Source/API/FSTFirestoreComponent.mm
T
Gil 7f84e84bf9 Fixes for issues discovered in the Unity desktop build (#4795)
* Make Filesystem's destructor virtual

* Avoid capturing a strong reference to FirestoreClient in GC tasks.

This prevents this task from keeping the FirestoreClient alive even
after the api::Firestore instance has been destroyed.

* Ensure that FirestoreClient is terminated

In the public C++ API, it's possible to delete the owning FirebaseApp
which triggers the destruction of the api::Firestore object even though
we haven't called Terminate.

This makes it such that api::Firestore calls a blocking Terminate call
on FirestoreClient in its destructor.

Additionally, change AsyncQueue to always run the operation enqueued on
shutdown, even if already shutdown. Make FirestoreClient capable of
short circuiting the termination work for itself. This makes terminate
idempotent in a more staightforward way.

* Clean up excess logging

* Remove defintion of weak_from_this

* Review feedback

* Make AsyncQueue extend std::enable_shared_from_this

Also hide the AsyncQueue's public constructor and force all users to
manage their reference to it via std::shared_ptr.

* Handle Executor shutdown from within its own pool
2020-02-04 13:38:59 -08:00

171 lines
6.0 KiB
Plaintext

/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Firestore/Source/API/FSTFirestoreComponent.h"
#import <FirebaseAuthInterop/FIRAuthInterop.h>
#import <FirebaseCore/FIRAppInternal.h>
#import <FirebaseCore/FIRComponent.h>
#import <FirebaseCore/FIRComponentContainer.h>
#import <FirebaseCore/FIRDependency.h>
#import <FirebaseCore/FIRLibrary.h>
#import <FirebaseCore/FIROptions.h>
#include <memory>
#include <string>
#include <utility>
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#include "Firestore/core/include/firebase/firestore/firestore_version.h"
#include "Firestore/core/src/firebase/firestore/api/firestore.h"
#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h"
#include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h"
#include "Firestore/core/src/firebase/firestore/util/async_queue.h"
#include "Firestore/core/src/firebase/firestore/util/exception.h"
#include "Firestore/core/src/firebase/firestore/util/executor.h"
#include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
#include "absl/memory/memory.h"
namespace util = firebase::firestore::util;
using firebase::firestore::api::Firestore;
using firebase::firestore::auth::CredentialsProvider;
using firebase::firestore::auth::FirebaseCredentialsProvider;
using firebase::firestore::util::AsyncQueue;
using firebase::firestore::util::Executor;
using firebase::firestore::util::ThrowInvalidArgument;
NS_ASSUME_NONNULL_BEGIN
@interface FSTFirestoreComponent () <FIRComponentLifecycleMaintainer, FIRLibrary>
@end
@implementation FSTFirestoreComponent
// Explicitly @synthesize because instances is part of the FSTInstanceProvider protocol.
@synthesize instances = _instances;
#pragma mark - Initialization
- (instancetype)initWithApp:(FIRApp *)app {
self = [super init];
if (self) {
_instances = [[NSMutableDictionary alloc] init];
HARD_ASSERT(app, "Cannot initialize Firestore with a nil FIRApp.");
_app = app;
}
return self;
}
- (NSString *)keyForDatabase:(NSString *)database {
return [NSString stringWithFormat:@"%@|%@", self.app.name, database];
}
#pragma mark - FSTInstanceProvider Conformance
- (FIRFirestore *)firestoreForDatabase:(NSString *)database {
if (!database) {
ThrowInvalidArgument("Database identifier may not be nil.");
}
NSString *projectID = self.app.options.projectID;
if (!projectID) {
ThrowInvalidArgument("FIROptions.projectID must be set to a valid project ID.");
}
NSString *key = [self keyForDatabase:database];
// Get the component from the container.
@synchronized(self.instances) {
FIRFirestore *firestore = _instances[key];
if (!firestore) {
std::string queue_name{"com.google.firebase.firestore"};
if (!self.app.isDefaultApp) {
absl::StrAppend(&queue_name, ".", util::MakeString(self.app.name));
}
auto executor = Executor::CreateSerial(queue_name.c_str());
auto workerQueue = AsyncQueue::Create(std::move(executor));
id<FIRAuthInterop> auth = FIR_COMPONENT(FIRAuthInterop, self.app.container);
auto credentialsProvider = std::make_shared<FirebaseCredentialsProvider>(self.app, auth);
model::DatabaseId databaseID{util::MakeString(projectID), util::MakeString(database)};
std::string persistenceKey = util::MakeString(self.app.name);
firestore = [[FIRFirestore alloc] initWithDatabaseID:std::move(databaseID)
persistenceKey:std::move(persistenceKey)
credentialsProvider:std::move(credentialsProvider)
workerQueue:std::move(workerQueue)
firebaseApp:self.app
instanceRegistry:self];
_instances[key] = firestore;
}
return firestore;
}
}
- (void)removeInstanceWithDatabase:(NSString *)database {
@synchronized(_instances) {
NSString *key = [self keyForDatabase:database];
[_instances removeObjectForKey:key];
}
}
#pragma mark - FIRComponentLifecycleMaintainer
- (void)appWillBeDeleted:(FIRApp *)app {
NSDictionary<NSString *, FIRFirestore *> *instances;
@synchronized(_instances) {
instances = [_instances copy];
[_instances removeAllObjects];
}
for (NSString *key in instances) {
[instances[key] terminateInternalWithCompletion:nil];
}
}
#pragma mark - Object Lifecycle
+ (void)load {
[FIRApp registerInternalLibrary:(Class<FIRLibrary>)self
withName:@"fire-fst"
withVersion:[NSString stringWithUTF8String:firebase::firestore::
kFirestoreVersionString]];
}
#pragma mark - Interoperability
+ (NSArray<FIRComponent *> *)componentsToRegister {
FIRDependency *auth = [FIRDependency dependencyWithProtocol:@protocol(FIRAuthInterop)
isRequired:NO];
FIRComponent *firestoreProvider = [FIRComponent
componentWithProtocol:@protocol(FSTFirestoreMultiDBProvider)
instantiationTiming:FIRInstantiationTimingLazy
dependencies:@[ auth ]
creationBlock:^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
FSTFirestoreComponent *multiDBComponent =
[[FSTFirestoreComponent alloc] initWithApp:container.app];
*isCacheable = YES;
return multiDBComponent;
}];
return @[ firestoreProvider ];
}
@end
NS_ASSUME_NONNULL_END