1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
6
7 #ifndef BASE_TASK_H_
8 #define BASE_TASK_H_
9
10 #include "base/revocable_store.h"
11 #include "base/tuple.h"
12 #include "mozilla/Tuple.h"
13 #include "nsISupportsImpl.h"
14 #include "nsThreadUtils.h"
15
16 #include <type_traits>
17 #include <utility>
18
19 // Helper functions so that we can call a function a pass it arguments that come
20 // from a Tuple.
21
22 namespace details {
23
24 // Call the given method on the given object. Arguments are passed by move
25 // semantics from the given tuple. If the tuple has length N, the sequence must
26 // be IndexSequence<0, 1, ..., N-1>.
27 template <size_t... Indices, class ObjT, class Method, typename... Args>
CallMethod(std::index_sequence<Indices...>,ObjT * obj,Method method,mozilla::Tuple<Args...> & arg)28 void CallMethod(std::index_sequence<Indices...>, ObjT* obj, Method method,
29 mozilla::Tuple<Args...>& arg) {
30 (obj->*method)(std::move(mozilla::Get<Indices>(arg))...);
31 }
32
33 // Same as above, but call a function.
34 template <size_t... Indices, typename Function, typename... Args>
CallFunction(std::index_sequence<Indices...>,Function function,mozilla::Tuple<Args...> & arg)35 void CallFunction(std::index_sequence<Indices...>, Function function,
36 mozilla::Tuple<Args...>& arg) {
37 (*function)(std::move(mozilla::Get<Indices>(arg))...);
38 }
39
40 } // namespace details
41
42 // Call a method on the given object. Arguments are passed by move semantics
43 // from the given tuple.
44 template <class ObjT, class Method, typename... Args>
DispatchTupleToMethod(ObjT * obj,Method method,mozilla::Tuple<Args...> & arg)45 void DispatchTupleToMethod(ObjT* obj, Method method,
46 mozilla::Tuple<Args...>& arg) {
47 details::CallMethod(std::index_sequence_for<Args...>{}, obj, method, arg);
48 }
49
50 // Same as above, but call a function.
51 template <typename Function, typename... Args>
DispatchTupleToFunction(Function function,mozilla::Tuple<Args...> & arg)52 void DispatchTupleToFunction(Function function, mozilla::Tuple<Args...>& arg) {
53 details::CallFunction(std::index_sequence_for<Args...>{}, function, arg);
54 }
55
56 // Scoped Factories ------------------------------------------------------------
57 //
58 // These scoped factory objects can be used by non-refcounted objects to safely
59 // place tasks in a message loop. Each factory guarantees that the tasks it
60 // produces will not run after the factory is destroyed. Commonly, factories
61 // are declared as class members, so the class' tasks will automatically cancel
62 // when the class instance is destroyed.
63 //
64 // Exampe Usage:
65 //
66 // class MyClass {
67 // private:
68 // // This factory will be used to schedule invocations of SomeMethod.
69 // ScopedRunnableMethodFactory<MyClass> some_method_factory_;
70 //
71 // public:
72 // // It is safe to suppress warning 4355 here.
73 // MyClass() : some_method_factory_(this) { }
74 //
75 // void SomeMethod() {
76 // // If this function might be called directly, you might want to revoke
77 // // any outstanding runnable methods scheduled to call it. If it's not
78 // // referenced other than by the factory, this is unnecessary.
79 // some_method_factory_.RevokeAll();
80 // ...
81 // }
82 //
83 // void ScheduleSomeMethod() {
84 // // If you'd like to only only have one pending task at a time, test for
85 // // |empty| before manufacturing another task.
86 // if (!some_method_factory_.empty())
87 // return;
88 //
89 // // The factories are not thread safe, so always invoke on
90 // // |MessageLoop::current()|.
91 // MessageLoop::current()->PostDelayedTask(
92 // some_method_factory_.NewRunnableMethod(&MyClass::SomeMethod),
93 // kSomeMethodDelayMS);
94 // }
95 // };
96
97 // A ScopedTaskFactory produces tasks of type |TaskType| and prevents them from
98 // running after it is destroyed.
99 template <class TaskType>
100 class ScopedTaskFactory : public RevocableStore {
101 public:
ScopedTaskFactory()102 ScopedTaskFactory() {}
103
104 // Create a new task.
NewTask()105 inline TaskType* NewTask() { return new TaskWrapper(this); }
106
107 class TaskWrapper : public TaskType {
108 public:
TaskWrapper(RevocableStore * store)109 explicit TaskWrapper(RevocableStore* store) : revocable_(store) {}
110
Run()111 NS_IMETHOD Run() override {
112 if (!revocable_.revoked()) TaskType::Run();
113 return NS_OK;
114 }
115
~TaskWrapper()116 ~TaskWrapper() { NS_ASSERT_OWNINGTHREAD(TaskWrapper); }
117
118 private:
119 Revocable revocable_;
120
121 NS_DECL_OWNINGTHREAD
122
123 DISALLOW_EVIL_CONSTRUCTORS(TaskWrapper);
124 };
125
126 private:
127 DISALLOW_EVIL_CONSTRUCTORS(ScopedTaskFactory);
128 };
129
130 // A ScopedRunnableMethodFactory creates runnable methods for a specified
131 // object. This is particularly useful for generating callbacks for
132 // non-reference counted objects when the factory is a member of the object.
133 template <class T>
134 class ScopedRunnableMethodFactory : public RevocableStore {
135 public:
ScopedRunnableMethodFactory(T * object)136 explicit ScopedRunnableMethodFactory(T* object) : object_(object) {}
137
138 template <class Method, typename... Elements>
NewRunnableMethod(Method method,Elements &&...elements)139 inline already_AddRefed<mozilla::Runnable> NewRunnableMethod(
140 Method method, Elements&&... elements) {
141 typedef mozilla::Tuple<std::decay_t<Elements>...> ArgsTuple;
142 typedef RunnableMethod<Method, ArgsTuple> Runnable;
143 typedef typename ScopedTaskFactory<Runnable>::TaskWrapper TaskWrapper;
144
145 RefPtr<TaskWrapper> task = new TaskWrapper(this);
146 task->Init(object_, method,
147 mozilla::MakeTuple(std::forward<Elements>(elements)...));
148 return task.forget();
149 }
150
151 protected:
152 template <class Method, class Params>
153 class RunnableMethod : public mozilla::Runnable {
154 public:
RunnableMethod()155 RunnableMethod()
156 : mozilla::Runnable("ScopedRunnableMethodFactory::RunnableMethod") {}
157
Init(T * obj,Method meth,Params && params)158 void Init(T* obj, Method meth, Params&& params) {
159 obj_ = obj;
160 meth_ = meth;
161 params_ = std::forward<Params>(params);
162 }
163
Run()164 NS_IMETHOD Run() override {
165 DispatchTupleToMethod(obj_, meth_, params_);
166 return NS_OK;
167 }
168
169 private:
170 T* MOZ_UNSAFE_REF(
171 "The validity of this pointer must be enforced by "
172 "external factors.") obj_;
173 Method meth_;
174 Params params_;
175
176 DISALLOW_EVIL_CONSTRUCTORS(RunnableMethod);
177 };
178
179 private:
180 T* object_;
181
182 DISALLOW_EVIL_CONSTRUCTORS(ScopedRunnableMethodFactory);
183 };
184
185 // General task implementations ------------------------------------------------
186
187 // Task to delete an object
188 template <class T>
189 class DeleteTask : public mozilla::CancelableRunnable {
190 public:
DeleteTask(T * obj)191 explicit DeleteTask(T* obj)
192 : mozilla::CancelableRunnable("DeleteTask"), obj_(obj) {}
Run()193 NS_IMETHOD Run() override {
194 delete obj_;
195 return NS_OK;
196 }
Cancel()197 virtual nsresult Cancel() override {
198 obj_ = NULL;
199 return NS_OK;
200 }
201
202 private:
203 T* MOZ_UNSAFE_REF(
204 "The validity of this pointer must be enforced by "
205 "external factors.") obj_;
206 };
207
208 // RunnableMethodTraits --------------------------------------------------------
209 //
210 // This traits-class is used by RunnableMethod to manage the lifetime of the
211 // callee object. By default, it is assumed that the callee supports AddRef
212 // and Release methods. A particular class can specialize this template to
213 // define other lifetime management. For example, if the callee is known to
214 // live longer than the RunnableMethod object, then a RunnableMethodTraits
215 // struct could be defined with empty RetainCallee and ReleaseCallee methods.
216
217 template <class T>
218 struct RunnableMethodTraits {
RetainCalleeRunnableMethodTraits219 static void RetainCallee(T* obj) { obj->AddRef(); }
ReleaseCalleeRunnableMethodTraits220 static void ReleaseCallee(T* obj) { obj->Release(); }
221 };
222
223 // This allows using the NewRunnableMethod() functions with a const pointer
224 // to the callee object. See the similar support in nsRefPtr for a rationale
225 // of why this is reasonable.
226 template <class T>
227 struct RunnableMethodTraits<const T> {
228 static void RetainCallee(const T* obj) { const_cast<T*>(obj)->AddRef(); }
229 static void ReleaseCallee(const T* obj) { const_cast<T*>(obj)->Release(); }
230 };
231
232 // RunnableMethod and RunnableFunction -----------------------------------------
233 //
234 // Runnable methods are a type of task that call a function on an object when
235 // they are run. We implement both an object and a set of NewRunnableMethod and
236 // NewRunnableFunction functions for convenience. These functions are
237 // overloaded and will infer the template types, simplifying calling code.
238 //
239 // The template definitions all use the following names:
240 // T - the class type of the object you're supplying
241 // this is not needed for the Static version of the call
242 // Method/Function - the signature of a pointer to the method or function you
243 // want to call
244 // Param - the parameter(s) to the method, possibly packed as a Tuple
245 // A - the first parameter (if any) to the method
246 // B - the second parameter (if any) to the mathod
247 //
248 // Put these all together and you get an object that can call a method whose
249 // signature is:
250 // R T::MyFunction([A[, B]])
251 //
252 // Usage:
253 // PostTask(NewRunnableMethod(object, &Object::method[, a[, b]])
254 // PostTask(NewRunnableFunction(&function[, a[, b]])
255
256 // RunnableMethod and NewRunnableMethod implementation -------------------------
257
258 template <class T, class Method, class Params>
259 class RunnableMethod : public mozilla::CancelableRunnable,
260 public RunnableMethodTraits<T> {
261 public:
262 RunnableMethod(T* obj, Method meth, Params&& params)
263 : mozilla::CancelableRunnable("RunnableMethod"),
264 obj_(obj),
265 meth_(meth),
266 params_(std::forward<Params>(params)) {
267 this->RetainCallee(obj_);
268 }
269 ~RunnableMethod() { ReleaseCallee(); }
270
271 NS_IMETHOD Run() override {
272 if (obj_) DispatchTupleToMethod(obj_, meth_, params_);
273 return NS_OK;
274 }
275
276 virtual nsresult Cancel() override {
277 ReleaseCallee();
278 return NS_OK;
279 }
280
281 private:
282 void ReleaseCallee() {
283 if (obj_) {
284 RunnableMethodTraits<T>::ReleaseCallee(obj_);
285 obj_ = nullptr;
286 }
287 }
288
289 // This is owning because of the RetainCallee and ReleaseCallee calls in the
290 // constructor and destructor.
291 T* MOZ_OWNING_REF obj_;
292 Method meth_;
293 Params params_;
294 };
295
296 namespace dont_add_new_uses_of_this {
297
298 // Don't add new uses of this!!!!
299 template <class T, class Method, typename... Args>
300 inline already_AddRefed<mozilla::Runnable> NewRunnableMethod(T* object,
301 Method method,
302 Args&&... args) {
303 typedef mozilla::Tuple<std::decay_t<Args>...> ArgsTuple;
304 RefPtr<mozilla::Runnable> t = new RunnableMethod<T, Method, ArgsTuple>(
305 object, method, mozilla::MakeTuple(std::forward<Args>(args)...));
306 return t.forget();
307 }
308
309 } // namespace dont_add_new_uses_of_this
310
311 // RunnableFunction and NewRunnableFunction implementation ---------------------
312
313 template <class Function, class Params>
314 class RunnableFunction : public mozilla::CancelableRunnable {
315 public:
316 RunnableFunction(const char* name, Function function, Params&& params)
317 : mozilla::CancelableRunnable(name),
318 function_(function),
319 params_(std::forward<Params>(params)) {}
320
321 ~RunnableFunction() {}
322
323 NS_IMETHOD Run() override {
324 if (function_) DispatchTupleToFunction(function_, params_);
325 return NS_OK;
326 }
327
328 virtual nsresult Cancel() override {
329 function_ = nullptr;
330 return NS_OK;
331 }
332
333 Function function_;
334 Params params_;
335 };
336
337 template <class Function, typename... Args>
338 inline already_AddRefed<mozilla::CancelableRunnable>
339 NewCancelableRunnableFunction(const char* name, Function function,
340 Args&&... args) {
341 typedef mozilla::Tuple<std::decay_t<Args>...> ArgsTuple;
342 RefPtr<mozilla::CancelableRunnable> t =
343 new RunnableFunction<Function, ArgsTuple>(
344 name, function, mozilla::MakeTuple(std::forward<Args>(args)...));
345 return t.forget();
346 }
347
348 template <class Function, typename... Args>
349 inline already_AddRefed<mozilla::Runnable> NewRunnableFunction(
350 const char* name, Function function, Args&&... args) {
351 typedef mozilla::Tuple<std::decay_t<Args>...> ArgsTuple;
352 RefPtr<mozilla::Runnable> t = new RunnableFunction<Function, ArgsTuple>(
353 name, function, mozilla::MakeTuple(std::forward<Args>(args)...));
354 return t.forget();
355 }
356
357 #endif // BASE_TASK_H_
358