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