corevm_guest/
transactions.rs1#![allow(clippy::missing_safety_doc)]
2
3use crate::{c, flags::RecvTransaction, GuestId, RecvTransactionOutcome};
4use core::mem::size_of_val;
5
6#[derive(Debug)]
8pub struct TransactionFailed;
9
10impl core::fmt::Display for TransactionFailed {
11 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
12 f.write_str("Transaction failed")
13 }
14}
15
16trait RetToResult {
17 fn into_result(self) -> Result<(), TransactionFailed>;
19}
20
21impl RetToResult for u64 {
22 fn into_result(self) -> Result<(), TransactionFailed> {
23 if self == 0 {
24 Ok(())
25 } else {
26 Err(TransactionFailed)
27 }
28 }
29}
30
31pub fn initiate_transaction(target: GuestId, payload: &[u8]) -> Result<(), TransactionFailed> {
38 let address = payload.as_ptr() as u64;
39 let len = size_of_val(payload) as u64;
40 unsafe { c::initiate_transaction(u64::from(target), address, len) }.into_result()
41}
42
43#[cfg(feature = "alloc")]
50pub fn try_recv_transaction() -> Option<(alloc::vec::Vec<u8>, GuestId)> {
51 let ret = unsafe { c::recv_transaction(0, 0, RecvTransaction::NON_BLOCKING.bits()) };
52 let (len, source) = RecvTransactionOutcome::from_reg(ret).into_inner()?;
53 let mut buf = alloc::vec::Vec::with_capacity(len as usize);
54 unsafe {
55 c::recv_transaction(
56 buf.as_mut_ptr() as u64,
57 u64::from(len),
58 RecvTransaction::NON_BLOCKING.bits(),
59 )
60 };
61 unsafe { buf.set_len(len as usize) };
62 Some((buf, source))
63}
64
65#[cfg(feature = "alloc")]
72pub fn recv_transaction() -> (alloc::vec::Vec<u8>, GuestId) {
73 let ret = unsafe { c::recv_transaction(0, 0, 0) };
74 let (len, source) = RecvTransactionOutcome::from_reg(ret)
75 .into_inner()
76 .expect("The call is blocking");
77 let mut buf = alloc::vec::Vec::with_capacity(len as usize);
78 unsafe { c::recv_transaction(buf.as_mut_ptr() as u64, u64::from(len), 0) };
79 unsafe { buf.set_len(len as usize) };
80 (buf, source)
81}
82
83#[inline]
94pub fn try_recv_transaction_into(buf: &mut [u8]) -> Option<Result<(usize, GuestId), usize>> {
95 let ret = unsafe {
96 c::recv_transaction(
97 buf.as_mut_ptr() as u64,
98 buf.len() as u64,
99 RecvTransaction::NON_BLOCKING.bits(),
100 )
101 };
102 let (len, source) = RecvTransactionOutcome::from_reg(ret).into_inner()?;
103 if len > buf.len() as u32 {
104 return Some(Err(len as usize));
105 }
106 Some(Ok((len as usize, source)))
107}
108
109#[inline]
120pub fn recv_transaction_into(buf: &mut [u8]) -> Result<(usize, GuestId), usize> {
121 let ret = unsafe { c::recv_transaction(buf.as_mut_ptr() as u64, buf.len() as u64, 0) };
122 let (len, source) = RecvTransactionOutcome::from_reg(ret)
123 .into_inner()
124 .expect("The call is blocking");
125 if len > buf.len() as u32 {
126 return Err(len as usize);
127 }
128 Ok((len as usize, source))
129}
130
131#[inline]
139pub fn commit_transaction() -> Result<(), TransactionFailed> {
140 unsafe { c::commit_transaction() }.into_result()
141}
142
143#[inline]
146pub fn rollback_transaction() {
147 unsafe { c::rollback_transaction() }
148}
149
150#[inline]
153pub fn end_transaction() {
154 unsafe { c::end_transaction() }
155}
156
157#[derive(Debug, PartialEq, Eq)]
158enum TransactionState {
159 Initial,
161 CommittedOrRolledBack,
163 Ended,
165}
166
167pub struct Transaction {
192 state: TransactionState,
193}
194
195impl Transaction {
196 #[inline]
200 pub fn initiate(target: GuestId, payload: &[u8]) -> Result<Self, TransactionFailed> {
201 initiate_transaction(target, payload)?;
202 Ok(Self { state: TransactionState::Initial })
203 }
204
205 #[cfg(feature = "alloc")]
211 #[inline]
212 pub fn recv() -> Result<(Self, alloc::vec::Vec<u8>, GuestId), TransactionFailed> {
213 let (payload, source) = recv_transaction();
214 let tx = Self { state: TransactionState::Initial };
215 Ok((tx, payload, source))
216 }
217
218 #[inline]
227 pub fn recv_into(payload: &mut [u8]) -> Result<(Self, usize, GuestId), usize> {
228 let (payload_len, source) = recv_transaction_into(payload)?;
229 let tx = Self { state: TransactionState::Initial };
230 Ok((tx, payload_len, source))
231 }
232
233 #[inline]
239 pub fn commit(&mut self) -> Result<(), TransactionFailed> {
240 assert_eq!(TransactionState::Initial, self.state);
241 commit_transaction()?;
242 self.state = TransactionState::CommittedOrRolledBack;
243 Ok(())
244 }
245
246 #[inline]
252 pub fn rollback(&mut self) {
253 assert_eq!(TransactionState::Initial, self.state);
254 rollback_transaction();
255 self.state = TransactionState::CommittedOrRolledBack;
256 }
257
258 #[inline]
264 pub fn end(mut self) {
265 self.inner_end();
266 }
267
268 fn inner_end(&mut self) {
269 assert_eq!(TransactionState::CommittedOrRolledBack, self.state);
270 end_transaction();
271 self.state = TransactionState::Ended;
272 }
273}
274
275impl Drop for Transaction {
276 fn drop(&mut self) {
277 if self.state == TransactionState::Initial {
278 self.rollback();
279 }
280 if self.state == TransactionState::CommittedOrRolledBack {
281 self.inner_end();
282 }
283 }
284}