aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firebase/Auth/Docs/threading.md
blob: 9c48a74d1e3174bec0cb0902a0f41ec18947cb68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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 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.