mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
944 lines
25 KiB
C++
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_
|