Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions litebox/src/fd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ impl<Platform: RawSyncPrimitivesProvider> Descriptors<Platform> {
) -> Option<T>
where
Subsystem: FdEnabledSubsystem,
T: core::any::Any + Send + Sync,
T: core::any::Any + Clone + Send + Sync,
{
self.entries[fd.x.as_usize()?]
.as_ref()
Expand Down Expand Up @@ -506,7 +506,7 @@ impl<Platform: RawSyncPrimitivesProvider> Descriptors<Platform> {
) -> Option<T>
where
Subsystem: FdEnabledSubsystem,
T: core::any::Any + Send + Sync,
T: core::any::Any + Clone + Send + Sync,
{
self.entries[fd.x.as_usize()?]
.as_mut()
Expand Down
96 changes: 89 additions & 7 deletions litebox/src/utilities/anymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,30 @@ use alloc::boxed::Box;
use core::any::{Any, TypeId};
use hashbrown::HashMap;

/// Type-erased clone function stored alongside each value.
///
/// We cannot use `Box<dyn Any + Send + Sync + Clone>` because `Clone` is not
/// object-safe (its `clone` method returns `Self`). Instead we store a
/// function pointer that knows the concrete type and can clone through the
/// trait object.
type CloneFn = fn(&(dyn Any + Send + Sync)) -> Box<dyn Any + Send + Sync>;

/// A safe store of exactly one value of any type `T`.
pub(crate) struct AnyMap {
// Invariant: the value at a particular typeid is guaranteed to be the correct type boxed up.
storage: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
storage: HashMap<TypeId, (Box<dyn Any + Send + Sync>, CloneFn)>,
}

const GUARANTEED: &str = "guaranteed correct type by invariant";

/// Create a clone function for a specific concrete type.
fn make_clone_fn<T: Any + Clone + Send + Sync>() -> CloneFn {
|val: &(dyn Any + Send + Sync)| -> Box<dyn Any + Send + Sync> {
let concrete = val.downcast_ref::<T>().expect(GUARANTEED);
Box::new(concrete.clone())
}
}

impl AnyMap {
/// Create a new empty `AnyMap`
pub(crate) fn new() -> Self {
Expand All @@ -35,20 +51,26 @@ impl AnyMap {
}

/// Insert `v`, replacing and returning the old value if one existed already.
pub(crate) fn insert<T: Any + Send + Sync>(&mut self, v: T) -> Option<T> {
let old = self.storage.insert(TypeId::of::<T>(), Box::new(v))?;
Some(*old.downcast().expect(GUARANTEED))
///
/// The `Clone` bound is required to capture a type-erased clone function
/// at insertion time. Read-only accessors (`get`, `get_mut`, `remove`) do
/// not require `Clone`.
pub(crate) fn insert<T: Any + Clone + Send + Sync>(&mut self, v: T) -> Option<T> {
let old = self
.storage
.insert(TypeId::of::<T>(), (Box::new(v), make_clone_fn::<T>()))?;
Some(*old.0.downcast().expect(GUARANTEED))
}

/// Get a reference to a value of type `T` if it exists.
pub(crate) fn get<T: Any + Send + Sync>(&self) -> Option<&T> {
let v = self.storage.get(&TypeId::of::<T>())?;
let v = &self.storage.get(&TypeId::of::<T>())?.0;
Some(v.downcast_ref().expect(GUARANTEED))
}

/// Get a mutable reference to a value of type `T` if it exists.
pub(crate) fn get_mut<T: Any + Send + Sync>(&mut self) -> Option<&mut T> {
let v = self.storage.get_mut(&TypeId::of::<T>())?;
let v = &mut self.storage.get_mut(&TypeId::of::<T>())?.0;
Some(v.downcast_mut().expect(GUARANTEED))
}

Expand All @@ -58,7 +80,67 @@ impl AnyMap {
)]
/// Remove and return the value of type `T` if it exists.
pub(crate) fn remove<T: Any + Send + Sync>(&mut self) -> Option<T> {
let v = self.storage.remove(&TypeId::of::<T>())?;
let v = self.storage.remove(&TypeId::of::<T>())?.0;
Some(*v.downcast().expect(GUARANTEED))
}
}

impl Clone for AnyMap {
fn clone(&self) -> Self {
Self {
storage: self
.storage
.iter()
.map(|(&type_id, (val, clone_fn))| (type_id, (clone_fn(val.as_ref()), *clone_fn)))
.collect(),
}
}
}

#[cfg(test)]
mod tests {
use alloc::string::String;

use super::AnyMap;

#[test]
fn insert_and_get() {
let mut map = AnyMap::new();
assert!(map.insert(42u32).is_none());
assert_eq!(map.get::<u32>(), Some(&42));
}

#[test]
fn clone_produces_independent_copy() {
let mut original = AnyMap::new();
original.insert(10u32);
original.insert(String::from("hello"));

let mut cloned = original.clone();

// Cloned values match.
assert_eq!(cloned.get::<u32>(), Some(&10));
assert_eq!(cloned.get::<String>().map(String::as_str), Some("hello"));

// Mutating the clone does not affect the original.
*cloned.get_mut::<u32>().unwrap() = 99;
assert_eq!(original.get::<u32>(), Some(&10));
assert_eq!(cloned.get::<u32>(), Some(&99));
}

#[test]
fn cloned_map_can_be_cloned_again() {
let mut map = AnyMap::new();
map.insert(7u64);
let clone1 = map.clone();
let clone2 = clone1.clone();
assert_eq!(clone2.get::<u64>(), Some(&7));
}

#[test]
fn clone_empty_map() {
let map = AnyMap::new();
let cloned = map.clone();
assert_eq!(cloned.get::<u32>(), None);
}
}
2 changes: 2 additions & 0 deletions litebox_shim_linux/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,11 @@ fn default_fs(
}

// Special override so that `GETFL` can return stdio-specific flags
#[derive(Clone)]
pub(crate) struct StdioStatusFlags(litebox::fs::OFlags);

/// Status flags for pipes
#[derive(Clone)]
pub(crate) struct PipeStatusFlags(pub litebox::fs::OFlags);

impl<FS: ShimFS> syscalls::file::FilesState<FS> {
Expand Down
4 changes: 3 additions & 1 deletion litebox_shim_linux/src/syscalls/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ impl SocketAddress {
}
}

#[derive(Default)]
#[derive(Default, Clone)]
pub(super) struct SocketOptions {
pub(super) reuse_address: bool,
pub(super) keep_alive: bool,
Expand All @@ -171,7 +171,9 @@ pub(super) struct SocketOptions {
pub(super) linger_timeout: Option<core::time::Duration>,
}

#[derive(Clone)]
pub(crate) struct SocketOFlags(pub OFlags);
#[derive(Clone)]
pub(crate) struct SocketProxy(pub Arc<NetworkProxy<Platform>>);

pub(super) enum SocketOptionValue {
Expand Down
Loading