1 /*
2  *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 #ifndef RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_H_
11 #define RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_H_
12 
13 #include <type_traits>
14 
15 #include "api/task_queue/task_queue_base.h"
16 #include "rtc_base/platform_thread_types.h"
17 #include "rtc_base/synchronization/mutex.h"
18 #include "rtc_base/system/rtc_export.h"
19 #include "rtc_base/thread_annotations.h"
20 
21 namespace webrtc {
22 // Real implementation of SequenceChecker, for use in debug mode, or
23 // for temporary use in release mode (e.g. to RTC_CHECK on a threading issue
24 // seen only in the wild).
25 //
26 // Note: You should almost always use the SequenceChecker class to get the
27 // right version for your build configuration.
28 class RTC_EXPORT SequenceCheckerImpl {
29  public:
30   SequenceCheckerImpl();
31   ~SequenceCheckerImpl();
32 
33   bool IsCurrent() const;
34   // Changes the task queue or thread that is checked for in IsCurrent. This can
35   // be useful when an object may be created on one task queue / thread and then
36   // used exclusively on another thread.
37   void Detach();
38 
39   // Returns a string that is formatted to match with the error string printed
40   // by RTC_CHECK() when a condition is not met.
41   // This is used in conjunction with the RTC_DCHECK_RUN_ON() macro.
42   std::string ExpectationToString() const;
43 
44  private:
45   mutable Mutex lock_;
46   // These are mutable so that IsCurrent can set them.
47   mutable bool attached_ RTC_GUARDED_BY(lock_);
48   mutable rtc::PlatformThreadRef valid_thread_ RTC_GUARDED_BY(lock_);
49   mutable const TaskQueueBase* valid_queue_ RTC_GUARDED_BY(lock_);
50   mutable const void* valid_system_queue_ RTC_GUARDED_BY(lock_);
51 };
52 
53 // Do nothing implementation, for use in release mode.
54 //
55 // Note: You should almost always use the SequenceChecker class to get the
56 // right version for your build configuration.
57 class SequenceCheckerDoNothing {
58  public:
IsCurrent()59   bool IsCurrent() const { return true; }
Detach()60   void Detach() {}
61 };
62 
63 // SequenceChecker is a helper class used to help verify that some methods
64 // of a class are called on the same task queue or thread. A
65 // SequenceChecker is bound to a a task queue if the object is
66 // created on a task queue, or a thread otherwise.
67 //
68 //
69 // Example:
70 // class MyClass {
71 //  public:
72 //   void Foo() {
73 //     RTC_DCHECK_RUN_ON(sequence_checker_);
74 //     ... (do stuff) ...
75 //   }
76 //
77 //  private:
78 //   SequenceChecker sequence_checker_;
79 // }
80 //
81 // In Release mode, IsCurrent will always return true.
82 #if RTC_DCHECK_IS_ON
83 class RTC_LOCKABLE SequenceChecker : public SequenceCheckerImpl {};
84 #else
85 class RTC_LOCKABLE SequenceChecker : public SequenceCheckerDoNothing {};
86 #endif  // RTC_ENABLE_THREAD_CHECKER
87 
88 namespace webrtc_seq_check_impl {
89 // Helper class used by RTC_DCHECK_RUN_ON (see example usage below).
90 class RTC_SCOPED_LOCKABLE SequenceCheckerScope {
91  public:
92   template <typename ThreadLikeObject>
SequenceCheckerScope(const ThreadLikeObject * thread_like_object)93   explicit SequenceCheckerScope(const ThreadLikeObject* thread_like_object)
94       RTC_EXCLUSIVE_LOCK_FUNCTION(thread_like_object) {}
95   SequenceCheckerScope(const SequenceCheckerScope&) = delete;
96   SequenceCheckerScope& operator=(const SequenceCheckerScope&) = delete;
RTC_UNLOCK_FUNCTION()97   ~SequenceCheckerScope() RTC_UNLOCK_FUNCTION() {}
98 
99   template <typename ThreadLikeObject>
IsCurrent(const ThreadLikeObject * thread_like_object)100   static bool IsCurrent(const ThreadLikeObject* thread_like_object) {
101     return thread_like_object->IsCurrent();
102   }
103 };
104 }  // namespace webrtc_seq_check_impl
105 }  // namespace webrtc
106 
107 // RTC_RUN_ON/RTC_GUARDED_BY/RTC_DCHECK_RUN_ON macros allows to annotate
108 // variables are accessed from same thread/task queue.
109 // Using tools designed to check mutexes, it checks at compile time everywhere
110 // variable is access, there is a run-time dcheck thread/task queue is correct.
111 //
112 // class ThreadExample {
113 //  public:
114 //   void NeedVar1() {
115 //     RTC_DCHECK_RUN_ON(network_thread_);
116 //     transport_->Send();
117 //   }
118 //
119 //  private:
120 //   rtc::Thread* network_thread_;
121 //   int transport_ RTC_GUARDED_BY(network_thread_);
122 // };
123 //
124 // class SequenceCheckerExample {
125 //  public:
126 //   int CalledFromPacer() RTC_RUN_ON(pacer_sequence_checker_) {
127 //     return var2_;
128 //   }
129 //
130 //   void CallMeFromPacer() {
131 //     RTC_DCHECK_RUN_ON(&pacer_sequence_checker_)
132 //        << "Should be called from pacer";
133 //     CalledFromPacer();
134 //   }
135 //
136 //  private:
137 //   int pacer_var_ RTC_GUARDED_BY(pacer_sequence_checker_);
138 //   SequenceChecker pacer_sequence_checker_;
139 // };
140 //
141 // class TaskQueueExample {
142 //  public:
143 //   class Encoder {
144 //    public:
145 //     rtc::TaskQueue* Queue() { return encoder_queue_; }
146 //     void Encode() {
147 //       RTC_DCHECK_RUN_ON(encoder_queue_);
148 //       DoSomething(var_);
149 //     }
150 //
151 //    private:
152 //     rtc::TaskQueue* const encoder_queue_;
153 //     Frame var_ RTC_GUARDED_BY(encoder_queue_);
154 //   };
155 //
156 //   void Encode() {
157 //     // Will fail at runtime when DCHECK is enabled:
158 //     // encoder_->Encode();
159 //     // Will work:
160 //     rtc::scoped_refptr<Encoder> encoder = encoder_;
161 //     encoder_->Queue()->PostTask([encoder] { encoder->Encode(); });
162 //   }
163 //
164 //  private:
165 //   rtc::scoped_refptr<Encoder> encoder_;
166 // }
167 
168 // Document if a function expected to be called from same thread/task queue.
169 #define RTC_RUN_ON(x) \
170   RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(x))
171 
172 namespace webrtc {
173 std::string ExpectationToString(const webrtc::SequenceChecker* checker);
174 
175 // Catch-all implementation for types other than explicitly supported above.
176 template <typename ThreadLikeObject>
ExpectationToString(const ThreadLikeObject *)177 std::string ExpectationToString(const ThreadLikeObject*) {
178   return std::string();
179 }
180 
181 }  // namespace webrtc
182 
183 #define RTC_DCHECK_RUN_ON(x)                                              \
184   webrtc::webrtc_seq_check_impl::SequenceCheckerScope seq_check_scope(x); \
185   RTC_DCHECK((x)->IsCurrent()) << webrtc::ExpectationToString(x)
186 
187 #endif  // RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_H_
188