Skip to content

Commit 3a29bea

Browse files
committed
Abandon coroutine handles during IOCP shutdown to avoid stack overflow
Coroutine frames cannot be destroyed during the shutdown drain loop because trampoline and inner-task coroutines have circular ownership that causes re-entrant destruction. Instead, abandon the handles and force outstanding_work_ to zero after draining both queues. Also adds /EHsc to the benchmark target for MSVC so Asio comparison files link correctly.
1 parent ae9afa7 commit 3a29bea

File tree

4 files changed

+14
-24
lines changed

4 files changed

+14
-24
lines changed

include/boost/corosio/native/detail/iocp/win_acceptor_service.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,6 @@ accept_op::do_complete(
153153
{
154154
auto* op = static_cast<accept_op*>(base);
155155

156-
// Destroy path (shutdown). Release resources owned by this
157-
// op before destroying the coroutine frame, whose tcp_socket
158-
// destructors will handle their own cleanup independently.
159156
if (!owner)
160157
{
161158
if (op->accepted_socket != INVALID_SOCKET)

include/boost/corosio/native/detail/iocp/win_overlapped_op.hpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,17 +147,11 @@ struct overlapped_op
147147
dispatch_coro(ex, h).resume();
148148
}
149149

150-
/** Cleanup without invoking handler (for destroy/shutdown path).
151-
Destroys the waiting coroutine frame to prevent leaks.
152-
*/
150+
/** Disarm cancellation and abandon the coroutine handle. */
153151
void cleanup_only()
154152
{
155153
stop_cb.reset();
156-
if (h)
157-
{
158-
h.destroy();
159-
h = {};
160-
}
154+
h = {};
161155
}
162156
};
163157

include/boost/corosio/native/detail/iocp/win_scheduler.hpp

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -196,48 +196,49 @@ win_scheduler::shutdown()
196196
{
197197
::InterlockedExchange(&shutdown_, 1);
198198

199-
// Stop timer wakeup mechanism
200199
if (timers_)
201200
timers_->stop();
202201

203-
// Drain all outstanding operations without invoking handlers
204-
while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0)
202+
for (;;)
205203
{
206-
// First drain the fallback queue
207204
op_queue ops;
208205
{
209206
std::lock_guard<win_mutex> lock(dispatch_mutex_);
210207
ops.splice(completed_ops_);
211208
}
212209

210+
bool drained_any = false;
211+
213212
while (auto* h = ops.pop())
214213
{
215-
::InterlockedDecrement(&outstanding_work_);
216214
h->destroy();
215+
drained_any = true;
217216
}
218217

219-
// Then drain from IOCP with zero timeout (non-blocking)
220218
DWORD bytes;
221219
ULONG_PTR key;
222220
LPOVERLAPPED overlapped;
223221
::GetQueuedCompletionStatus(iocp_, &bytes, &key, &overlapped, 0);
224222
if (overlapped)
225223
{
226-
::InterlockedDecrement(&outstanding_work_);
227224
if (key == key_posted)
228225
{
229-
// Posted scheduler_op*
230226
auto* op = reinterpret_cast<scheduler_op*>(overlapped);
231227
op->destroy();
232228
}
233229
else
234230
{
235-
// Actual I/O: convert OVERLAPPED* to overlapped_op*
236231
auto* op = overlapped_to_op(overlapped);
237232
op->destroy();
238233
}
234+
drained_any = true;
239235
}
236+
237+
if (!drained_any)
238+
break;
240239
}
240+
241+
::InterlockedExchange(&outstanding_work_, 0);
241242
}
242243

243244
inline void
@@ -253,9 +254,6 @@ win_scheduler::post(std::coroutine_handle<> h) const
253254
auto* self = static_cast<post_handler*>(base);
254255
if (!owner)
255256
{
256-
// Destroy path: destroy the coroutine frame, then self
257-
if (self->h_)
258-
self->h_.destroy();
259257
delete self;
260258
return;
261259
}

perf/bench/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ target_link_libraries(corosio_bench
3232
Threads::Threads)
3333

3434
target_compile_options(corosio_bench PRIVATE
35-
$<$<CXX_COMPILER_ID:GNU>:-fcoroutines>)
35+
$<$<CXX_COMPILER_ID:GNU>:-fcoroutines>
36+
$<$<CXX_COMPILER_ID:MSVC>:/EHsc>)
3637

3738
set_property(TARGET corosio_bench PROPERTY FOLDER "perf/benchmarks")
3839

0 commit comments

Comments
 (0)