📚 Phase 12.5 最適化戦略 & Phase 15 セルフホスティング計画
Phase 12.5: MIR15最適化戦略 - コンパイラ丸投げ作戦 - optimization-strategy.txt: 詳細戦略(MIR側は軽量、コンパイラに丸投げ) - implementation-examples.md: 具体的な実装例 - debug-safety-comparison.md: 現在のDebugBox vs ChatGPT5提案の比較分析 Phase 15: Nyashセルフホスティング - 究極の目標 - self-hosting-plan.txt: 内蔵Craneliftによる実現計画 - technical-details.md: CompilerBox設計とブートストラップ手順 - README.md: セルフホスティングのビジョン 重要な知見: - LLVM統合完了済み(Phase 11)だが依存が重すぎる - Craneliftが現実的な選択肢(3-5MB vs LLVM 50-100MB) - 「コンパイラもBox、すべてがBox」の夢へ MASTERロードマップ更新済み
This commit is contained in:
@ -589,9 +589,28 @@ impl VM {
|
||||
let future_val = self.get_value(future)?;
|
||||
|
||||
if let VMValue::Future(ref future_box) = future_val {
|
||||
// This blocks until the future is ready (Condvar-based)
|
||||
// Cooperative wait with scheduler polling and timeout to avoid deadlocks
|
||||
let max_ms: u64 = std::env::var("NYASH_AWAIT_MAX_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(5000);
|
||||
let start = std::time::Instant::now();
|
||||
let mut spins = 0usize;
|
||||
while !future_box.ready() {
|
||||
// Poll GC/scheduler similar to Safepoint
|
||||
self.runtime.gc.safepoint();
|
||||
if let Some(s) = &self.runtime.scheduler { s.poll(); }
|
||||
std::thread::yield_now();
|
||||
spins += 1;
|
||||
if spins % 1024 == 0 { std::thread::sleep(std::time::Duration::from_millis(1)); }
|
||||
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
|
||||
// Timeout -> Result.Err("Timeout")
|
||||
let err = Box::new(crate::box_trait::StringBox::new("Timeout"));
|
||||
let rb = crate::boxes::result::NyashResultBox::new_err(err);
|
||||
let vm_value = VMValue::from_nyash_box(Box::new(rb));
|
||||
self.set_value(dst, vm_value);
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
}
|
||||
// Ready: get value and wrap into Result.Ok
|
||||
let result = future_box.get();
|
||||
// Wrap into Result.Ok for unified semantics
|
||||
let ok = crate::boxes::result::NyashResultBox::new_ok(result);
|
||||
let vm_value = VMValue::from_nyash_box(Box::new(ok));
|
||||
self.set_value(dst, vm_value);
|
||||
|
||||
@ -85,6 +85,7 @@ pub mod gc_config_box;
|
||||
pub mod aot_config_box;
|
||||
pub mod aot_compiler_box;
|
||||
pub mod task_group_box;
|
||||
pub mod token_box;
|
||||
|
||||
// Web専用Box群(ブラウザ環境でのみ利用可能)
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -124,6 +125,7 @@ pub use jit_hostcall_registry_box::JitHostcallRegistryBox;
|
||||
pub use aot_config_box::AotConfigBox;
|
||||
pub use aot_compiler_box::AotCompilerBox;
|
||||
pub use task_group_box::TaskGroupBox;
|
||||
pub use token_box::TokenBox;
|
||||
|
||||
// EguiBoxの再エクスポート(非WASM環境のみ)
|
||||
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
|
||||
|
||||
@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TaskGroupInner {
|
||||
pub(super) strong: Mutex<Vec<crate::boxes::future::FutureBox>>,
|
||||
pub strong: Mutex<Vec<crate::boxes::future::FutureBox>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
38
src/boxes/token_box.rs
Normal file
38
src/boxes/token_box.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox};
|
||||
use std::any::Any;
|
||||
|
||||
/// Cancellation token as a Box for structured concurrency
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TokenBox {
|
||||
base: BoxBase,
|
||||
token: crate::runtime::scheduler::CancellationToken,
|
||||
}
|
||||
|
||||
impl TokenBox {
|
||||
pub fn new() -> Self { Self { base: BoxBase::new(), token: crate::runtime::scheduler::CancellationToken::new() } }
|
||||
pub fn from_token(token: crate::runtime::scheduler::CancellationToken) -> Self { Self { base: BoxBase::new(), token } }
|
||||
pub fn cancel(&self) { self.token.cancel(); }
|
||||
pub fn is_cancelled(&self) -> bool { self.token.is_cancelled() }
|
||||
pub fn inner(&self) -> crate::runtime::scheduler::CancellationToken { self.token.clone() }
|
||||
}
|
||||
|
||||
impl BoxCore for TokenBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "CancellationToken(cancelled={})", self.token.is_cancelled())
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for TokenBox {
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(format!("CancellationToken(cancelled={})", self.token.is_cancelled())) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(o) = other.as_any().downcast_ref::<TokenBox>() {
|
||||
BoolBox::new(self.is_cancelled() == o.is_cancelled())
|
||||
} else { BoolBox::new(false) }
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
}
|
||||
@ -7,19 +7,34 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::boxes::result::NyashResultBox;
|
||||
use crate::box_trait::StringBox;
|
||||
|
||||
impl NyashInterpreter {
|
||||
/// await式を実行 - 非同期操作の結果を待機
|
||||
pub(super) fn execute_await(&mut self, expression: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let value = self.execute_expression(expression)?;
|
||||
|
||||
// FutureBoxなら待機して結果を取得
|
||||
// FutureBoxなら協調待機して Result.Ok/Err を返す
|
||||
if let Some(future) = value.as_any().downcast_ref::<FutureBox>() {
|
||||
future.wait_and_get()
|
||||
.map_err(|msg| RuntimeError::InvalidOperation { message: msg })
|
||||
let max_ms: u64 = std::env::var("NYASH_AWAIT_MAX_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(5000);
|
||||
let start = std::time::Instant::now();
|
||||
let mut spins = 0usize;
|
||||
while !future.ready() {
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
std::thread::yield_now();
|
||||
spins += 1;
|
||||
if spins % 1024 == 0 { std::thread::sleep(std::time::Duration::from_millis(1)); }
|
||||
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
|
||||
let err = Box::new(StringBox::new("Timeout"));
|
||||
return Ok(Box::new(NyashResultBox::new_err(err)));
|
||||
}
|
||||
}
|
||||
let v = future.get();
|
||||
Ok(Box::new(NyashResultBox::new_ok(v)))
|
||||
} else {
|
||||
// FutureBoxでなければそのまま返す
|
||||
Ok(value)
|
||||
// FutureBoxでなければ Ok(value) で返す
|
||||
Ok(Box::new(NyashResultBox::new_ok(value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,17 +153,27 @@ impl NyashInterpreter {
|
||||
}
|
||||
|
||||
|
||||
/// await式を実行 - Execute await expression
|
||||
/// await式を実行 - Execute await expression (Result.Ok/Err統一)
|
||||
pub(super) fn execute_await(&mut self, expression: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let value = self.execute_expression(expression)?;
|
||||
|
||||
// FutureBoxなら待機して結果を取得
|
||||
if let Some(future) = value.as_any().downcast_ref::<FutureBox>() {
|
||||
future.wait_and_get()
|
||||
.map_err(|msg| RuntimeError::InvalidOperation { message: msg })
|
||||
let max_ms: u64 = std::env::var("NYASH_AWAIT_MAX_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(5000);
|
||||
let start = std::time::Instant::now();
|
||||
let mut spins = 0usize;
|
||||
while !future.ready() {
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
std::thread::yield_now();
|
||||
spins += 1;
|
||||
if spins % 1024 == 0 { std::thread::sleep(std::time::Duration::from_millis(1)); }
|
||||
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
|
||||
let err = Box::new(crate::box_trait::StringBox::new("Timeout"));
|
||||
return Ok(Box::new(crate::boxes::result::NyashResultBox::new_err(err)));
|
||||
}
|
||||
}
|
||||
let v = future.get();
|
||||
Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok(v)))
|
||||
} else {
|
||||
// FutureBoxでなければそのまま返す
|
||||
Ok(value)
|
||||
Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok(value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,8 @@ impl NyashInterpreter {
|
||||
self.declare_local_variable(param, value.clone_or_share());
|
||||
}
|
||||
|
||||
// static関数の本体を実行
|
||||
// static関数の本体を実行(TaskGroupスコープ)
|
||||
crate::runtime::global_hooks::push_task_scope();
|
||||
let mut result = Box::new(VoidBox::new()) as Box<dyn NyashBox>;
|
||||
for statement in &body {
|
||||
result = self.execute_statement(statement)?;
|
||||
@ -89,6 +90,7 @@ impl NyashInterpreter {
|
||||
}
|
||||
|
||||
// local変数スタックを復元
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
self.restore_local_vars(saved_locals);
|
||||
|
||||
// outbox変数スタックを復元
|
||||
@ -204,25 +206,27 @@ impl NyashInterpreter {
|
||||
self.declare_local_variable(param, value.clone_or_share());
|
||||
}
|
||||
|
||||
// メソッドの本体を実行
|
||||
let mut result = Box::new(VoidBox::new()) as Box<dyn NyashBox>;
|
||||
for statement in body {
|
||||
result = self.execute_statement(statement)?;
|
||||
|
||||
// return文チェック
|
||||
if let super::ControlFlow::Return(return_val) = &self.control_flow {
|
||||
result = return_val.clone_box();
|
||||
self.control_flow = super::ControlFlow::None;
|
||||
break;
|
||||
}
|
||||
// メソッドの本体を実行(TaskGroupスコープ)
|
||||
crate::runtime::global_hooks::push_task_scope();
|
||||
let mut result = Box::new(VoidBox::new()) as Box<dyn NyashBox>;
|
||||
for statement in body {
|
||||
result = self.execute_statement(statement)?;
|
||||
|
||||
// return文チェック
|
||||
if let super::ControlFlow::Return(return_val) = &self.control_flow {
|
||||
result = return_val.clone_box();
|
||||
self.control_flow = super::ControlFlow::None;
|
||||
break;
|
||||
}
|
||||
|
||||
// local変数スタックを復元
|
||||
self.restore_local_vars(saved_locals);
|
||||
|
||||
idebug!("✅ Static box method completed: {}.{}", name, method);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
// local変数スタックを復元
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
self.restore_local_vars(saved_locals);
|
||||
|
||||
idebug!("✅ Static box method completed: {}.{}", name, method);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -598,7 +602,8 @@ impl NyashInterpreter {
|
||||
self.declare_local_variable(param, value.clone_or_share());
|
||||
}
|
||||
|
||||
// 親メソッドの本体を実行
|
||||
// 親メソッドの本体を実行(TaskGroupスコープ)
|
||||
crate::runtime::global_hooks::push_task_scope();
|
||||
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
|
||||
for statement in &body {
|
||||
result = self.execute_statement(statement)?;
|
||||
@ -615,6 +620,7 @@ impl NyashInterpreter {
|
||||
idebug!("🔍 DEBUG: FromCall {}.{} result: {}", parent, method, result.to_string_box().value);
|
||||
|
||||
// local変数スタックを復元
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
self.restore_local_vars(saved_locals);
|
||||
|
||||
Ok(result)
|
||||
|
||||
@ -74,11 +74,12 @@ impl NyashInterpreter {
|
||||
self.declare_local_variable(param, value.clone_or_share());
|
||||
}
|
||||
|
||||
// 関数本体を実行
|
||||
// 関数本体を実行(TaskGroupスコープをプッシュ)
|
||||
crate::runtime::global_hooks::push_task_scope();
|
||||
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
|
||||
for statement in &body {
|
||||
result = self.execute_statement(statement)?;
|
||||
|
||||
|
||||
// return文チェック
|
||||
if let super::ControlFlow::Return(return_val) = &self.control_flow {
|
||||
result = return_val.clone_box();
|
||||
@ -86,10 +87,11 @@ impl NyashInterpreter {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 🌍 local変数スタックを復元(関数呼び出し終了)
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
self.restore_local_vars(saved_locals);
|
||||
|
||||
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(RuntimeError::InvalidOperation {
|
||||
|
||||
@ -247,10 +247,12 @@ impl NyashInterpreter {
|
||||
}
|
||||
});
|
||||
|
||||
// FutureBoxを現在のTaskGroupに登録(暗黙グループ best-effort)
|
||||
crate::runtime::global_hooks::register_future_to_current_group(&future_box);
|
||||
// FutureBoxを変数に保存
|
||||
let future_box_instance = Box::new(future_box) as Box<dyn NyashBox>;
|
||||
self.set_variable(variable, future_box_instance)?;
|
||||
|
||||
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -763,7 +763,8 @@ impl IRBuilder for CraneliftBuilder {
|
||||
let entry = self.blocks[0];
|
||||
fb.append_block_params_for_function_params(entry);
|
||||
fb.switch_to_block(entry);
|
||||
// Defer sealing to allow entry PHI params when needed
|
||||
// Seal entry immediately (no predecessors by definition)
|
||||
fb.seal_block(entry);
|
||||
self.entry_block = Some(entry);
|
||||
self.current_block_index = Some(0);
|
||||
// Prepare single-exit epilogue artifacts (ret_block with one i64 param)
|
||||
@ -818,6 +819,16 @@ impl IRBuilder for CraneliftBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// Seal all blocks including entry and ret to satisfy builder constraints
|
||||
{
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
if let Some(en) = self.entry_block { fb.seal_block(en); }
|
||||
for b in &self.blocks { fb.seal_block(*b); }
|
||||
if let Some(rb) = self.ret_block { fb.seal_block(rb); }
|
||||
fb.finalize();
|
||||
}
|
||||
|
||||
// Declare a unique function symbol for JIT
|
||||
let sym_name = self.current_name.clone().unwrap_or_else(|| "jit_fn".to_string());
|
||||
let func_id = self.module.declare_function(&sym_name, Linkage::Local, &self.ctx.func.signature)
|
||||
@ -1798,23 +1809,20 @@ impl IRBuilder for ObjectBuilder {
|
||||
fn emit_jump(&mut self) { self.stats.3 += 1; }
|
||||
fn emit_branch(&mut self) { self.stats.3 += 1; }
|
||||
fn emit_return(&mut self) {
|
||||
// ObjectBuilder: return directly (no dedicated ret_block)
|
||||
use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||
// If function has no return, just return
|
||||
if fb.func.signature.returns.is_empty() {
|
||||
fb.ins().return_(&[]);
|
||||
fb.finalize();
|
||||
return;
|
||||
}
|
||||
// Normalize to function return type and return directly (ObjectBuilder has no ret_block)
|
||||
let mut v = if let Some(x) = self.value_stack.pop() { x } else { fb.ins().iconst(types::I64, 0) };
|
||||
let ret_ty = fb.func.signature.returns.get(0).map(|p| p.value_type).unwrap_or(types::I64);
|
||||
let v_ty = fb.func.dfg.value_type(v);
|
||||
if ret_ty != v_ty {
|
||||
if ret_ty == types::F64 && v_ty == types::I64 { v = fb.ins().fcvt_from_sint(types::F64, v); }
|
||||
else if ret_ty == types::I64 && v_ty == types::F64 { v = fb.ins().fcvt_to_sint(types::I64, v); }
|
||||
else if ret_ty == types::I64 { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); v = fb.ins().select(b1, one, zero); }
|
||||
if v_ty != types::I64 {
|
||||
if v_ty == types::F64 { v = fb.ins().fcvt_to_sint(types::I64, v); }
|
||||
else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); v = fb.ins().select(v, one, zero); }
|
||||
}
|
||||
fb.ins().return_(&[v]);
|
||||
fb.finalize();
|
||||
|
||||
@ -1546,6 +1546,40 @@ impl MirBuilder {
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_id, value: ConstValue::Void })?;
|
||||
return Ok(void_id);
|
||||
},
|
||||
("task", "currentToken") => {
|
||||
let result_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: Some(result_id),
|
||||
iface_name: "env.task".to_string(),
|
||||
method_name: "currentToken".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::READ,
|
||||
})?;
|
||||
return Ok(result_id);
|
||||
},
|
||||
("task", "cancelCurrent") => {
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.task".to_string(),
|
||||
method_name: "cancelCurrent".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::IO,
|
||||
})?;
|
||||
let void_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_id, value: ConstValue::Void })?;
|
||||
return Ok(void_id);
|
||||
},
|
||||
("future", "delay") => {
|
||||
let result_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: Some(result_id),
|
||||
iface_name: "env.future".to_string(),
|
||||
method_name: "delay".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::READ.add(Effect::Io),
|
||||
})?;
|
||||
return Ok(result_id);
|
||||
},
|
||||
("console", "readLine") => {
|
||||
// env.console.readLine() → ExternCall returning string
|
||||
let result_id = self.value_gen.next();
|
||||
|
||||
@ -118,7 +118,45 @@ impl NyashRunner {
|
||||
match interpreter.execute(ast) {
|
||||
Ok(result) => {
|
||||
println!("✅ Execution completed successfully!");
|
||||
println!("Result: {}", result.to_string_box().value);
|
||||
// Normalize display via semantics: prefer numeric, then string, then fallback
|
||||
let disp = {
|
||||
// Special-case: plugin IntegerBox → call .get to fetch numeric value
|
||||
if let Some(p) = result.as_any().downcast_ref::<nyash_rust::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
if p.box_type == "IntegerBox" {
|
||||
// Scope the lock strictly to this block
|
||||
let fetched = {
|
||||
let host = nyash_rust::runtime::get_global_plugin_host();
|
||||
let res = if let Ok(ro) = host.read() {
|
||||
if let Ok(Some(vb)) = ro.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) {
|
||||
if let Some(ib) = vb.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() {
|
||||
Some(ib.value.to_string())
|
||||
} else {
|
||||
Some(vb.to_string_box().value)
|
||||
}
|
||||
} else { None }
|
||||
} else { None };
|
||||
res
|
||||
};
|
||||
if let Some(s) = fetched { s } else {
|
||||
nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref())
|
||||
.map(|i| i.to_string())
|
||||
.or_else(|| nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()))
|
||||
.unwrap_or_else(|| result.to_string_box().value)
|
||||
}
|
||||
} else {
|
||||
nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref())
|
||||
.map(|i| i.to_string())
|
||||
.or_else(|| nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()))
|
||||
.unwrap_or_else(|| result.to_string_box().value)
|
||||
}
|
||||
} else {
|
||||
nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref())
|
||||
.map(|i| i.to_string())
|
||||
.or_else(|| nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()))
|
||||
.unwrap_or_else(|| result.to_string_box().value)
|
||||
}
|
||||
};
|
||||
println!("Result: {}", disp);
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("❌ Runtime error:\n{}", e.detailed_message(Some(&code)));
|
||||
|
||||
@ -105,6 +105,8 @@ pub fn push_task_scope() {
|
||||
if let Ok(mut st) = group_stack_cell().write() {
|
||||
st.push(std::sync::Arc::new(crate::boxes::task_group_box::TaskGroupInner { strong: std::sync::Mutex::new(Vec::new()) }));
|
||||
}
|
||||
// Set a fresh cancellation token for this scope (best-effort)
|
||||
set_current_group_token(CancellationToken::new());
|
||||
}
|
||||
|
||||
/// Pop a task scope. When depth reaches 0, join outstanding futures.
|
||||
@ -137,6 +139,8 @@ pub fn pop_task_scope() {
|
||||
join_all_registered_futures(ms);
|
||||
}
|
||||
}
|
||||
// Reset token (best-effort)
|
||||
set_current_group_token(CancellationToken::new());
|
||||
}
|
||||
|
||||
/// Perform a runtime safepoint and poll the scheduler if available.
|
||||
@ -174,3 +178,19 @@ pub fn spawn_task_with_token(name: &str, token: crate::runtime::scheduler::Cance
|
||||
f();
|
||||
false
|
||||
}
|
||||
|
||||
/// Spawn a delayed task via scheduler if available; returns true if scheduled.
|
||||
pub fn spawn_task_after(delay_ms: u64, name: &str, f: Box<dyn FnOnce() + Send + 'static>) -> bool {
|
||||
if let Ok(s) = sched_cell().read() {
|
||||
if let Some(sched) = s.as_ref() {
|
||||
sched.spawn_after(delay_ms, name, f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Fallback: run inline after blocking sleep
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_millis(delay_ms));
|
||||
f();
|
||||
});
|
||||
false
|
||||
}
|
||||
|
||||
@ -468,6 +468,17 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
("env.task", "cancelCurrent") => {
|
||||
let tok = crate::runtime::global_hooks::current_group_token();
|
||||
tok.cancel();
|
||||
Ok(None)
|
||||
}
|
||||
("env.task", "currentToken") => {
|
||||
// Return a TokenBox representing current task group's cancellation token
|
||||
let tok = crate::runtime::global_hooks::current_group_token();
|
||||
let tb = crate::boxes::token_box::TokenBox::from_token(tok);
|
||||
Ok(Some(Box::new(tb)))
|
||||
}
|
||||
("env.debug", "trace") => {
|
||||
// Minimal debug trace; prints to stderr when enabled
|
||||
if std::env::var("NYASH_DEBUG_TRACE").ok().as_deref() == Some("1") {
|
||||
@ -530,6 +541,21 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
Ok(Some(Box::new(crate::boxes::result::NyashResultBox::new_err(Box::new(crate::box_trait::StringBox::new("InvalidArgs"))))))
|
||||
}
|
||||
("env.future", "delay") => {
|
||||
// delay(ms) -> FutureBox resolved to void after ms
|
||||
use crate::box_trait::NyashBox as _;
|
||||
let fut = crate::boxes::future::FutureBox::new();
|
||||
let ms = if let Some(arg0) = args.get(0) {
|
||||
if let Some(i) = arg0.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { i.value.max(0) as u64 }
|
||||
else { arg0.to_string_box().value.trim().parse::<i64>().unwrap_or(0).max(0) as u64 }
|
||||
} else { 0 };
|
||||
let fut_setter = fut.clone();
|
||||
let _scheduled = crate::runtime::global_hooks::spawn_task_after(ms, "env.future.delay", Box::new(move || {
|
||||
fut_setter.set_result(Box::new(crate::box_trait::VoidBox::new()));
|
||||
}));
|
||||
crate::runtime::global_hooks::register_future_to_current_group(&fut);
|
||||
Ok(Some(Box::new(fut)))
|
||||
}
|
||||
("env.future", "spawn_instance") => {
|
||||
// spawn_instance(recv, method_name, args...) -> FutureBox
|
||||
// If a scheduler is available, schedule the call; else invoke synchronously.
|
||||
|
||||
Reference in New Issue
Block a user