1use std::sync::{
18 Arc,
19 atomic::{AtomicBool, Ordering},
20};
21
22use super::{
26 Manager, OperationGeneration, OperationInstanceId, OperationStatus, Running, StreamInput, StreamItem, StreamOutput, StreamSettings,
27 stream,
28};
29use crate::operations::OperationDeclaration;
30use crate::supervision::instance::{Instance, Phase};
31use crate::{Error, Refusal, Result};
32use harmos_domain::MemorySize;
33
34pub type Upload<O> = Session<O, SessionInput<<<O as OperationDeclaration>::Input as crate::operations::Direction>::Item>, ()>;
41pub type Download<O> = Session<O, (), SessionOutput<<<O as OperationDeclaration>::Output as crate::operations::Direction>::Item>>;
42pub type Exchange<O> = Session<
43 O,
44 SessionInput<<<O as OperationDeclaration>::Input as crate::operations::Direction>::Item>,
45 SessionOutput<<<O as OperationDeclaration>::Output as crate::operations::Direction>::Item>,
46>;
47
48#[must_use = "dropping a streaming session requests cancellation"]
51pub struct Session<O: OperationDeclaration, I = (), R = ()> {
52 input: I,
53 output: R,
54 completion: Completion<O>,
55}
56
57pub struct SessionInput<T> {
58 sender: Option<StreamOutput<T>>,
59 ends: Arc<Ends>,
60 control: Arc<Instance>,
61}
62
63pub struct SessionOutput<T> {
64 receiver: StreamInput<T>,
65 ends: Arc<Ends>,
66 control: Arc<Instance>,
67}
68
69#[must_use = "join completion or explicitly cancel and settle"]
70pub struct Completion<O: OperationDeclaration> {
71 instance: Running<O>,
72 ends: Arc<Ends>,
73 delivered: bool,
74}
75
76struct Ends {
78 input: AtomicBool,
79 output: AtomicBool,
80}
81
82impl<O: OperationDeclaration, I, R> Session<O, I, R> {
89 pub fn id(&self) -> OperationInstanceId {
90 self.completion.id()
91 }
92
93 pub fn generation(&self) -> OperationGeneration {
94 self.completion.generation()
95 }
96
97 pub fn status(&self) -> OperationStatus {
98 self.completion.status()
99 }
100
101 pub fn cancel(&self) {
102 self.completion.cancel();
103 }
104
105 pub fn split(self) -> (I, R, Completion<O>) {
106 (self.input, self.output, self.completion)
107 }
108
109 pub async fn join(self) -> Result<crate::operations::Response<O::Response>> {
110 let (input, output, completion) = self.split();
111 drop(input);
112 drop(output);
113 completion.join().await
114 }
115
116 pub async fn cancel_and_settle(self) -> Result<()> {
117 self.cancel();
118 let (input, output, completion) = self.split();
119 drop(input);
120 drop(output);
121 completion.cancel_and_settle().await
122 }
123}
124
125impl<O: OperationDeclaration, T, R> Session<O, SessionInput<T>, R> {
126 pub fn finish_input(&mut self) {
127 self.input.finish_input();
128 }
129}
130impl<O: OperationDeclaration, T: MemorySize, R> Session<O, SessionInput<T>, R> {
131 pub async fn send(&self, item: T) -> Result<()> {
132 self.input.send(item).await
133 }
134
135 pub async fn send_with(&self, bytes: usize, make: impl FnOnce() -> Result<T>) -> Result<()> {
136 self.input.send_with(bytes, make).await
137 }
138}
139impl<O: OperationDeclaration, I, T> Session<O, I, SessionOutput<T>> {
140 pub async fn next(&mut self) -> Result<Option<StreamItem<T>>> {
141 self.output.next().await
142 }
143}
144
145impl<T> SessionInput<T> {
146 pub fn finish_input(&mut self) {
147 self.ends.input.store(true, Ordering::Release);
148 self.sender.take();
149 }
150}
151impl<T: MemorySize> SessionInput<T> {
152 pub async fn send(&self, item: T) -> Result<()> {
153 self.send_with(item.memory_size(), || Ok(item)).await
154 }
155
156 pub async fn send_with(&self, bytes: usize, make: impl FnOnce() -> Result<T>) -> Result<()> {
157 let sender = self.sender.as_ref().ok_or(Error::NotReady { domain: "operation input closed" })?;
158 tokio::select! {
159 biased;
160 error = ended(&self.control) => Err(error),
161 permit = sender.reserve(bytes) => {
162 let permit = permit?;
163 let value = make()?;
164 Ok(permit.send_with(|| Ok(value))?)
165 },
166 }
167 }
168}
169impl<T> Drop for SessionInput<T> {
170 fn drop(&mut self) {
171 if !self.ends.input.load(Ordering::Acquire) {
172 cancel_unsettled(&self.control);
173 }
174 }
175}
176
177impl<T> SessionOutput<T> {
178 pub async fn next(&mut self) -> Result<Option<StreamItem<T>>> {
179 if self.ends.output.load(Ordering::Acquire) {
180 return Ok(None);
181 }
182 let mut status = self.control.settlement();
183 loop {
184 let current = status.borrow_and_update().clone();
185 if current.cancellation_requested() {
186 self.receiver.close();
187 return Err(Refusal::Cancelled.into());
188 }
189 if current.outcome().is_some() {
190 self.receiver.close();
191 let item = self.receiver.try_recv().ok();
192 if item.is_none() {
193 self.ends.output.store(true, Ordering::Release);
194 }
195 return Ok(item);
196 }
197 tokio::select! {
198 biased;
199 item = self.receiver.recv() => {
200 if item.is_none() { self.ends.output.store(true, Ordering::Release); }
201 return Ok(item);
202 }
203 changed = status.changed() => {
204 if changed.is_err() { return Err(Refusal::Unavailable.into()); }
205 }
206 }
207 }
208 }
209}
210impl<T> Drop for SessionOutput<T> {
211 fn drop(&mut self) {
212 if !self.ends.output.load(Ordering::Acquire) {
213 cancel_unsettled(&self.control);
214 }
215 }
216}
217
218impl<O: OperationDeclaration> Completion<O> {
219 pub fn id(&self) -> OperationInstanceId {
220 self.instance.id()
221 }
222
223 pub fn generation(&self) -> OperationGeneration {
224 self.instance.generation()
225 }
226
227 pub fn status(&self) -> OperationStatus {
228 self.instance.status()
229 }
230
231 pub fn cancel(&self) {
232 self.instance.cancel();
233 }
234
235 pub async fn settle(&self) -> Result<()> {
236 self.instance.settle().await
237 }
238
239 pub async fn join(mut self) -> Result<crate::operations::Response<O::Response>> {
240 if !self.ends.input.load(Ordering::Acquire) || !self.ends.output.load(Ordering::Acquire) {
241 return Err(Error::InvalidInput {
242 field: "operation stream completion",
243 reason: "finish input and drain output before joining; use cancel_and_settle to abandon",
244 });
245 }
246 let result = (&mut self.instance).await;
247 self.delivered = true;
248 result
249 }
250
251 pub async fn cancel_and_settle(mut self) -> Result<()> {
252 self.instance.cancel();
253 let result = self.instance.settle().await;
254 self.delivered = true;
255 result
256 }
257}
258impl<O: OperationDeclaration> Drop for Completion<O> {
259 fn drop(&mut self) {
260 if !self.delivered {
261 cancel_unsettled(&self.instance.control);
262 }
263 }
264}
265
266impl Manager {
267 pub fn upload<O: crate::operations::OperationDeclaration>(&self, request: O::Request, settings: StreamSettings) -> Result<Upload<O>> {
268 upload(request, settings, |request, input| self.submit_endpoints::<O, _>(request, input))
269 }
270
271 pub fn download<O: crate::operations::OperationDeclaration>(
272 &self, request: O::Request, settings: StreamSettings,
273 ) -> Result<Download<O>> {
274 download(request, settings, |request, output| self.submit_endpoints::<O, _>(request, output))
275 }
276
277 pub fn exchange<O: crate::operations::OperationDeclaration>(
278 &self, request: O::Request, input: StreamSettings, output: StreamSettings,
279 ) -> Result<Exchange<O>> {
280 exchange(request, input, output, |request, endpoints| self.submit_endpoints::<O, _>(request, endpoints))
281 }
282}
283
284impl super::Context<'_> {
285 pub fn upload<O: crate::operations::OperationDeclaration>(&self, request: O::Request, settings: StreamSettings) -> Result<Upload<O>> {
286 upload(request, settings, |request, input| self.submit_endpoints::<O, _>(request, input))
287 }
288
289 pub fn download<O: crate::operations::OperationDeclaration>(
290 &self, request: O::Request, settings: StreamSettings,
291 ) -> Result<Download<O>> {
292 download(request, settings, |request, output| self.submit_endpoints::<O, _>(request, output))
293 }
294
295 pub fn exchange<O: crate::operations::OperationDeclaration>(
296 &self, request: O::Request, input: StreamSettings, output: StreamSettings,
297 ) -> Result<Exchange<O>> {
298 exchange(request, input, output, |request, endpoints| self.submit_endpoints::<O, _>(request, endpoints))
299 }
300}
301
302#[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
303impl super::Caller {
304 pub fn upload<O: crate::operations::OperationDeclaration>(&self, request: O::Request, settings: StreamSettings) -> Result<Upload<O>> {
305 upload(request, settings, |request, input| self.submit_endpoints::<O, _>(request, input))
306 }
307
308 pub fn download<O: crate::operations::OperationDeclaration>(
309 &self, request: O::Request, settings: StreamSettings,
310 ) -> Result<Download<O>> {
311 download(request, settings, |request, output| self.submit_endpoints::<O, _>(request, output))
312 }
313
314 pub fn exchange<O: crate::operations::OperationDeclaration>(
315 &self, request: O::Request, input: StreamSettings, output: StreamSettings,
316 ) -> Result<Exchange<O>> {
317 exchange(request, input, output, |request, endpoints| self.submit_endpoints::<O, _>(request, endpoints))
318 }
319}
320
321fn upload<O: crate::operations::OperationDeclaration>(
326 request: O::Request, settings: StreamSettings,
327 submit: impl FnOnce(
328 O::Request,
329 StreamInput<<<O as OperationDeclaration>::Input as crate::operations::Direction>::Item>,
330 ) -> Result<Running<O>>,
331) -> Result<Upload<O>> {
332 let (sender, input) = stream(settings)?;
333 let instance = submit(request, input)?;
334 let ends = Arc::new(Ends {
335 input: AtomicBool::new(false),
336 output: AtomicBool::new(true),
337 });
338 Ok(Session {
339 input: SessionInput {
340 sender: Some(sender),
341 ends: ends.clone(),
342 control: instance.control.clone(),
343 },
344 output: (),
345 completion: Completion { instance, ends, delivered: false },
346 })
347}
348fn download<O: crate::operations::OperationDeclaration>(
349 request: O::Request, settings: StreamSettings,
350 submit: impl FnOnce(
351 O::Request,
352 StreamOutput<<<O as OperationDeclaration>::Output as crate::operations::Direction>::Item>,
353 ) -> Result<Running<O>>,
354) -> Result<Download<O>> {
355 let (output, receiver) = stream(settings)?;
356 let instance = submit(request, output)?;
357 let ends = Arc::new(Ends {
358 input: AtomicBool::new(true),
359 output: AtomicBool::new(false),
360 });
361 Ok(Session {
362 input: (),
363 output: SessionOutput {
364 receiver,
365 ends: ends.clone(),
366 control: instance.control.clone(),
367 },
368 completion: Completion { instance, ends, delivered: false },
369 })
370}
371fn exchange<O: crate::operations::OperationDeclaration>(
372 request: O::Request, input: StreamSettings, output: StreamSettings,
373 submit: impl FnOnce(
374 O::Request,
375 (
376 StreamInput<<<O as OperationDeclaration>::Input as crate::operations::Direction>::Item>,
377 StreamOutput<<<O as OperationDeclaration>::Output as crate::operations::Direction>::Item>,
378 ),
379 ) -> Result<Running<O>>,
380) -> Result<Exchange<O>> {
381 let (sender, input) = stream(input)?;
382 let (output, receiver) = stream(output)?;
383 let instance = submit(request, (input, output))?;
384 let ends = Arc::new(Ends {
385 input: AtomicBool::new(false),
386 output: AtomicBool::new(false),
387 });
388 Ok(Session {
389 input: SessionInput {
390 sender: Some(sender),
391 ends: ends.clone(),
392 control: instance.control.clone(),
393 },
394 output: SessionOutput {
395 receiver,
396 ends: ends.clone(),
397 control: instance.control.clone(),
398 },
399 completion: Completion { instance, ends, delivered: false },
400 })
401}
402fn cancel_unsettled(control: &Instance) {
403 if control.status().phase() != Phase::Settled {
404 control.cancel();
405 }
406}
407async fn ended(control: &Instance) -> Error {
408 let mut status = control.settlement();
409 loop {
410 let current = status.borrow_and_update().clone();
411 if current.cancellation_requested() {
412 return Refusal::Cancelled.into();
413 }
414 if current.outcome().is_some() {
415 return Error::NotReady { domain: "operation completed" };
416 }
417 if status.changed().await.is_err() {
418 return Refusal::Unavailable.into();
419 }
420 }
421}
422
423