1use std::{
10 fmt::{self, Debug},
11 pin::Pin,
12 task::{self, Poll},
13};
14
15use pin_project_lite::pin_project;
16use tokio::sync::watch;
17
18pub struct GracefulShutdown {
21 tx: watch::Sender<()>,
22}
23
24pub struct Watcher {
30 rx: watch::Receiver<()>,
31}
32
33impl GracefulShutdown {
34 pub fn new() -> Self {
36 let (tx, _) = watch::channel(());
37 Self { tx }
38 }
39
40 pub fn watch<C: GracefulConnection>(
42 &self,
43 conn: C,
44 ) -> impl Future<Output = C::Output> + use<C> {
45 self.watcher().watch(conn)
46 }
47
48 pub fn watcher(&self) -> Watcher {
57 let rx = self.tx.subscribe();
58 Watcher { rx }
59 }
60
61 pub async fn shutdown(self) {
66 let Self { tx } = self;
67
68 let _ = tx.send(());
70 tx.closed().await;
72 }
73
74 pub fn count(&self) -> usize {
76 self.tx.receiver_count()
77 }
78}
79
80impl Debug for GracefulShutdown {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 f.debug_struct("GracefulShutdown").finish()
83 }
84}
85
86impl Default for GracefulShutdown {
87 fn default() -> Self {
88 Self::new()
89 }
90}
91
92impl Watcher {
93 pub fn watch<C: GracefulConnection>(self, conn: C) -> impl Future<Output = C::Output> {
95 let Watcher { mut rx } = self;
96 GracefulConnectionFuture::new(conn, async move {
97 let _ = rx.changed().await;
98 rx
100 })
101 }
102}
103
104impl Debug for Watcher {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 f.debug_struct("GracefulWatcher").finish()
107 }
108}
109
110pin_project! {
111 struct GracefulConnectionFuture<C, F: Future> {
112 #[pin]
113 conn: C,
114 #[pin]
115 cancel: F,
116 #[pin]
117 cancelled_guard: Option<F::Output>,
119 }
120}
121
122impl<C, F: Future> GracefulConnectionFuture<C, F> {
123 fn new(conn: C, cancel: F) -> Self {
124 Self {
125 conn,
126 cancel,
127 cancelled_guard: None,
128 }
129 }
130}
131
132impl<C, F: Future> Debug for GracefulConnectionFuture<C, F> {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 f.debug_struct("GracefulConnectionFuture").finish()
135 }
136}
137
138impl<C, F> Future for GracefulConnectionFuture<C, F>
139where
140 C: GracefulConnection,
141 F: Future,
142{
143 type Output = C::Output;
144
145 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
146 let mut this = self.project();
147 if this.cancelled_guard.is_none() {
148 if let Poll::Ready(guard) = this.cancel.poll(cx) {
149 this.cancelled_guard.set(Some(guard));
150 this.conn.as_mut().graceful_shutdown();
151 }
152 }
153 this.conn.poll(cx)
154 }
155}
156
157pub trait GracefulConnection: Future<Output = Result<(), Self::Error>> + private::Sealed {
160 type Error;
162
163 fn graceful_shutdown(self: Pin<&mut Self>);
165}
166
167#[cfg(feature = "http1")]
168impl<I, B, S> GracefulConnection for hyper::server::conn::http1::Connection<I, S>
169where
170 S: hyper::service::HttpService<hyper::body::Incoming, ResBody = B>,
171 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
172 I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static,
173 B: hyper::body::Body + 'static,
174 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
175{
176 type Error = hyper::Error;
177
178 fn graceful_shutdown(self: Pin<&mut Self>) {
179 hyper::server::conn::http1::Connection::graceful_shutdown(self);
180 }
181}
182
183#[cfg(feature = "http2")]
184impl<I, B, S, E> GracefulConnection for hyper::server::conn::http2::Connection<I, S, E>
185where
186 S: hyper::service::HttpService<hyper::body::Incoming, ResBody = B>,
187 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
188 I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static,
189 B: hyper::body::Body + 'static,
190 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
191 E: hyper::rt::bounds::Http2ServerConnExec<S::Future, B>,
192{
193 type Error = hyper::Error;
194
195 fn graceful_shutdown(self: Pin<&mut Self>) {
196 hyper::server::conn::http2::Connection::graceful_shutdown(self);
197 }
198}
199
200#[cfg(feature = "server-auto")]
201impl<I, B, S, E> GracefulConnection for crate::server::conn::auto::Connection<'_, I, S, E>
202where
203 S: hyper::service::Service<http::Request<hyper::body::Incoming>, Response = http::Response<B>>,
204 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
205 S::Future: 'static,
206 I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static,
207 B: hyper::body::Body + 'static,
208 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
209 E: hyper::rt::bounds::Http2ServerConnExec<S::Future, B>,
210{
211 type Error = Box<dyn std::error::Error + Send + Sync>;
212
213 fn graceful_shutdown(self: Pin<&mut Self>) {
214 crate::server::conn::auto::Connection::graceful_shutdown(self);
215 }
216}
217
218#[cfg(feature = "server-auto")]
219impl<I, B, S, E> GracefulConnection
220 for crate::server::conn::auto::UpgradeableConnection<'_, I, S, E>
221where
222 S: hyper::service::Service<http::Request<hyper::body::Incoming>, Response = http::Response<B>>,
223 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
224 S::Future: 'static,
225 I: hyper::rt::Read + hyper::rt::Write + Unpin + Send + 'static,
226 B: hyper::body::Body + 'static,
227 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
228 E: hyper::rt::bounds::Http2ServerConnExec<S::Future, B>,
229{
230 type Error = Box<dyn std::error::Error + Send + Sync>;
231
232 fn graceful_shutdown(self: Pin<&mut Self>) {
233 crate::server::conn::auto::UpgradeableConnection::graceful_shutdown(self);
234 }
235}
236
237mod private {
238 pub trait Sealed {}
239
240 #[cfg(feature = "http1")]
241 impl<I, B, S> Sealed for hyper::server::conn::http1::Connection<I, S>
242 where
243 S: hyper::service::HttpService<hyper::body::Incoming, ResBody = B>,
244 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
245 I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static,
246 B: hyper::body::Body + 'static,
247 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
248 {
249 }
250
251 #[cfg(feature = "http1")]
252 impl<I, B, S> Sealed for hyper::server::conn::http1::UpgradeableConnection<I, S>
253 where
254 S: hyper::service::HttpService<hyper::body::Incoming, ResBody = B>,
255 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
256 I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static,
257 B: hyper::body::Body + 'static,
258 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
259 {
260 }
261
262 #[cfg(feature = "http2")]
263 impl<I, B, S, E> Sealed for hyper::server::conn::http2::Connection<I, S, E>
264 where
265 S: hyper::service::HttpService<hyper::body::Incoming, ResBody = B>,
266 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
267 I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static,
268 B: hyper::body::Body + 'static,
269 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
270 E: hyper::rt::bounds::Http2ServerConnExec<S::Future, B>,
271 {
272 }
273
274 #[cfg(feature = "server-auto")]
275 impl<I, B, S, E> Sealed for crate::server::conn::auto::Connection<'_, I, S, E>
276 where
277 S: hyper::service::Service<
278 http::Request<hyper::body::Incoming>,
279 Response = http::Response<B>,
280 >,
281 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
282 S::Future: 'static,
283 I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static,
284 B: hyper::body::Body + 'static,
285 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
286 E: hyper::rt::bounds::Http2ServerConnExec<S::Future, B>,
287 {
288 }
289
290 #[cfg(feature = "server-auto")]
291 impl<I, B, S, E> Sealed for crate::server::conn::auto::UpgradeableConnection<'_, I, S, E>
292 where
293 S: hyper::service::Service<
294 http::Request<hyper::body::Incoming>,
295 Response = http::Response<B>,
296 >,
297 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
298 S::Future: 'static,
299 I: hyper::rt::Read + hyper::rt::Write + Unpin + Send + 'static,
300 B: hyper::body::Body + 'static,
301 B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
302 E: hyper::rt::bounds::Http2ServerConnExec<S::Future, B>,
303 {
304 }
305}
306
307#[cfg(test)]
308mod test {
309 use super::*;
310 use pin_project_lite::pin_project;
311 use std::sync::Arc;
312 use std::sync::atomic::{AtomicUsize, Ordering};
313
314 pin_project! {
315 #[derive(Debug)]
316 struct DummyConnection<F> {
317 #[pin]
318 future: F,
319 shutdown_counter: Arc<AtomicUsize>,
320 }
321 }
322
323 impl<F> private::Sealed for DummyConnection<F> {}
324
325 impl<F: Future> GracefulConnection for DummyConnection<F> {
326 type Error = ();
327
328 fn graceful_shutdown(self: Pin<&mut Self>) {
329 self.shutdown_counter.fetch_add(1, Ordering::SeqCst);
330 }
331 }
332
333 impl<F: Future> Future for DummyConnection<F> {
334 type Output = Result<(), ()>;
335
336 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
337 match self.project().future.poll(cx) {
338 Poll::Ready(_) => Poll::Ready(Ok(())),
339 Poll::Pending => Poll::Pending,
340 }
341 }
342 }
343
344 #[cfg(not(miri))]
345 #[tokio::test]
346 async fn test_graceful_shutdown_ok() {
347 let graceful = GracefulShutdown::new();
348 let shutdown_counter = Arc::new(AtomicUsize::new(0));
349 let (dummy_tx, _) = tokio::sync::broadcast::channel(1);
350
351 for i in 1..=3 {
352 let mut dummy_rx = dummy_tx.subscribe();
353 let shutdown_counter = shutdown_counter.clone();
354
355 let future = async move {
356 tokio::time::sleep(std::time::Duration::from_millis(i * 10)).await;
357 let _ = dummy_rx.recv().await;
358 };
359 let dummy_conn = DummyConnection {
360 future,
361 shutdown_counter,
362 };
363 let conn = graceful.watch(dummy_conn);
364 tokio::spawn(async move {
365 conn.await.unwrap();
366 });
367 }
368
369 assert_eq!(shutdown_counter.load(Ordering::SeqCst), 0);
370 let _ = dummy_tx.send(());
371
372 tokio::select! {
373 _ = tokio::time::sleep(std::time::Duration::from_millis(100)) => {
374 panic!("timeout")
375 },
376 _ = graceful.shutdown() => {
377 assert_eq!(shutdown_counter.load(Ordering::SeqCst), 3);
378 }
379 }
380 }
381
382 #[cfg(not(miri))]
383 #[tokio::test]
384 async fn test_graceful_shutdown_delayed_ok() {
385 let graceful = GracefulShutdown::new();
386 let shutdown_counter = Arc::new(AtomicUsize::new(0));
387
388 for i in 1..=3 {
389 let shutdown_counter = shutdown_counter.clone();
390
391 let future = async move {
393 tokio::time::sleep(std::time::Duration::from_millis(i * 50)).await;
394 };
395 let dummy_conn = DummyConnection {
396 future,
397 shutdown_counter,
398 };
399 let conn = graceful.watch(dummy_conn);
400 tokio::spawn(async move {
401 conn.await.unwrap();
402 });
403 }
404
405 assert_eq!(shutdown_counter.load(Ordering::SeqCst), 0);
406
407 tokio::select! {
408 _ = tokio::time::sleep(std::time::Duration::from_millis(200)) => {
409 panic!("timeout")
410 },
411 _ = graceful.shutdown() => {
412 assert_eq!(shutdown_counter.load(Ordering::SeqCst), 3);
413 }
414 }
415 }
416
417 #[cfg(not(miri))]
418 #[tokio::test]
419 async fn test_graceful_shutdown_multi_per_watcher_ok() {
420 let graceful = GracefulShutdown::new();
421 let shutdown_counter = Arc::new(AtomicUsize::new(0));
422
423 for i in 1..=3 {
424 let shutdown_counter = shutdown_counter.clone();
425
426 let mut futures = Vec::new();
427 for u in 1..=i {
428 let future = tokio::time::sleep(std::time::Duration::from_millis(u * 50));
429 let dummy_conn = DummyConnection {
430 future,
431 shutdown_counter: shutdown_counter.clone(),
432 };
433 let conn = graceful.watch(dummy_conn);
434 futures.push(conn);
435 }
436 tokio::spawn(async move {
437 futures_util::future::join_all(futures).await;
438 });
439 }
440
441 assert_eq!(shutdown_counter.load(Ordering::SeqCst), 0);
442
443 tokio::select! {
444 _ = tokio::time::sleep(std::time::Duration::from_millis(200)) => {
445 panic!("timeout")
446 },
447 _ = graceful.shutdown() => {
448 assert_eq!(shutdown_counter.load(Ordering::SeqCst), 6);
449 }
450 }
451 }
452
453 #[cfg(not(miri))]
454 #[tokio::test]
455 async fn test_graceful_shutdown_timeout() {
456 let graceful = GracefulShutdown::new();
457 let shutdown_counter = Arc::new(AtomicUsize::new(0));
458
459 for i in 1..=3 {
460 let shutdown_counter = shutdown_counter.clone();
461
462 let future = async move {
463 if i == 1 {
464 std::future::pending::<()>().await
465 } else {
466 std::future::ready(()).await
467 }
468 };
469 let dummy_conn = DummyConnection {
470 future,
471 shutdown_counter,
472 };
473 let conn = graceful.watch(dummy_conn);
474 tokio::spawn(async move {
475 conn.await.unwrap();
476 });
477 }
478
479 assert_eq!(shutdown_counter.load(Ordering::SeqCst), 0);
480
481 tokio::select! {
482 _ = tokio::time::sleep(std::time::Duration::from_millis(100)) => {
483 assert_eq!(shutdown_counter.load(Ordering::SeqCst), 3);
484 },
485 _ = graceful.shutdown() => {
486 panic!("shutdown should not be completed: as not all our conns finish")
487 }
488 }
489 }
490}