xref: /aosp_15_r20/external/pigweed/pw_rpc/public/pw_rpc/synchronous_call.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <utility>
17 
18 #include "pw_chrono/system_clock.h"
19 #include "pw_rpc/client.h"
20 #include "pw_rpc/internal/method_info.h"
21 #include "pw_rpc/internal/synchronous_call_impl.h"
22 #include "pw_rpc/synchronous_call_result.h"
23 
24 /// @file pw_rpc/synchronous_call.h
25 ///
26 /// `pw_rpc` provides wrappers that convert the asynchronous client API to a
27 /// synchronous API. The `SynchronousCall<RpcMethod>` functions wrap the
28 /// asynchronous client RPC call with a timed thread notification and returns
29 /// once a result is known or a timeout has occurred. Only unary methods are
30 /// supported.
31 ///
32 /// The Nanopb and pwpb APIs return a `SynchronousCallResult<Response>` object,
33 /// which can be queried to determine whether any error scenarios occurred and,
34 /// if not, access the response. The raw API executes a function when the call
35 /// completes or returns a `pw::Status` if it does not.
36 ///
37 /// `SynchronousCall<RpcMethod>` blocks indefinitely, whereas
38 /// `SynchronousCallFor<RpcMethod>` and `SynchronousCallUntil<RpcMethod>` block
39 /// for a given timeout or until a deadline, respectively. All wrappers work
40 /// with either the standalone static RPC functions or the generated service
41 /// client member methods.
42 ///
43 /// @note Use of the SynchronousCall wrappers requires a
44 /// @cpp_class{pw::sync::TimedThreadNotification} backend.
45 ///
46 /// The following examples use the Nanopb API to make a call that blocks
47 /// indefinitely. If you'd like to include a timeout for how long the call
48 /// should block for, use the `SynchronousCallFor()` or `SynchronousCallUntil()`
49 /// variants.
50 ///
51 /// @code{.cpp}
52 ///   pw_rpc_EchoMessage request{.msg = "hello" };
53 ///   pw::rpc::SynchronousCallResult<pw_rpc_EchoMessage> result =
54 ///     pw::rpc::SynchronousCall<EchoService::Echo>(rpc_client,
55 ///                                                 channel_id,
56 ///                                                 request);
57 ///   if (result.ok()) {
58 ///     PW_LOG_INFO("%s", result.response().msg);
59 ///   }
60 /// @endcode
61 ///
62 /// Additionally, the use of a generated `Client` object is supported:
63 ///
64 /// @code{.cpp}
65 ///   pw_rpc::nanopb::EchoService::Client client(rpc_client, channel_id);
66 ///   pw_rpc_EchoMessage request{.msg = "hello" };
67 ///   pw::rpc::SynchronousCallResult<pw_rpc_EchoMessage> result =
68 ///     pw::rpc::SynchronousCall<EchoService::Echo>(client, request);
69 ///
70 ///   if (result.ok()) {
71 ///     PW_LOG_INFO("%s", result.response().msg);
72 ///   }
73 /// @endcode
74 ///
75 /// `SynchronousCall<RpcMethod>` also supports using an optional custom response
76 /// message class, `SynchronousCall<RpcMethod, Response>`. This enables the use
77 /// of response messages with variable-length fields.
78 ///
79 /// @code{.cpp}
80 ///  pw_rpc_MyMethodRequestMessage request{};
81 ///  class CustomResponse : public pw_rpc_MyMethodResponseMessage {
82 ///   public:
83 ///    CustomResponse() {
84 ///      repeated_field.SetDecoder([this](
85 ///        MyMethodResponse::StreamDecoder& decoder) {
86 ///          return decoder.ReadRepeatedField(values);
87 ///        }
88 ///    }
89 ///    pw::Vector<uint32_t, 4> values();
90 ///   };
91 ///   pw::rpc::SynchronousCallResult<CustomResponse> result =
92 ///     pw::rpc::SynchronousCall<EchoService::Echo, CustomResponse>(rpc_client,
93 ///                                                                 channel_id,
94 ///                                                                 request);
95 ///   if (result.ok()) {
96 ///     PW_LOG_INFO("%d", result.response().values[0]);
97 ///   }
98 ///  };
99 /// @endcode
100 ///
101 /// The raw API works similarly to the Nanopb API, but takes a
102 /// @cpp_type{pw::Function} and returns a @cpp_class{pw::Status}. If the RPC
103 /// completes, the @cpp_type{pw::Function} is called with the response and
104 /// returned status, and the `SynchronousCall` invocation returns
105 /// @pw_status{OK}. If the RPC fails, `SynchronousCall` returns an error.
106 ///
107 /// @code{.cpp}
108 ///   pw::Status rpc_status = pw::rpc::SynchronousCall<EchoService::Echo>(
109 ///       rpc_client, channel_id, encoded_request,
110 ///       [](pw::ConstByteSpan reply, pw::Status status) {
111 ///         PW_LOG_INFO("Received %zu bytes with status %s",
112 ///                     reply.size(),
113 ///                     status.str());
114 ///       });
115 /// @endcode
116 ///
117 /// @warning These wrappers should not be used from any context that cannot be
118 /// blocked! This method will block the calling thread until the RPC completes,
119 /// and translate the response into a `pw::rpc::SynchronousCallResult` that
120 /// contains the error type and status or the proto response.
121 namespace pw::rpc {
122 
123 /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks indefinitely
124 /// until a response is received.
125 ///
126 /// @param client The `pw::rpc::Client` to use for the call
127 /// @param channel_id The ID of the RPC channel to make the call on
128 /// @param request The proto struct to send as the request
129 template <
130     auto kRpcMethod,
131     typename Response = typename internal::MethodInfo<kRpcMethod>::Response>
SynchronousCall(Client & client,uint32_t channel_id,const typename internal::MethodInfo<kRpcMethod>::Request & request)132 SynchronousCallResult<Response> SynchronousCall(
133     Client& client,
134     uint32_t channel_id,
135     const typename internal::MethodInfo<kRpcMethod>::Request& request) {
136   return internal::StructSynchronousCall<kRpcMethod, Response>(
137       internal::CallFreeFunctionWithCustomResponse<kRpcMethod, Response>(
138           client, channel_id, request));
139 }
140 
141 /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks indefinitely
142 /// until a response is received.
143 ///
144 /// @param client The generated service client to use for the call
145 /// @param request The proto struct to send as the request
146 template <auto kRpcMethod, typename GeneratedClient>
147 SynchronousCallResult<typename internal::MethodInfo<kRpcMethod>::Response>
SynchronousCall(const GeneratedClient & client,const typename internal::MethodInfo<kRpcMethod>::Request & request)148 SynchronousCall(
149     const GeneratedClient& client,
150     const typename internal::MethodInfo<kRpcMethod>::Request& request) {
151   return internal::StructSynchronousCall<kRpcMethod>(
152       internal::CallGeneratedClient<kRpcMethod>(client, request));
153 }
154 
155 /// Invokes a unary RPC synchronously using the raw API. Blocks until a
156 /// response is received.
157 template <auto kRpcMethod>
SynchronousCall(Client & client,uint32_t channel_id,ConstByteSpan request,Function<void (ConstByteSpan,Status)> && on_completed)158 Status SynchronousCall(Client& client,
159                        uint32_t channel_id,
160                        ConstByteSpan request,
161                        Function<void(ConstByteSpan, Status)>&& on_completed) {
162   return internal::RawSynchronousCall<kRpcMethod>(
163       std::move(on_completed),
164       internal::CallFreeFunction<kRpcMethod>(client, channel_id, request));
165 }
166 
167 /// Invokes a unary RPC synchronously using the raw API. Blocks until a
168 /// response is received.
169 template <auto kRpcMethod>
SynchronousCall(const typename internal::MethodInfo<kRpcMethod>::GeneratedClient & client,ConstByteSpan request,Function<void (ConstByteSpan,Status)> && on_completed)170 Status SynchronousCall(
171     const typename internal::MethodInfo<kRpcMethod>::GeneratedClient& client,
172     ConstByteSpan request,
173     Function<void(ConstByteSpan, Status)>&& on_completed) {
174   return internal::RawSynchronousCall<kRpcMethod>(
175       std::move(on_completed),
176       internal::CallGeneratedClient<kRpcMethod>(client, request));
177 }
178 
179 /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks until a
180 /// response is received or the provided timeout passes.
181 ///
182 /// @param client The `pw::rpc::Client` to use for the call
183 /// @param channel_id The ID of the RPC channel to make the call on
184 /// @param request The proto struct to send as the request
185 /// @param timeout Duration to block for before returning with Timeout
186 template <auto kRpcMethod>
187 SynchronousCallResult<typename internal::MethodInfo<kRpcMethod>::Response>
SynchronousCallFor(Client & client,uint32_t channel_id,const typename internal::MethodInfo<kRpcMethod>::Request & request,chrono::SystemClock::duration timeout)188 SynchronousCallFor(
189     Client& client,
190     uint32_t channel_id,
191     const typename internal::MethodInfo<kRpcMethod>::Request& request,
192     chrono::SystemClock::duration timeout) {
193   return internal::StructSynchronousCall<kRpcMethod>(
194       internal::CallFreeFunction<kRpcMethod>(client, channel_id, request),
195       timeout);
196 }
197 
198 /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks until a
199 /// response is received or the provided timeout passes.
200 ///
201 /// @param client The generated service client to use for the call
202 /// @param request The proto struct to send as the request
203 /// @param timeout Duration to block for before returning with Timeout
204 template <auto kRpcMethod, typename GeneratedClient>
205 SynchronousCallResult<typename internal::MethodInfo<kRpcMethod>::Response>
SynchronousCallFor(const GeneratedClient & client,const typename internal::MethodInfo<kRpcMethod>::Request & request,chrono::SystemClock::duration timeout)206 SynchronousCallFor(
207     const GeneratedClient& client,
208     const typename internal::MethodInfo<kRpcMethod>::Request& request,
209     chrono::SystemClock::duration timeout) {
210   return internal::StructSynchronousCall<kRpcMethod>(
211       internal::CallGeneratedClient<kRpcMethod>(client, request), timeout);
212 }
213 
214 /// Invokes a unary RPC synchronously using the raw API. Blocks until a
215 /// response is received or the provided timeout passes.
216 template <auto kRpcMethod>
SynchronousCallFor(Client & client,uint32_t channel_id,ConstByteSpan request,chrono::SystemClock::duration timeout,Function<void (ConstByteSpan,Status)> && on_completed)217 Status SynchronousCallFor(
218     Client& client,
219     uint32_t channel_id,
220     ConstByteSpan request,
221     chrono::SystemClock::duration timeout,
222     Function<void(ConstByteSpan, Status)>&& on_completed) {
223   return internal::RawSynchronousCall<kRpcMethod>(
224       std::move(on_completed),
225       internal::CallFreeFunction<kRpcMethod>(client, channel_id, request),
226       timeout);
227 }
228 
229 /// Invokes a unary RPC synchronously using the raw API. Blocks until a
230 /// response is received or the provided timeout passes.
231 template <auto kRpcMethod>
SynchronousCallFor(const typename internal::MethodInfo<kRpcMethod>::GeneratedClient & client,ConstByteSpan request,chrono::SystemClock::duration timeout,Function<void (ConstByteSpan,Status)> && on_completed)232 Status SynchronousCallFor(
233     const typename internal::MethodInfo<kRpcMethod>::GeneratedClient& client,
234     ConstByteSpan request,
235     chrono::SystemClock::duration timeout,
236     Function<void(ConstByteSpan, Status)>&& on_completed) {
237   return internal::RawSynchronousCall<kRpcMethod>(
238       std::move(on_completed),
239       internal::CallGeneratedClient<kRpcMethod>(client, request),
240       timeout);
241 }
242 
243 /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks until a
244 /// response is received or the provided deadline arrives.
245 ///
246 /// @param client The `pw::rpc::Client` to use for the call
247 /// @param channel_id The ID of the RPC channel to make the call on
248 /// @param request The proto struct to send as the request
249 /// @param deadline Timepoint to block until before returning with Timeout
250 template <auto kRpcMethod>
251 SynchronousCallResult<typename internal::MethodInfo<kRpcMethod>::Response>
SynchronousCallUntil(Client & client,uint32_t channel_id,const typename internal::MethodInfo<kRpcMethod>::Request & request,chrono::SystemClock::time_point deadline)252 SynchronousCallUntil(
253     Client& client,
254     uint32_t channel_id,
255     const typename internal::MethodInfo<kRpcMethod>::Request& request,
256     chrono::SystemClock::time_point deadline) {
257   return internal::StructSynchronousCall<kRpcMethod>(
258       internal::CallFreeFunction<kRpcMethod>(client, channel_id, request),
259       deadline);
260 }
261 
262 /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks until a
263 /// response is received or the provided deadline arrives.
264 ///
265 /// @param client The generated service client to use for the call
266 /// @param request The proto struct to send as the request
267 /// @param deadline Timepoint to block until before returning with Timeout
268 template <auto kRpcMethod>
269 SynchronousCallResult<typename internal::MethodInfo<kRpcMethod>::Response>
SynchronousCallUntil(const typename internal::MethodInfo<kRpcMethod>::GeneratedClient & client,const typename internal::MethodInfo<kRpcMethod>::Request & request,chrono::SystemClock::time_point deadline)270 SynchronousCallUntil(
271     const typename internal::MethodInfo<kRpcMethod>::GeneratedClient& client,
272     const typename internal::MethodInfo<kRpcMethod>::Request& request,
273     chrono::SystemClock::time_point deadline) {
274   return internal::StructSynchronousCall<kRpcMethod>(
275       internal::CallGeneratedClient<kRpcMethod>(client, request), deadline);
276 }
277 
278 /// Invokes a unary RPC synchronously using the raw API. Blocks until a
279 /// response is received or the provided deadline arrives.
280 template <auto kRpcMethod>
SynchronousCallUntil(Client & client,uint32_t channel_id,ConstByteSpan request,chrono::SystemClock::time_point deadline,Function<void (ConstByteSpan,Status)> && on_completed)281 Status SynchronousCallUntil(
282     Client& client,
283     uint32_t channel_id,
284     ConstByteSpan request,
285     chrono::SystemClock::time_point deadline,
286     Function<void(ConstByteSpan, Status)>&& on_completed) {
287   return internal::RawSynchronousCall<kRpcMethod>(
288       std::move(on_completed),
289       internal::CallFreeFunction<kRpcMethod>(client, channel_id, request),
290       deadline);
291 }
292 
293 /// Invokes a unary RPC synchronously using the raw API. Blocks until a
294 /// response is received or the provided deadline arrives.
295 template <auto kRpcMethod>
SynchronousCallUntil(const typename internal::MethodInfo<kRpcMethod>::GeneratedClient & client,ConstByteSpan request,chrono::SystemClock::time_point deadline,Function<void (ConstByteSpan,Status)> && on_completed)296 Status SynchronousCallUntil(
297     const typename internal::MethodInfo<kRpcMethod>::GeneratedClient& client,
298     ConstByteSpan request,
299     chrono::SystemClock::time_point deadline,
300     Function<void(ConstByteSpan, Status)>&& on_completed) {
301   return internal::RawSynchronousCall<kRpcMethod>(
302       std::move(on_completed),
303       internal::CallGeneratedClient<kRpcMethod>(client, request),
304       deadline);
305 }
306 
307 }  // namespace pw::rpc
308