]> git.sesse.net Git - casparcg/blob - common/future.h
2.1.0: -async: Don't lock unnecessarily while invoking "launch::async" functor.
[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 nonlocking_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         template<typename T, typename F>\r
42         static void locking_invoke(T& future_object, F& f)\r
43         {                               \r
44         try\r
45         {\r
46                         future_object.mark_finished_with_result(f());\r
47         }\r
48         catch(...)\r
49         {\r
50                         future_object.mark_exceptional_finish();\r
51         }\r
52         }\r
53 };\r
54 \r
55 template<>\r
56 struct future_object_helper<void>\r
57 {       \r
58         template<typename T, typename F>\r
59         static void nonlocking_invoke(T& future_object, F& f)\r
60         {                               \r
61         try\r
62         {\r
63                         f();\r
64                         future_object.mark_finished_with_result_internal();\r
65         }\r
66         catch(...)\r
67         {\r
68                         future_object.mark_exceptional_finish_internal(boost::current_exception());\r
69         }\r
70         }\r
71 \r
72         template<typename T, typename F>\r
73         static void locking_invoke(T& future_object, F& f)\r
74         {                               \r
75         try\r
76         {\r
77                         f();\r
78                         future_object.mark_finished_with_result();\r
79         }\r
80         catch(...)\r
81         {\r
82                         future_object.mark_exceptional_finish();\r
83         }\r
84         }\r
85 };\r
86 \r
87 template<typename R, typename F>\r
88 struct deferred_future_object : public boost::detail::future_object<R>\r
89 {       \r
90         F f;\r
91         bool done;\r
92 \r
93         template<typename F2>\r
94         deferred_future_object(F2&& f)\r
95                 : f(std::forward<F2>(f))\r
96                 , done(false)\r
97         {\r
98                 set_wait_callback(std::mem_fn(&detail::deferred_future_object<R, F>::operator()), this);\r
99         }\r
100                 \r
101         void operator()()\r
102         {               \r
103                 boost::lock_guard<boost::mutex> lock2(mutex);\r
104 \r
105                 if(done)\r
106                         return;\r
107 \r
108                 future_object_helper<R>::nonlocking_invoke(*this, f);\r
109 \r
110                 done = true;\r
111         }\r
112 };\r
113 \r
114 template<typename R, typename F>\r
115 struct async_future_object : public boost::detail::future_object<R>\r
116 {       \r
117         F f;\r
118         boost::thread thread;\r
119 \r
120         template<typename F2>\r
121         async_future_object(F2&& f)\r
122                 : f(std::forward<F2>(f))\r
123                 , thread([this]{run();})\r
124         {\r
125         }\r
126 \r
127         ~async_future_object()\r
128         {\r
129                 thread.join();\r
130         }\r
131 \r
132         void run()\r
133         {\r
134                 future_object_helper<R>::locking_invoke(*this, f);\r
135         }\r
136 };\r
137 \r
138 }\r
139         \r
140 template<typename F>\r
141 auto async(launch policy, F&& f) -> boost::unique_future<decltype(f())>\r
142 {               \r
143         typedef decltype(f())                                                           result_type;    \r
144         typedef boost::detail::future_object<result_type>       future_object_type;\r
145 \r
146         boost::shared_ptr<future_object_type> future_object;\r
147 \r
148         if((policy & launch::async) != 0)\r
149                 future_object = boost::static_pointer_cast<future_object_type>(boost::make_shared<detail::async_future_object<result_type, F>>(std::forward<F>(f)));\r
150         else if((policy & launch::deferred) != 0)\r
151                 future_object = boost::static_pointer_cast<future_object_type>(boost::make_shared<detail::deferred_future_object<result_type, F>>(std::forward<F>(f))); \r
152         else\r
153                 throw std::invalid_argument("policy");\r
154         \r
155         boost::unique_future<result_type> future;\r
156 \r
157         static_assert(sizeof(future) == sizeof(future_object), "");\r
158 \r
159         reinterpret_cast<boost::shared_ptr<future_object_type>&>(future) = std::move(future_object); // Get around the "private" encapsulation.\r
160         return std::move(future);\r
161 }\r
162         \r
163 template<typename F>\r
164 auto async(F&& f) -> boost::unique_future<decltype(f())>\r
165 {       \r
166         return async(launch::async | launch::deferred, std::forward<F>(f));\r
167 }\r
168 \r
169 template<typename T>\r
170 auto make_shared(boost::unique_future<T>&& f) -> boost::shared_future<T>\r
171 {       \r
172         return boost::shared_future<T>(std::move(f));\r
173 }\r
174 \r
175 template<typename T>\r
176 auto flatten(boost::unique_future<T>&& f) -> boost::unique_future<decltype(f.get().get())>\r
177 {\r
178         auto shared_f = make_shared(std::move(f));\r
179         return async(launch::deferred, [=]() mutable\r
180         {\r
181                 return shared_f.get().get();\r
182         });\r
183 }\r
184 \r
185 }