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