/* -*- 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/Move.h" #include "mozilla/RefPtr.h" #include "mozilla/Tuple.h" #include // 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) { return thread->Dispatch(runnable, flags); } template class runnable_args_base : public Runnable { public: runnable_args_base() : Runnable("media-runnable_args_base") {} NS_IMETHOD Run() override = 0; }; template struct RunnableFunctionCallHelper { template static R apply(FunType func, Tuple& args, std::index_sequence) { 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, std::index_sequence) { func(Get(args)...); } }; template struct RunnableMethodCallHelper { template static R apply(Class obj, M method, Tuple& args, std::index_sequence) { 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, std::index_sequence) { ((*obj).*method)(Get(args)...); } }; } // namespace detail template class runnable_args_func : public detail::runnable_args_base { public: // |explicit| to pacify static analysis when there are no |args|. template explicit runnable_args_func(FunType f, Arguments&&... args) : mFunc(f), mArgs(std::forward(args)...) {} NS_IMETHOD Run() override { detail::RunnableFunctionCallHelper::apply( mFunc, mArgs, std::index_sequence_for{}); return NS_OK; } private: FunType mFunc; Tuple mArgs; }; template runnable_args_func::Type...>* WrapRunnableNM(FunType f, Args&&... args) { return new runnable_args_func::Type...>( f, std::forward(args)...); } template class runnable_args_func_ret : public detail::runnable_args_base { public: template runnable_args_func_ret(Ret* ret, FunType f, Arguments&&... args) : mReturn(ret), mFunc(f), mArgs(std::forward(args)...) {} NS_IMETHOD Run() override { *mReturn = detail::RunnableFunctionCallHelper::apply( mFunc, mArgs, std::index_sequence_for{}); return NS_OK; } private: Ret* mReturn; FunType mFunc; Tuple mArgs; }; template runnable_args_func_ret::Type...>* WrapRunnableNMRet(R* ret, FunType f, Args&&... args) { return new runnable_args_func_ret::Type...>( ret, f, std::forward(args)...); } template class runnable_args_memfn : public detail::runnable_args_base { public: template runnable_args_memfn(Class obj, M method, Arguments&&... args) : mObj(obj), mMethod(method), mArgs(std::forward(args)...) {} NS_IMETHOD Run() override { detail::RunnableMethodCallHelper::apply( mObj, mMethod, mArgs, std::index_sequence_for{}); return NS_OK; } private: Class mObj; M mMethod; Tuple mArgs; }; template runnable_args_memfn::Type...>* WrapRunnable(Class obj, M method, Args&&... args) { return new runnable_args_memfn::Type...>( obj, method, std::forward(args)...); } template class runnable_args_memfn_ret : public detail::runnable_args_base { public: template runnable_args_memfn_ret(Ret* ret, Class obj, M method, Arguments... args) : mReturn(ret), mObj(obj), mMethod(method), mArgs(std::forward(args)...) {} NS_IMETHOD Run() override { *mReturn = detail::RunnableMethodCallHelper::apply( mObj, mMethod, mArgs, std::index_sequence_for{}); return NS_OK; } private: Ret* mReturn; Class mObj; M mMethod; Tuple mArgs; }; template runnable_args_memfn_ret::Type...>* WrapRunnableRet(R* ret, Class obj, M method, Args&&... args) { return new runnable_args_memfn_ret::Type...>( ret, obj, method, std::forward(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() override { ref_ = nullptr; return NS_OK; } private: RefPtr ref_; }; template DispatchedRelease* WrapRelease(already_AddRefed&& ref) { return new DispatchedRelease(ref); } } /* namespace mozilla */ #endif