| // Copyright 2023 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #pragma once |
| |
| #include <utility> |
| |
| #include "pw_chrono/system_clock.h" |
| #include "pw_rpc/client.h" |
| #include "pw_rpc/internal/method_info.h" |
| #include "pw_rpc/internal/synchronous_call_impl.h" |
| #include "pw_rpc/synchronous_call_result.h" |
| |
| /// @file pw_rpc/synchronous_call.h |
| /// |
| /// `pw_rpc` provides wrappers that convert the asynchronous client API to a |
| /// synchronous API. The `SynchronousCall<RpcMethod>` functions wrap the |
| /// asynchronous client RPC call with a timed thread notification and returns |
| /// once a result is known or a timeout has occurred. Only unary methods are |
| /// supported. |
| /// |
| /// The Nanopb and pwpb APIs return a `SynchronousCallResult<Response>` object, |
| /// which can be queried to determine whether any error scenarios occurred and, |
| /// if not, access the response. The raw API executes a function when the call |
| /// completes or returns a `pw::Status` if it does not. |
| /// |
| /// `SynchronousCall<RpcMethod>` blocks indefinitely, whereas |
| /// `SynchronousCallFor<RpcMethod>` and `SynchronousCallUntil<RpcMethod>` block |
| /// for a given timeout or until a deadline, respectively. All wrappers work |
| /// with either the standalone static RPC functions or the generated service |
| /// client member methods. |
| /// |
| /// @note Use of the SynchronousCall wrappers requires a |
| /// @cpp_class{pw::sync::TimedThreadNotification} backend. |
| /// |
| /// The following examples use the Nanopb API to make a call that blocks |
| /// indefinitely. If you'd like to include a timeout for how long the call |
| /// should block for, use the `SynchronousCallFor()` or `SynchronousCallUntil()` |
| /// variants. |
| /// |
| /// @code{.cpp} |
| /// pw_rpc_EchoMessage request{.msg = "hello" }; |
| /// pw::rpc::SynchronousCallResult<pw_rpc_EchoMessage> result = |
| /// pw::rpc::SynchronousCall<EchoService::Echo>(rpc_client, |
| /// channel_id, |
| /// request); |
| /// if (result.ok()) { |
| /// PW_LOG_INFO("%s", result.response().msg); |
| /// } |
| /// @endcode |
| /// |
| /// Additionally, the use of a generated `Client` object is supported: |
| /// |
| /// @code{.cpp} |
| /// pw_rpc::nanopb::EchoService::Client client(rpc_client, channel_id); |
| /// pw_rpc_EchoMessage request{.msg = "hello" }; |
| /// pw::rpc::SynchronousCallResult<pw_rpc_EchoMessage> result = |
| /// pw::rpc::SynchronousCall<EchoService::Echo>(client, request); |
| /// |
| /// if (result.ok()) { |
| /// PW_LOG_INFO("%s", result.response().msg); |
| /// } |
| /// @endcode |
| /// |
| /// `SynchronousCall<RpcMethod>` also supports using an optional custom response |
| /// message class, `SynchronousCall<RpcMethod, Response>`. This enables the use |
| /// of response messages with variable-length fields. |
| /// |
| /// @code{.cpp} |
| /// pw_rpc_MyMethodRequestMessage request{}; |
| /// class CustomResponse : public pw_rpc_MyMethodResponseMessage { |
| /// public: |
| /// CustomResponse() { |
| /// repeated_field.SetDecoder([this]( |
| /// MyMethodResponse::StreamDecoder& decoder) { |
| /// return decoder.ReadRepeatedField(values); |
| /// } |
| /// } |
| /// pw::Vector<uint32_t, 4> values(); |
| /// }; |
| /// pw::rpc::SynchronousCallResult<CustomResponse> result = |
| /// pw::rpc::SynchronousCall<EchoService::Echo, CustomResponse>(rpc_client, |
| /// channel_id, |
| /// request); |
| /// if (result.ok()) { |
| /// PW_LOG_INFO("%d", result.response().values[0]); |
| /// } |
| /// }; |
| /// @endcode |
| /// |
| /// The raw API works similarly to the Nanopb API, but takes a |
| /// @cpp_type{pw::Function} and returns a @cpp_class{pw::Status}. If the RPC |
| /// completes, the @cpp_type{pw::Function} is called with the response and |
| /// returned status, and the `SynchronousCall` invocation returns |
| /// @pw_status{OK}. If the RPC fails, `SynchronousCall` returns an error. |
| /// |
| /// @code{.cpp} |
| /// pw::Status rpc_status = pw::rpc::SynchronousCall<EchoService::Echo>( |
| /// rpc_client, channel_id, encoded_request, |
| /// [](pw::ConstByteSpan reply, pw::Status status) { |
| /// PW_LOG_INFO("Received %zu bytes with status %s", |
| /// reply.size(), |
| /// status.str()); |
| /// }); |
| /// @endcode |
| /// |
| /// @warning These wrappers should not be used from any context that cannot be |
| /// blocked! This method will block the calling thread until the RPC completes, |
| /// and translate the response into a `pw::rpc::SynchronousCallResult` that |
| /// contains the error type and status or the proto response. |
| namespace pw::rpc { |
| |
| /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks indefinitely |
| /// until a response is received. |
| /// |
| /// @param client The `pw::rpc::Client` to use for the call |
| /// @param channel_id The ID of the RPC channel to make the call on |
| /// @param request The proto struct to send as the request |
| template < |
| auto kRpcMethod, |
| typename Response = typename internal::MethodInfo<kRpcMethod>::Response> |
| SynchronousCallResult<Response> SynchronousCall( |
| Client& client, |
| uint32_t channel_id, |
| const typename internal::MethodInfo<kRpcMethod>::Request& request) { |
| return internal::StructSynchronousCall<kRpcMethod, Response>( |
| internal::CallFreeFunctionWithCustomResponse<kRpcMethod, Response>( |
| client, channel_id, request)); |
| } |
| |
| /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks indefinitely |
| /// until a response is received. |
| /// |
| /// @param client The generated service client to use for the call |
| /// @param request The proto struct to send as the request |
| template <auto kRpcMethod, typename GeneratedClient> |
| SynchronousCallResult<typename internal::MethodInfo<kRpcMethod>::Response> |
| SynchronousCall( |
| const GeneratedClient& client, |
| const typename internal::MethodInfo<kRpcMethod>::Request& request) { |
| return internal::StructSynchronousCall<kRpcMethod>( |
| internal::CallGeneratedClient<kRpcMethod>(client, request)); |
| } |
| |
| /// Invokes a unary RPC synchronously using the raw API. Blocks until a |
| /// response is received. |
| template <auto kRpcMethod> |
| Status SynchronousCall(Client& client, |
| uint32_t channel_id, |
| ConstByteSpan request, |
| Function<void(ConstByteSpan, Status)>&& on_completed) { |
| return internal::RawSynchronousCall<kRpcMethod>( |
| std::move(on_completed), |
| internal::CallFreeFunction<kRpcMethod>(client, channel_id, request)); |
| } |
| |
| /// Invokes a unary RPC synchronously using the raw API. Blocks until a |
| /// response is received. |
| template <auto kRpcMethod> |
| Status SynchronousCall( |
| const typename internal::MethodInfo<kRpcMethod>::GeneratedClient& client, |
| ConstByteSpan request, |
| Function<void(ConstByteSpan, Status)>&& on_completed) { |
| return internal::RawSynchronousCall<kRpcMethod>( |
| std::move(on_completed), |
| internal::CallGeneratedClient<kRpcMethod>(client, request)); |
| } |
| |
| /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks until a |
| /// response is received or the provided timeout passes. |
| /// |
| /// @param client The `pw::rpc::Client` to use for the call |
| /// @param channel_id The ID of the RPC channel to make the call on |
| /// @param request The proto struct to send as the request |
| /// @param timeout Duration to block for before returning with Timeout |
| template <auto kRpcMethod> |
| SynchronousCallResult<typename internal::MethodInfo<kRpcMethod>::Response> |
| SynchronousCallFor( |
| Client& client, |
| uint32_t channel_id, |
| const typename internal::MethodInfo<kRpcMethod>::Request& request, |
| chrono::SystemClock::duration timeout) { |
| return internal::StructSynchronousCall<kRpcMethod>( |
| internal::CallFreeFunction<kRpcMethod>(client, channel_id, request), |
| timeout); |
| } |
| |
| /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks until a |
| /// response is received or the provided timeout passes. |
| /// |
| /// @param client The generated service client to use for the call |
| /// @param request The proto struct to send as the request |
| /// @param timeout Duration to block for before returning with Timeout |
| template <auto kRpcMethod, typename GeneratedClient> |
| SynchronousCallResult<typename internal::MethodInfo<kRpcMethod>::Response> |
| SynchronousCallFor( |
| const GeneratedClient& client, |
| const typename internal::MethodInfo<kRpcMethod>::Request& request, |
| chrono::SystemClock::duration timeout) { |
| return internal::StructSynchronousCall<kRpcMethod>( |
| internal::CallGeneratedClient<kRpcMethod>(client, request), timeout); |
| } |
| |
| /// Invokes a unary RPC synchronously using the raw API. Blocks until a |
| /// response is received or the provided timeout passes. |
| template <auto kRpcMethod> |
| Status SynchronousCallFor( |
| Client& client, |
| uint32_t channel_id, |
| ConstByteSpan request, |
| chrono::SystemClock::duration timeout, |
| Function<void(ConstByteSpan, Status)>&& on_completed) { |
| return internal::RawSynchronousCall<kRpcMethod>( |
| std::move(on_completed), |
| internal::CallFreeFunction<kRpcMethod>(client, channel_id, request), |
| timeout); |
| } |
| |
| /// Invokes a unary RPC synchronously using the raw API. Blocks until a |
| /// response is received or the provided timeout passes. |
| template <auto kRpcMethod> |
| Status SynchronousCallFor( |
| const typename internal::MethodInfo<kRpcMethod>::GeneratedClient& client, |
| ConstByteSpan request, |
| chrono::SystemClock::duration timeout, |
| Function<void(ConstByteSpan, Status)>&& on_completed) { |
| return internal::RawSynchronousCall<kRpcMethod>( |
| std::move(on_completed), |
| internal::CallGeneratedClient<kRpcMethod>(client, request), |
| timeout); |
| } |
| |
| /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks until a |
| /// response is received or the provided deadline arrives. |
| /// |
| /// @param client The `pw::rpc::Client` to use for the call |
| /// @param channel_id The ID of the RPC channel to make the call on |
| /// @param request The proto struct to send as the request |
| /// @param deadline Timepoint to block until before returning with Timeout |
| template <auto kRpcMethod> |
| 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) { |
| return internal::StructSynchronousCall<kRpcMethod>( |
| internal::CallFreeFunction<kRpcMethod>(client, channel_id, request), |
| deadline); |
| } |
| |
| /// Invokes a unary RPC synchronously using Nanopb or pwpb. Blocks until a |
| /// response is received or the provided deadline arrives. |
| /// |
| /// @param client The generated service client to use for the call |
| /// @param request The proto struct to send as the request |
| /// @param deadline Timepoint to block until before returning with Timeout |
| template <auto kRpcMethod> |
| 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) { |
| return internal::StructSynchronousCall<kRpcMethod>( |
| internal::CallGeneratedClient<kRpcMethod>(client, request), deadline); |
| } |
| |
| /// Invokes a unary RPC synchronously using the raw API. Blocks until a |
| /// response is received or the provided deadline arrives. |
| template <auto kRpcMethod> |
| Status SynchronousCallUntil( |
| Client& client, |
| uint32_t channel_id, |
| ConstByteSpan request, |
| chrono::SystemClock::time_point deadline, |
| Function<void(ConstByteSpan, Status)>&& on_completed) { |
| return internal::RawSynchronousCall<kRpcMethod>( |
| std::move(on_completed), |
| internal::CallFreeFunction<kRpcMethod>(client, channel_id, request), |
| deadline); |
| } |
| |
| /// Invokes a unary RPC synchronously using the raw API. Blocks until a |
| /// response is received or the provided deadline arrives. |
| template <auto kRpcMethod> |
| Status SynchronousCallUntil( |
| const typename internal::MethodInfo<kRpcMethod>::GeneratedClient& client, |
| ConstByteSpan request, |
| chrono::SystemClock::time_point deadline, |
| Function<void(ConstByteSpan, Status)>&& on_completed) { |
| return internal::RawSynchronousCall<kRpcMethod>( |
| std::move(on_completed), |
| internal::CallGeneratedClient<kRpcMethod>(client, request), |
| deadline); |
| } |
| |
| } // namespace pw::rpc |