xref: /aosp_15_r20/external/pigweed/pw_multisink/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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