twizzler/object/
tx.rs

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