Prashanth Swaminathan | f36832a | 2021-01-27 16:20:32 -0800 | [diff] [blame] | 1 | .. _module-pw_multisink: |
| 2 | |
Prashanth Swaminathan | 76cd481 | 2021-05-17 11:11:56 -0700 | [diff] [blame] | 3 | ============ |
Prashanth Swaminathan | f36832a | 2021-01-27 16:20:32 -0800 | [diff] [blame] | 4 | pw_multisink |
Prashanth Swaminathan | 76cd481 | 2021-05-17 11:11:56 -0700 | [diff] [blame] | 5 | ============ |
Prashanth Swaminathan | f36832a | 2021-01-27 16:20:32 -0800 | [diff] [blame] | 6 | This is an module that forwards messages to multiple attached sinks, which |
| 7 | consume messages asynchronously. It is not ready for use and is under |
| 8 | construction. |
| 9 | |
Prashanth Swaminathan | 76cd481 | 2021-05-17 11:11:56 -0700 | [diff] [blame] | 10 | Module Configuration Options |
| 11 | ============================ |
| 12 | The following configurations can be adjusted via compile-time configuration |
| 13 | of this module, see the |
| 14 | :ref:`module documentation <module-structure-compile-time-configuration>` for |
| 15 | more details. |
| 16 | |
| 17 | .. c:macro:: PW_MULTISINK_CONFIG_LOCK_INTERRUPT_SAFE |
| 18 | |
Prashanth Swaminathan | 16541e7 | 2021-06-21 09:22:25 -0700 | [diff] [blame] | 19 | Whether an interrupt-safe lock is used to guard multisink read/write |
| 20 | operations. |
Prashanth Swaminathan | 76cd481 | 2021-05-17 11:11:56 -0700 | [diff] [blame] | 21 | |
Prashanth Swaminathan | 16541e7 | 2021-06-21 09:22:25 -0700 | [diff] [blame] | 22 | By default, this option is enabled and the multisink uses an interrupt |
| 23 | spin-lock to guard its transactions. If disabled, a mutex is used instead. |
Prashanth Swaminathan | 76cd481 | 2021-05-17 11:11:56 -0700 | [diff] [blame] | 24 | |
Prashanth Swaminathan | 16541e7 | 2021-06-21 09:22:25 -0700 | [diff] [blame] | 25 | Disabling this will alter the entry precondition of the multisink, |
| 26 | requiring that it not be called from an interrupt context. |
| 27 | |
Prashanth Swaminathan | 099f716 | 2021-07-15 13:42:20 -0700 | [diff] [blame] | 28 | Late Drain Attach |
| 29 | ================= |
| 30 | It is possible to push entries or inform the multisink of drops before any |
| 31 | drains are attached to it, allowing you to defer the creation of the drain |
| 32 | further into an application. The multisink maintains the location and drop |
| 33 | count of the oldest drain and will set drains to match on attachment. This |
| 34 | permits drains that are attached late to still consume any entries that were |
| 35 | pushed into the ring buffer, so long as those entries have not yet been evicted |
| 36 | by newer entries. This may be particularly useful in early-boot scenarios where |
| 37 | drain consumers may need time to initialize their output paths. |
| 38 | |
| 39 | .. code-block:: cpp |
| 40 | |
| 41 | // Create a multisink during global construction. |
| 42 | std::byte buffer[1024]; |
| 43 | MultiSink multisink(buffer); |
| 44 | |
| 45 | int main() { |
| 46 | // Do some initialization work for the application that pushes information |
| 47 | // into the multisink. |
| 48 | multisink.HandleEntry("Booting up!"); |
| 49 | Initialize(); |
| 50 | |
| 51 | multisink.HandleEntry("Prepare I/O!"); |
| 52 | PrepareIO(); |
| 53 | |
| 54 | // Start a thread to process logs in multisink. |
| 55 | StartLoggingThread(); |
| 56 | } |
| 57 | |
| 58 | void StartLoggingThread() { |
| 59 | MultiSink::Drain drain; |
| 60 | multisink.AttachDrain(drain); |
| 61 | |
| 62 | std::byte read_buffer[512]; |
| 63 | uint32_t drop_count = 0; |
| 64 | do { |
| 65 | Result<ConstByteSpan> entry = multisink.GetEntry(read_buffer, drop_count); |
| 66 | if (drop_count > 0) { |
| 67 | StringBuilder<32> sb; |
| 68 | sb.Format("Dropped %d entries.", drop_count); |
| 69 | // Note: PrintByteArray is not a provided utility function. |
| 70 | PrintByteArray(sb.as_bytes()); |
| 71 | } |
| 72 | |
| 73 | // Iterate through the entries, this will print out: |
| 74 | // "Booting up!" |
| 75 | // "Prepare I/O!" |
| 76 | // |
| 77 | // Even though the drain was attached after entries were pushed into the |
| 78 | // multisink, this drain will still be able to consume those entries. |
| 79 | // |
| 80 | // Note: PrintByteArray is not a provided utility function. |
| 81 | if (entry.status().ok()) { |
| 82 | PrintByteArray(read_buffer); |
| 83 | } |
| 84 | } while (true); |
| 85 | } |
| 86 | |
Prashanth Swaminathan | 16541e7 | 2021-06-21 09:22:25 -0700 | [diff] [blame] | 87 | Iterator |
| 88 | ======== |
| 89 | It may be useful to access the entries in the underlying buffer when no drains |
| 90 | are attached or in crash contexts where dumping out all entries is desirable, |
| 91 | even if those entries were previously consumed by a drain. This module provides |
| 92 | an iteration class that is thread-unsafe and like standard iterators, assumes |
| 93 | that the buffer is not being mutated while iterating. A |
| 94 | `MultiSink::UnsafeIterationWrapper` class that supports range-based for-loop |
| 95 | usage canbe acquired via `MultiSink::UnsafeIteration()`. |
| 96 | |
| 97 | The iterator starts from the oldest available entry in the buffer, regardless of |
| 98 | whether all attached drains have already consumed that entry. This allows the |
| 99 | iterator to be used even if no drains have been previously attached. |
| 100 | |
| 101 | .. code-block:: cpp |
| 102 | |
| 103 | // Create a multisink and a test string to push into it. |
| 104 | constexpr char kExampleEntry[] = "Example!"; |
| 105 | std::byte buffer[1024]; |
| 106 | MultiSink multisink(buffer); |
| 107 | MultiSink::Drain drain; |
| 108 | |
| 109 | // Push an entry before a drain is attached. |
| 110 | multisink.HandleEntry(kExampleEntry); |
| 111 | multisink.HandleEntry(kExampleEntry); |
| 112 | |
| 113 | // Iterate through the entries, this will print out: |
| 114 | // "Example!" |
| 115 | // "Example!" |
| 116 | // Note: PrintByteArray is not a provided utility function. |
| 117 | for (ConstByteSpan entry : multisink.UnsafeIteration()) { |
| 118 | PrintByteArray(entry); |
| 119 | } |
| 120 | |
| 121 | // Attach a drain and consume only one of the entries. |
| 122 | std::byte read_buffer[512]; |
| 123 | uint32_t drop_count = 0; |
| 124 | |
| 125 | multisink.AttachDrain(drain); |
| 126 | drain.GetEntry(read_buffer, drop_count); |
| 127 | |
| 128 | // !! A function causes a crash before we've read out all entries. |
| 129 | FunctionThatCrashes(); |
| 130 | |
| 131 | // ... Crash Context ... |
| 132 | |
| 133 | // You can use a range-based for-loop to walk through all entries, |
| 134 | // even though the attached drain has consumed one of them. |
| 135 | // This will also print out: |
| 136 | // "Example!" |
| 137 | // "Example!" |
| 138 | for (ConstByteSpan entry : multisink.UnsafeIteration()) { |
| 139 | PrintByteArray(entry); |
| 140 | } |