1*61c4878aSAndroid Build Coastguard Worker.. _module-pw_multisink: 2*61c4878aSAndroid Build Coastguard Worker 3*61c4878aSAndroid Build Coastguard Worker============ 4*61c4878aSAndroid Build Coastguard Workerpw_multisink 5*61c4878aSAndroid Build Coastguard Worker============ 6*61c4878aSAndroid Build Coastguard WorkerThis is an module that forwards messages to multiple attached sinks, which 7*61c4878aSAndroid Build Coastguard Workerconsume messages asynchronously. It is not ready for use and is under 8*61c4878aSAndroid Build Coastguard Workerconstruction. 9*61c4878aSAndroid Build Coastguard Worker 10*61c4878aSAndroid Build Coastguard WorkerModule Configuration Options 11*61c4878aSAndroid Build Coastguard Worker============================ 12*61c4878aSAndroid Build Coastguard WorkerThe following configurations can be adjusted via compile-time configuration 13*61c4878aSAndroid Build Coastguard Workerof this module, see the 14*61c4878aSAndroid Build Coastguard Worker:ref:`module documentation <module-structure-compile-time-configuration>` for 15*61c4878aSAndroid Build Coastguard Workermore details. 16*61c4878aSAndroid Build Coastguard Worker 17*61c4878aSAndroid Build Coastguard Worker.. c:macro:: PW_MULTISINK_CONFIG_LOCK_TYPE 18*61c4878aSAndroid Build Coastguard Worker 19*61c4878aSAndroid Build Coastguard Worker Set to configure the underlying lock used to guard multisink read/write 20*61c4878aSAndroid Build Coastguard Worker operations. By default, this is set to uses an interrupt spin-lock to guard 21*61c4878aSAndroid Build Coastguard Worker the multisink transactions. Should be set to one of the following options: 22*61c4878aSAndroid Build Coastguard Worker 23*61c4878aSAndroid Build Coastguard Worker - PW_MULTISINK_INTERRUPT_SPIN_LOCK: Use an interrupt spin lock 24*61c4878aSAndroid Build Coastguard Worker - PW_MULTISINK_MUTEX: Use a mutex. Not safe to use multisink in interrupt 25*61c4878aSAndroid Build Coastguard Worker context 26*61c4878aSAndroid Build Coastguard Worker - PW_MULTISINK_VIRTUAL_LOCK: User provided locking implementation. Interrupt 27*61c4878aSAndroid Build Coastguard Worker support will be left to the user to manage. 28*61c4878aSAndroid Build Coastguard Worker 29*61c4878aSAndroid Build Coastguard Worker 30*61c4878aSAndroid Build Coastguard Worker.. _module-pw_multisink-late_drain_attach: 31*61c4878aSAndroid Build Coastguard Worker 32*61c4878aSAndroid Build Coastguard WorkerLate Drain Attach 33*61c4878aSAndroid Build Coastguard Worker================= 34*61c4878aSAndroid Build Coastguard WorkerIt is possible to push entries or inform the multisink of drops before any 35*61c4878aSAndroid Build Coastguard Workerdrains are attached to it, allowing you to defer the creation of the drain 36*61c4878aSAndroid Build Coastguard Workerfurther into an application. The multisink maintains the location and drop 37*61c4878aSAndroid Build Coastguard Workercount of the oldest drain and will set drains to match on attachment. This 38*61c4878aSAndroid Build Coastguard Workerpermits drains that are attached late to still consume any entries that were 39*61c4878aSAndroid Build Coastguard Workerpushed into the ring buffer, so long as those entries have not yet been evicted 40*61c4878aSAndroid Build Coastguard Workerby newer entries. This may be particularly useful in early-boot scenarios where 41*61c4878aSAndroid Build Coastguard Workerdrain consumers may need time to initialize their output paths. Listeners are 42*61c4878aSAndroid Build Coastguard Workernotified immediately when attached, to allow late drain users to consume 43*61c4878aSAndroid Build Coastguard Workerexisting entries. If draining in response to the notification, ensure that 44*61c4878aSAndroid Build Coastguard Workerthe drain is attached prior to registering the listener; attempting to drain 45*61c4878aSAndroid Build Coastguard Workerwhen unattached will crash. 46*61c4878aSAndroid Build Coastguard Worker 47*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 48*61c4878aSAndroid Build Coastguard Worker 49*61c4878aSAndroid Build Coastguard Worker // Create a multisink during global construction. 50*61c4878aSAndroid Build Coastguard Worker std::byte buffer[1024]; 51*61c4878aSAndroid Build Coastguard Worker MultiSink multisink(buffer); 52*61c4878aSAndroid Build Coastguard Worker 53*61c4878aSAndroid Build Coastguard Worker int main() { 54*61c4878aSAndroid Build Coastguard Worker // Do some initialization work for the application that pushes information 55*61c4878aSAndroid Build Coastguard Worker // into the multisink. 56*61c4878aSAndroid Build Coastguard Worker multisink.HandleEntry("Booting up!"); 57*61c4878aSAndroid Build Coastguard Worker Initialize(); 58*61c4878aSAndroid Build Coastguard Worker 59*61c4878aSAndroid Build Coastguard Worker multisink.HandleEntry("Prepare I/O!"); 60*61c4878aSAndroid Build Coastguard Worker PrepareIO(); 61*61c4878aSAndroid Build Coastguard Worker 62*61c4878aSAndroid Build Coastguard Worker // Start a thread to process logs in multisink. 63*61c4878aSAndroid Build Coastguard Worker StartLoggingThread(); 64*61c4878aSAndroid Build Coastguard Worker } 65*61c4878aSAndroid Build Coastguard Worker 66*61c4878aSAndroid Build Coastguard Worker void StartLoggingThread() { 67*61c4878aSAndroid Build Coastguard Worker MultiSink::Drain drain; 68*61c4878aSAndroid Build Coastguard Worker multisink.AttachDrain(drain); 69*61c4878aSAndroid Build Coastguard Worker 70*61c4878aSAndroid Build Coastguard Worker std::byte read_buffer[512]; 71*61c4878aSAndroid Build Coastguard Worker uint32_t drop_count = 0; 72*61c4878aSAndroid Build Coastguard Worker do { 73*61c4878aSAndroid Build Coastguard Worker Result<ConstByteSpan> entry = drain.PopEntry(read_buffer, drop_count); 74*61c4878aSAndroid Build Coastguard Worker if (drop_count > 0) { 75*61c4878aSAndroid Build Coastguard Worker StringBuilder<32> sb; 76*61c4878aSAndroid Build Coastguard Worker sb.Format("Dropped %d entries.", drop_count); 77*61c4878aSAndroid Build Coastguard Worker // Note: PrintByteArray is not a provided utility function. 78*61c4878aSAndroid Build Coastguard Worker PrintByteArray(sb.as_bytes()); 79*61c4878aSAndroid Build Coastguard Worker } 80*61c4878aSAndroid Build Coastguard Worker 81*61c4878aSAndroid Build Coastguard Worker // Iterate through the entries, this will print out: 82*61c4878aSAndroid Build Coastguard Worker // "Booting up!" 83*61c4878aSAndroid Build Coastguard Worker // "Prepare I/O!" 84*61c4878aSAndroid Build Coastguard Worker // 85*61c4878aSAndroid Build Coastguard Worker // Even though the drain was attached after entries were pushed into the 86*61c4878aSAndroid Build Coastguard Worker // multisink, this drain will still be able to consume those entries. 87*61c4878aSAndroid Build Coastguard Worker // 88*61c4878aSAndroid Build Coastguard Worker // Note: PrintByteArray is not a provided utility function. 89*61c4878aSAndroid Build Coastguard Worker if (entry.status().ok()) { 90*61c4878aSAndroid Build Coastguard Worker PrintByteArray(read_buffer); 91*61c4878aSAndroid Build Coastguard Worker } 92*61c4878aSAndroid Build Coastguard Worker } while (true); 93*61c4878aSAndroid Build Coastguard Worker } 94*61c4878aSAndroid Build Coastguard Worker 95*61c4878aSAndroid Build Coastguard WorkerIterator 96*61c4878aSAndroid Build Coastguard Worker======== 97*61c4878aSAndroid Build Coastguard WorkerIt may be useful to access the entries in the underlying buffer when no drains 98*61c4878aSAndroid Build Coastguard Workerare attached or in crash contexts where dumping out all entries is desirable, 99*61c4878aSAndroid Build Coastguard Workereven if those entries were previously consumed by a drain. This module provides 100*61c4878aSAndroid Build Coastguard Workeran iteration class that is thread-unsafe and like standard iterators, assumes 101*61c4878aSAndroid Build Coastguard Workerthat the buffer is not being mutated while iterating. A 102*61c4878aSAndroid Build Coastguard Worker``MultiSink::UnsafeIterationWrapper`` class that supports range-based for-loop 103*61c4878aSAndroid Build Coastguard Workerusage can be acquired via ``MultiSink::UnsafeIteration()``. 104*61c4878aSAndroid Build Coastguard Worker 105*61c4878aSAndroid Build Coastguard WorkerThe iterator starts from the oldest available entry in the buffer, regardless of 106*61c4878aSAndroid Build Coastguard Workerwhether all attached drains have already consumed that entry. This allows the 107*61c4878aSAndroid Build Coastguard Workeriterator to be used even if no drains have been previously attached. 108*61c4878aSAndroid Build Coastguard Worker 109*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 110*61c4878aSAndroid Build Coastguard Worker 111*61c4878aSAndroid Build Coastguard Worker // Create a multisink and a test string to push into it. 112*61c4878aSAndroid Build Coastguard Worker constexpr char kExampleEntry[] = "Example!"; 113*61c4878aSAndroid Build Coastguard Worker std::byte buffer[1024]; 114*61c4878aSAndroid Build Coastguard Worker MultiSink multisink(buffer); 115*61c4878aSAndroid Build Coastguard Worker MultiSink::Drain drain; 116*61c4878aSAndroid Build Coastguard Worker 117*61c4878aSAndroid Build Coastguard Worker // Push an entry before a drain is attached. 118*61c4878aSAndroid Build Coastguard Worker multisink.HandleEntry(kExampleEntry); 119*61c4878aSAndroid Build Coastguard Worker multisink.HandleEntry(kExampleEntry); 120*61c4878aSAndroid Build Coastguard Worker 121*61c4878aSAndroid Build Coastguard Worker // Iterate through the entries, this will print out: 122*61c4878aSAndroid Build Coastguard Worker // "Example!" 123*61c4878aSAndroid Build Coastguard Worker // "Example!" 124*61c4878aSAndroid Build Coastguard Worker // Note: PrintByteArray is not a provided utility function. 125*61c4878aSAndroid Build Coastguard Worker for (ConstByteSpan entry : multisink.UnsafeIteration()) { 126*61c4878aSAndroid Build Coastguard Worker PrintByteArray(entry); 127*61c4878aSAndroid Build Coastguard Worker } 128*61c4878aSAndroid Build Coastguard Worker 129*61c4878aSAndroid Build Coastguard Worker // Attach a drain and consume only one of the entries. 130*61c4878aSAndroid Build Coastguard Worker std::byte read_buffer[512]; 131*61c4878aSAndroid Build Coastguard Worker uint32_t drop_count = 0; 132*61c4878aSAndroid Build Coastguard Worker 133*61c4878aSAndroid Build Coastguard Worker multisink.AttachDrain(drain); 134*61c4878aSAndroid Build Coastguard Worker drain.PopEntry(read_buffer, drop_count); 135*61c4878aSAndroid Build Coastguard Worker 136*61c4878aSAndroid Build Coastguard Worker // !! A function causes a crash before we've read out all entries. 137*61c4878aSAndroid Build Coastguard Worker FunctionThatCrashes(); 138*61c4878aSAndroid Build Coastguard Worker 139*61c4878aSAndroid Build Coastguard Worker // ... Crash Context ... 140*61c4878aSAndroid Build Coastguard Worker 141*61c4878aSAndroid Build Coastguard Worker // You can use a range-based for-loop to walk through all entries, 142*61c4878aSAndroid Build Coastguard Worker // even though the attached drain has consumed one of them. 143*61c4878aSAndroid Build Coastguard Worker // This will also print out: 144*61c4878aSAndroid Build Coastguard Worker // "Example!" 145*61c4878aSAndroid Build Coastguard Worker // "Example!" 146*61c4878aSAndroid Build Coastguard Worker for (ConstByteSpan entry : multisink.UnsafeIteration()) { 147*61c4878aSAndroid Build Coastguard Worker PrintByteArray(entry); 148*61c4878aSAndroid Build Coastguard Worker } 149*61c4878aSAndroid Build Coastguard Worker 150*61c4878aSAndroid Build Coastguard WorkerAs an alternative to using the ``UnsafeIterationWrapper``, 151*61c4878aSAndroid Build Coastguard Worker``MultiSink::UnsafeForEachEntry()`` may be used to run a callback for each 152*61c4878aSAndroid Build Coastguard Workerentry in the buffer. This helper also provides a way to limit the iteration to 153*61c4878aSAndroid Build Coastguard Workerthe ``N`` most recent entries. In certain cases such as when there isn't 154*61c4878aSAndroid Build Coastguard Workerenough space to copy the entire buffer, it is desirable to capture 155*61c4878aSAndroid Build Coastguard Workerthe latest entries rather than the first entries. In this case 156*61c4878aSAndroid Build Coastguard Worker``MultiSink::UnsafeForEachEntryFromEnd`` can be used. 157*61c4878aSAndroid Build Coastguard Worker 158*61c4878aSAndroid Build Coastguard WorkerPeek & Pop 159*61c4878aSAndroid Build Coastguard Worker========== 160*61c4878aSAndroid Build Coastguard WorkerA drain can peek the front multisink entry without removing it using 161*61c4878aSAndroid Build Coastguard Worker`PeekEntry`, which is the same as `PopEntry` without removing the entry from the 162*61c4878aSAndroid Build Coastguard Workermultisink. Once the drain is done with the peeked entry, `PopEntry` will tell 163*61c4878aSAndroid Build Coastguard Workerthe drain to remove the peeked entry from the multisink and advance one entry. 164*61c4878aSAndroid Build Coastguard Worker 165*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 166*61c4878aSAndroid Build Coastguard Worker 167*61c4878aSAndroid Build Coastguard Worker constexpr char kExampleEntry[] = "Example!"; 168*61c4878aSAndroid Build Coastguard Worker std::byte buffer[1024]; 169*61c4878aSAndroid Build Coastguard Worker MultiSink multisink(buffer); 170*61c4878aSAndroid Build Coastguard Worker MultiSink::Drain drain; 171*61c4878aSAndroid Build Coastguard Worker 172*61c4878aSAndroid Build Coastguard Worker multisink.AttachDrain(drain); 173*61c4878aSAndroid Build Coastguard Worker multisink.HandleEntry(kExampleEntry); 174*61c4878aSAndroid Build Coastguard Worker 175*61c4878aSAndroid Build Coastguard Worker std::byte read_buffer[512]; 176*61c4878aSAndroid Build Coastguard Worker uint32_t drop_count = 0; 177*61c4878aSAndroid Build Coastguard Worker Result<PeekedEntry> peeked_entry = drain.PeekEntry(read_buffer, drop_count); 178*61c4878aSAndroid Build Coastguard Worker // ... Handle drop_count ... 179*61c4878aSAndroid Build Coastguard Worker 180*61c4878aSAndroid Build Coastguard Worker if (peeked_entry.ok()) { 181*61c4878aSAndroid Build Coastguard Worker // Note: SendByteArray is not a provided utility function. 182*61c4878aSAndroid Build Coastguard Worker Status send_status = SendByteArray(peeked_entry.value().entry()); 183*61c4878aSAndroid Build Coastguard Worker if (send_status.ok()) { 184*61c4878aSAndroid Build Coastguard Worker drain.PopEntry(peeked_entry.value()); 185*61c4878aSAndroid Build Coastguard Worker } else { 186*61c4878aSAndroid Build Coastguard Worker // ... Handle send error ... 187*61c4878aSAndroid Build Coastguard Worker } 188*61c4878aSAndroid Build Coastguard Worker } 189*61c4878aSAndroid Build Coastguard Worker 190*61c4878aSAndroid Build Coastguard WorkerDrop Counts 191*61c4878aSAndroid Build Coastguard Worker=========== 192*61c4878aSAndroid Build Coastguard WorkerThe `PeekEntry` and `PopEntry` return two different drop counts, one for the 193*61c4878aSAndroid Build Coastguard Workernumber of entries a drain was skipped forward for providing a small buffer or 194*61c4878aSAndroid Build Coastguard Workerdraining too slow, and the other for entries that failed to be added to the 195*61c4878aSAndroid Build Coastguard WorkerMultiSink. 196*61c4878aSAndroid Build Coastguard Worker 197*61c4878aSAndroid Build Coastguard WorkerZephyr 198*61c4878aSAndroid Build Coastguard Worker====== 199*61c4878aSAndroid Build Coastguard WorkerTo enable `pw_multisink` with Zephyr use the following Kconfigs: 200*61c4878aSAndroid Build Coastguard Worker- `CONFIG_PIGWEED_MULTISINK` to link `pw_multisink` into your Zephyr build 201*61c4878aSAndroid Build Coastguard Worker- `CONFIG_PIGWEED_MULTISINK_UTIL` to link `pw_multisink.util` 202*61c4878aSAndroid Build Coastguard Worker 203*61c4878aSAndroid Build Coastguard WorkerTo enable the Pigweed config value `PW_MULTISINK_CONFIG_LOCK_INTERRUPT_SAFE`, use 204*61c4878aSAndroid Build Coastguard Worker`CONFIG_PIGWEED_MULTISINK_LOCK_INTERRUPT_SAFE`. 205