// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include #include #include namespace lsp { // internal functionality namespace detail { template struct promise_state { T val; std::mutex mutex; std::condition_variable cv; bool hasVal = false; }; } // namespace detail // forward declaration template class promise; // future_status is the enumeration returned by future::wait_for and // future::wait_until. enum class future_status { ready, timeout, }; // future is a minimal reimplementation of std::future, that does not suffer // from TSAN false positives. See: // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204 template class future { public: using State = detail::promise_state; // constructors inline future() = default; inline future(future&&) = default; // valid() returns true if the future has an internal state. bool valid() const; // get() blocks until the future has a valid result, and returns it. // The future must have a valid internal state to call this method. inline T get(); // wait() blocks until the future has a valid result. // The future must have a valid internal state to call this method. void wait() const; // wait_for() blocks until the future has a valid result, or the timeout is // reached. // The future must have a valid internal state to call this method. template future_status wait_for( const std::chrono::duration& timeout) const; // wait_until() blocks until the future has a valid result, or the timeout is // reached. // The future must have a valid internal state to call this method. template future_status wait_until( const std::chrono::time_point& timeout) const; private: friend promise; future(const future&) = delete; inline future(const std::shared_ptr& state); std::shared_ptr state = std::make_shared(); }; template future::future(const std::shared_ptr& s) : state(s) {} template bool future::valid() const { return static_cast(state); } template T future::get() { std::unique_lock lock(state->mutex); state->cv.wait(lock, [&] { return state->hasVal; }); return state->val; } template void future::wait() const { std::unique_lock lock(state->mutex); state->cv.wait(lock, [&] { return state->hasVal; }); } template template future_status future::wait_for( const std::chrono::duration& timeout) const { std::unique_lock lock(state->mutex); return state->cv.wait_for(lock, timeout, [&] { return state->hasVal; }) ? future_status::ready : future_status::timeout; } template template future_status future::wait_until( const std::chrono::time_point& timeout) const { std::unique_lock lock(state->mutex); return state->cv.wait_until(lock, timeout, [&] { return state->hasVal; }) ? future_status::ready : future_status::timeout; } // promise is a minimal reimplementation of std::promise, that does not suffer // from TSAN false positives. See: // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204 template class promise { public: // constructors inline promise() = default; inline promise(promise&& other) = default; inline promise(const promise& other) = default; // set_value() stores value to the shared state. // set_value() must only be called once. inline void set_value(const T& value) const; inline void set_value(T&& value) const; // get_future() returns a future sharing this promise's state. future get_future(); private: using State = detail::promise_state; std::shared_ptr state = std::make_shared(); }; template future promise::get_future() { return future(state); } template void promise::set_value(const T& value) const { std::unique_lock lock(state->mutex); state->val = value; state->hasVal = true; state->cv.notify_all(); } template void promise::set_value(T&& value) const { std::unique_lock lock(state->mutex); state->val = std::move(value); state->hasVal = true; state->cv.notify_all(); } } // namespace lsp