@@ -8,7 +8,7 @@ use std::net::{IpAddr, Shutdown, TcpStream, ToSocketAddrs};
8
8
use std:: str:: FromStr ;
9
9
use std:: sync:: {
10
10
atomic:: { AtomicU32 , Ordering } ,
11
- RwLock ,
11
+ Mutex , RwLock ,
12
12
} ;
13
13
use std:: time:: Duration ;
14
14
@@ -132,6 +132,8 @@ pub struct Client {
132
132
notif_recv : Receiver < notif:: Notification > ,
133
133
/// Active notification handles: these will be closed on Drop
134
134
notif_handles : RwLock < BTreeSet < ( AmsAddr , notif:: Handle ) > > ,
135
+ /// Mutex around IO operations
136
+ io_mutex : Mutex < ( ) > ,
135
137
/// If we opened our local port with the router
136
138
source_port_opened : bool ,
137
139
}
@@ -140,7 +142,8 @@ impl Drop for Client {
140
142
fn drop ( & mut self ) {
141
143
// the notif_handles lock should only be poisioned in panics coming
142
144
// from std code, so a panic is probably acceptable here.
143
- let handles = self . notif_handles
145
+ let handles = self
146
+ . notif_handles
144
147
. get_mut ( )
145
148
. expect ( "notification handle cache lock was poisoned" ) ;
146
149
@@ -264,6 +267,7 @@ impl Client {
264
267
read_timeout : timeouts. read ,
265
268
notif_handles : RwLock :: default ( ) ,
266
269
source_port_opened,
270
+ io_mutex : Mutex :: new ( ( ) ) ,
267
271
} )
268
272
}
269
273
@@ -336,21 +340,26 @@ impl Client {
336
340
for buf in data_in {
337
341
request. extend_from_slice ( buf) ;
338
342
}
339
- // &T impls Write for T: Write, so no &mut self required.
340
- ( & self . socket ) . write_all ( & request) . ctx ( "sending request" ) ?;
341
-
342
- // Get a reply from the reader thread, with timeout or not.
343
- let reply = if let Some ( tmo) = self . read_timeout {
344
- self . reply_recv
345
- . recv_timeout ( tmo)
346
- . map_err ( |_| io:: ErrorKind :: TimedOut . into ( ) )
347
- . ctx ( "receiving reply (route set?)" ) ?
348
- } else {
349
- self . reply_recv
350
- . recv ( )
351
- . map_err ( |_| io:: ErrorKind :: UnexpectedEof . into ( ) )
352
- . ctx ( "receiving reply (route set?)" ) ?
353
- } ?;
343
+
344
+ let reply = {
345
+ let _io_guard = self . io_mutex . lock ( ) . expect ( "attempted to read after an io panic" ) ;
346
+
347
+ // &T impls Write for T: Write, so no &mut self required.
348
+ ( & self . socket ) . write_all ( & request) . ctx ( "sending request" ) ?;
349
+
350
+ // Get a reply from the reader thread, with timeout or not.
351
+ if let Some ( tmo) = self . read_timeout {
352
+ self . reply_recv
353
+ . recv_timeout ( tmo)
354
+ . map_err ( |_| io:: ErrorKind :: TimedOut . into ( ) )
355
+ . ctx ( "receiving reply (route set?)" ) ?
356
+ } else {
357
+ self . reply_recv
358
+ . recv ( )
359
+ . map_err ( |_| io:: ErrorKind :: UnexpectedEof . into ( ) )
360
+ . ctx ( "receiving reply (route set?)" ) ?
361
+ } ?
362
+ } ;
354
363
355
364
// Validate the incoming reply. The reader thread already made sure that
356
365
// it is consistent and addressed to us.
0 commit comments