Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TFuture.hxx
Go to the documentation of this file.
1 // @(#)root/thread:$Id$
2 // Author: Danilo Piparo August 2017
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #ifndef ROOT_TFuture
13 #define ROOT_TFuture
14 
15 #include "RConfigure.h"
16 
17 #include "ROOT/TTaskGroup.hxx"
18 
19 #include <type_traits>
20 #include <future>
21 
22 // exclude in case ROOT does not have IMT support
23 #ifndef R__USE_IMT
24 // No need to error out for dictionaries.
25 #if !defined(__ROOTCLING__) && !defined(G__DICTIONARY)
26 #error "Cannot use ROOT::Experimental::Async without defining R__USE_IMT."
27 #endif
28 #else
29 
30 namespace ROOT {
31 
32 // fwd declaration
33 namespace Experimental {
34 template <typename T>
35 class TFuture;
36 }
37 
38 namespace Detail {
39 template <typename T>
40 class TFutureImpl {
41  template <typename V>
42  friend class Experimental::TFuture;
43 
44 protected:
45  using TTaskGroup = Experimental::TTaskGroup;
46  std::future<T> fStdFut;
47  std::unique_ptr<TTaskGroup> fTg{nullptr};
48 
49  TFutureImpl(std::future<T> &&fut, std::unique_ptr<TTaskGroup> &&tg) : fStdFut(std::move(fut))
50  {
51  fTg = std::move(tg);
52  };
53  TFutureImpl(){};
54 
55  TFutureImpl(std::future<T> &&fut) : fStdFut(std::move(fut)) {}
56 
57  TFutureImpl(TFutureImpl<T> &&other) : fStdFut(std::move(other.fStdFut)), fTg(std::move(other.fTg)) {}
58 
59  TFutureImpl &operator=(std::future<T> &&other) { fStdFut = std::move(other); }
60 
61  TFutureImpl<T> &operator=(TFutureImpl<T> &&other) = default;
62 
63 public:
64  TFutureImpl<T> &operator=(TFutureImpl<T> &other) = delete;
65 
66  TFutureImpl(const TFutureImpl<T> &other) = delete;
67 
68  void wait()
69  {
70  if (fTg)
71  fTg->Wait();
72  }
73 
74  bool valid() const { return fStdFut.valid(); };
75 };
76 }
77 
78 namespace Experimental {
79 
80 ////////////////////////////////////////////////////////////////////////////////
81 /// A TFuture class. It can wrap an std::future.
82 template <typename T>
83 class TFuture final : public ROOT::Detail::TFutureImpl<T> {
84  template <class Function, class... Args>
85  friend TFuture<
86  typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type>
87  Async(Function &&f, Args &&... args);
88 
89 private:
90  TFuture(std::future<T> &&fut, std::unique_ptr<TTaskGroup> &&tg)
91  : ROOT::Detail::TFutureImpl<T>(std::forward<std::future<T>>(fut), std::move(tg)){};
92 
93 public:
94  TFuture(std::future<T> &&fut) : ROOT::Detail::TFutureImpl<T>(std::forward<std::future<T>>(fut)){};
95 
96  T get()
97  {
98  this->wait();
99  return this->fStdFut.get();
100  }
101 };
102 /// \cond
103 // Two specialisations, for void and T& as for std::future
104 template <>
105 class TFuture<void> final : public ROOT::Detail::TFutureImpl<void> {
106  template <class Function, class... Args>
107  friend TFuture<
108  typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type>
109  Async(Function &&f, Args &&... args);
110 
111 private:
112  TFuture(std::future<void> &&fut, std::unique_ptr<TTaskGroup> &&tg)
113  : ROOT::Detail::TFutureImpl<void>(std::forward<std::future<void>>(fut), std::move(tg)){};
114 
115 public:
116  TFuture(std::future<void> &&fut) : ROOT::Detail::TFutureImpl<void>(std::forward<std::future<void>>(fut)){};
117 
118  void get()
119  {
120  this->wait();
121  fStdFut.get();
122  }
123 };
124 
125 template <typename T>
126 class TFuture<T &> final : public ROOT::Detail::TFutureImpl<T &> {
127  template <class Function, class... Args>
128  friend TFuture<
129  typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type>
130  Async(Function &&f, Args &&... args);
131 
132 private:
133  TFuture(std::future<T &> &&fut, std::unique_ptr<TTaskGroup> &&tg)
134  : ROOT::Detail::TFutureImpl<T &>(std::forward<std::future<T &>>(fut), std::move(tg)){};
135 
136 public:
137  TFuture(std::future<T &> &&fut) : ROOT::Detail::TFutureImpl<T &>(std::forward<std::future<T &>>(fut)){};
138 
139  T &get()
140  {
141  this->wait();
142  return this->fStdFut.get();
143  }
144 };
145 /// \endcond
146 
147 ////////////////////////////////////////////////////////////////////////////////
148 /// Runs a function asynchronously potentially in a new thread and returns a
149 /// ROOT TFuture that will hold the result.
150 template <class Function, class... Args>
151 TFuture<typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type>
152 Async(Function &&f, Args &&... args)
153 {
154  // The return type according to the standard implementation of std::future
155  // the long type is due to the fact that we want to be c++11 compatible.
156  // A more elegant version would be:
157  // std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
158  using Ret_t = typename std::result_of<typename std::decay<Function>::type(typename std::decay<Args>::type...)>::type;
159 
160  auto thisPt = std::make_shared<std::packaged_task<Ret_t()>>(std::bind(f, args...));
161  std::unique_ptr<ROOT::Experimental::TTaskGroup> tg(new ROOT::Experimental::TTaskGroup());
162  tg->Run([thisPt]() { (*thisPt)(); });
163 
164  return ROOT::Experimental::TFuture<Ret_t>(thisPt->get_future(), std::move(tg));
165 }
166 }
167 }
168 
169 #endif
170 #endif