Skip to main content

dope_core/driver/
route.rs

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