Phase 10.7 - JIT統計とイベント機能の完成
主要な実装: - PHI(b1)統計追跡: phi_total_slots/phi_b1_slotsをJSON出力 - 関数単位統計API: JitStatsBox.perFunction()で詳細統計取得 - JITイベントシステム: compile/execute/fallback/trapをJSONL形式で記録 - Store/Load命令対応: ローカル変数を含む関数のJIT実行が可能に 新しいBox: - JitStatsBox: JIT統計の取得 - JitConfigBox: JIT設定の管理(将来用) - JitEventsBox: イベントのJSONL出力(将来用) - JitPolicyBox: 実行ポリシー管理(将来用) CLI拡張: - --jit-exec, --jit-stats, --jit-dump等のフラグ追加 - --jit-directモードでの独立JIT実行 - NYASH_JIT_*環境変数によるきめ細かい制御 ドキュメント: - Phase 10.7実装計画の詳細化 - Phase 10.9 (ビルトインBox JIT) の計画追加 - JIT統計JSONスキーマ v1の仕様化 ChatGPT5との共同開発により、JIT基盤が大幅に強化されました。 次はPhase 10.9でビルトインBoxのJIT対応を進め、 Python統合(Phase 10.1)への道を開きます。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
139
src/boxes/jit_config_box.rs
Normal file
139
src/boxes/jit_config_box.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, VoidBox, BoxCore, BoxBase};
|
||||
use crate::jit::config::JitConfig;
|
||||
use crate::interpreter::RuntimeError;
|
||||
use std::any::Any;
|
||||
use std::sync::RwLock;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JitConfigBox {
|
||||
base: BoxBase,
|
||||
pub config: RwLock<JitConfig>,
|
||||
}
|
||||
|
||||
impl JitConfigBox {
|
||||
pub fn new() -> Self { Self { base: BoxBase::new(), config: RwLock::new(JitConfig::from_env()) } }
|
||||
/// Update internal config flags from runtime capability probe
|
||||
pub fn from_runtime_probe(&self) -> Box<dyn NyashBox> {
|
||||
let caps = crate::jit::config::probe_capabilities();
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
if cfg.native_bool_abi && !caps.supports_b1_sig { cfg.native_bool_abi = false; }
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_flag(&self, name: &str, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
match name {
|
||||
"exec" => cfg.exec = on,
|
||||
"stats" => cfg.stats = on,
|
||||
"stats_json" => cfg.stats_json = on,
|
||||
"dump" => cfg.dump = on,
|
||||
"phi_min" => cfg.phi_min = on,
|
||||
"hostcall" => cfg.hostcall = on,
|
||||
"handle_debug" => cfg.handle_debug = on,
|
||||
"native_f64" => cfg.native_f64 = on,
|
||||
"native_bool" => cfg.native_bool = on,
|
||||
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi = on,
|
||||
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1 = on,
|
||||
_ => return Err(RuntimeError::InvalidOperation { message: format!("Unknown flag: {}", name) }),
|
||||
}
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
pub fn get_flag(&self, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
let b = match name {
|
||||
"exec" => cfg.exec,
|
||||
"stats" => cfg.stats,
|
||||
"stats_json" => cfg.stats_json,
|
||||
"dump" => cfg.dump,
|
||||
"phi_min" => cfg.phi_min,
|
||||
"hostcall" => cfg.hostcall,
|
||||
"handle_debug" => cfg.handle_debug,
|
||||
"native_f64" => cfg.native_f64,
|
||||
"native_bool" => cfg.native_bool,
|
||||
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi,
|
||||
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1,
|
||||
_ => return Err(RuntimeError::InvalidOperation { message: format!("Unknown flag: {}", name) }),
|
||||
};
|
||||
Ok(Box::new(BoolBox::new(b)))
|
||||
}
|
||||
pub fn set_threshold(&self, v: i64) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
if v <= 0 { cfg.threshold = None; }
|
||||
else { cfg.threshold = Some(v as u32); }
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
pub fn get_threshold(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
Box::new(IntegerBox::new(cfg.threshold.map(|v| v as i64).unwrap_or(0)))
|
||||
}
|
||||
pub fn to_json(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
let val = serde_json::json!({
|
||||
"exec": cfg.exec,
|
||||
"stats": cfg.stats,
|
||||
"stats_json": cfg.stats_json,
|
||||
"dump": cfg.dump,
|
||||
"threshold": cfg.threshold,
|
||||
"phi_min": cfg.phi_min,
|
||||
"hostcall": cfg.hostcall,
|
||||
"handle_debug": cfg.handle_debug,
|
||||
"native_f64": cfg.native_f64,
|
||||
"native_bool": cfg.native_bool,
|
||||
"native_bool_abi": cfg.native_bool_abi,
|
||||
"ret_bool_b1": cfg.ret_bool_b1,
|
||||
});
|
||||
Box::new(StringBox::new(val.to_string()))
|
||||
}
|
||||
pub fn from_json(&self, s: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
let v: serde_json::Value = serde_json::from_str(s).map_err(|e| RuntimeError::InvalidOperation { message: format!("Invalid JSON: {}", e) })?;
|
||||
if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) { cfg.exec = b; }
|
||||
if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) { cfg.stats = b; }
|
||||
if let Some(b) = v.get("stats_json").and_then(|x| x.as_bool()) { cfg.stats_json = b; }
|
||||
if let Some(b) = v.get("dump").and_then(|x| x.as_bool()) { cfg.dump = b; }
|
||||
if let Some(n) = v.get("threshold").and_then(|x| x.as_u64()) { cfg.threshold = Some(n as u32); }
|
||||
if let Some(b) = v.get("phi_min").and_then(|x| x.as_bool()) { cfg.phi_min = b; }
|
||||
if let Some(b) = v.get("hostcall").and_then(|x| x.as_bool()) { cfg.hostcall = b; }
|
||||
if let Some(b) = v.get("handle_debug").and_then(|x| x.as_bool()) { cfg.handle_debug = b; }
|
||||
if let Some(b) = v.get("native_f64").and_then(|x| x.as_bool()) { cfg.native_f64 = b; }
|
||||
if let Some(b) = v.get("native_bool").and_then(|x| x.as_bool()) { cfg.native_bool = b; }
|
||||
if let Some(b) = v.get("native_bool_abi").and_then(|x| x.as_bool()) { cfg.native_bool_abi = b; }
|
||||
if let Some(b) = v.get("ret_bool_b1").and_then(|x| x.as_bool()) { cfg.ret_bool_b1 = b; }
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
pub fn apply(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap().clone();
|
||||
// Apply to env for CLI parity
|
||||
cfg.apply_env();
|
||||
// Also set global current JIT config for hot paths (env-less)
|
||||
crate::jit::config::set_current(cfg);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn summary(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
let s = format!(
|
||||
"exec={} stats={} json={} dump={} thr={:?} phi_min={} hostcall={} hdbg={} f64={} bool={}",
|
||||
cfg.exec, cfg.stats, cfg.stats_json, cfg.dump, cfg.threshold,
|
||||
cfg.phi_min, cfg.hostcall, cfg.handle_debug, cfg.native_f64, cfg.native_bool
|
||||
);
|
||||
Box::new(StringBox::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitConfigBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitConfigBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for JitConfigBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitConfigBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitConfigBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap().clone();
|
||||
Box::new(JitConfigBox { base: self.base.clone(), config: RwLock::new(cfg) })
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
}
|
||||
41
src/boxes/jit_events_box.rs
Normal file
41
src/boxes/jit_events_box.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitEventsBox { base: BoxBase }
|
||||
|
||||
impl JitEventsBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
|
||||
|
||||
impl BoxCore for JitEventsBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitEventsBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for JitEventsBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("JitEventsBox") }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitEventsBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitEventsBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
}
|
||||
|
||||
impl JitEventsBox {
|
||||
pub fn set_path(&self, path: &str) -> Box<dyn NyashBox> {
|
||||
std::env::set_var("NYASH_JIT_EVENTS_PATH", path);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on { std::env::set_var("NYASH_JIT_EVENTS", "1"); }
|
||||
else { std::env::remove_var("NYASH_JIT_EVENTS"); }
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn log(&self, kind: &str, function: &str, note_json: &str) -> Box<dyn NyashBox> {
|
||||
let extra = serde_json::from_str::<serde_json::Value>(note_json).unwrap_or_else(|_| serde_json::json!({"note": note_json}));
|
||||
crate::jit::events::emit(kind, function, None, None, extra);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
|
||||
52
src/boxes/jit_policy_box.rs
Normal file
52
src/boxes/jit_policy_box.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitPolicyBox { base: BoxBase }
|
||||
|
||||
impl JitPolicyBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
|
||||
|
||||
impl BoxCore for JitPolicyBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitPolicyBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for JitPolicyBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
let p = crate::jit::policy::current();
|
||||
let s = format!("read_only={} whitelist={}", p.read_only, p.hostcall_whitelist.join(","));
|
||||
StringBox::new(s)
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitPolicyBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitPolicyBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
}
|
||||
|
||||
// Methods (exposed via VM dispatch):
|
||||
impl JitPolicyBox {
|
||||
pub fn set_flag(&self, name: &str, on: bool) -> Box<dyn NyashBox> {
|
||||
let mut cur = crate::jit::policy::current();
|
||||
match name {
|
||||
"read_only" | "readonly" => cur.read_only = on,
|
||||
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name)))
|
||||
}
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn get_flag(&self, name: &str) -> Box<dyn NyashBox> {
|
||||
let cur = crate::jit::policy::current();
|
||||
let v = match name { "read_only" | "readonly" => cur.read_only, _ => false };
|
||||
Box::new(BoolBox::new(v))
|
||||
}
|
||||
pub fn set_whitelist_csv(&self, csv: &str) -> Box<dyn NyashBox> {
|
||||
let mut cur = crate::jit::policy::current();
|
||||
cur.hostcall_whitelist = csv.split(',').map(|t| t.trim().to_string()).filter(|s| !s.is_empty()).collect();
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
|
||||
41
src/boxes/jit_stats_box.rs
Normal file
41
src/boxes/jit_stats_box.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, VoidBox, BoxCore, BoxBase};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitStatsBox { base: BoxBase }
|
||||
|
||||
impl JitStatsBox {
|
||||
pub fn new() -> Self { Self { base: BoxBase::new() } }
|
||||
pub fn to_json(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = crate::jit::config::current();
|
||||
let caps = crate::jit::config::probe_capabilities();
|
||||
let mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" };
|
||||
let payload = serde_json::json!({
|
||||
"version": 1,
|
||||
"abi_mode": mode,
|
||||
"abi_b1_enabled": cfg.native_bool_abi,
|
||||
"abi_b1_supported": caps.supports_b1_sig,
|
||||
"b1_norm_count": crate::jit::rt::b1_norm_get(),
|
||||
"ret_bool_hint_count": crate::jit::rt::ret_bool_hint_get(),
|
||||
"phi_total_slots": crate::jit::rt::phi_total_get(),
|
||||
"phi_b1_slots": crate::jit::rt::phi_b1_get(),
|
||||
});
|
||||
Box::new(StringBox::new(payload.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitStatsBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitStatsBox") }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for JitStatsBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(self.to_json().to_string_box().value) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitStatsBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitStatsBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
}
|
||||
@ -74,6 +74,10 @@ pub mod qr_box;
|
||||
pub mod sound_box;
|
||||
pub mod map_box;
|
||||
pub mod console_box;
|
||||
pub mod jit_config_box;
|
||||
pub mod jit_stats_box;
|
||||
pub mod jit_policy_box;
|
||||
pub mod jit_events_box;
|
||||
|
||||
// Web専用Box群(ブラウザ環境でのみ利用可能)
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -104,6 +108,10 @@ pub use qr_box::QRBox;
|
||||
pub use sound_box::SoundBox;
|
||||
pub use map_box::MapBox;
|
||||
pub use console_box::ConsoleBox;
|
||||
pub use jit_config_box::JitConfigBox;
|
||||
pub use jit_stats_box::JitStatsBox;
|
||||
pub use jit_policy_box::JitPolicyBox;
|
||||
pub use jit_events_box::JitEventsBox;
|
||||
|
||||
// EguiBoxの再エクスポート(非WASM環境のみ)
|
||||
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
|
||||
|
||||
@ -122,9 +122,34 @@ impl P2PBox {
|
||||
last_intent_name: Arc::new(RwLock::new(None)),
|
||||
};
|
||||
|
||||
// Note: InProcess callback registration is postponed until a unified
|
||||
// Transport subscription API is provided. For now, loopback tracing is
|
||||
// handled in send() when sending to self.
|
||||
// Minimal built-in system handler: auto-respond to sys.ping
|
||||
// This enables health checks via ping() without requiring user wiring.
|
||||
if attach_cb {
|
||||
// capture for receive-side traces
|
||||
let last_from = Arc::clone(&p2p.last_from);
|
||||
let last_intent = Arc::clone(&p2p.last_intent_name);
|
||||
// capture transport Arc to use inside handler
|
||||
let transport_arc_outer = Arc::clone(&p2p.transport);
|
||||
{
|
||||
if let Ok(mut t) = transport_arc_outer.write() {
|
||||
let transport_arc_for_cb = Arc::clone(&transport_arc_outer);
|
||||
t.register_intent_handler("sys.ping", Box::new(move |env| {
|
||||
if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); }
|
||||
if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); }
|
||||
// Reply asynchronously to avoid deep call stacks
|
||||
let to = env.from.clone();
|
||||
let reply = crate::boxes::IntentBox::new("sys.pong".to_string(), serde_json::json!({}));
|
||||
let transport_arc = Arc::clone(&transport_arc_for_cb);
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
if let Ok(transport) = transport_arc.read() {
|
||||
let _ = transport.send(&to, reply, Default::default());
|
||||
}
|
||||
});
|
||||
}));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
p2p
|
||||
}
|
||||
@ -134,6 +159,51 @@ impl P2PBox {
|
||||
let node_id = self.node_id.read().unwrap().clone();
|
||||
Box::new(StringBox::new(node_id))
|
||||
}
|
||||
|
||||
/// Blocking ping: send sys.ping to target and wait for sys.pong
|
||||
/// Returns BoolBox(true) on success within timeout, else false.
|
||||
pub fn ping_with_timeout(&self, to: Box<dyn NyashBox>, timeout_ms: u64) -> Box<dyn NyashBox> {
|
||||
use std::sync::{mpsc, Arc};
|
||||
let to_str = to.to_string_box().value;
|
||||
|
||||
// Create oneshot channel for pong
|
||||
let (tx, rx) = mpsc::channel::<()>();
|
||||
let active = Arc::new(AtomicBool::new(true));
|
||||
let active_cb = Arc::clone(&active);
|
||||
|
||||
// Register temporary transport-level handler for sys.pong
|
||||
if let Ok(mut t) = self.transport.write() {
|
||||
t.register_intent_handler("sys.pong", Box::new(move |env| {
|
||||
if active_cb.load(Ordering::SeqCst) {
|
||||
// record last receive for visibility
|
||||
// Note: we cannot access self here safely; rely on tx notify only
|
||||
let _ = env; // suppress unused
|
||||
let _ = tx.send(());
|
||||
}
|
||||
}));
|
||||
|
||||
// Send sys.ping
|
||||
let ping = IntentBox::new("sys.ping".to_string(), serde_json::json!({}));
|
||||
match t.send(&to_str, ping, Default::default()) {
|
||||
Ok(()) => { /* proceed to wait */ }
|
||||
Err(_) => {
|
||||
return Box::new(BoolBox::new(false));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Box::new(BoolBox::new(false));
|
||||
}
|
||||
|
||||
// Wait for pong with timeout
|
||||
let ok = rx.recv_timeout(std::time::Duration::from_millis(timeout_ms)).is_ok();
|
||||
active.store(false, Ordering::SeqCst);
|
||||
Box::new(BoolBox::new(ok))
|
||||
}
|
||||
|
||||
/// Convenience default-timeout ping (200ms)
|
||||
pub fn ping(&self, to: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
self.ping_with_timeout(to, 200)
|
||||
}
|
||||
|
||||
/// 特定ノードにメッセージを送信
|
||||
pub fn send(&self, to: Box<dyn NyashBox>, intent: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
@ -453,4 +523,28 @@ mod tests {
|
||||
let c1 = p.debug_active_handler_count(Box::new(StringBox::new("bye")));
|
||||
assert_eq!(c1.to_string_box().value, "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ping_success_between_two_nodes() {
|
||||
let alice = P2PBox::new("alice".to_string(), TransportKind::InProcess);
|
||||
let bob = P2PBox::new("bob".to_string(), TransportKind::InProcess);
|
||||
// bob has built-in sys.ping -> sys.pong
|
||||
let ok = alice.ping(Box::new(StringBox::new("bob")));
|
||||
if let Some(b) = ok.as_any().downcast_ref::<BoolBox>() {
|
||||
assert!(b.value);
|
||||
} else {
|
||||
panic!("ping did not return BoolBox");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ping_timeout_on_missing_node() {
|
||||
let alice = P2PBox::new("alice".to_string(), TransportKind::InProcess);
|
||||
let ok = alice.ping_with_timeout(Box::new(StringBox::new("nobody")), 20);
|
||||
if let Some(b) = ok.as_any().downcast_ref::<BoolBox>() {
|
||||
assert!(!b.value);
|
||||
} else {
|
||||
panic!("ping_with_timeout did not return BoolBox");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user