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