WPILibC++ 2027.0.0-alpha-4
Loading...
Searching...
No Matches
AsyncFunction.hpp
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#pragma once
6
7#include <stdint.h>
8
9#include <concepts>
10#include <functional>
11#include <memory>
12#include <thread>
13#include <tuple>
14#include <utility>
15#include <vector>
16
17#include <uv.h>
18
19#include "wpi/net/uv/Handle.hpp"
20#include "wpi/net/uv/Loop.hpp"
21#include "wpi/util/future.hpp"
22#include "wpi/util/mutex.hpp"
23
24namespace wpi::net::uv {
25
26template <typename T>
28
29/**
30 * Function async handle.
31 * Async handles allow the user to "wakeup" the event loop and have a function
32 * called from another thread that returns a result to the calling thread.
33 */
34template <typename R, typename... T>
35class AsyncFunction<R(T...)> final
36 : public HandleImpl<AsyncFunction<R(T...)>, uv_async_t> {
37 struct private_init {};
38
39 public:
40 AsyncFunction(const std::shared_ptr<Loop>& loop,
41 std::function<void(wpi::util::promise<R>, T...)> func,
42 const private_init&)
43 : wakeup{std::move(func)}, m_loop{loop} {}
44 ~AsyncFunction() noexcept override {
45 if (auto loop = m_loop.lock()) {
46 this->Close();
47 } else {
48 this->ForceClosed();
49 }
50 }
51
52 /**
53 * Create an async handle.
54 *
55 * @param loop Loop object where this handle runs.
56 * @param func wakeup function to be called (sets wakeup value); the function
57 * needs to return void, and its first parameter is the promise
58 * for the result. If no value is set on the promise by the
59 * wakeup function, a default-constructed value is "returned".
60 */
61 static std::shared_ptr<AsyncFunction> Create(
62 Loop& loop,
63 std::function<void(wpi::util::promise<R>, T...)> func = nullptr) {
64 return Create(loop.shared_from_this(), std::move(func));
65 }
66
67 /**
68 * Create an async handle.
69 *
70 * @param loop Loop object where this handle runs.
71 * @param func wakeup function to be called (sets wakeup value); the function
72 * needs to return void, and its first parameter is the promise
73 * for the result. If no value is set on the promise by the
74 * wakeup function, a default-constructed value is "returned".
75 */
76 static std::shared_ptr<AsyncFunction> Create(
77 const std::shared_ptr<Loop>& loop,
78 std::function<void(wpi::util::promise<R>, T...)> func = nullptr) {
79 if (loop->IsClosing()) {
80 return nullptr;
81 }
82 auto h =
83 std::make_shared<AsyncFunction>(loop, std::move(func), private_init{});
84 int err =
85 uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
86 auto& h = *static_cast<AsyncFunction*>(handle->data);
87 std::unique_lock lock(h.m_mutex);
88
89 if (!h.m_params.empty()) {
90 // for each set of parameters in the input queue, call the wakeup
91 // function and put the result in the output queue if the caller is
92 // waiting for it
93 for (auto&& v : h.m_params) {
94 auto p = h.m_promises.CreatePromise(v.first);
95 if (h.wakeup) {
96 std::apply(h.wakeup,
97 std::tuple_cat(std::make_tuple(std::move(p)),
98 std::move(v.second)));
99 }
100 }
101 h.m_params.clear();
102 // wake up any threads that might be waiting for the result
103 lock.unlock();
104 h.m_promises.Notify();
105 }
106 });
107 if (err < 0) {
108 loop->ReportError(err);
109 return nullptr;
110 }
111 h->Keep();
112 return h;
113 }
114
115 /**
116 * Wakeup the event loop, call the async function, and return a future for
117 * the result.
118 *
119 * It’s safe to call this function from any thread including the loop thread.
120 * The async function will be called on the loop thread.
121 *
122 * The future will return a default-constructed result if this handle is
123 * destroyed while waiting for a result.
124 */
125 template <typename... U>
127 // create the future
128 uint64_t req = m_promises.CreateRequest();
129
130 auto loop = m_loop.lock();
131 if (loop->IsClosing()) {
132 if constexpr (std::same_as<R, void>) {
133 return m_promises.MakeReadyFuture();
134 } else {
135 return m_promises.MakeReadyFuture({});
136 }
137 }
138 if (loop && loop->GetThreadId() == std::this_thread::get_id()) {
139 // called from within the loop, just call the function directly
140 wakeup(m_promises.CreatePromise(req), std::forward<U>(u)...);
141 return m_promises.CreateFuture(req);
142 }
143
144 // add the parameters to the input queue
145 {
146 std::scoped_lock lock(m_mutex);
147 m_params.emplace_back(std::piecewise_construct,
148 std::forward_as_tuple(req),
149 std::forward_as_tuple(std::forward<U>(u)...));
150 }
151
152 // signal the loop
153 if (loop) {
154 this->Invoke(&uv_async_send, this->GetRaw());
155 }
156
157 // return future
158 return m_promises.CreateFuture(req);
159 }
160
161 template <typename... U>
163 return Call(std::forward<U>(u)...);
164 }
165
166 /**
167 * Function called (on event loop thread) when the async is called.
168 */
169 std::function<void(wpi::util::promise<R>, T...)> wakeup;
170
171 private:
172 wpi::util::mutex m_mutex;
173 std::vector<std::pair<uint64_t, std::tuple<T...>>> m_params;
175 std::weak_ptr<Loop> m_loop;
176};
177
178} // namespace wpi::net::uv
wpi::util::future< R > operator()(U &&... u)
Definition AsyncFunction.hpp:162
std::function< void(wpi::util::promise< R >, T...)> wakeup
Function called (on event loop thread) when the async is called.
Definition AsyncFunction.hpp:169
static std::shared_ptr< AsyncFunction > Create(const std::shared_ptr< Loop > &loop, std::function< void(wpi::util::promise< R >, T...)> func=nullptr)
Create an async handle.
Definition AsyncFunction.hpp:76
AsyncFunction(const std::shared_ptr< Loop > &loop, std::function< void(wpi::util::promise< R >, T...)> func, const private_init &)
Definition AsyncFunction.hpp:40
static std::shared_ptr< AsyncFunction > Create(Loop &loop, std::function< void(wpi::util::promise< R >, T...)> func=nullptr)
Create an async handle.
Definition AsyncFunction.hpp:61
~AsyncFunction() noexcept override
Definition AsyncFunction.hpp:44
wpi::util::future< R > Call(U &&... u)
Wakeup the event loop, call the async function, and return a future for the result.
Definition AsyncFunction.hpp:126
Definition AsyncFunction.hpp:27
void ForceClosed() noexcept
Definition Handle.hpp:259
bool Invoke(F &&f, Args &&... args) const
Definition Handle.hpp:265
void Close() noexcept
Request handle to be closed.
uv_async_t * GetRaw() const noexcept
Definition Handle.hpp:303
Event loop.
Definition Loop.hpp:35
A promise factory for lightweight futures.
Definition future.hpp:122
A lightweight version of std::future.
Definition future.hpp:270
A lightweight version of std::promise.
Definition future.hpp:527
Definition StringMap.hpp:773
Definition Errors.hpp:112
Definition Prepare.hpp:14
::std::mutex mutex
Definition mutex.hpp:17
struct uv_async_s uv_async_t
Definition uv.h:228
UV_EXTERN int uv_async_init(uv_loop_t *, uv_async_t *async, uv_async_cb async_cb)
UV_EXTERN int uv_async_send(uv_async_t *async)