Skip to main content

dope_core/driver/
route.rs

1use std::cell::Cell;
2use std::io::{self, Error, ErrorKind};
3
4use super::token::ROUTE_FRAMEWORK;
5use super::{DriverContext, DriverRef};
6use crate::driver::control::ContextControl;
7
8pub(crate) struct Routes {
9    live: [u64; 4],
10    poisoned: [u64; 4],
11}
12
13impl Routes {
14    pub(crate) const fn new() -> Self {
15        Self {
16            live: [0; 4],
17            poisoned: [0; 4],
18        }
19    }
20
21    pub(crate) fn reserve(&mut self, id: u8) -> bool {
22        let word = id as usize / 64;
23        let mask = 1u64 << (id % 64);
24        if self.live[word] & mask != 0 || self.poisoned[word] & mask != 0 {
25            return false;
26        }
27        self.live[word] |= mask;
28        true
29    }
30
31    pub(crate) fn release(&mut self, id: u8) {
32        self.live[id as usize / 64] &= !(1u64 << (id % 64));
33    }
34
35    pub(crate) fn poison(&mut self, id: u8) {
36        debug_assert!(!self.is_poisoned(id));
37        let word = id as usize / 64;
38        let mask = 1u64 << (id % 64);
39        self.live[word] &= !mask;
40        self.poisoned[word] |= mask;
41    }
42
43    pub(crate) fn is_poisoned(&self, id: u8) -> bool {
44        self.poisoned[id as usize / 64] & (1u64 << (id % 64)) != 0
45    }
46}
47
48pub struct Route<'d, const ID: u8> {
49    driver: DriverRef<'d>,
50    live: Cell<bool>,
51}
52
53#[doc(hidden)]
54pub struct RouteReservation<'a, 'c, 'd, const ID: u8> {
55    route: Option<Route<'d, ID>>,
56    driver: &'a mut DriverContext<'c, 'd>,
57}
58
59impl<'d, const ID: u8> Route<'d, ID> {
60    pub fn reserve(driver: &mut DriverContext<'_, 'd>) -> io::Result<Self> {
61        if ID == ROUTE_FRAMEWORK {
62            return Err(Error::new(ErrorKind::InvalidInput, "dope: reserved route"));
63        }
64        if !driver.reserve_route(ID) {
65            return Err(Error::new(
66                ErrorKind::AlreadyExists,
67                "dope: route already used",
68            ));
69        }
70        Ok(Self {
71            driver: driver.driver_ref(),
72            live: Cell::new(true),
73        })
74    }
75
76    #[doc(hidden)]
77    pub fn reserve_transaction<'a, 'c>(
78        driver: &'a mut DriverContext<'c, 'd>,
79    ) -> io::Result<RouteReservation<'a, 'c, 'd, ID>> {
80        let route = Self::reserve(driver)?;
81        Ok(RouteReservation {
82            route: Some(route),
83            driver,
84        })
85    }
86
87    pub fn poison(&self, driver: &mut DriverContext<'_, 'd>) {
88        if self.live.replace(false) {
89            driver.poison_route(ID);
90        }
91    }
92
93    pub fn driver(&self) -> DriverRef<'d> {
94        self.driver
95    }
96
97    pub fn release(self, driver: &mut DriverContext<'_, 'd>) {
98        if self.live.replace(false) {
99            driver.release_route(ID);
100        }
101    }
102
103    pub fn finish(&self, driver: &mut DriverContext<'_, 'd>, poison: bool) {
104        if !self.live.replace(false) {
105            return;
106        }
107        if poison {
108            driver.poison_route(ID);
109        } else {
110            driver.release_route(ID);
111        }
112    }
113}
114
115impl<'c, 'd, const ID: u8> RouteReservation<'_, 'c, 'd, ID> {
116    pub fn driver(&mut self) -> &mut DriverContext<'c, 'd> {
117        self.driver
118    }
119
120    pub fn commit(mut self) -> Route<'d, ID> {
121        let Some(route) = self.route.take() else {
122            unreachable!("dope: route reservation committed twice")
123        };
124        route
125    }
126}
127
128impl<const ID: u8> Drop for RouteReservation<'_, '_, '_, ID> {
129    fn drop(&mut self) {
130        if let Some(route) = self.route.take() {
131            route.release(self.driver);
132        }
133    }
134}