xref: /aosp_15_r20/external/pigweed/pw_channel/design.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker.. _module-pw_channel-design:
2*61c4878aSAndroid Build Coastguard Worker
3*61c4878aSAndroid Build Coastguard Worker======
4*61c4878aSAndroid Build Coastguard WorkerDesign
5*61c4878aSAndroid Build Coastguard Worker======
6*61c4878aSAndroid Build Coastguard Worker.. pigweed-module-subpage::
7*61c4878aSAndroid Build Coastguard Worker   :name: pw_channel
8*61c4878aSAndroid Build Coastguard Worker
9*61c4878aSAndroid Build Coastguard Worker.. _module-pw_channel-design-why:
10*61c4878aSAndroid Build Coastguard Worker
11*61c4878aSAndroid Build Coastguard Worker--------------
12*61c4878aSAndroid Build Coastguard WorkerWhy pw_channel
13*61c4878aSAndroid Build Coastguard Worker--------------
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard WorkerFlow control
16*61c4878aSAndroid Build Coastguard Worker============
17*61c4878aSAndroid Build Coastguard WorkerFlow control ensures that:
18*61c4878aSAndroid Build Coastguard Worker
19*61c4878aSAndroid Build Coastguard Worker- Channels do not send more data than the receiver is prepared to handle.
20*61c4878aSAndroid Build Coastguard Worker- Channels do not request more data than they are prepared to handle.
21*61c4878aSAndroid Build Coastguard Worker
22*61c4878aSAndroid Build Coastguard WorkerIf one node sends more data than the other node can receive, network performance
23*61c4878aSAndroid Build Coastguard Workerdegrades. The effects vary, but could include data loss, throughput drops, or
24*61c4878aSAndroid Build Coastguard Workereven crashes in one or both nodes. The ``Channel`` API avoids these issues by
25*61c4878aSAndroid Build Coastguard Workerproviding backpressure.
26*61c4878aSAndroid Build Coastguard Worker
27*61c4878aSAndroid Build Coastguard WorkerWhat is backpressure?
28*61c4878aSAndroid Build Coastguard Worker---------------------
29*61c4878aSAndroid Build Coastguard WorkerIn networking, backpressure is when a node, overwhelmed by inbound traffic,
30*61c4878aSAndroid Build Coastguard Workerexterts pressure on upstream nodes. Communications APIs have to provide ways to
31*61c4878aSAndroid Build Coastguard Workerrelease the pressure, allowing higher layers to can reduce data rates or drop
32*61c4878aSAndroid Build Coastguard Workerstale or unnecessary packets.
33*61c4878aSAndroid Build Coastguard Worker
34*61c4878aSAndroid Build Coastguard WorkerPitfalls of simplistic backpressure APIs
35*61c4878aSAndroid Build Coastguard Worker----------------------------------------
36*61c4878aSAndroid Build Coastguard WorkerExpressing backpressure in an API might seem simple. You could just return a
37*61c4878aSAndroid Build Coastguard Workerstatus code that indicates that the link isn't ready, and retry when it is.
38*61c4878aSAndroid Build Coastguard Worker
39*61c4878aSAndroid Build Coastguard Worker..  code-block:: cpp
40*61c4878aSAndroid Build Coastguard Worker
41*61c4878aSAndroid Build Coastguard Worker   // Returns `UNAVAILABLE` if the link isn't ready for data yet; retry later.
42*61c4878aSAndroid Build Coastguard Worker   Status Write(std::span<const std::byte> packet);
43*61c4878aSAndroid Build Coastguard Worker
44*61c4878aSAndroid Build Coastguard WorkerIn practice, this is very difficult to work with:
45*61c4878aSAndroid Build Coastguard Worker
46*61c4878aSAndroid Build Coastguard Worker..  code-block:: cpp
47*61c4878aSAndroid Build Coastguard Worker
48*61c4878aSAndroid Build Coastguard Worker    std::span packet = ExpensiveOperationToPreprarePacket();
49*61c4878aSAndroid Build Coastguard Worker    if (Write(packet).IsUnavailable()) {
50*61c4878aSAndroid Build Coastguard Worker      // ... then what?
51*61c4878aSAndroid Build Coastguard Worker    }
52*61c4878aSAndroid Build Coastguard Worker
53*61c4878aSAndroid Build Coastguard WorkerNow what do you do? You did work to prepare the packet, but you can't send it.
54*61c4878aSAndroid Build Coastguard WorkerDo you store the packet somewhere and retry? Or, wait a bit and recreate the
55*61c4878aSAndroid Build Coastguard Workerpacket, then try to send again? How long do you wait before sending?
56*61c4878aSAndroid Build Coastguard Worker
57*61c4878aSAndroid Build Coastguard WorkerThe result is inefficient code that is difficult to write correctly.
58*61c4878aSAndroid Build Coastguard Worker
59*61c4878aSAndroid Build Coastguard WorkerThere are other options: you can add an ``IsReadyToWrite()`` function, and only
60*61c4878aSAndroid Build Coastguard Workercall ``Write`` when that is true. But what if ``IsReadyToWrite()`` becomes false
61*61c4878aSAndroid Build Coastguard Workerwhile you're preparing the packet? Then, you're back in the same situation.
62*61c4878aSAndroid Build Coastguard WorkerAnother approach: block until the link is ready for a write. But this means an
63*61c4878aSAndroid Build Coastguard Workerentire thread and its resources are locked up for an arbitrary amount of time.
64*61c4878aSAndroid Build Coastguard Worker
65*61c4878aSAndroid Build Coastguard WorkerHow pw_channel addresses write-side backpressure
66*61c4878aSAndroid Build Coastguard Worker------------------------------------------------
67*61c4878aSAndroid Build Coastguard WorkerWhen writing into a ``Channel`` instance, the ``Channel`` may provide
68*61c4878aSAndroid Build Coastguard Workerbackpressure in several locations:
69*61c4878aSAndroid Build Coastguard Worker
70*61c4878aSAndroid Build Coastguard Worker- :cpp:func:`PendReadyToWrite <pw::channel::AnyChannel::PendReadyToWrite>` --
71*61c4878aSAndroid Build Coastguard Worker  Before writing to a channel, users must check that it is ready to receive
72*61c4878aSAndroid Build Coastguard Worker  writes. If the channel is not ready, the channel will wake up the async task
73*61c4878aSAndroid Build Coastguard Worker  when it becomes ready to accept outbound data.
74*61c4878aSAndroid Build Coastguard Worker- :cpp:func:`GetWriteAllocator <pw::channel::AnyChannel::GetWriteAllocator>` --
75*61c4878aSAndroid Build Coastguard Worker  Once the channel becomes ready to receive writes, the writer must ensure that
76*61c4878aSAndroid Build Coastguard Worker  there is space in an outgoing write buffer for the message they wish to send.
77*61c4878aSAndroid Build Coastguard Worker  If there is not yet enough space, the channel will wake up the async task
78*61c4878aSAndroid Build Coastguard Worker  once there is space again in the future.
79*61c4878aSAndroid Build Coastguard Worker
80*61c4878aSAndroid Build Coastguard WorkerOnly once these two operations have completed can the writing task may place its
81*61c4878aSAndroid Build Coastguard Workerdata into the outgoing buffer and send it into the channel.
82*61c4878aSAndroid Build Coastguard Worker
83*61c4878aSAndroid Build Coastguard WorkerHow pw_channel addresses read-side backpressure
84*61c4878aSAndroid Build Coastguard Worker-----------------------------------------------
85*61c4878aSAndroid Build Coastguard WorkerWhen reading from a ``Channel`` instance, the consumer of the ``Channel`` data
86*61c4878aSAndroid Build Coastguard Workerexerts backpressure by *not* invoking :cpp:func:`PendRead <pw::channel::AnyChannel::PendRead>`.
87*61c4878aSAndroid Build Coastguard WorkerThe buffers returned by ``PendRead`` are allocated by the ``Channel`` itself.
88*61c4878aSAndroid Build Coastguard Worker
89*61c4878aSAndroid Build Coastguard WorkerZero-copy
90*61c4878aSAndroid Build Coastguard Worker=========
91*61c4878aSAndroid Build Coastguard WorkerIt's common to see async IO APIs like this:
92*61c4878aSAndroid Build Coastguard Worker
93*61c4878aSAndroid Build Coastguard Worker..  code-block:: cpp
94*61c4878aSAndroid Build Coastguard Worker
95*61c4878aSAndroid Build Coastguard Worker   Status Read(pw::Function<void(pw::Result<std::span<const std::byte>)> callback);
96*61c4878aSAndroid Build Coastguard Worker
97*61c4878aSAndroid Build Coastguard WorkerThese APIs suffer from an obvious problem: what is the lifetime of the span
98*61c4878aSAndroid Build Coastguard Workerpassed into the callback? Usually, it only lasts for the duration of the
99*61c4878aSAndroid Build Coastguard Workercallback. Users must therefore copy the data into a separate buffer if
100*61c4878aSAndroid Build Coastguard Workerthey need it to persist.
101*61c4878aSAndroid Build Coastguard Worker
102*61c4878aSAndroid Build Coastguard WorkerAnother common structure uses user-provided buffers:
103*61c4878aSAndroid Build Coastguard Worker
104*61c4878aSAndroid Build Coastguard Worker..  code-block:: cpp
105*61c4878aSAndroid Build Coastguard Worker
106*61c4878aSAndroid Build Coastguard Worker   Status ReadIntoProvidedBuffer(std::span<const std::byte> buffer, pw::Function<...> callback);
107*61c4878aSAndroid Build Coastguard Worker
108*61c4878aSAndroid Build Coastguard WorkerBut this a similar problem: the low-level implementor of the read interface
109*61c4878aSAndroid Build Coastguard Workermust copy data from its source (usually a lower-level protocol buffer or
110*61c4878aSAndroid Build Coastguard Workera peripheral-associated DMA buffer) into the user-provided buffer. This copy
111*61c4878aSAndroid Build Coastguard Workeris also required when passing between layers of the stack that need to e.g.
112*61c4878aSAndroid Build Coastguard Workererase headers, perform defragmentation, or otherwise modify the structure
113*61c4878aSAndroid Build Coastguard Workerof the incoming data.
114*61c4878aSAndroid Build Coastguard Worker
115*61c4878aSAndroid Build Coastguard WorkerThis process requires both runtime overhead due to copying and memory overhead
116*61c4878aSAndroid Build Coastguard Workerdue to the need for multiple buffers to hold every message.
117*61c4878aSAndroid Build Coastguard Worker
118*61c4878aSAndroid Build Coastguard Worker``Channel`` avoids this problem by using
119*61c4878aSAndroid Build Coastguard Worker:cpp:class:`MultiBuf <pw::multibuf::MultiBuf>`. The lower layers of the stack
120*61c4878aSAndroid Build Coastguard Workerare responsible for allocating peripheral-compatible buffers that are then
121*61c4878aSAndroid Build Coastguard Workerpassed up the stack for the application code to read from or write into.
122*61c4878aSAndroid Build Coastguard Worker``MultiBuf`` allows for fragementation, coalescing, insertion of headers,
123*61c4878aSAndroid Build Coastguard Workerfooters etc. without the need for a copy.
124*61c4878aSAndroid Build Coastguard Worker
125*61c4878aSAndroid Build Coastguard WorkerComposable
126*61c4878aSAndroid Build Coastguard Worker==========
127*61c4878aSAndroid Build Coastguard WorkerMany traditional communications code hard-codes its lower layers, making it
128*61c4878aSAndroid Build Coastguard Workerdifficult or impossible to reused application code between e.g. a UART-based
129*61c4878aSAndroid Build Coastguard Workerprotocol and an IP-based one. By providing a single standard interface for byte
130*61c4878aSAndroid Build Coastguard Workerand packet streams, ``Channel`` allows communications stacks to be layered on
131*61c4878aSAndroid Build Coastguard Workertop of one another in various fashions without need rewrites or intermediate
132*61c4878aSAndroid Build Coastguard Workerbuffering of data.
133*61c4878aSAndroid Build Coastguard Worker
134*61c4878aSAndroid Build Coastguard WorkerAsynchronous
135*61c4878aSAndroid Build Coastguard Worker============
136*61c4878aSAndroid Build Coastguard Worker``Channel`` uses ``pw_async2`` to allow an unlimited number of channel IO
137*61c4878aSAndroid Build Coastguard Workeroperations without the need for dedicated threads. ``pw_async2``'s
138*61c4878aSAndroid Build Coastguard Workerdispatcher-based structure ensures that work is only done as-needed,
139*61c4878aSAndroid Build Coastguard Workercancellation and timeouts are built-in and composable, and there is no
140*61c4878aSAndroid Build Coastguard Workerneed for deeply-nested callbacks or careful consideration of what
141*61c4878aSAndroid Build Coastguard Workercontext a particular callback may be invoked from.
142*61c4878aSAndroid Build Coastguard Worker
143*61c4878aSAndroid Build Coastguard Worker------------------
144*61c4878aSAndroid Build Coastguard WorkerChannel attributes
145*61c4878aSAndroid Build Coastguard Worker------------------
146*61c4878aSAndroid Build Coastguard WorkerChannels may be reliable, readable, writable, or seekable. A channel may be
147*61c4878aSAndroid Build Coastguard Workersubstituted for another as long as it provides at least the same set of
148*61c4878aSAndroid Build Coastguard Workercapabilities; additional capabilities are okay. The channel's data type
149*61c4878aSAndroid Build Coastguard Worker(datagram or byte) implies different read/write semantics, so datagram/byte
150*61c4878aSAndroid Build Coastguard Workerchannels cannot be used interchangeably in general.
151*61c4878aSAndroid Build Coastguard Worker
152*61c4878aSAndroid Build Coastguard WorkerUsing datagram channels as byte channels
153*61c4878aSAndroid Build Coastguard Worker========================================
154*61c4878aSAndroid Build Coastguard WorkerFor datagram channels, the exact bytes provided to a write call will appear in a
155*61c4878aSAndroid Build Coastguard Workerread call on the other end. A zero-byte datagram write results in a zero-byte
156*61c4878aSAndroid Build Coastguard Workerdatagram read, so empty datagrams may convey information.
157*61c4878aSAndroid Build Coastguard Worker
158*61c4878aSAndroid Build Coastguard WorkerFor byte channels, bytes written may be grouped differently when read. A
159*61c4878aSAndroid Build Coastguard Workerzero-length byte write is meaningless and will not result in a zero-length byte
160*61c4878aSAndroid Build Coastguard Workerread. If a zero-length byte read occurs, it is ignored.
161*61c4878aSAndroid Build Coastguard Worker
162*61c4878aSAndroid Build Coastguard WorkerTo facilitate simple code reuse, datagram-oriented channels may used as
163*61c4878aSAndroid Build Coastguard Workerbyte-oriented channels when appropriate. Calling
164*61c4878aSAndroid Build Coastguard Worker:cpp:func:`Channel::IgnoreDatagramBoundaries` on a datagram channel returns a
165*61c4878aSAndroid Build Coastguard Workerbyte channel reference to it. The byte view of the channel is simply the
166*61c4878aSAndroid Build Coastguard Workerconcatenation of the contents of the datagrams.
167*61c4878aSAndroid Build Coastguard Worker
168*61c4878aSAndroid Build Coastguard WorkerThis is only valid if, for the datagram channel:
169*61c4878aSAndroid Build Coastguard Worker
170*61c4878aSAndroid Build Coastguard Worker- datagram boundaries have no significance or meaning,
171*61c4878aSAndroid Build Coastguard Worker- zero-length datagrams are not used to convey information, since they are
172*61c4878aSAndroid Build Coastguard Worker  meaningless for byte channels,
173*61c4878aSAndroid Build Coastguard Worker- short or zero-length writes through the byte API will not result in
174*61c4878aSAndroid Build Coastguard Worker  unacceptable overhead.
175