xref: /aosp_15_r20/external/webrtc/pc/proxy.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright 2013 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 
11 // This file contains Macros for creating proxies for webrtc MediaStream and
12 // PeerConnection classes.
13 
14 // The proxied objects are initialized with either one or two thread
15 // objects that operations can be proxied to: The primary and secondary
16 // threads.
17 // In common usage, the primary thread will be the PeerConnection's
18 // signaling thread, and the secondary thread will be either the
19 // PeerConnection's worker thread or the PeerConnection's network thread.
20 
21 //
22 // Example usage:
23 //
24 // class TestInterface : public rtc::RefCountInterface {
25 //  public:
26 //   std::string FooA() = 0;
27 //   std::string FooB(bool arg1) const = 0;
28 //   std::string FooC(bool arg1) = 0;
29 //  };
30 //
31 // Note that return types can not be a const reference.
32 //
33 // class Test : public TestInterface {
34 // ... implementation of the interface.
35 // };
36 //
37 // BEGIN_PROXY_MAP(Test)
38 //   PROXY_PRIMARY_THREAD_DESTRUCTOR()
39 //   PROXY_METHOD0(std::string, FooA)
40 //   PROXY_CONSTMETHOD1(std::string, FooB, arg1)
41 //   PROXY_SECONDARY_METHOD1(std::string, FooC, arg1)
42 // END_PROXY_MAP()
43 //
44 // Where the destructor and first two methods are invoked on the primary
45 // thread, and the third is invoked on the secondary thread.
46 //
47 // The proxy can be created using
48 //
49 //   TestProxy::Create(Thread* signaling_thread, Thread* worker_thread,
50 //                     TestInterface*).
51 //
52 // The variant defined with BEGIN_PRIMARY_PROXY_MAP is unaware of
53 // the secondary thread, and invokes all methods on the primary thread.
54 //
55 
56 #ifndef PC_PROXY_H_
57 #define PC_PROXY_H_
58 
59 #include <stddef.h>
60 
61 #include <memory>
62 #include <string>
63 #include <tuple>
64 #include <type_traits>
65 #include <utility>
66 
67 #include "api/scoped_refptr.h"
68 #include "api/task_queue/task_queue_base.h"
69 #include "rtc_base/event.h"
70 #include "rtc_base/string_utils.h"
71 #include "rtc_base/system/rtc_export.h"
72 #include "rtc_base/thread.h"
73 
74 #if !defined(RTC_DISABLE_PROXY_TRACE_EVENTS) && !defined(WEBRTC_CHROMIUM_BUILD)
75 #define RTC_DISABLE_PROXY_TRACE_EVENTS
76 #endif
77 
78 namespace webrtc {
79 namespace proxy_internal {
80 
81 // Class for tracing the lifetime of MethodCall::Marshal.
82 class ScopedTrace {
83  public:
84   explicit ScopedTrace(const char* class_and_method_name);
85   ~ScopedTrace();
86 
87  private:
88   [[maybe_unused]] const char* const class_and_method_name_;
89 };
90 }  // namespace proxy_internal
91 
92 template <typename R>
93 class ReturnType {
94  public:
95   template <typename C, typename M, typename... Args>
Invoke(C * c,M m,Args &&...args)96   void Invoke(C* c, M m, Args&&... args) {
97     r_ = (c->*m)(std::forward<Args>(args)...);
98   }
99 
moved_result()100   R moved_result() { return std::move(r_); }
101 
102  private:
103   R r_;
104 };
105 
106 template <>
107 class ReturnType<void> {
108  public:
109   template <typename C, typename M, typename... Args>
Invoke(C * c,M m,Args &&...args)110   void Invoke(C* c, M m, Args&&... args) {
111     (c->*m)(std::forward<Args>(args)...);
112   }
113 
moved_result()114   void moved_result() {}
115 };
116 
117 template <typename C, typename R, typename... Args>
118 class MethodCall {
119  public:
120   typedef R (C::*Method)(Args...);
MethodCall(C * c,Method m,Args &&...args)121   MethodCall(C* c, Method m, Args&&... args)
122       : c_(c),
123         m_(m),
124         args_(std::forward_as_tuple(std::forward<Args>(args)...)) {}
125 
Marshal(rtc::Thread * t)126   R Marshal(rtc::Thread* t) {
127     if (t->IsCurrent()) {
128       Invoke(std::index_sequence_for<Args...>());
129     } else {
130       t->PostTask([this] {
131         Invoke(std::index_sequence_for<Args...>());
132         event_.Set();
133       });
134       event_.Wait(rtc::Event::kForever);
135     }
136     return r_.moved_result();
137   }
138 
139  private:
140   template <size_t... Is>
Invoke(std::index_sequence<Is...>)141   void Invoke(std::index_sequence<Is...>) {
142     r_.Invoke(c_, m_, std::move(std::get<Is>(args_))...);
143   }
144 
145   C* c_;
146   Method m_;
147   ReturnType<R> r_;
148   std::tuple<Args&&...> args_;
149   rtc::Event event_;
150 };
151 
152 template <typename C, typename R, typename... Args>
153 class ConstMethodCall {
154  public:
155   typedef R (C::*Method)(Args...) const;
ConstMethodCall(const C * c,Method m,Args &&...args)156   ConstMethodCall(const C* c, Method m, Args&&... args)
157       : c_(c),
158         m_(m),
159         args_(std::forward_as_tuple(std::forward<Args>(args)...)) {}
160 
Marshal(rtc::Thread * t)161   R Marshal(rtc::Thread* t) {
162     if (t->IsCurrent()) {
163       Invoke(std::index_sequence_for<Args...>());
164     } else {
165       t->PostTask([this] {
166         Invoke(std::index_sequence_for<Args...>());
167         event_.Set();
168       });
169       event_.Wait(rtc::Event::kForever);
170     }
171     return r_.moved_result();
172   }
173 
174  private:
175   template <size_t... Is>
Invoke(std::index_sequence<Is...>)176   void Invoke(std::index_sequence<Is...>) {
177     r_.Invoke(c_, m_, std::move(std::get<Is>(args_))...);
178   }
179 
180   const C* c_;
181   Method m_;
182   ReturnType<R> r_;
183   std::tuple<Args&&...> args_;
184   rtc::Event event_;
185 };
186 
187 #define PROXY_STRINGIZE_IMPL(x) #x
188 #define PROXY_STRINGIZE(x) PROXY_STRINGIZE_IMPL(x)
189 
190 // Helper macros to reduce code duplication.
191 #define PROXY_MAP_BOILERPLATE(class_name)                              \
192   template <class INTERNAL_CLASS>                                      \
193   class class_name##ProxyWithInternal;                                 \
194   typedef class_name##ProxyWithInternal<class_name##Interface>         \
195       class_name##Proxy;                                               \
196   template <class INTERNAL_CLASS>                                      \
197   class class_name##ProxyWithInternal : public class_name##Interface { \
198    protected:                                                          \
199     static constexpr char proxy_name_[] = #class_name "Proxy";         \
200     typedef class_name##Interface C;                                   \
201                                                                        \
202    public:                                                             \
203     const INTERNAL_CLASS* internal() const { return c(); }             \
204     INTERNAL_CLASS* internal() { return c(); }
205 
206 // clang-format off
207 // clang-format would put the semicolon alone,
208 // leading to a presubmit error (cpplint.py)
209 #define END_PROXY_MAP(class_name)                                       \
210   };                                                                    \
211   template <class INTERNAL_CLASS>                                       \
212   constexpr char class_name##ProxyWithInternal<INTERNAL_CLASS>::proxy_name_[];
213 // clang-format on
214 
215 #define PRIMARY_PROXY_MAP_BOILERPLATE(class_name)                     \
216  protected:                                                           \
217   class_name##ProxyWithInternal(rtc::Thread* primary_thread,          \
218                                 rtc::scoped_refptr<INTERNAL_CLASS> c) \
219       : primary_thread_(primary_thread), c_(std::move(c)) {}          \
220                                                                       \
221  private:                                                             \
222   mutable rtc::Thread* primary_thread_;
223 
224 #define SECONDARY_PROXY_MAP_BOILERPLATE(class_name)                   \
225  protected:                                                           \
226   class_name##ProxyWithInternal(rtc::Thread* primary_thread,          \
227                                 rtc::Thread* secondary_thread,        \
228                                 rtc::scoped_refptr<INTERNAL_CLASS> c) \
229       : primary_thread_(primary_thread),                              \
230         secondary_thread_(secondary_thread),                          \
231         c_(std::move(c)) {}                                           \
232                                                                       \
233  private:                                                             \
234   mutable rtc::Thread* primary_thread_;                               \
235   mutable rtc::Thread* secondary_thread_;
236 
237 // Note that the destructor is protected so that the proxy can only be
238 // destroyed via RefCountInterface.
239 #define REFCOUNTED_PROXY_MAP_BOILERPLATE(class_name)            \
240  protected:                                                     \
241   ~class_name##ProxyWithInternal() {                            \
242     MethodCall<class_name##ProxyWithInternal, void> call(       \
243         this, &class_name##ProxyWithInternal::DestroyInternal); \
244     call.Marshal(destructor_thread());                          \
245   }                                                             \
246                                                                 \
247  private:                                                       \
248   const INTERNAL_CLASS* c() const { return c_.get(); }          \
249   INTERNAL_CLASS* c() { return c_.get(); }                      \
250   void DestroyInternal() { c_ = nullptr; }                      \
251   rtc::scoped_refptr<INTERNAL_CLASS> c_;
252 
253 // Note: This doesn't use a unique_ptr, because it intends to handle a corner
254 // case where an object's deletion triggers a callback that calls back into
255 // this proxy object. If relying on a unique_ptr to delete the object, its
256 // inner pointer would be set to null before this reentrant callback would have
257 // a chance to run, resulting in a segfault.
258 #define OWNED_PROXY_MAP_BOILERPLATE(class_name)                 \
259  public:                                                        \
260   ~class_name##ProxyWithInternal() {                            \
261     MethodCall<class_name##ProxyWithInternal, void> call(       \
262         this, &class_name##ProxyWithInternal::DestroyInternal); \
263     call.Marshal(destructor_thread());                          \
264   }                                                             \
265                                                                 \
266  private:                                                       \
267   const INTERNAL_CLASS* c() const { return c_; }                \
268   INTERNAL_CLASS* c() { return c_; }                            \
269   void DestroyInternal() { delete c_; }                         \
270   INTERNAL_CLASS* c_;
271 
272 #define BEGIN_PRIMARY_PROXY_MAP(class_name)                                \
273   PROXY_MAP_BOILERPLATE(class_name)                                        \
274   PRIMARY_PROXY_MAP_BOILERPLATE(class_name)                                \
275   REFCOUNTED_PROXY_MAP_BOILERPLATE(class_name)                             \
276  public:                                                                   \
277   static rtc::scoped_refptr<class_name##ProxyWithInternal> Create(         \
278       rtc::Thread* primary_thread, rtc::scoped_refptr<INTERNAL_CLASS> c) { \
279     return rtc::make_ref_counted<class_name##ProxyWithInternal>(           \
280         primary_thread, std::move(c));                                     \
281   }
282 
283 #define BEGIN_PROXY_MAP(class_name)                                \
284   PROXY_MAP_BOILERPLATE(class_name)                                \
285   SECONDARY_PROXY_MAP_BOILERPLATE(class_name)                      \
286   REFCOUNTED_PROXY_MAP_BOILERPLATE(class_name)                     \
287  public:                                                           \
288   static rtc::scoped_refptr<class_name##ProxyWithInternal> Create( \
289       rtc::Thread* primary_thread, rtc::Thread* secondary_thread,  \
290       rtc::scoped_refptr<INTERNAL_CLASS> c) {                      \
291     return rtc::make_ref_counted<class_name##ProxyWithInternal>(   \
292         primary_thread, secondary_thread, std::move(c));           \
293   }
294 
295 #define PROXY_PRIMARY_THREAD_DESTRUCTOR()                            \
296  private:                                                            \
297   rtc::Thread* destructor_thread() const { return primary_thread_; } \
298                                                                      \
299  public:  // NOLINTNEXTLINE
300 
301 #define PROXY_SECONDARY_THREAD_DESTRUCTOR()                            \
302  private:                                                              \
303   rtc::Thread* destructor_thread() const { return secondary_thread_; } \
304                                                                        \
305  public:  // NOLINTNEXTLINE
306 
307 #if defined(RTC_DISABLE_PROXY_TRACE_EVENTS)
308 #define TRACE_BOILERPLATE(method) \
309   do {                            \
310   } while (0)
311 #else  // if defined(RTC_DISABLE_PROXY_TRACE_EVENTS)
312 #define TRACE_BOILERPLATE(method)                       \
313   static constexpr auto class_and_method_name =         \
314       rtc::MakeCompileTimeString(proxy_name_)           \
315           .Concat(rtc::MakeCompileTimeString("::"))     \
316           .Concat(rtc::MakeCompileTimeString(#method)); \
317   proxy_internal::ScopedTrace scoped_trace(class_and_method_name.string)
318 
319 #endif  // if defined(RTC_DISABLE_PROXY_TRACE_EVENTS)
320 
321 #define PROXY_METHOD0(r, method)            \
322   r method() override {                     \
323     TRACE_BOILERPLATE(method);              \
324     MethodCall<C, r> call(c(), &C::method); \
325     return call.Marshal(primary_thread_);   \
326   }
327 
328 #define PROXY_CONSTMETHOD0(r, method)            \
329   r method() const override {                    \
330     TRACE_BOILERPLATE(method);                   \
331     ConstMethodCall<C, r> call(c(), &C::method); \
332     return call.Marshal(primary_thread_);        \
333   }
334 
335 #define PROXY_METHOD1(r, method, t1)                           \
336   r method(t1 a1) override {                                   \
337     TRACE_BOILERPLATE(method);                                 \
338     MethodCall<C, r, t1> call(c(), &C::method, std::move(a1)); \
339     return call.Marshal(primary_thread_);                      \
340   }
341 
342 #define PROXY_CONSTMETHOD1(r, method, t1)                           \
343   r method(t1 a1) const override {                                  \
344     TRACE_BOILERPLATE(method);                                      \
345     ConstMethodCall<C, r, t1> call(c(), &C::method, std::move(a1)); \
346     return call.Marshal(primary_thread_);                           \
347   }
348 
349 #define PROXY_METHOD2(r, method, t1, t2)                          \
350   r method(t1 a1, t2 a2) override {                               \
351     TRACE_BOILERPLATE(method);                                    \
352     MethodCall<C, r, t1, t2> call(c(), &C::method, std::move(a1), \
353                                   std::move(a2));                 \
354     return call.Marshal(primary_thread_);                         \
355   }
356 
357 #define PROXY_METHOD3(r, method, t1, t2, t3)                          \
358   r method(t1 a1, t2 a2, t3 a3) override {                            \
359     TRACE_BOILERPLATE(method);                                        \
360     MethodCall<C, r, t1, t2, t3> call(c(), &C::method, std::move(a1), \
361                                       std::move(a2), std::move(a3));  \
362     return call.Marshal(primary_thread_);                             \
363   }
364 
365 #define PROXY_METHOD4(r, method, t1, t2, t3, t4)                          \
366   r method(t1 a1, t2 a2, t3 a3, t4 a4) override {                         \
367     TRACE_BOILERPLATE(method);                                            \
368     MethodCall<C, r, t1, t2, t3, t4> call(c(), &C::method, std::move(a1), \
369                                           std::move(a2), std::move(a3),   \
370                                           std::move(a4));                 \
371     return call.Marshal(primary_thread_);                                 \
372   }
373 
374 #define PROXY_METHOD5(r, method, t1, t2, t3, t4, t5)                          \
375   r method(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) override {                      \
376     TRACE_BOILERPLATE(method);                                                \
377     MethodCall<C, r, t1, t2, t3, t4, t5> call(c(), &C::method, std::move(a1), \
378                                               std::move(a2), std::move(a3),   \
379                                               std::move(a4), std::move(a5));  \
380     return call.Marshal(primary_thread_);                                     \
381   }
382 
383 // Define methods which should be invoked on the secondary thread.
384 #define PROXY_SECONDARY_METHOD0(r, method)  \
385   r method() override {                     \
386     TRACE_BOILERPLATE(method);              \
387     MethodCall<C, r> call(c(), &C::method); \
388     return call.Marshal(secondary_thread_); \
389   }
390 
391 #define PROXY_SECONDARY_CONSTMETHOD0(r, method)  \
392   r method() const override {                    \
393     TRACE_BOILERPLATE(method);                   \
394     ConstMethodCall<C, r> call(c(), &C::method); \
395     return call.Marshal(secondary_thread_);      \
396   }
397 
398 #define PROXY_SECONDARY_METHOD1(r, method, t1)                 \
399   r method(t1 a1) override {                                   \
400     TRACE_BOILERPLATE(method);                                 \
401     MethodCall<C, r, t1> call(c(), &C::method, std::move(a1)); \
402     return call.Marshal(secondary_thread_);                    \
403   }
404 
405 #define PROXY_SECONDARY_CONSTMETHOD1(r, method, t1)                 \
406   r method(t1 a1) const override {                                  \
407     TRACE_BOILERPLATE(method);                                      \
408     ConstMethodCall<C, r, t1> call(c(), &C::method, std::move(a1)); \
409     return call.Marshal(secondary_thread_);                         \
410   }
411 
412 #define PROXY_SECONDARY_METHOD2(r, method, t1, t2)                \
413   r method(t1 a1, t2 a2) override {                               \
414     TRACE_BOILERPLATE(method);                                    \
415     MethodCall<C, r, t1, t2> call(c(), &C::method, std::move(a1), \
416                                   std::move(a2));                 \
417     return call.Marshal(secondary_thread_);                       \
418   }
419 
420 #define PROXY_SECONDARY_CONSTMETHOD2(r, method, t1, t2)                \
421   r method(t1 a1, t2 a2) const override {                              \
422     TRACE_BOILERPLATE(method);                                         \
423     ConstMethodCall<C, r, t1, t2> call(c(), &C::method, std::move(a1), \
424                                        std::move(a2));                 \
425     return call.Marshal(secondary_thread_);                            \
426   }
427 
428 #define PROXY_SECONDARY_METHOD3(r, method, t1, t2, t3)                \
429   r method(t1 a1, t2 a2, t3 a3) override {                            \
430     TRACE_BOILERPLATE(method);                                        \
431     MethodCall<C, r, t1, t2, t3> call(c(), &C::method, std::move(a1), \
432                                       std::move(a2), std::move(a3));  \
433     return call.Marshal(secondary_thread_);                           \
434   }
435 
436 #define PROXY_SECONDARY_CONSTMETHOD3(r, method, t1, t2)                    \
437   r method(t1 a1, t2 a2, t3 a3) const override {                           \
438     TRACE_BOILERPLATE(method);                                             \
439     ConstMethodCall<C, r, t1, t2, t3> call(c(), &C::method, std::move(a1), \
440                                            std::move(a2), std::move(a3));  \
441     return call.Marshal(secondary_thread_);                                \
442   }
443 
444 // For use when returning purely const state (set during construction).
445 // Use with caution. This method should only be used when the return value will
446 // always be the same.
447 #define BYPASS_PROXY_CONSTMETHOD0(r, method) \
448   r method() const override {                \
449     TRACE_BOILERPLATE(method);               \
450     return c_->method();                     \
451   }
452 
453 }  // namespace webrtc
454 
455 #endif  //  PC_PROXY_H_
456