twizzler/object/
tx.rs

1use std::{alloc::Layout, mem::MaybeUninit};
2
3use twizzler_abi::object::NULLPAGE_SIZE;
4use twizzler_rt_abi::object::ObjectHandle;
5
6use crate::{
7    marker::BaseType,
8    object::{MutObject, Object, RawObject, TypedObject},
9    ptr::{GlobalPtr, RefMut},
10    Result,
11};
12
13#[repr(C)]
14pub struct TxObject<T = ()> {
15    obj: MutObject<T>,
16    static_alloc: usize,
17    sync_on_drop: bool,
18}
19
20impl<T> TxObject<T> {
21    const MIN_ALIGN: usize = 32;
22    pub fn new(object: Object<T>) -> Result<Self> {
23        // TODO: start tx
24        Ok(Self {
25            obj: unsafe { object.as_mut()? },
26            static_alloc: (size_of::<T>() + align_of::<T>()).next_multiple_of(Self::MIN_ALIGN)
27                + NULLPAGE_SIZE,
28            sync_on_drop: true,
29        })
30    }
31
32    pub fn commit(&mut self) -> Result<()> {
33        self.obj.sync()
34    }
35
36    pub fn abort(&mut self) {
37        self.sync_on_drop = false;
38    }
39
40    pub fn into_object(mut self) -> Result<Object<T>> {
41        if self.sync_on_drop {
42            self.obj.sync()?;
43            self.sync_on_drop = false;
44        }
45        unsafe { Ok(Object::from_handle_unchecked(self.handle().clone())) }
46    }
47
48    pub fn base_mut(&mut self) -> RefMut<'_, T> {
49        unsafe { RefMut::from_raw_parts(self.base_mut_ptr(), self.handle()) }
50    }
51
52    pub unsafe fn cast<U>(mut self) -> TxObject<U> {
53        self.sync_on_drop = false;
54        TxObject {
55            obj: unsafe { self.obj.clone().cast() },
56            static_alloc: self.static_alloc,
57            sync_on_drop: self.sync_on_drop,
58        }
59    }
60
61    pub fn into_unit(self) -> TxObject<()> {
62        unsafe { self.cast() }
63    }
64
65    pub fn as_mut(&mut self) -> &mut MutObject<T> {
66        &mut self.obj
67    }
68
69    pub fn as_ref(&mut self) -> &MutObject<T> {
70        &self.obj
71    }
72
73    pub fn into_handle(self) -> ObjectHandle {
74        self.handle().clone()
75    }
76
77    pub(crate) unsafe fn from_mut_object(mo: MutObject<T>) -> Self {
78        Self {
79            obj: mo,
80            static_alloc: (size_of::<T>() + align_of::<T>()).next_multiple_of(Self::MIN_ALIGN)
81                + NULLPAGE_SIZE,
82            sync_on_drop: true,
83        }
84    }
85
86    pub(crate) fn nosync(&mut self) {
87        self.sync_on_drop = false;
88    }
89
90    #[allow(dead_code)]
91    pub(crate) fn is_nosync(&self) -> bool {
92        !self.sync_on_drop
93    }
94}
95
96impl<B> TxObject<MaybeUninit<B>> {
97    pub fn write(self, baseval: B) -> Result<TxObject<B>> {
98        let base = unsafe { self.base_mut_ptr::<MaybeUninit<B>>().as_mut().unwrap() };
99        base.write(baseval);
100        Ok(unsafe { self.cast() })
101    }
102
103    pub unsafe fn assume_init(self) -> TxObject<B> {
104        self.cast()
105    }
106
107    pub fn static_alloc_inplace<T>(
108        &mut self,
109        f: impl FnOnce(&mut MaybeUninit<T>) -> Result<&mut T>,
110    ) -> Result<GlobalPtr<T>> {
111        let layout = Layout::new::<T>();
112        let start = self.static_alloc.next_multiple_of(layout.align());
113        let next_start = (start + layout.size() + layout.align()).next_multiple_of(Self::MIN_ALIGN);
114        self.static_alloc = next_start;
115        let ptr = unsafe {
116            self.obj
117                .handle()
118                .start()
119                .add(start)
120                .cast::<MaybeUninit<T>>()
121        };
122        let mu = unsafe { &mut *ptr };
123        f(mu)?;
124        let gp = GlobalPtr::new(self.obj.id(), start as u64);
125        Ok(gp)
126    }
127
128    pub fn static_alloc<T>(&mut self, value: T) -> Result<GlobalPtr<T>> {
129        self.static_alloc_inplace(|mu| Ok(mu.write(value)))
130    }
131}
132
133impl<T> RawObject for TxObject<T> {
134    fn handle(&self) -> &twizzler_rt_abi::object::ObjectHandle {
135        self.obj.handle()
136    }
137}
138
139impl<B: BaseType> TypedObject for TxObject<B> {
140    type Base = B;
141
142    fn base_ref(&self) -> crate::ptr::Ref<'_, Self::Base> {
143        unsafe { crate::ptr::Ref::from_raw_parts(self.base_ptr(), self.handle()) }
144    }
145
146    fn base(&self) -> &Self::Base {
147        unsafe { self.base_ptr::<Self::Base>().as_ref().unwrap_unchecked() }
148    }
149}
150
151impl<B> Drop for TxObject<B> {
152    #[track_caller]
153    fn drop(&mut self) {
154        tracing::trace!(
155            "TxObject {:?} drop from {}",
156            self.id(),
157            core::panic::Location::caller()
158        );
159        if self.sync_on_drop {
160            let _ = self
161                .obj
162                .sync()
163                .inspect_err(|e| tracing::error!("TxObject sync on drop failed: {}", e));
164        }
165    }
166}
167
168impl<B> AsRef<TxObject<()>> for TxObject<B> {
169    fn as_ref(&self) -> &TxObject<()> {
170        let this = self as *const Self;
171        // Safety: This phantom data is the only generic field, and we are repr(C).
172        unsafe { this.cast::<TxObject<()>>().as_ref().unwrap() }
173    }
174}
175
176impl<B> Into<ObjectHandle> for TxObject<B> {
177    fn into(self) -> ObjectHandle {
178        self.obj.handle().clone()
179    }
180}
181
182impl<B> Into<ObjectHandle> for &TxObject<B> {
183    fn into(self) -> ObjectHandle {
184        self.obj.handle().clone()
185    }
186}
187
188impl<B> AsRef<ObjectHandle> for TxObject<B> {
189    fn as_ref(&self) -> &ObjectHandle {
190        self.obj.handle()
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use crate::{
197        marker::BaseType,
198        object::{ObjectBuilder, TypedObject},
199    };
200
201    struct Simple {
202        x: u32,
203    }
204
205    impl BaseType for Simple {}
206
207    #[test]
208    fn single_tx() {
209        let builder = ObjectBuilder::default();
210        let obj = builder.build(Simple { x: 3 }).unwrap();
211        let base = obj.base_ref();
212        assert_eq!(base.x, 3);
213        drop(base);
214
215        let mut tx = obj.into_tx().unwrap();
216        let mut base = tx.base_mut();
217        base.x = 42;
218        drop(base);
219        tx.commit().unwrap();
220        let obj = tx.into_object().unwrap();
221        assert_eq!(obj.base().x, 42);
222    }
223}