Skip to content

Commit 51ae47c

Browse files
Fix shared_ptr reference cycle causing ASAN leaks in CoroTaskRunner
After a coroutine completes, the frame remains alive holding a captured shared_ptr<CoroTaskRunner> back to its owner. This creates an unreachable cycle: runner -> task_ -> frame -> shared_ptr<runner>. Break the cycle in resume() by destroying the coroutine frame (task_ = {}) and the stored callable when the coroutine is done. Also fix runnable() to handle the null-handle state after cleanup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 185921e commit 51ae47c

File tree

1 file changed

+12
-2
lines changed

1 file changed

+12
-2
lines changed

include/xrpl/core/CoroTaskRunner.ipp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,17 @@ JobQueue::CoroTaskRunner::resume()
251251
task_.handle().resume();
252252
detail::getLocalValues().release();
253253
detail::getLocalValues().reset(saved);
254-
#ifndef NDEBUG
255254
if (task_.done())
255+
{
256+
#ifndef NDEBUG
256257
finished_ = true;
257258
#endif
259+
// Destroy the coroutine frame to break the shared_ptr cycle:
260+
// frame -> lambda captures shared_ptr<CoroTaskRunner> -> this.
261+
// Also release the heap-stored callable (no longer needed).
262+
task_ = {};
263+
storedFunc_.reset();
264+
}
258265
std::lock_guard lk(mutex_run_);
259266
running_ = false;
260267
cv_.notify_all();
@@ -266,7 +273,9 @@ JobQueue::CoroTaskRunner::resume()
266273
inline bool
267274
JobQueue::CoroTaskRunner::runnable() const
268275
{
269-
return !task_.done();
276+
// After normal completion, task_ is reset to break the shared_ptr cycle
277+
// (handle_ becomes null). A null handle means the coroutine is done.
278+
return task_.handle() && !task_.done();
270279
}
271280

272281
/**
@@ -292,6 +301,7 @@ JobQueue::CoroTaskRunner::expectEarlyExit()
292301
// destroying it is safe. Without this, the frame holds a shared_ptr
293302
// back to this CoroTaskRunner, creating an unreachable reference cycle.
294303
task_ = {};
304+
storedFunc_.reset();
295305
}
296306

297307
/**

0 commit comments

Comments
 (0)