@@ -18,8 +18,8 @@ use std::{
1818 num:: { NonZeroU32 , NonZeroUsize } ,
1919 path:: PathBuf ,
2020} ;
21- use tokio:: { io:: Interest , net:: UnixStream } ;
22- use tracing:: { debug, info} ;
21+ use tokio:: { io:: Interest , net:: UnixStream , task :: JoinError } ;
22+ use tracing:: { debug, error , info, trace } ;
2323
2424#[ derive( Debug , Deserialize , PartialEq , Eq ) ]
2525/// Configuration of this generator.
@@ -51,6 +51,9 @@ pub enum Error {
5151 /// Generic IO error
5252 #[ error( "IO error: {0}" ) ]
5353 Io ( #[ from] std:: io:: Error ) ,
54+ /// Subtask error
55+ #[ error( "Subtask failure: {0}" ) ]
56+ Subtask ( #[ from] JoinError ) ,
5457}
5558
5659#[ derive( Debug ) ]
@@ -142,62 +145,76 @@ impl Uds {
142145
143146 let send_task = tokio:: spawn ( async move {
144147 let mut blocks = self . block_cache . iter ( ) . cycle ( ) ;
148+ let mut unix_stream = Option :: < UnixStream > :: None ;
149+
145150 loop {
146- // Connect to the stream interior to the spawn allows us to
147- // re-connect within the same task, does require that we unwrap
148- // failures, catastrophically failing lading.
149- let stream = UnixStream :: connect ( & self . path )
150- . await
151- . map_err ( Error :: Io )
152- . unwrap ( ) ;
153- let blk = blocks. next ( ) . unwrap ( ) ;
154- let total_bytes = blk. total_bytes ;
155-
156- tokio:: task:: yield_now ( ) . await ;
157- self . rate_limiter
158- . until_n_ready ( total_bytes)
159- . await
160- . map_err ( Error :: Governor )
161- . unwrap ( ) ;
162-
163- // NOTE When we write into a unix stream it may be that only
164- // some of the written bytes make it through in which case we
165- // must cycle back around and try to write the remainder of the
166- // buffer.
167- let blk_max: usize = total_bytes. get ( ) as usize ;
168- let mut blk_offset = 0 ;
169- while blk_offset < blk_max {
170- let ready = stream
171- . ready ( Interest :: WRITABLE )
151+ if let Some ( stream) = & unix_stream {
152+ let blk = blocks. next ( ) . unwrap ( ) ;
153+ let total_bytes = blk. total_bytes ;
154+
155+ tokio:: task:: yield_now ( ) . await ;
156+ self . rate_limiter
157+ . until_n_ready ( total_bytes)
172158 . await
173- . map_err ( Error :: Io )
174- . unwrap ( ) ; // Cannot ? in a spawned task :<. Mimics UDP generator.
175- if ready. is_writable ( ) {
176- // Try to write data, this may still fail with `WouldBlock`
177- // if the readiness event is a false positive.
178- match stream. try_write ( & blk. bytes [ blk_offset..] ) {
179- Ok ( bytes) => {
180- counter ! ( "bytes_written" , bytes as u64 , & labels) ;
181- blk_offset = bytes;
182- }
183- Err ( ref e) if e. kind ( ) == tokio:: io:: ErrorKind :: WouldBlock => {
184- // If the read side has hung up we will never
185- // know and will keep attempting to write into
186- // the stream. This yield means we won't hog the
187- // whole CPU.
188- tokio:: task:: yield_now ( ) . await ;
189- continue ;
190- }
191- Err ( err) => {
192- debug ! ( "write failed: {}" , err) ;
159+ . map_err ( Error :: Governor )
160+ . unwrap ( ) ;
161+
162+ // NOTE When we write into a unix stream it may be that only
163+ // some of the written bytes make it through in which case we
164+ // must cycle back around and try to write the remainder of the
165+ // buffer.
166+ let blk_max: usize = total_bytes. get ( ) as usize ;
167+ let mut blk_offset = 0 ;
168+ while blk_offset < blk_max {
169+ let ready = stream
170+ . ready ( Interest :: WRITABLE )
171+ . await
172+ . map_err ( Error :: Io )
173+ . unwrap ( ) ; // Cannot ? in a spawned task :<. Mimics UDP generator.
174+ if ready. is_writable ( ) {
175+ // Try to write data, this may still fail with `WouldBlock`
176+ // if the readiness event is a false positive.
177+ match stream. try_write ( & blk. bytes [ blk_offset..] ) {
178+ Ok ( bytes) => {
179+ counter ! ( "bytes_written" , bytes as u64 , & labels) ;
180+ blk_offset = bytes;
181+ }
182+ Err ( ref e) if e. kind ( ) == tokio:: io:: ErrorKind :: WouldBlock => {
183+ // If the read side has hung up we will never
184+ // know and will keep attempting to write into
185+ // the stream. This yield means we won't hog the
186+ // whole CPU.
187+ tokio:: task:: yield_now ( ) . await ;
188+ continue ;
189+ }
190+ Err ( err) => {
191+ debug ! ( "write failed: {}" , err) ;
193192
194- let mut error_labels = labels. clone ( ) ;
195- error_labels. push ( ( "error" . to_string ( ) , err. to_string ( ) ) ) ;
196- counter ! ( "request_failure" , 1 , & error_labels) ;
197- break ; // while blk_offset
193+ let mut error_labels = labels. clone ( ) ;
194+ error_labels. push ( ( "error" . to_string ( ) , err. to_string ( ) ) ) ;
195+ counter ! ( "request_failure" , 1 , & error_labels) ;
196+ break ; // while blk_offset
197+ }
198198 }
199199 }
200200 }
201+ } else {
202+ // Connect to the stream interior to the spawn allows us to
203+ // re-connect within the same task, does require that we unwrap
204+ // failures, catastrophically failing lading.
205+ match UnixStream :: connect ( & self . path ) . await {
206+ Ok ( sock) => {
207+ debug ! ( "UDS socket opened for writing." ) ;
208+ unix_stream = Some ( sock) ;
209+ }
210+ Err ( err) => {
211+ trace ! ( "opening UDS path failed: {}" , err) ;
212+
213+ let mut error_labels = labels. clone ( ) ;
214+ error_labels. push ( ( "error" . to_string ( ) , err. to_string ( ) ) ) ;
215+ counter ! ( "connection_failure" , 1 , & error_labels) ;
216+ }
217+ }
201218 }
202219 }
203220 } ) ;
0 commit comments