1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//! Graceful shutdown management for async services.
//!
//! This module provides the [`Graceful`] type for coordinating clean shutdown
//! of async tasks in network services. It ensures all spawned tasks complete
//! before the service exits.
//!
//! # Overview
//!
//! When building network services, you often spawn multiple async tasks for
//! handling connections, background work, etc. The `Graceful` type helps you:
//!
//! - Signal all tasks to stop via cancellation tokens
//! - Track all spawned tasks to ensure they complete
//! - Coordinate shutdown across multiple components
//!
//! # Example: Basic HTTP Server with Graceful Shutdown
//!
//! ```no_run
//! use fastn_net::Graceful;
//! use tokio::net::TcpListener;
//!
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let graceful = Graceful::new();
//!
//! // Spawn a server task
//! let server_graceful = graceful.clone();
//! graceful.spawn(async move {
//! let listener = TcpListener::bind("127.0.0.1:8080").await?;
//!
//! loop {
//! tokio::select! {
//! // Accept new connections
//! Ok((stream, _)) = listener.accept() => {
//! // Handle connection in a tracked task
//! server_graceful.spawn(async move {
//! // Process the connection...
//! Ok::<(), eyre::Error>(())
//! });
//! }
//! // Stop accepting when cancelled
//! _ = server_graceful.cancelled() => {
//! println!("Server shutting down...");
//! break;
//! }
//! }
//! }
//! Ok::<(), eyre::Error>(())
//! });
//!
//! // In your main or signal handler:
//! // graceful.shutdown().await;
//! # Ok(())
//! # }
//! ```
//!
//! # Example: P2P Service with Multiple Components
//!
//! ```no_run
//! use fastn_net::{Graceful, global_iroh_endpoint};
//!
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let graceful = Graceful::new();
//! let endpoint = global_iroh_endpoint().await;
//!
//! // Component 1: Accept incoming P2P connections
//! let p2p_graceful = graceful.clone();
//! graceful.spawn(async move {
//! while let Some(conn) = endpoint.accept().await {
//! tokio::select! {
//! _ = p2p_graceful.cancelled() => {
//! break;
//! }
//! else => {
//! // Handle each connection in a tracked task
//! p2p_graceful.spawn(async move {
//! // Process P2P connection...
//! Ok::<(), eyre::Error>(())
//! });
//! }
//! }
//! }
//! Ok::<(), eyre::Error>(())
//! });
//!
//! // Component 2: HTTP API server
//! let api_graceful = graceful.clone();
//! graceful.spawn(async move {
//! // Run HTTP server with cancellation check
//! loop {
//! tokio::select! {
//! _ = api_graceful.cancelled() => {
//! break;
//! }
//! _ = tokio::time::sleep(std::time::Duration::from_millis(100)) => {
//! // Handle HTTP requests...
//! }
//! }
//! }
//! Ok::<(), eyre::Error>(())
//! });
//!
//! // Graceful shutdown on Ctrl+C
//! tokio::select! {
//! _ = tokio::signal::ctrl_c() => {
//! println!("Shutting down gracefully...");
//! graceful.shutdown().await?;
//! println!("All tasks completed");
//! }
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Best Practices
//!
//! 1. **Clone for each component**: Each async task or component should get
//! its own clone of `Graceful` to spawn sub-tasks.
//!
//! 2. **Check cancellation in loops**: Long-running loops should use
//! `select!` with `cancelled()` for proper cancellation handling.
//!
//! 3. **Use spawn() for all tasks**: Always use `graceful.spawn()` instead of
//! `tokio::spawn()` to ensure tasks are tracked.
//!
//! 4. **Handle errors**: Tasks spawned with `spawn()` should return `Result`
//! to properly propagate errors during shutdown.
//!
//! 5. **Shutdown order**: Call `shutdown()` from your main function or signal
//! handler, which will:
//! - Cancel all tasks via the cancellation token
//! - Wait for all tracked tasks to complete
//! - Return any errors from failed tasks
use Context;
use JoinHandle;
/// Manages graceful shutdown of async tasks.
///
/// Combines cancellation signaling with task tracking to ensure
/// clean shutdown of all spawned tasks. Clone this freely - all
/// clones share the same underlying state.