Skip to content

Commit 7a09801

Browse files
committed
perf(sidecar): inline net.write scaffold (SECURE_EXEC_INLINE_SOCKET_DATA, default-OFF) — REFUTED: slower+flaky vs baseline (per-op off-broker inlining introduces races/overhead that negate the pickup-latency win); gated off, kept as scaffold; the wholesale per-VM re-shard is the real lever
1 parent ffa89e3 commit 7a09801

11 files changed

Lines changed: 752 additions & 12 deletions

File tree

crates/execution/src/javascript.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,18 @@ pub trait InlineNetDrain: Send + Sync {
486486
None
487487
}
488488

489+
/// F10-INLINE-WRITE: service a unix `net.write` INLINE on the per-session bridge thread (write the
490+
/// bytes to the socket + notify the peer's readiness) instead of queuing it on the single dispatch
491+
/// task. Returns the written-byte count as the on-pump handler would, or `None` to fall through to the
492+
/// service loop (unknown/tcp socket, write error — the pump owns error/teardown handling).
493+
fn try_socket_write(
494+
&self,
495+
_socket_id: &str,
496+
_chunk_value: &serde_json::Value,
497+
) -> Option<serde_json::Value> {
498+
None
499+
}
500+
489501
/// Service the NON-BLOCKING `net.server_accept` "nothing pending" case inline,
490502
/// off the single shared service task. `listener_id` is arg0. The real accept
491503
/// needs `&mut kernel`/the socket table, so this path does only a
@@ -2897,6 +2909,23 @@ fn spawn_v8_event_bridge(
28972909
// Handle logging locally (produce stdout/stderr events)
28982910
if method == "_log" || method == "_error" {
28992911
let output = decode_bridge_output_args(&args);
2912+
// [tee] SECURE_EXEC_TEE_GUEST_STDERR=1 (default-OFF): echo guest process.stdout/
2913+
// stderr writes (incl. the gated guest-side probes POLLTRACE/[rt]/[pollstat]) to the
2914+
// sidecar's own stderr so they reach host.log. The desktop guests' Stdout/Stderr
2915+
// events are otherwise not surfaced in the boot harness, hiding all guest probes.
2916+
{
2917+
use std::sync::OnceLock;
2918+
static TEE: OnceLock<bool> = OnceLock::new();
2919+
if *TEE.get_or_init(|| {
2920+
std::env::var("SECURE_EXEC_TEE_GUEST_STDERR").as_deref() == Ok("1")
2921+
}) {
2922+
use std::io::Write;
2923+
let mut e = std::io::stderr().lock();
2924+
let _ = e.write_all(b"[guest] ");
2925+
let _ = e.write_all(&output);
2926+
let _ = e.write_all(b"\n");
2927+
}
2928+
}
29002929
// Respond to the bridge call
29012930
let _ = v8_session.send_bridge_response(
29022931
call_id,
@@ -2975,6 +3004,29 @@ fn spawn_v8_event_bridge(
29753004
}
29763005
}
29773006

3007+
// F10-INLINE-WRITE: service unix `net.write` (X protocol requests) INLINE on this
3008+
// per-session bridge thread, skipping the single dispatch task's ~636µs per-hop pickup
3009+
// latency (~4.7k writes/boot). The drain writes the bytes + notifies the peer, returning
3010+
// the written-byte count; `None` (unknown/tcp socket, decode/write error, or gate off)
3011+
// falls through to the service loop, which owns error/teardown handling.
3012+
if method == "net.write" {
3013+
if let Some(net_drain) = local_bridge.net_drain.clone() {
3014+
if let (Some(Value::String(socket_id)), Some(chunk_value)) =
3015+
(args.first(), args.get(1))
3016+
{
3017+
if let Some(value) =
3018+
net_drain.try_socket_write(socket_id.as_str(), chunk_value)
3019+
{
3020+
let payload = translate_legacy_bridge_value_to_v8(&value);
3021+
let cbor = v8_runtime::json_to_cbor_payload(&payload)
3022+
.unwrap_or_default();
3023+
let _ = v8_session.send_bridge_response(call_id, 0, cbor);
3024+
continue;
3025+
}
3026+
}
3027+
}
3028+
}
3029+
29783030
// C-lite: service the hot NON-BLOCKING kernel `__kernel_fd_poll`
29793031
// (`_kernelFdPollRaw`, timeout 0) INLINE on this per-session
29803032
// bridge thread, bypassing the single shared `select!` task

crates/execution/src/node_import_cache.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8894,6 +8894,10 @@ function pollTrace(msg) { if (globalThis.__polltrace) { try { process.stderr.wri
88948894
// L-opcount (SECURE_EXEC_POLL_SCAN=1, default-OFF): collapse net_poll's per-iteration drain + pipe-poll +
88958895
// gen-snapshot (3 sync-RPCs) into ONE net.poll_scan round-trip. Reduces the per-render op count ~1.5-2x.
88968896
try { if (process.env.SECURE_EXEC_POLL_SCAN === '1') globalThis.__pollScan = true; } catch (_e) {}
8897+
// D2.1 (SECURE_EXEC_RECV_OFFBROKER=1, default-OFF): make a blocking socket recv() block via the
8898+
// off-broker net.poll_wait (PollWaiterPool) instead of a blocking net.poll(50) that sleeps the 50ms
8899+
// clamp ON the single sync-RPC dispatch task and freezes EVERY guest. Mirrors net_poll's proven pattern.
8900+
try { if (process.env.SECURE_EXEC_RECV_OFFBROKER === '1') globalThis.__recvOffbroker = true; } catch (_e) {}
88978901
const prewarmOnly = process.env.AGENT_OS_WASM_PREWARM_ONLY === '1';
88988902
const maxMemoryBytesValue = Number(process.env.AGENT_OS_WASM_MAX_MEMORY_BYTES);
88998903
const maxMemoryPages = Number.isFinite(maxMemoryBytesValue)
@@ -12482,14 +12486,39 @@ const hostNetImport = {
1248212486
return writeGuestUint32(retReceivedPtr, 0);
1248312487
}
1248412488

12485-
const pollWaitMs =
12486-
deadline == null ? 50 : Math.max(0, Math.min(50, deadline - Date.now()));
12487-
if (deadline != null && pollWaitMs === 0) {
12488-
return WASI_ERRNO_AGAIN;
12489-
}
12490-
pollHostNetSocket(socket, pollWaitMs);
12491-
if (deadline != null && Date.now() >= deadline) {
12492-
return WASI_ERRNO_AGAIN;
12489+
if (globalThis.__recvOffbroker) {
12490+
// D2.1: block OFF the shared sync-RPC broker. A blocking net.poll(wait) sleeps `wait` (up to the
12491+
// 50ms clamp) ON the single dispatch task, freezing EVERY guest (measured: 8×50ms co-boot
12492+
// freezes). Mirror net_poll's proven off-broker pattern: snapshot the process readiness
12493+
// generation, non-blocking-drain THIS socket, then net.poll_wait — which defers to the
12494+
// PollWaiterPool so only this guest's recv blocks, exactly like a native recv() blocks only its
12495+
// own thread. Snapshotting the generation BEFORE the drain is the lost-wakeup guard: data landing
12496+
// during/after the drain advances the generation past __gen, so poll_wait returns immediately.
12497+
const __genR = callSyncRpc('net.poll_wait', [0, 0]);
12498+
const __gen = __genR && typeof __genR.generation === 'number' ? __genR.generation : 0;
12499+
pollHostNetSocket(socket, 0);
12500+
if (socket.readChunks.length > 0 || socket.lastError ||
12501+
socket.readableEnded || socket.closed || !socket.socketId) {
12502+
continue; // loop top consumes the drained bytes / returns EOF / fault
12503+
}
12504+
const remain = deadline == null ? 1000 : Math.max(0, deadline - Date.now());
12505+
if (deadline != null && remain === 0) {
12506+
return WASI_ERRNO_AGAIN;
12507+
}
12508+
callSyncRpc('net.poll_wait', [__gen, remain]); // off-broker block on readiness (sidecar clamps)
12509+
if (deadline != null && Date.now() >= deadline) {
12510+
return WASI_ERRNO_AGAIN;
12511+
}
12512+
} else {
12513+
const pollWaitMs =
12514+
deadline == null ? 50 : Math.max(0, Math.min(50, deadline - Date.now()));
12515+
if (deadline != null && pollWaitMs === 0) {
12516+
return WASI_ERRNO_AGAIN;
12517+
}
12518+
pollHostNetSocket(socket, pollWaitMs);
12519+
if (deadline != null && Date.now() >= deadline) {
12520+
return WASI_ERRNO_AGAIN;
12521+
}
1249312522
}
1249412523
}
1249512524
} catch {

crates/execution/src/wasm.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,6 +2451,8 @@ fn build_wasm_internal_env(
24512451
"SECURE_EXEC_WAKEPROF",
24522452
"SECURE_EXEC_RTPROBE",
24532453
"SECURE_EXEC_DRAINPROF",
2454+
"SECURE_EXEC_RECV_OFFBROKER",
2455+
"SECURE_EXEC_POLL_TRACE",
24542456
] {
24552457
if let Ok(value) = std::env::var(key) {
24562458
internal_env.insert(key.to_string(), value);

crates/sidecar/src/execution.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,8 @@ impl ActiveProcess {
454454
events: std::sync::Arc::clone(&socket.events),
455455
event_sender: socket.event_sender.clone(),
456456
remote_path: socket.remote_path.clone(),
457+
stream: std::sync::Arc::clone(&socket.stream),
458+
peer_readiness: socket.peer_readiness.clone(),
457459
},
458460
);
459461
}
@@ -14288,6 +14290,16 @@ fn new_unix_inline_drain(
1428814290
/// committed behavior). Set to `1`/`true` to inject the inline net-drain + kernel
1428914291
/// poll handle so those hops are serviced off the funnel on the per-session
1429014292
/// bridge thread.
14293+
fn inline_socket_data_enabled() -> bool {
14294+
// F10-INLINE-WRITE (SECURE_EXEC_INLINE_SOCKET_DATA=1, default-OFF): service unix `net.write` on the
14295+
// per-session bridge thread instead of the single dispatch task, removing the ~636µs pickup latency for
14296+
// the ~4.7k X-request writes/boot. Gated for A/B + instant revert while validating determinism.
14297+
static EN: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14298+
*EN.get_or_init(|| {
14299+
std::env::var("SECURE_EXEC_INLINE_SOCKET_DATA").as_deref() == Ok("1")
14300+
})
14301+
}
14302+
1429114303
fn inline_dispatch_enabled() -> bool {
1429214304
// DEFAULT-ON (2026-06-30): inline net.poll + non-blocking __kernel_fd_poll on the per-session bridge
1429314305
// thread (levers A + C-lite). Measured against the TRUSTWORTHY glyph-render ir metric it is a clean
@@ -14453,6 +14465,43 @@ impl secure_exec_execution::InlineNetDrain for UnixInlineNetDrain {
1445314465
}
1445414466
}
1445514467

14468+
fn try_socket_write(&self, socket_id: &str, chunk_value: &Value) -> Option<Value> {
14469+
// F10-INLINE-WRITE (gated SECURE_EXEC_INLINE_SOCKET_DATA): write the bytes to the socket + notify
14470+
// the peer INLINE on the per-session bridge thread, mirroring the on-pump `net.write` handler
14471+
// byte-for-byte, so the ~4.7k X-request writes/boot skip the ~636µs dispatch-pickup latency. Fall
14472+
// through (None) for any socket this drain can't see or a write error — the service loop owns
14473+
// error/teardown handling.
14474+
if !inline_socket_data_enabled() {
14475+
return None;
14476+
}
14477+
// Decode the chunk exactly as the on-pump handler does (string or base64 bytes payload); fall
14478+
// through on any decode error so the service loop reports it identically.
14479+
let chunk = javascript_sync_rpc_bytes_arg(std::slice::from_ref(chunk_value), 0, "net.write chunk")
14480+
.ok()?;
14481+
let chunk = chunk.as_slice();
14482+
let registry = self.registry.as_ref()?;
14483+
// Brief registry lock: clone the InlineSock, then release before touching the stream (same lock
14484+
// discipline as try_poll — the service loop never locks the registry while holding the stream).
14485+
let sock = {
14486+
let registry = registry.lock().ok()?;
14487+
registry.get(socket_id).cloned()?
14488+
};
14489+
{
14490+
xtrace_dump(sock.remote_path.as_deref(), "C>S", chunk);
14491+
let mut stream = sock.stream.lock().ok()?;
14492+
use std::io::Write;
14493+
if stream.write_all(chunk).is_err() {
14494+
return None; // fall through to the service loop for error/teardown handling
14495+
}
14496+
}
14497+
// Wake the peer (e.g. the X server) now that its request is on the wire — the SAME readiness object
14498+
// + notify() the on-pump handler uses (lever 1), so there is no lost-wakeup window.
14499+
if let Some(peer) = &sock.peer_readiness {
14500+
peer.notify();
14501+
}
14502+
Some(json!(chunk.len()))
14503+
}
14504+
1445614505
fn try_fd_poll(&self, fds: &[u32]) -> Option<Value> {
1445714506
let (handle, kernel_pid) = self.fd_poll.as_ref()?;
1445814507
// Mirror the service handler: an empty fd list yields an empty array,
@@ -21502,6 +21551,34 @@ where
2150221551
);
2150321552
return Ok(json!({ "generation": current }));
2150421553
}
21554+
// [deadprobe] D1 Phase-1 (SECURE_EXEC_DEADLINE_PROBE=1): we are about to block (guest drained
21555+
// nothing, readiness unchanged past last_seen). Non-blocking-poll this process's socket OS
21556+
// buffers: data already readable here means the per-socket reader thread is behind and the wake
21557+
// will land late (a = LATE notify); empty means a genuine no-data wait (b). Non-consuming.
21558+
if crate::state::deadline_probe_enabled() {
21559+
use std::os::fd::AsFd;
21560+
let mut os_readable = false;
21561+
for sock in process.unix_sockets.values() {
21562+
if let Ok(guard) = sock.stream.lock() {
21563+
let mut fds =
21564+
[nix::poll::PollFd::new(guard.as_fd(), nix::poll::PollFlags::POLLIN)];
21565+
if nix::poll::poll(&mut fds, nix::poll::PollTimeout::ZERO).unwrap_or(0) > 0
21566+
&& fds[0]
21567+
.revents()
21568+
.is_some_and(|r| r.intersects(nix::poll::PollFlags::POLLIN))
21569+
{
21570+
os_readable = true;
21571+
}
21572+
}
21573+
if os_readable {
21574+
break;
21575+
}
21576+
}
21577+
crate::state::deadline_probe_record(
21578+
std::sync::Arc::as_ptr(&readiness) as usize,
21579+
os_readable,
21580+
);
21581+
}
2150521582
// L-L fd-scoped wakeups (SECURE_EXEC_FD_SCOPED_POLL): if the guest passed the host-net socket
2150621583
// ids it is awaiting (arg 2) and EVERY one resolves to a keyed unix socket in THIS process,
2150721584
// collect their per-source keys + current-generation snapshots so the pool worker completes

crates/sidecar/src/service.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,20 @@ impl JavascriptSocketPathContext {
829829

830830
// ActiveProcess, NetworkResourceCounts moved to crate::state
831831

832+
// [rpc-block] threshold (microseconds), env-tunable for funnel profiling. Default 300ms preserves the
833+
// original probe; lower it (e.g. SECURE_EXEC_RPC_BLOCK_US=1000) to capture the per-method cumulative
834+
// histogram of what holds the single dispatch task during concurrent boot. Observability-only.
835+
fn rpc_block_threshold_us() -> u64 {
836+
use std::sync::OnceLock;
837+
static T: OnceLock<u64> = OnceLock::new();
838+
*T.get_or_init(|| {
839+
std::env::var("SECURE_EXEC_RPC_BLOCK_US")
840+
.ok()
841+
.and_then(|v| v.parse().ok())
842+
.unwrap_or(300_000)
843+
})
844+
}
845+
832846
pub struct NativeSidecar<B> {
833847
pub(crate) config: NativeSidecarConfig,
834848
pub(crate) bridge: SharedBridge<B>,
@@ -2250,7 +2264,7 @@ where
22502264
if let Some(s) = __m0 {
22512265
if !poll_deferred.get() {
22522266
let t = secure_exec_bridge::perf_now_micros().saturating_sub(s);
2253-
if t > 300_000 {
2267+
if t > rpc_block_threshold_us() {
22542268
eprintln!("[rpc-block] {} took {}us", request.method, t);
22552269
}
22562270
}

crates/sidecar/src/state.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,57 @@ pub(crate) fn wakeprof_record(key: usize, cause_idx: usize) {
518518
}
519519
}
520520

521+
// [deadprobe] D1 Phase-1 mechanism confirmation (SECURE_EXEC_DEADLINE_PROBE=1, default-OFF; run alongside
522+
// SECURE_EXEC_WAKEPROF=1 so the pid/name maps are populated). At the moment a net.poll_wait is ABOUT TO BLOCK
523+
// (guest drained nothing, readiness unchanged), the handler non-blocking-polls this process's socket OS
524+
// buffers. If data is ALREADY readable there, the per-socket reader thread is BEHIND — a client's bytes sit
525+
// unread in the kernel buffer, so the notify will land after the 50ms deadline (a = LATE notify). If empty,
526+
// the block is a genuine no-data wait (b). Per-process, reuses the wakeprof pid/name maps. Diagnostic only.
527+
pub(crate) fn deadline_probe_enabled() -> bool {
528+
static EN: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
529+
*EN.get_or_init(|| {
530+
std::env::var("SECURE_EXEC_DEADLINE_PROBE").map(|v| v == "1").unwrap_or(false)
531+
})
532+
}
533+
534+
pub(crate) fn deadline_probe_record(key: usize, os_readable: bool) {
535+
if !deadline_probe_enabled() {
536+
return;
537+
}
538+
static REG: std::sync::OnceLock<std::sync::Mutex<BTreeMap<usize, [u64; 2]>>> =
539+
std::sync::OnceLock::new();
540+
let reg = REG.get_or_init(|| std::sync::Mutex::new(BTreeMap::new()));
541+
static TOTAL: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
542+
let n = TOTAL.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + 1;
543+
if let Ok(mut reg) = reg.lock() {
544+
let entry = reg.entry(key).or_insert([0u64; 2]); // [empty, os_readable]
545+
entry[usize::from(os_readable)] = entry[usize::from(os_readable)].saturating_add(1);
546+
if n % 2000 == 0 {
547+
let pids = wakeprof_pidmap().lock().map(|m| m.clone()).unwrap_or_default();
548+
let names = wakeprof_namemap().lock().map(|m| m.clone()).unwrap_or_default();
549+
eprintln!(
550+
"[deadprobe] === block-entry OS-buffer readability (a=LATE-notify if OSdata%% high) ==="
551+
);
552+
for (k, c) in reg.iter() {
553+
let tot = c[0] + c[1];
554+
if tot == 0 {
555+
continue;
556+
}
557+
let pid = pids.get(k).copied().unwrap_or(0);
558+
let name = names.get(&pid).map(|s| s.as_str()).unwrap_or("?");
559+
eprintln!(
560+
"[deadprobe] {:<16} pid={} block_entry_with_OSdata={} empty={} ({:.1}% had-data)",
561+
name,
562+
pid,
563+
c[1],
564+
c[0],
565+
(c[1] as f64) * 100.0 / (tot as f64)
566+
);
567+
}
568+
}
569+
}
570+
}
571+
521572
fn wakeprof_dump(reg: &BTreeMap<usize, [u64; 7]>) {
522573
let pids = wakeprof_pidmap().lock().map(|m| m.clone()).unwrap_or_default();
523574
let names = wakeprof_namemap().lock().map(|m| m.clone()).unwrap_or_default();
@@ -1624,6 +1675,14 @@ pub(crate) struct InlineSock {
16241675
pub(crate) events: Arc<Mutex<Receiver<JavascriptTcpSocketEvent>>>,
16251676
pub(crate) event_sender: Sender<JavascriptTcpSocketEvent>,
16261677
pub(crate) remote_path: Option<String>,
1678+
/// The socket's write half (shared `Arc<Mutex<UnixStream>>`), so the per-session bridge thread can
1679+
/// service `net.write` INLINE (F10-INLINE-WRITE) instead of queuing it on the single dispatch task —
1680+
/// removing the ~636µs per-hop pickup latency for the 4.7k net.writes/boot (X requests).
1681+
pub(crate) stream: Arc<Mutex<UnixStream>>,
1682+
/// The PEER's readiness (e.g. the X server) to `notify()` after an inline write lands its request, so
1683+
/// the peer's blocked `net.poll_wait` wakes immediately — the SAME object + notify the on-pump handler
1684+
/// uses (lever 1), so no lost wakeup. `None` when unpaired.
1685+
pub(crate) peer_readiness: Option<Arc<SocketReadiness>>,
16271686
}
16281687

16291688
/// Per-process registry of inline-drainable unix sockets, keyed by the

0 commit comments

Comments
 (0)