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