1*61c4878aSAndroid Build Coastguard Worker.. _seed-0124: 2*61c4878aSAndroid Build Coastguard Worker 3*61c4878aSAndroid Build Coastguard Worker=============================================================== 4*61c4878aSAndroid Build Coastguard Worker0124: Interfaces for Retrieving Size Information from Multisink 5*61c4878aSAndroid Build Coastguard Worker=============================================================== 6*61c4878aSAndroid Build Coastguard Worker.. seed:: 7*61c4878aSAndroid Build Coastguard Worker :number: 124 8*61c4878aSAndroid Build Coastguard Worker :name: Interfaces for Retrieving Size Information from Multisink 9*61c4878aSAndroid Build Coastguard Worker :status: open_for_comments 10*61c4878aSAndroid Build Coastguard Worker :proposal_date: 2024-01-22 11*61c4878aSAndroid Build Coastguard Worker :cl: 188670 12*61c4878aSAndroid Build Coastguard Worker :authors: Jiacheng Lu 13*61c4878aSAndroid Build Coastguard Worker :facilitator: Carlos Chinchilla 14*61c4878aSAndroid Build Coastguard Worker 15*61c4878aSAndroid Build Coastguard Worker------- 16*61c4878aSAndroid Build Coastguard WorkerSummary 17*61c4878aSAndroid Build Coastguard Worker------- 18*61c4878aSAndroid Build Coastguard WorkerThis SEED proposes adding interfaces to :ref:`module-pw_multisink` to retrieve 19*61c4878aSAndroid Build Coastguard Workerthe capacity and filled size from its underlying buffer. 20*61c4878aSAndroid Build Coastguard Worker 21*61c4878aSAndroid Build Coastguard Worker---------- 22*61c4878aSAndroid Build Coastguard WorkerMotivation 23*61c4878aSAndroid Build Coastguard Worker---------- 24*61c4878aSAndroid Build Coastguard WorkerCurrently, ``pw_multisink`` provides ``Listener`` to help schedule draining of 25*61c4878aSAndroid Build Coastguard Workerentries. Interfaces of ``pw_multisink`` and its ``Listener`` provides no 26*61c4878aSAndroid Build Coastguard Workerinformation about capacity or consumed size of the underlying buffer. 27*61c4878aSAndroid Build Coastguard Worker 28*61c4878aSAndroid Build Coastguard WorkerBy adding interfaces to ``pw_multisink`` to provide size information of its 29*61c4878aSAndroid Build Coastguard Workerunderlying buffer, scheduling policies can be implemented to trigger draining 30*61c4878aSAndroid Build Coastguard Workerbased on, for example, the size of unread data in the buffer. 31*61c4878aSAndroid Build Coastguard Worker 32*61c4878aSAndroid Build Coastguard Worker-------- 33*61c4878aSAndroid Build Coastguard WorkerProposal 34*61c4878aSAndroid Build Coastguard Worker-------- 35*61c4878aSAndroid Build Coastguard Worker 36*61c4878aSAndroid Build Coastguard WorkerAdd new interface ``UnsafeGetUnreadEntriesSize()`` to 37*61c4878aSAndroid Build Coastguard Worker``pw_multisink::MultiSink::Drain`` that reports size of unread data in the 38*61c4878aSAndroid Build Coastguard Workerunderlying buffer. 39*61c4878aSAndroid Build Coastguard Worker 40*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 41*61c4878aSAndroid Build Coastguard Worker 42*61c4878aSAndroid Build Coastguard Worker class MutilSink { 43*61c4878aSAndroid Build Coastguard Worker public: 44*61c4878aSAndroid Build Coastguard Worker ... 45*61c4878aSAndroid Build Coastguard Worker 46*61c4878aSAndroid Build Coastguard Worker class Drain { 47*61c4878aSAndroid Build Coastguard Worker ... 48*61c4878aSAndroid Build Coastguard Worker 49*61c4878aSAndroid Build Coastguard Worker // Both two APIs beturn size in bytes of the valid data in the 50*61c4878aSAndroid Build Coastguard Worker // underlying buffer that has not been read by this drain. 51*61c4878aSAndroid Build Coastguard Worker 52*61c4878aSAndroid Build Coastguard Worker // This is a thread unsafe version. It requires the `lock` of 53*61c4878aSAndroid Build Coastguard Worker // `multisink` being held. For example, it can be used inside 54*61c4878aSAndroid Build Coastguard Worker // Listeners' methods, where lock already held, to check size 55*61c4878aSAndroid Build Coastguard Worker // on handling each new entry. 56*61c4878aSAndroid Build Coastguard Worker size_t UnsafeGetUnreadEntriesSize() PW_NO_LOCK_SAFETY_ANALYSIS; 57*61c4878aSAndroid Build Coastguard Worker 58*61c4878aSAndroid Build Coastguard Worker // A thread-safe verson. 59*61c4878aSAndroid Build Coastguard Worker size_t GetUnreadEntriesSize() PW_LOCKS_EXCLUDED(multisink_->lock_); 60*61c4878aSAndroid Build Coastguard Worker 61*61c4878aSAndroid Build Coastguard Worker ... 62*61c4878aSAndroid Build Coastguard Worker 63*61c4878aSAndroid Build Coastguard Worker protected: 64*61c4878aSAndroid Build Coastguard Worker friend class MultiSink; 65*61c4878aSAndroid Build Coastguard Worker 66*61c4878aSAndroid Build Coastguard Worker MultiSink* multisink_; 67*61c4878aSAndroid Build Coastguard Worker }; 68*61c4878aSAndroid Build Coastguard Worker 69*61c4878aSAndroid Build Coastguard Worker ... 70*61c4878aSAndroid Build Coastguard Worker 71*61c4878aSAndroid Build Coastguard Worker private: 72*61c4878aSAndroid Build Coastguard Worker LockType lock_; 73*61c4878aSAndroid Build Coastguard Worker }; 74*61c4878aSAndroid Build Coastguard Worker 75*61c4878aSAndroid Build Coastguard Worker 76*61c4878aSAndroid Build Coastguard Worker--------------------- 77*61c4878aSAndroid Build Coastguard WorkerProblem investigation 78*61c4878aSAndroid Build Coastguard Worker--------------------- 79*61c4878aSAndroid Build Coastguard Worker``pw_multisink`` is a popular choice to buffer data from logs for Pigweed based 80*61c4878aSAndroid Build Coastguard Workersoftwares. An example is :ref:`module-pw_log_rpc` where an instance of 81*61c4878aSAndroid Build Coastguard Worker``pw_multisink`` is used to buffer logs before they are drained and transmitted. 82*61c4878aSAndroid Build Coastguard WorkerMake sure ``pw_multisink`` are drained in time is important to reduce the 83*61c4878aSAndroid Build Coastguard Workerchances of dropping logs because of running out of space. 84*61c4878aSAndroid Build Coastguard WorkerHowever, the draining and transmission behavior may be constrained by the 85*61c4878aSAndroid Build Coastguard Workerproperty of underlying physical channels. For certain physical channels, there 86*61c4878aSAndroid Build Coastguard Workerare tradeoffs between frequency of transmission and costs. 87*61c4878aSAndroid Build Coastguard Worker 88*61c4878aSAndroid Build Coastguard WorkerPCI is one of the example of physical channels that have the above mentioned 89*61c4878aSAndroid Build Coastguard Workertradeoffs. PCI implementation normally have different levels of power states for 90*61c4878aSAndroid Build Coastguard Workerpower efficiency. Transmitting data over PCI continuously may result to it not 91*61c4878aSAndroid Build Coastguard Workerbeing able to enter deep sleep states. Also, PCI are normally have a high 92*61c4878aSAndroid Build Coastguard Workertransmission bandwidth. Buffering data to a certain threshold and then 93*61c4878aSAndroid Build Coastguard Workersending them all over in a single transmission fits better with its design. 94*61c4878aSAndroid Build Coastguard Worker 95*61c4878aSAndroid Build Coastguard WorkerThe ``OnNewEntryAvailable`` on the current ``Listener`` interfaces does not 96*61c4878aSAndroid Build Coastguard Workerprovide enough information about buffered data size because the current 97*61c4878aSAndroid Build Coastguard Workerimplementation of underlying buffer is :ref:`module-pw_ring_buffer`, it stores 98*61c4878aSAndroid Build Coastguard Workeradditional, variable lenghted data for its internal for each entry. 99*61c4878aSAndroid Build Coastguard Worker 100*61c4878aSAndroid Build Coastguard Worker 101*61c4878aSAndroid Build Coastguard WorkerAssuming the proposed interface is avaible, the imagined use case looks like: 102*61c4878aSAndroid Build Coastguard Worker 103*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 104*61c4878aSAndroid Build Coastguard Worker 105*61c4878aSAndroid Build Coastguard Worker class DrainThread: public pw::thread::ThreadCore, 106*61c4878aSAndroid Build Coastguard Worker public pw::multisink::MultiSink::Listener { 107*61c4878aSAndroid Build Coastguard Worker public: 108*61c4878aSAndroid Build Coastguard Worker 109*61c4878aSAndroid Build Coastguard Worker ... // initialize with drain 110*61c4878aSAndroid Build Coastguard Worker 111*61c4878aSAndroid Build Coastguard Worker bool NeedFlush(size_t unread_entries_size) { 112*61c4878aSAndroid Build Coastguard Worker ... 113*61c4878aSAndroid Build Coastguard Worker } 114*61c4878aSAndroid Build Coastguard Worker 115*61c4878aSAndroid Build Coastguard Worker void Flush(pw::multisink::MultiSink::Drain& drain) { 116*61c4878aSAndroid Build Coastguard Worker ... 117*61c4878aSAndroid Build Coastguard Worker } 118*61c4878aSAndroid Build Coastguard Worker 119*61c4878aSAndroid Build Coastguard Worker void OnNewEntryAvailable() override { 120*61c4878aSAndroid Build Coastguard Worker if (NeedFlush(drain_.UnsafeGetUnreadEntriesSize())) { 121*61c4878aSAndroid Build Coastguard Worker flush_threshold_reached_notification_.release(); 122*61c4878aSAndroid Build Coastguard Worker } 123*61c4878aSAndroid Build Coastguard Worker } 124*61c4878aSAndroid Build Coastguard Worker 125*61c4878aSAndroid Build Coastguard Worker void Run() override { 126*61c4878aSAndroid Build Coastguard Worker multisink_.AttachListner(*this); 127*61c4878aSAndroid Build Coastguard Worker 128*61c4878aSAndroid Build Coastguard Worker while (true) { 129*61c4878aSAndroid Build Coastguard Worker flush_threshold_reached_notification_.acquire(); 130*61c4878aSAndroid Build Coastguard Worker Flush(drain_); 131*61c4878aSAndroid Build Coastguard Worker } 132*61c4878aSAndroid Build Coastguard Worker } 133*61c4878aSAndroid Build Coastguard Worker 134*61c4878aSAndroid Build Coastguard Worker 135*61c4878aSAndroid Build Coastguard Worker private: 136*61c4878aSAndroid Build Coastguard Worker pw::multisink::MultiSink& multisink_; 137*61c4878aSAndroid Build Coastguard Worker pw::multisink::MultiSink::Drain& drain_; 138*61c4878aSAndroid Build Coastguard Worker pw::ThreadNotification flush_threshold_reached_notification_; 139*61c4878aSAndroid Build Coastguard Worker }; 140*61c4878aSAndroid Build Coastguard Worker 141*61c4878aSAndroid Build Coastguard Worker 142*61c4878aSAndroid Build Coastguard Worker--------------- 143*61c4878aSAndroid Build Coastguard WorkerDetailed design 144*61c4878aSAndroid Build Coastguard Worker--------------- 145*61c4878aSAndroid Build Coastguard Worker 146*61c4878aSAndroid Build Coastguard WorkerImplement ``EntriesSize()`` in 147*61c4878aSAndroid Build Coastguard Worker``pw_ring_buffer::PrefixedEntryRingBufferMulti::Reader`` to provide the number 148*61c4878aSAndroid Build Coastguard Workerof bytes between its reader pointer and ring buffer's writer pointer. 149*61c4878aSAndroid Build Coastguard Worker 150*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 151*61c4878aSAndroid Build Coastguard Worker 152*61c4878aSAndroid Build Coastguard Worker class PrefixedEntryRingBufferMulti { 153*61c4878aSAndroid Build Coastguard Worker class Reader : public IntrusiveList<Reader>::Item { 154*61c4878aSAndroid Build Coastguard Worker public: 155*61c4878aSAndroid Build Coastguard Worker 156*61c4878aSAndroid Build Coastguard Worker // Get the size of the unread entries currently in the ring buffer. 157*61c4878aSAndroid Build Coastguard Worker // Return value: 158*61c4878aSAndroid Build Coastguard Worker // Number of bytes 159*61c4878aSAndroid Build Coastguard Worker size_t EntriesSize() const { 160*61c4878aSAndroid Build Coastguard Worker // Case: Not wrapped. 161*61c4878aSAndroid Build Coastguard Worker if (read_idx_ < buffer_->write_idx_) { 162*61c4878aSAndroid Build Coastguard Worker return buffer_->write_idx_ - read_idx_; 163*61c4878aSAndroid Build Coastguard Worker } 164*61c4878aSAndroid Build Coastguard Worker // Case: Wrapped. 165*61c4878aSAndroid Build Coastguard Worker if (read_idx_ > buffer_->write_idx_) { 166*61c4878aSAndroid Build Coastguard Worker return buffer_->buffer_bytes_ - (read_idx_ - buffer_->write_idx_); 167*61c4878aSAndroid Build Coastguard Worker } 168*61c4878aSAndroid Build Coastguard Worker 169*61c4878aSAndroid Build Coastguard Worker // No entries remaining. 170*61c4878aSAndroid Build Coastguard Worker if (entry_count_ == 0) { 171*61c4878aSAndroid Build Coastguard Worker return 0; 172*61c4878aSAndroid Build Coastguard Worker } 173*61c4878aSAndroid Build Coastguard Worker 174*61c4878aSAndroid Build Coastguard Worker return buffer_->buffer_bytes_; 175*61c4878aSAndroid Build Coastguard Worker } 176*61c4878aSAndroid Build Coastguard Worker 177*61c4878aSAndroid Build Coastguard Worker private: 178*61c4878aSAndroid Build Coastguard Worker PrefixedEntryRingBufferMulti* buffer_; 179*61c4878aSAndroid Build Coastguard Worker size_t read_idx_; 180*61c4878aSAndroid Build Coastguard Worker }; 181*61c4878aSAndroid Build Coastguard Worker 182*61c4878aSAndroid Build Coastguard Worker private: 183*61c4878aSAndroid Build Coastguard Worker size_t write_idx_; 184*61c4878aSAndroid Build Coastguard Worker size_t buffer_bytes_; 185*61c4878aSAndroid Build Coastguard Worker }; 186*61c4878aSAndroid Build Coastguard Worker 187*61c4878aSAndroid Build Coastguard Worker 188*61c4878aSAndroid Build Coastguard WorkerThe unread data size of ``Drain`` is directly fetched from ring buffer's 189*61c4878aSAndroid Build Coastguard Worker``Reader``. Because ``pw_multisink`` uses ``lock_`` to protect accesses to all 190*61c4878aSAndroid Build Coastguard Workerlisteners' methods already, in order to support calling the proposed interfaces 191*61c4878aSAndroid Build Coastguard Workerfrom listeners, this design introduces two versions of API, one thread-safe 192*61c4878aSAndroid Build Coastguard Workerversion that is intended to be used outside of listeners, and one thread-unsafe 193*61c4878aSAndroid Build Coastguard Workerversion requires that ``lock_`` of ``pw_multisink`` being held when invoking. 194*61c4878aSAndroid Build Coastguard Worker 195*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 196*61c4878aSAndroid Build Coastguard Worker 197*61c4878aSAndroid Build Coastguard Worker namespace pw { 198*61c4878aSAndroid Build Coastguard Worker namespace multisink { 199*61c4878aSAndroid Build Coastguard Worker 200*61c4878aSAndroid Build Coastguard Worker class MutilSink { 201*61c4878aSAndroid Build Coastguard Worker public: 202*61c4878aSAndroid Build Coastguard Worker ... 203*61c4878aSAndroid Build Coastguard Worker 204*61c4878aSAndroid Build Coastguard Worker class Drain { 205*61c4878aSAndroid Build Coastguard Worker public: 206*61c4878aSAndroid Build Coastguard Worker 207*61c4878aSAndroid Build Coastguard Worker // Both two APIs beturn size in bytes of the valid data in the 208*61c4878aSAndroid Build Coastguard Worker // underlying buffer that has not been read by this drain. 209*61c4878aSAndroid Build Coastguard Worker 210*61c4878aSAndroid Build Coastguard Worker // Ideally it should use annotation 211*61c4878aSAndroid Build Coastguard Worker // PW_EXCLUSIVE_LOCKS_REQUIRED(multisink_->lock_) 212*61c4878aSAndroid Build Coastguard Worker // however, Listener interfaces, where it is intended to be called, 213*61c4878aSAndroid Build Coastguard Worker // cannot be annotated using multisink's lock. Static analysis is not 214*61c4878aSAndroid Build Coastguard Worker // doable. 215*61c4878aSAndroid Build Coastguard Worker size_t UnsafeGetUnreadEntriesSize() PW_NO_LOCK_SAFETY_ANALYSIS { 216*61c4878aSAndroid Build Coastguard Worker return reader_.EntriesSize(); 217*61c4878aSAndroid Build Coastguard Worker } 218*61c4878aSAndroid Build Coastguard Worker 219*61c4878aSAndroid Build Coastguard Worker size_t GetUnreadEntriesSize() PW_LOCKS_EXCLUDED(multisink_->lock_) { 220*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(multisink_->lock_); 221*61c4878aSAndroid Build Coastguard Worker return UnsafeGetUnreadEntriesSize(); 222*61c4878aSAndroid Build Coastguard Worker } 223*61c4878aSAndroid Build Coastguard Worker 224*61c4878aSAndroid Build Coastguard Worker protected: 225*61c4878aSAndroid Build Coastguard Worker friend class MultiSink; 226*61c4878aSAndroid Build Coastguard Worker 227*61c4878aSAndroid Build Coastguard Worker MultiSink* multisink_; 228*61c4878aSAndroid Build Coastguard Worker ring_buffer::PrefixedEntryRingBufferMulti::Reader reader_; 229*61c4878aSAndroid Build Coastguard Worker }; 230*61c4878aSAndroid Build Coastguard Worker 231*61c4878aSAndroid Build Coastguard Worker ... 232*61c4878aSAndroid Build Coastguard Worker 233*61c4878aSAndroid Build Coastguard Worker private: 234*61c4878aSAndroid Build Coastguard Worker LockType lock_; 235*61c4878aSAndroid Build Coastguard Worker ring_buffer::PrefixedEntryRingBufferMulti ring_buffer_ 236*61c4878aSAndroid Build Coastguard Worker PW_GUARDED_BY(lock_); 237*61c4878aSAndroid Build Coastguard Worker }; 238*61c4878aSAndroid Build Coastguard Worker 239*61c4878aSAndroid Build Coastguard Worker } // namespace multisink 240*61c4878aSAndroid Build Coastguard Worker } // namespace pw 241*61c4878aSAndroid Build Coastguard Worker 242*61c4878aSAndroid Build Coastguard Worker------------ 243*61c4878aSAndroid Build Coastguard WorkerAlternatives 244*61c4878aSAndroid Build Coastguard Worker------------ 245*61c4878aSAndroid Build Coastguard Worker 246*61c4878aSAndroid Build Coastguard WorkerAdd on buffer size change interface to listener 247*61c4878aSAndroid Build Coastguard Worker=============================================== 248*61c4878aSAndroid Build Coastguard WorkerAdd ``OnBufferSizeChange`` interface to ``pw_multisink::MultiSinkListener``. The 249*61c4878aSAndroid Build Coastguard Workernew interface gets invoked when the available size of the underlying buffer 250*61c4878aSAndroid Build Coastguard Workerchanges. 251*61c4878aSAndroid Build Coastguard Worker 252*61c4878aSAndroid Build Coastguard WorkerInterface example: 253*61c4878aSAndroid Build Coastguard Worker 254*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 255*61c4878aSAndroid Build Coastguard Worker 256*61c4878aSAndroid Build Coastguard Worker class MultiSink { 257*61c4878aSAndroid Build Coastguard Worker public: 258*61c4878aSAndroid Build Coastguard Worker class Listener { 259*61c4878aSAndroid Build Coastguard Worker public: 260*61c4878aSAndroid Build Coastguard Worker 261*61c4878aSAndroid Build Coastguard Worker ... 262*61c4878aSAndroid Build Coastguard Worker 263*61c4878aSAndroid Build Coastguard Worker virtual void OnNewEntryAvailable() = 0; 264*61c4878aSAndroid Build Coastguard Worker 265*61c4878aSAndroid Build Coastguard Worker virtual void OnBufferSizeChange(size_t total_size, size_t used_sized); 266*61c4878aSAndroid Build Coastguard Worker }; 267*61c4878aSAndroid Build Coastguard Worker 268*61c4878aSAndroid Build Coastguard Worker ... 269*61c4878aSAndroid Build Coastguard Worker } 270*61c4878aSAndroid Build Coastguard Worker 271*61c4878aSAndroid Build Coastguard Worker 272*61c4878aSAndroid Build Coastguard WorkerImagined implementation of ``OnBufferSizeChange`` being invoked after an entry 273*61c4878aSAndroid Build Coastguard Workerpush or pop. It uses existing interfaces of the underlying 274*61c4878aSAndroid Build Coastguard Worker:ref:`module-pw_ring_buffer`. In reality, this implementation does not work well, 275*61c4878aSAndroid Build Coastguard Workerexplained in the **problems** sections below. 276*61c4878aSAndroid Build Coastguard Worker 277*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 278*61c4878aSAndroid Build Coastguard Worker 279*61c4878aSAndroid Build Coastguard Worker void MutilSink::HandleEntry(ConstByteSpan entry) { 280*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(lock_); 281*61c4878aSAndroid Build Coastguard Worker ... 282*61c4878aSAndroid Build Coastguard Worker ring_buffer_.PushBack(entry); 283*61c4878aSAndroid Build Coastguard Worker NotifyListenersBufferSizeChanged( 284*61c4878aSAndroid Build Coastguard Worker ring_buffer_.TotalSizeBytes(), 285*61c4878aSAndroid Build Coastguard Worker ring_buffer_.TotalUsedBytes()); 286*61c4878aSAndroid Build Coastguard Worker ... 287*61c4878aSAndroid Build Coastguard Worker } 288*61c4878aSAndroid Build Coastguard Worker 289*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 290*61c4878aSAndroid Build Coastguard Worker 291*61c4878aSAndroid Build Coastguard Worker void MutilSink::PopEntry(Drain& drain, ConstByteSpan entry) { 292*61c4878aSAndroid Build Coastguard Worker std::lock_guard lock(lock_); 293*61c4878aSAndroid Build Coastguard Worker ... 294*61c4878aSAndroid Build Coastguard Worker const size_t used_size_before_pop = ring_buffer_.TotalUsedBytes(); 295*61c4878aSAndroid Build Coastguard Worker drain.reader_.PopFront(); 296*61c4878aSAndroid Build Coastguard Worker const size_t used_size_after_pop = ring_buffer_.TotalUsedBytes(); 297*61c4878aSAndroid Build Coastguard Worker if (used_size_before_pop != used_size_after_pop) { 298*61c4878aSAndroid Build Coastguard Worker NotifyListenersBufferSizeChanged( 299*61c4878aSAndroid Build Coastguard Worker ring_buffer_.TotalSizeBytes(), 300*61c4878aSAndroid Build Coastguard Worker used_size_after_pop); 301*61c4878aSAndroid Build Coastguard Worker } 302*61c4878aSAndroid Build Coastguard Worker ... 303*61c4878aSAndroid Build Coastguard Worker } 304*61c4878aSAndroid Build Coastguard Worker 305*61c4878aSAndroid Build Coastguard Worker 306*61c4878aSAndroid Build Coastguard WorkerProblem 1. Find the slowest drain 307*61c4878aSAndroid Build Coastguard Worker^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 308*61c4878aSAndroid Build Coastguard WorkerWhen there are multiple drains attached to ``pw_multisink``, only the slowest 309*61c4878aSAndroid Build Coastguard Workerdrain(s) frees space from the underlying ``pw_ring_buffer`` when pops. 310*61c4878aSAndroid Build Coastguard Worker 311*61c4878aSAndroid Build Coastguard Worker``pw_multisink`` supports :ref:`module-pw_multisink-late_drain_attach` which 312*61c4878aSAndroid Build Coastguard Workerattaches an internal drain that never pops. The ``TotalUsedBytes()`` reported by 313*61c4878aSAndroid Build Coastguard Workerunderlying ``pw_ring_buffer`` counts from the slowest drain and always reports 314*61c4878aSAndroid Build Coastguard Workerthe full capacity instead of the real used size. 315*61c4878aSAndroid Build Coastguard Worker 316*61c4878aSAndroid Build Coastguard Worker 317*61c4878aSAndroid Build Coastguard WorkerProblem 2. Push a entry when buffer is full may decrease used size 318*61c4878aSAndroid Build Coastguard Worker^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 319*61c4878aSAndroid Build Coastguard WorkerWhen the pushing of a new entry exceeds the remaining free space of the 320*61c4878aSAndroid Build Coastguard Workerunderlying buffer, the push can still succeed, by silent dropping entries from 321*61c4878aSAndroid Build Coastguard Workerthe slowest drain. However, depending on the size of dropped entries and the 322*61c4878aSAndroid Build Coastguard Workersize of the new entry, the used buffer size may increase, decrease or stay the 323*61c4878aSAndroid Build Coastguard Workersame. 324*61c4878aSAndroid Build Coastguard Worker 325*61c4878aSAndroid Build Coastguard WorkerIf the user of the proposed ``OnBufferSizeChange`` interface is comparing the 326*61c4878aSAndroid Build Coastguard Workerreported used bytes with a threshold value, it is likely that the moment of 327*61c4878aSAndroid Build Coastguard Workerunderlying buffer being full may not be catched. 328*61c4878aSAndroid Build Coastguard Worker 329*61c4878aSAndroid Build Coastguard WorkerAlthough it is possible to also trigger ``OnBufferSizeChange`` with 330*61c4878aSAndroid Build Coastguard Worker``used_size == total_size`` when the above situation happens, the implementation 331*61c4878aSAndroid Build Coastguard Workermay also require exposing internal states of ``pw_ring_buffer``. 332*61c4878aSAndroid Build Coastguard Worker 333*61c4878aSAndroid Build Coastguard Worker-------------- 334*61c4878aSAndroid Build Coastguard WorkerOpen questions 335*61c4878aSAndroid Build Coastguard Worker-------------- 336