/*----------------------------------------------------------------------------*/ /* Copyright (c) 2018-2019 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 #include #include #include #include #include #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 class Async final : public HandleImpl, uv_async_t> { struct private_init {}; public: Async(const std::shared_ptr& 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 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 Create(const std::shared_ptr& loop) { auto h = std::make_shared(loop, private_init{}); int err = uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) { auto& h = *static_cast(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. * * It’s safe to call this function from any thread including the loop thread. * An async event will be emitted on the loop thread. */ template 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)...); return; } { std::scoped_lock lock(m_mutex); m_data.emplace_back(std::forward_as_tuple(std::forward(u)...)); } if (loop) this->Invoke(&uv_async_send, this->GetRaw()); } /** * Signal generated (on event loop thread) when the async event occurs. */ sig::Signal wakeup; private: wpi::mutex m_mutex; std::vector> m_data; std::weak_ptr 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, uv_async_t> { struct private_init {}; public: Async(const std::shared_ptr& 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 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 Create(const std::shared_ptr& loop); /** * 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. */ 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 m_loop; }; } // namespace uv } // namespace wpi #endif // WPIUTIL_WPI_UV_ASYNC_H_