@@ -1262,7 +1262,9 @@ SC::Result SC::AsyncEventLoop::Internal::completeAndReactivateOrTeardown(AsyncEv
12621262 AsyncTeardown teardown;
12631263 prepareTeardown (eventLoop, async, teardown);
12641264 bool hasBeenReactivated = false ;
1265+ async.flags |= Flag_NeedsTeardown;
12651266 SC_TRY (completeAsync (eventLoop, kernelEvents, async, eventIndex, returnCode, &hasBeenReactivated));
1267+ async.flags &= ~Flag_NeedsTeardown;
12661268 // hasBeenReactivated is required to avoid accessing async when it has been not reactivated (and maybe deallocated)
12671269 if (hasBeenReactivated and async.state == AsyncRequest::State::Reactivate)
12681270 {
@@ -1457,8 +1459,15 @@ void SC::AsyncEventLoop::Internal::runStepExecuteCompletions(AsyncEventLoop& eve
14571459 }
14581460 const int32_t eventIndex = static_cast <int32_t >(idx);
14591461
1460- AsyncRequest& async = *request;
1461- Result result = Result (kernelEvents.validateEvent (idx, continueProcessing));
1462+ AsyncRequest& async = *request;
1463+ if (async.flags & Flag_NeedsTeardown)
1464+ {
1465+ // runOnce has been called from a callback for this request on poll based API (kevent / epoll).
1466+ // As teardown for it has not been executed yet, the kernel will still signal this as writable
1467+ // or readable for this run too, but it would be incorrect to call its completion again.
1468+ continue ;
1469+ }
1470+ Result result = Result (kernelEvents.validateEvent (idx, continueProcessing));
14621471 if (not result)
14631472 {
14641473 reportError (eventLoop, kernelEvents, async, result, eventIndex);
0 commit comments