Files
allwpilib/wpiutil/src/main/native/include/wpi/future.h
2020-12-29 14:26:19 -08:00

944 lines
25 KiB
C++

// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_FUTURE_H_
#define WPIUTIL_WPI_FUTURE_H_
#include <stdint.h>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <functional>
#include <type_traits>
#include <utility>
#include <vector>
#include "wpi/condition_variable.h"
#include "wpi/mutex.h"
namespace wpi {
template <typename T>
class PromiseFactory;
template <typename T>
class future;
template <typename T>
class promise;
template <>
class promise<void>;
namespace detail {
class PromiseFactoryBase {
public:
~PromiseFactoryBase();
bool IsActive() const { return m_active; }
wpi::mutex& GetResultMutex() { return m_resultMutex; }
void Notify() { m_resultCv.notify_all(); }
// must be called with locked lock == ResultMutex
void Wait(std::unique_lock<wpi::mutex>& lock) { m_resultCv.wait(lock); }
// returns false if timeout reached
template <class Clock, class Duration>
bool WaitUntil(std::unique_lock<wpi::mutex>& lock,
const std::chrono::time_point<Clock, Duration>& timeout_time) {
return m_resultCv.wait_until(lock, timeout_time) ==
std::cv_status::no_timeout;
}
void IgnoreResult(uint64_t request);
uint64_t CreateRequest();
// returns true if request was pending
// must be called with ResultMutex held
bool EraseRequest(uint64_t request);
// same as doing CreateRequest() followed by EraseRequest()
// must be called with ResultMutex held
uint64_t CreateErasedRequest() { return ++m_uid; }
private:
wpi::mutex m_resultMutex;
std::atomic_bool m_active{true};
wpi::condition_variable m_resultCv;
uint64_t m_uid = 0;
std::vector<uint64_t> m_requests;
};
template <typename To, typename From>
struct FutureThen {
template <typename F>
static future<To> Create(PromiseFactory<From>& fromFactory, uint64_t request,
PromiseFactory<To>& factory, F&& func);
};
template <typename From>
struct FutureThen<void, From> {
template <typename F>
static future<void> Create(PromiseFactory<From>& fromFactory,
uint64_t request, PromiseFactory<void>& factory,
F&& func);
};
template <typename To>
struct FutureThen<To, void> {
template <typename F>
static future<To> Create(PromiseFactory<void>& fromFactory, uint64_t request,
PromiseFactory<To>& factory, F&& func);
};
template <>
struct FutureThen<void, void> {
template <typename F>
static future<void> Create(PromiseFactory<void>& fromFactory,
uint64_t request, PromiseFactory<void>& factory,
F&& func);
};
} // namespace detail
/**
* A promise factory for lightweight futures.
*
* The lifetime of the factory must be ensured to be longer than any futures
* it creates.
*
* Use CreateRequest() to create the future request id, and then CreateFuture()
* and CreatePromise() to create future and promise objects. A promise should
* only be created once for any given request id.
*
* @tparam T the "return" type of the promise/future
*/
template <typename T>
class PromiseFactory final : public detail::PromiseFactoryBase {
friend class future<T>;
public:
using detail::PromiseFactoryBase::Notify;
using ThenFunction = std::function<void(uint64_t, T)>;
/**
* Creates a future.
*
* @param request the request id returned by CreateRequest()
* @return the future
*/
future<T> CreateFuture(uint64_t request);
/**
* Creates a future and makes it immediately ready.
*
* @return the future
*/
future<T> MakeReadyFuture(T&& value);
/**
* Creates a promise.
*
* @param request the request id returned by CreateRequest()
* @return the promise
*/
promise<T> CreatePromise(uint64_t request);
/**
* Sets a value directly for a future without creating a promise object.
* Identical to `CreatePromise(request).set_value(value)`.
*
* @param request request id, as returned by CreateRequest()
* @param value lvalue
*/
void SetValue(uint64_t request, const T& value);
/**
* Sets a value directly for a future without creating a promise object.
* Identical to `CreatePromise(request).set_value(value)`.
*
* @param request request id, as returned by CreateRequest()
* @param value rvalue
*/
void SetValue(uint64_t request, T&& value);
void SetThen(uint64_t request, uint64_t outRequest, ThenFunction func);
bool IsReady(uint64_t request) noexcept;
T GetResult(uint64_t request);
void WaitResult(uint64_t request);
template <class Clock, class Duration>
bool WaitResultUntil(
uint64_t request,
const std::chrono::time_point<Clock, Duration>& timeout_time);
static PromiseFactory& GetInstance();
private:
struct Then {
Then(uint64_t request_, uint64_t outRequest_, ThenFunction func_)
: request(request_), outRequest(outRequest_), func(std::move(func_)) {}
uint64_t request;
uint64_t outRequest;
ThenFunction func;
};
std::vector<Then> m_thens;
std::vector<std::pair<uint64_t, T>> m_results;
};
/**
* Explicit specialization for PromiseFactory<void>.
*/
template <>
class PromiseFactory<void> final : public detail::PromiseFactoryBase {
friend class future<void>;
public:
using detail::PromiseFactoryBase::Notify;
using ThenFunction = std::function<void(uint64_t)>;
/**
* Creates a future.
*
* @param request the request id returned by CreateRequest()
* @return std::pair of the future and the request id
*/
future<void> CreateFuture(uint64_t request);
/**
* Creates a future and makes it immediately ready.
*
* @return the future
*/
future<void> MakeReadyFuture();
/**
* Creates a promise.
*
* @param request the request id returned by CreateRequest()
* @return the promise
*/
promise<void> CreatePromise(uint64_t request);
/**
* Sets a value directly for a future without creating a promise object.
* Identical to `promise(factory, request).set_value()`.
*
* @param request request id, as returned by CreateRequest()
*/
void SetValue(uint64_t request);
void SetThen(uint64_t request, uint64_t outRequest, ThenFunction func);
bool IsReady(uint64_t request) noexcept;
void GetResult(uint64_t request);
void WaitResult(uint64_t request);
template <class Clock, class Duration>
bool WaitResultUntil(
uint64_t request,
const std::chrono::time_point<Clock, Duration>& timeout_time);
static PromiseFactory& GetInstance();
private:
struct Then {
Then(uint64_t request_, uint64_t outRequest_, ThenFunction func_)
: request(request_), outRequest(outRequest_), func(std::move(func_)) {}
uint64_t request;
uint64_t outRequest;
ThenFunction func;
};
std::vector<Then> m_thens;
std::vector<uint64_t> m_results;
};
/**
* A lightweight version of std::future.
*
* Use either promise::get_future() or PromiseFactory::CreateFuture() to create.
*
* @tparam T the "return" type
*/
template <typename T>
class future final {
friend class PromiseFactory<T>;
friend class promise<T>;
public:
/**
* Constructs an empty (invalid) future.
*/
future() noexcept = default;
future(future&& oth) noexcept {
this->m_request = oth.m_request;
this->m_promises = oth.m_promises;
oth.m_request = 0;
oth.m_promises = nullptr;
}
future(const future&) = delete;
template <typename R>
future(future<R>&& oth) noexcept // NOLINT
: future(oth.then([](R&& val) -> T { return val; })) {}
/**
* Ignores the result of the future if it has not been retrieved.
*/
~future() {
if (m_promises) {
m_promises->IgnoreResult(m_request);
}
}
future& operator=(future&& oth) noexcept {
this->m_request = oth.m_request;
this->m_promises = oth.m_promises;
oth.m_request = 0;
oth.m_promises = nullptr;
return *this;
}
future& operator=(const future&) = delete;
/**
* Gets the value. Calls wait() if the value is not yet available.
* Can only be called once. The future will be marked invalid after the call.
*
* @return The value provided by the corresponding promise.set_value().
*/
T get() {
if (m_promises) {
return m_promises->GetResult(m_request);
} else {
return T();
}
}
template <typename R, typename F>
future<R> then(PromiseFactory<R>& factory, F&& func) {
if (m_promises) {
auto promises = m_promises;
m_promises = nullptr;
return detail::FutureThen<R, T>::Create(*promises, m_request, factory,
func);
} else {
return future<R>();
}
}
template <typename F, typename R = typename std::result_of<F && (T &&)>::type>
future<R> then(F&& func) {
return then(PromiseFactory<R>::GetInstance(), std::forward<F>(func));
}
bool is_ready() const noexcept {
return m_promises && m_promises->IsReady(m_request);
}
/**
* Checks if the future is valid.
* A default-constructed future or one where get() has been called is invalid.
*
* @return True if valid
*/
bool valid() const noexcept { return m_promises; }
/**
* Waits for the promise to provide a value.
* Does not return until the value is available or the promise is destroyed
* (in which case a default-constructed value is "returned").
* If the value has already been provided, returns immediately.
*/
void wait() const {
if (m_promises) {
m_promises->WaitResult(m_request);
}
}
/**
* Waits for the promise to provide a value, or the specified time has been
* reached.
*
* @return True if the promise provided a value, false if timed out.
*/
template <class Clock, class Duration>
bool wait_until(
const std::chrono::time_point<Clock, Duration>& timeout_time) const {
return m_promises && m_promises->WaitResultUntil(m_request, timeout_time);
}
/**
* Waits for the promise to provide a value, or the specified amount of time
* has elapsed.
*
* @return True if the promise provided a value, false if timed out.
*/
template <class Rep, class Period>
bool wait_for(
const std::chrono::duration<Rep, Period>& timeout_duration) const {
return wait_until(std::chrono::steady_clock::now() + timeout_duration);
}
private:
future(PromiseFactory<T>* promises, uint64_t request) noexcept
: m_request(request), m_promises(promises) {}
uint64_t m_request = 0;
PromiseFactory<T>* m_promises = nullptr;
};
/**
* Explicit specialization for future<void>.
*/
template <>
class future<void> final {
friend class PromiseFactory<void>;
friend class promise<void>;
public:
/**
* Constructs an empty (invalid) future.
*/
future() noexcept = default;
future(future&& oth) noexcept {
m_request = oth.m_request;
m_promises = oth.m_promises;
oth.m_request = 0;
oth.m_promises = nullptr;
}
future(const future&) = delete;
/**
* Ignores the result of the future if it has not been retrieved.
*/
~future() {
if (m_promises) {
m_promises->IgnoreResult(m_request);
}
}
future& operator=(future&& oth) noexcept {
m_request = oth.m_request;
m_promises = oth.m_promises;
oth.m_request = 0;
oth.m_promises = nullptr;
return *this;
}
future& operator=(const future&) = delete;
/**
* Gets the value. Calls wait() if the value is not yet available.
* Can only be called once. The future will be marked invalid after the call.
*/
void get() {
if (m_promises) {
m_promises->GetResult(m_request);
}
}
template <typename R, typename F>
future<R> then(PromiseFactory<R>& factory, F&& func) {
if (m_promises) {
auto promises = m_promises;
m_promises = nullptr;
return detail::FutureThen<R, void>::Create(*promises, m_request, factory,
func);
} else {
return future<R>();
}
}
template <typename F, typename R = typename std::result_of<F && ()>::type>
future<R> then(F&& func) {
return then(PromiseFactory<R>::GetInstance(), std::forward<F>(func));
}
bool is_ready() const noexcept {
return m_promises && m_promises->IsReady(m_request);
}
/**
* Checks if the future is valid.
* A default-constructed future or one where get() has been called is invalid.
*
* @return True if valid
*/
bool valid() const noexcept { return m_promises; }
/**
* Waits for the promise to provide a value.
* Does not return until the value is available or the promise is destroyed
* If the value has already been provided, returns immediately.
*/
void wait() const {
if (m_promises) {
m_promises->WaitResult(m_request);
}
}
/**
* Waits for the promise to provide a value, or the specified time has been
* reached.
*
* @return True if the promise provided a value, false if timed out.
*/
template <class Clock, class Duration>
bool wait_until(
const std::chrono::time_point<Clock, Duration>& timeout_time) const {
return m_promises && m_promises->WaitResultUntil(m_request, timeout_time);
}
/**
* Waits for the promise to provide a value, or the specified amount of time
* has elapsed.
*
* @return True if the promise provided a value, false if timed out.
*/
template <class Rep, class Period>
bool wait_for(
const std::chrono::duration<Rep, Period>& timeout_duration) const {
return wait_until(std::chrono::steady_clock::now() + timeout_duration);
}
private:
future(PromiseFactory<void>* promises, uint64_t request) noexcept
: m_request(request), m_promises(promises) {}
uint64_t m_request = 0;
PromiseFactory<void>* m_promises = nullptr;
};
/**
* A lightweight version of std::promise.
*
* Use PromiseFactory::CreatePromise() to create.
*
* @tparam T the "return" type
*/
template <typename T>
class promise final {
friend class PromiseFactory<T>;
public:
/**
* Constructs an empty promise.
*/
promise() : m_promises(&PromiseFactory<T>::GetInstance()) {
m_request = m_promises->CreateRequest();
}
promise(promise&& oth) noexcept
: m_request(oth.m_request), m_promises(oth.m_promises) {
oth.m_request = 0;
oth.m_promises = nullptr;
}
promise(const promise&) = delete;
/**
* Sets the promised value to a default-constructed T if not already set.
*/
~promise() {
if (m_promises) {
m_promises->SetValue(m_request, T());
}
}
promise& operator=(promise&& oth) noexcept {
m_request = oth.m_request;
m_promises = oth.m_promises;
oth.m_request = 0;
oth.m_promises = nullptr;
return *this;
}
promise& operator=(const promise&) = delete;
/**
* Swaps this promise with another one.
*/
void swap(promise& oth) noexcept {
std::swap(m_request, oth.m_request);
std::swap(m_promises, oth.m_promises);
}
/**
* Gets a future for this promise.
*
* @return The future
*/
future<T> get_future() noexcept { return future<T>(m_promises, m_request); }
/**
* Sets the promised value.
* Only effective once (subsequent calls will be ignored).
*
* @param value The value to provide to the waiting future
*/
void set_value(const T& value) {
if (m_promises) {
m_promises->SetValue(m_request, value);
}
m_promises = nullptr;
}
/**
* Sets the promised value.
* Only effective once (subsequent calls will be ignored).
*
* @param value The value to provide to the waiting future
*/
void set_value(T&& value) {
if (m_promises) {
m_promises->SetValue(m_request, std::move(value));
}
m_promises = nullptr;
}
private:
promise(PromiseFactory<T>* promises, uint64_t request) noexcept
: m_request(request), m_promises(promises) {}
uint64_t m_request = 0;
PromiseFactory<T>* m_promises = nullptr;
};
/**
* Explicit specialization for promise<void>.
*/
template <>
class promise<void> final {
friend class PromiseFactory<void>;
public:
/**
* Constructs an empty promise.
*/
promise() : m_promises(&PromiseFactory<void>::GetInstance()) {
m_request = m_promises->CreateRequest();
}
promise(promise&& oth) noexcept
: m_request(oth.m_request), m_promises(oth.m_promises) {
oth.m_request = 0;
oth.m_promises = nullptr;
}
promise(const promise&) = delete;
/**
* Sets the promised value if not already set.
*/
~promise() {
if (m_promises) {
m_promises->SetValue(m_request);
}
}
promise& operator=(promise&& oth) noexcept {
m_request = oth.m_request;
m_promises = oth.m_promises;
oth.m_request = 0;
oth.m_promises = nullptr;
return *this;
}
promise& operator=(const promise&) = delete;
/**
* Swaps this promise with another one.
*/
void swap(promise& oth) noexcept {
std::swap(m_request, oth.m_request);
std::swap(m_promises, oth.m_promises);
}
/**
* Gets a future for this promise.
*
* @return The future
*/
future<void> get_future() noexcept {
return future<void>(m_promises, m_request);
}
/**
* Sets the promised value.
* Only effective once (subsequent calls will be ignored).
*/
void set_value() {
if (m_promises) {
m_promises->SetValue(m_request);
}
m_promises = nullptr;
}
private:
promise(PromiseFactory<void>* promises, uint64_t request) noexcept
: m_request(request), m_promises(promises) {}
uint64_t m_request = 0;
PromiseFactory<void>* m_promises = nullptr;
};
/**
* Constructs a valid future with the value set.
*/
template <typename T>
inline future<T> make_ready_future(T&& value) {
return PromiseFactory<T>::GetInstance().MakeReadyFuture(
std::forward<T>(value));
}
/**
* Constructs a valid future with the value set.
*/
inline future<void> make_ready_future() {
return PromiseFactory<void>::GetInstance().MakeReadyFuture();
}
template <typename T>
inline future<T> PromiseFactory<T>::CreateFuture(uint64_t request) {
return future<T>{this, request};
}
template <typename T>
future<T> PromiseFactory<T>::MakeReadyFuture(T&& value) {
std::unique_lock lock(GetResultMutex());
uint64_t req = CreateErasedRequest();
m_results.emplace_back(std::piecewise_construct, std::forward_as_tuple(req),
std::forward_as_tuple(std::move(value)));
return future<T>{this, req};
}
template <typename T>
inline promise<T> PromiseFactory<T>::CreatePromise(uint64_t request) {
return promise<T>{this, request};
}
template <typename T>
void PromiseFactory<T>::SetValue(uint64_t request, const T& value) {
std::unique_lock lock(GetResultMutex());
if (!EraseRequest(request)) {
return;
}
auto it = std::find_if(m_thens.begin(), m_thens.end(),
[=](const auto& x) { return x.request == request; });
if (it != m_thens.end()) {
uint64_t outRequest = it->outRequest;
ThenFunction func = std::move(it->func);
m_thens.erase(it);
lock.unlock();
return func(outRequest, value);
}
m_results.emplace_back(std::piecewise_construct,
std::forward_as_tuple(request),
std::forward_as_tuple(value));
Notify();
}
template <typename T>
void PromiseFactory<T>::SetValue(uint64_t request, T&& value) {
std::unique_lock lock(GetResultMutex());
if (!EraseRequest(request)) {
return;
}
auto it = std::find_if(m_thens.begin(), m_thens.end(),
[=](const auto& x) { return x.request == request; });
if (it != m_thens.end()) {
uint64_t outRequest = it->outRequest;
ThenFunction func = std::move(it->func);
m_thens.erase(it);
lock.unlock();
return func(outRequest, std::move(value));
}
m_results.emplace_back(std::piecewise_construct,
std::forward_as_tuple(request),
std::forward_as_tuple(std::move(value)));
Notify();
}
template <typename T>
void PromiseFactory<T>::SetThen(uint64_t request, uint64_t outRequest,
ThenFunction func) {
std::unique_lock lock(GetResultMutex());
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r.first == request; });
if (it != m_results.end()) {
auto val = std::move(it->second);
m_results.erase(it);
lock.unlock();
return func(outRequest, std::move(val));
}
m_thens.emplace_back(request, outRequest, func);
}
template <typename T>
bool PromiseFactory<T>::IsReady(uint64_t request) noexcept {
std::unique_lock lock(GetResultMutex());
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r.first == request; });
return it != m_results.end();
}
template <typename T>
T PromiseFactory<T>::GetResult(uint64_t request) {
// wait for response
std::unique_lock lock(GetResultMutex());
while (IsActive()) {
// Did we get a response to *our* request?
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r.first == request; });
if (it != m_results.end()) {
// Yes, remove it from the vector and we're done.
auto rv = std::move(it->second);
m_results.erase(it);
return rv;
}
// No, keep waiting for a response
Wait(lock);
}
return T();
}
template <typename T>
void PromiseFactory<T>::WaitResult(uint64_t request) {
// wait for response
std::unique_lock lock(GetResultMutex());
while (IsActive()) {
// Did we get a response to *our* request?
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r.first == request; });
if (it != m_results.end()) {
return;
}
// No, keep waiting for a response
Wait(lock);
}
}
template <typename T>
template <class Clock, class Duration>
bool PromiseFactory<T>::WaitResultUntil(
uint64_t request,
const std::chrono::time_point<Clock, Duration>& timeout_time) {
std::unique_lock lock(GetResultMutex());
bool timeout = false;
while (IsActive()) {
// Did we get a response to *our* request?
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r.first == request; });
if (it != m_results.end()) {
return true;
}
if (timeout) {
break;
}
// No, keep waiting for a response
if (!WaitUntil(lock, timeout_time)) {
timeout = true;
}
}
return false;
}
template <typename T>
PromiseFactory<T>& PromiseFactory<T>::GetInstance() {
static PromiseFactory inst;
return inst;
}
inline future<void> PromiseFactory<void>::CreateFuture(uint64_t request) {
return future<void>{this, request};
}
inline promise<void> PromiseFactory<void>::CreatePromise(uint64_t request) {
return promise<void>{this, request};
}
template <class Clock, class Duration>
bool PromiseFactory<void>::WaitResultUntil(
uint64_t request,
const std::chrono::time_point<Clock, Duration>& timeout_time) {
std::unique_lock lock(GetResultMutex());
bool timeout = false;
while (IsActive()) {
// Did we get a response to *our* request?
auto it = std::find_if(m_results.begin(), m_results.end(),
[=](const auto& r) { return r == request; });
if (it != m_results.end()) {
return true;
}
if (timeout) {
break;
}
// No, keep waiting for a response
if (!WaitUntil(lock, timeout_time)) {
timeout = true;
}
}
return false;
}
template <typename To, typename From>
template <typename F>
future<To> detail::FutureThen<To, From>::Create(
PromiseFactory<From>& fromFactory, uint64_t request,
PromiseFactory<To>& factory, F&& func) {
uint64_t req = factory.CreateRequest();
fromFactory.SetThen(request, req, [&factory, func](uint64_t r, From value) {
factory.SetValue(r, func(std::move(value)));
});
return factory.CreateFuture(req);
}
template <typename From>
template <typename F>
future<void> detail::FutureThen<void, From>::Create(
PromiseFactory<From>& fromFactory, uint64_t request,
PromiseFactory<void>& factory, F&& func) {
uint64_t req = factory.CreateRequest();
fromFactory.SetThen(request, req, [&factory, func](uint64_t r, From value) {
func(std::move(value));
factory.SetValue(r);
});
return factory.CreateFuture(req);
}
template <typename To>
template <typename F>
future<To> detail::FutureThen<To, void>::Create(
PromiseFactory<void>& fromFactory, uint64_t request,
PromiseFactory<To>& factory, F&& func) {
uint64_t req = factory.CreateRequest();
fromFactory.SetThen(request, req, [&factory, func](uint64_t r) {
factory.SetValue(r, func());
});
return factory.CreateFuture(req);
}
template <typename F>
future<void> detail::FutureThen<void, void>::Create(
PromiseFactory<void>& fromFactory, uint64_t request,
PromiseFactory<void>& factory, F&& func) {
uint64_t req = factory.CreateRequest();
fromFactory.SetThen(request, req, [&factory, func](uint64_t r) {
func();
factory.SetValue(r);
});
return factory.CreateFuture(req);
}
} // namespace wpi
#endif // WPIUTIL_WPI_FUTURE_H_