mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
feat: 远程分片加绑定门与认证门,worker 无特权口径落文档
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
//! Worker 模式:远程分片的执行半区。连监督者端点收窗口指派帧,in-process
|
||||
//! 执行测试,发心跳/失败/现场/DONE 帧。
|
||||
//!
|
||||
//! 认证门:连接后首帧发 `Auth` 事件帧(token 经 `OXIDE_TEST262_WORKER_TOKEN`
|
||||
//! 带外注入),读回 `AuthOk` 命令帧才进主循环;缺失 token、读超时、首帧非
|
||||
//! `AuthOk` 均报错退出。握手必须在看门狗线程启动前完成,否则看门狗会把
|
||||
//! `AuthOk` 帧当命令帧消费。
|
||||
//!
|
||||
//! 看门狗线程独立读控制帧(kill → 置标志),主线程在测试间隙检查标志后从
|
||||
//! 主线程上下文发现场帧再退出。`KernelCore` 是 `!Send`,看门狗不得触引擎;
|
||||
//! 现场帧内容来自主线程上下文(last-pc 采样行)。断连按死亡处理(恢复由
|
||||
//! 监督者侧同构完成)。
|
||||
|
||||
use std::io::BufReader;
|
||||
use std::net::TcpStream;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
@@ -15,7 +21,7 @@ use crate::config::RunConfig;
|
||||
use crate::harness::{HarnessModuleCache, HarnessPrefixCache, HarnessSources, HARNESS};
|
||||
use crate::judge::{categorize_fail, TestOutcome};
|
||||
use crate::runner::{build_runner_engine, process_path, CURRENT_TEST_PATH};
|
||||
use crate::shard_protocol::{encode_event, parse_command, FrameReader, ShardCommand, ShardEvent};
|
||||
use crate::shard_protocol::{encode_event, parse_command, FrameReader, ShardCommand, ShardEvent, AUTH_READ_TIMEOUT};
|
||||
use crate::stats::RunStats;
|
||||
use oxide_net::Conn;
|
||||
|
||||
@@ -34,11 +40,12 @@ enum ChannelMsg {
|
||||
Kill,
|
||||
}
|
||||
|
||||
/// Worker 模式入口:连监督者端点,收窗口指派帧,in-process 执行,发帧。
|
||||
/// Worker 模式入口:连监督者端点,过认证门,收窗口指派帧,in-process 执行,发帧。
|
||||
///
|
||||
/// 端点经 `OXIDE_TEST262_WORKER_ENDPOINT` 环境变量传入(host:port 形态)。
|
||||
/// 看门狗线程独立读控制帧(kill → 置标志),主线程在测试间隙检查标志后
|
||||
/// 发现场帧再退出。断连按死亡处理(恢复由监督者侧完成)。
|
||||
/// 端点经 `OXIDE_TEST262_WORKER_ENDPOINT` 环境变量传入(host:port 形态),
|
||||
/// token 经 `OXIDE_TEST262_WORKER_TOKEN` 带外注入(监督者前台打印,不经网络
|
||||
/// 分发)。看门狗线程独立读控制帧(kill → 置标志),主线程在测试间隙检查
|
||||
/// 标志后发现场帧再退出。断连按死亡处理(恢复由监督者侧完成)。
|
||||
pub(crate) fn run_worker(config: &RunConfig) -> bool {
|
||||
let endpoint = match std::env::var("OXIDE_TEST262_WORKER_ENDPOINT") {
|
||||
Ok(ep) => ep,
|
||||
@@ -47,6 +54,16 @@ pub(crate) fn run_worker(config: &RunConfig) -> bool {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// token 带外注入:缺失或为空在连接前报错退出,不占连接。
|
||||
let token = match std::env::var("OXIDE_TEST262_WORKER_TOKEN") {
|
||||
Ok(t) if !t.is_empty() => t,
|
||||
_ => {
|
||||
eprintln!(
|
||||
"worker mode requires OXIDE_TEST262_WORKER_TOKEN (out-of-band from the supervisor's foreground print)"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let stream = match TcpStream::connect(&endpoint) {
|
||||
Ok(s) => s,
|
||||
@@ -57,17 +74,58 @@ pub(crate) fn run_worker(config: &RunConfig) -> bool {
|
||||
};
|
||||
let mut conn = Conn::Tcp(stream);
|
||||
|
||||
// 看门狗线程:独立读控制帧。指派帧经通道转主线程(主线程持引擎,
|
||||
// 看门狗不得触引擎);kill 帧置标志并经通道发 kill 信号(主线程阻塞
|
||||
// 在通道时也能收到,不依赖标志轮询)。
|
||||
// 认证门:读超时 30 秒(半开连接不永久占进程),发 Auth 帧后读回首帧,
|
||||
// 必须是 AuthOk,否则报错退出。握手必须在看门狗线程启动前完成:看门狗
|
||||
// 独立读命令帧,提前启动会消费 AuthOk 帧。
|
||||
if conn.set_read_timeout(Some(AUTH_READ_TIMEOUT)).is_err() {
|
||||
eprintln!("worker: failed to set read timeout");
|
||||
return false;
|
||||
}
|
||||
let auth_frame = encode_event(&ShardEvent::Auth { token });
|
||||
if conn.write_all(auth_frame.as_bytes()).is_err() {
|
||||
eprintln!("worker: failed to send auth frame");
|
||||
return false;
|
||||
}
|
||||
let mut reader = FrameReader::new(BufReader::new(conn));
|
||||
let first = match reader.read_frame() {
|
||||
Ok(Some(frame)) => frame,
|
||||
Ok(None) => {
|
||||
eprintln!("worker: supervisor closed the connection before auth ok");
|
||||
return false;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("worker: failed to read auth ok frame: {e}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if !matches!(parse_command(&first), Ok(ShardCommand::AuthOk)) {
|
||||
eprintln!("worker: supervisor did not confirm auth (first frame is not authok)");
|
||||
return false;
|
||||
}
|
||||
// 握手完成:清读超时(看门狗阻塞读不得超时),克隆写句柄供主线程发
|
||||
// 事件帧,读句柄交给看门狗。
|
||||
if reader.get_mut().get_mut().set_read_timeout(None).is_err() {
|
||||
eprintln!("worker: failed to clear read timeout");
|
||||
return false;
|
||||
}
|
||||
let writer = match reader.get_mut().get_mut().try_clone() {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
eprintln!("worker: failed to clone conn for writer: {e}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let mut conn = writer;
|
||||
|
||||
// 看门狗线程:独立读控制帧(读句柄移入)。指派帧经通道转主线程
|
||||
// (主线程持引擎,看门狗不得触引擎);kill 帧置标志并经通道发 kill
|
||||
// 信号(主线程阻塞在通道时也能收到,不依赖标志轮询)。
|
||||
let kill_flag = Arc::new(AtomicBool::new(false));
|
||||
let (msg_tx, msg_rx) = mpsc::channel::<ChannelMsg>();
|
||||
let watchdog_kill = Arc::clone(&kill_flag);
|
||||
let watchdog_tx = msg_tx;
|
||||
let watchdog_conn = conn.try_clone().expect("failed to clone conn for watchdog");
|
||||
let watchdog_buf = std::io::BufReader::new(watchdog_conn);
|
||||
std::thread::spawn(move || {
|
||||
let mut reader = FrameReader::new(watchdog_buf);
|
||||
let mut reader = reader;
|
||||
loop {
|
||||
match reader.read_frame() {
|
||||
Ok(Some(frame)) => match parse_command(&frame) {
|
||||
@@ -79,6 +137,11 @@ pub(crate) fn run_worker(config: &RunConfig) -> bool {
|
||||
}) => {
|
||||
let _ = watchdog_tx.send(ChannelMsg::Assign(AssignWindow { wstart, paths }));
|
||||
}
|
||||
Ok(ShardCommand::AuthOk) => {
|
||||
// AuthOk 只出现一次且已被主线程握手消费;重复到达
|
||||
// 忽略(协议上监督者不会重发)。
|
||||
continue;
|
||||
}
|
||||
Ok(ShardCommand::Kill) => {
|
||||
watchdog_kill.store(true, Ordering::SeqCst);
|
||||
let _ = watchdog_tx.send(ChannelMsg::Kill);
|
||||
@@ -235,3 +298,17 @@ fn send_scene_frame(conn: &mut Conn) {
|
||||
eprintln!("worker: failed to send scene frame: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// token 缺失时 worker 在连接前报错退出(不占连接)。
|
||||
#[test]
|
||||
fn worker_missing_token_exits_before_connect() {
|
||||
std::env::set_var("OXIDE_TEST262_WORKER_ENDPOINT", "127.0.0.1:1");
|
||||
std::env::remove_var("OXIDE_TEST262_WORKER_TOKEN");
|
||||
let config = RunConfig::new();
|
||||
assert!(!run_worker(&config), "token 缺失应报错退出");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user