use super::PoolingInstanceAllocator;
use crate::vm::{MemoryAllocationIndex, MemoryImageSlot, Table, TableAllocationIndex};
use smallvec::SmallVec;
use std::io;
#[cfg(feature = "async")]
use wasmtime_fiber::FiberStack;
#[cfg(unix)]
#[expect(non_camel_case_types, reason = "matching libc naming")]
type iovec = libc::iovec;
#[cfg(not(unix))]
#[expect(non_camel_case_types, reason = "matching libc naming")]
struct iovec {
iov_base: *mut libc::c_void,
iov_len: libc::size_t,
}
#[repr(transparent)]
struct IoVec(iovec);
unsafe impl Send for IoVec {}
unsafe impl Sync for IoVec {}
impl std::fmt::Debug for IoVec {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("IoVec")
.field("base", &self.0.iov_base)
.field("len", &self.0.iov_len)
.finish()
}
}
#[cfg(feature = "async")]
struct SendSyncStack(FiberStack);
#[cfg(feature = "async")]
unsafe impl Send for SendSyncStack {}
#[cfg(feature = "async")]
unsafe impl Sync for SendSyncStack {}
#[derive(Default)]
pub struct DecommitQueue {
raw: SmallVec<[IoVec; 2]>,
memories: SmallVec<[(MemoryAllocationIndex, MemoryImageSlot, usize); 1]>,
tables: SmallVec<[(TableAllocationIndex, Table, usize); 1]>,
#[cfg(feature = "async")]
stacks: SmallVec<[(SendSyncStack, usize); 1]>,
}
impl std::fmt::Debug for DecommitQueue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DecommitQueue")
.field("raw", &self.raw)
.finish_non_exhaustive()
}
}
impl DecommitQueue {
pub fn append(
&mut self,
Self {
raw,
memories,
tables,
#[cfg(feature = "async")]
stacks,
}: &mut Self,
) {
self.raw.append(raw);
self.memories.append(memories);
self.tables.append(tables);
#[cfg(feature = "async")]
self.stacks.append(stacks);
}
pub fn raw_len(&self) -> usize {
self.raw.len()
}
pub unsafe fn push_raw(&mut self, ptr: *mut u8, len: usize) {
self.raw.push(IoVec(iovec {
iov_base: ptr.cast(),
iov_len: len,
}));
}
pub unsafe fn push_memory(
&mut self,
allocation_index: MemoryAllocationIndex,
image: MemoryImageSlot,
bytes_resident: usize,
) {
self.memories
.push((allocation_index, image, bytes_resident));
}
pub unsafe fn push_table(
&mut self,
allocation_index: TableAllocationIndex,
table: Table,
bytes_resident: usize,
) {
self.tables.push((allocation_index, table, bytes_resident));
}
#[cfg(feature = "async")]
pub unsafe fn push_stack(&mut self, stack: FiberStack, bytes_resident: usize) {
self.stacks.push((SendSyncStack(stack), bytes_resident));
}
fn decommit_all_raw(&mut self) -> io::Result<()> {
for iovec in self.raw.drain(..) {
unsafe {
crate::vm::sys::vm::decommit_pages(iovec.0.iov_base.cast(), iovec.0.iov_len)?;
}
}
Ok(())
}
pub fn flush(mut self, pool: &PoolingInstanceAllocator) -> bool {
let decommit_succeeded = self.decommit_all_raw().is_ok();
let mut deallocated_any = false;
for (allocation_index, image, bytes_resident) in self.memories {
deallocated_any = true;
let image = if decommit_succeeded {
Some(image)
} else {
None
};
unsafe {
pool.memories
.deallocate(allocation_index, image, bytes_resident);
}
}
for (allocation_index, table, bytes_resident) in self.tables {
deallocated_any = true;
unsafe {
pool.tables
.deallocate(allocation_index, table, bytes_resident);
}
}
#[cfg(feature = "async")]
for (stack, bytes_resident) in self.stacks {
deallocated_any = true;
unsafe {
pool.stacks.deallocate(stack.0, bytes_resident);
}
}
deallocated_any
}
}