@@ -26,7 +26,7 @@ use tokio::{
26
26
mpsc:: { self , error:: SendError as TokioSendError } ,
27
27
oneshot,
28
28
} ,
29
- task:: JoinError ,
29
+ task:: JoinSet ,
30
30
} ;
31
31
use tokio_util:: time:: FutureExt as TimeFutureExt ;
32
32
use tracing:: { debug, error, trace} ;
@@ -86,7 +86,8 @@ struct Context {
86
86
///
87
87
/// This includes the normal iroh connection errors as well as pool specific
88
88
/// errors such as timeouts and connection limits.
89
- #[ derive( Debug , Clone ) ]
89
+ #[ derive( Debug , Clone , Snafu ) ]
90
+ #[ snafu( module) ]
90
91
pub enum PoolConnectError {
91
92
/// Connection pool is shut down
92
93
Shutdown ,
@@ -95,23 +96,27 @@ pub enum PoolConnectError {
95
96
/// Too many connections
96
97
TooManyConnections ,
97
98
/// Error during connect
98
- ConnectError ( Arc < ConnectError > ) ,
99
- /// Handler actor panicked
100
- JoinError ( Arc < JoinError > ) ,
99
+ ConnectError { source : Arc < ConnectError > } ,
101
100
}
102
101
103
- impl std:: fmt:: Display for PoolConnectError {
104
- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
105
- match self {
106
- PoolConnectError :: Shutdown => write ! ( f, "Connection pool is shut down" ) ,
107
- PoolConnectError :: Timeout => write ! ( f, "Connection timed out" ) ,
108
- PoolConnectError :: TooManyConnections => write ! ( f, "Too many connections" ) ,
109
- PoolConnectError :: ConnectError ( e) => write ! ( f, "Connection error: {e}" ) ,
110
- PoolConnectError :: JoinError ( e) => write ! ( f, "Join error: {e}" ) ,
102
+ impl From < ConnectError > for PoolConnectError {
103
+ fn from ( e : ConnectError ) -> Self {
104
+ PoolConnectError :: ConnectError {
105
+ source : Arc :: new ( e) ,
111
106
}
112
107
}
113
108
}
114
109
110
+ /// Error when calling a fn on the [`ConnectionPool`].
111
+ ///
112
+ /// The only thing that can go wrong is that the connection pool is shut down.
113
+ #[ derive( Debug , Snafu ) ]
114
+ #[ snafu( module) ]
115
+ pub enum ConnectionPoolError {
116
+ /// The connection pool has been shut down
117
+ Shutdown ,
118
+ }
119
+
115
120
pub type PoolConnectResult = std:: result:: Result < Connection , PoolConnectError > ;
116
121
117
122
enum ActorMessage {
@@ -125,7 +130,7 @@ struct RequestRef {
125
130
tx : oneshot:: Sender < Result < ConnectionRef , PoolConnectError > > ,
126
131
}
127
132
128
- /// Run a connection actor for a single node
133
+ /// Run a connection actor for a single remote node id
129
134
async fn run_connection_actor (
130
135
node_id : NodeId ,
131
136
mut rx : mpsc:: Receiver < RequestRef > ,
@@ -139,7 +144,7 @@ async fn run_connection_actor(
139
144
. await
140
145
{
141
146
Ok ( Ok ( conn) ) => Ok ( conn) ,
142
- Ok ( Err ( e) ) => Err ( PoolConnectError :: ConnectError ( Arc :: new ( e ) ) ) ,
147
+ Ok ( Err ( e) ) => Err ( PoolConnectError :: from ( e ) ) ,
143
148
Err ( _) => Err ( PoolConnectError :: Timeout ) ,
144
149
} ;
145
150
if let Err ( e) = & state {
@@ -221,6 +226,8 @@ struct Actor {
221
226
// idle set (most recent last)
222
227
// todo: use a better data structure if this becomes a performance issue
223
228
idle : VecDeque < NodeId > ,
229
+ // per connection tasks
230
+ tasks : JoinSet < ( ) > ,
224
231
}
225
232
226
233
impl Actor {
@@ -241,6 +248,7 @@ impl Actor {
241
248
endpoint,
242
249
owner : ConnectionPool { tx : tx. clone ( ) } ,
243
250
} ) ,
251
+ tasks : JoinSet :: new ( ) ,
244
252
} ,
245
253
tx,
246
254
)
@@ -264,70 +272,84 @@ impl Actor {
264
272
self . remove_idle ( id) ;
265
273
}
266
274
267
- pub async fn run ( mut self ) {
268
- while let Some ( msg) = self . rx . recv ( ) . await {
269
- match msg {
270
- ActorMessage :: RequestRef ( mut msg) => {
271
- let id = msg. id ;
272
- self . remove_idle ( id) ;
273
- // Try to send to existing connection actor
274
- if let Some ( conn_tx) = self . connections . get ( & id) {
275
- if let Err ( TokioSendError ( e) ) = conn_tx. send ( msg) . await {
276
- msg = e;
277
- } else {
278
- continue ;
279
- }
280
- // Connection actor died, remove it
281
- self . remove_connection ( id) ;
275
+ async fn handle_msg ( & mut self , msg : ActorMessage ) {
276
+ match msg {
277
+ ActorMessage :: RequestRef ( mut msg) => {
278
+ let id = msg. id ;
279
+ self . remove_idle ( id) ;
280
+ // Try to send to existing connection actor
281
+ if let Some ( conn_tx) = self . connections . get ( & id) {
282
+ if let Err ( TokioSendError ( e) ) = conn_tx. send ( msg) . await {
283
+ msg = e;
284
+ } else {
285
+ return ;
282
286
}
287
+ // Connection actor died, remove it
288
+ self . remove_connection ( id) ;
289
+ }
283
290
284
- // No connection actor or it died - check limits
285
- if self . connections . len ( ) >= self . context . options . max_connections {
286
- if let Some ( idle) = self . pop_oldest_idle ( ) {
287
- // remove the oldest idle connection to make room for one more
288
- trace ! ( "removing oldest idle connection {}" , idle) ;
289
- self . connections . remove ( & idle) ;
290
- } else {
291
- msg. tx . send ( Err ( PoolConnectError :: TooManyConnections ) ) . ok ( ) ;
292
- continue ;
293
- }
291
+ // No connection actor or it died - check limits
292
+ if self . connections . len ( ) >= self . context . options . max_connections {
293
+ if let Some ( idle) = self . pop_oldest_idle ( ) {
294
+ // remove the oldest idle connection to make room for one more
295
+ trace ! ( "removing oldest idle connection {}" , idle) ;
296
+ self . connections . remove ( & idle) ;
297
+ } else {
298
+ msg. tx . send ( Err ( PoolConnectError :: TooManyConnections ) ) . ok ( ) ;
299
+ return ;
294
300
}
295
- let ( conn_tx, conn_rx) = mpsc:: channel ( 100 ) ;
296
- self . connections . insert ( id, conn_tx. clone ( ) ) ;
301
+ }
302
+ let ( conn_tx, conn_rx) = mpsc:: channel ( 100 ) ;
303
+ self . connections . insert ( id, conn_tx. clone ( ) ) ;
297
304
298
- let context = self . context . clone ( ) ;
305
+ let context = self . context . clone ( ) ;
299
306
300
- tokio :: spawn ( run_connection_actor ( id, conn_rx, context) ) ;
307
+ self . tasks . spawn ( run_connection_actor ( id, conn_rx, context) ) ;
301
308
302
- // Send the handler to the new actor
303
- if conn_tx. send ( msg) . await . is_err ( ) {
304
- error ! ( %id, "Failed to send handler to new connection actor" ) ;
305
- self . connections . remove ( & id) ;
306
- }
309
+ // Send the handler to the new actor
310
+ if conn_tx. send ( msg) . await . is_err ( ) {
311
+ error ! ( %id, "Failed to send handler to new connection actor" ) ;
312
+ self . connections . remove ( & id) ;
307
313
}
308
- ActorMessage :: ConnectionIdle { id } => {
309
- self . add_idle ( id) ;
310
- trace ! ( %id, "connection idle" ) ;
314
+ }
315
+ ActorMessage :: ConnectionIdle { id } => {
316
+ self . add_idle ( id) ;
317
+ trace ! ( %id, "connection idle" ) ;
318
+ }
319
+ ActorMessage :: ConnectionShutdown { id } => {
320
+ // Remove the connection from our map - this closes the channel
321
+ self . remove_connection ( id) ;
322
+ trace ! ( %id, "removed connection" ) ;
323
+ }
324
+ }
325
+ }
326
+
327
+ pub async fn run ( mut self ) {
328
+ loop {
329
+ tokio:: select! {
330
+ biased;
331
+
332
+ msg = self . rx. recv( ) => {
333
+ if let Some ( msg) = msg {
334
+ self . handle_msg( msg) . await ;
335
+ } else {
336
+ break ;
337
+ }
311
338
}
312
- ActorMessage :: ConnectionShutdown { id } => {
313
- // Remove the connection from our map - this closes the channel
314
- self . remove_connection ( id) ;
315
- trace ! ( %id, "removed connection" ) ;
339
+
340
+ res = self . tasks. join_next( ) , if !self . tasks. is_empty( ) => {
341
+ if let Some ( Err ( e) ) = res {
342
+ // panic during either connection establishment or
343
+ // timeout. Message handling is outside the actor's
344
+ // control, so we should hopefully never get this.
345
+ error!( "Connection actor failed: {e}" ) ;
346
+ }
316
347
}
317
348
}
318
349
}
319
350
}
320
351
}
321
352
322
- /// Error when calling a fn on the [`ConnectionPool`].
323
- ///
324
- /// The only thing that can go wrong is that the connection pool is shut down.
325
- #[ derive( Debug , Snafu ) ]
326
- pub enum ConnectionPoolError {
327
- /// The connection pool has been shut down
328
- Shutdown ,
329
- }
330
-
331
353
/// A connection pool
332
354
#[ derive( Debug , Clone ) ]
333
355
pub struct ConnectionPool {
0 commit comments