1use std::{cell, fmt, future, pin::Pin, ptr, rc::Rc, task::Context, task::Poll};
2
3use crate::state::{Noop, State};
4use crate::{Ctx, IntoService, Service, ctx::WaitersRef, util::BoxFuture};
5
6pub use crate::pl_factory::PipelineFactory;
7pub use crate::pl_nost::{PipelineWithState, PipelineWithStateBinding};
8
9pub struct Pipeline<Req, Res, Err> {
13 state: Rc<dyn PipelineApi<Req, Res, Err>>,
14}
15
16pub struct PipelineBinding<Req, Res, Err> {
18 index: u32,
19 state: Rc<dyn PipelineApi<Req, Res, Err>>,
20}
21
22struct PipelineState<S: Service<St, Req>, St, Req, Ctl> {
23 s: S,
24 st: St,
25 st_ctl: Ctl,
26 st_runtime: cell::UnsafeCell<RuntimeState<S::Error>>,
27 waiters: WaitersRef,
28}
29
30impl<Req, Res, Err> Pipeline<Req, Res, Err>
31where
32 Req: 'static,
33 Res: 'static,
34 Err: 'static,
35{
36 #[inline]
37 pub fn new<S, St>(f: impl IntoService<S, St, Req>) -> Self
39 where
40 S: Service<St, Req, Res = Res, Error = Err> + 'static,
41 St: Default + 'static,
42 {
43 Self::create(f.into_service(), St::default(), Noop)
44 }
45
46 #[inline]
47 pub fn with<S, St>(st: St, f: impl IntoService<S, St, Req>) -> Self
49 where
50 S: Service<St, Req, Res = Res, Error = Err> + 'static,
51 St: 'static,
52 {
53 Self::create(f.into_service(), st, Noop)
54 }
55
56 #[inline]
57 pub fn with_ctl<S, St, Ctl>(st: St, ctl: Ctl, f: impl IntoService<S, St, Req>) -> Self
59 where
60 S: Service<St, Req, Res = Res, Error = Err> + 'static,
61 St: 'static,
62 Ctl: State<St, Req> + 'static,
63 {
64 Self::create(f.into_service(), st, ctl)
65 }
66
67 fn create<S, St, Ctl>(s: S, st: St, ctl: Ctl) -> Self
68 where
69 S: Service<St, Req, Res = Res, Error = Err> + 'static,
70 St: 'static,
71 Ctl: State<St, Req> + 'static,
72 {
73 Pipeline {
74 state: Rc::new(PipelineState {
75 s,
76 st,
77 waiters: WaitersRef::new(),
78 st_ctl: ctl,
79 st_runtime: cell::UnsafeCell::new(RuntimeState::New),
80 }),
81 }
82 }
83
84 #[inline]
85 pub async fn ready(&self) -> Result<(), Err> {
87 future::poll_fn(|cx| self.state.poll_ready(cx)).await
88 }
89
90 #[inline]
91 pub async fn call(&self, req: Req) -> Result<Res, Err> {
94 let pl = self.bind();
95 pl.state.call(pl.index, req, true).await
96 }
97
98 #[inline]
99 pub fn call_static(&self, req: Req) -> PipelineCall<Req, Res, Err> {
104 PipelineCall::new(self.bind(), req, true)
105 }
106
107 #[inline]
108 pub fn call_nowait(&self, req: Req) -> PipelineCall<Req, Res, Err> {
113 PipelineCall::new(self.bind(), req, false)
114 }
115
116 #[inline]
117 pub fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Err>> {
119 self.state.poll_ready(cx)
120 }
121
122 #[inline]
123 pub fn poll_shutdown(&self, cx: &mut Context<'_>) -> Poll<()> {
125 self.state.poll_shutdown(cx)
126 }
127
128 #[inline]
129 pub fn is_shutdown(&self) -> bool {
131 self.state.is_shutdown()
132 }
133
134 #[inline]
135 pub async fn shutdown(&self) {
137 future::poll_fn(|cx| self.state.poll_shutdown(cx)).await;
138 }
139
140 #[inline]
141 pub fn bind(&self) -> PipelineBinding<Req, Res, Err> {
145 PipelineBinding::new(self)
146 }
147}
148
149impl<Req, Res, Err> Drop for Pipeline<Req, Res, Err> {
150 #[inline]
151 fn drop(&mut self) {
152 self.state.unreg(0);
153 }
154}
155
156impl<Req, Res, Err> PipelineBinding<Req, Res, Err>
157where
158 Req: 'static,
159 Res: 'static,
160 Err: 'static,
161{
162 fn new(pl: &Pipeline<Req, Res, Err>) -> Self {
163 Self {
164 index: pl.state.reg(),
165 state: pl.state.clone(),
166 }
167 }
168
169 #[inline]
170 pub async fn ready(&self) -> Result<(), Err> {
172 self.state.ready(self.index).await
173 }
174
175 #[inline]
176 pub async fn call(&self, req: Req) -> Result<Res, Err> {
179 let pl = self.clone();
180 pl.state.call(pl.index, req, true).await
181 }
182
183 #[inline]
184 pub fn call_static(&self, req: Req) -> PipelineCall<Req, Res, Err> {
189 PipelineCall::new(self.clone(), req, true)
190 }
191
192 #[inline]
193 pub fn call_nowait(&self, req: Req) -> PipelineCall<Req, Res, Err> {
198 PipelineCall::new(self.clone(), req, false)
199 }
200
201 #[inline]
202 pub async fn shutdown(&self) {
204 future::poll_fn(|cx| self.state.poll_shutdown(cx)).await;
205 }
206}
207
208impl<Req, Res, Err> Drop for PipelineBinding<Req, Res, Err> {
209 #[inline]
210 fn drop(&mut self) {
211 self.state.unreg(self.index);
212 }
213}
214
215impl<Req, Res, Err> Clone for PipelineBinding<Req, Res, Err> {
216 fn clone(&self) -> Self {
217 Self {
218 index: self.state.reg(),
219 state: self.state.clone(),
220 }
221 }
222}
223
224#[must_use = "futures do nothing unless polled"]
225pub struct PipelineCall<Req, Res, Err> {
227 #[allow(dead_code)]
228 pl: PipelineBinding<Req, Res, Err>,
229 fut: BoxFuture<'static, Result<Res, Err>>,
230}
231
232impl<Req, Res, Err> PipelineCall<Req, Res, Err> {
233 #[allow(clippy::missing_transmute_annotations)]
234 fn new(pl: PipelineBinding<Req, Res, Err>, req: Req, ready: bool) -> Self {
235 PipelineCall {
238 fut: unsafe { std::mem::transmute(pl.state.call(pl.index, req, ready)) },
239 pl,
240 }
241 }
242}
243
244impl<Req, Res, Err> future::Future for PipelineCall<Req, Res, Err> {
245 type Output = Result<Res, Err>;
246
247 #[inline]
248 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
249 Pin::new(&mut self.as_mut().fut).poll(cx)
250 }
251}
252
253impl<S, St, Req, Ctl> PipelineState<S, St, Req, Ctl>
254where
255 S: Service<St, Req>,
256 Ctl: State<St, Req>,
257{
258 fn st(&self, req: &Req) -> StateRef<'_, St> {
259 if let Some(s) = self.st_ctl.on_req(&self.st, req) {
260 StateRef::Owned(s)
261 } else {
262 StateRef::Ref(&self.st)
263 }
264 }
265}
266
267enum RuntimeState<E> {
268 New,
269 Readiness(BoxFuture<'static, Result<(), E>>),
270 Shutdown(BoxFuture<'static, ()>),
271 Done,
272}
273
274enum StateRef<'a, T> {
275 Ref(&'a T),
276 Owned(T),
277}
278
279impl<'a, T> StateRef<'a, T> {
280 fn get_ref(&'a self) -> &'a T {
281 match self {
282 StateRef::Ref(t) => t,
283 StateRef::Owned(t) => t,
284 }
285 }
286}
287
288trait PipelineApi<Req, Res, Err> {
289 fn reg(&self) -> u32;
290
291 fn unreg(&self, idx: u32);
292
293 fn ready(&self, idx: u32) -> BoxFuture<'_, Result<(), Err>>;
294
295 fn call(&self, idx: u32, req: Req, ready: bool) -> BoxFuture<'_, Result<Res, Err>>;
296
297 fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Err>>;
298
299 fn poll_shutdown(&self, cx: &mut Context<'_>) -> Poll<()>;
300
301 fn is_shutdown(&self) -> bool;
302}
303
304impl<S, St, Req, Ctl> PipelineApi<Req, S::Res, S::Error> for PipelineState<S, St, Req, Ctl>
305where
306 S: Service<St, Req> + 'static,
307 St: 'static,
308 Req: 'static,
309 Ctl: State<St, Req> + 'static,
310{
311 fn reg(&self) -> u32 {
312 self.waiters.insert()
313 }
314
315 fn unreg(&self, index: u32) {
316 self.waiters.remove(index);
317 }
318
319 fn ready(&self, idx: u32) -> BoxFuture<'_, Result<(), S::Error>> {
320 Box::pin(async move {
321 Ctx::<'_, S, St>::new(idx, &self.waiters, &self.st)
322 .ready(&self.s)
323 .await
324 })
325 }
326
327 fn call(&self, idx: u32, req: Req, ready: bool) -> BoxFuture<'_, Result<S::Res, S::Error>> {
328 Box::pin(async move {
329 let st = self.st(&req);
330
331 if ready {
332 Ctx::<'_, S, St>::new(idx, &self.waiters, st.get_ref())
333 .call(&self.s, req)
334 .await
335 } else {
336 Ctx::<'_, S, St>::new(idx, &self.waiters, st.get_ref())
337 .call_nowait(&self.s, req)
338 .await
339 }
340 })
341 }
342
343 fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), S::Error>> {
344 let st = unsafe { &mut *self.st_runtime.get() };
345 match st {
346 RuntimeState::New => {
347 let pl = unsafe { &*(ptr::from_ref(self)) };
351 let fut = Box::pin(CheckReadiness {
352 pl,
353 f: ready,
354 fut: None,
355 });
356 *st = RuntimeState::Readiness(fut);
357 self.poll_ready(cx)
358 }
359 RuntimeState::Readiness(fut) => Pin::new(fut).poll(cx),
360 RuntimeState::Shutdown(_) | RuntimeState::Done => Poll::Ready(Ok(())),
361 }
362 }
363
364 fn poll_shutdown(&self, cx: &mut Context<'_>) -> Poll<()> {
365 let st = unsafe { &mut *self.st_runtime.get() };
366 match st {
367 RuntimeState::New | RuntimeState::Readiness(_) => {
368 let pl = unsafe { &*(ptr::from_ref(self)) };
372
373 let fut = Box::pin(async move {
374 let ctx = Ctx::<'_, S, St>::new(0, &pl.waiters, &pl.st);
375 pl.s.shutdown(ctx).await;
376 });
377 *st = RuntimeState::Shutdown(fut);
378 pl.waiters.shutdown();
379 self.poll_shutdown(cx)
380 }
381 RuntimeState::Shutdown(fut) => {
382 let res = Pin::new(fut).poll(cx);
383 if res.is_ready() {
384 *st = RuntimeState::Done;
385 }
386 res
387 }
388 RuntimeState::Done => Poll::Ready(()),
389 }
390 }
391
392 fn is_shutdown(&self) -> bool {
393 self.waiters.is_shutdown()
394 }
395}
396
397fn ready<S, St, Req, Ctl>(
398 pl: &'static PipelineState<S, St, Req, Ctl>,
399) -> impl future::Future<Output = Result<(), S::Error>>
400where
401 S: Service<St, Req>,
402 Ctl: State<St, Req>,
403{
404 pl.s.ready(Ctx::<'_, S, St>::new(0, &pl.waiters, &pl.st))
405}
406
407struct CheckReadiness<S, St, Req, Ctl, F, Fut>
408where
409 S: Service<St, Req> + 'static,
410 St: 'static,
411 Req: 'static,
412 Ctl: 'static,
413{
414 f: F,
415 fut: Option<Fut>,
416 pl: &'static PipelineState<S, St, Req, Ctl>,
417}
418
419impl<S: Service<St, Req>, St, Req, Ctl, F, Fut> Unpin for CheckReadiness<S, St, Req, Ctl, F, Fut> {}
420
421impl<S: Service<St, Req>, St, Req, Ctl, F, Fut> Drop for CheckReadiness<S, St, Req, Ctl, F, Fut> {
422 fn drop(&mut self) {
423 if self.fut.is_some() {
425 self.pl.waiters.notify();
426 }
427 }
428}
429
430impl<S, St, Req, Ctl, F, Fut> Future for CheckReadiness<S, St, Req, Ctl, F, Fut>
431where
432 S: Service<St, Req>,
433 F: Fn(&'static PipelineState<S, St, Req, Ctl>) -> Fut,
434 Fut: Future<Output = Result<(), S::Error>>,
435{
436 type Output = Result<(), S::Error>;
437
438 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
439 let mut this = self.as_mut();
440
441 this.pl.waiters.run(0, cx, |cx| {
442 if this.fut.is_none() {
443 this.fut = Some((this.f)(this.pl));
444 }
445 let fut = this.fut.as_mut().unwrap();
446 let result = unsafe { Pin::new_unchecked(fut) }.poll(cx);
447 if result.is_ready() {
448 let _ = this.fut.take();
449 }
450 result
451 })
452 }
453}
454
455impl<Req, Res, Err> fmt::Debug for Pipeline<Req, Res, Err> {
456 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457 f.debug_struct("Pipeline").finish()
458 }
459}
460
461impl<Req, Res, Err> fmt::Debug for PipelineBinding<Req, Res, Err> {
462 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
463 f.debug_struct("PipelineBinding")
464 .field("idx", &self.index)
465 .finish()
466 }
467}
468
469impl<Req, Res, Err> fmt::Debug for PipelineCall<Req, Res, Err> {
470 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
471 f.debug_struct("PipelineCall").finish()
472 }
473}