Skip to main content

PooledVm

Struct PooledVm 

Source
pub struct PooledVm<'a> { /* private fields */ }
Expand description

A Vm checked out of an Image’s hidden pool. Derefs to Vm, so every method on Vm is callable. On Drop the VM returns to the pool — the next Image::acquire gets a freshly snapshot-restored worker in ~5 ms.

Bound to the Image’s lifetime so the pool can’t outlive its owner. Acquires currently serialize on the pool’s single worker; concurrent acquires from one process block until the previous PooledVm is dropped.

Implementations§

Source§

impl PooledVm<'_>

Source

pub fn snapshot(&self, dest_dir: impl Into<PathBuf>) -> Result<Image, Error>

Capture a live snapshot of this pooled VM (Linux/KVM), non-consuming, so the builder can snapshot one VM repeatedly across layers. Delegates to Vm::snapshot_live.

Source

pub fn snapshot_diff( &self, dest_dir: impl Into<PathBuf>, base_path: &Path, ) -> Result<Image, Error>

Live differential snapshot against base_path (a full snapshot’s restore.snap) — non-consuming. Delegates to Vm::snapshot_diff_live.

Methods from Deref<Target = Vm>§

Source

pub fn vsock_path(&self) -> &Path

Path to the host-side unix socket that proxies bytes to / from the first TSI listener inside the guest. Connect to it with UnixStream::connect (or via Vm::connect).

Source

pub fn exec_path(&self) -> &Path

Path to the host-side unix socket that bridges to the in-guest exec agent (native AF_VSOCK on the guest side). Reachable once the agent lands in the initramfs and is running guest-side; until then dialing it returns an immediate EOF.

Source

pub fn exec<I, S>(&self, argv: I) -> Result<ExecChild>
where I: IntoIterator<Item = S>, S: Into<String>,

Spawn a process inside the running guest. Equivalent to docker exec. Returns an crate::exec::ExecChild handle you can read stdout/stderr from, write stdin to, and wait() for an exit status.

let image = Image::from_snapshot("path/to/snapshot")?;
let vm = Vm::start(&image, &VmConfig::new())?;
let mut child = vm.exec(["sh", "-c", "echo hi"])?;
let mut buf = String::new();
child.stdout().unwrap().read_to_string(&mut buf)?;
assert_eq!(buf, "hi\n");
child.wait()?;
Source

pub fn exec_builder(&self) -> ExecBuilder

Configurable exec — TTY, env vars, cwd, initial winsize, timeout, and the crate::exec::ExecBuilder::output convenience that drains stdio + collects exit status into one crate::exec::ExecOutcome.

Source

pub fn write_file(&self, path: &str, bytes: &[u8]) -> Result<()>

Write bytes to path inside the guest, atomically. Native vsock RPC — no exec, no shell. Roughly ~100 µs per call regardless of file size (up to the 12 MiB raw limit imposed by the agent’s frame cap).

The guest agent stages to a sibling tmp file then renames for atomicity, so partial writes don’t leave a half-baked file at path.

let image = Image::from_snapshot("path/to/snapshot")?;
let vm = image.start(&VmConfig::new())?;
vm.write_file("/tmp/main.rs", b"fn main() { println!(\"hi\"); }")?;
let out = vm.exec_builder()
    .argv(["rustc", "/tmp/main.rs", "-o", "/tmp/main"])
    .output()?;
assert!(out.success());
Source

pub fn read_file(&self, path: &str) -> Result<Vec<u8>>

Read path from inside the guest. Default cap is 32 MiB (raised in 0.5.0 from the prior 4 MiB to accommodate Playwright trace.zip artifacts and similar mid-sized blobs). For larger files use Vm::read_file_with_max_bytes with an explicit cap, or stream via Vm::exec (cat).

Source

pub fn read_file_with_max_bytes( &self, path: &str, max_bytes: u64, ) -> Result<Vec<u8>>

Like Vm::read_file but with an explicit byte cap. Returns an error if the file exceeds max_bytes so the caller never gets a partial result silently. Cap is enforced inside the guest before any bytes leave it.

Source

pub fn workload_signal(&self, signum: i32) -> Result<()>

Send a Unix signal to the guest’s main workload process. Use this for docker stop-style graceful shutdown:

vm.workload_signal(libc::SIGTERM)?;
// ...wait for the workload to clean up...
vm.stop()?;

Implementation: dials the in-guest exec agent on a fresh connection with a CONTROL frame; the agent reads /run/supermachine-workload.pid (written by init-oci’s PID-1 supervisor) and kill(pid, signum) it. Returns Err(NotFound) if the workload hasn’t been spawned yet (only happens during the bake-time window).

Source

pub fn connect(&self) -> Result<UnixStream>

Connect to the guest’s first TSI listener. The returned UnixStream is byte-equivalent to a TcpStream to the guest’s :80 (or whatever port it bound).

For HTTP, just write a request and read the response: supermachine’s vsock-mux is a transparent proxy.

Source

pub fn request_balloon_inflate(&self, pages: u32) -> bool

Reclaim guest memory via virtio-balloon: ask the guest to release pages 4 KiB pages, which the device then madvise(MADV_FREE)s on the host RAM map (the kernel can drop them under pressure). Requires the VM to have been started with VmConfig::with_balloon(true) on a cold boot; returns false if no balloon device is attached. Asynchronous — the guest’s balloon driver frees pages in the background.

Source

pub fn expose_tcp( &self, host_port: u16, guest_port: u16, ) -> Result<TcpForwarder>

Bind a TCP listener on 127.0.0.1:host_port that forwards each accepted connection to the guest’s TSI listener (the same destination as Vm::connect). Returns a TcpForwarder that owns the accept-loop thread; drop it (or call TcpForwarder::stop) to stop accepting new connections. In-flight connections continue until they close naturally.

host_port = 0 lets the OS pick a free port; read the actual address back via TcpForwarder::local_addr.

guest_port pins the host-port → guest-port mapping. The host writes a small per-connection routing header (SMUX-PORT -V1\0\0\0\0 + u32 BE port) before piping bytes, and the vsock-mux peeks (MSG_PEEK) that header and routes to the matching TSI listener instead of falling back to “first AF_INET listener.” Pass guest_port = 0 to opt out of the header and keep the legacy first-listener behavior — useful when the guest only binds one port and you want a router- style any-port forward.

Use this when you want the embedded VM to look like a normal localhost service (e.g. http://127.0.0.1:9090/) rather than having every caller go through vm.connect().

let image = Image::from_snapshot("path/to/snapshot")?;
let vm = Vm::start(&image, &VmConfig::new())?;
let fwd = vm.expose_tcp(9090, 80)?;
println!("nginx is on {}", fwd.local_addr());
// ... do work ...
drop(fwd); // stop forwarding
Source

pub fn expose_tls(&self, cfg: TlsConfig) -> Result<SocketAddr>

(Linux/KVM) Start a host-side TLS terminator for this VM: accept HTTPS on cfg.listen_addr, terminate with rustls, and bridge decrypted plaintext to the guest’s TSI listener — the guest serves plain HTTP, no TLS code in the guest. This is the in-process counterpart of the macOS/HVF worker’s --tls-* flags (feature parity). Fire-and-forget: the acceptor runs for the VM’s lifetime. Requires vsock enabled.

Source

pub fn snapshot_live( &self, dest_dir: impl Into<PathBuf>, ) -> Result<Image, Error>

LIVE snapshot (Linux/KVM): capture a loadable Image WITHOUT stopping the guest — the VM keeps running and can be snapshotted again. Backs the builder’s one-VM-per-layer snapshotting. &self (non-consuming).

Source

pub fn snapshot_diff_live( &self, dest_dir: impl Into<PathBuf>, base_snap: &Path, ) -> Result<Image, Error>

LIVE differential snapshot (Linux/KVM): like Vm::snapshot_live but stores only the guest-RAM pages changed vs base_snap (a full snapshot’s restore.snap). Non-consuming — the VM keeps running. The builder chains these per layer off one VM.

Trait Implementations§

Source§

impl Deref for PooledVm<'_>

Source§

type Target = Vm

The resulting type after dereferencing.
Source§

fn deref(&self) -> &Vm

Dereferences the value.
Source§

impl DerefMut for PooledVm<'_>

Source§

fn deref_mut(&mut self) -> &mut Vm

Mutably dereferences the value.
Source§

impl Drop for PooledVm<'_>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl<'a> !Freeze for PooledVm<'a>

§

impl<'a> !RefUnwindSafe for PooledVm<'a>

§

impl<'a> !Sync for PooledVm<'a>

§

impl<'a> !UnwindSafe for PooledVm<'a>

§

impl<'a> Send for PooledVm<'a>

§

impl<'a> Unpin for PooledVm<'a>

§

impl<'a> UnsafeUnpin for PooledVm<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more