@@ -6,7 +6,7 @@ use std::io::{self, Read, Write};
66use std:: mem;
77use std:: mem:: MaybeUninit ;
88use std:: os:: unix:: prelude:: * ;
9- use std:: path:: { Path , PathBuf } ;
9+ use std:: path:: Path ;
1010use std:: process:: Command ;
1111use std:: ptr;
1212use std:: sync:: {
@@ -17,17 +17,19 @@ use std::thread::{self, Builder, JoinHandle};
1717use std:: time:: Duration ;
1818
1919#[ derive( Debug ) ]
20- pub enum Client {
21- /// `--jobserver-auth=R,W`
22- Pipe { read : File , write : File } ,
23- /// `--jobserver-auth=fifo:PATH`
24- Fifo {
25- file : File ,
26- path : PathBuf ,
27- /// it can only go from false -> true but not the other way around, since that
28- /// could cause a race condition.
29- is_non_blocking : AtomicBool ,
30- } ,
20+ enum ClientCreationArg {
21+ Fds { read : c_int , write : c_int } ,
22+ Fifo ( Box < Path > ) ,
23+ }
24+
25+ #[ derive( Debug ) ]
26+ pub struct Client {
27+ read : File ,
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
31+ /// could cause a race condition.
32+ is_non_blocking : Option < AtomicBool > ,
3133}
3234
3335#[ derive( Debug ) ]
@@ -43,7 +45,7 @@ impl Client {
4345 // wrong!
4446 const BUFFER : [ u8 ; 128 ] = [ b'|' ; 128 ] ;
4547
46- let mut write = client. write ( ) ;
48+ let mut write = & client. write ;
4749
4850 set_nonblocking ( write. as_raw_fd ( ) , true ) ?;
4951
@@ -111,16 +113,20 @@ impl Client {
111113 FromEnvErrorInner :: CannotParse ( "expected a path after `fifo:`" . to_string ( ) )
112114 } ) ?;
113115 let path = Path :: new ( path_str) ;
116+
114117 let file = OpenOptions :: new ( )
115118 . read ( true )
116119 . write ( true )
117120 . open ( path)
118121 . map_err ( |err| FromEnvErrorInner :: CannotOpenPath ( path_str. to_string ( ) , err) ) ?;
119122
120- Ok ( Some ( Client :: Fifo {
121- file,
122- path : path. into ( ) ,
123- is_non_blocking : AtomicBool :: new ( false ) ,
123+ Ok ( Some ( Client {
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,40 +182,36 @@ 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 ( jobserver) ) =
178- Self :: from_fifo ( & format ! ( "fifo:/dev/fd/{}" , read. as_raw_fd( ) ) )
179- {
180- 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+ } ) ) ;
181197 }
182198 }
183199 }
184200
185- Ok ( Some ( Client :: Pipe {
201+ Ok ( Some ( Client {
186202 read : clone_fd_and_set_cloexec ( read) ?,
187203 write : clone_fd_and_set_cloexec ( write) ?,
204+ creation_arg,
205+ is_non_blocking : None ,
188206 } ) )
189207 }
190208
191209 unsafe fn from_fds ( read : c_int , write : c_int ) -> Client {
192- Client :: Pipe {
210+ Client {
193211 read : File :: from_raw_fd ( read) ,
194212 write : File :: from_raw_fd ( write) ,
195- }
196- }
197-
198- /// Gets the read end of our jobserver client.
199- fn read ( & self ) -> & File {
200- match self {
201- Client :: Pipe { read, .. } => read,
202- Client :: Fifo { file, .. } => file,
203- }
204- }
205-
206- /// Gets the write end of our jobserver client.
207- fn write ( & self ) -> & File {
208- match self {
209- Client :: Pipe { write, .. } => write,
210- Client :: Fifo { file, .. } => file,
213+ creation_arg : ClientCreationArg :: Fds { read, write } ,
214+ is_non_blocking : None ,
211215 }
212216 }
213217
@@ -245,7 +249,7 @@ impl Client {
245249 // to shut us down, so we otherwise punt all errors upwards.
246250 unsafe {
247251 let mut fd: libc:: pollfd = mem:: zeroed ( ) ;
248- let mut read = self . read ( ) ;
252+ let mut read = & self . read ;
249253 fd. fd = read. as_raw_fd ( ) ;
250254 fd. events = libc:: POLLIN ;
251255 loop {
@@ -284,19 +288,15 @@ impl Client {
284288
285289 pub fn try_acquire ( & self ) -> io:: Result < Option < Acquired > > {
286290 let mut buf = [ 0 ] ;
291+ let mut fifo = & self . read ;
287292
288- let ( mut fifo, is_non_blocking) = match self {
289- Self :: Fifo {
290- file,
291- is_non_blocking,
292- ..
293- } => ( file, is_non_blocking) ,
294- _ => return Err ( io:: ErrorKind :: Unsupported . into ( ) ) ,
295- } ;
296-
297- if !is_non_blocking. load ( Ordering :: Relaxed ) {
298- set_nonblocking ( fifo. as_raw_fd ( ) , true ) ?;
299- is_non_blocking. store ( true , Ordering :: Relaxed ) ;
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 {
299+ return Err ( io:: ErrorKind :: Unsupported . into ( ) ) ;
300300 }
301301
302302 loop {
@@ -323,7 +323,7 @@ impl Client {
323323 // always quickly release a token). If that turns out to not be the
324324 // case we'll get an error anyway!
325325 let byte = data. map ( |d| d. byte ) . unwrap_or ( b'+' ) ;
326- match self . write ( ) . write ( & [ byte] ) ? {
326+ match ( & self . write ) . write ( & [ byte] ) ? {
327327 1 => Ok ( ( ) ) ,
328328 _ => Err ( io:: Error :: new (
329329 io:: ErrorKind :: Other ,
@@ -333,31 +333,30 @@ impl Client {
333333 }
334334
335335 pub fn string_arg ( & self ) -> String {
336- match self {
337- Client :: Pipe { read , write } => format ! ( "{},{} " , read . as_raw_fd ( ) , write . as_raw_fd ( ) ) ,
338- Client :: Fifo { path , .. } => format ! ( "fifo:{} " , path . to_str ( ) . unwrap ( ) ) ,
336+ match & self . creation_arg {
337+ ClientCreationArg :: Fifo ( path ) => format ! ( "fifo:{} " , path . display ( ) ) ,
338+ ClientCreationArg :: Fds { read , write } => format ! ( "{},{} " , read , write ) ,
339339 }
340340 }
341341
342342 pub fn available ( & self ) -> io:: Result < usize > {
343343 let mut len = MaybeUninit :: < c_int > :: uninit ( ) ;
344- 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 ( ) ) } ) ?;
345345 Ok ( unsafe { len. assume_init ( ) } as usize )
346346 }
347347
348348 pub fn configure ( & self , cmd : & mut Command ) {
349- match self {
349+ if matches ! ( self . creation_arg , ClientCreationArg :: Fifo { .. } ) {
350350 // We `File::open`ed it when inheriting from environment,
351351 // so no need to set cloexec for fifo.
352- Client :: Fifo { .. } => return ,
353- Client :: Pipe { .. } => { }
354- } ;
352+ return ;
353+ }
355354 // Here we basically just want to say that in the child process
356355 // we'll configure the read/write file descriptors to *not* be
357356 // cloexec, so they're inherited across the exec and specified as
358357 // integers through `string_arg` above.
359- let read = self . read ( ) . as_raw_fd ( ) ;
360- let write = self . write ( ) . as_raw_fd ( ) ;
358+ let read = self . read . as_raw_fd ( ) ;
359+ let write = self . write . as_raw_fd ( ) ;
361360 unsafe {
362361 cmd. pre_exec ( move || {
363362 set_cloexec ( read, false ) ?;
0 commit comments