aster_forge_cloud_files_macos_bridge/
session.rs1use std::{
4 fmt,
5 sync::{Arc, Mutex, MutexGuard},
6};
7
8use aster_forge_cloud_files_core::{SessionGeneration, SessionState};
9
10use crate::{MacosBridgeError, Result};
11
12#[derive(Debug)]
13struct SessionLifecycle {
14 state: SessionState,
15 active_requests: usize,
16}
17
18#[derive(Debug)]
19struct SessionInner {
20 generation: SessionGeneration,
21 lifecycle: Mutex<SessionLifecycle>,
22}
23
24#[derive(Debug, Clone)]
26pub struct MacosExtensionSession {
27 inner: Arc<SessionInner>,
28}
29
30impl MacosExtensionSession {
31 #[must_use]
33 pub fn new(generation: SessionGeneration) -> Self {
34 Self {
35 inner: Arc::new(SessionInner {
36 generation,
37 lifecycle: Mutex::new(SessionLifecycle {
38 state: SessionState::Accepting,
39 active_requests: 0,
40 }),
41 }),
42 }
43 }
44
45 #[must_use]
47 pub fn generation(&self) -> SessionGeneration {
48 self.inner.generation
49 }
50
51 #[must_use]
53 pub fn state(&self) -> SessionState {
54 lock(&self.inner.lifecycle).state
55 }
56
57 #[must_use]
59 pub fn active_requests(&self) -> usize {
60 lock(&self.inner.lifecycle).active_requests
61 }
62
63 pub(crate) fn into_ffi_handle(self) -> *const () {
64 Arc::into_raw(self.inner).cast()
65 }
66
67 pub(crate) unsafe fn clone_from_ffi_handle(handle: *const ()) -> Result<Self> {
68 if handle.is_null() {
69 return Err(MacosBridgeError::InvalidFfiInput {
70 reason: "extension session handle must not be null",
71 });
72 }
73 let pointer = handle.cast::<SessionInner>();
74 unsafe { Arc::increment_strong_count(pointer) };
77 let inner = unsafe { Arc::from_raw(pointer) };
79 Ok(Self { inner })
80 }
81
82 pub(crate) unsafe fn release_ffi_handle(handle: *const ()) {
83 if handle.is_null() {
84 return;
85 }
86 let pointer = handle.cast::<SessionInner>();
87 let session = unsafe { Arc::from_raw(pointer) };
90 drop(session);
91 }
92
93 pub fn begin_request(
99 &self,
100 callback_generation: SessionGeneration,
101 ) -> Result<MacosExtensionRequestLease> {
102 if callback_generation != self.inner.generation {
103 return Err(MacosBridgeError::StaleSessionGeneration {
104 expected: self.inner.generation.get(),
105 actual: callback_generation.get(),
106 });
107 }
108 let mut lifecycle = lock(&self.inner.lifecycle);
109 if lifecycle.state != SessionState::Accepting {
110 return Err(MacosBridgeError::SessionNotAccepting {
111 state: lifecycle.state,
112 });
113 }
114 lifecycle.active_requests = lifecycle
115 .active_requests
116 .checked_add(1)
117 .ok_or(MacosBridgeError::ActiveRequestCountOverflow)?;
118 drop(lifecycle);
119 Ok(MacosExtensionRequestLease {
120 session: self.inner.clone(),
121 released: false,
122 })
123 }
124
125 #[must_use]
127 pub fn begin_closing(&self) -> bool {
128 let mut lifecycle = lock(&self.inner.lifecycle);
129 if lifecycle.state == SessionState::Accepting {
130 lifecycle.state = SessionState::Closing;
131 true
132 } else {
133 false
134 }
135 }
136
137 pub fn mark_disconnected(&self) -> Result<()> {
143 let mut lifecycle = lock(&self.inner.lifecycle);
144 match lifecycle.state {
145 SessionState::Closing => {
146 lifecycle.state = if lifecycle.active_requests == 0 {
147 SessionState::Closed
148 } else {
149 SessionState::Draining
150 };
151 Ok(())
152 }
153 SessionState::Draining | SessionState::Closed => Ok(()),
154 SessionState::Accepting => Err(MacosBridgeError::InvalidSessionTransition {
155 from: SessionState::Accepting,
156 to: SessionState::Draining,
157 }),
158 }
159 }
160}
161
162pub struct MacosExtensionRequestLease {
164 session: Arc<SessionInner>,
165 released: bool,
166}
167
168impl MacosExtensionRequestLease {
169 #[must_use]
171 pub fn generation(&self) -> SessionGeneration {
172 self.session.generation
173 }
174
175 #[must_use]
177 pub fn accepts_completion(&self, session: &MacosExtensionSession) -> bool {
178 !self.released
179 && Arc::ptr_eq(&self.session, &session.inner)
180 && lock(&self.session.lifecycle).state != SessionState::Closed
181 }
182
183 pub fn release(mut self) {
185 self.release_inner();
186 }
187
188 pub(crate) fn into_ffi_handle(self) -> *mut () {
189 Box::into_raw(Box::new(self)).cast()
190 }
191
192 pub(crate) unsafe fn release_ffi_handle(handle: *mut ()) {
193 if handle.is_null() {
194 return;
195 }
196 let lease = unsafe { Box::from_raw(handle.cast::<Self>()) };
199 drop(lease);
200 }
201
202 fn release_inner(&mut self) {
203 if self.released {
204 return;
205 }
206 let mut lifecycle = lock(&self.session.lifecycle);
207 lifecycle.active_requests = lifecycle.active_requests.saturating_sub(1);
208 if lifecycle.state == SessionState::Draining && lifecycle.active_requests == 0 {
209 lifecycle.state = SessionState::Closed;
210 }
211 self.released = true;
212 }
213}
214
215impl fmt::Debug for MacosExtensionRequestLease {
216 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
217 formatter
218 .debug_struct("MacosExtensionRequestLease")
219 .field("generation", &self.session.generation)
220 .field("released", &self.released)
221 .finish()
222 }
223}
224
225impl Drop for MacosExtensionRequestLease {
226 fn drop(&mut self) {
227 self.release_inner();
228 }
229}
230
231fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
232 match mutex.lock() {
233 Ok(guard) => guard,
234 Err(poisoned) => poisoned.into_inner(),
235 }
236}