]> git.sesse.net Git - casparcg/blob - common/future.h
- Removed need of non-deterministic sleeps during server shutdown.
[casparcg] / common / future.h
1 #pragma once
2
3 #include <boost/thread/mutex.hpp>
4 #include <boost/function.hpp>
5 #include <boost/optional.hpp>
6
7 #include <functional>
8 #include <future>
9
10 namespace caspar {
11
12 template<typename T>
13 auto flatten(std::future<T>&& f) -> std::future<typename std::decay<decltype(f.get().get())>::type>
14 {
15         auto shared_f = f.share();
16         return std::async(std::launch::deferred, [=]() mutable -> typename std::decay<decltype(f.get().get())>::type
17         {
18                 return shared_f.get().get();
19         });
20 }
21
22 template<typename F>
23 bool is_ready(const F& future)
24 {
25         return future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
26 }
27
28 /**
29  * A utility that helps the producer side of a future when the task is not
30  * able to complete immediately but there are known retry points in the code.
31  */
32 template<class R>
33 class retry_task
34 {
35 public:
36         typedef boost::function<boost::optional<R> ()> func_type;
37         
38         retry_task() : done_(false) {}
39
40         /**
41          * Reset the state with a new task. If the previous task has not completed
42          * the old one will be discarded.
43          *
44          * @param func The function that tries to calculate future result. If the
45          *             optional return value is set the future is marked as ready.
46          */
47         void set_task(const func_type& func)
48         {
49                 boost::unique_lock<boost::mutex> lock(mutex_);
50
51                 func_ = func;
52                 done_ = false;
53                 promise_ = std::promise<R>();
54         }
55
56         /**
57          * Take ownership of the future for the current task. Cannot only be called
58          * once for each task.
59          *
60          * @return the future.
61          */
62         std::future<R> get_future()
63         {
64                 boost::unique_lock<boost::mutex> lock(mutex_);
65
66                 return promise_.get_future();
67         }
68
69         /**
70          * Call this when it is guaranteed or probable that the task will be able
71          * to complete.
72          *
73          * @return true if the task completed (the future will have a result).
74          */
75         bool try_completion()
76         {
77                 boost::unique_lock<boost::mutex> lock(mutex_);
78
79                 return try_completion_internal();
80         }
81
82         /**
83          * Call this when it is certain that the result should be ready, and if not
84          * it should be regarded as an unrecoverable error (retrying again would
85          * be useless), so the future will be marked as failed.
86          *
87          * @param exception The exception to mark the future with *if* the task
88          *                  completion fails.
89          */
90         template <class E>
91         void try_or_fail(const E& exception)
92         {
93                 boost::unique_lock<boost::mutex> lock(mutex_);
94
95                 if (!try_completion_internal())
96                 {
97                         try
98                         {
99                                 throw exception;
100                         }
101                         catch (...)
102                         {
103                                 CASPAR_LOG_CURRENT_EXCEPTION();
104                                 promise_.set_exception(std::current_exception());
105                                 done_ = true;
106                         }
107                 }
108         }
109 private:
110         bool try_completion_internal()
111         {
112                 if (!func_)
113                         return false;
114
115                 if (done_)
116                         return true;
117
118                 boost::optional<R> result;
119
120                 try
121                 {
122                         result = func_();
123                 }
124                 catch (...)
125                 {
126                         CASPAR_LOG_CURRENT_EXCEPTION();
127                         promise_.set_exception(std::current_exception());
128                         done_ = true;
129
130                         return true;
131                 }
132
133                 if (result)
134                 {
135                         promise_.set_value(*result);
136                         done_ = true;
137                 }
138
139                 return done_;
140         }
141 private:
142         boost::mutex mutex_;
143         func_type func_;
144         std::promise<R> promise_;
145         bool done_;
146 };
147
148 /**
149  * Wrap a value in a future with an already known result.
150  * <p>
151  * Useful when the result of an operation is already known at the time of
152  * calling.
153  *
154  * @param value The r-value to wrap.
155  *
156  * @return The future with the result set.
157  */
158 template<class R>
159 std::future<R> make_ready_future(R&& value)
160 {
161         std::promise<R> p;
162
163         p.set_value(value);
164
165         return p.get_future();
166 }
167
168 static std::future<void> make_ready_future()
169 {
170         std::promise<void> p;
171
172         p.set_value();
173
174         return p.get_future();
175 }
176
177 }