From 2a980d9de46a398efd2e8bedf14c920e4a7c3816 Mon Sep 17 00:00:00 2001 From: Nyannyacha Date: Tue, 19 Aug 2025 04:17:31 +0000 Subject: [PATCH 1/2] fix: another edge case of `Uncaught null` --- crates/base/src/runtime/mod.rs | 25 +++++++-- .../test_cases/issue-func-280/cpu/index.ts | 40 ++++++++++++++ .../test_cases/issue-func-280/mem/index.ts | 43 +++++++++++++++ crates/base/tests/integration_tests.rs | 53 +++++++++++++++++++ 4 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 crates/base/test_cases/issue-func-280/cpu/index.ts create mode 100644 crates/base/test_cases/issue-func-280/mem/index.ts diff --git a/crates/base/src/runtime/mod.rs b/crates/base/src/runtime/mod.rs index a0a8f3c6e..0f3f4fdfc 100644 --- a/crates/base/src/runtime/mod.rs +++ b/crates/base/src/runtime/mod.rs @@ -1573,8 +1573,9 @@ where } let js_runtime = &mut this.js_runtime; + let op_state = js_runtime.op_state(); let cpu_metrics_guard = get_cpu_metrics_guard( - js_runtime.op_state(), + op_state.clone(), maybe_cpu_usage_metrics_tx, accumulated_cpu_time_ns, ); @@ -1630,15 +1631,22 @@ where beforeunload_cpu_threshold.load().as_deref().copied() { let threshold_ns = (threshold_ms as i128) * 1_000_000; - let accumulated_cpu_time_ns = *accumulated_cpu_time_ns as i128; - - if accumulated_cpu_time_ns >= threshold_ns { + if (*accumulated_cpu_time_ns as i128) >= threshold_ns { beforeunload_cpu_threshold.store(None); if !state.is_terminated() { + let _cpu_metrics_guard = get_cpu_metrics_guard( + op_state.clone(), + maybe_cpu_usage_metrics_tx, + accumulated_cpu_time_ns, + ); + if let Err(err) = MaybeDenoRuntime::DenoRuntime(&mut this) .dispatch_beforeunload_event(WillTerminateReason::CPU) { + if state.is_terminated() { + return Poll::Ready(Err(anyhow!("execution terminated"))); + } return Poll::Ready(Err(err)); } } @@ -1662,9 +1670,18 @@ where beforeunload_mem_threshold.store(None); if !state.is_terminated() && !mem_state.is_exceeded() { + let _cpu_metrics_guard = get_cpu_metrics_guard( + op_state, + maybe_cpu_usage_metrics_tx, + accumulated_cpu_time_ns, + ); + if let Err(err) = MaybeDenoRuntime::DenoRuntime(&mut this) .dispatch_beforeunload_event(WillTerminateReason::Memory) { + if state.is_terminated() { + return Poll::Ready(Err(anyhow!("execution terminated"))); + } return Poll::Ready(Err(err)); } } diff --git a/crates/base/test_cases/issue-func-280/cpu/index.ts b/crates/base/test_cases/issue-func-280/cpu/index.ts new file mode 100644 index 000000000..a53c82eaa --- /dev/null +++ b/crates/base/test_cases/issue-func-280/cpu/index.ts @@ -0,0 +1,40 @@ +function mySlowFunction(baseNumber) { + console.time("mySlowFunction"); + let now = Date.now(); + let result = 0; + for (var i = Math.pow(baseNumber, 7); i >= 0; i--) { + result += Math.atan(i) * Math.tan(i); + } + let duration = Date.now() - now; + console.timeEnd("mySlowFunction"); + return { result: result, duration: duration }; +} + +let keep = true; + +async function sleep(ms: number) { + return new Promise((res) => { + setTimeout(() => { + res(void 0); + }, ms); + }); +} + +setInterval(() => { + if (keep) { + mySlowFunction(10); + } +}, 800); + +addEventListener("beforeunload", () => { + keep = false; + while (true) { + mySlowFunction(10); + console.log("cpu"); + } +}); + +const never = new Promise(() => {}); +EdgeRuntime.waitUntil(never); + +Deno.serve((_req) => new Response("Hello, world")); diff --git a/crates/base/test_cases/issue-func-280/mem/index.ts b/crates/base/test_cases/issue-func-280/mem/index.ts new file mode 100644 index 000000000..cc712e4c1 --- /dev/null +++ b/crates/base/test_cases/issue-func-280/mem/index.ts @@ -0,0 +1,43 @@ +function mySlowFunction(baseNumber) { + console.time("mySlowFunction"); + let now = Date.now(); + let result = 0; + for (var i = Math.pow(baseNumber, 7); i >= 0; i--) { + result += Math.atan(i) * Math.tan(i); + } + let duration = Date.now() - now; + console.timeEnd("mySlowFunction"); + return { result: result, duration: duration }; +} + +let keep = true; + +async function sleep(ms: number) { + return new Promise((res) => { + setTimeout(() => { + res(void 0); + }, ms); + }); +} + +const arr: ArrayBuffer[] = []; + +setInterval(async () => { + if (keep) { + await sleep(300); + arr.push(new ArrayBuffer(1024 * 1024)); + } +}, 800); + +addEventListener("beforeunload", () => { + keep = false; + while (true) { + arr.push(new ArrayBuffer(1024 * 1024)); + console.log("mem"); + } +}); + +const never = new Promise(() => {}); +EdgeRuntime.waitUntil(never); + +Deno.serve((_req) => new Response("Hello, world")); diff --git a/crates/base/tests/integration_tests.rs b/crates/base/tests/integration_tests.rs index 0ea953720..fa1d637f9 100644 --- a/crates/base/tests/integration_tests.rs +++ b/crates/base/tests/integration_tests.rs @@ -2553,6 +2553,59 @@ async fn test_issue_func_205() { unreachable!("test failed"); } +#[tokio::test] +#[serial] +async fn test_issue_func_280() { + async fn run(func_name: &'static str, reason: ShutdownReason) { + let (tx, mut rx) = mpsc::unbounded_channel(); + let tb = TestBedBuilder::new("./test_cases/main") + .with_per_worker_policy(None) + .with_worker_event_sender(Some(tx)) + .with_server_flags(ServerFlags { + beforeunload_cpu_pct: Some(90), + beforeunload_memory_pct: Some(90), + ..Default::default() + }) + .build() + .await; + + let resp = tb + .request(|b| { + b.uri("/meow") + .header("x-cpu-time-soft-limit-ms", HeaderValue::from_static("500")) + .header("x-cpu-time-hard-limit-ms", HeaderValue::from_static("1000")) + .header("x-memory-limit-mb", "30") + .header("x-service-path", format!("issue-func-280/{}", func_name)) + .body(Body::empty()) + .context("can't make request") + }) + .await + .unwrap(); + + assert_eq!(resp.status().as_u16(), StatusCode::OK); + + while let Some(ev) = rx.recv().await { + match ev.event { + WorkerEvents::Log(ev) => { + tracing::info!("{}", ev.msg); + continue; + } + WorkerEvents::Shutdown(ev) => { + tb.exit(Duration::from_secs(TESTBED_DEADLINE_SEC)).await; + assert_eq!(ev.reason, reason); + return; + } + _ => continue, + } + } + + unreachable!("test failed"); + } + + run("cpu", ShutdownReason::CPUTime).await; + run("mem", ShutdownReason::Memory).await; +} + #[tokio::test] #[serial] async fn test_should_render_detailed_failed_to_create_graph_error() { From 8e90fd9e8f55a486863821deb29517378d40175d Mon Sep 17 00:00:00 2001 From: Nyannyacha Date: Tue, 19 Aug 2025 04:20:19 +0000 Subject: [PATCH 2/2] stamp: adjust cpu time --- crates/base/tests/integration_tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/base/tests/integration_tests.rs b/crates/base/tests/integration_tests.rs index fa1d637f9..fc461f708 100644 --- a/crates/base/tests/integration_tests.rs +++ b/crates/base/tests/integration_tests.rs @@ -2572,8 +2572,8 @@ async fn test_issue_func_280() { let resp = tb .request(|b| { b.uri("/meow") - .header("x-cpu-time-soft-limit-ms", HeaderValue::from_static("500")) - .header("x-cpu-time-hard-limit-ms", HeaderValue::from_static("1000")) + .header("x-cpu-time-soft-limit-ms", HeaderValue::from_static("1000")) + .header("x-cpu-time-hard-limit-ms", HeaderValue::from_static("2000")) .header("x-memory-limit-mb", "30") .header("x-service-path", format!("issue-func-280/{}", func_name)) .body(Body::empty())