@@ -6,7 +6,7 @@ use std::io::{self, Read, Write};
6
6
use std:: mem;
7
7
use std:: mem:: MaybeUninit ;
8
8
use std:: os:: unix:: prelude:: * ;
9
- use std:: path:: { Path , PathBuf } ;
9
+ use std:: path:: Path ;
10
10
use std:: process:: Command ;
11
11
use std:: ptr;
12
12
use std:: sync:: {
@@ -17,17 +17,19 @@ use std::thread::{self, Builder, JoinHandle};
17
17
use std:: time:: Duration ;
18
18
19
19
#[ 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 > ,
31
33
}
32
34
33
35
#[ derive( Debug ) ]
@@ -43,7 +45,7 @@ impl Client {
43
45
// wrong!
44
46
const BUFFER : [ u8 ; 128 ] = [ b'|' ; 128 ] ;
45
47
46
- let mut write = client. write ( ) ;
48
+ let mut write = & client. write ;
47
49
48
50
set_nonblocking ( write. as_raw_fd ( ) , true ) ?;
49
51
@@ -111,16 +113,20 @@ impl Client {
111
113
FromEnvErrorInner :: CannotParse ( "expected a path after `fifo:`" . to_string ( ) )
112
114
} ) ?;
113
115
let path = Path :: new ( path_str) ;
116
+
114
117
let file = OpenOptions :: new ( )
115
118
. read ( true )
116
119
. write ( true )
117
120
. open ( path)
118
121
. map_err ( |err| FromEnvErrorInner :: CannotOpenPath ( path_str. to_string ( ) , err) ) ?;
119
122
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 ) ) ,
124
130
} ) )
125
131
}
126
132
@@ -148,6 +154,8 @@ impl Client {
148
154
return Err ( FromEnvErrorInner :: NegativeFd ( write) ) ;
149
155
}
150
156
157
+ let creation_arg = ClientCreationArg :: Fds { read, write } ;
158
+
151
159
// Ok so we've got two integers that look like file descriptors, but
152
160
// for extra sanity checking let's see if they actually look like
153
161
// valid files and instances of a pipe if feature enabled before we
@@ -174,40 +182,36 @@ impl Client {
174
182
//
175
183
// I tested this on macOS 14 and Linux 6.5.13
176
184
#[ 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
+ } ) ) ;
181
197
}
182
198
}
183
199
}
184
200
185
- Ok ( Some ( Client :: Pipe {
201
+ Ok ( Some ( Client {
186
202
read : clone_fd_and_set_cloexec ( read) ?,
187
203
write : clone_fd_and_set_cloexec ( write) ?,
204
+ creation_arg,
205
+ is_non_blocking : None ,
188
206
} ) )
189
207
}
190
208
191
209
unsafe fn from_fds ( read : c_int , write : c_int ) -> Client {
192
- Client :: Pipe {
210
+ Client {
193
211
read : File :: from_raw_fd ( read) ,
194
212
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 ,
211
215
}
212
216
}
213
217
@@ -245,7 +249,7 @@ impl Client {
245
249
// to shut us down, so we otherwise punt all errors upwards.
246
250
unsafe {
247
251
let mut fd: libc:: pollfd = mem:: zeroed ( ) ;
248
- let mut read = self . read ( ) ;
252
+ let mut read = & self . read ;
249
253
fd. fd = read. as_raw_fd ( ) ;
250
254
fd. events = libc:: POLLIN ;
251
255
loop {
@@ -284,19 +288,15 @@ impl Client {
284
288
285
289
pub fn try_acquire ( & self ) -> io:: Result < Option < Acquired > > {
286
290
let mut buf = [ 0 ] ;
291
+ let mut fifo = & self . read ;
287
292
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 ( ) ) ;
300
300
}
301
301
302
302
loop {
@@ -323,7 +323,7 @@ impl Client {
323
323
// always quickly release a token). If that turns out to not be the
324
324
// case we'll get an error anyway!
325
325
let byte = data. map ( |d| d. byte ) . unwrap_or ( b'+' ) ;
326
- match self . write ( ) . write ( & [ byte] ) ? {
326
+ match ( & self . write ) . write ( & [ byte] ) ? {
327
327
1 => Ok ( ( ) ) ,
328
328
_ => Err ( io:: Error :: new (
329
329
io:: ErrorKind :: Other ,
@@ -333,31 +333,30 @@ impl Client {
333
333
}
334
334
335
335
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 ) ,
339
339
}
340
340
}
341
341
342
342
pub fn available ( & self ) -> io:: Result < usize > {
343
343
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 ( ) ) } ) ?;
345
345
Ok ( unsafe { len. assume_init ( ) } as usize )
346
346
}
347
347
348
348
pub fn configure ( & self , cmd : & mut Command ) {
349
- match self {
349
+ if matches ! ( self . creation_arg , ClientCreationArg :: Fifo { .. } ) {
350
350
// We `File::open`ed it when inheriting from environment,
351
351
// so no need to set cloexec for fifo.
352
- Client :: Fifo { .. } => return ,
353
- Client :: Pipe { .. } => { }
354
- } ;
352
+ return ;
353
+ }
355
354
// Here we basically just want to say that in the child process
356
355
// we'll configure the read/write file descriptors to *not* be
357
356
// cloexec, so they're inherited across the exec and specified as
358
357
// 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 ( ) ;
361
360
unsafe {
362
361
cmd. pre_exec ( move || {
363
362
set_cloexec ( read, false ) ?;
0 commit comments