diff options
Diffstat (limited to 'ipc/chromium/src/base/task.h')
-rw-r--r-- | ipc/chromium/src/base/task.h | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/task.h b/ipc/chromium/src/base/task.h new file mode 100644 index 0000000000..842fa8487f --- /dev/null +++ b/ipc/chromium/src/base/task.h @@ -0,0 +1,356 @@ +/* -*- 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 "nsISupportsImpl.h" +#include "nsThreadUtils.h" + +#include <type_traits> +#include <utility> + +// 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(std::index_sequence<Indices...>, ObjT* obj, Method method, + std::tuple<Args...>& arg) { + (obj->*method)(std::move(std::get<Indices>(arg))...); +} + +// Same as above, but call a function. +template <size_t... Indices, typename Function, typename... Args> +void CallFunction(std::index_sequence<Indices...>, Function function, + std::tuple<Args...>& arg) { + (*function)(std::move(std::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, std::tuple<Args...>& arg) { + details::CallMethod(std::index_sequence_for<Args...>{}, obj, method, arg); +} + +// Same as above, but call a function. +template <typename Function, typename... Args> +void DispatchTupleToFunction(Function function, std::tuple<Args...>& arg) { + details::CallFunction(std::index_sequence_for<Args...>{}, 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 std::tuple<std::decay_t<Elements>...> ArgsTuple; + typedef RunnableMethod<Method, ArgsTuple> Runnable; + typedef typename ScopedTaskFactory<Runnable>::TaskWrapper TaskWrapper; + + RefPtr<TaskWrapper> task = new TaskWrapper(this); + task->Init(object_, method, + std::make_tuple(std::forward<Elements>(elements)...)); + return task.forget(); + } + + protected: + template <class Method, class Params> + class RunnableMethod : public mozilla::Runnable { + public: + RunnableMethod() + : mozilla::Runnable("ScopedRunnableMethodFactory::RunnableMethod") {} + + void Init(T* obj, Method meth, Params&& params) { + obj_ = obj; + meth_ = meth; + params_ = std::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) + : mozilla::CancelableRunnable("DeleteTask"), 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) + : mozilla::CancelableRunnable("RunnableMethod"), + obj_(obj), + meth_(meth), + params_(std::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 std::tuple<std::decay_t<Args>...> ArgsTuple; + RefPtr<mozilla::Runnable> t = new RunnableMethod<T, Method, ArgsTuple>( + object, method, std::make_tuple(std::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(const char* name, Function function, Params&& params) + : mozilla::CancelableRunnable(name), + function_(function), + params_(std::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(const char* name, Function function, + Args&&... args) { + typedef std::tuple<std::decay_t<Args>...> ArgsTuple; + RefPtr<mozilla::CancelableRunnable> t = + new RunnableFunction<Function, ArgsTuple>( + name, function, std::make_tuple(std::forward<Args>(args)...)); + return t.forget(); +} + +template <class Function, typename... Args> +inline already_AddRefed<mozilla::Runnable> NewRunnableFunction( + const char* name, Function function, Args&&... args) { + typedef std::tuple<std::decay_t<Args>...> ArgsTuple; + RefPtr<mozilla::Runnable> t = new RunnableFunction<Function, ArgsTuple>( + name, function, std::make_tuple(std::forward<Args>(args)...)); + return t.forget(); +} + +#endif // BASE_TASK_H_ |