2018-08-13 18:48:36 -07:00
|
|
|
|
/*----------------------------------------------------------------------------*/
|
2019-07-07 15:44:43 -07:00
|
|
|
|
/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
|
2018-08-13 18:48:36 -07:00
|
|
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
|
|
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
|
|
|
|
/* the project. */
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef WPIUTIL_WPI_UV_ASYNCFUNCTION_H_
|
|
|
|
|
|
#define WPIUTIL_WPI_UV_ASYNCFUNCTION_H_
|
|
|
|
|
|
|
2018-09-05 23:01:57 -07:00
|
|
|
|
#include <stdint.h>
|
2018-08-13 18:48:36 -07:00
|
|
|
|
#include <uv.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
|
|
#include <memory>
|
2018-10-06 09:14:55 -07:00
|
|
|
|
#include <thread>
|
2018-08-13 18:48:36 -07:00
|
|
|
|
#include <tuple>
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
2018-09-05 23:01:57 -07:00
|
|
|
|
#include "wpi/future.h"
|
2018-08-13 18:48:36 -07:00
|
|
|
|
#include "wpi/mutex.h"
|
|
|
|
|
|
#include "wpi/uv/Handle.h"
|
|
|
|
|
|
#include "wpi/uv/Loop.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace wpi {
|
|
|
|
|
|
namespace uv {
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
|
class AsyncFunction;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Function async handle.
|
|
|
|
|
|
* Async handles allow the user to "wakeup" the event loop and have a function
|
|
|
|
|
|
* called from another thread that returns a result to the calling thread.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template <typename R, typename... T>
|
|
|
|
|
|
class AsyncFunction<R(T...)> final
|
2018-09-05 23:01:57 -07:00
|
|
|
|
: public HandleImpl<AsyncFunction<R(T...)>, uv_async_t> {
|
2018-08-13 18:48:36 -07:00
|
|
|
|
struct private_init {};
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2018-09-16 15:36:19 -07:00
|
|
|
|
AsyncFunction(const std::shared_ptr<Loop>& loop,
|
|
|
|
|
|
std::function<void(promise<R>, T...)> func, const private_init&)
|
|
|
|
|
|
: wakeup{func}, m_loop{loop} {}
|
|
|
|
|
|
~AsyncFunction() noexcept override {
|
|
|
|
|
|
if (auto loop = m_loop.lock())
|
|
|
|
|
|
this->Close();
|
|
|
|
|
|
else
|
|
|
|
|
|
this->ForceClosed();
|
|
|
|
|
|
}
|
2018-08-13 18:48:36 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Create an async handle.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param loop Loop object where this handle runs.
|
2018-09-05 23:01:57 -07:00
|
|
|
|
* @param func wakeup function to be called (sets wakeup value); the function
|
|
|
|
|
|
* needs to return void, and its first parameter is the promise
|
|
|
|
|
|
* for the result. If no value is set on the promise by the
|
|
|
|
|
|
* wakeup function, a default-constructed value is "returned".
|
2018-08-13 18:48:36 -07:00
|
|
|
|
*/
|
|
|
|
|
|
static std::shared_ptr<AsyncFunction> Create(
|
2018-09-05 23:01:57 -07:00
|
|
|
|
Loop& loop, std::function<void(promise<R>, T...)> func = nullptr) {
|
2018-09-16 15:36:19 -07:00
|
|
|
|
return Create(loop.shared_from_this(), std::move(func));
|
2018-08-13 18:48:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Create an async handle.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param loop Loop object where this handle runs.
|
2018-09-05 23:01:57 -07:00
|
|
|
|
* @param func wakeup function to be called (sets wakeup value); the function
|
|
|
|
|
|
* needs to return void, and its first parameter is the promise
|
|
|
|
|
|
* for the result. If no value is set on the promise by the
|
|
|
|
|
|
* wakeup function, a default-constructed value is "returned".
|
2018-08-13 18:48:36 -07:00
|
|
|
|
*/
|
|
|
|
|
|
static std::shared_ptr<AsyncFunction> Create(
|
|
|
|
|
|
const std::shared_ptr<Loop>& loop,
|
2018-09-05 23:01:57 -07:00
|
|
|
|
std::function<void(promise<R>, T...)> func = nullptr) {
|
2018-09-16 15:36:19 -07:00
|
|
|
|
auto h =
|
|
|
|
|
|
std::make_shared<AsyncFunction>(loop, std::move(func), private_init{});
|
|
|
|
|
|
int err =
|
|
|
|
|
|
uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
|
|
|
|
|
|
auto& h = *static_cast<AsyncFunction*>(handle->data);
|
|
|
|
|
|
std::unique_lock<wpi::mutex> lock(h.m_mutex);
|
|
|
|
|
|
|
|
|
|
|
|
if (!h.m_params.empty()) {
|
|
|
|
|
|
// for each set of parameters in the input queue, call the wakeup
|
|
|
|
|
|
// function and put the result in the output queue if the caller is
|
|
|
|
|
|
// waiting for it
|
|
|
|
|
|
for (auto&& v : h.m_params) {
|
|
|
|
|
|
auto p = h.m_promises.CreatePromise(v.first);
|
|
|
|
|
|
if (h.wakeup)
|
2019-07-07 15:44:43 -07:00
|
|
|
|
std::apply(h.wakeup,
|
|
|
|
|
|
std::tuple_cat(std::make_tuple(std::move(p)),
|
|
|
|
|
|
std::move(v.second)));
|
2018-09-16 15:36:19 -07:00
|
|
|
|
}
|
|
|
|
|
|
h.m_params.clear();
|
|
|
|
|
|
// wake up any threads that might be waiting for the result
|
|
|
|
|
|
lock.unlock();
|
|
|
|
|
|
h.m_promises.Notify();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
|
loop->ReportError(err);
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
h->Keep();
|
|
|
|
|
|
return h;
|
2018-08-13 18:48:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2018-09-05 23:01:57 -07:00
|
|
|
|
* Wakeup the event loop, call the async function, and return a future for
|
|
|
|
|
|
* the result.
|
2018-08-13 18:48:36 -07:00
|
|
|
|
*
|
2018-10-06 09:14:55 -07:00
|
|
|
|
* It’s safe to call this function from any thread including the loop thread.
|
2018-08-13 18:48:36 -07:00
|
|
|
|
* The async function will be called on the loop thread.
|
|
|
|
|
|
*
|
2018-09-05 23:01:57 -07:00
|
|
|
|
* The future will return a default-constructed result if this handle is
|
|
|
|
|
|
* destroyed while waiting for a result.
|
2018-08-13 18:48:36 -07:00
|
|
|
|
*/
|
|
|
|
|
|
template <typename... U>
|
2018-09-05 23:01:57 -07:00
|
|
|
|
future<R> Call(U&&... u) {
|
|
|
|
|
|
// create the future
|
|
|
|
|
|
uint64_t req = m_promises.CreateRequest();
|
2018-08-13 18:48:36 -07:00
|
|
|
|
|
2018-10-06 09:14:55 -07:00
|
|
|
|
auto loop = m_loop.lock();
|
|
|
|
|
|
if (loop && loop->GetThreadId() == std::this_thread::get_id()) {
|
|
|
|
|
|
// called from within the loop, just call the function directly
|
|
|
|
|
|
wakeup(m_promises.CreatePromise(req), std::forward<U>(u)...);
|
|
|
|
|
|
return m_promises.CreateFuture(req);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-13 18:48:36 -07:00
|
|
|
|
// add the parameters to the input queue
|
|
|
|
|
|
{
|
2018-09-05 23:01:57 -07:00
|
|
|
|
std::lock_guard<wpi::mutex> lock(m_mutex);
|
2018-08-13 18:48:36 -07:00
|
|
|
|
m_params.emplace_back(std::piecewise_construct,
|
2018-09-05 23:01:57 -07:00
|
|
|
|
std::forward_as_tuple(req),
|
2018-08-13 18:48:36 -07:00
|
|
|
|
std::forward_as_tuple(std::forward<U>(u)...));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// signal the loop
|
2018-10-06 09:14:55 -07:00
|
|
|
|
if (loop) this->Invoke(&uv_async_send, this->GetRaw());
|
2018-08-13 18:48:36 -07:00
|
|
|
|
|
2018-09-05 23:01:57 -07:00
|
|
|
|
// return future
|
|
|
|
|
|
return m_promises.CreateFuture(req);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename... U>
|
|
|
|
|
|
future<R> operator()(U&&... u) {
|
|
|
|
|
|
return Call(std::forward<U>(u)...);
|
2018-08-13 18:48:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Function called (on event loop thread) when the async is called.
|
|
|
|
|
|
*/
|
2018-09-05 23:01:57 -07:00
|
|
|
|
std::function<void(promise<R>, T...)> wakeup;
|
2018-08-13 18:48:36 -07:00
|
|
|
|
|
|
|
|
|
|
private:
|
2018-09-05 23:01:57 -07:00
|
|
|
|
wpi::mutex m_mutex;
|
|
|
|
|
|
std::vector<std::pair<uint64_t, std::tuple<T...>>> m_params;
|
|
|
|
|
|
PromiseFactory<R> m_promises;
|
2018-09-16 15:36:19 -07:00
|
|
|
|
std::weak_ptr<Loop> m_loop;
|
2018-08-13 18:48:36 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace uv
|
|
|
|
|
|
} // namespace wpi
|
|
|
|
|
|
|
|
|
|
|
|
#endif // WPIUTIL_WPI_UV_ASYNCFUNCTION_H_
|