#![allow(dead_code)]
use anyhow::Result;
#[cfg(not(all(target_os = "linux", feature = "hyperlight")))]
use anyhow::bail;
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
use anyhow::Context;
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
use hyperlight_wasm::{LoadedWasmSandbox, SandboxBuilder, WasmSandbox};
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
use std::collections::VecDeque;
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
use std::sync::{Arc, Mutex};
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
use std::time::Instant;
pub fn hyperlight_available() -> bool {
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
{
std::path::Path::new("/dev/kvm").exists() && hyperlight_wasm::is_hypervisor_present()
}
#[cfg(not(all(target_os = "linux", feature = "hyperlight")))]
{
false
}
}
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
pub struct HyperlightSandbox {
name: String,
sandbox: Option<LoadedWasmSandbox>,
}
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
impl HyperlightSandbox {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
sandbox: None,
}
}
pub fn init_with_wasm(&mut self, wasm_bytes: &[u8]) -> Result<()> {
let proto = SandboxBuilder::new()
.with_guest_heap_size(10_000_000) .with_guest_stack_size(1_000_000) .build()
.context("Failed to build Hyperlight sandbox")?;
let wasm_sandbox = proto
.load_runtime()
.context("Failed to load Hyperlight runtime")?;
let loaded = wasm_sandbox
.load_module_from_buffer(wasm_bytes)
.context("Failed to load Wasm module")?;
self.sandbox = Some(loaded);
Ok(())
}
pub fn call_function<Output: hyperlight_wasm::SupportedReturnType>(
&mut self,
name: &str,
) -> Result<Output> {
let sandbox = self
.sandbox
.as_mut()
.ok_or_else(|| anyhow::anyhow!("Sandbox not initialized"))?;
sandbox
.call_guest_function::<Output>(name, ())
.context("Failed to call guest function")
}
pub fn run_wasi(&mut self) -> Result<i32> {
self.call_function::<i32>("_start")
}
pub fn is_initialized(&self) -> bool {
self.sandbox.is_some()
}
pub fn name(&self) -> &str {
&self.name
}
}
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
#[derive(Debug, Clone)]
pub struct HyperlightPoolConfig {
pub min_warm: usize,
pub max_warm: usize,
pub guest_heap_size: u64,
pub guest_stack_size: u64,
}
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
impl Default for HyperlightPoolConfig {
fn default() -> Self {
Self {
min_warm: 3,
max_warm: 10,
guest_heap_size: 10_000_000, guest_stack_size: 1_000_000, }
}
}
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
pub struct PooledRuntime {
sandbox: WasmSandbox,
pub created_at: Instant,
}
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
pub struct HyperlightPool {
warm_pool: Arc<Mutex<VecDeque<PooledRuntime>>>,
config: HyperlightPoolConfig,
acquired_count: AtomicUsize,
shutdown: AtomicBool,
}
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
impl HyperlightPool {
pub fn new(config: HyperlightPoolConfig) -> Result<Self> {
if !hyperlight_available() {
anyhow::bail!("Hyperlight is not available on this system");
}
Ok(Self {
warm_pool: Arc::new(Mutex::new(VecDeque::new())),
config,
acquired_count: AtomicUsize::new(0),
shutdown: AtomicBool::new(false),
})
}
pub fn with_defaults() -> Result<Self> {
Self::new(HyperlightPoolConfig::default())
}
pub fn warm_up(&self) -> Result<()> {
let current = self.warm_pool.lock().unwrap().len();
let needed = self.config.min_warm.saturating_sub(current);
for _ in 0..needed {
if self.shutdown.load(Ordering::SeqCst) {
break;
}
match self.create_runtime() {
Ok(runtime) => {
self.warm_pool.lock().unwrap().push_back(runtime);
}
Err(e) => {
eprintln!("Failed to warm up Hyperlight runtime: {}", e);
return Err(e);
}
}
}
Ok(())
}
pub fn acquire(&self) -> Result<WasmSandbox> {
self.acquired_count.fetch_add(1, Ordering::SeqCst);
{
let mut pool = self.warm_pool.lock().unwrap();
if let Some(runtime) = pool.pop_front() {
return Ok(runtime.sandbox);
}
}
let runtime = self.create_runtime()?;
Ok(runtime.sandbox)
}
pub fn refill_if_needed(&self) -> Result<()> {
let current = self.warm_pool.lock().unwrap().len();
if current < self.config.min_warm {
self.refill_one()?;
}
Ok(())
}
pub fn add_one(&self) -> Result<()> {
let current = self.warm_pool.lock().unwrap().len();
if current >= self.config.max_warm {
return Ok(()); }
let runtime = self.create_runtime()?;
self.warm_pool.lock().unwrap().push_back(runtime);
Ok(())
}
pub fn warm_to(&self, count: usize) -> Result<()> {
let target = count.min(self.config.max_warm);
let current = self.warm_pool.lock().unwrap().len();
let needed = target.saturating_sub(current);
for _ in 0..needed {
if self.shutdown.load(Ordering::SeqCst) {
break;
}
match self.create_runtime() {
Ok(runtime) => {
self.warm_pool.lock().unwrap().push_back(runtime);
}
Err(e) => {
eprintln!("Failed to warm up Hyperlight runtime: {}", e);
return Err(e);
}
}
}
Ok(())
}
pub fn stats(&self) -> HyperlightPoolStats {
let warm_count = self.warm_pool.lock().unwrap().len();
HyperlightPoolStats {
warm_count,
acquired_total: self.acquired_count.load(Ordering::SeqCst),
config_min: self.config.min_warm,
config_max: self.config.max_warm,
}
}
fn create_runtime(&self) -> Result<PooledRuntime> {
let proto = SandboxBuilder::new()
.with_guest_heap_size(self.config.guest_heap_size)
.with_guest_stack_size(self.config.guest_stack_size)
.build()
.context("Failed to build Hyperlight sandbox")?;
let sandbox = proto
.load_runtime()
.context("Failed to load Hyperlight runtime")?;
Ok(PooledRuntime {
sandbox,
created_at: Instant::now(),
})
}
fn refill_one(&self) -> Result<()> {
let current = self.warm_pool.lock().unwrap().len();
if current >= self.config.max_warm {
return Ok(()); }
let runtime = self.create_runtime()?;
self.warm_pool.lock().unwrap().push_back(runtime);
Ok(())
}
pub fn shutdown(&self) {
self.shutdown.store(true, Ordering::SeqCst);
}
pub fn clear(&self) {
let mut pool = self.warm_pool.lock().unwrap();
pool.clear();
}
}
#[cfg(all(target_os = "linux", feature = "hyperlight"))]
#[derive(Debug, Clone)]
pub struct HyperlightPoolStats {
pub warm_count: usize,
pub acquired_total: usize,
pub config_min: usize,
pub config_max: usize,
}
#[cfg(not(all(target_os = "linux", feature = "hyperlight")))]
#[derive(Debug, Clone, Default)]
pub struct HyperlightPoolConfig {
pub min_warm: usize,
pub max_warm: usize,
pub guest_heap_size: u64,
pub guest_stack_size: u64,
}
#[cfg(not(all(target_os = "linux", feature = "hyperlight")))]
#[derive(Debug, Clone)]
pub struct HyperlightPoolStats {
pub warm_count: usize,
pub acquired_total: usize,
pub config_min: usize,
pub config_max: usize,
}
#[cfg(not(all(target_os = "linux", feature = "hyperlight")))]
pub struct HyperlightPool;
#[cfg(not(all(target_os = "linux", feature = "hyperlight")))]
impl HyperlightPool {
pub fn new(_config: HyperlightPoolConfig) -> Result<Self> {
bail!("Hyperlight pool is not available on this platform")
}
pub fn with_defaults() -> Result<Self> {
bail!("Hyperlight pool is not available on this platform")
}
pub fn warm_up(&self) -> Result<()> {
bail!("Hyperlight pool is not available on this platform")
}
pub fn stats(&self) -> HyperlightPoolStats {
HyperlightPoolStats {
warm_count: 0,
acquired_total: 0,
config_min: 0,
config_max: 0,
}
}
pub fn shutdown(&self) {}
pub fn clear(&self) {}
}
#[cfg(not(all(target_os = "linux", feature = "hyperlight")))]
pub struct HyperlightSandbox {
name: String,
}
#[cfg(not(all(target_os = "linux", feature = "hyperlight")))]
impl HyperlightSandbox {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
}
}
pub fn init_with_wasm(&mut self, _wasm_bytes: &[u8]) -> Result<()> {
bail!(
"Hyperlight is not available on this platform. Requires Linux with KVM and --features hyperlight"
)
}
pub fn call_function<T: Default>(&mut self, _name: &str) -> Result<T> {
bail!("Hyperlight is not available on this platform")
}
pub fn run_wasi(&mut self) -> Result<i32> {
bail!("Hyperlight is not available on this platform")
}
pub fn is_initialized(&self) -> bool {
false
}
pub fn name(&self) -> &str {
&self.name
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hyperlight_availability_check() {
let _ = hyperlight_available();
}
#[test]
fn test_sandbox_creation() {
let sandbox = HyperlightSandbox::new("test");
assert_eq!(sandbox.name(), "test");
assert!(!sandbox.is_initialized());
}
}