revert(js): drop the "cancellation-safe" event loop pumping
The premise was wrong. deno_core 0.350's run_event_loop is:
pub async fn run_event_loop(&mut self, opts) -> Result<(), CoreError> {
poll_fn(|cx| self.poll_event_loop(cx, opts)).await
}
so replacing `timeout(budget, run_event_loop())` with a hand-rolled
`poll_fn(poll_event_loop)` loop is the same code with the same cancellation
semantics, not safer ones. The mechanism I described cannot happen either:
`poll` is a synchronous `&mut self` call, a future can only be dropped between
polls, and the HandleScope/ContextScope live inside poll_event_loop as stack
locals that are destroyed before it returns. The isolate is exited between
polls both before and after that change.
What actually fixes #430 is confining each connection's isolates to their own
OS thread, which the next commits do. The regression test still reproduces the
abort on the pre-thread-per-connection tree and still passes here without this
commit, so nothing depended on it.
It was not merely a no-op. Two regressions it introduced:
- resolve_promises_until inverted the meaning of tick_ms. It was "pump the
event loop for up to tick_ms, returning early on idle"; it became one single
poll followed by an unconditional sleep(tick_ms). With the 1->50ms backoff
that settles at 20 event-loop turns per second, so an awaited promise chain
needing many turns crawls, and past the budget the caller silently reads a
stale __obscura_await_meta.
- drive_module_eval replaced `e?` with `let _ = poll_event_loop(...)`,
discarding event-loop errors. A module stalled on a top-level await used to
exit immediately with a logged error; it instead spun for the whole budget
logging nothing -- the exact #374 starvation the surrounding comment claims
to prevent.
Keep the drive_module_eval extraction (it genuinely dedupes load_module and
load_inline_module) but restore the original select!-based body.
Refs #430.
This commit is contained in:
1 parent
3015f7d8ad
commit
319c6034fe
2 files changed
+62
-136
No files matched your search
@@ -763,13 +763,13 @@ impl Page {
|
||||
if tokio::time::Instant::now() >= deadline {
|
||||
break;
|
||||
}
|
||||
// Cancellation-safe single tick: a per-poll `tokio::time::timeout`
|
||||
// wrapping `run_event_loop` would drop that future mid-poll and
|
||||
// leave this isolate entered on the thread, aborting the process
|
||||
// under concurrent pages (#430). `poll_event_loop_once` exits the
|
||||
// isolate before returning, so the tick is safe to bound.
|
||||
match js.poll_event_loop_once().await {
|
||||
Some(Ok(())) => {
|
||||
let result = tokio::time::timeout(
|
||||
tokio::time::Duration::from_millis(10),
|
||||
js.run_event_loop(),
|
||||
).await;
|
||||
|
||||
match result {
|
||||
Ok(Ok(())) => {
|
||||
if self.http_client.active_requests() == 0 {
|
||||
idle_count += 1;
|
||||
if idle_count >= 2 {
|
||||
@@ -781,10 +781,9 @@ impl Page {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
|
||||
}
|
||||
}
|
||||
Some(Err(_)) => break,
|
||||
None => {
|
||||
Ok(Err(_)) => break,
|
||||
Err(_) => {
|
||||
idle_count = 0;
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1227,11 +1226,10 @@ impl Page {
|
||||
}
|
||||
|
||||
if let Some(js) = &mut self.js {
|
||||
// Cancellation-safe tick (see #430): never drop a
|
||||
// `run_event_loop` future mid-poll, which leaks the entered
|
||||
// isolate. `poll_event_loop_once` exits the isolate per tick.
|
||||
let _ = js.poll_event_loop_once().await;
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
|
||||
let _ = tokio::time::timeout(
|
||||
tokio::time::Duration::from_millis(50),
|
||||
js.run_event_loop(),
|
||||
).await;
|
||||
} else {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user