Artem Titov | a617867 | 2023-01-30 10:51:01 | [diff] [blame] | 1 | <!-- go/cmark --> |
| 2 | <!--* freshness: {owner: 'hta' reviewed: '2021-05-31'} *--> |
Harald Alvestrand | 5cb983b | 2021-06-01 07:46:54 | [diff] [blame] | 3 | |
| 4 | # Basic concepts and primitives |
| 5 | |
| 6 | ## Time |
| 7 | |
| 8 | Internally, time is represent using the [webrtc::Timestamp][1] class. This |
| 9 | represents |
| 10 | time with a resolution of one microsecond, using a 64-bit integer, and provides |
| 11 | converters to milliseconds or seconds as needed. |
| 12 | |
| 13 | All timestamps need to be measured from the system monotonic time. |
| 14 | |
| 15 | The epoch is not specified (because we can't always know if the system clock is |
| 16 | correct), but whenever an absolute epoch is needed, the Unix time |
| 17 | epoch (Jan 1, 1970 at 0:00 GMT) is used. |
| 18 | |
| 19 | Conversion from/to other formats (for example milliseconds, NTP times, |
| 20 | timestamp strings) should happen as close to the interface requiring that |
| 21 | format as possible. |
| 22 | |
| 23 | NOTE: There are parts of the codebase that don't use Timestamp, parts of the |
| 24 | codebase that use the NTP epoch, and parts of the codebase that don't use the |
| 25 | monotonic clock. They need to |
| 26 | be updated. |
| 27 | |
| 28 | ## Threads |
| 29 | |
| 30 | All execution happens on a TaskQueue instance. How a TaskQueue is implemented |
| 31 | varies by platform, but they all have the [webrtc::TaskQueueBase][3] API. |
| 32 | |
| 33 | This API offers primitives for posting tasks, with or without delay. |
| 34 | |
| 35 | Some core parts use the [rtc::Thread][2], which is a subclass of TaskQueueBase. |
| 36 | This may contain a SocketServer for processing I/O, and is used for policing |
| 37 | certain calling pattern between a few core threads (the NetworkThread cannot |
| 38 | do Invoke on the Worker thread, for instance). |
| 39 | |
Harald Alvestrand | c1be89f | 2022-06-21 09:28:32 | [diff] [blame] | 40 | ## Reserved class suffixes |
| 41 | |
| 42 | C++ classes with names ending in the suffixes "Factory", "Builder" and "Manager" are supposed to behave |
| 43 | in certain well known ways. |
| 44 | |
| 45 | For a particular class name Foo, the following classes, if they exist, should |
| 46 | behave as follows: |
| 47 | |
| 48 | * FooFactory: Has a Create function that creates a Foo object and returns the |
| 49 | object or an owning reference to it (for instance std::unique_ptr or |
| 50 | rtc::scoped_refptr<Foo>). The Create function should NOT alter the factory |
| 51 | state; ideally, it is marked const. Ownership of the returned object is only |
| 52 | with the caller. |
| 53 | |
| 54 | * FooBuilder: Has a Build function that returns ownership of a Foo object (as |
| 55 | above). The Builder can only be used once, and resources given to the Builder |
| 56 | before the Build function is called are either released or owned by the Foo |
| 57 | object. The Create function may be reference-qualified (declared as ```Foo |
| 58 | Build() &&```), which means it is invoked as ```std::move(builder).Build()```, |
| 59 | and C++ will ensure that it is not used again. |
| 60 | |
| 61 | * FooManager: Has a Create function that returns an rtc::scoped_refptr<Foo> (if |
| 62 | shared ownership) or a Foo* (if the Manager retains sole ownership). If |
| 63 | Create() cannot fail, consider returning a Foo&. The Manager is responsible |
| 64 | for keeping track of the object; if the Create function returns a Foo*, the |
| 65 | Foo object is guaranteed to be destroyed when the FooManager is destroyed. |
| 66 | |
| 67 | If a Manager class manages multiple classes of objects, the Create functions |
| 68 | should be appropriately named (the FooAndBarManager would have CreateFoo() and |
| 69 | CreateBar() functions), and the class will have a suitable name for the group of |
| 70 | objects it is managing. |
| 71 | |
| 72 | FooFactory is mainly useful for the case where preparation for producing Foo |
| 73 | objects is complex. If Foo can be created with just an argument list, consider |
| 74 | exposing its constructor instead; if Foo creation can fail, consider having |
| 75 | a free function called CreateFoo instead of a factory. |
| 76 | |
| 77 | Note that classes with these names exist that do not follow these conventions. |
| 78 | When they are detected, they need to be marked with TODO statements and bugs |
| 79 | filed on them to get them into a conformant state. |
| 80 | |
Harald Alvestrand | 5cb983b | 2021-06-01 07:46:54 | [diff] [blame] | 81 | ## Synchronization primitives |
| 82 | |
| 83 | ### PostTask and thread-guarded variables |
| 84 | |
| 85 | The preferred method for synchronization is to post tasks between threads, |
| 86 | and to let each thread take care of its own variables (lock-free programming). |
| 87 | All variables in |
| 88 | classes intended to be used with multiple threads should therefore be |
| 89 | annotated with RTC_GUARDED_BY(thread). |
| 90 | |
| 91 | For classes used with only one thread, the recommended pattern is to let |
| 92 | them own a webrtc::SequenceChecker (conventionally named sequence_checker_) |
| 93 | and let all variables be RTC_GUARDED_BY(sequence_checker_). |
| 94 | |
| 95 | Member variables marked const do not need to be guarded, since they never |
| 96 | change. (But note that they may point to objects that can change!) |
| 97 | |
| 98 | When posting tasks with callbacks, it is the duty of the caller to check |
| 99 | that the object one is calling back into still exists when the callback |
| 100 | is made. A helper for this task is the [webrtc::ScopedTaskSafety][5] |
| 101 | flag, which can automatically drop callbacks in this situation, and |
| 102 | associated classes. |
| 103 | |
| 104 | ### Synchronization primitives to be used when needed |
| 105 | |
| 106 | When it is absolutely necessary to let one thread wait for another thread |
| 107 | to do something, Thread::Invoke can be used. This function is DISCOURAGED, |
| 108 | since it leads to performance issues, but is currently still widespread. |
| 109 | |
| 110 | When it is absolutely necessary to access one variable from multiple threads, |
| 111 | the webrtc::Mutex can be used. Such variables MUST be marked up with |
| 112 | RTC_GUARDED_BY(mutex), to allow static analysis that lessens the chance of |
| 113 | deadlocks or unintended consequences. |
| 114 | |
| 115 | ### Synchronization primitives that are being removed |
| 116 | The following non-exhaustive list of synchronization primitives are |
| 117 | in the (slow) process of being removed from the codebase. |
| 118 | |
| 119 | * sigslot. Use [webrtc::CallbackList][4] instead, or, when there's only one |
| 120 | signal consumer, a single std::function. |
| 121 | |
| 122 | * AsyncInvoker. |
| 123 | |
Harald Alvestrand | 0fe60bd | 2021-06-22 07:47:34 | [diff] [blame] | 124 | * RecursiveCriticalSection. Try to use [webrtc::Mutex][6] instead, and don't recurse. |
| 125 | |
Harald Alvestrand | 31b03e9 | 2021-11-02 10:54:38 | [diff] [blame] | 126 | ## Enum-To-String functions |
| 127 | If there is a need to convert an enum to a string representation, such as for |
| 128 | enums exposed at the Javascript API interface, the recommended way is to write |
| 129 | a function named AsString, declared "static constexpr" and returning an |
| 130 | absl::string_view. The declaration should be right after the enum declaration, |
| 131 | in the same scope; the implementation (which must be marked "inline") should |
| 132 | be at the end of the same header file. |
Harald Alvestrand | 5cb983b | 2021-06-01 07:46:54 | [diff] [blame] | 133 | |
Harald Alvestrand | 31b03e9 | 2021-11-02 10:54:38 | [diff] [blame] | 134 | If the enum is not defined within a class, the "static" keyword is not needed. |
Harald Alvestrand | 5cb983b | 2021-06-01 07:46:54 | [diff] [blame] | 135 | |
| 136 | [1]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/api/units/timestamp.h;drc=b95d90b78a3491ef8e8aa0640dd521515ec881ca;l=29 |
| 137 | [2]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/rtc_base/thread.h;drc=1107751b6f11c35259a1c5c8a0f716e227b7e3b4;l=194 |
| 138 | [3]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/api/task_queue/task_queue_base.h;drc=1107751b6f11c35259a1c5c8a0f716e227b7e3b4;l=25 |
| 139 | [4]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/rtc_base/callback_list.h;drc=54b91412de3f579a2d5ccdead6e04cc2cc5ca3a1;l=162 |
| 140 | [5]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/rtc_base/task_utils/pending_task_safety_flag.h;drc=86ee89f73e4f4799b3ebcc0b5c65837c9601fe6d;l=117 |
Harald Alvestrand | 0fe60bd | 2021-06-22 07:47:34 | [diff] [blame] | 141 | [6]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/rtc_base/synchronization/mutex.h;drc=0d3c09a8fe5f12dfbc9f1bcd5790fda8830624ec;l=40 |