@@ -16,16 +16,20 @@ use std::sync::{
1616use std:: thread:: { self , Builder , JoinHandle } ;
1717use std:: time:: Duration ;
1818
19+ #[ derive( Debug ) ]
20+ enum ClientCreationArg {
21+ Fds { read : c_int , write : c_int } ,
22+ Fifo ( Box < Path > ) ,
23+ }
24+
1925#[ derive( Debug ) ]
2026pub struct Client {
2127 read : File ,
22- write : Option < File > ,
23- /// If path is not None, then a fifo jobserver is created.
24- path : Option < Box < Path > > ,
25- supports_non_blocking : bool ,
26- /// it can only go from false -> true but not the other way around, since that
28+ write : File ,
29+ creation_arg : ClientCreationArg ,
30+ /// it can only go from Some(false) -> Some(true) but not the other way around, since that
2731 /// could cause a race condition.
28- is_non_blocking : AtomicBool ,
32+ is_non_blocking : Option < AtomicBool > ,
2933}
3034
3135#[ derive( Debug ) ]
@@ -41,7 +45,7 @@ impl Client {
4145 // wrong!
4246 const BUFFER : [ u8 ; 128 ] = [ b'|' ; 128 ] ;
4347
44- let mut write = client. write ( ) ;
48+ let mut write = & client. write ;
4549
4650 set_nonblocking ( write. as_raw_fd ( ) , true ) ?;
4751
@@ -109,18 +113,20 @@ impl Client {
109113 FromEnvErrorInner :: CannotParse ( "expected a path after `fifo:`" . to_string ( ) )
110114 } ) ?;
111115 let path = Path :: new ( path_str) ;
116+
112117 let file = OpenOptions :: new ( )
113118 . read ( true )
114119 . write ( true )
115120 . open ( path)
116121 . map_err ( |err| FromEnvErrorInner :: CannotOpenPath ( path_str. to_string ( ) , err) ) ?;
117122
118123 Ok ( Some ( Client {
119- read : file,
120- write : None ,
121- path : Some ( path. into ( ) ) ,
122- supports_non_blocking : true ,
123- is_non_blocking : AtomicBool :: new ( false ) ,
124+ read : file
125+ . try_clone ( )
126+ . map_err ( |err| FromEnvErrorInner :: CannotClone ( file. as_raw_fd ( ) , err) ) ?,
127+ write : file,
128+ creation_arg : ClientCreationArg :: Fifo ( path. into ( ) ) ,
129+ is_non_blocking : Some ( AtomicBool :: new ( false ) ) ,
124130 } ) )
125131 }
126132
@@ -148,6 +154,8 @@ impl Client {
148154 return Err ( FromEnvErrorInner :: NegativeFd ( write) ) ;
149155 }
150156
157+ let creation_arg = ClientCreationArg :: Fds { read, write } ;
158+
151159 // Ok so we've got two integers that look like file descriptors, but
152160 // for extra sanity checking let's see if they actually look like
153161 // valid files and instances of a pipe if feature enabled before we
@@ -174,44 +182,39 @@ impl Client {
174182 //
175183 // I tested this on macOS 14 and Linux 6.5.13
176184 #[ cfg( target_os = "linux" ) ]
177- if let Ok ( Some ( mut jobserver) ) =
178- Self :: from_fifo ( & format ! ( "fifo:/dev/fd/{}" , read. as_raw_fd( ) ) )
179- {
180- jobserver. path . take ( ) ;
181- return Ok ( Some ( jobserver) ) ;
185+ if let ( Ok ( read) , Ok ( write) ) = (
186+ File :: open ( format ! ( "/dev/fd/{}" , read) ) ,
187+ OpenOptions :: new ( )
188+ . write ( true )
189+ . open ( format ! ( "/dev/fd/{}" , write) ) ,
190+ ) {
191+ return Ok ( Some ( Client {
192+ read,
193+ write,
194+ creation_arg,
195+ is_non_blocking : Some ( AtomicBool :: new ( false ) ) ,
196+ } ) ) ;
182197 }
183198 }
184199 }
185200
186201 Ok ( Some ( Client {
187202 read : clone_fd_and_set_cloexec ( read) ?,
188- write : Some ( clone_fd_and_set_cloexec ( write) ?) ,
189- path : None ,
190- supports_non_blocking : false ,
191- is_non_blocking : AtomicBool :: new ( false ) ,
203+ write : clone_fd_and_set_cloexec ( write) ?,
204+ creation_arg,
205+ is_non_blocking : None ,
192206 } ) )
193207 }
194208
195209 unsafe fn from_fds ( read : c_int , write : c_int ) -> Client {
196210 Client {
197211 read : File :: from_raw_fd ( read) ,
198- write : Some ( File :: from_raw_fd ( write) ) ,
199- path : None ,
200- supports_non_blocking : false ,
201- is_non_blocking : AtomicBool :: new ( false ) ,
212+ write : File :: from_raw_fd ( write) ,
213+ creation_arg : ClientCreationArg :: Fds { read, write } ,
214+ is_non_blocking : None ,
202215 }
203216 }
204217
205- /// Gets the read end of our jobserver client.
206- fn read ( & self ) -> & File {
207- & self . read
208- }
209-
210- /// Gets the write end of our jobserver client.
211- fn write ( & self ) -> & File {
212- self . write . as_ref ( ) . unwrap_or ( & self . read )
213- }
214-
215218 pub fn acquire ( & self ) -> io:: Result < Acquired > {
216219 // Ignore interrupts and keep trying if that happens
217220 loop {
@@ -246,7 +249,7 @@ impl Client {
246249 // to shut us down, so we otherwise punt all errors upwards.
247250 unsafe {
248251 let mut fd: libc:: pollfd = mem:: zeroed ( ) ;
249- let mut read = self . read ( ) ;
252+ let mut read = & self . read ;
250253 fd. fd = read. as_raw_fd ( ) ;
251254 fd. events = libc:: POLLIN ;
252255 loop {
@@ -285,18 +288,17 @@ impl Client {
285288
286289 pub fn try_acquire ( & self ) -> io:: Result < Option < Acquired > > {
287290 let mut buf = [ 0 ] ;
291+ let mut fifo = & self . read ;
288292
289- if !self . supports_non_blocking {
293+ if let Some ( is_non_blocking) = self . is_non_blocking . as_ref ( ) {
294+ if !is_non_blocking. load ( Ordering :: Relaxed ) {
295+ set_nonblocking ( fifo. as_raw_fd ( ) , true ) ?;
296+ is_non_blocking. store ( true , Ordering :: Relaxed ) ;
297+ }
298+ } else {
290299 return Err ( io:: ErrorKind :: Unsupported . into ( ) ) ;
291300 }
292301
293- let mut fifo = self . read ( ) ;
294-
295- if !self . is_non_blocking . load ( Ordering :: Relaxed ) {
296- set_nonblocking ( fifo. as_raw_fd ( ) , true ) ?;
297- self . is_non_blocking . store ( true , Ordering :: Relaxed ) ;
298- }
299-
300302 loop {
301303 match fifo. read ( & mut buf) {
302304 Ok ( 1 ) => break Ok ( Some ( Acquired { byte : buf[ 0 ] } ) ) ,
@@ -321,7 +323,7 @@ impl Client {
321323 // always quickly release a token). If that turns out to not be the
322324 // case we'll get an error anyway!
323325 let byte = data. map ( |d| d. byte ) . unwrap_or ( b'+' ) ;
324- match self . write ( ) . write ( & [ byte] ) ? {
326+ match ( & self . write ) . write ( & [ byte] ) ? {
325327 1 => Ok ( ( ) ) ,
326328 _ => Err ( io:: Error :: new (
327329 io:: ErrorKind :: Other ,
@@ -331,20 +333,20 @@ impl Client {
331333 }
332334
333335 pub fn string_arg ( & self ) -> String {
334- self . path
335- . as_deref ( )
336- . map ( |path| format ! ( "fifo:{} " , path . display ( ) ) )
337- . unwrap_or_else ( || format ! ( "{},{}" , self . read ( ) . as_raw_fd ( ) , self . write ( ) . as_raw_fd ( ) ) )
336+ match & self . creation_arg {
337+ ClientCreationArg :: Fifo ( path ) => format ! ( "fifo:{}" , path . display ( ) ) ,
338+ ClientCreationArg :: Fds { read , write } => format ! ( "{},{} " , read , write ) ,
339+ }
338340 }
339341
340342 pub fn available ( & self ) -> io:: Result < usize > {
341343 let mut len = MaybeUninit :: < c_int > :: uninit ( ) ;
342- cvt ( unsafe { libc:: ioctl ( self . read ( ) . as_raw_fd ( ) , libc:: FIONREAD , len. as_mut_ptr ( ) ) } ) ?;
344+ cvt ( unsafe { libc:: ioctl ( self . read . as_raw_fd ( ) , libc:: FIONREAD , len. as_mut_ptr ( ) ) } ) ?;
343345 Ok ( unsafe { len. assume_init ( ) } as usize )
344346 }
345347
346348 pub fn configure ( & self , cmd : & mut Command ) {
347- if self . path . is_some ( ) {
349+ if matches ! ( self . creation_arg , ClientCreationArg :: Fifo { .. } ) {
348350 // We `File::open`ed it when inheriting from environment,
349351 // so no need to set cloexec for fifo.
350352 return ;
@@ -353,8 +355,8 @@ impl Client {
353355 // we'll configure the read/write file descriptors to *not* be
354356 // cloexec, so they're inherited across the exec and specified as
355357 // integers through `string_arg` above.
356- let read = self . read ( ) . as_raw_fd ( ) ;
357- let write = self . write ( ) . as_raw_fd ( ) ;
358+ let read = self . read . as_raw_fd ( ) ;
359+ let write = self . write . as_raw_fd ( ) ;
358360 unsafe {
359361 cmd. pre_exec ( move || {
360362 set_cloexec ( read, false ) ?;
0 commit comments