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