diff options
Diffstat (limited to 'media/mtransport/runnable_utils.h')
-rw-r--r-- | media/mtransport/runnable_utils.h | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/media/mtransport/runnable_utils.h b/media/mtransport/runnable_utils.h new file mode 100644 index 000000000..0d7b1ce6f --- /dev/null +++ b/media/mtransport/runnable_utils.h @@ -0,0 +1,258 @@ +/* -*- 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<nsIRunnable> 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<RunnableResult result> +class runnable_args_base : public Runnable { + public: + NS_IMETHOD Run() = 0; +}; + + +template<typename R> +struct RunnableFunctionCallHelper +{ + template<typename FunType, typename... Args, size_t... Indices> + static R apply(FunType func, Tuple<Args...>& args, IndexSequence<Indices...>) + { + return func(Get<Indices>(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<void> +{ + template<typename FunType, typename... Args, size_t... Indices> + static void apply(FunType func, Tuple<Args...>& args, IndexSequence<Indices...>) + { + func(Get<Indices>(args)...); + } +}; + +template<typename R> +struct RunnableMethodCallHelper +{ + template<typename Class, typename M, typename... Args, size_t... Indices> + static R apply(Class obj, M method, Tuple<Args...>& args, IndexSequence<Indices...>) + { + return ((*obj).*method)(Get<Indices>(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<void> +{ + template<typename Class, typename M, typename... Args, size_t... Indices> + static void apply(Class obj, M method, Tuple<Args...>& args, IndexSequence<Indices...>) + { + ((*obj).*method)(Get<Indices>(args)...); + } +}; + +} + +template<typename FunType, typename... Args> +class runnable_args_func : public detail::runnable_args_base<detail::NoResult> +{ +public: + // |explicit| to pacify static analysis when there are no |args|. + explicit runnable_args_func(FunType f, Args&&... args) + : mFunc(f), mArgs(Forward<Args>(args)...) + {} + + NS_IMETHOD Run() { + detail::RunnableFunctionCallHelper<void>::apply(mFunc, mArgs, typename IndexSequenceFor<Args...>::Type()); + return NS_OK; + } + +private: + FunType mFunc; + Tuple<Args...> mArgs; +}; + +template<typename FunType, typename... Args> +runnable_args_func<FunType, Args...>* +WrapRunnableNM(FunType f, Args... args) +{ + return new runnable_args_func<FunType, Args...>(f, Move(args)...); +} + +template<typename Ret, typename FunType, typename... Args> +class runnable_args_func_ret : public detail::runnable_args_base<detail::ReturnsResult> +{ +public: + runnable_args_func_ret(Ret* ret, FunType f, Args&&... args) + : mReturn(ret), mFunc(f), mArgs(Forward<Args>(args)...) + {} + + NS_IMETHOD Run() { + *mReturn = detail::RunnableFunctionCallHelper<Ret>::apply(mFunc, mArgs, typename IndexSequenceFor<Args...>::Type()); + return NS_OK; + } + +private: + Ret* mReturn; + FunType mFunc; + Tuple<Args...> mArgs; +}; + +template<typename R, typename FunType, typename... Args> +runnable_args_func_ret<R, FunType, Args...>* +WrapRunnableNMRet(R* ret, FunType f, Args... args) +{ + return new runnable_args_func_ret<R, FunType, Args...>(ret, f, Move(args)...); +} + +template<typename Class, typename M, typename... Args> +class runnable_args_memfn : public detail::runnable_args_base<detail::NoResult> +{ +public: + runnable_args_memfn(Class obj, M method, Args&&... args) + : mObj(obj), mMethod(method), mArgs(Forward<Args>(args)...) + {} + + NS_IMETHOD Run() { + detail::RunnableMethodCallHelper<void>::apply(mObj, mMethod, mArgs, typename IndexSequenceFor<Args...>::Type()); + return NS_OK; + } + +private: + Class mObj; + M mMethod; + Tuple<Args...> mArgs; +}; + +template<typename Class, typename M, typename... Args> +runnable_args_memfn<Class, M, Args...>* +WrapRunnable(Class obj, M method, Args... args) +{ + return new runnable_args_memfn<Class, M, Args...>(obj, method, Move(args)...); +} + +template<typename Ret, typename Class, typename M, typename... Args> +class runnable_args_memfn_ret : public detail::runnable_args_base<detail::ReturnsResult> +{ +public: + runnable_args_memfn_ret(Ret* ret, Class obj, M method, Args... args) + : mReturn(ret), mObj(obj), mMethod(method), mArgs(Forward<Args>(args)...) + {} + + NS_IMETHOD Run() { + *mReturn = detail::RunnableMethodCallHelper<Ret>::apply(mObj, mMethod, mArgs, typename IndexSequenceFor<Args...>::Type()); + return NS_OK; + } + +private: + Ret* mReturn; + Class mObj; + M mMethod; + Tuple<Args...> mArgs; +}; + +template<typename R, typename Class, typename M, typename... Args> +runnable_args_memfn_ret<R, Class, M, Args...>* +WrapRunnableRet(R* ret, Class obj, M method, Args... args) +{ + return new runnable_args_memfn_ret<R, Class, M, Args...>(ret, obj, method, Move(args)...); +} + +static inline nsresult RUN_ON_THREAD(nsIEventTarget *thread, detail::runnable_args_base<detail::NoResult> *runnable, uint32_t flags) { + return detail::RunOnThreadInternal(thread, static_cast<nsIRunnable *>(runnable), flags); +} + +static inline nsresult +RUN_ON_THREAD(nsIEventTarget *thread, detail::runnable_args_base<detail::ReturnsResult> *runnable) +{ + return detail::RunOnThreadInternal(thread, static_cast<nsIRunnable *>(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 T> +class DispatchedRelease : public detail::runnable_args_base<detail::NoResult> { +public: + explicit DispatchedRelease(already_AddRefed<T>& ref) : ref_(ref) {} + + NS_IMETHOD Run() { + ref_ = nullptr; + return NS_OK; + } +private: + RefPtr<T> ref_; +}; + +template <typename T> +DispatchedRelease<T>* WrapRelease(already_AddRefed<T>&& ref) +{ + return new DispatchedRelease<T>(ref); +} + +} /* namespace mozilla */ + +#endif |