Files
allwpilib/wpiutil/src/main/native/include/wpi/uv/Async.h
Peter Johnson 2aed432b4b Add braces to C++ single-line loops and conditionals (NFC) (#2973)
This makes code easier to read and more consistent between C++ and Java.
Also update clang-format settings to always add a line break (even if no braces are used).
2020-12-28 12:58:06 -08:00

172 lines
4.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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_UV_ASYNC_H_
#define WPIUTIL_WPI_UV_ASYNC_H_
#include <uv.h>
#include <memory>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>
#include "wpi/Signal.h"
#include "wpi/mutex.h"
#include "wpi/uv/Handle.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
/**
* Async handle.
* Async handles allow the user to "wakeup" the event loop and have a signal
* generated from another thread.
*
* 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:
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();
}
}
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(Loop& loop) {
return Create(loop.shared_from_this());
}
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop) {
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::scoped_lock lock(h.m_mutex);
for (auto&& v : h.m_data) {
std::apply(h.wakeup, v);
}
h.m_data.clear();
});
if (err < 0) {
loop->ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
/**
* Wakeup the event loop and emit the event.
*
* Its safe to call this function from any thread including the loop thread.
* An async event will be emitted on the loop thread.
*/
template <typename... U>
void Send(U&&... u) {
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;
}
{
std::scoped_lock lock(m_mutex);
m_data.emplace_back(std::forward_as_tuple(std::forward<U>(u)...));
}
if (loop) {
this->Invoke(&uv_async_send, this->GetRaw());
}
}
/**
* 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;
std::weak_ptr<Loop> m_loop;
};
/**
* 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).
*/
template <>
class Async<> final : public HandleImpl<Async<>, uv_async_t> {
struct private_init {};
public:
Async(const std::shared_ptr<Loop>& loop, const private_init&)
: m_loop(loop) {}
~Async() noexcept override;
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(Loop& loop) {
return Create(loop.shared_from_this());
}
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop);
/**
* Wakeup the event loop and emit the event.
*
* Its safe to call this function from any thread.
* An async event will be emitted on the loop thread.
*/
void Send() {
if (auto loop = m_loop.lock()) {
Invoke(&uv_async_send, GetRaw());
}
}
/**
* Signal generated (on event loop thread) when the async event occurs.
*/
sig::Signal<> wakeup;
private:
std::weak_ptr<Loop> m_loop;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_ASYNC_H_