Skip to content

Conversation

patrick-ogrady
Copy link
Contributor

@patrick-ogrady patrick-ogrady commented Oct 9, 2025

Fixes: #1838

To support testing with external processes (that aren't managed by runtime), we must wait some "realtime" for said processes to perform useful work (without allowing tasks tracked by the deterministic runtime to "run ahead").

@patrick-ogrady patrick-ogrady changed the title [runtime] Support realtime [runtime/deterministic] Support realtime Oct 9, 2025
Copy link

cloudflare-workers-and-pages bot commented Oct 9, 2025

Deploying monorepo with  Cloudflare Pages  Cloudflare Pages

Latest commit: 58d717b
Status: ✅  Deploy successful!
Preview URL: https://bdf97f56.monorepo-eu0.pages.dev
Branch Preview URL: https://runtime-real-wait.monorepo-eu0.pages.dev

View logs

@patrick-ogrady patrick-ogrady marked this pull request as ready for review October 9, 2025 19:33
@patrick-ogrady
Copy link
Contributor Author

patrick-ogrady commented Oct 9, 2025

I suppose determinism in this case can depend wholly on when the application actually returns (and that the variance must be contained entirely within cycle)?

You can imagine a simple case where a task that returns when polled the ith time will have a different execution pattern when returned the jth time (again because we don't determine when the external process returns).

@patrick-ogrady patrick-ogrady marked this pull request as draft October 9, 2025 20:31
andresilva
andresilva previously approved these changes Oct 9, 2025
@patrick-ogrady
Copy link
Contributor Author

The complete fix here is to have a special "poll" that resolves at some timeout (panicking if the task doesn't complete by that point). We can add jitter to the timeout with the seed to still add some interesting sequencing dynamics.

@patrick-ogrady
Copy link
Contributor Author

Instead of panicking at the end of the Range, we should consider blocking forever (will be much less flaky -> we can warn)?

@SuperFluffy
Copy link
Contributor

Instead of panicking at the end of the Range, we should consider blocking forever (will be much less flaky -> we can warn)?

Similar to how cargo test says that a test ran too long? Yeah, I think that makes sense.

@patrick-ogrady
Copy link
Contributor Author

Instead of panicking at the end of the Range, we should consider blocking forever (will be much less flaky -> we can warn)?

Similar to how cargo test says that a test ran too long? Yeah, I think that makes sense.

Yeah, exactly. This is probably the right balance of "hostility" from the API (without requiring users to set a long bound). It also makes it super easy to set a very wide range of execution duration to explore pretty odd execution paths.

@patrick-ogrady patrick-ogrady marked this pull request as ready for review October 11, 2025 01:02
*this.future = None;
break Poll::Ready(value);
}
Poll::Pending => blocker.wait(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If this is the "first loop", emit a warning log.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With the "Range" logic, it is common to underestimate the actual value to create more unique scenarios. We can probably defer this as a result.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After thinking about this for a few more days, opted to not add logging in this case.

///
/// If we are operating in [Config::realtime], we will always try to keep making
/// progress after the passage of time by polling all pending tasks.
fn assert_liveness(&self) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now that we have the Pacer, it may be tempting to delete the actual sleep handling here but we are opting to keep it for now because external processes may receive some timeout invocation after context.sleep() (and if that moves virtually, it could end much sooner than it should).

Copy link
Contributor

@Copilot Copilot AI left a comment

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 adds support for "realtime" mode in the deterministic runtime to enable testing with external processes. It introduces a Pacer trait for controlling future execution timing and implements synchronization primitives to coordinate between deterministic and real-time execution.

  • Adds a realtime configuration option that disables time simulation and stall detection
  • Implements the Pacer trait with deterministic pacing using random delays and blocking
  • Introduces a Blocker synchronization primitive for thread coordination via wakers

Reviewed Changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
runtime/src/utils/mod.rs Adds Blocker synchronization primitive and comprehensive tests
runtime/src/utils/cell.rs Implements Pacer trait delegation for Cell wrapper
runtime/src/tokio/runtime.rs Implements passthrough Pacer for tokio runtime
runtime/src/lib.rs Defines Pacer trait and FutureExt extension
runtime/src/deterministic.rs Core realtime support with pacing implementation and time management
runtime/Cargo.toml Adds pin-project-lite dependency
Cargo.toml Adds pin-project-lite to workspace dependencies

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@patrick-ogrady
Copy link
Contributor Author

patrick-ogrady commented Oct 13, 2025

Before merging, I also plan to:

  • Put pacer behind a feature flag (you should strongly prefer using the runtime for everything): [runtime] Add pacer Feature #1882 (ended up being too coarse/annoying to work with, we can revisit)
  • Put a note in the docs that pacer should not be needed in the default case

Copy link

codecov bot commented Oct 14, 2025

Codecov Report

❌ Patch coverage is 92.09040% with 28 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.21%. Comparing base (a68f75e) to head (58d717b).
⚠️ Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
runtime/src/tokio/runtime.rs 0.00% 10 Missing ⚠️
runtime/src/utils/cell.rs 0.00% 10 Missing ⚠️
runtime/src/deterministic.rs 96.82% 8 Missing ⚠️
@@            Coverage Diff             @@
##             main    #1848      +/-   ##
==========================================
+ Coverage   92.20%   92.21%   +0.01%     
==========================================
  Files         305      308       +3     
  Lines       79509    81571    +2062     
==========================================
+ Hits        73310    75221    +1911     
- Misses       6199     6350     +151     
Files with missing lines Coverage Δ
runtime/src/lib.rs 97.19% <100.00%> (+0.02%) ⬆️
runtime/src/utils/mod.rs 93.12% <100.00%> (+4.13%) ⬆️
runtime/src/deterministic.rs 96.06% <96.82%> (+0.33%) ⬆️
runtime/src/tokio/runtime.rs 79.19% <0.00%> (-2.75%) ⬇️
runtime/src/utils/cell.rs 61.31% <0.00%> (-4.83%) ⬇️

... and 22 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a68f75e...58d717b. Read the comment docs.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[runtime/deterministic] Add Support for Externally-Driven Tasks

3 participants