1extern crate alloc;
7
8use alloc::rc::Rc;
9use core::cell::RefCell;
10use core::sync::atomic::{AtomicU32, Ordering};
11
12use crate::error::{FabricError, Result};
13use crate::host;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum LeaseStatus {
18 Active,
20 Expired,
22 Revoked,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct LeaseInfo {
29 pub lease_id: u128,
30 pub created_at_unix_secs: u64,
31 pub expires_at_unix_secs: u64,
32 pub status: LeaseStatus,
33}
34
35#[derive(Debug)]
36pub(crate) struct LeaseState {
37 pub lease_id: u128,
38 pub created_at_unix_secs: u64,
39 pub expires_at_unix_secs: u64,
40 pub status: LeaseStatus,
41 pub resource_tag: u8,
42}
43
44pub(crate) type SharedLeaseState = Rc<RefCell<LeaseState>>;
45
46static LEASE_SEQ: AtomicU32 = AtomicU32::new(1);
47
48fn next_lease_id(tag: u8, now_unix_secs: u64) -> u128 {
49 let seq = LEASE_SEQ.fetch_add(1, Ordering::Relaxed) as u128;
50 ((now_unix_secs as u128) << 64) | ((tag as u128) << 56) | seq
51}
52
53pub(crate) fn new_shared_lease(tag: u8, lease_secs: u64) -> SharedLeaseState {
54 let created_at_unix_secs = host::unix_time_secs();
55 let ttl = lease_secs.max(1);
56 Rc::new(RefCell::new(LeaseState {
57 lease_id: next_lease_id(tag, created_at_unix_secs),
58 created_at_unix_secs,
59 expires_at_unix_secs: created_at_unix_secs.saturating_add(ttl),
60 status: LeaseStatus::Active,
61 resource_tag: tag,
62 }))
63}
64
65pub(crate) fn new_shared_lease_from_parts(
66 lease_id: u128,
67 created_at_unix_secs: u64,
68 expires_at_unix_secs: u64,
69 status: LeaseStatus,
70) -> SharedLeaseState {
71 let resource_tag = ((lease_id >> 56) & 0xFF) as u8;
73 Rc::new(RefCell::new(LeaseState {
74 lease_id,
75 created_at_unix_secs,
76 expires_at_unix_secs,
77 status,
78 resource_tag,
79 }))
80}
81
82fn refresh_status(state: &mut LeaseState, now_unix_secs: u64) {
83 if state.status == LeaseStatus::Active && now_unix_secs >= state.expires_at_unix_secs {
84 state.status = LeaseStatus::Expired;
85 #[cfg(feature = "observe")]
86 crate::observe_hooks::on_lease_expired(state.resource_tag, state.lease_id, "local");
87 }
88}
89
90pub(crate) fn status(state: &SharedLeaseState) -> LeaseStatus {
91 let mut s = state.borrow_mut();
92 refresh_status(&mut s, host::unix_time_secs());
93 s.status
94}
95
96pub(crate) fn ensure_active(state: &SharedLeaseState) -> Result<()> {
97 match status(state) {
98 LeaseStatus::Active => Ok(()),
99 LeaseStatus::Revoked => Err(FabricError::Revoked),
100 LeaseStatus::Expired => Err(FabricError::LeaseExpired),
101 }
102}
103
104pub(crate) fn lease_id(state: &SharedLeaseState) -> u128 {
105 state.borrow().lease_id
106}
107
108pub(crate) fn created_at_unix_secs(state: &SharedLeaseState) -> u64 {
109 state.borrow().created_at_unix_secs
110}
111
112pub(crate) fn expires_at_unix_secs(state: &SharedLeaseState) -> u64 {
113 let mut s = state.borrow_mut();
114 refresh_status(&mut s, host::unix_time_secs());
115 s.expires_at_unix_secs
116}
117
118pub(crate) fn info(state: &SharedLeaseState) -> LeaseInfo {
119 let mut s = state.borrow_mut();
120 refresh_status(&mut s, host::unix_time_secs());
121 LeaseInfo {
122 lease_id: s.lease_id,
123 created_at_unix_secs: s.created_at_unix_secs,
124 expires_at_unix_secs: s.expires_at_unix_secs,
125 status: s.status,
126 }
127}
128
129pub(crate) fn renew(state: &SharedLeaseState, duration_secs: u64) -> Result<()> {
130 if duration_secs == 0 {
131 return Err(FabricError::IoError(-1));
132 }
133
134 let now_unix_secs = host::unix_time_secs();
135 let mut s = state.borrow_mut();
136 refresh_status(&mut s, now_unix_secs);
137 if s.status != LeaseStatus::Active {
138 return Err(FabricError::LeaseExpired);
139 }
140
141 let requested_exp = now_unix_secs.saturating_add(duration_secs);
142 if requested_exp > s.expires_at_unix_secs {
143 s.expires_at_unix_secs = requested_exp;
144 }
145 Ok(())
146}
147
148pub(crate) fn free(state: &SharedLeaseState) {
149 let mut s = state.borrow_mut();
150 s.status = LeaseStatus::Revoked;
151}
152
153pub(crate) fn set_status(state: &SharedLeaseState, new_status: LeaseStatus) {
154 let mut s = state.borrow_mut();
155 #[cfg(feature = "observe")]
156 let old_status = s.status;
157 s.status = new_status;
158 #[cfg(feature = "observe")]
159 {
160 if old_status == LeaseStatus::Active && new_status == LeaseStatus::Expired {
161 crate::observe_hooks::on_lease_expired(s.resource_tag, s.lease_id, "local");
162 } else if old_status == LeaseStatus::Active && new_status == LeaseStatus::Revoked {
163 crate::observe_hooks::on_lease_revoked(s.resource_tag, s.lease_id, "local");
164 }
165 }
166}
167
168pub(crate) fn set_expires_at_unix_secs(state: &SharedLeaseState, expires_at_unix_secs: u64) {
169 let mut s = state.borrow_mut();
170 s.expires_at_unix_secs = expires_at_unix_secs;
171}