mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
fix(runtime): share uptime anchor across threads
This commit is contained in:
@@ -1103,8 +1103,27 @@ void scr_process_exit(double code) {
|
||||
* attribute monotonic stamp — the binary's own start, which is what
|
||||
* "the current Node.js process" means for a compiled program). */
|
||||
#ifdef _WIN32
|
||||
#if defined(SCR_LIB) && defined(SCR_THREAD_INSTANCES)
|
||||
/* The uptime anchor belongs to the host PROCESS, not to a library
|
||||
* instance. InitOnce keeps the lazy Windows spelling race-free when
|
||||
* several thread instances ask for the clock concurrently. */
|
||||
static INIT_ONCE scr_uptime_once = INIT_ONCE_STATIC_INIT;
|
||||
static double scr_uptime_t0_ms;
|
||||
static BOOL CALLBACK scr_uptime_anchor_once(PINIT_ONCE once, PVOID param,
|
||||
PVOID *ctx) {
|
||||
(void)once;
|
||||
(void)param;
|
||||
(void)ctx;
|
||||
scr_uptime_t0_ms = (double)GetTickCount64();
|
||||
return TRUE;
|
||||
}
|
||||
static void scr_uptime_anchor_init(void) {
|
||||
InitOnceExecuteOnce(&scr_uptime_once, scr_uptime_anchor_once, NULL, NULL);
|
||||
}
|
||||
#else
|
||||
static SCR_TL double scr_uptime_t0_ms;
|
||||
static void scr_uptime_anchor_init(void) { scr_uptime_t0_ms = (double)GetTickCount64(); }
|
||||
#endif
|
||||
static double scr_uptime_now_ms(void) { return (double)GetTickCount64(); }
|
||||
#else
|
||||
#include <sys/resource.h>
|
||||
@@ -1112,7 +1131,11 @@ static double scr_uptime_now_ms(void) { return (double)GetTickCount64(); }
|
||||
#ifdef __APPLE__
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
static SCR_TL double scr_uptime_t0_ms;
|
||||
/* A load-time, process-wide anchor: the constructor runs on only the
|
||||
* initial thread, while thread-instanced archives are entered from worker
|
||||
* threads whose TLS slots would otherwise stay zero. It is immutable once
|
||||
* main starts, so sharing it adds no cross-instance mutable state. */
|
||||
static double scr_uptime_t0_ms;
|
||||
static double scr_uptime_now_ms(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
@@ -1124,7 +1147,9 @@ __attribute__((constructor)) static void scr_uptime_anchor_init(void) {
|
||||
#endif
|
||||
|
||||
double scr_process_uptime(void) {
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && defined(SCR_LIB) && defined(SCR_THREAD_INSTANCES)
|
||||
scr_uptime_anchor_init();
|
||||
#elif defined(_WIN32)
|
||||
if (scr_uptime_t0_ms == 0) scr_uptime_anchor_init();
|
||||
#endif
|
||||
return (scr_uptime_now_ms() - scr_uptime_t0_ms) / 1000.0;
|
||||
@@ -1134,7 +1159,9 @@ double scr_process_uptime(void) {
|
||||
* start (Node's timeOrigin anchor), fractional — the same monotonic
|
||||
* clock and anchor as uptime, in Node's performance.now units. */
|
||||
double scr_perf_now(void) {
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && defined(SCR_LIB) && defined(SCR_THREAD_INSTANCES)
|
||||
scr_uptime_anchor_init();
|
||||
#elif defined(_WIN32)
|
||||
if (scr_uptime_t0_ms == 0) scr_uptime_anchor_init();
|
||||
#endif
|
||||
return scr_uptime_now_ms() - scr_uptime_t0_ms;
|
||||
|
||||
@@ -446,11 +446,11 @@ function buildThreadProbe(
|
||||
return bin;
|
||||
}
|
||||
|
||||
const THREADED_EXPECTED = `t0: bump x100 -> 101, calls_seen 100, sums_ok=1, trap fell through 0
|
||||
const THREADED_EXPECTED = `t0: bump x100 -> 101, calls_seen 100, sums_ok=1, clocks_ok=1, trap fell through 0
|
||||
t0 sink: calls=1 ctx_ok=1 fields=3 code=[SC4014] symbol=[mt_boom] addr_nonzero=1
|
||||
t1: bump x150 -> 151, calls_seen 150, sums_ok=1, post_ok=1
|
||||
t2: bump x200 -> 201, calls_seen 200, sums_ok=1, post_ok=1
|
||||
t3: bump x250 -> 251, calls_seen 250, sums_ok=1, post_ok=1
|
||||
t1: bump x150 -> 151, calls_seen 150, sums_ok=1, clocks_ok=1, post_ok=1
|
||||
t2: bump x200 -> 201, calls_seen 200, sums_ok=1, clocks_ok=1, post_ok=1
|
||||
t3: bump x250 -> 251, calls_seen 250, sums_ok=1, clocks_ok=1, post_ok=1
|
||||
survivor sinks: 0 0 0
|
||||
`;
|
||||
|
||||
@@ -510,7 +510,7 @@ localizationTest("M7: thread-instanced and runtime-localized archives compose in
|
||||
: [];
|
||||
expect([...defined].sort()).toEqual(
|
||||
[
|
||||
"mt_boom", "mt_bump", "mt_calls_seen", "mt_collect", "mt_init", "mt_set_panic_sink", "mt_sum_to",
|
||||
"mt_boom", "mt_bump", "mt_calls_seen", "mt_collect", "mt_init", "mt_perf_now", "mt_set_panic_sink", "mt_sum_to", "mt_uptime",
|
||||
...toolchainDefinitions,
|
||||
].sort(),
|
||||
);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// (array index OOB — the runtime's own range trap) that must reach only the
|
||||
// calling thread's sink. No top-level output: four instances init
|
||||
// concurrently in the probe.
|
||||
import { performance } from "node:perf_hooks";
|
||||
|
||||
let calls = 0;
|
||||
|
||||
export function bump(x: number): number {
|
||||
@@ -27,3 +29,11 @@ export function boom(i: number): number {
|
||||
const xs = [1, 2, 3];
|
||||
return xs[i]!;
|
||||
}
|
||||
|
||||
export function uptime(): number {
|
||||
return process.uptime();
|
||||
}
|
||||
|
||||
export function perfNow(): number {
|
||||
return performance.now();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ extern double mt_bump(double x);
|
||||
extern double mt_calls_seen(void);
|
||||
extern double mt_sum_to(double n);
|
||||
extern double mt_boom(double i);
|
||||
extern double mt_uptime(void);
|
||||
extern double mt_perf_now(void);
|
||||
|
||||
#define NTHREADS 4
|
||||
|
||||
@@ -80,6 +82,7 @@ typedef struct {
|
||||
double last_bump;
|
||||
double calls_seen;
|
||||
int sums_ok;
|
||||
int clocks_ok;
|
||||
int trap_fell_through; /* thread 0 only */
|
||||
int post_ok; /* threads 1..3 only */
|
||||
} Worker;
|
||||
@@ -121,6 +124,9 @@ static void *worker(void *arg) {
|
||||
Worker *w = arg;
|
||||
mt_set_panic_sink(sink, &w->sink);
|
||||
mt_init(); /* concurrent across all four threads: each init is instance-local */
|
||||
double uptime = mt_uptime();
|
||||
double perf_now = mt_perf_now();
|
||||
w->clocks_ok = uptime >= 0 && uptime < 60 && perf_now >= 0 && perf_now < 60000;
|
||||
w->sums_ok = 1;
|
||||
double last = 0;
|
||||
for (int i = 0; i < w->iters; i++) {
|
||||
@@ -163,7 +169,7 @@ int main(void) {
|
||||
for (int i = 0; i < NTHREADS; i++) pthread_join(threads[i], NULL);
|
||||
for (int i = 0; i < NTHREADS; i++) {
|
||||
Worker *w = &workers[i];
|
||||
printf("t%d: bump x%d -> %.0f, calls_seen %.0f, sums_ok=%d", i, w->iters, w->last_bump, w->calls_seen, w->sums_ok);
|
||||
printf("t%d: bump x%d -> %.0f, calls_seen %.0f, sums_ok=%d, clocks_ok=%d", i, w->iters, w->last_bump, w->calls_seen, w->sums_ok, w->clocks_ok);
|
||||
if (i == 0) {
|
||||
printf(", trap fell through %d\n", w->trap_fell_through);
|
||||
printf("t0 sink: calls=%d ctx_ok=%d fields=%d code=[%s] symbol=[%s] addr_nonzero=%d\n",
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
{ "export": "bump", "symbol": "mt_bump", "params": ["f64"], "returns": "f64" },
|
||||
{ "export": "callsSeen", "symbol": "mt_calls_seen", "params": [], "returns": "f64" },
|
||||
{ "export": "sumTo", "symbol": "mt_sum_to", "params": ["f64"], "returns": "f64" },
|
||||
{ "export": "boom", "symbol": "mt_boom", "params": ["f64"], "returns": "f64" }
|
||||
{ "export": "boom", "symbol": "mt_boom", "params": ["f64"], "returns": "f64" },
|
||||
{ "export": "uptime", "symbol": "mt_uptime", "params": [], "returns": "f64" },
|
||||
{ "export": "perfNow", "symbol": "mt_perf_now", "params": [], "returns": "f64" }
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user