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 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 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}