blob: 70eb0b50ecb68c8a0a9887b8c7bfe3759fd79779 [file] [log] [blame]
Prashanth Swaminathanf36832a2021-01-27 16:20:32 -08001.. _module-pw_multisink:
2
Prashanth Swaminathan76cd4812021-05-17 11:11:56 -07003============
Prashanth Swaminathanf36832a2021-01-27 16:20:32 -08004pw_multisink
Prashanth Swaminathan76cd4812021-05-17 11:11:56 -07005============
Prashanth Swaminathanf36832a2021-01-27 16:20:32 -08006This is an module that forwards messages to multiple attached sinks, which
7consume messages asynchronously. It is not ready for use and is under
8construction.
9
Prashanth Swaminathan76cd4812021-05-17 11:11:56 -070010Module Configuration Options
11============================
12The following configurations can be adjusted via compile-time configuration
13of this module, see the
14:ref:`module documentation <module-structure-compile-time-configuration>` for
15more details.
16
17.. c:macro:: PW_MULTISINK_CONFIG_LOCK_INTERRUPT_SAFE
18
Prashanth Swaminathan16541e72021-06-21 09:22:25 -070019 Whether an interrupt-safe lock is used to guard multisink read/write
20 operations.
Prashanth Swaminathan76cd4812021-05-17 11:11:56 -070021
Prashanth Swaminathan16541e72021-06-21 09:22:25 -070022 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 Swaminathan76cd4812021-05-17 11:11:56 -070024
Prashanth Swaminathan16541e72021-06-21 09:22:25 -070025 Disabling this will alter the entry precondition of the multisink,
26 requiring that it not be called from an interrupt context.
27
Prashanth Swaminathan099f7162021-07-15 13:42:20 -070028Late Drain Attach
29=================
30It is possible to push entries or inform the multisink of drops before any
31drains are attached to it, allowing you to defer the creation of the drain
32further into an application. The multisink maintains the location and drop
33count of the oldest drain and will set drains to match on attachment. This
34permits drains that are attached late to still consume any entries that were
35pushed into the ring buffer, so long as those entries have not yet been evicted
36by newer entries. This may be particularly useful in early-boot scenarios where
37drain 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 Swaminathan16541e72021-06-21 09:22:25 -070087Iterator
88========
89It may be useful to access the entries in the underlying buffer when no drains
90are attached or in crash contexts where dumping out all entries is desirable,
91even if those entries were previously consumed by a drain. This module provides
92an iteration class that is thread-unsafe and like standard iterators, assumes
93that the buffer is not being mutated while iterating. A
94`MultiSink::UnsafeIterationWrapper` class that supports range-based for-loop
95usage canbe acquired via `MultiSink::UnsafeIteration()`.
96
97The iterator starts from the oldest available entry in the buffer, regardless of
98whether all attached drains have already consumed that entry. This allows the
99iterator 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 }