Matter SDK Coverage Report
Current view: top level - lib/core - GroupedCallbackList.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 0.0 % 71 0
Test Date: 2025-01-17 19:00:11 Functions: 0.0 % 23 0

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2024 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : 
      18              : #pragma once
      19              : 
      20              : #include <lib/core/CHIPCallback.h>
      21              : #include <lib/support/CodeUtils.h>
      22              : 
      23              : #include <utility>
      24              : 
      25              : namespace chip {
      26              : namespace Callback {
      27              : 
      28              : namespace detail {
      29              : // Internal helper functions
      30              : template <size_t Index>
      31              : void TaggedDequeueGroup(Cancelable * cancelable);
      32              : void EnqueueWithGroup(Cancelable * cancelable, Cancelable *& group, Cancelable * anchor, void (*cancelFn)(Cancelable *));
      33              : void LinkGroup(Cancelable * prev, Cancelable * next);
      34              : inline Cancelable * ClearCancelable(Cancelable * cancelable);
      35              : } // namespace detail
      36              : 
      37              : /**
      38              :  * A GroupedCallbackList manages a list of Callback objects (see CHIPCallback.h).
      39              :  * The state of the list is maintained using the prev/next pointers of each Callback.
      40              :  * Unlike a normal linked list where entries are managed individually, this class
      41              :  * manages a number of related callbacks as a group, with the callback function types
      42              :  * given as template parameters.
      43              :  *
      44              :  * For example, a `GroupedCallbackList<SuccessFn, FailureFn>` manages groups of a
      45              :  * `Callback<SuccessFn>` and a `Callback<FailureFn>`.
      46              :  *
      47              :  * Groups of callbacks are enqueued and dequeued (or cancelled) as a unit.
      48              :  * Within a group each callback is optional (i.e. can be null), however attempting
      49              :  * to enqueue a group where all callbacks are null has no effect.
      50              :  */
      51              : template <typename... T>
      52              : class GroupedCallbackList : protected Cancelable
      53              : {
      54              : public:
      55            0 :     GroupedCallbackList() = default;
      56            0 :     ~GroupedCallbackList() { Clear(); }
      57              : 
      58              :     GroupedCallbackList(GroupedCallbackList const &)             = delete;
      59              :     GroupedCallbackList & operator=(GroupedCallbackList const &) = delete;
      60              : 
      61            0 :     bool IsEmpty() { return mNext == this; }
      62              : 
      63              :     /**
      64              :      * Enqueues the specified group of callbacks, any of which may be null.
      65              :      */
      66            0 :     void Enqueue(Callback<T> *... callback) { Enqueue(std::index_sequence_for<T...>{}, callback...); }
      67              : 
      68              :     /**
      69              :      * If the list is non-empty, populates the reference arguments with the first
      70              :      * group of callbacks and returns true. Returns false if the list is empty.
      71              :      */
      72            0 :     bool Peek(Callback<T> *&... callback) const { return Peek(std::index_sequence_for<T...>{}, callback...); }
      73              : 
      74              :     /**
      75              :      * Like Peek(), but additionally removes the first group of callbacks from the list.
      76              :      */
      77            0 :     bool Take(Callback<T> *&... callback)
      78              :     {
      79            0 :         VerifyOrReturnValue(Peek(callback...), false);
      80            0 :         mNext->Cancel();
      81            0 :         return true;
      82              :     }
      83              : 
      84              :     /**
      85              :      * Moves all elements of the source list into this list, leaving the source list empty.
      86              :      */
      87            0 :     void EnqueueTakeAll(GroupedCallbackList & source)
      88              :     {
      89            0 :         VerifyOrReturn(!source.IsEmpty() && this != &source);
      90            0 :         detail::LinkGroup(mPrev, source.mNext);
      91            0 :         source.mPrev->mNext = this;
      92            0 :         mPrev               = source.mPrev;
      93              : 
      94            0 :         source.mPrev = source.mNext = &source;
      95              :     }
      96              : 
      97            0 :     void Clear()
      98              :     {
      99            0 :         Cancelable * next = mNext;
     100            0 :         while (next != this)
     101              :         {
     102            0 :             next = detail::ClearCancelable(next);
     103              :         }
     104            0 :         mPrev = mNext = this;
     105            0 :     }
     106              : 
     107              : private:
     108              :     /*
     109              :      *  The grouped list structure is similar to a normal doubly linked list,
     110              :      *  with the list object itself (via inheriting from Cancelable) acting as
     111              :      *  an external "anchor" node that is both the head and tail of the list.
     112              :      *
     113              :      *  However we have the additional requirement of representing node grouping.
     114              :      *  Due to the requirement so support sparse groups (one or more callbacks may
     115              :      *  not be present in a particular group) we cannot rely on a fixed group size.
     116              :      *  This problem is solved by having the "prev" pointer for all nodes in a group
     117              :      *  point to the node before the group, as illustrated below:
     118              :      *
     119              :      *     |Anchor|    |Grp 1|    |====== Group 2 ======|
     120              :      *     _______________________________________________
     121              :      *    /                                               \
     122              :      *    \  +---+      +---+      +---+   +---+   +---+  /
     123              :      *     ->|###|----->|   |----->|   |-->|   |-->|   |--
     124              :      *       |###|      |   |      |   |   |   |   |   |
     125              :      *     --|###|<-----|   |<-----|   |  -|   |  -|   |<-
     126              :      *    /  +---+      +---+  \   +---+ / +---+ / +---+  \
     127              :      *   |                      \_______/       /          |
     128              :      *   |                       \_____________/           |
     129              :      *    \_______________________________________________/
     130              :      *
     131              :      *  This allows the start of a group to be reached from any group member via
     132              :      *  ->prev->next. Nodes in a group can be enumerated by via the "next" chain,
     133              :      *  inspecting the "prev" pointers to detect the end of the group. The price
     134              :      *  for encoding grouping in this way is that upon removal of a group we have
     135              :      *  to update not just the "prev" pointer of the following node, but of all
     136              :      *  nodes in the following group.
     137              :      *
     138              :      *  When retrieving a (sparse) group from the list, we also need to be able
     139              :      *  to tell which callbacks are present: In a grouped list with types (A, B)
     140              :      *  both (a, nullptr) and (nullptr, b) are by necessity represented by only
     141              :      *  a single node in the list. To be able to recover this information, we use
     142              :      *  distinct trampolines that tag the "cancel" function pointer stored in each
     143              :      *  node with the index of the callback type within the argument type tuple.
     144              :      */
     145              : 
     146              :     template <std::size_t... Index>
     147            0 :     void Enqueue(std::index_sequence<Index...>, Callback<T> *... callback)
     148              :     {
     149            0 :         Cancelable * group = nullptr;
     150              :         (
     151            0 :             [&] {
     152            0 :                 VerifyOrReturn(callback != nullptr);
     153            0 :                 detail::EnqueueWithGroup(callback->Cancel(), group, this, &detail::TaggedDequeueGroup<Index>);
     154            0 :             }(),
     155              :             ...);
     156            0 :     }
     157              : 
     158              :     template <std::size_t... Index>
     159            0 :     bool Peek(std::index_sequence<Index...>, Callback<T> *&... callback) const
     160              :     {
     161            0 :         Cancelable * cancelable = mNext;
     162            0 :         VerifyOrReturnValue(cancelable != this, false);
     163            0 :         Cancelable * groupPrev = cancelable->mPrev;
     164              :         (
     165            0 :             [&] {
     166            0 :                 if (cancelable->mPrev == groupPrev && cancelable->mCancel == &detail::TaggedDequeueGroup<Index>)
     167              :                 {
     168            0 :                     callback   = Callback<decltype(callback->mCall)>::FromCancelable(cancelable);
     169            0 :                     cancelable = cancelable->mNext;
     170              :                 }
     171              :                 else
     172              :                 {
     173            0 :                     callback = nullptr;
     174              :                 }
     175            0 :             }(),
     176              :             ...);
     177            0 :         return true;
     178              :     }
     179              : };
     180              : 
     181              : namespace detail {
     182              : 
     183              : // Inserts `cancelable` before `anchor`, either starting a new `group`
     184              : // (populating the passed pointer if it is null) or adding to it.
     185            0 : inline void EnqueueWithGroup(Cancelable * cancelable, Cancelable *& group, Cancelable * anchor, void (*cancelFn)(Cancelable *))
     186              : {
     187            0 :     cancelable->mCancel = cancelFn;
     188            0 :     cancelable->mNext   = anchor;
     189            0 :     if (!group)
     190              :     {
     191            0 :         group             = cancelable;
     192            0 :         cancelable->mPrev = anchor->mPrev;
     193              :     }
     194              :     else
     195              :     {
     196            0 :         cancelable->mPrev = group->mPrev;
     197              :     }
     198            0 :     anchor->mPrev->mNext = cancelable;
     199            0 :     anchor->mPrev        = cancelable;
     200            0 : }
     201              : 
     202              : // Establish prev/next links between `prev` and the group starting at `cancelable`.
     203            0 : inline void LinkGroup(Cancelable * prev, Cancelable * cancelable)
     204              : {
     205            0 :     prev->mNext = cancelable;
     206              : 
     207            0 :     Cancelable * groupPrev = cancelable->mPrev;
     208              :     do
     209              :     {
     210            0 :         cancelable->mPrev = prev;
     211            0 :         cancelable        = cancelable->mNext;
     212            0 :     } while (cancelable->mPrev == groupPrev);
     213            0 : }
     214              : 
     215              : // Clears the state of a cancelable and returns the following one.
     216              : // Does NOT touch the state of adjacent nodes.
     217            0 : inline Cancelable * ClearCancelable(Cancelable * cancelable)
     218              : {
     219            0 :     auto * next       = cancelable->mNext;
     220            0 :     cancelable->mPrev = cancelable->mNext = cancelable;
     221            0 :     cancelable->mCancel                   = nullptr;
     222            0 :     return next;
     223              : }
     224              : 
     225              : // Dequeues `cancelable` and all otehr nodes in the same group.
     226            0 : inline void DequeueGroup(Cancelable * cancelable)
     227              : {
     228            0 :     Cancelable * prev = cancelable->mPrev;
     229            0 :     Cancelable * next = prev->mNext;
     230              :     do
     231              :     {
     232            0 :         next = ClearCancelable(next);
     233            0 :     } while (next->mPrev == prev);
     234            0 :     LinkGroup(prev, next);
     235            0 : }
     236              : 
     237              : template <size_t Index>
     238            0 : void TaggedDequeueGroup(Cancelable * cancelable)
     239              : {
     240              :     (void) Index; // not used, we only care that instantiations have unique addresses
     241            0 :     DequeueGroup(cancelable);
     242            0 : }
     243              : 
     244              : } // namespace detail
     245              : } // namespace Callback
     246              : } // namespace chip
        

Generated by: LCOV version 2.0-1