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