1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef dap_future_h
16#define dap_future_h
17
18#include <condition_variable>
19#include <memory>
20#include <mutex>
21
22namespace dap {
23
24// internal functionality
25namespace detail {
26template <typename T>
27struct promise_state {
28 T val;
29 std::mutex mutex;
30 std::condition_variable cv;
31 bool hasVal = false;
32};
33} // namespace detail
34
35// forward declaration
36template <typename T>
37class promise;
38
39// future_status is the enumeration returned by future::wait_for and
40// future::wait_until.
41enum class future_status {
42 ready,
43 timeout,
44};
45
46// future is a minimal reimplementation of std::future, that does not suffer
47// from TSAN false positives. See:
48// https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204
49template <typename T>
50class future {
51 public:
52 using State = detail::promise_state<T>;
53
54 // constructors
55 inline future() = default;
56 inline future(future&&) = default;
57
58 // valid() returns true if the future has an internal state.
59 bool valid() const;
60
61 // get() blocks until the future has a valid result, and returns it.
62 // The future must have a valid internal state to call this method.
63 inline T get();
64
65 // wait() blocks until the future has a valid result.
66 // The future must have a valid internal state to call this method.
67 void wait() const;
68
69 // wait_for() blocks until the future has a valid result, or the timeout is
70 // reached.
71 // The future must have a valid internal state to call this method.
72 template <class Rep, class Period>
73 future_status wait_for(
74 const std::chrono::duration<Rep, Period>& timeout) const;
75
76 // wait_until() blocks until the future has a valid result, or the timeout is
77 // reached.
78 // The future must have a valid internal state to call this method.
79 template <class Clock, class Duration>
80 future_status wait_until(
81 const std::chrono::time_point<Clock, Duration>& timeout) const;
82
83 private:
84 friend promise<T>;
85 future(const future&) = delete;
86 inline future(const std::shared_ptr<State>& state);
87
88 std::shared_ptr<State> state = std::make_shared<State>();
89};
90
91template <typename T>
92future<T>::future(const std::shared_ptr<State>& s) : state(s) {}
93
94template <typename T>
95bool future<T>::valid() const {
96 return static_cast<bool>(state);
97}
98
99template <typename T>
100T future<T>::get() {
101 std::unique_lock<std::mutex> lock(state->mutex);
102 state->cv.wait(lock, [&] { return state->hasVal; });
103 return state->val;
104}
105
106template <typename T>
107void future<T>::wait() const {
108 std::unique_lock<std::mutex> lock(state->mutex);
109 state->cv.wait(lock, [&] { return state->hasVal; });
110}
111
112template <typename T>
113template <class Rep, class Period>
114future_status future<T>::wait_for(
115 const std::chrono::duration<Rep, Period>& timeout) const {
116 std::unique_lock<std::mutex> lock(state->mutex);
117 return state->cv.wait_for(lock, timeout, [&] { return state->hasVal; })
118 ? future_status::ready
119 : future_status::timeout;
120}
121
122template <typename T>
123template <class Clock, class Duration>
124future_status future<T>::wait_until(
125 const std::chrono::time_point<Clock, Duration>& timeout) const {
126 std::unique_lock<std::mutex> lock(state->mutex);
127 return state->cv.wait_until(lock, timeout, [&] { return state->hasVal; })
128 ? future_status::ready
129 : future_status::timeout;
130}
131
132// promise is a minimal reimplementation of std::promise, that does not suffer
133// from TSAN false positives. See:
134// https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204
135template <typename T>
136class promise {
137 public:
138 // constructors
139 inline promise() = default;
140 inline promise(promise&& other) = default;
141 inline promise(const promise& other) = default;
142
143 // set_value() stores value to the shared state.
144 // set_value() must only be called once.
145 inline void set_value(const T& value) const;
146 inline void set_value(T&& value) const;
147
148 // get_future() returns a future sharing this promise's state.
149 future<T> get_future();
150
151 private:
152 using State = detail::promise_state<T>;
153 std::shared_ptr<State> state = std::make_shared<State>();
154};
155
156template <typename T>
157future<T> promise<T>::get_future() {
158 return future<T>(state);
159}
160
161template <typename T>
162void promise<T>::set_value(const T& value) const {
163 std::unique_lock<std::mutex> lock(state->mutex);
164 state->val = value;
165 state->hasVal = true;
166 state->cv.notify_all();
167}
168
169template <typename T>
170void promise<T>::set_value(T&& value) const {
171 std::unique_lock<std::mutex> lock(state->mutex);
172 state->val = std::move(value);
173 state->hasVal = true;
174 state->cv.notify_all();
175}
176
177} // namespace dap
178
179#endif // dap_future_h
180