Sponsored Content
Skip to content

fix: Guard Coro::resume() against completed coroutines - #6608

Merged
bthomee merged 5 commits into
developfrom
pratik/fix-coro-resume-after-completion
Mar 27, 2026
Merged

fix: Guard Coro::resume() against completed coroutines#6608
bthomee merged 5 commits into
developfrom
pratik/fix-coro-resume-after-completion

Conversation

@pratikmankawde

Copy link
Copy Markdown
Contributor

High Level Overview of Change

Guard Coro::resume() against being called on an already-completed coroutine, fixing an intermittent assertion failure in CI.

Context of Change

A documented race condition (JobQueue.h:354-377) allows post() to schedule a resume() job that executes after the coroutine has already completed. With boost::coroutine v1, calling operator() on a completed coroutine was technically UB but happened to be benign in Release builds (BOOST_ASSERT compiled out). After the switch to boost::coroutine2 (PR #6372), the same call becomes genuine UB — the underlying boost::context::continuation is in a moved-from state.

The XRPL_ASSERT at Coro.ipp:79 caught this in CI Release builds with -Dassert=ON, surfacing as an intermittent crash that killed the test process.

Source analysis:

  • v1 pull_coroutine_impl::pull(): guards with BOOST_ASSERT(!is_complete()) — compiled out in Release
  • v2 pull_control_block::resume(): no guard at all — unconditionally calls std::move(c).resume() on completed continuation (UB)

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

API Impact

  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)

Before / After

Before: A late resume() call (from the documented race) hits XRPL_ASSERT → process abort in assert-enabled builds, or UB (calling operator() on completed coroutine2) in non-assert builds.

After: A late resume() detects !coro_, cleans up (LocalValues, running_, cv_), and returns early. The race condition is handled safely by design.

Test Plan

  • Existing unit tests cover coroutine lifecycle
  • The fix addresses an intermittent CI failure — the race depends on thread scheduling and is not reliably reproducible in a unit test
  • CI run on this branch will validate no regressions

Jira: RIPD-5379

A documented race condition (JobQueue.h:354-377) allows post() to
schedule a resume() job that executes after the coroutine has already
completed. With boost::coroutine v1, calling operator() on a completed
coroutine was technically UB but happened to be benign in Release
builds (BOOST_ASSERT compiled out). After the switch to
boost::coroutine2, the same call becomes genuine UB — the underlying
boost::context::continuation is in a moved-from state, leading to
crashes or memory corruption.

The XRPL_ASSERT at Coro.ipp:79 caught this in CI Release builds with
-Dassert=ON, surfacing as an intermittent assertion failure.

Replace the assertion with an early-return guard: if coro_ is already
exhausted when resume() acquires the mutex, clean up and return. This
makes the documented race condition safe by design rather than relying
on undefined behavior being benign.

Update the race condition documentation and resume() doc comment to
reflect the new behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov

codecov Bot commented Mar 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.5%. Comparing base (509677a) to head (d594614).
⚠️ Report is 2 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #6608     +/-   ##
=========================================
- Coverage     81.5%   81.5%   -0.0%     
=========================================
  Files          998     998             
  Lines        74456   74456             
  Branches      7578    7558     -20     
=========================================
- Hits         60648   60645      -3     
- Misses       13808   13811      +3     
Files with missing lines Coverage Δ
include/xrpl/core/Coro.ipp 100.0% <100.0%> (ø)
include/xrpl/core/JobQueue.h 100.0% <ø> (ø)

... and 3 files with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pratikmankawde

Copy link
Copy Markdown
Contributor Author

/ai-review

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deadlock risk found in coroutine lock management.

Review by Claude Opus 4.6 · Prompt: V12

Comment thread include/xrpl/core/Coro.ipp Outdated

@kuznetsss kuznetsss left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍

@pratikmankawde pratikmankawde added the Ready to merge *PR author* thinks it's ready to merge. Has passed code review. Perf sign-off may still be required. label Mar 27, 2026
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two low-severity correctness concerns flagged inline: a potential LocalValues TLS lifecycle violation on the early-return path (line 76), and loss of diagnostic coverage from removing the assertion (line 86).

Review by Claude Opus 4.6 · Prompt: V12

Comment thread include/xrpl/core/Coro.ipp
Comment thread include/xrpl/core/Coro.ipp
@bthomee

bthomee commented Mar 27, 2026

Copy link
Copy Markdown
Collaborator

@pratikmankawde are there any action items from the AI reviewer or can they be dismissed?

@kuznetsss
kuznetsss requested a review from Copilot March 27, 2026 13:57
@pratikmankawde

Copy link
Copy Markdown
Contributor Author

@pratikmankawde are there any action items from the AI reviewer or can they be dismissed?

No action needed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens JobQueue::Coro::resume() against being invoked after a coroutine has already completed, addressing an intermittent CI assertion/UB triggered by the documented post()-before-yield() race in JobQueue.

Changes:

  • Update Coro::resume() to avoid invoking boost::coroutines2 pull coroutines once they are completed.
  • Update JobQueue coroutine documentation to describe the completed-coroutine behavior and the boost::coroutine2 UB risk.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
include/xrpl/core/JobQueue.h Updates API/race-condition documentation to reflect safe handling of late resume().
include/xrpl/core/Coro.ipp Adds a runtime guard so resume() does not call operator() on a completed coroutine.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread include/xrpl/core/Coro.ipp
Comment thread include/xrpl/core/Coro.ipp Outdated
Comment thread include/xrpl/core/JobQueue.h Outdated
@a1q123456

Copy link
Copy Markdown
Contributor

i see that you’re now suffering from the same pain I had yesterday

Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nSuspend_ counter leak on late-resume path — see inline.

Review by Claude Opus 4.6 · Prompt: V12

Comment thread include/xrpl/core/Coro.ipp Outdated
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
@pratikmankawde

Copy link
Copy Markdown
Contributor Author

i see that you’re now suffering from the same pain I had yesterday

No pain no gain ;-)

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One observability concern flagged inline — the silent if (coro_) guard correctly prevents UB from the documented race, but also silently swallows unrelated double-resume bugs that the removed assert would have caught. A warning log in the else branch is recommended.

Review by Claude Opus 4.6 · Prompt: V12

// completion. Calling operator() on a completed boost::coroutine2 is
// undefined behavior, so we must check and skip invoking the coroutine
// body if it has already completed.
if (coro_)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Silent guard swallows double-resume bugs too — add a diagnostic log in the else branch to preserve observability:

    if (coro_)
    {
        coro_();
    }
    else
    {
        JLOG(j_.warn()) << "Coro::resume() called on completed coroutine — possible double-resume bug";
    }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@pratikmankawde I'll go ahead and merge this change, but if this comment is valid it will be easy to address in a follow-up PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A double resume is quite rare as per the current architecture. So the else part is an extraneous detail to have. And it won't be needed after switching to std::coroutine. So, we can ignore this.

@bthomee
bthomee enabled auto-merge March 27, 2026 18:40
@bthomee
bthomee added this pull request to the merge queue Mar 27, 2026
Merged via the queue into develop with commit 3d9c545 Mar 27, 2026
3 checks passed
@bthomee
bthomee deleted the pratik/fix-coro-resume-after-completion branch March 27, 2026 19:14
bthomee pushed a commit that referenced this pull request Mar 30, 2026
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
bthomee pushed a commit that referenced this pull request Mar 30, 2026
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@mvadari mvadari added this to the 3.2.0 milestone May 20, 2026
beartec-jpg pushed a commit to beartec-jpg/FalconLedger that referenced this pull request Jun 1, 2026
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready to merge *PR author* thinks it's ready to merge. Has passed code review. Perf sign-off may still be required.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants