xref: /aosp_15_r20/external/pigweed/pw_rpc/cpp.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_rpc-cpp:
2
3=====================
4C++ server and client
5=====================
6.. pigweed-module-subpage::
7   :name: pw_rpc
8
9This page provides further guidance on how to use the C++ server
10and client libraries.
11
12----------
13RPC server
14----------
15Declare an instance of ``rpc::Server`` and register services with it.
16
17Size report
18===========
19The following size report showcases the memory usage of the core RPC server. It
20is configured with a single channel using a basic transport interface that
21directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte
22packet buffer, which comprises the plurality of the example's RAM usage. This is
23not a suitable transport for an actual product; a real implementation would have
24additional overhead proportional to the complexity of the transport.
25
26.. include:: server_size
27
28RPC server implementation
29=========================
30
31The Method class
32----------------
33The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method``
34serves as the bridge between the ``pw_rpc`` server library and the user-defined
35RPC functions. Each supported protobuf implementation extends ``Method`` to
36implement its request and response proto handling. The ``pw_rpc`` server
37calls into the ``Method`` implementation through the base class's ``Invoke``
38function.
39
40``Method`` implementations store metadata about each method, including a
41function pointer to the user-defined method implementation. They also provide
42``static constexpr`` functions for creating each type of method. ``Method``
43implementations must satisfy the ``MethodImplTester`` test class in
44``pw_rpc/internal/method_impl_tester.h``.
45
46See ``pw_rpc/internal/method.h`` for more details about ``Method``.
47
48Packet flow
49-----------
50
51Requests
52^^^^^^^^
53
54.. mermaid::
55   :alt: Request Packet Flow
56
57   flowchart LR
58       packets[Packets]
59
60       subgraph pw_rpc [pw_rpc Library]
61           direction TB
62           internalMethod[[internal::Method]]
63           Server --> Service --> internalMethod
64       end
65
66       packets --> Server
67
68       generatedServices{{generated services}}
69       userDefinedRPCs(user-defined RPCs)
70
71       generatedServices --> userDefinedRPCs
72       internalMethod --> generatedServices
73
74Responses
75^^^^^^^^^
76
77.. mermaid::
78   :alt: Request Packet Flow
79
80   flowchart LR
81       generatedServices{{generated services}}
82       userDefinedRPCs(user-defined RPCs)
83
84       subgraph pw_rpc [pw_rpc Library]
85           direction TB
86           internalMethod[[internal::Method]]
87           internalMethod --> Server --> Channel
88       end
89
90       packets[Packets]
91       Channel --> packets
92
93       userDefinedRPCs --> generatedServices
94       generatedServices --> internalMethod
95
96----------
97RPC client
98----------
99The RPC client is used to send requests to a server and manages the contexts of
100ongoing RPCs.
101
102Setting up a client
103===================
104The ``pw::rpc::Client`` class is instantiated with a list of channels that it
105uses to communicate. These channels can be shared with a server, but multiple
106clients cannot use the same channels.
107
108To send incoming RPC packets from the transport layer to be processed by a
109client, the client's ``ProcessPacket`` function is called with the packet data.
110
111.. code-block:: c++
112
113   #include "pw_rpc/client.h"
114
115   namespace {
116
117   pw::rpc::Channel my_channels[] = {
118       pw::rpc::Channel::Create<1>(&my_channel_output)};
119   pw::rpc::Client my_client(my_channels);
120
121   }  // namespace
122
123   // Called when the transport layer receives an RPC packet.
124   void ProcessRpcPacket(ConstByteSpan packet) {
125     my_client.ProcessPacket(packet);
126   }
127
128Note that client processing such as callbacks will be invoked within
129the body of ``ProcessPacket``.
130
131If certain packets need to be filtered out, or if certain client processing
132needs to be invoked from a specific thread or context, the ``PacketMeta`` class
133can be used to determine which service or channel a packet is targeting. After
134filtering, ``ProcessPacket`` can be called from the appropriate environment.
135
136.. _module-pw_rpc-making-calls:
137
138Making RPC calls
139================
140RPC calls are not made directly through the client, but using one of its
141registered channels instead. A service client class is generated from a .proto
142file for each selected protobuf library, which is then used to send RPC requests
143through a given channel. The API for this depends on the protobuf library;
144please refer to the
145:ref:`appropriate documentation <module-pw_rpc-libraries>`. Multiple
146service client implementations can exist simulatenously and share the same
147``Client`` class.
148
149When a call is made, a call object is returned to the caller. This object tracks
150the ongoing RPC call, and can be used to manage it. An RPC call is only active
151as long as its call object is alive.
152
153.. tip::
154
155   Use ``std::move`` when passing around call objects to keep RPCs alive.
156
157Example
158-------
159.. code-block:: c++
160
161   #include "pw_rpc/echo_service_nanopb.h"
162
163   namespace {
164   // Generated clients are namespaced with their proto library.
165   using EchoClient = pw_rpc::nanopb::EchoService::Client;
166
167   // RPC channel ID on which to make client calls. RPC calls cannot be made on
168   // channel 0 (Channel::kUnassignedChannelId).
169   constexpr uint32_t kDefaultChannelId = 1;
170
171   pw::rpc::NanopbUnaryReceiver<pw_rpc_EchoMessage> echo_call;
172
173   // Callback invoked when a response is received. This is called synchronously
174   // from Client::ProcessPacket.
175   void EchoResponse(const pw_rpc_EchoMessage& response,
176                     pw::Status status) {
177     if (status.ok()) {
178       PW_LOG_INFO("Received echo response: %s", response.msg);
179     } else {
180       PW_LOG_ERROR("Echo failed with status %d",
181                    static_cast<int>(status.code()));
182     }
183   }
184
185   }  // namespace
186
187   void CallEcho(const char* message) {
188     // Create a client to call the EchoService.
189     EchoClient echo_client(my_rpc_client, kDefaultChannelId);
190
191     pw_rpc_EchoMessage request{};
192     pw::string::Copy(message, request.msg);
193
194     // By assigning the returned call to the global echo_call, the RPC
195     // call is kept alive until it completes. When a response is received, it
196     // will be logged by the handler function and the call will complete.
197     echo_call = echo_client.Echo(request, EchoResponse);
198     if (!echo_call.active()) {
199       // The RPC call was not sent. This could occur due to, for example, an
200       // invalid channel ID. Handle if necessary.
201     }
202   }
203
204--------
205Channels
206--------
207``pw_rpc`` sends all of its packets over channels. These are logical,
208application-layer routes used to tell the RPC system where a packet should go.
209
210Channels over a client-server connection must all have a unique ID, which can be
211assigned statically at compile time or dynamically.
212
213.. code-block:: cpp
214
215   // Creating a channel with the static ID 3.
216   pw::rpc::Channel static_channel = pw::rpc::Channel::Create<3>(&output);
217
218   // Grouping channel IDs within an enum can lead to clearer code.
219   enum ChannelId {
220     kUartChannel = 1,
221     kSpiChannel = 2,
222   };
223
224   // Creating a channel with a static ID defined within an enum.
225   pw::rpc::Channel another_static_channel =
226       pw::rpc::Channel::Create<ChannelId::kUartChannel>(&output);
227
228   // Creating a channel with a dynamic ID (note that no output is provided; it
229   // will be set when the channel is used.
230   pw::rpc::Channel dynamic_channel;
231
232Sometimes, the ID and output of a channel are not known at compile time as they
233depend on information stored on the physical device. To support this use case, a
234dynamically-assignable channel can be configured once at runtime with an ID and
235output.
236
237.. code-block:: cpp
238
239   // Create a dynamic channel without a compile-time ID or output.
240   pw::rpc::Channel dynamic_channel;
241
242   void Init() {
243     // Called during boot to pull the channel configuration from the system.
244     dynamic_channel.Configure(GetChannelId(), some_output);
245   }
246
247Adding and removing channels
248============================
249New channels may be registered with the ``OpenChannel`` function. If dynamic
250allocation is enabled (:c:macro:`PW_RPC_DYNAMIC_ALLOCATION` is 1), any number of
251channels may be registered. If dynamic allocation is disabled, new channels may
252only be registered if there are availale channel slots in the span provided to
253the RPC endpoint at construction.
254
255A channel may be closed and unregistered with an endpoint by calling
256``ChannelClose`` on the endpoint with the corresponding channel ID.  This
257will terminate any pending calls and call their ``on_error`` callback
258with the ``ABORTED`` status.
259
260.. code-block:: cpp
261
262   // When a channel is closed, any pending calls will receive
263   // on_error callbacks with ABORTED status.
264   client->CloseChannel(1);
265
266.. _module-pw_rpc-remap:
267
268Remapping channels
269==================
270Some pw_rpc deployments may find it helpful to remap channel IDs in RPC packets.
271This can remove the need for globally known channel IDs. Clients can use a
272generic channel ID. The server remaps the generic channel ID to an ID associated
273with the transport the client is using.
274
275.. cpp:namespace-push:: pw::rpc
276
277.. doxygengroup:: pw_rpc_channel_functions
278   :content-only:
279
280.. cpp:namespace-pop::
281
282A future revision of the pw_rpc protocol will remove the need for global channel
283IDs without requiring remapping.
284
285Example deployment
286==================
287This section describes a hypothetical pw_rpc deployment that supports arbitrary
288pw_rpc clients with one pw_rpc server. Note that this assumes that the
289underlying transport provides some sort of addressing that the server-side can
290associate with a channel ID.
291
292- A pw_rpc server is running on one core. A variable number of pw_rpc clients
293  need to call RPCs on the server from a different core.
294- The client core opens a socket (or similar feature) to connect to the server
295  core.
296- The server core detects the inbound connection and allocates a new channel ID.
297  It creates a new channel by calling :cpp:func:`pw::rpc::Server::OpenChannel`
298  with the channel ID and a :cpp:class:`pw::rpc::ChannelOutput` associated with
299  the new connection.
300- The server maintains a mapping between channel IDs and pw_rpc client
301  connections.
302- On the client core, pw_rpc clients all use the same channel ID (e.g.  ``1``).
303- As packets arrive from pw_rpc client connections, the server-side code calls
304  :cpp:func:`pw::rpc::ChangeEncodedChannelId` on the encoded packet to replace
305  the generic channel ID (``1``) with the server-side channel ID allocated when
306  the client connected. The encoded packet is then passed to
307  :cpp:func:`pw::rpc::Server::ProcessPacket`.
308- When the server sends pw_rpc packets, the :cpp:class:`pw::rpc::ChannelOutput`
309  calls :cpp:func:`pw::rpc::ChangeEncodedChannelId` to set the channel ID back
310  to the generic ``1``.
311
312------------------------------
313C++ payload sizing limitations
314------------------------------
315The individual size of each sent RPC request or response is limited by
316``pw_rpc``'s ``PW_RPC_ENCODING_BUFFER_SIZE_BYTES`` configuration option when
317using Pigweed's C++ implementation. While multiple RPC messages can be enqueued
318(as permitted by the underlying transport), if a single individual sent message
319exceeds the limitations of the statically allocated encode buffer, the packet
320will fail to encode and be dropped.
321
322This applies to all C++ RPC service implementations (nanopb, raw, and pwpb),
323so it's important to ensure request and response message sizes do not exceed
324this limitation.
325
326As ``pw_rpc`` has some additional encoding overhead, a helper,
327``pw::rpc::MaxSafePayloadSize()`` is provided to expose the practical max RPC
328message payload size.
329
330.. code-block:: cpp
331
332   #include "pw_file/file.raw_rpc.pb.h"
333   #include "pw_rpc/channel.h"
334
335   namespace pw::file {
336
337   class FileSystemService : public pw_rpc::raw::FileSystem::Service<FileSystemService> {
338    public:
339     void List(ConstByteSpan request, RawServerWriter& writer);
340
341    private:
342     // Allocate a buffer for building proto responses.
343     static constexpr size_t kEncodeBufferSize = pw::rpc::MaxSafePayloadSize();
344     std::array<std::byte, kEncodeBufferSize> encode_buffer_;
345   };
346
347   }  // namespace pw::file
348
349------------
350Call objects
351------------
352An RPC call is represented by a call object. Server and client calls use the
353same base call class in C++, but the public API is different depending on the
354type of call and whether it is being used by the server or client. See
355:ref:`module-pw_rpc-design-lifecycle`.
356
357The public call types are as follows:
358
359.. list-table::
360   :header-rows: 1
361
362   * - RPC Type
363     - Server call
364     - Client call
365   * - Unary
366     - ``(Raw|Nanopb|Pwpb)UnaryResponder``
367     - ``(Raw|Nanopb|Pwpb)UnaryReceiver``
368   * - Server streaming
369     - ``(Raw|Nanopb|Pwpb)ServerWriter``
370     - ``(Raw|Nanopb|Pwpb)ClientReader``
371   * - Client streaming
372     - ``(Raw|Nanopb|Pwpb)ServerReader``
373     - ``(Raw|Nanopb|Pwpb)ClientWriter``
374   * - Bidirectional streaming
375     - ``(Raw|Nanopb|Pwpb)ServerReaderWriter``
376     - ``(Raw|Nanopb|Pwpb)ClientReaderWriter``
377
378Client call API
379===============
380Client call objects provide a few common methods.
381
382.. cpp:class:: pw::rpc::ClientCallType
383
384   The ``ClientCallType`` will be one of the following types:
385
386   - ``(Raw|Nanopb|Pwpb)UnaryReceiver`` for unary
387   - ``(Raw|Nanopb|Pwpb)ClientReader`` for server streaming
388   - ``(Raw|Nanopb|Pwpb)ClientWriter`` for client streaming
389   - ``(Raw|Nanopb|Pwpb)ClientReaderWriter`` for bidirectional streaming
390
391   .. cpp:function:: bool active() const
392
393      Returns true if the call is active.
394
395   .. cpp:function:: uint32_t channel_id() const
396
397      Returns the channel ID of this call, which is 0 if the call is inactive.
398
399   .. cpp:function:: uint32_t id() const
400
401      Returns the call ID, a unique identifier for this call.
402
403   .. cpp:function:: void Write(RequestType)
404
405      Only available on client and bidirectional streaming calls. Sends a stream
406      request. Returns:
407
408      - ``OK`` - the request was successfully sent
409      - ``FAILED_PRECONDITION`` - the writer is closed
410      - ``INTERNAL`` - pw_rpc was unable to encode message; does not apply to
411        raw calls
412      - other errors - the :cpp:class:`ChannelOutput` failed to send the packet;
413        the error codes are determined by the :cpp:class:`ChannelOutput`
414        implementation
415
416   .. cpp:function:: void WriteCallback(Function<StatusWithSize(ByteSpan)>)
417
418      Raw RPC only. Invokes the provided callback with the available RPC payload
419      buffer, allowing payloads to be encoded directly into it. Sends a stream
420      packet with the payload if the callback is successful.
421
422      The buffer provided to the callback is only valid for the duration of the
423      callback. The callback should return an OK status with the size of the
424      encoded payload on success, or an error status on failure.
425
426   .. cpp:function:: pw::Status RequestCompletion()
427
428      Notifies the server that client has requested for call completion. On
429      client and bidirectional streaming calls no further client stream messages
430      will be sent.
431
432   .. cpp:function:: pw::Status Cancel()
433
434      Cancels this RPC. Closes the call and sends a ``CANCELLED`` error to the
435      server. Return statuses are the same as :cpp:func:`Write`.
436
437   .. cpp:function:: void Abandon()
438
439      Closes this RPC locally. Sends a ``CLIENT_REQUEST_COMPLETION``, but no cancellation
440      packet. Future packets for this RPC are dropped, and the client sends a
441      ``FAILED_PRECONDITION`` error in response because the call is not active.
442
443   .. cpp:function:: void CloseAndWaitForCallbacks()
444
445      Abandons this RPC and additionally blocks on completion of any running callbacks.
446
447   .. cpp:function:: void set_on_completed(pw::Function<void(ResponseTypeIfUnaryOnly, pw::Status)>)
448
449      Sets the callback that is called when the RPC completes normally. The
450      signature depends on whether the call has a unary or stream response.
451
452   .. cpp:function:: void set_on_error(pw::Function<void(pw::Status)>)
453
454      Sets the callback that is called when the RPC is terminated due to an error.
455
456   .. cpp:function:: void set_on_next(pw::Function<void(ResponseType)>)
457
458      Only available on server and bidirectional streaming calls. Sets the callback
459      that is called for each stream response.
460
461Callbacks
462=========
463The C++ call objects allow users to set callbacks that are invoked when RPC
464:ref:`events <module-pw_rpc-design-events>` occur.
465
466.. list-table::
467   :header-rows: 1
468
469   * - Name
470     - Stream signature
471     - Non-stream signature
472     - Server
473     - Client
474   * - ``on_error``
475     - ``void(pw::Status)``
476     - ``void(pw::Status)``
477     - ✅
478     - ✅
479   * - ``on_next``
480     - n/a
481     - ``void(const PayloadType&)``
482     - ✅
483     - ✅
484   * - ``on_completed``
485     - ``void(pw::Status)``
486     - ``void(const PayloadType&, pw::Status)``
487     -
488     - ✅
489   * - ``on_client_requested_completion``
490     - ``void()``
491     - n/a
492     - ✅ (:c:macro:`optional <PW_RPC_COMPLETION_REQUEST_CALLBACK>`)
493     -
494
495Limitations and restrictions
496----------------------------
497RPC callbacks are free to perform most actions, including invoking new RPCs or
498cancelling pending calls. However, the C++ implementation imposes some
499limitations and restrictions that must be observed.
500
501Destructors & moves wait for callbacks to complete
502^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
503* Callbacks must not destroy their call object. Attempting to do so will result
504  in deadlock.
505* Other threads may destroy a call while its callback is running, but that
506  thread will block until all callbacks complete.
507* Callbacks must not move their call object if it the call is still active. They
508  may move their call object after it has terminated. Callbacks may move a
509  different call into their call object, since moving closes the destination
510  call.
511* Other threads may move a call object while it has a callback running, but they
512  will block until the callback completes if the call is still active.
513
514.. warning::
515
516   Deadlocks or crashes occur if a callback:
517
518   - attempts to destroy its call object
519   - attempts to move its call object while the call is still active
520   - never returns
521
522   If ``pw_rpc`` a callback violates these restrictions, a crash may occur,
523   depending on the value of :c:macro:`PW_RPC_CALLBACK_TIMEOUT_TICKS`. These
524   crashes have a message like the following:
525
526   .. code-block:: text
527
528      A callback for RPC 1:cc0f6de0/31e616ce has not finished after 10000 ticks.
529      This may indicate that an RPC callback attempted to destroy or move its own
530      call object, which is not permitted. Fix this condition or change the value of
531      PW_RPC_CALLBACK_TIMEOUT_TICKS to avoid this crash.
532
533      See https://pigweed.dev/pw_rpc#destructors-moves-wait-for-callbacks-to-complete
534      for details.
535
536Only one thread at a time may execute ``on_next``
537^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
538Only one thread may execute the ``on_next`` callback for a specific service
539method at a time. If a second thread calls ``ProcessPacket()`` with a stream
540packet before the ``on_next`` callback for the previous packet completes, the
541second packet will be dropped. The RPC endpoint logs a warning when this occurs.
542
543Example warning for a dropped stream packet:
544
545.. code-block:: text
546
547   WRN  Received stream packet for 1:cc0f6de0/31e616ce before the callback for
548        a previous packet completed! This packet will be dropped. This can be
549        avoided by handling packets for a particular RPC on only one thread.
550
551-----------------------
552RPC calls introspection
553-----------------------
554``pw_rpc`` provides ``pw_rpc/method_info.h`` header that allows to obtain
555information about the generated RPC method in compile time.
556
557For now it provides only two types: ``MethodRequestType<RpcMethod>`` and
558``MethodResponseType<RpcMethod>``. They are aliases to the types that are used
559as a request and response respectively for the given RpcMethod.
560
561Example
562=======
563We have an RPC service ``SpecialService`` with ``MyMethod`` method:
564
565.. code-block:: protobuf
566
567   package some.package;
568   service SpecialService {
569     rpc MyMethod(MyMethodRequest) returns (MyMethodResponse) {}
570   }
571
572We also have a templated Storage type alias:
573
574.. code-block:: c++
575
576   template <auto kMethod>
577   using Storage =
578      std::pair<MethodRequestType<kMethod>, MethodResponseType<kMethod>>;
579
580``Storage<some::package::pw_rpc::pwpb::SpecialService::MyMethod>`` will
581instantiate as:
582
583.. code-block:: c++
584
585   std::pair<some::package::MyMethodRequest::Message,
586             some::package::MyMethodResponse::Message>;
587
588.. note::
589
590   Only nanopb and pw_protobuf have real types as
591   ``MethodRequestType<RpcMethod>``/``MethodResponseType<RpcMethod>``. Raw has
592   them both set as ``void``. In reality, they are ``pw::ConstByteSpan``. Any
593   helper/trait that wants to use this types for raw methods should do a custom
594   implementation that copies the bytes under the span instead of copying just
595   the span.
596
597.. _module-pw_rpc-client-sync-call-wrappers:
598
599--------------------------------
600Client synchronous call wrappers
601--------------------------------
602.. doxygenfile:: pw_rpc/synchronous_call.h
603   :sections: detaileddescription
604
605Example
606=======
607.. code-block:: c++
608
609   #include "pw_rpc/synchronous_call.h"
610
611   void InvokeUnaryRpc() {
612     pw::rpc::Client client;
613     pw::rpc::Channel channel;
614
615     RoomInfoRequest request;
616     SynchronousCallResult<RoomInfoResponse> result =
617       SynchronousCall<Chat::GetRoomInformation>(client, channel.id(), request);
618
619     if (result.is_rpc_error()) {
620       ShutdownClient(client);
621     } else if (result.is_server_error()) {
622       HandleServerError(result.status());
623     } else if (result.is_timeout()) {
624       // SynchronousCall will block indefinitely, so we should never get here.
625       PW_UNREACHABLE();
626     }
627     HandleRoomInformation(std::move(result).response());
628   }
629
630   void AnotherExample() {
631     pw_rpc::nanopb::Chat::Client chat_client(client, channel);
632     constexpr auto kTimeout = pw::chrono::SystemClock::for_at_least(500ms);
633
634     RoomInfoRequest request;
635     auto result = SynchronousCallFor<Chat::GetRoomInformation>(
636         chat_client, request, kTimeout);
637
638     if (result.is_timeout()) {
639       RetryRoomRequest();
640     } else {
641     ...
642     }
643   }
644
645The ``SynchronousCallResult<Response>`` is also compatible with the
646:c:macro:`PW_TRY` family of macros, but users should be aware that their use
647will lose information about the type of error. This should only be used if the
648caller will handle all error scenarios the same.
649
650.. code-block:: c++
651
652   pw::Status SyncRpc() {
653     const RoomInfoRequest request;
654     PW_TRY_ASSIGN(const RoomInfoResponse& response,
655                   SynchronousCall<Chat::GetRoomInformation>(client, request));
656     HandleRoomInformation(response);
657     return pw::OkStatus();
658   }
659
660------------
661ClientServer
662------------
663Sometimes, a device needs to both process RPCs as a server, as well as making
664calls to another device as a client. To do this, both a client and server must
665be set up, and incoming packets must be sent to both of them.
666
667Pigweed simplifies this setup by providing a ``ClientServer`` class which wraps
668an RPC client and server with the same set of channels.
669
670.. code-block:: cpp
671
672   pw::rpc::Channel channels[] = {
673       pw::rpc::Channel::Create<1>(&channel_output)};
674
675   // Creates both a client and a server.
676   pw::rpc::ClientServer client_server(channels);
677
678   void ProcessRpcData(pw::ConstByteSpan packet) {
679     // Calls into both the client and the server, sending the packet to the
680     // appropriate one.
681     client_server.ProcessPacket(packet);
682   }
683
684.. _module-pw_rpc-cpp-testing:
685
686-------
687Testing
688-------
689``pw_rpc`` provides utilities for unit testing RPC services and client calls.
690
691Client unit testing in C++
692==========================
693``pw_rpc`` supports invoking RPCs, simulating server responses, and checking
694what packets are sent by an RPC client in tests. Raw, Nanopb and Pwpb interfaces
695are supported. Code that uses the raw API may be tested with the raw test
696helpers, and vice versa. The Nanopb and Pwpb APIs also provides a test helper
697with a real client-server pair that supports testing of asynchronous messaging.
698
699To test synchronous code that invokes RPCs, declare a ``RawClientTestContext``,
700``PwpbClientTestContext``,  or ``NanopbClientTestContext``. These test context
701objects provide a preconfigured RPC client, channel, server fake, and buffer for
702encoding packets.
703
704These test classes are defined in ``pw_rpc/raw/client_testing.h``,
705``pw_rpc/pwpb/client_testing.h``, or ``pw_rpc/nanopb/client_testing.h``.
706
707Use the context's ``client()`` and ``channel()`` to invoke RPCs. Use the
708context's ``server()`` to simulate responses. To verify that the client sent the
709expected data, use the context's ``output()``, which is a ``FakeChannelOutput``.
710
711For example, the following tests a class that invokes an RPC. It checks that
712the expected data was sent and then simulates a response from the server.
713
714.. code-block:: cpp
715
716   #include "pw_rpc/raw/client_testing.h"
717
718   class ClientUnderTest {
719    public:
720     // To support injecting an RPC client for testing, classes that make RPC
721     // calls should take an RPC client and channel ID or an RPC service client
722     // (e.g. pw_rpc::raw::MyService::Client).
723     ClientUnderTest(pw::rpc::Client& client, uint32_t channel_id);
724
725     void DoSomethingThatInvokesAnRpc();
726
727     bool SetToTrueWhenRpcCompletes();
728   };
729
730   TEST(TestAThing, InvokesRpcAndHandlesResponse) {
731     RawClientTestContext context;
732     ClientUnderTest thing(context.client(), context.channel().id());
733
734     // Execute the code that invokes the MyService.TheMethod RPC.
735     things.DoSomethingThatInvokesAnRpc();
736
737     // Find and verify the payloads sent for the MyService.TheMethod RPC.
738     auto msgs = context.output().payloads<pw_rpc::raw::MyService::TheMethod>();
739     ASSERT_EQ(msgs.size(), 1u);
740
741     VerifyThatTheExpectedMessageWasSent(msgs.back());
742
743     // Send the response packet from the server and verify that the class reacts
744     // accordingly.
745     EXPECT_FALSE(thing.SetToTrueWhenRpcCompletes());
746
747     context_.server().SendResponse<pw_rpc::raw::MyService::TheMethod>(
748         final_message, OkStatus());
749
750     EXPECT_TRUE(thing.SetToTrueWhenRpcCompletes());
751   }
752
753To test client code that uses asynchronous responses, encapsulates multiple
754rpc calls to one or more services, or uses a custom service implementation,
755declare a ``NanopbClientServerTestContextThreaded`` or
756``PwpbClientServerTestContextThreaded``. These test object are defined in
757``pw_rpc/nanopb/client_server_testing_threaded.h`` and
758``pw_rpc/pwpb/client_server_testing_threaded.h``.
759
760Use the context's ``server()`` to register a ``Service`` implementation, and
761``client()`` and ``channel()`` to invoke RPCs. Create a ``Thread`` using the
762context as a ``ThreadCore`` to have it asynchronously forward request/responses or
763call ``ForwardNewPackets`` to synchronously process all messages. To verify that
764the client/server sent the expected data, use the context's
765``request(uint32_t index)`` and ``response(uint32_t index)`` to retrieve the
766ordered messages.
767
768For example, the following tests a class that invokes an RPC and blocks till a
769response is received. It verifies that expected data was both sent and received.
770
771.. code-block:: cpp
772
773   #include "my_library_protos/my_service.rpc.pb.h"
774   #include "pw_rpc/nanopb/client_server_testing_threaded.h"
775   #include "pw_thread_stl/options.h"
776
777   class ClientUnderTest {
778    public:
779     // To support injecting an RPC client for testing, classes that make RPC
780     // calls should take an RPC client and channel ID or an RPC service client
781     // (e.g. pw_rpc::raw::MyService::Client).
782     ClientUnderTest(pw::rpc::Client& client, uint32_t channel_id);
783
784     Status BlockOnResponse(uint32_t value);
785   };
786
787
788   class TestService final : public MyService<TestService> {
789    public:
790     Status TheMethod(const pw_rpc_test_TheMethod& request,
791                         pw_rpc_test_TheMethod& response) {
792       response.value = request.integer + 1;
793       return pw::OkStatus();
794     }
795   };
796
797   TEST(TestServiceTest, ReceivesUnaryRpcResponse) {
798     NanopbClientServerTestContextThreaded<> ctx(pw::thread::stl::Options{});
799     TestService service;
800     ctx.server().RegisterService(service);
801     ClientUnderTest client(ctx.client(), ctx.channel().id());
802
803     // Execute the code that invokes the MyService.TheMethod RPC.
804     constexpr uint32_t value = 1;
805     const auto result = client.BlockOnResponse(value);
806     const auto request = ctx.request<MyService::TheMethod>(0);
807     const auto response = ctx.response<MyService::TheMethod>(0);
808
809     // Verify content of messages
810     EXPECT_EQ(result, pw::OkStatus());
811     EXPECT_EQ(request.value, value);
812     EXPECT_EQ(response.value, value + 1);
813   }
814
815Use the context's
816``response(uint32_t index, Response<kMethod>& response)`` to decode messages
817into a provided response object. You would use this version if decoder callbacks
818are needed to fully decode a message. For instance if it uses ``repeated``
819fields.
820
821.. code-block:: cpp
822
823   TestResponse::Message response{};
824   response.repeated_field.SetDecoder(
825       [&values](TestResponse::StreamDecoder& decoder) {
826         return decoder.ReadRepeatedField(values);
827       });
828   ctx.response<test::GeneratedService::TestAnotherUnaryRpc>(0, response);
829
830Synchronous versions of these test contexts also exist that may be used on
831non-threaded systems ``NanopbClientServerTestContext`` and
832``PwpbClientServerTestContext``. While these do not allow for asynchronous
833messaging they support the use of service implementations and use a similar
834syntax. When these are used ``.ForwardNewPackets()`` should be called after each
835rpc call to trigger sending of queued messages.
836
837For example, the following tests a class that invokes an RPC that is responded
838to with a test service implementation.
839
840.. code-block:: cpp
841
842   #include "my_library_protos/my_service.rpc.pb.h"
843   #include "pw_rpc/nanopb/client_server_testing.h"
844
845   class ClientUnderTest {
846    public:
847     ClientUnderTest(pw::rpc::Client& client, uint32_t channel_id);
848
849     Status SendRpcCall(uint32_t value);
850   };
851
852
853   class TestService final : public MyService<TestService> {
854    public:
855     Status TheMethod(const pw_rpc_test_TheMethod& request,
856                         pw_rpc_test_TheMethod& response) {
857       response.value = request.integer + 1;
858       return pw::OkStatus();
859     }
860   };
861
862   TEST(TestServiceTest, ReceivesUnaryRpcResponse) {
863     NanopbClientServerTestContext<> ctx();
864     TestService service;
865     ctx.server().RegisterService(service);
866     ClientUnderTest client(ctx.client(), ctx.channel().id());
867
868     // Execute the code that invokes the MyService.TheMethod RPC.
869     constexpr uint32_t value = 1;
870     const auto result = client.SendRpcCall(value);
871     // Needed after ever RPC call to trigger forward of packets
872     ctx.ForwardNewPackets();
873     const auto request = ctx.request<MyService::TheMethod>(0);
874     const auto response = ctx.response<MyService::TheMethod>(0);
875
876     // Verify content of messages
877     EXPECT_EQ(result, pw::OkStatus());
878     EXPECT_EQ(request.value, value);
879     EXPECT_EQ(response.value, value + 1);
880   }
881
882Custom packet processing for ClientServerTestContext
883====================================================
884Optional constructor arguments for nanopb/pwpb ``*ClientServerTestContext`` and
885``*ClientServerTestContextThreaded`` allow allow customized packet processing.
886By default the only thing is done is ``ProcessPacket()`` call on the
887``ClientServer`` instance.
888
889For cases when additional instrumentation or offloading to separate thread is
890needed, separate client and server processors can be passed to context
891constructors. A packet processor is a function that returns ``pw::Status`` and
892accepts two arguments: ``pw::rpc::ClientServer&`` and ``pw::ConstByteSpan``.
893Default packet processing is equivalent to the next processor:
894
895.. code-block:: cpp
896
897   [](ClientServer& client_server, pw::ConstByteSpan packet) -> pw::Status {
898     return client_server.ProcessPacket(packet);
899   };
900
901The Server processor will be applied to all packets sent to the server (i.e.
902requests) and client processor will be applied to all packets sent to the client
903(i.e. responses).
904
905.. note::
906
907  The packet processor MUST call ``ClientServer::ProcessPacket()`` method.
908  Otherwise the packet won't be processed.
909
910.. note::
911
912  If the packet processor offloads processing to the separate thread, it MUST
913  copy the ``packet``. After the packet processor returns, the underlying array
914  can go out of scope or be reused for other purposes.
915
916SendResponseIfCalled() helper
917=============================
918``SendResponseIfCalled()`` function waits on ``*ClientTestContext*`` output to
919have a call for the specified method and then responses to it. It supports
920timeout for the waiting part (default timeout is 100ms).
921
922.. code-block:: c++
923
924   #include "pw_rpc/test_helpers.h"
925
926   pw::rpc::PwpbClientTestContext client_context;
927   other::pw_rpc::pwpb::OtherService::Client other_service_client(
928       client_context.client(), client_context.channel().id());
929
930   PW_PWPB_TEST_METHOD_CONTEXT(MyService, GetData)
931   context(other_service_client);
932   context.call({});
933
934   PW_TEST_ASSERT_OK(pw::rpc::test::SendResponseIfCalled<
935             other::pw_rpc::pwpb::OtherService::GetPart>(
936       client_context, {.value = 42}));
937
938   // At this point MyService::GetData handler received the GetPartResponse.
939
940Integration testing with ``pw_rpc``
941===================================
942``pw_rpc`` provides utilities to simplify writing integration tests for systems
943that communicate with ``pw_rpc``. The integration test utitilies set up a socket
944to use for IPC between an RPC server and client process.
945
946The server binary uses the system RPC server facade defined
947``pw_rpc_system_server/rpc_server.h``. The client binary uses the functions
948defined in ``pw_rpc/integration_testing.h``:
949
950.. cpp:var:: constexpr uint32_t kChannelId
951
952   The RPC channel for integration test RPCs.
953
954.. cpp:function:: pw::rpc::Client& pw::rpc::integration_test::Client()
955
956   Returns the global RPC client for integration test use.
957
958.. cpp:function:: pw::Status pw::rpc::integration_test::InitializeClient(int argc, char* argv[], const char* usage_args = "PORT")
959
960   Initializes logging and the global RPC client for integration testing. Starts
961   a background thread that processes incoming.
962
963---------------------
964Configuration options
965---------------------
966The following configurations can be adjusted via compile-time configuration of
967this module, see the
968:ref:`module documentation <module-structure-compile-time-configuration>` for
969more details.
970
971.. doxygenfile:: pw_rpc/public/pw_rpc/internal/config.h
972   :sections: define
973
974------------------------------
975Sharing server and client code
976------------------------------
977Streaming RPCs support writing multiple requests or responses. To facilitate
978sharing code between servers and clients, ``pw_rpc`` provides the
979``pw::rpc::Writer`` interface. On the client side, a client or bidirectional
980streaming RPC call object (``ClientWriter`` or ``ClientReaderWriter``) can be
981used as a ``pw::rpc::Writer&``. On the server side, a server or bidirectional
982streaming RPC call object (``ServerWriter`` or ``ServerReaderWriter``) can be
983used as a ``pw::rpc::Writer&``. Call ``as_writer()`` to get a ``Writer&`` of the
984client or server call object.
985
986----------------------------
987Encoding and sending packets
988----------------------------
989``pw_rpc`` has to manage interactions among multiple RPC clients, servers,
990client calls, and server calls. To safely synchronize these interactions with
991minimal overhead, ``pw_rpc`` uses a single, global mutex (when
992``PW_RPC_USE_GLOBAL_MUTEX`` is enabled).
993
994Because ``pw_rpc`` uses a global mutex, it also uses a global buffer to encode
995outgoing packets. The size of the buffer is set with
996``PW_RPC_ENCODING_BUFFER_SIZE_BYTES``, which defaults to 512 B. If dynamic
997allocation is enabled, this size does not affect how large RPC messages can be,
998but it is still used for sizing buffers in test utilities.
999
1000Users of ``pw_rpc`` must implement the :cpp:class:`pw::rpc::ChannelOutput`
1001interface.
1002
1003.. _module-pw_rpc-ChannelOutput:
1004.. cpp:class:: pw::rpc::ChannelOutput
1005
1006   ``pw_rpc`` endpoints use :cpp:class:`ChannelOutput` instances to send
1007   packets.  Systems that integrate pw_rpc must use one or more
1008   :cpp:class:`ChannelOutput` instances.
1009
1010   .. cpp:member:: static constexpr size_t kUnlimited = std::numeric_limits<size_t>::max()
1011
1012      Value returned from :cpp:func:`MaximumTransmissionUnit` to indicate an
1013      unlimited MTU.
1014
1015   .. cpp:function:: virtual size_t MaximumTransmissionUnit()
1016
1017      Returns the size of the largest packet the :cpp:class:`ChannelOutput` can
1018      send. :cpp:class:`ChannelOutput` implementations should only override this
1019      function if they impose a limit on the MTU. The default implementation
1020      returns :cpp:member:`kUnlimited`, which indicates that there is no MTU
1021      limit.
1022
1023   .. cpp:function:: virtual pw::Status Send(span<std::byte> packet)
1024
1025      Sends an encoded RPC packet. Returns OK if further packets may be sent,
1026      even if the current packet could not be sent. Returns any other status if
1027      the Channel is no longer able to send packets.
1028
1029      The RPC system's internal lock is held while this function is
1030      called. Avoid long-running operations, since these will delay any other
1031      users of the RPC system.
1032
1033      .. danger::
1034
1035         No ``pw_rpc`` APIs may be accessed in this function! Implementations
1036         MUST NOT access any RPC endpoints (:cpp:class:`pw::rpc::Client`,
1037         :cpp:class:`pw::rpc::Server`) or call objects
1038         (:cpp:class:`pw::rpc::ServerReaderWriter`
1039         :cpp:class:`pw::rpc::ClientReaderWriter`, etc.) inside the
1040         :cpp:func:`Send` function or any descendent calls. Doing so will result
1041         in deadlock! RPC APIs may be used by other threads, just not within
1042         :cpp:func:`Send`.
1043
1044         The buffer provided in ``packet`` must NOT be accessed outside of this
1045         function. It must be sent immediately or copied elsewhere before the
1046         function returns.
1047