blob: 523e9a95d162fff98a145309dd3b3c327f725286 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#ifndef RTC_BASE_ASYNCINVOKER_H_
12#define RTC_BASE_ASYNCINVOKER_H_
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000013
deadbeef3af63b02017-08-08 17:59:47 -070014#include <atomic>
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020015#include <memory>
16#include <utility>
deadbeefa8bc1a12017-02-17 18:06:26 -080017
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/asyncinvoker-inl.h"
19#include "rtc_base/bind.h"
20#include "rtc_base/constructormagic.h"
21#include "rtc_base/event.h"
22#include "rtc_base/refcountedobject.h"
23#include "rtc_base/scoped_ref_ptr.h"
24#include "rtc_base/sigslot.h"
25#include "rtc_base/thread.h"
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020026
27namespace 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// };
deadbeef3af63b02017-08-08 17:59:47 -070076//
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.
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020090class 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 const FunctorT& functor,
101 uint32_t id = 0) {
102 std::unique_ptr<AsyncClosure> closure(
103 new FireAndForgetAsyncClosure<FunctorT>(this, functor));
104 DoInvoke(posted_from, thread, std::move(closure), id);
105 }
106
107 // Call |functor| asynchronously on |thread| with |delay_ms|, with no callback
108 // upon completion. Returns immediately.
109 template <class ReturnT, class FunctorT>
110 void AsyncInvokeDelayed(const Location& posted_from,
111 Thread* thread,
112 const FunctorT& functor,
113 uint32_t delay_ms,
114 uint32_t id = 0) {
115 std::unique_ptr<AsyncClosure> closure(
116 new FireAndForgetAsyncClosure<FunctorT>(this, functor));
117 DoInvokeDelayed(posted_from, thread, std::move(closure), delay_ms, id);
118 }
119
120 // Synchronously execute on |thread| all outstanding calls we own
121 // that are pending on |thread|, and wait for calls to complete
122 // before returning. Optionally filter by message id.
123 // The destructor will not wait for outstanding calls, so if that
124 // behavior is desired, call Flush() before destroying this object.
125 void Flush(Thread* thread, uint32_t id = MQID_ANY);
126
Chris Dziemborowiczc38d3202018-01-31 12:52:24 -0800127 // Cancels any outstanding calls we own that are pending on any thread, and
128 // which have not yet started to execute. This does not wait for any calls
129 // that have already started executing to complete.
130 void Clear();
131
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200132 private:
133 void OnMessage(Message* msg) override;
134 void DoInvoke(const Location& posted_from,
135 Thread* thread,
136 std::unique_ptr<AsyncClosure> closure,
137 uint32_t id);
138 void DoInvokeDelayed(const Location& posted_from,
139 Thread* thread,
140 std::unique_ptr<AsyncClosure> closure,
141 uint32_t delay_ms,
142 uint32_t id);
deadbeef3af63b02017-08-08 17:59:47 -0700143
144 // Used to keep track of how many invocations (AsyncClosures) are still
145 // alive, so that the destructor can wait for them to finish, as described in
146 // the class documentation.
147 //
148 // TODO(deadbeef): Using a raw std::atomic like this is prone to error and
149 // difficult to maintain. We should try to wrap this functionality in a
150 // separate class to reduce the chance of errors being introduced in the
151 // future.
152 std::atomic<int> pending_invocations_;
153
154 // Reference counted so that if the AsyncInvoker destructor finishes before
155 // an AsyncClosure's destructor that's about to call
156 // "invocation_complete_->Set()", it's not dereferenced after being
157 // destroyed.
158 scoped_refptr<RefCountedObject<Event>> invocation_complete_;
159
160 // This flag is used to ensure that if an application AsyncInvokes tasks that
161 // recursively AsyncInvoke other tasks ad infinitum, the cycle eventually
162 // terminates.
163 std::atomic<bool> destroying_;
164
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200165 friend class AsyncClosure;
166
167 RTC_DISALLOW_COPY_AND_ASSIGN(AsyncInvoker);
168};
169
170// Similar to AsyncInvoker, but guards against the Thread being destroyed while
171// there are outstanding dangling pointers to it. It will connect to the current
172// thread in the constructor, and will get notified when that thread is
173// destroyed. After GuardedAsyncInvoker is constructed, it can be used from
174// other threads to post functors to the thread it was constructed on. If that
175// thread dies, any further calls to AsyncInvoke() will be safely ignored.
176class GuardedAsyncInvoker : public sigslot::has_slots<> {
177 public:
178 GuardedAsyncInvoker();
179 ~GuardedAsyncInvoker() override;
180
181 // Synchronously execute all outstanding calls we own, and wait for calls to
182 // complete before returning. Optionally filter by message id. The destructor
183 // will not wait for outstanding calls, so if that behavior is desired, call
184 // Flush() first. Returns false if the thread has died.
185 bool Flush(uint32_t id = MQID_ANY);
186
187 // Call |functor| asynchronously with no callback upon completion. Returns
188 // immediately. Returns false if the thread has died.
189 template <class ReturnT, class FunctorT>
190 bool AsyncInvoke(const Location& posted_from,
191 const FunctorT& functor,
192 uint32_t id = 0) {
deadbeef3af63b02017-08-08 17:59:47 -0700193 CritScope cs(&crit_);
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200194 if (thread_ == nullptr)
195 return false;
196 invoker_.AsyncInvoke<ReturnT, FunctorT>(posted_from, thread_, functor, id);
197 return true;
198 }
199
200 // Call |functor| asynchronously with |delay_ms|, with no callback upon
201 // completion. Returns immediately. Returns false if the thread has died.
202 template <class ReturnT, class FunctorT>
203 bool AsyncInvokeDelayed(const Location& posted_from,
204 const FunctorT& functor,
205 uint32_t delay_ms,
206 uint32_t id = 0) {
deadbeef3af63b02017-08-08 17:59:47 -0700207 CritScope cs(&crit_);
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200208 if (thread_ == nullptr)
209 return false;
210 invoker_.AsyncInvokeDelayed<ReturnT, FunctorT>(posted_from, thread_,
211 functor, delay_ms, id);
212 return true;
213 }
214
215 // Call |functor| asynchronously, calling |callback| when done. Returns false
216 // if the thread has died.
217 template <class ReturnT, class FunctorT, class HostT>
218 bool AsyncInvoke(const Location& posted_from,
219 const Location& callback_posted_from,
220 const FunctorT& functor,
221 void (HostT::*callback)(ReturnT),
222 HostT* callback_host,
223 uint32_t id = 0) {
deadbeef3af63b02017-08-08 17:59:47 -0700224 CritScope cs(&crit_);
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200225 if (thread_ == nullptr)
226 return false;
227 invoker_.AsyncInvoke<ReturnT, FunctorT, HostT>(
228 posted_from, callback_posted_from, thread_, functor, callback,
229 callback_host, id);
230 return true;
231 }
232
233 // Call |functor| asynchronously calling |callback| when done. Overloaded for
234 // void return. Returns false if the thread has died.
235 template <class ReturnT, class FunctorT, class HostT>
236 bool AsyncInvoke(const Location& posted_from,
237 const Location& callback_posted_from,
238 const FunctorT& functor,
239 void (HostT::*callback)(),
240 HostT* callback_host,
241 uint32_t id = 0) {
deadbeef3af63b02017-08-08 17:59:47 -0700242 CritScope cs(&crit_);
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200243 if (thread_ == nullptr)
244 return false;
245 invoker_.AsyncInvoke<ReturnT, FunctorT, HostT>(
246 posted_from, callback_posted_from, thread_, functor, callback,
247 callback_host, id);
248 return true;
249 }
250
251 private:
252 // Callback when |thread_| is destroyed.
253 void ThreadDestroyed();
254
255 CriticalSection crit_;
danilchap3c6abd22017-09-06 05:46:29 -0700256 Thread* thread_ RTC_GUARDED_BY(crit_);
257 AsyncInvoker invoker_ RTC_GUARDED_BY(crit_);
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200258};
259
260} // namespace rtc
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000261
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200262#endif // RTC_BASE_ASYNCINVOKER_H_