mj_controller/session_manager/
channels.rs1use super::*;
2
3#[derive(Debug, Clone)]
4pub struct SessionManagerUpdate {
5 pub session_id: String,
6 pub view: ManagedSessionView,
7}
8
9pub struct SessionManagerChannels {
10 pub targets: watch::Sender<Vec<RelaySessionTarget>>,
11 pub control: SessionManagerControl,
12 pub updates: SessionManagerUpdates,
13 pub shutdown: SessionManagerShutdown,
14}
15
16pub struct RemoteSessionManagerChannels {
22 pub targets: watch::Sender<Vec<RelaySessionTarget>>,
23 pub control: SessionManagerControl,
24 pub updates: SessionManagerUpdates,
25 pub shutdown: SessionManagerShutdown,
26 pub publisher: RemoteSessionPublisher,
27 pub requests: RemoteSessionRequests,
28}
29
30#[derive(Clone)]
31pub struct RemoteSessionPublisher {
32 pub(super) updates: mpsc::UnboundedSender<RemoteManagerUpdate>,
33}
34
35impl RemoteSessionPublisher {
36 pub async fn publish(&self, session_id: String, view: ManagedSessionView) -> Result<()> {
37 self.updates
38 .send(RemoteManagerUpdate::Publish { session_id, view })
39 .context("remote session manager stopped")
40 }
41
42 pub fn try_publish(&self, session_id: String, view: ManagedSessionView) -> Result<()> {
43 self.updates
44 .send(RemoteManagerUpdate::Publish { session_id, view })
45 .context("remote session manager update queue is unavailable")
46 }
47}
48
49pub struct RemoteSessionRequests {
50 pub(super) requests: mpsc::Receiver<RemoteSessionRequest>,
51}
52
53impl RemoteSessionRequests {
54 pub async fn recv(&mut self) -> Option<RemoteSessionRequest> {
55 self.requests.recv().await
56 }
57}
58
59pub enum RemoteSessionRequest {
60 Submit {
61 session_id: String,
62 command_id: String,
63 command: RelayCommand,
64 admission: Option<ReviewDeliveryAdmission>,
65 reply: oneshot::Sender<std::result::Result<u64, String>>,
66 },
67 Sync {
68 session_id: String,
69 reply: oneshot::Sender<std::result::Result<(), String>>,
70 },
71 RespondElicitation {
72 session_id: String,
73 elicitation_id: String,
74 response: ElicitationResponse,
75 reply: oneshot::Sender<std::result::Result<(), String>>,
76 },
77 StopBackgroundTask {
78 session_id: String,
79 background_task_id: String,
80 reply: oneshot::Sender<std::result::Result<(), String>>,
81 },
82 Reviewer {
83 session_id: String,
84 role: Option<String>,
86 action: ReviewerAction,
87 reply: oneshot::Sender<std::result::Result<ReviewerOutcome, String>>,
88 },
89}
90
91impl RemoteSessionRequest {
92 pub fn session_id(&self) -> &str {
95 match self {
96 Self::Submit { session_id, .. }
97 | Self::Sync { session_id, .. }
98 | Self::RespondElicitation { session_id, .. }
99 | Self::StopBackgroundTask { session_id, .. }
100 | Self::Reviewer { session_id, .. } => session_id,
101 }
102 }
103}
104
105#[derive(Default)]
115pub struct SessionRequestOrder {
116 pub(super) latest: std::collections::HashMap<SessionRequestStream, tokio::task::JoinHandle<()>>,
117}
118
119#[derive(Debug, PartialEq, Eq, Hash)]
120pub(super) enum SessionRequestStream {
121 Primary(String),
122 Reviewer(String, Option<String>),
123}
124
125impl SessionRequestOrder {
126 #[must_use]
127 pub fn new() -> Self {
128 Self::default()
129 }
130
131 pub fn dispatch<F, Fut>(&mut self, request: RemoteSessionRequest, forward: F)
135 where
136 F: FnOnce(RemoteSessionRequest) -> Fut + Send + 'static,
137 Fut: std::future::Future<Output = ()> + Send,
138 {
139 self.latest.retain(|_, handle| !handle.is_finished());
143 let stream = match &request {
144 RemoteSessionRequest::Reviewer {
145 session_id, role, ..
146 } => SessionRequestStream::Reviewer(session_id.clone(), role.clone()),
147 _ => SessionRequestStream::Primary(request.session_id().to_owned()),
148 };
149 let previous = self.latest.remove(&stream);
150 let handle = tokio::spawn(async move {
151 if let Some(previous) = previous {
152 if let Err(error) = previous.await {
156 tracing::error!(%error, "previous session request task failed");
157 }
158 }
159 forward(request).await;
160 });
161 self.latest.insert(stream, handle);
162 }
163}
164
165pub struct SessionManagerShutdown {
171 pub(super) signal: Option<oneshot::Sender<()>>,
172 pub(super) task: Option<tokio::task::JoinHandle<()>>,
173}
174
175impl SessionManagerShutdown {
176 pub async fn shutdown(mut self) -> Result<()> {
177 if let Some(signal) = self.signal.take() {
178 let _ = signal.send(());
179 }
180 if let Some(task) = self.task.take() {
181 task.await.context("session manager shutdown task failed")?;
182 }
183 Ok(())
184 }
185}
186
187impl Drop for SessionManagerShutdown {
188 fn drop(&mut self) {
189 if let Some(signal) = self.signal.take() {
190 let _ = signal.send(());
191 }
192 if let Some(task) = self.task.take() {
193 task.abort();
194 }
195 }
196}
197
198#[derive(Clone)]
199pub(super) struct CoalescedUpdateSender {
200 pub(super) pending: Arc<Mutex<BTreeMap<String, SessionManagerUpdate>>>,
201 pub(super) wake: mpsc::Sender<()>,
202}
203
204pub struct SessionManagerUpdates {
207 pub(super) pending: Arc<Mutex<BTreeMap<String, SessionManagerUpdate>>>,
208 pub(super) wake: mpsc::Receiver<()>,
209}
210
211impl CoalescedUpdateSender {
212 pub(super) fn send(&self, update: SessionManagerUpdate) {
213 if self.wake.is_closed() {
214 return;
215 }
216 self.pending
217 .lock()
218 .expect("session update coalescer poisoned")
219 .insert(update.session_id.clone(), update);
220 let _ = self.wake.try_send(());
221 }
222}
223
224impl SessionManagerUpdates {
225 pub(super) fn pop_pending(&self) -> Option<SessionManagerUpdate> {
226 self.pending
227 .lock()
228 .expect("session update coalescer poisoned")
229 .pop_first()
230 .map(|(_, update)| update)
231 }
232
233 pub async fn recv(&mut self) -> Option<SessionManagerUpdate> {
234 loop {
235 if let Some(update) = self.pop_pending() {
236 return Some(update);
237 }
238 self.wake.recv().await?;
239 }
240 }
241
242 pub fn try_recv(
243 &mut self,
244 ) -> std::result::Result<SessionManagerUpdate, mpsc::error::TryRecvError> {
245 if let Some(update) = self.pop_pending() {
246 return Ok(update);
247 }
248 self.wake.try_recv()?;
249 self.pop_pending().ok_or(mpsc::error::TryRecvError::Empty)
250 }
251}
252
253pub(super) fn coalesced_update_channel() -> (CoalescedUpdateSender, SessionManagerUpdates) {
254 let pending = Arc::new(Mutex::new(BTreeMap::new()));
255 let (wake_tx, wake_rx) = mpsc::channel(1);
256 (
257 CoalescedUpdateSender {
258 pending: pending.clone(),
259 wake: wake_tx,
260 },
261 SessionManagerUpdates {
262 pending,
263 wake: wake_rx,
264 },
265 )
266}