/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ // Original author: ekr@rtfm.com #ifndef runnable_utils_h__ #define runnable_utils_h__ #include "nsThreadUtils.h" #include "mozilla/IndexSequence.h" #include "mozilla/Move.h" #include "mozilla/RefPtr.h" #include "mozilla/Tuple.h" // Abstract base class for all of our templates namespace mozilla { namespace detail { enum RunnableResult { NoResult, ReturnsResult }; static inline nsresult RunOnThreadInternal(nsIEventTarget *thread, nsIRunnable *runnable, uint32_t flags) { nsCOMPtr runnable_ref(runnable); if (thread) { bool on; nsresult rv; rv = thread->IsOnCurrentThread(&on); // If the target thread has already shut down, we don't want to assert. if (rv != NS_ERROR_NOT_INITIALIZED) { MOZ_ASSERT(NS_SUCCEEDED(rv)); } if (NS_WARN_IF(NS_FAILED(rv))) { // we're going to destroy the runnable on this thread! return rv; } if (!on) { return thread->Dispatch(runnable_ref.forget(), flags); } } return runnable_ref->Run(); } template class runnable_args_base : public nsRunnable { public: NS_IMETHOD Run() = 0; }; template struct RunnableFunctionCallHelper { template static R apply(FunType func, Tuple& args, IndexSequence) { return func(Get(args)...); } }; // A void specialization is needed in the case where the template instantiator // knows we don't want to return a value, but we don't know whether the called // function returns void or something else. template<> struct RunnableFunctionCallHelper { template static void apply(FunType func, Tuple& args, IndexSequence) { func(Get(args)...); } }; template struct RunnableMethodCallHelper { template static R apply(Class obj, M method, Tuple& args, IndexSequence) { return ((*obj).*method)(Get(args)...); } }; // A void specialization is needed in the case where the template instantiator // knows we don't want to return a value, but we don't know whether the called // method returns void or something else. template<> struct RunnableMethodCallHelper { template static void apply(Class obj, M method, Tuple& args, IndexSequence) { ((*obj).*method)(Get(args)...); } }; } template class runnable_args_func : public detail::runnable_args_base { public: // |explicit| to pacify static analysis when there are no |args|. explicit runnable_args_func(FunType f, Args&&... args) : mFunc(f), mArgs(Forward(args)...) {} NS_IMETHOD Run() { detail::RunnableFunctionCallHelper::apply(mFunc, mArgs, typename IndexSequenceFor::Type()); return NS_OK; } private: FunType mFunc; Tuple mArgs; }; template runnable_args_func* WrapRunnableNM(FunType f, Args... args) { return new runnable_args_func(f, Move(args)...); } template class runnable_args_func_ret : public detail::runnable_args_base { public: runnable_args_func_ret(Ret* ret, FunType f, Args&&... args) : mReturn(ret), mFunc(f), mArgs(Forward(args)...) {} NS_IMETHOD Run() { *mReturn = detail::RunnableFunctionCallHelper::apply(mFunc, mArgs, typename IndexSequenceFor::Type()); return NS_OK; } private: Ret* mReturn; FunType mFunc; Tuple mArgs; }; template runnable_args_func_ret* WrapRunnableNMRet(R* ret, FunType f, Args... args) { return new runnable_args_func_ret(ret, f, Move(args)...); } template class runnable_args_memfn : public detail::runnable_args_base { public: runnable_args_memfn(Class obj, M method, Args&&... args) : mObj(obj), mMethod(method), mArgs(Forward(args)...) {} NS_IMETHOD Run() { detail::RunnableMethodCallHelper::apply(mObj, mMethod, mArgs, typename IndexSequenceFor::Type()); return NS_OK; } private: Class mObj; M mMethod; Tuple mArgs; }; template runnable_args_memfn* WrapRunnable(Class obj, M method, Args... args) { return new runnable_args_memfn(obj, method, Move(args)...); } template class runnable_args_memfn_ret : public detail::runnable_args_base { public: runnable_args_memfn_ret(Ret* ret, Class obj, M method, Args... args) : mReturn(ret), mObj(obj), mMethod(method), mArgs(Forward(args)...) {} NS_IMETHOD Run() { *mReturn = detail::RunnableMethodCallHelper::apply(mObj, mMethod, mArgs, typename IndexSequenceFor::Type()); return NS_OK; } private: Ret* mReturn; Class mObj; M mMethod; Tuple mArgs; }; template runnable_args_memfn_ret* WrapRunnableRet(R* ret, Class obj, M method, Args... args) { return new runnable_args_memfn_ret(ret, obj, method, Move(args)...); } static inline nsresult RUN_ON_THREAD(nsIEventTarget *thread, detail::runnable_args_base *runnable, uint32_t flags) { return detail::RunOnThreadInternal(thread, static_cast(runnable), flags); } static inline nsresult RUN_ON_THREAD(nsIEventTarget *thread, detail::runnable_args_base *runnable) { return detail::RunOnThreadInternal(thread, static_cast(runnable), NS_DISPATCH_SYNC); } #ifdef DEBUG #define ASSERT_ON_THREAD(t) do { \ if (t) { \ bool on; \ nsresult rv; \ rv = t->IsOnCurrentThread(&on); \ MOZ_ASSERT(NS_SUCCEEDED(rv)); \ MOZ_ASSERT(on); \ } \ } while(0) #else #define ASSERT_ON_THREAD(t) #endif template class DispatchedRelease : public detail::runnable_args_base { public: explicit DispatchedRelease(already_AddRefed& ref) : ref_(ref) {} NS_IMETHOD Run() { ref_ = nullptr; return NS_OK; } private: RefPtr ref_; }; template DispatchedRelease* WrapRelease(already_AddRefed&& ref) { return new DispatchedRelease(ref); } } /* namespace mozilla */ #endif