LCOV - code coverage report
Current view: top level - corosio/detail - intrusive.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 100.0 % 103 103
Test Date: 2026-10-08 18:13:32 Functions: 87.3 % 157 137 20

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : //
       4                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/corosio
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_COROSIO_DETAIL_INTRUSIVE_HPP
      11                 : #define BOOST_COROSIO_DETAIL_INTRUSIVE_HPP
      12                 : 
      13                 : namespace boost::corosio::detail {
      14                 : 
      15                 : /** An intrusive doubly linked list.
      16                 : 
      17                 :     This container provides O(1) push and pop operations for
      18                 :     elements that derive from @ref node. Elements are not
      19                 :     copied or moved; they are linked directly into the list.
      20                 : 
      21                 :     @tparam T The element type. Must derive from `intrusive_list<T>::node`.
      22                 : */
      23                 : template<class T>
      24                 : class intrusive_list
      25                 : {
      26                 : public:
      27                 :     /** Base class for list elements.
      28                 : 
      29                 :         Derive from this class to make a type usable with
      30                 :         @ref intrusive_list. The `next_` and `prev_` pointers
      31                 :         are private and accessible only to the list.
      32                 :     */
      33                 :     class node
      34                 :     {
      35                 :         friend class intrusive_list;
      36                 : 
      37                 :     private:
      38                 :         T* next_ = nullptr;
      39                 :         T* prev_ = nullptr;
      40                 :     };
      41                 : 
      42                 : private:
      43                 :     T* head_ = nullptr;
      44                 :     T* tail_ = nullptr;
      45                 : 
      46                 : public:
      47 HIT       10576 :     intrusive_list() = default;
      48                 : 
      49               1 :     intrusive_list(intrusive_list&& other) noexcept
      50               1 :         : head_(other.head_)
      51               1 :         , tail_(other.tail_)
      52                 :     {
      53               1 :         other.head_ = nullptr;
      54               1 :         other.tail_ = nullptr;
      55               1 :     }
      56                 : 
      57                 :     intrusive_list(intrusive_list const&)            = delete;
      58                 :     intrusive_list& operator=(intrusive_list const&) = delete;
      59                 :     intrusive_list& operator=(intrusive_list&&)      = delete;
      60                 : 
      61              16 :     bool empty() const noexcept
      62                 :     {
      63              16 :         return head_ == nullptr;
      64                 :     }
      65                 : 
      66                 :     /// Peek at the head element without removing it.
      67               6 :     T* front() const noexcept
      68                 :     {
      69               6 :         return head_;
      70                 :     }
      71                 : 
      72           20476 :     void push_front(T* w) noexcept
      73                 :     {
      74           20476 :         auto* n  = static_cast<node*>(w);
      75           20476 :         n->prev_ = nullptr;
      76           20476 :         n->next_ = head_;
      77           20476 :         if (head_)
      78           12504 :             static_cast<node*>(head_)->prev_ = w;
      79                 :         else
      80            7972 :             tail_ = w;
      81           20476 :         head_ = w;
      82           20476 :     }
      83                 : 
      84           55789 :     void push_back(T* w) noexcept
      85                 :     {
      86           55789 :         auto* n  = static_cast<node*>(w);
      87           55789 :         n->next_ = nullptr;
      88           55789 :         n->prev_ = tail_;
      89           55789 :         if (tail_)
      90           40606 :             static_cast<node*>(tail_)->next_ = w;
      91                 :         else
      92           15183 :             head_ = w;
      93           55789 :         tail_ = w;
      94           55789 :     }
      95                 : 
      96               3 :     void splice_back(intrusive_list& other) noexcept
      97                 :     {
      98               3 :         if (other.empty())
      99               1 :             return;
     100               2 :         if (tail_)
     101                 :         {
     102               1 :             static_cast<node*>(tail_)->next_       = other.head_;
     103               1 :             static_cast<node*>(other.head_)->prev_ = tail_;
     104               1 :             tail_                                  = other.tail_;
     105                 :         }
     106                 :         else
     107                 :         {
     108               1 :             head_ = other.head_;
     109               1 :             tail_ = other.tail_;
     110                 :         }
     111               2 :         other.head_ = nullptr;
     112               2 :         other.tail_ = nullptr;
     113                 :     }
     114                 : 
     115          346627 :     T* pop_front() noexcept
     116                 :     {
     117          346627 :         if (!head_)
     118          306027 :             return nullptr;
     119           40600 :         T* w  = head_;
     120           40600 :         head_ = static_cast<node*>(head_)->next_;
     121           40600 :         if (head_)
     122           22167 :             static_cast<node*>(head_)->prev_ = nullptr;
     123                 :         else
     124           18433 :             tail_ = nullptr;
     125                 :         // Defensive: clear stale linkage so remove() on a
     126                 :         // popped node cannot corrupt the list.
     127           40600 :         auto* n  = static_cast<node*>(w);
     128           40600 :         n->next_ = nullptr;
     129           40600 :         n->prev_ = nullptr;
     130           40600 :         return w;
     131                 :     }
     132                 : 
     133                 :     /** Unlink @p w if it is a member of this list.
     134                 : 
     135                 :         @pre @p w is either a genuine member of `*this` list, or is
     136                 :         fully detached (not linked in *any* `intrusive_list<T>`,
     137                 :         including a different instance over the same `T`). This is
     138                 :         not a membership test: `T::node` holds one shared `next_`/
     139                 :         `prev_` pair regardless of which list currently owns it, so a
     140                 :         node actually linked in a different list looks "not detached"
     141                 :         here and would be spliced out using *this* list's `head_`/
     142                 :         `tail_` for the boundary cases — corrupting both lists. A
     143                 :         caller that cannot rule that out needs its own bookkeeping
     144                 :         (e.g. a flag for which list currently owns the node) before
     145                 :         calling this.
     146                 : 
     147                 :         @return True if @p w was linked in and has now been removed;
     148                 :         false if it was already detached (e.g. a reentrant call from
     149                 :         a self-referential keepalive dropping during the same
     150                 :         teardown that already popped it elsewhere). Callers that gate
     151                 :         a one-time action (recycling, deleting) on actually having
     152                 :         removed something rely on this to make that action idempotent.
     153                 :     */
     154           35665 :     bool remove(T* w) noexcept
     155                 :     {
     156           35665 :         auto* n = static_cast<node*>(w);
     157                 :         // Already detached — nothing to do.
     158           35665 :         if (!n->next_ && !n->prev_ && head_ != w && tail_ != w)
     159               3 :             return false;
     160           35662 :         if (n->prev_)
     161           20020 :             static_cast<node*>(n->prev_)->next_ = n->next_;
     162                 :         else
     163           15642 :             head_ = n->next_;
     164           35662 :         if (n->next_)
     165           20091 :             static_cast<node*>(n->next_)->prev_ = n->prev_;
     166                 :         else
     167           15571 :             tail_ = n->prev_;
     168           35662 :         n->next_ = nullptr;
     169           35662 :         n->prev_ = nullptr;
     170           35662 :         return true;
     171                 :     }
     172                 : 
     173                 :     /// Invoke @p f for each element in the list.
     174                 :     template<class F>
     175            5710 :     void for_each(F f)
     176                 :     {
     177            6109 :         for (T* p = head_; p; p = static_cast<node*>(p)->next_)
     178             399 :             f(p);
     179            5710 :     }
     180                 : };
     181                 : 
     182                 : /** An intrusive singly linked FIFO queue.
     183                 : 
     184                 :     This container provides O(1) push and pop operations for
     185                 :     elements that derive from @ref node. Elements are not
     186                 :     copied or moved; they are linked directly into the queue.
     187                 : 
     188                 :     Unlike @ref intrusive_list, this uses only a single `next_`
     189                 :     pointer per node, saving memory at the cost of not supporting
     190                 :     O(1) removal of arbitrary elements.
     191                 : 
     192                 :     @tparam T The element type. Must derive from `intrusive_queue<T>::node`.
     193                 : */
     194                 : template<class T>
     195                 : class intrusive_queue
     196                 : {
     197                 : public:
     198                 :     /** Base class for queue elements.
     199                 : 
     200                 :         Derive from this class to make a type usable with
     201                 :         @ref intrusive_queue. The `next_` pointer is private
     202                 :         and accessible only to the queue.
     203                 :     */
     204                 :     class node
     205                 :     {
     206                 :         friend class intrusive_queue;
     207                 : 
     208                 :     private:
     209                 :         T* next_ = nullptr;
     210                 :     };
     211                 : 
     212                 : private:
     213                 :     T* head_ = nullptr;
     214                 :     T* tail_ = nullptr;
     215                 : 
     216                 : public:
     217            2313 :     intrusive_queue() = default;
     218                 : 
     219               1 :     intrusive_queue(intrusive_queue&& other) noexcept
     220               1 :         : head_(other.head_)
     221               1 :         , tail_(other.tail_)
     222                 :     {
     223               1 :         other.head_ = nullptr;
     224               1 :         other.tail_ = nullptr;
     225               1 :     }
     226                 : 
     227                 :     intrusive_queue(intrusive_queue const&)            = delete;
     228                 :     intrusive_queue& operator=(intrusive_queue const&) = delete;
     229                 :     intrusive_queue& operator=(intrusive_queue&&)      = delete;
     230                 : 
     231            1351 :     bool empty() const noexcept
     232                 :     {
     233            1351 :         return head_ == nullptr;
     234                 :     }
     235                 : 
     236             834 :     void push(T* w) noexcept
     237                 :     {
     238             834 :         w->next_ = nullptr;
     239             834 :         if (tail_)
     240             262 :             tail_->next_ = w;
     241                 :         else
     242             572 :             head_ = w;
     243             834 :         tail_ = w;
     244             834 :     }
     245                 : 
     246               3 :     void splice(intrusive_queue& other) noexcept
     247                 :     {
     248               3 :         if (other.empty())
     249               1 :             return;
     250               2 :         if (tail_)
     251               1 :             tail_->next_ = other.head_;
     252                 :         else
     253               1 :             head_ = other.head_;
     254               2 :         tail_       = other.tail_;
     255               2 :         other.head_ = nullptr;
     256               2 :         other.tail_ = nullptr;
     257                 :     }
     258                 : 
     259            3446 :     T* pop() noexcept
     260                 :     {
     261            3446 :         if (!head_)
     262            2612 :             return nullptr;
     263             834 :         T* w  = head_;
     264             834 :         head_ = head_->next_;
     265             834 :         if (!head_)
     266             571 :             tail_ = nullptr;
     267                 :         // Defensive: clear stale linkage on popped node.
     268             834 :         w->next_ = nullptr;
     269             834 :         return w;
     270                 :     }
     271                 : };
     272                 : 
     273                 : } // namespace boost::corosio::detail
     274                 : 
     275                 : #endif
        

Generated by: LCOV version 2.3