dope_core/driver/
route.rs1use 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}