#[cfg(not(target_os = "wasi"))]
use crate::wasip1;
#[cfg(target_os = "wasi")]
use wasip1;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicBool, Ordering};
use parking_lot::RwLock;
use smallvec::SmallVec;
#[repr(C)]
#[derive(Clone, Debug)]
pub struct TargetMemoryMetadata {
pub base_ptr: i32,
pub limit_ptr: i32,
pub current_pages: u32,
pub max_pages: u32,
}
#[repr(C)]
#[derive(Clone, Debug)]
pub struct SharedMemoryManagerBuilder {
pub lock_ptr: *const u8,
pub metadata_ptr: *const TargetMemoryMetadata,
pub target_count: usize,
pub register_target_ptr: fn(*const TargetMemoryMetadata, i32, i32, u32) -> i32,
pub grow_memory_ptr: fn(i32, u32) -> i32,
pub get_lock_ptr: fn(i32) -> *const u8,
}
impl SharedMemoryManagerBuilder {
pub fn build(self) -> SharedMemoryManager {
SharedMemoryManager {
lock_ptr: self.lock_ptr,
metadata_ptr: self.metadata_ptr,
target_count: self.target_count,
register_target_ptr: self.register_target_ptr,
grow_memory_ptr: self.grow_memory_ptr,
get_lock_ptr: self.get_lock_ptr,
}
}
}
#[derive(Clone, Debug)]
pub struct SharedMemoryManager {
pub lock_ptr: *const u8,
pub metadata_ptr: *const TargetMemoryMetadata,
pub target_count: usize,
pub register_target_ptr: fn(*const TargetMemoryMetadata, i32, i32, u32) -> i32,
pub grow_memory_ptr: fn(i32, u32) -> i32,
pub get_lock_ptr: fn(i32) -> *const u8,
}
impl SharedMemoryManager {
pub fn register_target(
&self,
metadata: *const TargetMemoryMetadata,
base: i32,
limit: i32,
pages: u32,
) -> i32 {
(self.register_target_ptr)(metadata, base, limit, pages)
}
pub fn grow_memory(&self, metadata_ptr: i32, pages: u32) -> i32 {
(self.grow_memory_ptr)(metadata_ptr, pages)
}
pub fn get_lock_ptr(&self, metadata_ptr: i32) -> *const u8 {
(self.get_lock_ptr)(metadata_ptr)
}
}
pub trait SharedMemoryManagerTrait {
type Generated;
fn restore(&self, generated: Self::Generated) -> SharedMemoryManager;
fn receive_manager(&self, builder: SharedMemoryManagerBuilder) -> Self::Generated;
}
#[derive(Debug)]
pub struct StandardSharedMemoryHolder {
#[cfg(feature = "threads")]
pub once: AtomicBool,
pub manager: core::cell::UnsafeCell<Option<SharedMemoryManager>>,
}
unsafe impl Send for StandardSharedMemoryHolder {}
unsafe impl Sync for StandardSharedMemoryHolder {}
impl StandardSharedMemoryHolder {
pub const fn new() -> Self {
Self {
#[cfg(feature = "threads")]
once: AtomicBool::new(false),
manager: core::cell::UnsafeCell::new(None),
}
}
pub fn get(&self) -> Option<&SharedMemoryManager> {
unsafe { (*self.manager.get()).as_ref() }
}
pub fn get_or_panic(&self) -> &SharedMemoryManager {
self.get()
.expect("SharedMemoryManager is not initialized yet")
}
}
impl SharedMemoryManagerTrait for StandardSharedMemoryHolder {
type Generated = ();
fn restore(&self, _: Self::Generated) -> SharedMemoryManager {
self.get_or_panic().clone()
}
fn receive_manager(&self, builder: SharedMemoryManagerBuilder) -> Self::Generated {
let manager = builder.build();
#[cfg(feature = "threads")]
{
if self
.once
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
return ();
}
unsafe { *self.manager.get() = Some(manager) };
}
#[cfg(not(feature = "threads"))]
{
if unsafe { &*self.manager.get() }.is_some() {
panic!("SharedMemoryManager is already initialized");
}
unsafe { *self.manager.get() = Some(manager) };
}
}
}
#[derive(Debug)]
pub struct StandardSharedMemoryMultiHolder {
#[cfg(feature = "threads")]
pub managers: RwLock<SmallVec<[SharedMemoryManager; 4]>>,
#[cfg(not(feature = "threads"))]
pub managers: core::cell::UnsafeCell<SmallVec<[SharedMemoryManager; 4]>>,
}
unsafe impl Send for StandardSharedMemoryMultiHolder {}
unsafe impl Sync for StandardSharedMemoryMultiHolder {}
impl StandardSharedMemoryMultiHolder {
pub const fn new() -> Self {
Self {
#[cfg(feature = "threads")]
managers: RwLock::new(SmallVec::new_const()),
#[cfg(not(feature = "threads"))]
managers: core::cell::UnsafeCell::new(SmallVec::new_const()),
}
}
pub fn add_manager(&self, manager: SharedMemoryManager) {
#[cfg(feature = "threads")]
{
self.managers.write().push(manager);
}
#[cfg(not(feature = "threads"))]
{
unsafe { (*self.managers.get()).push(manager) };
}
}
pub fn get_manager_with<R>(&self, id: usize, f: impl FnOnce(&SharedMemoryManager) -> R) -> R {
#[cfg(feature = "threads")]
{
let managers = self.managers.read();
let manager = &managers[id];
f(manager)
}
#[cfg(not(feature = "threads"))]
{
let managers = unsafe { &*self.managers.get() };
let manager = &managers[id];
f(manager)
}
}
pub fn manager_count(&self) -> usize {
#[cfg(feature = "threads")]
{
self.managers.read().len()
}
#[cfg(not(feature = "threads"))]
{
unsafe { (*self.managers.get()).len() }
}
}
}
impl SharedMemoryManagerTrait for StandardSharedMemoryMultiHolder {
type Generated = usize;
fn restore(&self, id: Self::Generated) -> SharedMemoryManager {
self.get_manager_with(id, |m| m.clone())
}
fn receive_manager(&self, builder: SharedMemoryManagerBuilder) -> Self::Generated {
let manager = builder.build();
let id = self.manager_count();
self.add_manager(manager);
id
}
}
#[macro_export]
macro_rules! export_shared_memory_manager {
($name:ident) => {
$crate::__private::paste::paste! {
$crate::export_shared_memory_manager!($name; &$name);
}
};
($name:ident; $holder:expr) => {
const _: () = {
const fn __asserter(_: &impl $crate::shared_memory::SharedMemoryManagerTrait) {}
__asserter($holder);
};
$crate::__private::paste::paste! {
#[unsafe(no_mangle)]
pub extern "C" fn [<__wasi_export_shared_memory_manager_ $name>](
builder: $crate::shared_memory::SharedMemoryManagerBuilder
) {
let holder = $holder;
holder.receive_manager(builder);
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metadata_size() {
assert_eq!(core::mem::size_of::<TargetMemoryMetadata>(), 16);
}
#[test]
fn test_manager_builder() {
fn dummy_register(_: *const TargetMemoryMetadata, _: i32, _: i32, _: u32) -> i32 {
0
}
fn dummy_grow(_: i32, _: u32) -> i32 {
0
}
fn dummy_lock(_: i32) -> *const u8 {
core::ptr::null()
}
let builder = SharedMemoryManagerBuilder {
lock_ptr: core::ptr::null(),
metadata_ptr: core::ptr::null(),
target_count: 0,
register_target_ptr: dummy_register,
grow_memory_ptr: dummy_grow,
get_lock_ptr: dummy_lock,
};
let _manager = builder.build();
}
#[test]
fn test_single_holder() {
let holder = StandardSharedMemoryHolder::new();
assert!(holder.get().is_none());
}
#[test]
fn test_multi_holder() {
let holder = StandardSharedMemoryMultiHolder::new();
assert_eq!(holder.manager_count(), 0);
}
}