/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_TASK_H_ #define BASE_TASK_H_ #include "base/revocable_store.h" #include "base/tuple.h" #include "mozilla/IndexSequence.h" #include "mozilla/Tuple.h" #include "nsISupportsImpl.h" #include "nsThreadUtils.h" // Helper functions so that we can call a function a pass it arguments that come // from a Tuple. namespace details { // Call the given method on the given object. Arguments are passed by move // semantics from the given tuple. If the tuple has length N, the sequence must // be IndexSequence<0, 1, ..., N-1>. template<size_t... Indices, class ObjT, class Method, typename... Args> void CallMethod(mozilla::IndexSequence<Indices...>, ObjT* obj, Method method, mozilla::Tuple<Args...>& arg) { (obj->*method)(mozilla::Move(mozilla::Get<Indices>(arg))...); } // Same as above, but call a function. template<size_t... Indices, typename Function, typename... Args> void CallFunction(mozilla::IndexSequence<Indices...>, Function function, mozilla::Tuple<Args...>& arg) { (*function)(mozilla::Move(mozilla::Get<Indices>(arg))...); } } // namespace details // Call a method on the given object. Arguments are passed by move semantics // from the given tuple. template<class ObjT, class Method, typename... Args> void DispatchTupleToMethod(ObjT* obj, Method method, mozilla::Tuple<Args...>& arg) { details::CallMethod(typename mozilla::IndexSequenceFor<Args...>::Type(), obj, method, arg); } // Same as above, but call a function. template<typename Function, typename... Args> void DispatchTupleToFunction(Function function, mozilla::Tuple<Args...>& arg) { details::CallFunction(typename mozilla::IndexSequenceFor<Args...>::Type(), function, arg); } // Scoped Factories ------------------------------------------------------------ // // These scoped factory objects can be used by non-refcounted objects to safely // place tasks in a message loop. Each factory guarantees that the tasks it // produces will not run after the factory is destroyed. Commonly, factories // are declared as class members, so the class' tasks will automatically cancel // when the class instance is destroyed. // // Exampe Usage: // // class MyClass { // private: // // This factory will be used to schedule invocations of SomeMethod. // ScopedRunnableMethodFactory<MyClass> some_method_factory_; // // public: // // It is safe to suppress warning 4355 here. // MyClass() : some_method_factory_(this) { } // // void SomeMethod() { // // If this function might be called directly, you might want to revoke // // any outstanding runnable methods scheduled to call it. If it's not // // referenced other than by the factory, this is unnecessary. // some_method_factory_.RevokeAll(); // ... // } // // void ScheduleSomeMethod() { // // If you'd like to only only have one pending task at a time, test for // // |empty| before manufacturing another task. // if (!some_method_factory_.empty()) // return; // // // The factories are not thread safe, so always invoke on // // |MessageLoop::current()|. // MessageLoop::current()->PostDelayedTask( // some_method_factory_.NewRunnableMethod(&MyClass::SomeMethod), // kSomeMethodDelayMS); // } // }; // A ScopedTaskFactory produces tasks of type |TaskType| and prevents them from // running after it is destroyed. template<class TaskType> class ScopedTaskFactory : public RevocableStore { public: ScopedTaskFactory() { } // Create a new task. inline TaskType* NewTask() { return new TaskWrapper(this); } class TaskWrapper : public TaskType { public: explicit TaskWrapper(RevocableStore* store) : revocable_(store) { } NS_IMETHOD Run() override { if (!revocable_.revoked()) TaskType::Run(); return NS_OK; } ~TaskWrapper() { NS_ASSERT_OWNINGTHREAD(TaskWrapper); } private: Revocable revocable_; NS_DECL_OWNINGTHREAD DISALLOW_EVIL_CONSTRUCTORS(TaskWrapper); }; private: DISALLOW_EVIL_CONSTRUCTORS(ScopedTaskFactory); }; // A ScopedRunnableMethodFactory creates runnable methods for a specified // object. This is particularly useful for generating callbacks for // non-reference counted objects when the factory is a member of the object. template<class T> class ScopedRunnableMethodFactory : public RevocableStore { public: explicit ScopedRunnableMethodFactory(T* object) : object_(object) { } template <class Method, typename... Elements> inline already_AddRefed<mozilla::Runnable> NewRunnableMethod(Method method, Elements&&... elements) { typedef mozilla::Tuple<typename mozilla::Decay<Elements>::Type...> ArgsTuple; typedef RunnableMethod<Method, ArgsTuple> Runnable; typedef typename ScopedTaskFactory<Runnable>::TaskWrapper TaskWrapper; RefPtr<TaskWrapper> task = new TaskWrapper(this); task->Init(object_, method, mozilla::MakeTuple(mozilla::Forward<Elements>(elements)...)); return task.forget(); } protected: template <class Method, class Params> class RunnableMethod : public mozilla::Runnable { public: RunnableMethod() { } void Init(T* obj, Method meth, Params&& params) { obj_ = obj; meth_ = meth; params_ = mozilla::Forward<Params>(params); } NS_IMETHOD Run() override { DispatchTupleToMethod(obj_, meth_, params_); return NS_OK; } private: T* MOZ_UNSAFE_REF("The validity of this pointer must be enforced by " "external factors.") obj_; Method meth_; Params params_; DISALLOW_EVIL_CONSTRUCTORS(RunnableMethod); }; private: T* object_; DISALLOW_EVIL_CONSTRUCTORS(ScopedRunnableMethodFactory); }; // General task implementations ------------------------------------------------ // Task to delete an object template<class T> class DeleteTask : public mozilla::CancelableRunnable { public: explicit DeleteTask(T* obj) : obj_(obj) { } NS_IMETHOD Run() override { delete obj_; return NS_OK; } virtual nsresult Cancel() override { obj_ = NULL; return NS_OK; } private: T* MOZ_UNSAFE_REF("The validity of this pointer must be enforced by " "external factors.") obj_; }; // RunnableMethodTraits -------------------------------------------------------- // // This traits-class is used by RunnableMethod to manage the lifetime of the // callee object. By default, it is assumed that the callee supports AddRef // and Release methods. A particular class can specialize this template to // define other lifetime management. For example, if the callee is known to // live longer than the RunnableMethod object, then a RunnableMethodTraits // struct could be defined with empty RetainCallee and ReleaseCallee methods. template <class T> struct RunnableMethodTraits { static void RetainCallee(T* obj) { obj->AddRef(); } static void ReleaseCallee(T* obj) { obj->Release(); } }; // This allows using the NewRunnableMethod() functions with a const pointer // to the callee object. See the similar support in nsRefPtr for a rationale // of why this is reasonable. template <class T> struct RunnableMethodTraits<const T> { static void RetainCallee(const T* obj) { const_cast<T*>(obj)->AddRef(); } static void ReleaseCallee(const T* obj) { const_cast<T*>(obj)->Release(); } }; // RunnableMethod and RunnableFunction ----------------------------------------- // // Runnable methods are a type of task that call a function on an object when // they are run. We implement both an object and a set of NewRunnableMethod and // NewRunnableFunction functions for convenience. These functions are // overloaded and will infer the template types, simplifying calling code. // // The template definitions all use the following names: // T - the class type of the object you're supplying // this is not needed for the Static version of the call // Method/Function - the signature of a pointer to the method or function you // want to call // Param - the parameter(s) to the method, possibly packed as a Tuple // A - the first parameter (if any) to the method // B - the second parameter (if any) to the mathod // // Put these all together and you get an object that can call a method whose // signature is: // R T::MyFunction([A[, B]]) // // Usage: // PostTask(NewRunnableMethod(object, &Object::method[, a[, b]]) // PostTask(NewRunnableFunction(&function[, a[, b]]) // RunnableMethod and NewRunnableMethod implementation ------------------------- template <class T, class Method, class Params> class RunnableMethod : public mozilla::CancelableRunnable, public RunnableMethodTraits<T> { public: RunnableMethod(T* obj, Method meth, Params&& params) : obj_(obj), meth_(meth), params_(mozilla::Forward<Params>(params)) { this->RetainCallee(obj_); } ~RunnableMethod() { ReleaseCallee(); } NS_IMETHOD Run() override { if (obj_) DispatchTupleToMethod(obj_, meth_, params_); return NS_OK; } virtual nsresult Cancel() override { ReleaseCallee(); return NS_OK; } private: void ReleaseCallee() { if (obj_) { RunnableMethodTraits<T>::ReleaseCallee(obj_); obj_ = nullptr; } } // This is owning because of the RetainCallee and ReleaseCallee calls in the // constructor and destructor. T* MOZ_OWNING_REF obj_; Method meth_; Params params_; }; namespace dont_add_new_uses_of_this { // Don't add new uses of this!!!! template <class T, class Method, typename... Args> inline already_AddRefed<mozilla::Runnable> NewRunnableMethod(T* object, Method method, Args&&... args) { typedef mozilla::Tuple<typename mozilla::Decay<Args>::Type...> ArgsTuple; RefPtr<mozilla::Runnable> t = new RunnableMethod<T, Method, ArgsTuple>(object, method, mozilla::MakeTuple(mozilla::Forward<Args>(args)...)); return t.forget(); } } // namespace dont_add_new_uses_of_this // RunnableFunction and NewRunnableFunction implementation --------------------- template <class Function, class Params> class RunnableFunction : public mozilla::CancelableRunnable { public: RunnableFunction(Function function, Params&& params) : function_(function), params_(mozilla::Forward<Params>(params)) { } ~RunnableFunction() { } NS_IMETHOD Run() override { if (function_) DispatchTupleToFunction(function_, params_); return NS_OK; } virtual nsresult Cancel() override { function_ = nullptr; return NS_OK; } Function function_; Params params_; }; template <class Function, typename... Args> inline already_AddRefed<mozilla::CancelableRunnable> NewCancelableRunnableFunction(Function function, Args&&... args) { typedef mozilla::Tuple<typename mozilla::Decay<Args>::Type...> ArgsTuple; RefPtr<mozilla::CancelableRunnable> t = new RunnableFunction<Function, ArgsTuple>(function, mozilla::MakeTuple(mozilla::Forward<Args>(args)...)); return t.forget(); } template <class Function, typename... Args> inline already_AddRefed<mozilla::Runnable> NewRunnableFunction(Function function, Args&&... args) { typedef mozilla::Tuple<typename mozilla::Decay<Args>::Type...> ArgsTuple; RefPtr<mozilla::Runnable> t = new RunnableFunction<Function, ArgsTuple>(function, mozilla::MakeTuple(mozilla::Forward<Args>(args)...)); return t.forget(); } #endif // BASE_TASK_H_