appcore_transfer/
control.rs1use crate::{TransferError as E, TransferResult};
13use std::sync::{
14 atomic::{AtomicBool, Ordering},
15 Arc,
16};
17use std::time::{Duration, Instant};
18#[derive(Debug, Clone)]
20pub struct TransferControl {
21 cancelled: Arc<AtomicBool>,
22 deadline: Instant,
23}
24impl TransferControl {
25 pub fn new(timeout: Duration) -> TransferResult<Self> {
27 if timeout < Duration::from_millis(1) || timeout > Duration::from_secs(86400) {
28 return Err(E::InvalidContract);
29 }
30 Ok(Self {
31 cancelled: Arc::new(AtomicBool::new(false)),
32 deadline: Instant::now()
33 .checked_add(timeout)
34 .ok_or(E::InvalidContract)?,
35 })
36 }
37 pub fn cancel(&self) {
39 self.cancelled.store(true, Ordering::Release);
40 }
41 pub fn checkpoint(&self) -> TransferResult<()> {
43 if self.cancelled.load(Ordering::Acquire) {
44 return Err(E::Cancelled);
45 }
46 if Instant::now() >= self.deadline {
47 return Err(E::Deadline);
48 }
49 Ok(())
50 }
51 pub fn remaining(&self) -> Duration {
53 self.deadline.saturating_duration_since(Instant::now())
54 }
55 pub(crate) fn wait(&self, delay: Duration) -> TransferResult<()> {
56 let until = Instant::now()
57 .checked_add(delay)
58 .ok_or(E::InvalidContract)?;
59 while Instant::now() < until {
60 self.checkpoint()?;
61 std::thread::sleep(
62 until
63 .saturating_duration_since(Instant::now())
64 .min(Duration::from_millis(10)),
65 );
66 }
67 self.checkpoint()
68 }
69}