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