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
3 changes: 2 additions & 1 deletion include/boost/corosio/native/detail/iocp/win_signal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ class win_signal final
win_signals& svc_;
signal_registration* signals_ = nullptr;
signal_op pending_op_;
bool waiting_ = false;
bool waiting_ = false;
bool cancelled_ = false;

public:
explicit win_signal(win_signals& svc) noexcept;
Expand Down
58 changes: 40 additions & 18 deletions include/boost/corosio/native/detail/iocp/win_signals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ win_signals::cancel_wait(win_signal& impl)

{
std::lock_guard<win_mutex> lock(mutex_);
impl.cancelled_ = true;
if (impl.waiting_)
{
was_waiting = true;
Expand All @@ -621,32 +622,53 @@ win_signals::cancel_wait(win_signal& impl)
inline void
win_signals::start_wait(win_signal& impl, signal_op* op)
{
bool was_cancelled = false;

{
std::lock_guard<win_mutex> lock(mutex_);

// Check for queued signals first
signal_registration* reg = impl.signals_;
while (reg)
// Check if cancel() was called before this wait started
if (impl.cancelled_)
{
was_cancelled = true;
impl.cancelled_ = false;
if (op->ec_out)
*op->ec_out = make_error_code(capy::error::canceled);
if (op->signal_out)
*op->signal_out = 0;
op->cont_op.cont.h = op->h;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
else
{
if (reg->undelivered > 0)
// Check for queued signals first
signal_registration* reg = impl.signals_;
while (reg)
{
--reg->undelivered;
op->signal_number = reg->signal_number;
op->svc = nullptr; // No extra work_finished needed
// Post for immediate completion - post() handles work tracking
post(op);
return;
if (reg->undelivered > 0)
{
--reg->undelivered;
op->signal_number = reg->signal_number;
op->svc = nullptr; // No extra work_finished needed
// Post for immediate completion - post() handles work tracking
post(op);
return;
}
reg = reg->next_in_set;
}
reg = reg->next_in_set;
}

// No queued signals, wait for delivery
// We call work_started() to keep io_context alive while waiting.
// Set svc so signal_op::operator() will call work_finished().
impl.waiting_ = true;
op->svc = this;
sched_.work_started();
// No queued signals, wait for delivery
// We call work_started() to keep io_context alive while waiting.
// Set svc so signal_op::operator() will call work_finished().
impl.waiting_ = true;
op->svc = this;
sched_.work_started();
}
}

// Dispatch outside the lock to avoid deadlock if the resumed
// coroutine re-enters cancel()/add()/remove()
if (was_cancelled)
dispatch_coro(op->d, op->cont_op.cont).resume();
}

inline void
Expand Down
3 changes: 2 additions & 1 deletion include/boost/corosio/native/detail/posix/posix_signal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ class posix_signal final
posix_signal_service& svc_;
signal_registration* signals_ = nullptr;
signal_op pending_op_;
bool waiting_ = false;
bool waiting_ = false;
bool cancelled_ = false;

public:
explicit posix_signal(posix_signal_service& svc) noexcept;
Expand Down
14 changes: 14 additions & 0 deletions include/boost/corosio/native/detail/posix/posix_signal_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@ posix_signal_service::cancel_wait(posix_signal& impl)

{
std::lock_guard lock(mutex_);
impl.cancelled_ = true;
if (impl.waiting_)
{
was_waiting = true;
Expand All @@ -667,6 +668,19 @@ posix_signal_service::start_wait(posix_signal& impl, signal_op* op)
{
std::lock_guard lock(mutex_);

// Check if cancel() was called before this wait started
if (impl.cancelled_)
{
impl.cancelled_ = false;
if (op->ec_out)
*op->ec_out = make_error_code(capy::error::canceled);
if (op->signal_out)
*op->signal_out = 0;
op->cont_op.cont.h = op->h;
op->d.post(op->cont_op.cont);
return;
}

// Check for queued signals first (signal arrived before wait started)
signal_registration* reg = impl.signals_;
while (reg)
Expand Down
27 changes: 27 additions & 0 deletions test/unit/signal_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,32 @@ struct signal_set_test
BOOST_TEST(result_ec == capy::cond::canceled);
}

void testCancelBeforeWait()
{
io_context ioc(Backend);
signal_set s(ioc, SIGINT);

bool completed = false;
std::error_code result_ec;

auto wait_task = [](signal_set& s_ref, std::error_code& ec_out,
bool& done_out) -> capy::task<> {
auto [ec, signum] = co_await s_ref.wait();
ec_out = ec;
done_out = true;
(void)signum;
};
capy::run_async(ioc.get_executor())(
wait_task(s, result_ec, completed));

// Cancel before io_context::run() — coroutine hasn't reached wait() yet
s.cancel();

ioc.run();
BOOST_TEST(completed);
BOOST_TEST(result_ec == capy::cond::canceled);
}

void testCancelNoWaiters()
{
io_context ioc(Backend);
Expand Down Expand Up @@ -723,6 +749,7 @@ struct signal_set_test

// Cancellation tests
testCancel();
testCancelBeforeWait();
testCancelNoWaiters();
testCancelMultipleTimes();

Expand Down
Loading