• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2014 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 #ifndef RTC_BASE_ASYNC_INVOKER_H_
12 #define RTC_BASE_ASYNC_INVOKER_H_
13 
14 #include <atomic>
15 #include <memory>
16 #include <utility>
17 
18 #include "api/scoped_refptr.h"
19 #include "rtc_base/async_invoker_inl.h"
20 #include "rtc_base/bind.h"
21 #include "rtc_base/constructor_magic.h"
22 #include "rtc_base/event.h"
23 #include "rtc_base/ref_counted_object.h"
24 #include "rtc_base/third_party/sigslot/sigslot.h"
25 #include "rtc_base/thread.h"
26 
27 namespace rtc {
28 
29 // Invokes function objects (aka functors) asynchronously on a Thread, and
30 // owns the lifetime of calls (ie, when this object is destroyed, calls in
31 // flight are cancelled). AsyncInvoker can optionally execute a user-specified
32 // function when the asynchronous call is complete, or operates in
33 // fire-and-forget mode otherwise.
34 //
35 // AsyncInvoker does not own the thread it calls functors on.
36 //
37 // A note about async calls and object lifetimes: users should
38 // be mindful of object lifetimes when calling functions asynchronously and
39 // ensure objects used by the function _cannot_ be deleted between the
40 // invocation and execution of the functor. AsyncInvoker is designed to
41 // help: any calls in flight will be cancelled when the AsyncInvoker used to
42 // make the call is destructed, and any calls executing will be allowed to
43 // complete before AsyncInvoker destructs.
44 //
45 // The easiest way to ensure lifetimes are handled correctly is to create a
46 // class that owns the Thread and AsyncInvoker objects, and then call its
47 // methods asynchronously as needed.
48 //
49 // Example:
50 //   class MyClass {
51 //    public:
52 //     void FireAsyncTaskWithResult(Thread* thread, int x) {
53 //       // Specify a callback to get the result upon completion.
54 //       invoker_.AsyncInvoke<int>(RTC_FROM_HERE,
55 //           thread, Bind(&MyClass::AsyncTaskWithResult, this, x),
56 //           &MyClass::OnTaskComplete, this);
57 //     }
58 //     void FireAnotherAsyncTask(Thread* thread) {
59 //       // No callback specified means fire-and-forget.
60 //       invoker_.AsyncInvoke<void>(RTC_FROM_HERE,
61 //           thread, Bind(&MyClass::AnotherAsyncTask, this));
62 //
63 //    private:
64 //     int AsyncTaskWithResult(int x) {
65 //       // Some long running process...
66 //       return x * x;
67 //     }
68 //     void AnotherAsyncTask() {
69 //       // Some other long running process...
70 //     }
71 //     void OnTaskComplete(int result) { result_ = result; }
72 //
73 //     AsyncInvoker invoker_;
74 //     int result_;
75 //   };
76 //
77 // More details about threading:
78 // - It's safe to construct/destruct AsyncInvoker on different threads.
79 // - It's safe to call AsyncInvoke from different threads.
80 // - It's safe to call AsyncInvoke recursively from *within* a functor that's
81 //   being AsyncInvoked.
82 // - However, it's *not* safe to call AsyncInvoke from *outside* a functor
83 //   that's being AsyncInvoked while the AsyncInvoker is being destroyed on
84 //   another thread. This is just inherently unsafe and there's no way to
85 //   prevent that. So, the user of this class should ensure that the start of
86 //   each "chain" of invocations is synchronized somehow with the AsyncInvoker's
87 //   destruction. This can be done by starting each chain of invocations on the
88 //   same thread on which it will be destroyed, or by using some other
89 //   synchronization method.
90 class AsyncInvoker : public MessageHandler {
91  public:
92   AsyncInvoker();
93   ~AsyncInvoker() override;
94 
95   // Call |functor| asynchronously on |thread|, with no callback upon
96   // completion. Returns immediately.
97   template <class ReturnT, class FunctorT>
98   void AsyncInvoke(const Location& posted_from,
99                    Thread* thread,
100                    FunctorT&& functor,
101                    uint32_t id = 0) {
102     std::unique_ptr<AsyncClosure> closure(
103         new FireAndForgetAsyncClosure<FunctorT>(
104             this, std::forward<FunctorT>(functor)));
105     DoInvoke(posted_from, thread, std::move(closure), id);
106   }
107 
108   // Call |functor| asynchronously on |thread| with |delay_ms|, with no callback
109   // upon completion. Returns immediately.
110   template <class ReturnT, class FunctorT>
111   void AsyncInvokeDelayed(const Location& posted_from,
112                           Thread* thread,
113                           FunctorT&& functor,
114                           uint32_t delay_ms,
115                           uint32_t id = 0) {
116     std::unique_ptr<AsyncClosure> closure(
117         new FireAndForgetAsyncClosure<FunctorT>(
118             this, std::forward<FunctorT>(functor)));
119     DoInvokeDelayed(posted_from, thread, std::move(closure), delay_ms, id);
120   }
121 
122   // Synchronously execute on |thread| all outstanding calls we own
123   // that are pending on |thread|, and wait for calls to complete
124   // before returning. Optionally filter by message id.
125   // The destructor will not wait for outstanding calls, so if that
126   // behavior is desired, call Flush() before destroying this object.
127   void Flush(Thread* thread, uint32_t id = MQID_ANY);
128 
129   // Cancels any outstanding calls we own that are pending on any thread, and
130   // which have not yet started to execute. This does not wait for any calls
131   // that have already started executing to complete.
132   void Clear();
133 
134  private:
135   void OnMessage(Message* msg) override;
136   void DoInvoke(const Location& posted_from,
137                 Thread* thread,
138                 std::unique_ptr<AsyncClosure> closure,
139                 uint32_t id);
140   void DoInvokeDelayed(const Location& posted_from,
141                        Thread* thread,
142                        std::unique_ptr<AsyncClosure> closure,
143                        uint32_t delay_ms,
144                        uint32_t id);
145 
146   // Used to keep track of how many invocations (AsyncClosures) are still
147   // alive, so that the destructor can wait for them to finish, as described in
148   // the class documentation.
149   //
150   // TODO(deadbeef): Using a raw std::atomic like this is prone to error and
151   // difficult to maintain. We should try to wrap this functionality in a
152   // separate class to reduce the chance of errors being introduced in the
153   // future.
154   std::atomic<int> pending_invocations_;
155 
156   // Reference counted so that if the AsyncInvoker destructor finishes before
157   // an AsyncClosure's destructor that's about to call
158   // "invocation_complete_->Set()", it's not dereferenced after being
159   // destroyed.
160   scoped_refptr<RefCountedObject<Event>> invocation_complete_;
161 
162   // This flag is used to ensure that if an application AsyncInvokes tasks that
163   // recursively AsyncInvoke other tasks ad infinitum, the cycle eventually
164   // terminates.
165   std::atomic<bool> destroying_;
166 
167   friend class AsyncClosure;
168 
169   RTC_DISALLOW_COPY_AND_ASSIGN(AsyncInvoker);
170 };
171 
172 }  // namespace rtc
173 
174 #endif  // RTC_BASE_ASYNC_INVOKER_H_
175