From 226fe9d8f7538e4f5b505732d9ee1a1a89dbb576 Mon Sep 17 00:00:00 2001 From: Danilo Piparo <danilo.piparo@cern.ch> Date: Fri, 25 Aug 2017 14:43:07 +0200 Subject: [PATCH] Adopt naming conventions of std::future --- core/imt/CMakeLists.txt | 2 +- core/imt/inc/ROOT/Future.hxx | 161 +++++++++++++++++++++++++++++++++++ core/imt/src/Future.cxx | 28 ++++++ 3 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 core/imt/inc/ROOT/Future.hxx create mode 100644 core/imt/src/Future.cxx diff --git a/core/imt/CMakeLists.txt b/core/imt/CMakeLists.txt index 6c1b12ff6e7..9c735982b5e 100644 --- a/core/imt/CMakeLists.txt +++ b/core/imt/CMakeLists.txt @@ -5,7 +5,7 @@ set(sources base.cxx) if (imt) - set(headers ROOT/TPoolManager.hxx ROOT/TThreadExecutor.hxx ROOT/TTaskGroup.hxx) + set(headers ROOT/TPoolManager.hxx ROOT/TThreadExecutor.hxx ROOT/TTaskGroup.hxx ROOT/Future.hxx) ROOT_GENERATE_DICTIONARY(G__Imt ${headers} STAGE1 MODULE Imt LINKDEF LinkDef.h DEPENDENCIES Core Thread BUILTINS TBB) # For auto{loading,parsing} set(sources ${sources} TImplicitMT.cxx TThreadExecutor.cxx TPoolManager.cxx TTaskGroup.cxx G__Imt.cxx) endif() diff --git a/core/imt/inc/ROOT/Future.hxx b/core/imt/inc/ROOT/Future.hxx new file mode 100644 index 00000000000..80d5826e1c4 --- /dev/null +++ b/core/imt/inc/ROOT/Future.hxx @@ -0,0 +1,161 @@ +// @(#)root/thread:$Id$ +// Author: Danilo Piparo August 2017 + +/************************************************************************* + * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifndef ROOT_Async +#define ROOT_Async + +#include "RConfigure.h" + +#include "ROOT/TTaskGroup.hxx" + +#include <type_traits> +#include <future> + +// exclude in case ROOT does not have IMT support +#ifndef R__USE_IMT +// No need to error out for dictionaries. +#if !defined(__ROOTCLING__) && !defined(G__DICTIONARY) +#error "Cannot use ROOT::Experimental::Async without defining R__USE_IMT." +#endif +#else + +namespace ROOT { + +// fwd declaration +namespace Experimental { +template <typename T> +class Future; +} + +namespace Detail { +template <typename T> +class FutureImpl { + template<typename V> friend class Future; +protected: + using TTaskGroup = Experimental::TTaskGroup; + std::future<T> fStdFut; + std::unique_ptr<TTaskGroup> fTg {nullptr}; + + FutureImpl(std::future<T> &&fut, std::unique_ptr<TTaskGroup> &&tg) : fStdFut(std::move(fut)) { + fTg = std::move(tg); + }; + FutureImpl(){}; + + FutureImpl(std::future<T> &&fut) : fStdFut(std::move(fut)) {} + + FutureImpl(FutureImpl<T> &&other) { *this = std::move(other); } + + FutureImpl(const FutureImpl<T> &other) = delete; + +public: + + FutureImpl<T> &operator=(FutureImpl<T> &&other) + { + fStdFut = std::move(other.fStdFut); + fTg = std::move(other.fTg); + return *this; + } + + FutureImpl<T> &operator=(FutureImpl<T> &other) = delete; + + void wait() { fTg->Wait(); } + + bool valid() const { return fStdFut.valid(); }; +}; +} + +namespace Experimental { + +//////////////////////////////////////////////////////////////////////////////// +/// A future class. It can wrap an std::future. +template <typename T> +class Future final : public Detail::FutureImpl<T> { +template <class Function, class... Args> + friend Future<typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type> Async(Function &&f, Args &&... args); +private: + Future(std::future<T> &&fut, std::unique_ptr<TTaskGroup> &&tg) + : Detail::FutureImpl<T>(std::forward<std::future<T>>(fut), std::move(tg)){}; +public: + Future(std::future<T> &&fut) : Detail::FutureImpl<T>(std::forward<std::future<T>>(fut)) {}; + + Future(Future<T>&& other): Detail::FutureImpl<T>(std::forward<Future<T>>(other)) {} + + T get() + { + this->wait(); + return this->fStdFut.get(); + } +}; +/// \cond +// Two specialisations, for void and T& as for std::future +template <> +class Future<void> final : public Detail::FutureImpl<void> { +template <class Function, class... Args> + friend Future<typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type> Async(Function &&f, Args &&... args); +private: + Future(std::future<void> &&fut, std::unique_ptr<TTaskGroup> &&tg) + : Detail::FutureImpl<void>(std::forward<std::future<void>>(fut), std::move(tg)){}; +public: + Future(std::future<void> &&fut) : Detail::FutureImpl<void>(std::forward<std::future<void>>(fut)) {}; + + Future(Future<void> &&other) : Detail::FutureImpl<void>(std::forward<Future<void>>(other)) {} + + void get() + { + this->wait(); + fStdFut.get(); + } +}; + +template <typename T> +class Future<T &> final : public Detail::FutureImpl<T &> { +template <class Function, class... Args> + friend Future<typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type> Async(Function &&f, Args &&... args); +private: + Future(std::future<T &> &&fut, std::unique_ptr<TTaskGroup> &&tg) + : Detail::FutureImpl<T &>(std::forward<std::future<T &>>(fut), std::move(tg)){}; +public: + Future(std::future<T&> &&fut) : Detail::FutureImpl<T>(std::forward<std::future<T&>>(fut)) {}; + + Future(Future<T&> &&other) : Detail::FutureImpl<T&>(std::forward<Future<T&>>(other)) {} + + T &get() + { + this->wait(); + return this->fStdFut.get(); + } +}; +/// \endcond + +//////////////////////////////////////////////////////////////////////////////// +/// Runs a function asynchronously potentially in a new thread and returns a +/// ROOT Future that will hold the result. +template <class Function, class... Args> +Future<typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type> +Async(Function &&f, Args &&... args) +{ + // The return type according to the standard implementation of std::future + // the long type is due to the fact that we want to be c++11 compatible. + // A more elegant version would be: + // std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> + using Ret_t = typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type; + + auto thisPt = std::make_shared<std::packaged_task<Ret_t()>>(std::bind(f, args...)); + std::unique_ptr<ROOT::Experimental::TTaskGroup> tg(new ROOT::Experimental::TTaskGroup()); + tg->Run([thisPt]() { (*thisPt)(); }); + + return ROOT::Experimental::Future<Ret_t>(thisPt->get_future(), std::move(tg)); +} +} +} + +#endif +#endif diff --git a/core/imt/src/Future.cxx b/core/imt/src/Future.cxx new file mode 100644 index 00000000000..bd21115ec71 --- /dev/null +++ b/core/imt/src/Future.cxx @@ -0,0 +1,28 @@ +// @(#)root/thread:$Id$ +// Author: Danilo Piparo August 2017 + +/************************************************************************* + * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +// #include "ROOT/TTaskGroup.hxx" +// +// #include <memory> +// +// namespace ROOT { +// +// namespace Internal { +// namespace AsyncUtils{ +// +// std::shared_ptr<Experimental::TTaskGroup> GetAsyncTaskGroup() { +// static auto gAsyncTaskGroup = std::make_shared<Experimental::TTaskGroup>(); +// return gAsyncTaskGroup; +// } +// +// } +// } +// } -- GitLab