• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _module-pw_rpc:
2
3======
4pw_rpc
5======
6.. pigweed-module::
7   :name: pw_rpc
8
9The ``pw_rpc`` module provides a system for defining and invoking remote
10procedure calls (RPCs) on a device.
11
12This document discusses the ``pw_rpc`` protocol and its C++ implementation.
13``pw_rpc`` implementations for other languages are described in their own
14documents:
15
16.. toctree::
17   :maxdepth: 1
18
19   py/docs
20   ts/docs
21
22.. admonition:: Try it out!
23
24   For a quick intro to ``pw_rpc``, see the
25   :ref:`module-pw_hdlc-rpc-example` in the :ref:`module-pw_hdlc` module.
26
27.. warning::
28
29   This documentation is under construction. Many sections are outdated or
30   incomplete. The content needs to be reorgnanized.
31
32---------------
33Implementations
34---------------
35Pigweed provides several client and server implementations of ``pw_rpc``.
36
37.. list-table::
38  :header-rows: 1
39
40  * - Language
41    - Server
42    - Client
43  * - C++ (raw)
44    - ✅
45    - ✅
46  * - C++ (Nanopb)
47    - ✅
48    - ✅
49  * - C++ (pw_protobuf)
50    - ✅
51    - ✅
52  * - Java
53    -
54    - ✅
55  * - Python
56    -
57    - ✅
58  * - TypeScript
59    -
60    - in development
61
62.. warning::
63
64  ``pw_protobuf`` and ``nanopb`` RPC services cannot currently coexist within the
65  same RPC server. Unless you are running multiple RPC servers, you cannot
66  incrementally migrate services from one protobuf implementation to another,
67  or otherwise mix and match. See
68  `Issue 234874320 <https://issues.pigweed.dev/issues/234874320>`_.
69
70-------------
71RPC semantics
72-------------
73The semantics of ``pw_rpc`` are similar to `gRPC
74<https://grpc.io/docs/what-is-grpc/core-concepts/>`_.
75
76RPC call lifecycle
77==================
78In ``pw_rpc``, an RPC begins when the client sends an initial packet. The server
79receives the packet, looks up the relevant service method, then calls into the
80RPC function. The RPC is considered active until the server sends a status to
81finish the RPC. The client may terminate an ongoing RPC by cancelling it.
82Multiple concurrent RPC requests to the same method may be made simultaneously
83(Note: Concurrent requests are not yet possible using the Java client. See
84`Issue 237418397 <https://issues.pigweed.dev/issues/237418397>`_).
85
86Depending the type of RPC, the client and server exchange zero or more protobuf
87request or response payloads. There are four RPC types:
88
89* **Unary**. The client sends one request and the server sends one
90  response with a status.
91* **Server streaming**. The client sends one request and the server sends zero
92  or more responses followed by a status.
93* **Client streaming**. The client sends zero or more requests and the server
94  sends one response with a status.
95* **Bidirectional streaming**. The client sends zero or more requests and the
96  server sends zero or more responses followed by a status.
97
98Events
99------
100The key events in the RPC lifecycle are:
101
102* **Start**. The client initiates the RPC. The server's RPC body executes.
103* **Finish**. The server sends a status and completes the RPC. The client calls
104  a callback.
105* **Request**. The client sends a request protobuf. The server calls a callback
106  when it receives it. In unary and server streaming RPCs, there is only one
107  request and it is handled when the RPC starts.
108* **Response**. The server sends a response protobuf. The client calls a
109  callback when it receives it. In unary and client streaming RPCs, there is
110  only one response and it is handled when the RPC completes.
111* **Error**. The server or client terminates the RPC abnormally with a status.
112  The receiving endpoint calls a callback.
113* **Request Completion**. The client sends a message that it would like to
114  request call completion. The server calls a callback when it receives it. Some
115  servers may ignore the request completion message. In client and bidirectional
116  streaming RPCs, this also indicates that client has finished sending requests.
117
118Status codes
119============
120``pw_rpc`` call objects (``ClientReaderWriter``, ``ServerReaderWriter``, etc.)
121use certain status codes to indicate what occurred. These codes are returned
122from functions like ``Write()`` or ``Finish()``.
123
124* ``OK`` -- The operation succeeded.
125* ``UNAVAILABLE`` -- The channel is not currently registered with the server or
126  client.
127* ``UNKNOWN`` -- Sending a packet failed due to an unrecoverable
128  :cpp:func:`pw::rpc::ChannelOutput::Send` error.
129
130Unrequested responses
131=====================
132``pw_rpc`` supports sending responses to RPCs that have not yet been invoked by
133a client. This is useful in testing and in situations like an RPC that triggers
134reboot. After the reboot, the device opens the writer object and sends its
135response to the client.
136
137The C++ API for opening a server reader/writer takes the generated RPC function
138as a template parameter. The server to use, channel ID, and service instance are
139passed as arguments. The API is the same for all RPC types, except the
140appropriate reader/writer class must be used.
141
142.. code-block:: c++
143
144   // Open a ServerWriter for a server streaming RPC.
145   auto writer = RawServerWriter::Open<pw_rpc::raw::ServiceName::MethodName>(
146       server, channel_id, service_instance);
147
148   // Send some responses, even though the client has not yet called this RPC.
149   CHECK_OK(writer.Write(encoded_response_1));
150   CHECK_OK(writer.Write(encoded_response_2));
151
152   // Finish the RPC.
153   CHECK_OK(writer.Finish(OkStatus()));
154
155Errata
156------
157Prior to support for concurrent requests to a single method, no identifier was
158present to distinguish different calls to the same method. When a "call ID"
159feature was first introduced to solve this issue, existing clients and servers
160(1) set this value to zero and (2) ignored this value.
161
162When initial support for concurrent methods was added, a separate "open call ID"
163was introduced to distinguish unrequested responses. However, legacy servers
164built prior to this change continue to send unrequested responses with call ID
165zero. Prior to `this fix
166<https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/192311>`_, clients
167which used "open call ID" would not accept unrequested responses from legacy
168servers. Clients built after that change will accept unrequested responses which
169use both "open call ID" and call ID zero.
170
171See `Issue 237418397 <https://issues.pigweed.dev/issues/237418397>`_ for more
172details and discussion.
173
174---------------
175Creating an RPC
176---------------
177
1781. RPC service declaration
179==========================
180Pigweed RPCs are declared in a protocol buffer service definition.
181
182* `Protocol Buffer service documentation
183  <https://developers.google.com/protocol-buffers/docs/proto3#services>`_
184* `gRPC service definition documentation
185  <https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition>`_
186
187.. code-block:: protobuf
188
189   syntax = "proto3";
190
191   package foo.bar;
192
193   message Request {}
194
195   message Response {
196     int32 number = 1;
197   }
198
199   service TheService {
200     rpc MethodOne(Request) returns (Response) {}
201     rpc MethodTwo(Request) returns (stream Response) {}
202   }
203
204This protocol buffer is declared in a ``BUILD.gn`` file as follows:
205
206.. code-block:: python
207
208   import("//build_overrides/pigweed.gni")
209   import("$dir_pw_protobuf_compiler/proto.gni")
210
211   pw_proto_library("the_service_proto") {
212     sources = [ "foo_bar/the_service.proto" ]
213   }
214
215.. admonition:: proto2 or proto3 syntax?
216
217   Always use proto3 syntax rather than proto2 for new protocol buffers. Proto2
218   protobufs can be compiled for ``pw_rpc``, but they are not as well supported
219   as proto3. Specifically, ``pw_rpc`` lacks support for non-zero default values
220   in proto2. When using Nanopb with ``pw_rpc``, proto2 response protobufs with
221   non-zero field defaults should be manually initialized to the default struct.
222
223   In the past, proto3 was sometimes avoided because it lacked support for field
224   presence detection. Fortunately, this has been fixed: proto3 now supports
225   ``optional`` fields, which are equivalent to proto2 ``optional`` fields.
226
227   If you need to distinguish between a default-valued field and a missing field,
228   mark the field as ``optional``. The presence of the field can be detected
229   with ``std::optional``, a ``HasField(name)``, or ``has_<field>`` member,
230   depending on the library.
231
232   Optional fields have some overhead --- if using Nanopb, default-valued fields
233   are included in the encoded proto, and the proto structs have a
234   ``has_<field>`` flag for each optional field. Use plain fields if field
235   presence detection is not needed.
236
237   .. code-block:: protobuf
238
239      syntax = "proto3";
240
241      message MyMessage {
242        // Leaving this field unset is equivalent to setting it to 0.
243        int32 number = 1;
244
245        // Setting this field to 0 is different from leaving it unset.
246        optional int32 other_number = 2;
247      }
248
2492. RPC code generation
250======================
251``pw_rpc`` generates a C++ header file for each ``.proto`` file. This header is
252generated in the build output directory. Its exact location varies by build
253system and toolchain, but the C++ include path always matches the sources
254declaration in the ``pw_proto_library``. The ``.proto`` extension is replaced
255with an extension corresponding to the protobuf library in use.
256
257================== =============== =============== =============
258Protobuf libraries Build subtarget Protobuf header pw_rpc header
259================== =============== =============== =============
260Raw only           .raw_rpc        (none)          .raw_rpc.pb.h
261Nanopb or raw      .nanopb_rpc     .pb.h           .rpc.pb.h
262pw_protobuf or raw .pwpb_rpc       .pwpb.h         .rpc.pwpb.h
263================== =============== =============== =============
264
265For example, the generated RPC header for ``"foo_bar/the_service.proto"`` is
266``"foo_bar/the_service.rpc.pb.h"`` for Nanopb or
267``"foo_bar/the_service.raw_rpc.pb.h"`` for raw RPCs.
268
269The generated header defines a base class for each RPC service declared in the
270``.proto`` file. A service named ``TheService`` in package ``foo.bar`` would
271generate the following base class for pw_protobuf:
272
273.. cpp:class:: template <typename Implementation> foo::bar::pw_rpc::pwpb::TheService::Service
274
2753. RPC service definition
276=========================
277The serivce class is implemented by inheriting from the generated RPC service
278base class and defining a method for each RPC. The methods must match the name
279and function signature for one of the supported protobuf implementations.
280Services may mix and match protobuf implementations within one service.
281
282.. tip::
283
284   The generated code includes RPC service implementation stubs. You can
285   reference or copy and paste these to get started with implementing a service.
286   These stub classes are generated at the bottom of the pw_rpc proto header.
287
288   To use the stubs, do the following:
289
290   #. Locate the generated RPC header in the build directory. For example:
291
292      .. code-block:: sh
293
294         find out/ -name <proto_name>.rpc.pwpb.h
295
296   #. Scroll to the bottom of the generated RPC header.
297   #. Copy the stub class declaration to a header file.
298   #. Copy the member function definitions to a source file.
299   #. Rename the class or change the namespace, if desired.
300   #. List these files in a build target with a dependency on the
301      ``pw_proto_library``.
302
303A pw_protobuf implementation of this service would be as follows:
304
305.. code-block:: cpp
306
307   #include "foo_bar/the_service.rpc.pwpb.h"
308
309   namespace foo::bar {
310
311   class TheService : public pw_rpc::pwpb::TheService::Service<TheService> {
312    public:
313     pw::Status MethodOne(const Request::Message& request,
314                          Response::Message& response) {
315       // implementation
316       response.number = 123;
317       return pw::OkStatus();
318     }
319
320     void MethodTwo(const Request::Message& request,
321                    ServerWriter<Response::Message>& response) {
322       // implementation
323       response.Write({.number = 123});
324     }
325   };
326
327   }  // namespace foo::bar
328
329The pw_protobuf implementation would be declared in a ``BUILD.gn``:
330
331.. code-block:: python
332
333   import("//build_overrides/pigweed.gni")
334
335   import("$dir_pw_build/target_types.gni")
336
337   pw_source_set("the_service") {
338     public_configs = [ ":public" ]
339     public = [ "public/foo_bar/service.h" ]
340     public_deps = [ ":the_service_proto.pwpb_rpc" ]
341   }
342
3434. Register the service with a server
344=====================================
345This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc>`
346channel output and the example service.
347
348.. code-block:: cpp
349
350   // Set up the output channel for the pw_rpc server to use. This configures the
351   // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must
352   // adapt this as necessary.
353   pw::stream::SysIoWriter writer;
354   pw::rpc::FixedMtuChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
355       writer, pw::hdlc::kDefaultRpcAddress, "HDLC output");
356
357   // Allocate an array of channels for the server to use. If dynamic allocation
358   // is enabled (PW_RPC_DYNAMIC_ALLOCATION=1), the server can be initialized
359   // without any channels, and they can be added later.
360   pw::rpc::Channel channels[] = {
361       pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
362
363   // Declare the pw_rpc server with the HDLC channel.
364   pw::rpc::Server server(channels);
365
366   foo::bar::TheService the_service;
367   pw::rpc::SomeOtherService some_other_service;
368
369   void RegisterServices() {
370     // Register the foo.bar.TheService example service and another service.
371     server.RegisterService(the_service, some_other_service);
372   }
373
374   int main() {
375     // Set up the server.
376     RegisterServices();
377
378     // Declare a buffer for decoding incoming HDLC frames.
379     constexpr size_t kDecoderBufferSize =
380       pw::hdlc::Decoder::RequiredBufferSizeForFrameSize(kMaxTransmissionUnit);
381
382     std::array<std::byte, kDecoderBufferSize> input_buffer;
383
384     PW_LOG_INFO("Starting pw_rpc server");
385     pw::hdlc::Decoder decoder(input_buffer);
386
387     while (true) {
388       std::byte byte;
389       pw::Status ret_val = pw::sys_io::ReadByte(&byte);
390       if (!ret_val.ok()) {
391         return ret_val;
392       }
393       if (auto result = decoder.Process(byte); result.ok()) {
394         pw::hdlc::Frame& frame = result.value();
395         if (frame.address() == pw::hdlc::kDefaultRpcAddress) {
396           server.ProcessPacket(frame.data());
397         }
398       }
399     }
400   }
401
402--------
403Channels
404--------
405``pw_rpc`` sends all of its packets over channels. These are logical,
406application-layer routes used to tell the RPC system where a packet should go.
407
408Channels over a client-server connection must all have a unique ID, which can be
409assigned statically at compile time or dynamically.
410
411.. code-block:: cpp
412
413   // Creating a channel with the static ID 3.
414   pw::rpc::Channel static_channel = pw::rpc::Channel::Create<3>(&output);
415
416   // Grouping channel IDs within an enum can lead to clearer code.
417   enum ChannelId {
418     kUartChannel = 1,
419     kSpiChannel = 2,
420   };
421
422   // Creating a channel with a static ID defined within an enum.
423   pw::rpc::Channel another_static_channel =
424       pw::rpc::Channel::Create<ChannelId::kUartChannel>(&output);
425
426   // Creating a channel with a dynamic ID (note that no output is provided; it
427   // will be set when the channel is used.
428   pw::rpc::Channel dynamic_channel;
429
430Sometimes, the ID and output of a channel are not known at compile time as they
431depend on information stored on the physical device. To support this use case, a
432dynamically-assignable channel can be configured once at runtime with an ID and
433output.
434
435.. code-block:: cpp
436
437   // Create a dynamic channel without a compile-time ID or output.
438   pw::rpc::Channel dynamic_channel;
439
440   void Init() {
441     // Called during boot to pull the channel configuration from the system.
442     dynamic_channel.Configure(GetChannelId(), some_output);
443   }
444
445Adding and removing channels
446============================
447New channels may be registered with the ``OpenChannel`` function. If dynamic
448allocation is enabled (:c:macro:`PW_RPC_DYNAMIC_ALLOCATION` is 1), any number of
449channels may be registered. If dynamic allocation is disabled, new channels may
450only be registered if there are availale channel slots in the span provided to
451the RPC endpoint at construction.
452
453A channel may be closed and unregistered with an endpoint by calling
454``ChannelClose`` on the endpoint with the corresponding channel ID.  This
455will terminate any pending calls and call their ``on_error`` callback
456with the ``ABORTED`` status.
457
458.. code-block:: cpp
459
460   // When a channel is closed, any pending calls will receive
461   // on_error callbacks with ABORTED status.
462   client->CloseChannel(1);
463
464.. _module-pw_rpc-remap:
465
466Remapping channels
467==================
468Some pw_rpc deployments may find it helpful to remap channel IDs in RPC packets.
469This can remove the need for globally known channel IDs. Clients can use a
470generic channel ID. The server remaps the generic channel ID to an ID associated
471with the transport the client is using.
472
473.. cpp:namespace-push:: pw::rpc
474
475.. doxygengroup:: pw_rpc_channel_functions
476   :content-only:
477
478.. cpp:namespace-pop::
479
480A future revision of the pw_rpc protocol will remove the need for global channel
481IDs without requiring remapping.
482
483Example deployment
484------------------
485This section describes a hypothetical pw_rpc deployment that supports arbitrary
486pw_rpc clients with one pw_rpc server. Note that this assumes that the
487underlying transport provides some sort of addressing that the server-side can
488associate with a channel ID.
489
490- A pw_rpc server is running on one core. A variable number of pw_rpc clients
491  need to call RPCs on the server from a different core.
492- The client core opens a socket (or similar feature) to connect to the server
493  core.
494- The server core detects the inbound connection and allocates a new channel ID.
495  It creates a new channel by calling :cpp:func:`pw::rpc::Server::OpenChannel`
496  with the channel ID and a :cpp:class:`pw::rpc::ChannelOutput` associated with
497  the new connection.
498- The server maintains a mapping between channel IDs and pw_rpc client
499  connections.
500- On the client core, pw_rpc clients all use the same channel ID (e.g.  ``1``).
501- As packets arrive from pw_rpc client connections, the server-side code calls
502  :cpp:func:`pw::rpc::ChangeEncodedChannelId` on the encoded packet to replace
503  the generic channel ID (``1``) with the server-side channel ID allocated when
504  the client connected. The encoded packet is then passed to
505  :cpp:func:`pw::rpc::Server::ProcessPacket`.
506- When the server sends pw_rpc packets, the :cpp:class:`pw::rpc::ChannelOutput`
507  calls :cpp:func:`pw::rpc::ChangeEncodedChannelId` to set the channel ID back
508  to the generic ``1``.
509
510--------
511Services
512--------
513A service is a logical grouping of RPCs defined within a .proto file. ``pw_rpc``
514uses these .proto definitions to generate code for a base service, from which
515user-defined RPCs are implemented.
516
517``pw_rpc`` supports multiple protobuf libraries, and the generated code API
518depends on which is used.
519
520Services must be registered with a server in order to call their methods.
521Services may later be unregistered, which aborts calls for methods in that
522service and prevents future calls to them, until the service is re-registered.
523
524.. _module-pw_rpc-protobuf-library-apis:
525
526---------------------
527Protobuf library APIs
528---------------------
529
530.. toctree::
531   :maxdepth: 1
532
533   pwpb/docs
534   nanopb/docs
535
536----------------------------
537Testing a pw_rpc integration
538----------------------------
539After setting up a ``pw_rpc`` server in your project, you can test that it is
540working as intended by registering the provided ``EchoService``, defined in
541``echo.proto``, which echoes back a message that it receives.
542
543.. literalinclude:: echo.proto
544   :language: protobuf
545   :lines: 14-
546
547For example, in C++ with pw_protobuf:
548
549.. code-block:: c++
550
551   #include "pw_rpc/server.h"
552
553   // Include the apporpriate header for your protobuf library.
554   #include "pw_rpc/echo_service_pwpb.h"
555
556   constexpr pw::rpc::Channel kChannels[] = { /* ... */ };
557   static pw::rpc::Server server(kChannels);
558
559   static pw::rpc::EchoService echo_service;
560
561   void Init() {
562     server.RegisterService(echo_service);
563   }
564
565Benchmarking and stress testing
566===============================
567
568.. toctree::
569   :maxdepth: 1
570   :hidden:
571
572   benchmark
573
574``pw_rpc`` provides an RPC service and Python module for stress testing and
575benchmarking a ``pw_rpc`` deployment. See :ref:`module-pw_rpc-benchmark`.
576
577------
578Naming
579------
580
581Reserved names
582==============
583``pw_rpc`` reserves a few service method names so they can be used for generated
584classes. The following names cannnot be used for service methods:
585
586- ``Client``
587- ``Service``
588- Any reserved words in the languages ``pw_rpc`` supports (e.g. ``class``).
589
590``pw_rpc`` does not reserve any service names, but the restriction of avoiding
591reserved words in supported languages applies.
592
593Service naming style
594====================
595``pw_rpc`` service names should use capitalized camel case and should not use
596the term "Service". Appending "Service" to a service name is redundant, similar
597to appending "Class" or "Function" to a class or function name. The
598C++ implementation class may use "Service" in its name, however.
599
600For example, a service for accessing a file system should simply be named
601``service FileSystem``, rather than ``service FileSystemService``, in the
602``.proto`` file.
603
604.. code-block:: protobuf
605
606   // file.proto
607   package pw.file;
608
609   service FileSystem {
610       rpc List(ListRequest) returns (stream ListResponse);
611   }
612
613The C++ service implementation class may append "Service" to the name.
614
615.. code-block:: cpp
616
617   // file_system_service.h
618   #include "pw_file/file.raw_rpc.pb.h"
619
620   namespace pw::file {
621
622   class FileSystemService : public pw_rpc::raw::FileSystem::Service<FileSystemService> {
623     void List(ConstByteSpan request, RawServerWriter& writer);
624   };
625
626   }  // namespace pw::file
627
628For upstream Pigweed services, this naming style is a requirement. Note that
629some services created before this was established may use non-compliant
630names. For Pigweed users, this naming style is a suggestion.
631
632------------------------------
633C++ payload sizing limitations
634------------------------------
635The individual size of each sent RPC request or response is limited by
636``pw_rpc``'s ``PW_RPC_ENCODING_BUFFER_SIZE_BYTES`` configuration option when
637using Pigweed's C++ implementation. While multiple RPC messages can be enqueued
638(as permitted by the underlying transport), if a single individual sent message
639exceeds the limitations of the statically allocated encode buffer, the packet
640will fail to encode and be dropped.
641
642This applies to all C++ RPC service implementations (nanopb, raw, and pwpb),
643so it's important to ensure request and response message sizes do not exceed
644this limitation.
645
646As ``pw_rpc`` has some additional encoding overhead, a helper,
647``pw::rpc::MaxSafePayloadSize()`` is provided to expose the practical max RPC
648message payload size.
649
650.. code-block:: cpp
651
652   #include "pw_file/file.raw_rpc.pb.h"
653   #include "pw_rpc/channel.h"
654
655   namespace pw::file {
656
657   class FileSystemService : public pw_rpc::raw::FileSystem::Service<FileSystemService> {
658    public:
659     void List(ConstByteSpan request, RawServerWriter& writer);
660
661    private:
662     // Allocate a buffer for building proto responses.
663     static constexpr size_t kEncodeBufferSize = pw::rpc::MaxSafePayloadSize();
664     std::array<std::byte, kEncodeBufferSize> encode_buffer_;
665   };
666
667   }  // namespace pw::file
668
669--------------------
670Protocol description
671--------------------
672Pigweed RPC servers and clients communicate using ``pw_rpc`` packets. These
673packets are used to send requests and responses, control streams, cancel ongoing
674RPCs, and report errors.
675
676Packet format
677=============
678Pigweed RPC packets consist of a type and a set of fields. The packets are
679encoded as protocol buffers. The full packet format is described in
680``pw_rpc/pw_rpc/internal/packet.proto``.
681
682.. literalinclude:: internal/packet.proto
683   :language: protobuf
684   :lines: 14-
685
686The packet type and RPC type determine which fields are present in a Pigweed RPC
687packet. Each packet type is only sent by either the client or the server.
688These tables describe the meaning of and fields included with each packet type.
689
690Client-to-server packets
691------------------------
692+---------------------------+-------------------------------------+
693| packet type               | description                         |
694+===========================+=====================================+
695| REQUEST                   | Invoke an RPC                       |
696|                           |                                     |
697|                           | .. code-block:: text                |
698|                           |                                     |
699|                           |   - channel_id                      |
700|                           |   - service_id                      |
701|                           |   - method_id                       |
702|                           |   - payload                         |
703|                           |     (unary & server streaming only) |
704|                           |   - call_id (optional)              |
705|                           |                                     |
706+---------------------------+-------------------------------------+
707| CLIENT_STREAM             | Message in a client stream          |
708|                           |                                     |
709|                           | .. code-block:: text                |
710|                           |                                     |
711|                           |   - channel_id                      |
712|                           |   - service_id                      |
713|                           |   - method_id                       |
714|                           |   - payload                         |
715|                           |   - call_id (if set in REQUEST)     |
716|                           |                                     |
717+---------------------------+-------------------------------------+
718| CLIENT_REQUEST_COMPLETION | Client requested stream completion  |
719|                           |                                     |
720|                           | .. code-block:: text                |
721|                           |                                     |
722|                           |   - channel_id                      |
723|                           |   - service_id                      |
724|                           |   - method_id                       |
725|                           |   - call_id (if set in REQUEST)     |
726|                           |                                     |
727+---------------------------+-------------------------------------+
728| CLIENT_ERROR              | Abort an ongoing RPC                |
729|                           |                                     |
730|                           | .. code-block:: text                |
731|                           |                                     |
732|                           |   - channel_id                      |
733|                           |   - service_id                      |
734|                           |   - method_id                       |
735|                           |   - status                          |
736|                           |   - call_id (if set in REQUEST)     |
737|                           |                                     |
738+---------------------------+-------------------------------------+
739
740**Client errors**
741
742The client sends ``CLIENT_ERROR`` packets to a server when it receives a packet
743it did not request. If possible, the server should abort it.
744
745The status code indicates the type of error. The status code is logged, but all
746status codes result in the same action by the server: aborting the RPC.
747
748* ``CANCELLED`` -- The client requested that the RPC be cancelled.
749* ``ABORTED`` -- The RPC was aborted due its channel being closed.
750* ``NOT_FOUND`` -- Received a packet for a service method the client does not
751  recognize.
752* ``FAILED_PRECONDITION`` -- Received a packet for a service method that the
753  client did not invoke.
754* ``DATA_LOSS`` -- Received a corrupt packet for a pending service method.
755* ``INVALID_ARGUMENT`` -- The server sent a packet type to an RPC that does not
756  support it (a ``SERVER_STREAM`` was sent to an RPC with no server stream).
757* ``UNAVAILABLE`` -- Received a packet for an unknown channel.
758
759Server-to-client packets
760------------------------
761+-------------------+-------------------------------------+
762| packet type       | description                         |
763+===================+=====================================+
764| RESPONSE          | The RPC is complete                 |
765|                   |                                     |
766|                   | .. code-block:: text                |
767|                   |                                     |
768|                   |   - channel_id                      |
769|                   |   - service_id                      |
770|                   |   - method_id                       |
771|                   |   - status                          |
772|                   |   - payload                         |
773|                   |     (unary & client streaming only) |
774|                   |   - call_id (if set in REQUEST)     |
775|                   |                                     |
776+-------------------+-------------------------------------+
777| SERVER_STREAM     | Message in a server stream          |
778|                   |                                     |
779|                   | .. code-block:: text                |
780|                   |                                     |
781|                   |   - channel_id                      |
782|                   |   - service_id                      |
783|                   |   - method_id                       |
784|                   |   - payload                         |
785|                   |   - call_id (if set in REQUEST)     |
786|                   |                                     |
787+-------------------+-------------------------------------+
788| SERVER_ERROR      | Received unexpected packet          |
789|                   |                                     |
790|                   | .. code-block:: text                |
791|                   |                                     |
792|                   |   - channel_id                      |
793|                   |   - service_id (if relevant)        |
794|                   |   - method_id (if relevant)         |
795|                   |   - status                          |
796|                   |   - call_id (if set in REQUEST)     |
797|                   |                                     |
798+-------------------+-------------------------------------+
799
800All server packets contain the same ``call_id`` that was set in the initial
801request made by the client, if any.
802
803**Server errors**
804
805The server sends ``SERVER_ERROR`` packets when it receives a packet it cannot
806process. The client should abort any RPC for which it receives an error. The
807status field indicates the type of error.
808
809* ``NOT_FOUND`` -- The requested service or method does not exist.
810* ``FAILED_PRECONDITION`` -- A client stream or cancel packet was sent for an
811  RPC that is not pending.
812* ``INVALID_ARGUMENT`` -- The client sent a packet type to an RPC that does not
813  support it (a ``CLIENT_STREAM`` was sent to an RPC with no client stream).
814* ``RESOURCE_EXHAUSTED`` -- The request came on a new channel, but a channel
815  could not be allocated for it.
816* ``ABORTED`` -- The RPC was aborted due its channel being closed.
817* ``INTERNAL`` -- The server was unable to respond to an RPC due to an
818  unrecoverable internal error.
819* ``UNAVAILABLE`` -- Received a packet for an unknown channel.
820
821Invoking a service method
822=========================
823Calling an RPC requires a specific sequence of packets. This section describes
824the protocol for calling service methods of each type: unary, server streaming,
825client streaming, and bidirectional streaming.
826
827The basic flow for all RPC invocations is as follows:
828
829* Client sends a ``REQUEST`` packet. Includes a payload for unary & server
830  streaming RPCs.
831* For client and bidirectional streaming RPCs, the client may send any number of
832  ``CLIENT_STREAM`` packets with payloads.
833* For server and bidirectional streaming RPCs, the server may send any number of
834  ``SERVER_STREAM`` packets.
835* The server sends a ``RESPONSE`` packet. Includes a payload for unary & client
836  streaming RPCs. The RPC is complete.
837
838The client may cancel an ongoing RPC at any time by sending a ``CLIENT_ERROR``
839packet with status ``CANCELLED``. The server may finish an ongoing RPC at any
840time by sending the ``RESPONSE`` packet.
841
842Unary RPC
843---------
844In a unary RPC, the client sends a single request and the server sends a single
845response.
846
847.. mermaid::
848   :alt: Unary RPC
849   :align: center
850
851   sequenceDiagram
852       participant C as Client
853       participant S as Server
854       C->>+S: request
855       Note left of C: PacketType.REQUEST<br>channel ID<br>service ID<br>method ID<br>payload
856
857       S->>-C: response
858       Note right of S: PacketType.RESPONSE<br>channel ID<br>service ID<br>method ID<br>payload<br>status
859
860The client may attempt to cancel a unary RPC by sending a ``CLIENT_ERROR``
861packet with status ``CANCELLED``. The server sends no response to a cancelled
862RPC. If the server processes the unary RPC synchronously (the handling thread
863sends the response), it may not be possible to cancel the RPC.
864
865.. mermaid::
866   :alt: Cancelled Unary RPC
867   :align: center
868
869   sequenceDiagram
870       participant C as Client
871       participant S as Server
872       C->>+S: request
873       Note left of C: PacketType.REQUEST<br>channel ID<br>service ID<br>method ID<br>payload
874
875       C->>S: cancel
876       Note left of C: PacketType.CLIENT_ERROR<br>channel ID<br>service ID<br>method ID<br>status=CANCELLED
877
878
879Server streaming RPC
880--------------------
881In a server streaming RPC, the client sends a single request and the server
882sends any number of ``SERVER_STREAM`` packets followed by a ``RESPONSE`` packet.
883
884.. mermaid::
885   :alt: Server Streaming RPC
886   :align: center
887
888   sequenceDiagram
889       participant C as Client
890       participant S as Server
891       C->>+S: request
892       Note left of C: PacketType.REQUEST<br>channel ID<br>service ID<br>method ID<br>payload
893
894       S-->>C: messages (zero or more)
895       Note right of S: PacketType.SERVER_STREAM<br>channel ID<br>service ID<br>method ID<br>payload
896
897       S->>-C: done
898       Note right of S: PacketType.RESPONSE<br>channel ID<br>service ID<br>method ID<br>status
899
900
901The client may terminate a server streaming RPC by sending a ``CLIENT_STREAM``
902packet with status ``CANCELLED``. The server sends no response.
903
904.. mermaid::
905   :alt: Cancelled Server Streaming RPC
906   :align: center
907
908   sequenceDiagram
909       participant C as Client
910       participant S as Server
911       C->>S: request
912       Note left of C: PacketType.REQUEST<br>channel ID<br>service ID<br>method ID<br>payload
913
914       S-->>C: messages (zero or more)
915       Note right of S: PacketType.SERVER_STREAM<br>channel ID<br>service ID<br>method ID<br>payload
916
917       C->>S: cancel
918       Note left of C: PacketType.CLIENT_ERROR<br>channel ID<br>service ID<br>method ID<br>status=CANCELLED
919
920
921Client streaming RPC
922--------------------
923In a client streaming RPC, the client starts the RPC by sending a ``REQUEST``
924packet with no payload. It then sends any number of messages in
925``CLIENT_STREAM`` packets, followed by a ``CLIENT_REQUEST_COMPLETION``. The server sends
926a single ``RESPONSE`` to finish the RPC.
927
928.. mermaid::
929   :alt: Client Streaming RPC
930   :align: center
931
932   sequenceDiagram
933       participant C as Client
934       participant S as Server
935       C->>S: start
936       Note left of C: PacketType.REQUEST<br>channel ID<br>service ID<br>method ID<br>payload
937
938       C-->>S: messages (zero or more)
939       Note left of C: PacketType.CLIENT_STREAM<br>channel ID<br>service ID<br>method ID<br>payload
940
941       C->>S: done
942       Note left of C: PacketType.CLIENT_REQUEST_COMPLETION<br>channel ID<br>service ID<br>method ID
943
944       S->>C: response
945       Note right of S: PacketType.RESPONSE<br>channel ID<br>service ID<br>method ID<br>payload<br>status
946
947The server may finish the RPC at any time by sending its ``RESPONSE`` packet,
948even if it has not yet received the ``CLIENT_REQUEST_COMPLETION`` packet. The client may
949terminate the RPC at any time by sending a ``CLIENT_ERROR`` packet with status
950``CANCELLED``.
951
952.. mermaid::
953   :alt: Cancelled Client Streaming RPC
954   :align: center
955
956   sequenceDiagram
957       participant C as Client
958       participant S as Server
959       C->>S: start
960       Note left of C: PacketType.REQUEST<br>channel ID<br>service ID<br>method ID
961
962       C-->>S: messages (zero or more)
963       Note left of C: PacketType.CLIENT_STREAM<br>channel ID<br>service ID<br>method ID<br>payload
964
965       C->>S: cancel
966       Note right of S: PacketType.CLIENT_ERROR<br>channel ID<br>service ID<br>method ID<br>status=CANCELLED
967
968Bidirectional streaming RPC
969---------------------------
970In a bidirectional streaming RPC, the client sends any number of requests and
971the server sends any number of responses. The client invokes the RPC by sending
972a ``REQUEST`` with no payload. It sends a ``CLIENT_REQUEST_COMPLETION`` packet when it
973has finished sending requests. The server sends a ``RESPONSE`` packet to finish
974the RPC.
975
976.. mermaid::
977   :alt: Bidirectional Streaming RPC
978   :align: center
979
980   sequenceDiagram
981       participant C as Client
982       participant S as Server
983       C->>S: start
984       Note left of C: PacketType.REQUEST<br>channel ID<br>service ID<br>method ID<br>payload
985
986       C-->>S: messages (zero or more)
987       Note left of C: PacketType.CLIENT_STREAM<br>channel ID<br>service ID<br>method ID<br>payload
988
989       C-->S: (messages in any order)
990
991       S-->>C: messages (zero or more)
992       Note right of S: PacketType.SERVER_STREAM<br>channel ID<br>service ID<br>method ID<br>payload
993
994       C->>S: done
995       Note left of C: PacketType.CLIENT_REQUEST_COMPLETION<br>channel ID<br>service ID<br>method ID
996
997       S->>C: done
998       Note right of S: PacketType.RESPONSE<br>channel ID<br>service ID<br>method ID<br>status
999
1000
1001The server may finish the RPC at any time by sending the ``RESPONSE`` packet,
1002even if it has not received the ``CLIENT_REQUEST_COMPLETION`` packet. The client may
1003terminate the RPC at any time by sending a ``CLIENT_ERROR`` packet with status
1004``CANCELLED``.
1005
1006.. mermaid::
1007   :alt: Client Streaming RPC
1008   :align: center
1009
1010   sequenceDiagram
1011       participant C as Client
1012       participant S as Server
1013       C->>S: start
1014       Note left of C: PacketType.REQUEST<br>channel ID<br>service ID<br>method ID<br>payload
1015
1016       C-->>S: messages (zero or more)
1017       Note left of C: PacketType.CLIENT_STREAM<br>channel ID<br>service ID<br>method ID<br>payload
1018
1019       C->>S: done
1020       Note left of C: PacketType.CLIENT_REQUEST_COMPLETION<br>channel ID<br>service ID<br>method ID
1021
1022       S->>C: response
1023       Note right of S: PacketType.RESPONSE<br>channel ID<br>service ID<br>method ID<br>payload<br>status
1024
1025-------
1026C++ API
1027-------
1028
1029RPC server
1030==========
1031Declare an instance of ``rpc::Server`` and register services with it.
1032
1033.. admonition:: TODO
1034
1035   Document the public interface
1036
1037Size report
1038-----------
1039The following size report showcases the memory usage of the core RPC server. It
1040is configured with a single channel using a basic transport interface that
1041directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte
1042packet buffer, which comprises the plurality of the example's RAM usage. This is
1043not a suitable transport for an actual product; a real implementation would have
1044additional overhead proportional to the complexity of the transport.
1045
1046.. include:: server_size
1047
1048RPC server implementation
1049-------------------------
1050
1051The Method class
1052^^^^^^^^^^^^^^^^
1053The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method``
1054serves as the bridge between the ``pw_rpc`` server library and the user-defined
1055RPC functions. Each supported protobuf implementation extends ``Method`` to
1056implement its request and response proto handling. The ``pw_rpc`` server
1057calls into the ``Method`` implementation through the base class's ``Invoke``
1058function.
1059
1060``Method`` implementations store metadata about each method, including a
1061function pointer to the user-defined method implementation. They also provide
1062``static constexpr`` functions for creating each type of method. ``Method``
1063implementations must satisfy the ``MethodImplTester`` test class in
1064``pw_rpc/internal/method_impl_tester.h``.
1065
1066See ``pw_rpc/internal/method.h`` for more details about ``Method``.
1067
1068Packet flow
1069^^^^^^^^^^^
1070
1071Requests
1072........
1073
1074.. mermaid::
1075   :alt: Request Packet Flow
1076
1077   flowchart LR
1078       packets[Packets]
1079
1080       subgraph pw_rpc [pw_rpc Library]
1081           direction TB
1082           internalMethod[[internal::Method]]
1083           Server --> Service --> internalMethod
1084       end
1085
1086       packets --> Server
1087
1088       generatedServices{{generated services}}
1089       userDefinedRPCs(user-defined RPCs)
1090
1091       generatedServices --> userDefinedRPCs
1092       internalMethod --> generatedServices
1093
1094Responses
1095.........
1096
1097.. mermaid::
1098   :alt: Request Packet Flow
1099
1100   flowchart LR
1101       generatedServices{{generated services}}
1102       userDefinedRPCs(user-defined RPCs)
1103
1104       subgraph pw_rpc [pw_rpc Library]
1105           direction TB
1106           internalMethod[[internal::Method]]
1107           internalMethod --> Server --> Channel
1108       end
1109
1110       packets[Packets]
1111       Channel --> packets
1112
1113       userDefinedRPCs --> generatedServices
1114       generatedServices --> internalMethod
1115
1116RPC client
1117==========
1118The RPC client is used to send requests to a server and manages the contexts of
1119ongoing RPCs.
1120
1121Setting up a client
1122-------------------
1123The ``pw::rpc::Client`` class is instantiated with a list of channels that it
1124uses to communicate. These channels can be shared with a server, but multiple
1125clients cannot use the same channels.
1126
1127To send incoming RPC packets from the transport layer to be processed by a
1128client, the client's ``ProcessPacket`` function is called with the packet data.
1129
1130.. code-block:: c++
1131
1132   #include "pw_rpc/client.h"
1133
1134   namespace {
1135
1136   pw::rpc::Channel my_channels[] = {
1137       pw::rpc::Channel::Create<1>(&my_channel_output)};
1138   pw::rpc::Client my_client(my_channels);
1139
1140   }  // namespace
1141
1142   // Called when the transport layer receives an RPC packet.
1143   void ProcessRpcPacket(ConstByteSpan packet) {
1144     my_client.ProcessPacket(packet);
1145   }
1146
1147Note that client processing such as callbacks will be invoked within
1148the body of ``ProcessPacket``.
1149
1150If certain packets need to be filtered out, or if certain client processing
1151needs to be invoked from a specific thread or context, the ``PacketMeta`` class
1152can be used to determine which service or channel a packet is targeting. After
1153filtering, ``ProcessPacket`` can be called from the appropriate environment.
1154
1155.. _module-pw_rpc-making-calls:
1156
1157Making RPC calls
1158----------------
1159RPC calls are not made directly through the client, but using one of its
1160registered channels instead. A service client class is generated from a .proto
1161file for each selected protobuf library, which is then used to send RPC requests
1162through a given channel. The API for this depends on the protobuf library;
1163please refer to the
1164:ref:`appropriate documentation<module-pw_rpc-protobuf-library-apis>`. Multiple
1165service client implementations can exist simulatenously and share the same
1166``Client`` class.
1167
1168When a call is made, a call object is returned to the caller. This object tracks
1169the ongoing RPC call, and can be used to manage it. An RPC call is only active
1170as long as its call object is alive.
1171
1172.. tip::
1173
1174   Use ``std::move`` when passing around call objects to keep RPCs alive.
1175
1176Example
1177^^^^^^^
1178.. code-block:: c++
1179
1180   #include "pw_rpc/echo_service_nanopb.h"
1181
1182   namespace {
1183   // Generated clients are namespaced with their proto library.
1184   using EchoClient = pw_rpc::nanopb::EchoService::Client;
1185
1186   // RPC channel ID on which to make client calls. RPC calls cannot be made on
1187   // channel 0 (Channel::kUnassignedChannelId).
1188   constexpr uint32_t kDefaultChannelId = 1;
1189
1190   pw::rpc::NanopbUnaryReceiver<pw_rpc_EchoMessage> echo_call;
1191
1192   // Callback invoked when a response is received. This is called synchronously
1193   // from Client::ProcessPacket.
1194   void EchoResponse(const pw_rpc_EchoMessage& response,
1195                     pw::Status status) {
1196     if (status.ok()) {
1197       PW_LOG_INFO("Received echo response: %s", response.msg);
1198     } else {
1199       PW_LOG_ERROR("Echo failed with status %d",
1200                    static_cast<int>(status.code()));
1201     }
1202   }
1203
1204   }  // namespace
1205
1206   void CallEcho(const char* message) {
1207     // Create a client to call the EchoService.
1208     EchoClient echo_client(my_rpc_client, kDefaultChannelId);
1209
1210     pw_rpc_EchoMessage request{};
1211     pw::string::Copy(message, request.msg);
1212
1213     // By assigning the returned call to the global echo_call, the RPC
1214     // call is kept alive until it completes. When a response is received, it
1215     // will be logged by the handler function and the call will complete.
1216     echo_call = echo_client.Echo(request, EchoResponse);
1217     if (!echo_call.active()) {
1218       // The RPC call was not sent. This could occur due to, for example, an
1219       // invalid channel ID. Handle if necessary.
1220     }
1221   }
1222
1223Call objects
1224============
1225An RPC call is represented by a call object. Server and client calls use the
1226same base call class in C++, but the public API is different depending on the
1227type of call (see `RPC call lifecycle`_) and whether it is being used by the
1228server or client.
1229
1230The public call types are as follows:
1231
1232.. list-table::
1233   :header-rows: 1
1234
1235   * - RPC Type
1236     - Server call
1237     - Client call
1238   * - Unary
1239     - ``(Raw|Nanopb|Pwpb)UnaryResponder``
1240     - ``(Raw|Nanopb|Pwpb)UnaryReceiver``
1241   * - Server streaming
1242     - ``(Raw|Nanopb|Pwpb)ServerWriter``
1243     - ``(Raw|Nanopb|Pwpb)ClientReader``
1244   * - Client streaming
1245     - ``(Raw|Nanopb|Pwpb)ServerReader``
1246     - ``(Raw|Nanopb|Pwpb)ClientWriter``
1247   * - Bidirectional streaming
1248     - ``(Raw|Nanopb|Pwpb)ServerReaderWriter``
1249     - ``(Raw|Nanopb|Pwpb)ClientReaderWriter``
1250
1251Client call API
1252---------------
1253Client call objects provide a few common methods.
1254
1255.. cpp:class:: pw::rpc::ClientCallType
1256
1257   The ``ClientCallType`` will be one of the following types:
1258
1259   - ``(Raw|Nanopb|Pwpb)UnaryReceiver`` for unary
1260   - ``(Raw|Nanopb|Pwpb)ClientReader`` for server streaming
1261   - ``(Raw|Nanopb|Pwpb)ClientWriter`` for client streaming
1262   - ``(Raw|Nanopb|Pwpb)ClientReaderWriter`` for bidirectional streaming
1263
1264   .. cpp:function:: bool active() const
1265
1266      Returns true if the call is active.
1267
1268   .. cpp:function:: uint32_t channel_id() const
1269
1270      Returns the channel ID of this call, which is 0 if the call is inactive.
1271
1272   .. cpp:function:: uint32_t id() const
1273
1274      Returns the call ID, a unique identifier for this call.
1275
1276   .. cpp:function:: void Write(RequestType)
1277
1278      Only available on client and bidirectional streaming calls. Sends a stream
1279      request. Returns:
1280
1281      - ``OK`` - the request was successfully sent
1282      - ``FAILED_PRECONDITION`` - the writer is closed
1283      - ``INTERNAL`` - pw_rpc was unable to encode message; does not apply to
1284        raw calls
1285      - other errors - the :cpp:class:`ChannelOutput` failed to send the packet;
1286        the error codes are determined by the :cpp:class:`ChannelOutput`
1287        implementation
1288
1289   .. cpp:function:: pw::Status RequestCompletion()
1290
1291      Notifies the server that client has requested for call completion. On
1292      client and bidirectional streaming calls no further client stream messages
1293      will be sent.
1294
1295   .. cpp:function:: pw::Status Cancel()
1296
1297      Cancels this RPC. Closes the call and sends a ``CANCELLED`` error to the
1298      server. Return statuses are the same as :cpp:func:`Write`.
1299
1300   .. cpp:function:: void Abandon()
1301
1302      Closes this RPC locally. Sends a ``CLIENT_REQUEST_COMPLETION``, but no cancellation
1303      packet. Future packets for this RPC are dropped, and the client sends a
1304      ``FAILED_PRECONDITION`` error in response because the call is not active.
1305
1306   .. cpp:function:: void CloseAndWaitForCallbacks()
1307
1308      Abandons this RPC and additionally blocks on completion of any running callbacks.
1309
1310   .. cpp:function:: void set_on_completed(pw::Function<void(ResponseTypeIfUnaryOnly, pw::Status)>)
1311
1312      Sets the callback that is called when the RPC completes normally. The
1313      signature depends on whether the call has a unary or stream response.
1314
1315   .. cpp:function:: void set_on_error(pw::Function<void(pw::Status)>)
1316
1317      Sets the callback that is called when the RPC is terminated due to an error.
1318
1319   .. cpp:function:: void set_on_next(pw::Function<void(ResponseType)>)
1320
1321      Only available on server and bidirectional streaming calls. Sets the callback
1322      that is called for each stream response.
1323
1324Callbacks
1325---------
1326The C++ call objects allow users to set callbacks that are invoked when RPC
1327`events`_ occur.
1328
1329.. list-table::
1330   :header-rows: 1
1331
1332   * - Name
1333     - Stream signature
1334     - Non-stream signature
1335     - Server
1336     - Client
1337   * - ``on_error``
1338     - ``void(pw::Status)``
1339     - ``void(pw::Status)``
1340     - ✅
1341     - ✅
1342   * - ``on_next``
1343     - n/a
1344     - ``void(const PayloadType&)``
1345     - ✅
1346     - ✅
1347   * - ``on_completed``
1348     - ``void(pw::Status)``
1349     - ``void(const PayloadType&, pw::Status)``
1350     -
1351     - ✅
1352   * - ``on_client_requested_completion``
1353     - ``void()``
1354     - n/a
1355     - ✅ (:c:macro:`optional <PW_RPC_COMPLETION_REQUEST_CALLBACK>`)
1356     -
1357
1358Limitations and restrictions
1359^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1360RPC callbacks are free to perform most actions, including invoking new RPCs or
1361cancelling pending calls. However, the C++ implementation imposes some
1362limitations and restrictions that must be observed.
1363
1364Destructors & moves wait for callbacks to complete
1365...................................................
1366* Callbacks must not destroy their call object. Attempting to do so will result
1367  in deadlock.
1368* Other threads may destroy a call while its callback is running, but that
1369  thread will block until all callbacks complete.
1370* Callbacks must not move their call object if it the call is still active. They
1371  may move their call object after it has terminated. Callbacks may move a
1372  different call into their call object, since moving closes the destination
1373  call.
1374* Other threads may move a call object while it has a callback running, but they
1375  will block until the callback completes if the call is still active.
1376
1377.. warning::
1378
1379   Deadlocks or crashes occur if a callback:
1380
1381   - attempts to destroy its call object
1382   - attempts to move its call object while the call is still active
1383   - never returns
1384
1385   If ``pw_rpc`` a callback violates these restrictions, a crash may occur,
1386   depending on the value of :c:macro:`PW_RPC_CALLBACK_TIMEOUT_TICKS`. These
1387   crashes have a message like the following:
1388
1389   .. code-block:: text
1390
1391      A callback for RPC 1:cc0f6de0/31e616ce has not finished after 10000 ticks.
1392      This may indicate that an RPC callback attempted to destroy or move its own
1393      call object, which is not permitted. Fix this condition or change the value of
1394      PW_RPC_CALLBACK_TIMEOUT_TICKS to avoid this crash.
1395
1396      See https://pigweed.dev/pw_rpc#destructors-moves-wait-for-callbacks-to-complete
1397      for details.
1398
1399Only one thread at a time may execute ``on_next``
1400.................................................
1401Only one thread may execute the ``on_next`` callback for a specific service
1402method at a time. If a second thread calls ``ProcessPacket()`` with a stream
1403packet before the ``on_next`` callback for the previous packet completes, the
1404second packet will be dropped. The RPC endpoint logs a warning when this occurs.
1405
1406Example warning for a dropped stream packet:
1407
1408.. code-block:: text
1409
1410   WRN  Received stream packet for 1:cc0f6de0/31e616ce before the callback for
1411        a previous packet completed! This packet will be dropped. This can be
1412        avoided by handling packets for a particular RPC on only one thread.
1413
1414RPC calls introspection
1415=======================
1416``pw_rpc`` provides ``pw_rpc/method_info.h`` header that allows to obtain
1417information about the generated RPC method in compile time.
1418
1419For now it provides only two types: ``MethodRequestType<RpcMethod>`` and
1420``MethodResponseType<RpcMethod>``. They are aliases to the types that are used
1421as a request and response respectively for the given RpcMethod.
1422
1423Example
1424-------
1425We have an RPC service ``SpecialService`` with ``MyMethod`` method:
1426
1427.. code-block:: protobuf
1428
1429   package some.package;
1430   service SpecialService {
1431     rpc MyMethod(MyMethodRequest) returns (MyMethodResponse) {}
1432   }
1433
1434We also have a templated Storage type alias:
1435
1436.. code-block:: c++
1437
1438   template <auto kMethod>
1439   using Storage =
1440      std::pair<MethodRequestType<kMethod>, MethodResponseType<kMethod>>;
1441
1442``Storage<some::package::pw_rpc::pwpb::SpecialService::MyMethod>`` will
1443instantiate as:
1444
1445.. code-block:: c++
1446
1447   std::pair<some::package::MyMethodRequest::Message,
1448             some::package::MyMethodResponse::Message>;
1449
1450.. note::
1451
1452   Only nanopb and pw_protobuf have real types as
1453   ``MethodRequestType<RpcMethod>``/``MethodResponseType<RpcMethod>``. Raw has
1454   them both set as ``void``. In reality, they are ``pw::ConstByteSpan``. Any
1455   helper/trait that wants to use this types for raw methods should do a custom
1456   implementation that copies the bytes under the span instead of copying just
1457   the span.
1458
1459.. _module-pw_rpc-client-sync-call-wrappers:
1460
1461Client synchronous call wrappers
1462================================
1463.. doxygenfile:: pw_rpc/synchronous_call.h
1464   :sections: detaileddescription
1465
1466Example
1467-------
1468.. code-block:: c++
1469
1470   #include "pw_rpc/synchronous_call.h"
1471
1472   void InvokeUnaryRpc() {
1473     pw::rpc::Client client;
1474     pw::rpc::Channel channel;
1475
1476     RoomInfoRequest request;
1477     SynchronousCallResult<RoomInfoResponse> result =
1478       SynchronousCall<Chat::GetRoomInformation>(client, channel.id(), request);
1479
1480     if (result.is_rpc_error()) {
1481       ShutdownClient(client);
1482     } else if (result.is_server_error()) {
1483       HandleServerError(result.status());
1484     } else if (result.is_timeout()) {
1485       // SynchronousCall will block indefinitely, so we should never get here.
1486       PW_UNREACHABLE();
1487     }
1488     HandleRoomInformation(std::move(result).response());
1489   }
1490
1491   void AnotherExample() {
1492     pw_rpc::nanopb::Chat::Client chat_client(client, channel);
1493     constexpr auto kTimeout = pw::chrono::SystemClock::for_at_least(500ms);
1494
1495     RoomInfoRequest request;
1496     auto result = SynchronousCallFor<Chat::GetRoomInformation>(
1497         chat_client, request, kTimeout);
1498
1499     if (result.is_timeout()) {
1500       RetryRoomRequest();
1501     } else {
1502     ...
1503     }
1504   }
1505
1506The ``SynchronousCallResult<Response>`` is also compatible with the
1507:c:macro:`PW_TRY` family of macros, but users should be aware that their use
1508will lose information about the type of error. This should only be used if the
1509caller will handle all error scenarios the same.
1510
1511.. code-block:: c++
1512
1513   pw::Status SyncRpc() {
1514     const RoomInfoRequest request;
1515     PW_TRY_ASSIGN(const RoomInfoResponse& response,
1516                   SynchronousCall<Chat::GetRoomInformation>(client, request));
1517     HandleRoomInformation(response);
1518     return pw::OkStatus();
1519   }
1520
1521ClientServer
1522============
1523Sometimes, a device needs to both process RPCs as a server, as well as making
1524calls to another device as a client. To do this, both a client and server must
1525be set up, and incoming packets must be sent to both of them.
1526
1527Pigweed simplifies this setup by providing a ``ClientServer`` class which wraps
1528an RPC client and server with the same set of channels.
1529
1530.. code-block:: cpp
1531
1532   pw::rpc::Channel channels[] = {
1533       pw::rpc::Channel::Create<1>(&channel_output)};
1534
1535   // Creates both a client and a server.
1536   pw::rpc::ClientServer client_server(channels);
1537
1538   void ProcessRpcData(pw::ConstByteSpan packet) {
1539     // Calls into both the client and the server, sending the packet to the
1540     // appropriate one.
1541     client_server.ProcessPacket(packet);
1542   }
1543
1544Testing
1545=======
1546``pw_rpc`` provides utilities for unit testing RPC services and client calls.
1547
1548Client unit testing in C++
1549--------------------------
1550``pw_rpc`` supports invoking RPCs, simulating server responses, and checking
1551what packets are sent by an RPC client in tests. Raw, Nanopb and Pwpb interfaces
1552are supported. Code that uses the raw API may be tested with the raw test
1553helpers, and vice versa. The Nanopb and Pwpb APIs also provides a test helper
1554with a real client-server pair that supports testing of asynchronous messaging.
1555
1556To test synchronous code that invokes RPCs, declare a ``RawClientTestContext``,
1557``PwpbClientTestContext``,  or ``NanopbClientTestContext``. These test context
1558objects provide a preconfigured RPC client, channel, server fake, and buffer for
1559encoding packets.
1560
1561These test classes are defined in ``pw_rpc/raw/client_testing.h``,
1562``pw_rpc/pwpb/client_testing.h``, or ``pw_rpc/nanopb/client_testing.h``.
1563
1564Use the context's ``client()`` and ``channel()`` to invoke RPCs. Use the
1565context's ``server()`` to simulate responses. To verify that the client sent the
1566expected data, use the context's ``output()``, which is a ``FakeChannelOutput``.
1567
1568For example, the following tests a class that invokes an RPC. It checks that
1569the expected data was sent and then simulates a response from the server.
1570
1571.. code-block:: cpp
1572
1573   #include "pw_rpc/raw/client_testing.h"
1574
1575   class ClientUnderTest {
1576    public:
1577     // To support injecting an RPC client for testing, classes that make RPC
1578     // calls should take an RPC client and channel ID or an RPC service client
1579     // (e.g. pw_rpc::raw::MyService::Client).
1580     ClientUnderTest(pw::rpc::Client& client, uint32_t channel_id);
1581
1582     void DoSomethingThatInvokesAnRpc();
1583
1584     bool SetToTrueWhenRpcCompletes();
1585   };
1586
1587   TEST(TestAThing, InvokesRpcAndHandlesResponse) {
1588     RawClientTestContext context;
1589     ClientUnderTest thing(context.client(), context.channel().id());
1590
1591     // Execute the code that invokes the MyService.TheMethod RPC.
1592     things.DoSomethingThatInvokesAnRpc();
1593
1594     // Find and verify the payloads sent for the MyService.TheMethod RPC.
1595     auto msgs = context.output().payloads<pw_rpc::raw::MyService::TheMethod>();
1596     ASSERT_EQ(msgs.size(), 1u);
1597
1598     VerifyThatTheExpectedMessageWasSent(msgs.back());
1599
1600     // Send the response packet from the server and verify that the class reacts
1601     // accordingly.
1602     EXPECT_FALSE(thing.SetToTrueWhenRpcCompletes());
1603
1604     context_.server().SendResponse<pw_rpc::raw::MyService::TheMethod>(
1605         final_message, OkStatus());
1606
1607     EXPECT_TRUE(thing.SetToTrueWhenRpcCompletes());
1608   }
1609
1610To test client code that uses asynchronous responses, encapsulates multiple
1611rpc calls to one or more services, or uses a custom service implemenation,
1612declare a ``NanopbClientServerTestContextThreaded`` or
1613``PwpbClientServerTestContextThreaded``. These test object are defined in
1614``pw_rpc/nanopb/client_server_testing_threaded.h`` and
1615``pw_rpc/pwpb/client_server_testing_threaded.h``.
1616
1617Use the context's ``server()`` to register a ``Service`` implementation, and
1618``client()`` and ``channel()`` to invoke RPCs. Create a ``Thread`` using the
1619context as a ``ThreadCore`` to have it asycronously forward request/responses or
1620call ``ForwardNewPackets`` to synchronously process all messages. To verify that
1621the client/server sent the expected data, use the context's
1622``request(uint32_t index)`` and ``response(uint32_t index)`` to retrieve the
1623ordered messages.
1624
1625For example, the following tests a class that invokes an RPC and blocks till a
1626response is received. It verifies that expected data was both sent and received.
1627
1628.. code-block:: cpp
1629
1630   #include "my_library_protos/my_service.rpc.pb.h"
1631   #include "pw_rpc/nanopb/client_server_testing_threaded.h"
1632   #include "pw_thread_stl/options.h"
1633
1634   class ClientUnderTest {
1635    public:
1636     // To support injecting an RPC client for testing, classes that make RPC
1637     // calls should take an RPC client and channel ID or an RPC service client
1638     // (e.g. pw_rpc::raw::MyService::Client).
1639     ClientUnderTest(pw::rpc::Client& client, uint32_t channel_id);
1640
1641     Status BlockOnResponse(uint32_t value);
1642   };
1643
1644
1645   class TestService final : public MyService<TestService> {
1646    public:
1647     Status TheMethod(const pw_rpc_test_TheMethod& request,
1648                         pw_rpc_test_TheMethod& response) {
1649       response.value = request.integer + 1;
1650       return pw::OkStatus();
1651     }
1652   };
1653
1654   TEST(TestServiceTest, ReceivesUnaryRpcReponse) {
1655     NanopbClientServerTestContextThreaded<> ctx(pw::thread::stl::Options{});
1656     TestService service;
1657     ctx.server().RegisterService(service);
1658     ClientUnderTest client(ctx.client(), ctx.channel().id());
1659
1660     // Execute the code that invokes the MyService.TheMethod RPC.
1661     constexpr uint32_t value = 1;
1662     const auto result = client.BlockOnResponse(value);
1663     const auto request = ctx.request<MyService::TheMethod>(0);
1664     const auto response = ctx.resonse<MyService::TheMethod>(0);
1665
1666     // Verify content of messages
1667     EXPECT_EQ(result, pw::OkStatus());
1668     EXPECT_EQ(request.value, value);
1669     EXPECT_EQ(response.value, value + 1);
1670   }
1671
1672Use the context's
1673``response(uint32_t index, Response<kMethod>& response)`` to decode messages
1674into a provided response object. You would use this version if decoder callbacks
1675are needed to fully decode a message. For instance if it uses ``repeated``
1676fields.
1677
1678.. code-block:: cpp
1679
1680   TestResponse::Message response{};
1681   response.repeated_field.SetDecoder(
1682       [&values](TestResponse::StreamDecoder& decoder) {
1683         return decoder.ReadRepeatedField(values);
1684       });
1685   ctx.response<test::GeneratedService::TestAnotherUnaryRpc>(0, response);
1686
1687Synchronous versions of these test contexts also exist that may be used on
1688non-threaded systems ``NanopbClientServerTestContext`` and
1689``PwpbClientServerTestContext``. While these do not allow for asynchronous
1690messaging they support the use of service implemenations and use a similar
1691syntax. When these are used ``.ForwardNewPackets()`` should be called after each
1692rpc call to trigger sending of queued messages.
1693
1694For example, the following tests a class that invokes an RPC that is responded
1695to with a test service implemenation.
1696
1697.. code-block:: cpp
1698
1699   #include "my_library_protos/my_service.rpc.pb.h"
1700   #include "pw_rpc/nanopb/client_server_testing.h"
1701
1702   class ClientUnderTest {
1703    public:
1704     ClientUnderTest(pw::rpc::Client& client, uint32_t channel_id);
1705
1706     Status SendRpcCall(uint32_t value);
1707   };
1708
1709
1710   class TestService final : public MyService<TestService> {
1711    public:
1712     Status TheMethod(const pw_rpc_test_TheMethod& request,
1713                         pw_rpc_test_TheMethod& response) {
1714       response.value = request.integer + 1;
1715       return pw::OkStatus();
1716     }
1717   };
1718
1719   TEST(TestServiceTest, ReceivesUnaryRpcResponse) {
1720     NanopbClientServerTestContext<> ctx();
1721     TestService service;
1722     ctx.server().RegisterService(service);
1723     ClientUnderTest client(ctx.client(), ctx.channel().id());
1724
1725     // Execute the code that invokes the MyService.TheMethod RPC.
1726     constexpr uint32_t value = 1;
1727     const auto result = client.SendRpcCall(value);
1728     // Needed after ever RPC call to trigger forward of packets
1729     ctx.ForwardNewPackets();
1730     const auto request = ctx.request<MyService::TheMethod>(0);
1731     const auto response = ctx.response<MyService::TheMethod>(0);
1732
1733     // Verify content of messages
1734     EXPECT_EQ(result, pw::OkStatus());
1735     EXPECT_EQ(request.value, value);
1736     EXPECT_EQ(response.value, value + 1);
1737   }
1738
1739Custom packet processing for ClientServerTestContext
1740^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1741Optional constructor arguments for nanopb/pwpb ``*ClientServerTestContext`` and
1742``*ClientServerTestContextThreaded`` allow allow customized packet processing.
1743By default the only thing is done is ``ProcessPacket()`` call on the
1744``ClientServer`` instance.
1745
1746For cases when additional instrumentation or offloading to separate thread is
1747needed, separate client and server processors can be passed to context
1748constructors. A packet processor is a function that returns ``pw::Status`` and
1749accepts two arguments: ``pw::rpc::ClientServer&`` and ``pw::ConstByteSpan``.
1750Default packet processing is equivalent to the next processor:
1751
1752.. code-block:: cpp
1753
1754   [](ClientServer& client_server, pw::ConstByteSpan packet) -> pw::Status {
1755     return client_server.ProcessPacket(packet);
1756   };
1757
1758The Server processor will be applied to all packets sent to the server (i.e.
1759requests) and client processor will be applied to all packets sent to the client
1760(i.e. responses).
1761
1762.. note::
1763
1764  The packet processor MUST call ``ClientServer::ProcessPacket()`` method.
1765  Otherwise the packet won't be processed.
1766
1767.. note::
1768
1769  If the packet processor offloads processing to the separate thread, it MUST
1770  copy the ``packet``. After the packet processor returns, the underlying array
1771  can go out of scope or be reused for other purposes.
1772
1773SendResponseIfCalled() helper
1774-----------------------------
1775``SendResponseIfCalled()`` function waits on ``*ClientTestContext*`` output to
1776have a call for the specified method and then responses to it. It supports
1777timeout for the waiting part (default timeout is 100ms).
1778
1779.. code-block:: c++
1780
1781   #include "pw_rpc/test_helpers.h"
1782
1783   pw::rpc::PwpbClientTestContext client_context;
1784   other::pw_rpc::pwpb::OtherService::Client other_service_client(
1785       client_context.client(), client_context.channel().id());
1786
1787   PW_PWPB_TEST_METHOD_CONTEXT(MyService, GetData)
1788   context(other_service_client);
1789   context.call({});
1790
1791   ASSERT_OK(pw::rpc::test::SendResponseIfCalled<
1792             other::pw_rpc::pwpb::OtherService::GetPart>(
1793       client_context, {.value = 42}));
1794
1795   // At this point MyService::GetData handler received the GetPartResponse.
1796
1797Integration testing with ``pw_rpc``
1798-----------------------------------
1799``pw_rpc`` provides utilities to simplify writing integration tests for systems
1800that communicate with ``pw_rpc``. The integration test utitilies set up a socket
1801to use for IPC between an RPC server and client process.
1802
1803The server binary uses the system RPC server facade defined
1804``pw_rpc_system_server/rpc_server.h``. The client binary uses the functions
1805defined in ``pw_rpc/integration_testing.h``:
1806
1807.. cpp:var:: constexpr uint32_t kChannelId
1808
1809   The RPC channel for integration test RPCs.
1810
1811.. cpp:function:: pw::rpc::Client& pw::rpc::integration_test::Client()
1812
1813   Returns the global RPC client for integration test use.
1814
1815.. cpp:function:: pw::Status pw::rpc::integration_test::InitializeClient(int argc, char* argv[], const char* usage_args = "PORT")
1816
1817   Initializes logging and the global RPC client for integration testing. Starts
1818   a background thread that processes incoming.
1819
1820Module Configuration Options
1821============================
1822The following configurations can be adjusted via compile-time configuration of
1823this module, see the
1824:ref:`module documentation <module-structure-compile-time-configuration>` for
1825more details.
1826
1827.. doxygenfile:: pw_rpc/public/pw_rpc/internal/config.h
1828   :sections: define
1829
1830Sharing server and client code
1831==============================
1832Streaming RPCs support writing multiple requests or responses. To facilitate
1833sharing code between servers and clients, ``pw_rpc`` provides the
1834``pw::rpc::Writer`` interface. On the client side, a client or bidirectional
1835streaming RPC call object (``ClientWriter`` or ``ClientReaderWriter``) can be
1836used as a ``pw::rpc::Writer&``. On the server side, a server or bidirectional
1837streaming RPC call object (``ServerWriter`` or ``ServerReaderWriter``) can be
1838used as a ``pw::rpc::Writer&``. Call ``as_writer()`` to get a ``Writer&`` of the
1839client or server call object.
1840
1841Zephyr
1842======
1843To enable ``pw_rpc.*`` for Zephyr add ``CONFIG_PIGWEED_RPC=y`` to the project's
1844configuration. This will enable the Kconfig menu for the following:
1845
1846* ``pw_rpc.server`` which can be enabled via ``CONFIG_PIGWEED_RPC_SERVER=y``.
1847* ``pw_rpc.client`` which can be enabled via ``CONFIG_PIGWEED_RPC_CLIENT=y``.
1848* ``pw_rpc.client_server`` which can be enabled via
1849  ``CONFIG_PIGWEED_RPC_CLIENT_SERVER=y``.
1850* ``pw_rpc.common` which can be enabled via ``CONFIG_PIGWEED_RPC_COMMON=y``.
1851
1852Encoding and sending packets
1853============================
1854``pw_rpc`` has to manage interactions among multiple RPC clients, servers,
1855client calls, and server calls. To safely synchronize these interactions with
1856minimal overhead, ``pw_rpc`` uses a single, global mutex (when
1857``PW_RPC_USE_GLOBAL_MUTEX`` is enabled).
1858
1859Because ``pw_rpc`` uses a global mutex, it also uses a global buffer to encode
1860outgoing packets. The size of the buffer is set with
1861``PW_RPC_ENCODING_BUFFER_SIZE_BYTES``, which defaults to 512 B. If dynamic
1862allocation is enabled, this size does not affect how large RPC messages can be,
1863but it is still used for sizing buffers in test utilities.
1864
1865Users of ``pw_rpc`` must implement the :cpp:class:`pw::rpc::ChannelOutput`
1866interface.
1867
1868.. _module-pw_rpc-ChannelOutput:
1869.. cpp:class:: pw::rpc::ChannelOutput
1870
1871   ``pw_rpc`` endpoints use :cpp:class:`ChannelOutput` instances to send
1872   packets.  Systems that integrate pw_rpc must use one or more
1873   :cpp:class:`ChannelOutput` instances.
1874
1875   .. cpp:member:: static constexpr size_t kUnlimited = std::numeric_limits<size_t>::max()
1876
1877      Value returned from :cpp:func:`MaximumTransmissionUnit` to indicate an
1878      unlimited MTU.
1879
1880   .. cpp:function:: virtual size_t MaximumTransmissionUnit()
1881
1882      Returns the size of the largest packet the :cpp:class:`ChannelOutput` can
1883      send. :cpp:class:`ChannelOutput` implementations should only override this
1884      function if they impose a limit on the MTU. The default implementation
1885      returns :cpp:member:`kUnlimited`, which indicates that there is no MTU
1886      limit.
1887
1888   .. cpp:function:: virtual pw::Status Send(span<std::byte> packet)
1889
1890      Sends an encoded RPC packet. Returns OK if further packets may be sent,
1891      even if the current packet could not be sent. Returns any other status if
1892      the Channel is no longer able to send packets.
1893
1894      The RPC system's internal lock is held while this function is
1895      called. Avoid long-running operations, since these will delay any other
1896      users of the RPC system.
1897
1898      .. danger::
1899
1900         No ``pw_rpc`` APIs may be accessed in this function! Implementations
1901         MUST NOT access any RPC endpoints (:cpp:class:`pw::rpc::Client`,
1902         :cpp:class:`pw::rpc::Server`) or call objects
1903         (:cpp:class:`pw::rpc::ServerReaderWriter`
1904         :cpp:class:`pw::rpc::ClientReaderWriter`, etc.) inside the
1905         :cpp:func:`Send` function or any descendent calls. Doing so will result
1906         in deadlock! RPC APIs may be used by other threads, just not within
1907         :cpp:func:`Send`.
1908
1909         The buffer provided in ``packet`` must NOT be accessed outside of this
1910         function. It must be sent immediately or copied elsewhere before the
1911         function returns.
1912
1913Evolution
1914=========
1915Concurrent requests were not initially supported in pw_rpc (added in `C++
1916<https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/109077>`_, `Python
1917<https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/139610>`_, and
1918`TypeScript
1919<https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/160792>`_). As a
1920result, some user-written service implementations may not expect or correctly
1921support concurrent requests.
1922