2018-07-17 01:06:24 -07:00
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
|
|
|
|
|
/* 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_ASYNC_H_
|
|
|
|
|
|
#define WPIUTIL_WPI_UV_ASYNC_H_
|
|
|
|
|
|
|
|
|
|
|
|
#include <uv.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <memory>
|
2018-10-06 09:14:55 -07:00
|
|
|
|
#include <thread>
|
2018-08-06 11:08:17 -07:00
|
|
|
|
#include <tuple>
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
#include <vector>
|
2018-07-17 01:06:24 -07:00
|
|
|
|
|
2018-08-06 11:08:17 -07:00
|
|
|
|
#include "wpi/STLExtras.h"
|
2018-07-17 01:06:24 -07:00
|
|
|
|
#include "wpi/Signal.h"
|
2018-08-06 11:08:17 -07:00
|
|
|
|
#include "wpi/mutex.h"
|
2018-07-17 01:06:24 -07:00
|
|
|
|
#include "wpi/uv/Handle.h"
|
2018-08-06 11:08:17 -07:00
|
|
|
|
#include "wpi/uv/Loop.h"
|
2018-07-17 01:06:24 -07:00
|
|
|
|
|
|
|
|
|
|
namespace wpi {
|
|
|
|
|
|
namespace uv {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Async handle.
|
|
|
|
|
|
* Async handles allow the user to "wakeup" the event loop and have a signal
|
|
|
|
|
|
* generated from another thread.
|
2018-08-06 11:08:17 -07:00
|
|
|
|
*
|
|
|
|
|
|
* Data may be passed into the callback called on the event loop by using
|
|
|
|
|
|
* template parameters. If data parameters are used, the async callback will
|
|
|
|
|
|
* be called once for every call to Send(). If no data parameters are used,
|
|
|
|
|
|
* the async callback may or may not be called for every call to Send() (e.g.
|
|
|
|
|
|
* the calls may be coaleasced).
|
|
|
|
|
|
*/
|
|
|
|
|
|
template <typename... T>
|
|
|
|
|
|
class Async final : public HandleImpl<Async<T...>, uv_async_t> {
|
|
|
|
|
|
struct private_init {};
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2018-09-16 15:36:19 -07:00
|
|
|
|
Async(const std::shared_ptr<Loop>& loop, const private_init&)
|
|
|
|
|
|
: m_loop{loop} {}
|
|
|
|
|
|
~Async() noexcept override {
|
|
|
|
|
|
if (auto loop = m_loop.lock())
|
|
|
|
|
|
this->Close();
|
|
|
|
|
|
else
|
|
|
|
|
|
this->ForceClosed();
|
|
|
|
|
|
}
|
2018-08-06 11:08:17 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Create an async handle.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param loop Loop object where this handle runs.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static std::shared_ptr<Async> Create(Loop& loop) {
|
2018-09-16 15:36:19 -07:00
|
|
|
|
return Create(loop.shared_from_this());
|
2018-08-06 11:08:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Create an async handle.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param loop Loop object where this handle runs.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop) {
|
2018-09-16 15:36:19 -07:00
|
|
|
|
auto h = std::make_shared<Async>(loop, private_init{});
|
|
|
|
|
|
int err =
|
|
|
|
|
|
uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
|
|
|
|
|
|
auto& h = *static_cast<Async*>(handle->data);
|
|
|
|
|
|
std::lock_guard<wpi::mutex> lock(h.m_mutex);
|
|
|
|
|
|
for (auto&& v : h.m_data) apply_tuple(h.wakeup, v);
|
|
|
|
|
|
h.m_data.clear();
|
|
|
|
|
|
});
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
|
loop->ReportError(err);
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
h->Keep();
|
|
|
|
|
|
return h;
|
2018-08-06 11:08:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Wakeup the event loop and emit the event.
|
|
|
|
|
|
*
|
2018-10-06 09:14:55 -07:00
|
|
|
|
* It’s safe to call this function from any thread including the loop thread.
|
2018-08-06 11:08:17 -07:00
|
|
|
|
* An async event will be emitted on the loop thread.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template <typename... U>
|
|
|
|
|
|
void Send(U&&... u) {
|
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(std::forward<U>(u)...);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-06 11:08:17 -07:00
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<wpi::mutex> lock(m_mutex);
|
|
|
|
|
|
m_data.emplace_back(std::forward_as_tuple(std::forward<U>(u)...));
|
|
|
|
|
|
}
|
2018-10-06 09:14:55 -07:00
|
|
|
|
if (loop) this->Invoke(&uv_async_send, this->GetRaw());
|
2018-08-06 11:08:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Signal generated (on event loop thread) when the async event occurs.
|
|
|
|
|
|
*/
|
|
|
|
|
|
sig::Signal<T...> wakeup;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
wpi::mutex m_mutex;
|
|
|
|
|
|
std::vector<std::tuple<T...>> m_data;
|
2018-09-16 15:36:19 -07:00
|
|
|
|
std::weak_ptr<Loop> m_loop;
|
2018-08-06 11:08:17 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Async specialization for no data parameters. The async callback may or may
|
|
|
|
|
|
* not be called for every call to Send() (e.g. the calls may be coaleasced).
|
2018-07-17 01:06:24 -07:00
|
|
|
|
*/
|
2018-08-06 11:08:17 -07:00
|
|
|
|
template <>
|
|
|
|
|
|
class Async<> final : public HandleImpl<Async<>, uv_async_t> {
|
2018-07-17 01:06:24 -07:00
|
|
|
|
struct private_init {};
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2018-09-16 15:36:19 -07:00
|
|
|
|
Async(const std::shared_ptr<Loop>& loop, const private_init&)
|
|
|
|
|
|
: m_loop(loop) {}
|
|
|
|
|
|
~Async() noexcept override;
|
2018-07-17 01:06:24 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Create an async handle.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param loop Loop object where this handle runs.
|
|
|
|
|
|
*/
|
2018-09-16 15:36:19 -07:00
|
|
|
|
static std::shared_ptr<Async> Create(Loop& loop) {
|
|
|
|
|
|
return Create(loop.shared_from_this());
|
|
|
|
|
|
}
|
2018-07-17 01:06:24 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Create an async handle.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param loop Loop object where this handle runs.
|
|
|
|
|
|
*/
|
2018-09-16 15:36:19 -07:00
|
|
|
|
static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop);
|
2018-07-17 01:06:24 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Wakeup the event loop and emit the event.
|
|
|
|
|
|
*
|
|
|
|
|
|
* It’s safe to call this function from any thread.
|
|
|
|
|
|
* An async event will be emitted on the loop thread.
|
|
|
|
|
|
*/
|
2018-09-16 15:36:19 -07:00
|
|
|
|
void Send() {
|
|
|
|
|
|
if (auto loop = m_loop.lock()) Invoke(&uv_async_send, GetRaw());
|
|
|
|
|
|
}
|
2018-07-17 01:06:24 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Signal generated (on event loop thread) when the async event occurs.
|
|
|
|
|
|
*/
|
|
|
|
|
|
sig::Signal<> wakeup;
|
2018-09-16 15:36:19 -07:00
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
std::weak_ptr<Loop> m_loop;
|
2018-07-17 01:06:24 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace uv
|
|
|
|
|
|
} // namespace wpi
|
|
|
|
|
|
|
|
|
|
|
|
#endif // WPIUTIL_WPI_UV_ASYNC_H_
|