Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ env:

jobs:
autofix:
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') }}
name: Format
runs-on: ubuntu-latest
concurrency:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ env:

jobs:
lint-js:
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') }}
name: "Lint JavaScript"
runs-on: ubuntu-latest
steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/openclaw-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ concurrency:

jobs:
linux-x64:
if: github.repository == 'openclaw/bun'
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') && (github.repository == 'openclaw/bun') }}
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 115
env:
Expand Down Expand Up @@ -155,7 +155,7 @@ jobs:
retention-days: 7

darwin-arm64:
if: github.repository == 'openclaw/bun'
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') && (github.repository == 'openclaw/bun') }}
runs-on: macos-15
timeout-minutes: 115
env:
Expand Down
23 changes: 15 additions & 8 deletions .github/workflows/openclaw-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: source
ref: ${{ env.RELEASE_TAG && format('refs/tags/{0}', env.RELEASE_TAG) || github.sha }}
ref: ${{ env.RELEASE_TAG && format('refs/tags/{0}', env.RELEASE_TAG) || github.event.pull_request.head.sha || github.sha }}
# History for the ancestry check against main; blobs are fetched on demand.
fetch-depth: 0
filter: blob:none
Expand All @@ -73,12 +73,13 @@ jobs:
with:
bun-version: "1.4.4"
- name: Test pipeline
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') }}
working-directory: pipeline
run: bun test test/internal/source-lints/openclaw-release.test.ts
- name: Plan
id: plan
env:
TARGETS: ${{ inputs.targets }}
TARGETS: ${{ startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') && 'windows-x64,windows-arm64' || inputs.targets }}
WINDOWS_SIGNED: ${{ vars.OPENCLAW_RELEASE_WINDOWS_SIGNED }}
run: |
args=(--source source --main origin/main)
Expand Down Expand Up @@ -353,7 +354,7 @@ jobs:
smoke:
name: Smoke ${{ matrix.target }}
needs: [plan, build, sign-macos, sign-windows, windows-test-only]
if: ${{ !cancelled() && needs.build.result == 'success' && needs.sign-macos.result != 'failure' && (needs.plan.outputs.windows != 'true' || (needs.plan.outputs.publish == 'true' && needs.sign-windows.result == 'success') || (needs.plan.outputs.publish != 'true' && needs.windows-test-only.result == 'success')) }}
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') && !cancelled() && needs.build.result == 'success' && needs.sign-macos.result != 'failure' && (needs.plan.outputs.windows != 'true' || (needs.plan.outputs.publish == 'true' && needs.sign-windows.result == 'success') || (needs.plan.outputs.publish != 'true' && needs.windows-test-only.result == 'success')) }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
strategy:
Expand Down Expand Up @@ -435,15 +436,21 @@ jobs:
$arch = & $exe -p process.arch
if ($LASTEXITCODE -ne 0 -or $arch -cne $env:EXPECTED_ARCH) { throw 'Unexpected runtime architecture.' }
$env:PATH = "$(Split-Path $exe);$env:PATH"
& $exe ../pipeline/scripts/openclaw-ci/tests.ts select-windows
if ($LASTEXITCODE -ne 0) { throw 'Test selection failed.' }
& $exe ../pipeline/scripts/openclaw-ci/tests.ts test
if ($LASTEXITCODE -ne 0) { throw 'Windows compatibility tests failed.' }
New-Item -ItemType Directory -Force build/openclaw-ci/traces | Out-Null
$env:BUN_DIAG_GC_WAKE_DIR = (Resolve-Path build/openclaw-ci/traces).Path
$env:BUN_DIAG_GC_WAKE_MODE = 'memory'
& $exe --revision
& $exe -p 'process.versions.webkit'
git rev-parse HEAD
node scripts/runner.node.ts --exec-path $exe --vendor=false --retries=0 --results-json build/openclaw-ci/results.json --include js/node/worker_threads/worker_threads.test.ts
$fullExit = $LASTEXITCODE
node ../pipeline/scripts/openclaw-release/gc-wake-trace-probe.mjs $exe build/openclaw-ci/probe
if ($LASTEXITCODE -ne 0 -or $fullExit -ne 0) { throw 'Worker diagnostic failures retained.' }
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: compatibility-${{ matrix.target }}
path: source/build/openclaw-ci/*.json
path: source/build/openclaw-ci/
if-no-files-found: error
retention-days: 14

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/packages-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ env:

jobs:
bun-plugin-svelte:
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/rust-lints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ env:

jobs:
clippy:
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') }}
name: cargo clippy
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -74,6 +75,7 @@ jobs:
run: cargo check --workspace --all-targets --keep-going

miri:
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') }}
name: cargo miri test
runs-on: ubuntu-latest
steps:
Expand All @@ -96,6 +98,7 @@ jobs:
run: bun run rust:miri

lolhtml:
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') }}
# The vendored lol-html is a fork (oven-sh/lol-html, `bun` branch) carrying
# content-handler suspension, and its own test suite is the only thing that
# guards the fork's invariants: nothing in the Bun test suite reaches, for
Expand Down Expand Up @@ -125,7 +128,7 @@ jobs:
mordant:
name: mordant
runs-on: ubuntu-latest
if: github.event_name != 'merge_group'
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') && (github.event_name != 'merge_group') }}
# Advisory while the pack is new: shows up on the PR, does not block it.
continue-on-error: true
env:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/source-lints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ env:

jobs:
source-lints:
if: ${{ !startsWith(github.head_ref, 'claude/diag-parked-worker-gc-') }}
name: "Source lints"
runs-on: ubuntu-latest
timeout-minutes: 10
Expand Down
8 changes: 7 additions & 1 deletion packages/bun-usockets/src/eventing/libuv.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#ifdef LIBUS_USE_LIBUV

extern void Bun__traceGCWake(const char *event, const void *owner, const void *object, double detail);

/* Windows does not reliably latch a received RST in SO_ERROR (POSIX does);
* the reset surfaces on the next I/O. A zero-byte send observes it without
* touching the stream: 0 on a healthy socket, SOCKET_ERROR with a fatal
Expand Down Expand Up @@ -178,6 +180,7 @@ static void timer_cb(uv_timer_t *t) {

static void async_cb(uv_async_t *a) {
struct us_internal_callback_t *cb = a->data;
Bun__traceGCWake("uv_async_callback", cb->loop->data.jsc_vm, cb->loop, 0);
// internal asyncs give their loop, not themselves
cb->cb((struct us_internal_callback_t *)cb->loop);
}
Expand Down Expand Up @@ -475,7 +478,9 @@ void us_loop_run(struct us_loop_t *loop) {
* that waits for a promise re-enters here while the outer dispatch is still
* using its socket. us_loop_run_bun_tick does the same on POSIX. */
loop->data.tick_depth++;
Bun__traceGCWake("uv_poll_begin", loop->data.jsc_vm, loop, 0);
uv_run(loop->uv_loop, UV_RUN_ONCE);
Bun__traceGCWake("uv_poll_end", loop->data.jsc_vm, loop, 0);
loop->data.tick_depth--;
}

Expand Down Expand Up @@ -630,7 +635,8 @@ void us_internal_async_wakeup(struct us_internal_async *a) {
(struct us_internal_callback_t *)a;

uv_async_t *uv_async = (uv_async_t *)(internal_cb + 1);
uv_async_send(uv_async);
int result = uv_async_send(uv_async);
Bun__traceGCWake("uv_async_send", NULL, internal_cb->loop, result);
}

int us_socket_get_error(struct us_socket_t *s) {
Expand Down
60 changes: 60 additions & 0 deletions scripts/openclaw-release/gc-wake-trace-probe.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { createHash } from "node:crypto";

const [exe, outputArg] = process.argv.slice(2);
assert(exe && outputArg);
const output = resolve(outputArg);
mkdirSync(output, { recursive: true });
const testFile = "test/js/node/worker_threads/worker_threads.test.ts";
const testName = "wakes a parked worker for an immediate GC timer";
const source = readFileSync(testFile, "utf8");
const start = source.indexOf(` test.concurrent("${testName}"`);
assert(start >= 0);
const end = source.indexOf("\n test(", start);
assert.equal(createHash("sha256").update(source.slice(start, end)).digest("hex"), "bad4f3c5c0bd81289843ce29719182dc536ecffc4b2c247e1882095c01f56efc");
const results = [];
for (let round = 1; round <= 10; round++) {
for (const mode of round % 2 ? ["plain", "memory", "background"] : ["background", "memory", "plain"]) {
const label = `${String(round).padStart(2, "0")}-${mode}`;
const env = {
...process.env,
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "1",
BUN_GARBAGE_COLLECTOR_LEVEL: "1",
BUN_JSC_randomIntegrityAuditRate: "1.0",
BUN_RUNTIME_TRANSPILER_CACHE_PATH: "0",
BUN_DEBUG_QUIET_LOGS: "1",
NO_COLOR: "1",
};
delete env.NODE_OPTIONS;
delete env.BUN_TEST_DRAIN_EVENT_LOOP;
delete env.BUN_DIAG_GC_WAKE_DIR;
delete env.BUN_DIAG_GC_WAKE_MODE;
if (mode !== "plain") {
const directory = join(output, label);
mkdirSync(directory, { recursive: true });
env.BUN_DIAG_GC_WAKE_DIR = directory;
env.BUN_DIAG_GC_WAKE_MODE = mode;
}
const started = Date.now();
const child = spawnSync(exe, ["test", testFile, "-t", testName], {
env,
encoding: "utf8",
maxBuffer: 32 * 1024 * 1024,
// Process watchdog only; the test's own ten-second limit is unchanged.
timeout: 30_000,
});
const text = ((child.stdout ?? "") + (child.stderr ?? "")).replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
writeFileSync(join(output, `${label}.log`), text);
const outcome = child.error || child.signal ? "invalid"
: child.status === 0 && /\b1 pass\b/.test(text) && /\b0 fail\b/.test(text) ? "pass"
: child.status !== 0 && text.includes(testName) && /\b1 fail\b/.test(text) ? "fail" : "invalid";
results.push({ round, mode, outcome, exitCode: child.status, signal: child.signal, error: child.error?.message, durationMs: Date.now() - started });
writeFileSync(join(output, "probe.json"), JSON.stringify(results, null, 2) + "\n");
console.log(`${label}: ${outcome} (${Date.now() - started} ms)`);
if (outcome === "invalid") throw new Error(`Invalid probe: ${label}`);
}
}
if (results.some(result => result.outcome !== "pass")) process.exitCode = 1;
22 changes: 22 additions & 0 deletions src/jsc/VmHandle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ use crate::event_loop::EventLoop;
use crate::virtual_machine::VirtualMachine;
use bun_event_loop::ConcurrentTask::ConcurrentTask as ConcurrentTaskItem;

unsafe extern "C" {
safe fn Bun__traceGCWake(
event: *const core::ffi::c_char,
owner: *const core::ffi::c_void,
object: *const core::ffi::c_void,
detail: f64,
);
}

pub use bun_event_loop::Posted;

#[repr(u8)]
Expand Down Expand Up @@ -402,7 +411,20 @@ impl VmHandle {
/// Wake the VM's loop (no-op once closed). Any thread.
pub fn wake(&self) {
if let Some(_a) = self.enter() {
Bun__traceGCWake(
c"vm_wake".as_ptr(),
self.0.hot.vm.cast(),
core::ptr::null(),
0.0,
);
self.0.loop_of(LoopKind::Regular).wakeup();
} else {
Bun__traceGCWake(
c"vm_wake_closed".as_ptr(),
self.0.hot.vm.cast(),
core::ptr::null(),
0.0,
);
}
}

Expand Down
Loading
Loading