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