twizzler/object/
builder.rs

1use std::{marker::PhantomData, mem::MaybeUninit};
2
3use twizzler_abi::{
4    object::{ObjID, Protections},
5    syscall::{
6        BackingType, CreateTieSpec, LifetimeType, ObjectCreate, ObjectCreateFlags, ObjectSource,
7    },
8};
9use twizzler_rt_abi::{bindings::CREATE_KIND_NEW, object::MapFlags};
10
11use super::{Object, TxObject};
12use crate::{
13    marker::{BaseType, StoreCopy},
14    Result,
15};
16
17/// An object builder, for constructing objects using a builder API.
18#[derive(Clone)]
19pub struct ObjectBuilder<Base: BaseType> {
20    spec: ObjectCreate,
21    src_objs: Vec<ObjectSource>,
22    ties: Vec<CreateTieSpec>,
23    name: Option<String>,
24    _pd: PhantomData<Base>,
25}
26
27impl<Base: BaseType> ObjectBuilder<Base> {
28    /// Make a new object builder.
29    pub fn new(spec: ObjectCreate) -> Self {
30        Self {
31            spec,
32            _pd: PhantomData,
33            name: None,
34            src_objs: Vec::new(),
35            ties: Vec::new(),
36        }
37    }
38
39    /// Make the object persistent.
40    pub fn persist(mut self) -> Self {
41        self.spec.lt = LifetimeType::Persistent;
42        self
43    }
44
45    /// Cast the base type.
46    pub fn cast<U: BaseType>(self) -> ObjectBuilder<U> {
47        ObjectBuilder::<U>::new(self.spec)
48    }
49
50    /// Add a Source Object that this new object will copy from.
51    pub fn add_src(mut self, obj_src: ObjectSource) -> Self {
52        self.src_objs.push(obj_src);
53        self
54    }
55
56    /// Add a tie specification for this object creation.
57    pub fn add_tie(mut self, tie: CreateTieSpec) -> Self {
58        self.ties.push(tie);
59        self
60    }
61
62    pub fn named(mut self, name: impl ToString) -> Self {
63        self.name = Some(name.to_string());
64        self
65    }
66}
67
68fn bind_name(id: ObjID, name: &str) -> Result<()> {
69    let create = twizzler_rt_abi::bindings::create_options {
70        id: id.raw(),
71        kind: CREATE_KIND_NEW,
72    };
73    let fd = twizzler_rt_abi::fd::twz_rt_fd_open(name, create, 0)?;
74    twizzler_rt_abi::fd::twz_rt_fd_close(fd);
75    Ok(())
76}
77
78impl<Base: BaseType + StoreCopy> ObjectBuilder<Base> {
79    /// Build an object using the provided base vale.
80    /// # Example
81    /// ```
82    /// # use twizzler::object::ObjectBuilder;
83    /// let builder = ObjectBuilder::default();
84    /// let obj = builder.build(42u32).unwrap();
85    /// ```
86    pub fn build(&self, base: Base) -> Result<Object<Base>> {
87        self.build_inplace(|tx| tx.write(base))
88    }
89}
90
91impl<Base: BaseType> ObjectBuilder<Base> {
92    /// Build an object using the provided constructor function.
93    ///
94    /// The constructor should call the .write() method on the TxObject, and
95    /// return the result.
96    /// # Example
97    /// ```
98    /// # use twizzler::object::ObjectBuilder;
99    /// let builder = ObjectBuilder::default();
100    /// let obj = builder.build_inplace(|tx| tx.write(42u32)).unwrap();
101    /// ```
102    pub fn build_inplace<F>(&self, ctor: F) -> Result<Object<Base>>
103    where
104        F: FnOnce(TxObject<MaybeUninit<Base>>) -> Result<TxObject<Base>>,
105    {
106        let id = twizzler_abi::syscall::sys_object_create(
107            self.spec,
108            self.src_objs.as_slice(),
109            self.ties.as_slice(),
110        )?;
111        let mut flags = MapFlags::READ | MapFlags::WRITE;
112        if self.spec.lt == LifetimeType::Persistent {
113            flags.insert(MapFlags::PERSIST);
114            if let Some(ref name) = self.name {
115                bind_name(id, name)?;
116            }
117        } else {
118            if let Some(ref name) = self.name {
119                tracing::warn!(
120                    "tried to name volatile object at creation time: {} {}",
121                    id,
122                    name
123                );
124            }
125        }
126        let mu_object = unsafe { Object::<MaybeUninit<Base>>::map_unchecked(id, flags) }?;
127        let object = ctor(mu_object.into_tx()?)?;
128        object.into_object()
129    }
130
131    /// Build an object using the provided constructor function.
132    ///
133    /// The constructor should call the .write() method on the TxObject or
134    /// otherwise ensure that it is safe to call .assume_init on the underlying
135    /// MaybeUninit.
136    ///
137    /// # Safety
138    /// The caller must ensure that the base is initialized, see MaybeUninit::assume_init.
139    ///
140    /// # Example
141    /// ```
142    /// # use twizzler::object::ObjectBuilder;
143    /// let builder = ObjectBuilder::default();
144    /// let obj = unsafe {
145    ///     builder
146    ///         .build_ctor(|tx| {
147    ///             tx.write(42u32);
148    ///         })
149    ///         .unwrap()
150    /// };
151    /// ```
152    pub unsafe fn build_ctor<F>(&self, ctor: F) -> Result<Object<Base>>
153    where
154        F: FnOnce(&mut TxObject<MaybeUninit<Base>>),
155    {
156        self.build_inplace(|mut tx| {
157            ctor(&mut tx);
158            Ok(tx.assume_init())
159        })
160    }
161}
162
163impl<Base: BaseType> Default for ObjectBuilder<Base> {
164    fn default() -> Self {
165        Self::new(ObjectCreate::new(
166            BackingType::Normal,
167            LifetimeType::Volatile,
168            None,
169            ObjectCreateFlags::empty(),
170            Protections::all(),
171        ))
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::ObjectBuilder;
178    use crate::{marker::BaseType, object::TypedObject, ptr::InvPtr};
179
180    #[test]
181    fn builder_simple() {
182        let builder = ObjectBuilder::default();
183        let obj = builder.build(42u32).unwrap();
184        let base = obj.base();
185        assert_eq!(*base, 42);
186    }
187
188    struct Foo {
189        ptr: InvPtr<u32>,
190    }
191    impl BaseType for Foo {}
192
193    #[test]
194    fn builder_complex() {
195        let builder = ObjectBuilder::default();
196        let obj_1 = builder.build(42u32).unwrap();
197        let base = obj_1.base_ref();
198        assert_eq!(*base, 42);
199
200        let builder = ObjectBuilder::<Foo>::default();
201        let obj = builder
202            .build_inplace(|tx| {
203                let foo = Foo {
204                    ptr: InvPtr::new(&tx, base)?,
205                };
206                tx.write(foo)
207            })
208            .unwrap();
209        let base_foo = obj.base();
210        let r = unsafe { base_foo.ptr.resolve() };
211        assert_eq!(*r, 42);
212    }
213}