mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
120 lines
4.2 KiB
Markdown
120 lines
4.2 KiB
Markdown
# Firebase Auth Thread Safety
|
|
|
|
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.
|
|
|
|
## Local Synchronization
|
|
|
|
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`.
|
|
|
|
## Global Work Queue
|
|
|
|
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](../Source/Private/FIRAuthGlobalWorkQueue.h)
|
|
and any serial task queue created by
|
|
[FIRAuthSerialTaskQueue.h](../Source/Private/FIRAuthSerialTaskQueue.h)
|
|
already has its target set properly.
|
|
|
|
In following sub-sections, we divided methods into three categories, according
|
|
to the two criteria below:
|
|
|
|
1. Whether the method is public or private:
|
|
* A public method can be directly called by developers.
|
|
* A private method can only be called by our own code.
|
|
2. Whether the method is synchronous or asynchronous.
|
|
* A synchronous method returns some value or object in the calling
|
|
thread immediately.
|
|
* An asynchronous method returns nothing but calls the callback provided
|
|
by the caller at some point in future.
|
|
|
|
### Public Asynchronous Methods
|
|
|
|
Unless it's a simple wrapper of another public asynchronous method, a public
|
|
asynchronous method shall
|
|
|
|
* Dispatch asynchronously to the auth global work queue immediately.
|
|
* Dispatch asynchronously to the main queue before calling the callback.
|
|
This is to make developers' life easier so they don't have to manage
|
|
thread-safety.
|
|
|
|
The code would look like:
|
|
|
|
```objectivec
|
|
- (void)doSomethingWithCompletion:(nullable CompletionBlock)completion {
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^{
|
|
// Do things...
|
|
if (completion) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
completion(args);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
```
|
|
|
|
### Public Synchronous Methods
|
|
|
|
A public synchronous method that needs protection shall dispatch
|
|
*synchronously* to the auth global work queue for its work. The code would
|
|
look like:
|
|
|
|
```objectivec
|
|
- (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,
|
|
|
|
```objectivec
|
|
- (ReturnType)somethingInternal {
|
|
// Compute result.
|
|
return computedResult;
|
|
}
|
|
|
|
- (ReturnType)something {
|
|
__block ReturnType result;
|
|
dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
|
|
result = [self somethingInternal];
|
|
});
|
|
return result;
|
|
}
|
|
```
|
|
|
|
### Private Methods
|
|
|
|
Generally speaking there is nothing special needed to be done for private
|
|
methods:
|
|
|
|
* The calling code should already be in the auth global work queue.
|
|
* The callback, if any, is provided by our own code, so it expects to called
|
|
in the auth global work queue as well. This is usually already the case,
|
|
unless the method pass the callback to some other asynchronous methods
|
|
outside our library, in which case we need to manually make the callback
|
|
called in the auth global work queue.
|
|
|
|
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.
|