This document describes how Firebase Auth maintains thread-safety. The Firebase Auth library (not including Firebase Auth UI and Auth Provider UIs for now) must be thread-safe, meaning developers are free to call any method in any thread at any time. Thus, all code that may take part in race conditions must be protected in some way.
When contested data and accessing code is limited in scope, for example,
a mutable array accessed only by two methods, a @synchronized directive is
probably the simplest solution. Make sure the object to be locked on is not
nil, e.g., self.
A more scalable solution used throughout the current code base is to execute all potentially conflicting code in the same serial dispatch queue, which is referred as "the auth global work queue", or in some other serial queue that has its target queue set to this auth global work queue. This way we don't have to think about which variables may be contested. We only need to make sure all public APIs that may have thread-safety issues make the dispatch. The auth global work queue is defined in FIRAuthGlobalWorkQueue.h.
In following sub-sections, we divided methods into three categories, according to the two criteria below:
Unless it's a simple wrapper of another public asynchronous method, a public asynchronous method shall
The code would look like:
- (void)doSomethingWithCompletion:(nullable CompletionBlock)completion {
dispatch_async(FIRAuthGlobalWorkQueue(), ^{
// Do things...
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(args);
});
}
});
}
A public synchronous method that needs protection shall dispatch synchronously to the auth global work queue for its work. The code would look like:
- (ReturnType)something {
__block ReturnType result;
dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
// Compute result.
result = computedResult;
});
return result;
}
But don't call methods protected this way from private methods, or a
deadlock would occur. This is because you are not supposed to
dispatch_sync to the queue you're already in. This can be easily worked
around by creating an equivalent private synchronous method to be called by
both public and private methods and making the public synchronous method a
wrapper of that. For example,
- (ReturnType)somethingInternal {
// Compute result.
return computedResult;
}
- (ReturnType)something {
__block ReturnType result;
dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
result = [self somethingInternal];
});
return result;
}
Generally speaking there is nothing special needed to be done for private methods:
Just beware you can't call public synchronous methods protected by the auth global work queue from private methods as stated in the preceding sub-section.