Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/corosio/src/detail/epoll/resolver_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <boost/capy/ex/any_executor_ref.hpp>
#include <boost/capy/concept/io_awaitable.hpp>
#include <boost/capy/ex/execution_context.hpp>
#include <boost/capy/core/intrusive_list.hpp>
#include "src/detail/intrusive.hpp"

#include <mutex>
#include <stdexcept>
Expand All @@ -44,7 +44,7 @@ class epoll_resolver_impl;
*/
class epoll_resolver_impl
: public resolver::resolver_impl
, public capy::intrusive_list<epoll_resolver_impl>::node
, public intrusive_list<epoll_resolver_impl>::node
{
friend class epoll_resolver_service;

Expand Down Expand Up @@ -137,7 +137,7 @@ class epoll_resolver_service

private:
std::mutex mutex_;
capy::intrusive_list<epoll_resolver_impl> resolver_list_;
intrusive_list<epoll_resolver_impl> resolver_list_;
};

//------------------------------------------------------------------------------
Expand Down
10 changes: 5 additions & 5 deletions src/corosio/src/detail/epoll/sockets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <boost/capy/ex/any_executor_ref.hpp>
#include <boost/capy/concept/io_awaitable.hpp>
#include <boost/capy/ex/execution_context.hpp>
#include <boost/capy/core/intrusive_list.hpp>
#include "src/detail/intrusive.hpp"

#include "src/detail/epoll/op.hpp"
#include "src/detail/epoll/scheduler.hpp"
Expand Down Expand Up @@ -105,7 +105,7 @@ class epoll_acceptor_impl;
class epoll_socket_impl
: public socket::socket_impl
, public std::enable_shared_from_this<epoll_socket_impl>
, public capy::intrusive_list<epoll_socket_impl>::node
, public intrusive_list<epoll_socket_impl>::node
{
friend class epoll_sockets;

Expand Down Expand Up @@ -173,7 +173,7 @@ class epoll_socket_impl
class epoll_acceptor_impl
: public acceptor::acceptor_impl
, public std::enable_shared_from_this<epoll_acceptor_impl>
, public capy::intrusive_list<epoll_acceptor_impl>::node
, public intrusive_list<epoll_acceptor_impl>::node
{
friend class epoll_sockets;

Expand Down Expand Up @@ -239,8 +239,8 @@ class epoll_sockets

// Dual tracking: intrusive_list for fast shutdown iteration,
// vectors for shared_ptr ownership. See "Impl Lifetime" in file header.
capy::intrusive_list<epoll_socket_impl> socket_list_;
capy::intrusive_list<epoll_acceptor_impl> acceptor_list_;
intrusive_list<epoll_socket_impl> socket_list_;
intrusive_list<epoll_acceptor_impl> acceptor_list_;
std::vector<std::shared_ptr<epoll_socket_impl>> socket_ptrs_;
std::vector<std::shared_ptr<epoll_acceptor_impl>> acceptor_ptrs_;
};
Expand Down
230 changes: 230 additions & 0 deletions src/corosio/src/detail/intrusive.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/corosio
//

#ifndef BOOST_COROSIO_DETAIL_INTRUSIVE_HPP
#define BOOST_COROSIO_DETAIL_INTRUSIVE_HPP

namespace boost {
namespace corosio {
namespace detail {

//------------------------------------------------

/** An intrusive doubly linked list.
This container provides O(1) push and pop operations for
elements that derive from @ref node. Elements are not
copied or moved; they are linked directly into the list.
@tparam T The element type. Must derive from `intrusive_list<T>::node`.
*/
template<class T>
class intrusive_list
{
public:
/** Base class for list elements.
Derive from this class to make a type usable with
@ref intrusive_list. The `next_` and `prev_` pointers
are private and accessible only to the list.
*/
class node
{
friend class intrusive_list;

private:
T* next_;
T* prev_;
};

private:
T* head_ = nullptr;
T* tail_ = nullptr;

public:
intrusive_list() = default;

intrusive_list(intrusive_list&& other) noexcept
: head_(other.head_)
, tail_(other.tail_)
{
other.head_ = nullptr;
other.tail_ = nullptr;
}

intrusive_list(intrusive_list const&) = delete;
intrusive_list& operator=(intrusive_list const&) = delete;
intrusive_list& operator=(intrusive_list&&) = delete;

bool
empty() const noexcept
{
return head_ == nullptr;
}

void
push_back(T* w) noexcept
{
w->next_ = nullptr;
w->prev_ = tail_;
if(tail_)
tail_->next_ = w;
else
head_ = w;
tail_ = w;
}

void
splice_back(intrusive_list& other) noexcept
{
if(other.empty())
return;
if(tail_)
{
tail_->next_ = other.head_;
other.head_->prev_ = tail_;
tail_ = other.tail_;
}
else
{
head_ = other.head_;
tail_ = other.tail_;
}
other.head_ = nullptr;
other.tail_ = nullptr;
}
Comment on lines +83 to +101
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against self-splice to avoid list/queue corruption.

Splicing into the same container will currently create cycles and then clear head_/tail_, effectively corrupting the structure. A simple self-check avoids this class of bugs.

🛠️ Proposed fix
 void
 splice_back(intrusive_list& other) noexcept
 {
+    if (this == &other)
+        return;
     if(other.empty())
         return;
     if(tail_)
     {
         tail_->next_ = other.head_;
         other.head_->prev_ = tail_;
         tail_ = other.tail_;
     }
     else
     {
         head_ = other.head_;
         tail_ = other.tail_;
     }
     other.head_ = nullptr;
     other.tail_ = nullptr;
 }
 void
 splice(intrusive_queue& other) noexcept
 {
+    if (this == &other)
+        return;
     if(other.empty())
         return;
     if(tail_)
         tail_->next_ = other.head_;
     else
         head_ = other.head_;
     tail_ = other.tail_;
     other.head_ = nullptr;
     other.tail_ = nullptr;
 }

Also applies to: 199-211

🤖 Prompt for AI Agents
In `@src/corosio/src/detail/intrusive.hpp` around lines 83 - 101, The splice_back
implementation can corrupt the list when splicing the list into itself; add a
self-splice guard at the top of splice_back by returning immediately if &other
== this (before checking other.empty()), ensuring head_/tail_ are not modified
when other is the same object; apply the identical self-check to the other
splice overload in intrusive_list that takes an intrusive_list& (the other
splice method handling head_/tail_ adjustments) so both paths skip self-splice.


T*
pop_front() noexcept
{
if(!head_)
return nullptr;
T* w = head_;
head_ = head_->next_;
if(head_)
head_->prev_ = nullptr;
else
tail_ = nullptr;
return w;
}

void
remove(T* w) noexcept
{
if(w->prev_)
w->prev_->next_ = w->next_;
else
head_ = w->next_;
if(w->next_)
w->next_->prev_ = w->prev_;
else
tail_ = w->prev_;
}
};

//------------------------------------------------

/** An intrusive singly linked FIFO queue.
This container provides O(1) push and pop operations for
elements that derive from @ref node. Elements are not
copied or moved; they are linked directly into the queue.
Unlike @ref intrusive_list, this uses only a single `next_`
pointer per node, saving memory at the cost of not supporting
O(1) removal of arbitrary elements.
@tparam T The element type. Must derive from `intrusive_queue<T>::node`.
*/
template<class T>
class intrusive_queue
{
public:
/** Base class for queue elements.
Derive from this class to make a type usable with
@ref intrusive_queue. The `next_` pointer is private
and accessible only to the queue.
*/
class node
{
friend class intrusive_queue;

private:
T* next_;
};

private:
T* head_ = nullptr;
T* tail_ = nullptr;

public:
intrusive_queue() = default;

intrusive_queue(intrusive_queue&& other) noexcept
: head_(other.head_)
, tail_(other.tail_)
{
other.head_ = nullptr;
other.tail_ = nullptr;
}

intrusive_queue(intrusive_queue const&) = delete;
intrusive_queue& operator=(intrusive_queue const&) = delete;
intrusive_queue& operator=(intrusive_queue&&) = delete;

bool
empty() const noexcept
{
return head_ == nullptr;
}

void
push(T* w) noexcept
{
w->next_ = nullptr;
if(tail_)
tail_->next_ = w;
else
head_ = w;
tail_ = w;
}

void
splice(intrusive_queue& other) noexcept
{
if(other.empty())
return;
if(tail_)
tail_->next_ = other.head_;
else
head_ = other.head_;
tail_ = other.tail_;
other.head_ = nullptr;
other.tail_ = nullptr;
}

T*
pop() noexcept
{
if(!head_)
return nullptr;
T* w = head_;
head_ = head_->next_;
if(!head_)
tail_ = nullptr;
return w;
}
};

} // detail
} // corosio
} // boost

#endif
6 changes: 3 additions & 3 deletions src/corosio/src/detail/iocp/resolver_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#include <boost/capy/ex/any_executor_ref.hpp>
#include <boost/capy/concept/io_awaitable.hpp>
#include <boost/capy/ex/execution_context.hpp>
#include <boost/capy/core/intrusive_list.hpp>
#include "src/detail/intrusive.hpp"

#include "src/detail/iocp/windows.hpp"
#include "src/detail/iocp/overlapped_op.hpp"
Expand Down Expand Up @@ -82,7 +82,7 @@ struct resolve_op : overlapped_op
*/
class win_resolver_impl
: public resolver::resolver_impl
, public capy::intrusive_list<win_resolver_impl>::node
, public intrusive_list<win_resolver_impl>::node
{
friend class win_resolver_service;
friend struct resolve_op;
Expand Down Expand Up @@ -166,7 +166,7 @@ class win_resolver_service
private:
win_scheduler& sched_;
win_mutex mutex_;
capy::intrusive_list<win_resolver_impl> resolver_list_;
intrusive_list<win_resolver_impl> resolver_list_;
};

} // namespace detail
Expand Down
18 changes: 9 additions & 9 deletions src/corosio/src/detail/iocp/sockets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <boost/capy/ex/any_executor_ref.hpp>
#include <boost/capy/concept/io_awaitable.hpp>
#include <boost/capy/ex/execution_context.hpp>
#include <boost/capy/core/intrusive_list.hpp>
#include "src/detail/intrusive.hpp"

#include "src/detail/iocp/windows.hpp"
#include "src/detail/iocp/completion_key.hpp"
Expand Down Expand Up @@ -119,7 +119,7 @@ struct accept_op : overlapped_op
@note Internal implementation detail. Users interact with socket class.
*/
class win_socket_impl_internal
: public capy::intrusive_list<win_socket_impl_internal>::node
: public intrusive_list<win_socket_impl_internal>::node
, public std::enable_shared_from_this<win_socket_impl_internal>
{
friend class win_sockets;
Expand Down Expand Up @@ -181,7 +181,7 @@ class win_socket_impl_internal
*/
class win_socket_impl
: public socket::socket_impl
, public capy::intrusive_list<win_socket_impl>::node
, public intrusive_list<win_socket_impl>::node
{
std::shared_ptr<win_socket_impl_internal> internal_;

Expand Down Expand Up @@ -254,7 +254,7 @@ class win_socket_impl
@note Internal implementation detail. Users interact with acceptor class.
*/
class win_acceptor_impl_internal
: public capy::intrusive_list<win_acceptor_impl_internal>::node
: public intrusive_list<win_acceptor_impl_internal>::node
, public std::enable_shared_from_this<win_acceptor_impl_internal>
{
friend class win_sockets;
Expand Down Expand Up @@ -296,7 +296,7 @@ class win_acceptor_impl_internal
*/
class win_acceptor_impl
: public acceptor::acceptor_impl
, public capy::intrusive_list<win_acceptor_impl>::node
, public intrusive_list<win_acceptor_impl>::node
{
std::shared_ptr<win_acceptor_impl_internal> internal_;

Expand Down Expand Up @@ -450,10 +450,10 @@ class win_sockets
win_scheduler& sched_;
overlapped_key overlapped_key_;
win_mutex mutex_;
capy::intrusive_list<win_socket_impl_internal> socket_list_;
capy::intrusive_list<win_acceptor_impl_internal> acceptor_list_;
capy::intrusive_list<win_socket_impl> socket_wrapper_list_;
capy::intrusive_list<win_acceptor_impl> acceptor_wrapper_list_;
intrusive_list<win_socket_impl_internal> socket_list_;
intrusive_list<win_acceptor_impl_internal> acceptor_list_;
intrusive_list<win_socket_impl> socket_wrapper_list_;
intrusive_list<win_acceptor_impl> acceptor_wrapper_list_;
void* iocp_;
LPFN_CONNECTEX connect_ex_ = nullptr;
LPFN_ACCEPTEX accept_ex_ = nullptr;
Expand Down
Loading
Loading