diff options
Diffstat (limited to 'Firebase/Auth/Docs/threading.md')
-rw-r--r-- | Firebase/Auth/Docs/threading.md | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/Firebase/Auth/Docs/threading.md b/Firebase/Auth/Docs/threading.md new file mode 100644 index 0000000..2f5b782 --- /dev/null +++ b/Firebase/Auth/Docs/threading.md @@ -0,0 +1,119 @@ +# 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 deveopers 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 asychronous 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. |