]> git.sesse.net Git - casparcg/blob - common/future.h
2.1.0: -async: Refactored. Call thread.join() when futures created with launch::async...
[casparcg] / common / future.h
1 #pragma once\r
2 \r
3 #include "enum_class.h"\r
4 \r
5 #include <boost/thread/future.hpp>\r
6 #include <boost/thread/thread.hpp>\r
7 #include <boost/shared_ptr.hpp>\r
8 \r
9 #include <functional>\r
10 \r
11 namespace caspar {\r
12         \r
13 struct launch_policy_def\r
14 {\r
15         enum type\r
16         {\r
17                 async = 1,\r
18                 deferred = 2\r
19         };\r
20 };\r
21 typedef caspar::enum_class<launch_policy_def> launch;\r
22 \r
23 namespace detail {\r
24         \r
25 template<typename R>\r
26 struct future_object_helper\r
27 {       \r
28         template<typename T, typename F>\r
29         static void invoke(T& future_object, F& f)\r
30         {                               \r
31         try\r
32         {\r
33                         future_object.mark_finished_with_result_internal(f());\r
34         }\r
35         catch(...)\r
36         {\r
37                         future_object.mark_exceptional_finish_internal(boost::current_exception());\r
38         }\r
39         }\r
40 };\r
41 \r
42 template<>\r
43 struct future_object_helper<void>\r
44 {       \r
45         template<typename T, typename F>\r
46         static void invoke(T& future_object, F& f)\r
47         {                               \r
48         try\r
49         {\r
50                         f();\r
51                         future_object.mark_finished_with_result_internal();\r
52         }\r
53         catch(...)\r
54         {\r
55                         future_object.mark_exceptional_finish_internal(boost::current_exception());\r
56         }\r
57         }\r
58 };\r
59 \r
60 template<typename R, typename F>\r
61 struct deferred_future_object : public boost::detail::future_object<R>\r
62 {       \r
63         F f;\r
64         bool done;\r
65 \r
66         template<typename F2>\r
67         deferred_future_object(F2&& f)\r
68                 : f(std::forward<F2>(f))\r
69                 , done(false)\r
70         {\r
71                 set_wait_callback(std::mem_fn(&detail::deferred_future_object<R, F>::operator()), this);\r
72         }\r
73                 \r
74         void operator()()\r
75         {               \r
76                 boost::lock_guard<boost::mutex> lock2(mutex);\r
77 \r
78                 if(done)\r
79                         return;\r
80 \r
81                 future_object_helper<R>::invoke(*this, f);\r
82 \r
83                 done = true;\r
84         }\r
85 };\r
86 \r
87 template<typename R, typename F>\r
88 struct async_future_object : public boost::detail::future_object<R>\r
89 {       \r
90         F f;\r
91         boost::thread thread;\r
92 \r
93         template<typename F2>\r
94         async_future_object(F2&& f)\r
95                 : f(std::forward<F2>(f))\r
96                 , thread([this]{run();})\r
97         {\r
98         }\r
99 \r
100         ~async_future_object()\r
101         {\r
102                 thread.join();\r
103         }\r
104 \r
105         void run()\r
106         {\r
107                 boost::lock_guard<boost::mutex> lock2(mutex);           \r
108 \r
109                 future_object_helper<R>::invoke(*this, f);\r
110         }\r
111 };\r
112 \r
113 }\r
114         \r
115 template<typename F>\r
116 auto async(launch policy, F&& f) -> boost::unique_future<decltype(f())>\r
117 {               \r
118         typedef decltype(f())                                                           result_type;    \r
119         typedef boost::detail::future_object<result_type>       future_object_type;\r
120 \r
121         boost::shared_ptr<future_object_type> future_object;\r
122 \r
123         if((policy & launch::async) != 0)\r
124                 future_object = boost::static_pointer_cast<future_object_type>(boost::make_shared<detail::async_future_object<result_type, F>>(std::forward<F>(f)));\r
125         else if((policy & launch::deferred) != 0)\r
126                 future_object = boost::static_pointer_cast<future_object_type>(boost::make_shared<detail::deferred_future_object<result_type, F>>(std::forward<F>(f))); \r
127         else\r
128                 throw std::invalid_argument("policy");\r
129         \r
130         boost::unique_future<result_type> future;\r
131 \r
132         static_assert(sizeof(future) == sizeof(future_object), "");\r
133 \r
134         reinterpret_cast<boost::shared_ptr<future_object_type>&>(future) = std::move(future_object); // Get around the "private" encapsulation.\r
135         return std::move(future);\r
136 }\r
137         \r
138 template<typename F>\r
139 auto async(F&& f) -> boost::unique_future<decltype(f())>\r
140 {       \r
141         return async(launch::async | launch::deferred, std::forward<F>(f));\r
142 }\r
143 \r
144 template<typename T>\r
145 auto make_shared(boost::unique_future<T>&& f) -> boost::shared_future<T>\r
146 {       \r
147         return boost::shared_future<T>(std::move(f));\r
148 }\r
149 \r
150 template<typename T>\r
151 auto flatten(boost::unique_future<T>&& f) -> boost::unique_future<decltype(f.get().get())>\r
152 {\r
153         auto shared_f = make_shared(std::move(f));\r
154         return async(launch::deferred, [=]() mutable\r
155         {\r
156                 return shared_f.get().get();\r
157         });\r
158 }\r
159 \r
160 }