@@ -36,6 +36,8 @@ extern char** environ;
36
36
37
37
using namespace std ;
38
38
39
+ static ExitStatus ParseExitStatus (int status);
40
+
39
41
Subprocess::Subprocess (bool use_console) : fd_(-1 ), pid_(-1 ),
40
42
use_console_(use_console) {
41
43
}
@@ -49,26 +51,34 @@ Subprocess::~Subprocess() {
49
51
}
50
52
51
53
bool Subprocess::Start (SubprocessSet* set, const string& command) {
52
- int output_pipe[2 ];
53
- if (pipe (output_pipe) < 0 )
54
- Fatal (" pipe: %s" , strerror (errno));
55
- fd_ = output_pipe[0 ];
54
+ int subproc_stdout_fd = -1 ;
55
+ if (use_console_) {
56
+ fd_ = -1 ;
57
+ } else {
58
+ int output_pipe[2 ];
59
+ if (pipe (output_pipe) < 0 )
60
+ Fatal (" pipe: %s" , strerror (errno));
61
+ fd_ = output_pipe[0 ];
62
+ subproc_stdout_fd = output_pipe[1 ];
56
63
#if !defined(USE_PPOLL)
57
- // If available, we use ppoll in DoWork(); otherwise we use pselect
58
- // and so must avoid overly-large FDs.
59
- if (fd_ >= static_cast <int >(FD_SETSIZE))
60
- Fatal (" pipe: %s" , strerror (EMFILE));
64
+ // If available, we use ppoll in DoWork(); otherwise we use pselect
65
+ // and so must avoid overly-large FDs.
66
+ if (fd_ >= static_cast <int >(FD_SETSIZE))
67
+ Fatal (" pipe: %s" , strerror (EMFILE));
61
68
#endif // !USE_PPOLL
62
- SetCloseOnExec (fd_);
69
+ SetCloseOnExec (fd_);
70
+ }
63
71
64
72
posix_spawn_file_actions_t action;
65
73
int err = posix_spawn_file_actions_init (&action);
66
74
if (err != 0 )
67
75
Fatal (" posix_spawn_file_actions_init: %s" , strerror (err));
68
76
69
- err = posix_spawn_file_actions_addclose (&action, output_pipe[0 ]);
70
- if (err != 0 )
71
- Fatal (" posix_spawn_file_actions_addclose: %s" , strerror (err));
77
+ if (!use_console_) {
78
+ err = posix_spawn_file_actions_addclose (&action, fd_);
79
+ if (err != 0 )
80
+ Fatal (" posix_spawn_file_actions_addclose: %s" , strerror (err));
81
+ }
72
82
73
83
posix_spawnattr_t attr;
74
84
err = posix_spawnattr_init (&attr);
@@ -97,18 +107,17 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) {
97
107
Fatal (" posix_spawn_file_actions_addopen: %s" , strerror (err));
98
108
}
99
109
100
- err = posix_spawn_file_actions_adddup2 (&action, output_pipe[ 1 ] , 1 );
110
+ err = posix_spawn_file_actions_adddup2 (&action, subproc_stdout_fd , 1 );
101
111
if (err != 0 )
102
112
Fatal (" posix_spawn_file_actions_adddup2: %s" , strerror (err));
103
- err = posix_spawn_file_actions_adddup2 (&action, output_pipe[ 1 ] , 2 );
113
+ err = posix_spawn_file_actions_adddup2 (&action, subproc_stdout_fd , 2 );
104
114
if (err != 0 )
105
115
Fatal (" posix_spawn_file_actions_adddup2: %s" , strerror (err));
106
- err = posix_spawn_file_actions_addclose (&action, output_pipe[ 1 ] );
116
+ err = posix_spawn_file_actions_addclose (&action, subproc_stdout_fd );
107
117
if (err != 0 )
108
118
Fatal (" posix_spawn_file_actions_addclose: %s" , strerror (err));
109
- // In the console case, output_pipe is still inherited by the child and
110
- // closed when the subprocess finishes, which then notifies ninja.
111
119
}
120
+
112
121
#ifdef POSIX_SPAWN_USEVFORK
113
122
flags |= POSIX_SPAWN_USEVFORK;
114
123
#endif
@@ -130,7 +139,8 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) {
130
139
if (err != 0 )
131
140
Fatal (" posix_spawn_file_actions_destroy: %s" , strerror (err));
132
141
133
- close (output_pipe[1 ]);
142
+ if (!use_console_)
143
+ close (subproc_stdout_fd);
134
144
return true ;
135
145
}
136
146
@@ -147,13 +157,30 @@ void Subprocess::OnPipeReady() {
147
157
}
148
158
}
149
159
150
- ExitStatus Subprocess::Finish () {
160
+
161
+ bool Subprocess::TryFinish (int waitpid_options) {
151
162
assert (pid_ != -1 );
152
- int status;
153
- if (waitpid (pid_, &status, 0 ) < 0 )
154
- Fatal (" waitpid(%d): %s" , pid_, strerror (errno));
163
+ int status, ret;
164
+ while ((ret = waitpid (pid_, &status, waitpid_options)) < 0 ) {
165
+ if (errno != EINTR)
166
+ Fatal (" waitpid(%d): %s" , pid_, strerror (errno));
167
+ }
168
+ if (ret == 0 )
169
+ return false ; // Subprocess is alive (WNOHANG-only).
155
170
pid_ = -1 ;
171
+ exit_status_ = ParseExitStatus (status);
172
+ return true ; // Subprocess has terminated.
173
+ }
174
+
175
+ ExitStatus Subprocess::Finish () {
176
+ if (pid_ != -1 ) {
177
+ TryFinish (0 );
178
+ assert (pid_ == -1 );
179
+ }
180
+ return exit_status_;
181
+ }
156
182
183
+ static ExitStatus ParseExitStatus (int status) {
157
184
#ifdef _AIX
158
185
if (WIFEXITED (status) && WEXITSTATUS (status) & 0x80 ) {
159
186
// Map the shell's exit code used for signal failure (128 + signal) to the
@@ -177,19 +204,28 @@ ExitStatus Subprocess::Finish() {
177
204
}
178
205
179
206
bool Subprocess::Done () const {
180
- return fd_ == -1 ;
207
+ // Console subprocesses share console with ninja, and we consider them done
208
+ // when they exit.
209
+ // For other processes, we consider them done when we have consumed all their
210
+ // output and closed their associated pipe.
211
+ return (use_console_ && pid_ == -1 ) || (!use_console_ && fd_ == -1 );
181
212
}
182
213
183
214
const string& Subprocess::GetOutput () const {
184
215
return buf_;
185
216
}
186
217
187
- int SubprocessSet::interrupted_;
218
+ volatile int SubprocessSet::interrupted_;
219
+ volatile sig_atomic_t SubprocessSet::s_sigchld_received;
188
220
189
221
void SubprocessSet::SetInterruptedFlag (int signum) {
190
222
interrupted_ = signum;
191
223
}
192
224
225
+ void SubprocessSet::SigChldHandler (int signo, siginfo_t * info, void * context) {
226
+ s_sigchld_received = 1 ;
227
+ }
228
+
193
229
void SubprocessSet::HandlePendingInterruption () {
194
230
sigset_t pending;
195
231
sigemptyset (&pending);
@@ -206,11 +242,14 @@ void SubprocessSet::HandlePendingInterruption() {
206
242
}
207
243
208
244
SubprocessSet::SubprocessSet () {
245
+ // Block all these signals.
246
+ // Their handlers will only be enabled during ppoll/pselect().
209
247
sigset_t set;
210
248
sigemptyset (&set);
211
249
sigaddset (&set, SIGINT);
212
250
sigaddset (&set, SIGTERM);
213
251
sigaddset (&set, SIGHUP);
252
+ sigaddset (&set, SIGCHLD);
214
253
if (sigprocmask (SIG_BLOCK, &set, &old_mask_) < 0 )
215
254
Fatal (" sigprocmask: %s" , strerror (errno));
216
255
@@ -223,6 +262,25 @@ SubprocessSet::SubprocessSet() {
223
262
Fatal (" sigaction: %s" , strerror (errno));
224
263
if (sigaction (SIGHUP, &act, &old_hup_act_) < 0 )
225
264
Fatal (" sigaction: %s" , strerror (errno));
265
+
266
+ memset (&act, 0 , sizeof (act));
267
+ act.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;
268
+ act.sa_sigaction = SigChldHandler;
269
+ if (sigaction (SIGCHLD, &act, &old_chld_act_) < 0 )
270
+ Fatal (" sigaction: %s" , strerror (errno));
271
+ }
272
+
273
+ void SubprocessSet::CheckConsoleProcessTerminated () {
274
+ if (!s_sigchld_received)
275
+ return ;
276
+ for (auto i = running_.begin (); i != running_.end (); ) {
277
+ if ((*i)->use_console_ && (*i)->TryFinish (WNOHANG)) {
278
+ finished_.push (*i);
279
+ i = running_.erase (i);
280
+ } else {
281
+ ++i;
282
+ }
283
+ }
226
284
}
227
285
228
286
SubprocessSet::~SubprocessSet () {
@@ -234,6 +292,8 @@ SubprocessSet::~SubprocessSet() {
234
292
Fatal (" sigaction: %s" , strerror (errno));
235
293
if (sigaction (SIGHUP, &old_hup_act_, 0 ) < 0 )
236
294
Fatal (" sigaction: %s" , strerror (errno));
295
+ if (sigaction (SIGCHLD, &old_chld_act_, 0 ) < 0 )
296
+ Fatal (" sigaction: %s" , strerror (errno));
237
297
if (sigprocmask (SIG_SETMASK, &old_mask_, 0 ) < 0 )
238
298
Fatal (" sigprocmask: %s" , strerror (errno));
239
299
}
@@ -262,17 +322,29 @@ bool SubprocessSet::DoWork() {
262
322
fds.push_back (pfd);
263
323
++nfds;
264
324
}
325
+ if (nfds == 0 ) {
326
+ // Add a dummy entry to prevent using an empty pollfd vector.
327
+ // ppoll() allows to do this by setting fd < 0.
328
+ pollfd pfd = { -1 , 0 , 0 };
329
+ fds.push_back (pfd);
330
+ ++nfds;
331
+ }
265
332
266
333
interrupted_ = 0 ;
334
+ s_sigchld_received = 0 ;
267
335
int ret = ppoll (&fds.front (), nfds, NULL , &old_mask_);
268
336
if (ret == -1 ) {
269
337
if (errno != EINTR) {
270
338
perror (" ninja: ppoll" );
271
339
return false ;
272
340
}
341
+ CheckConsoleProcessTerminated ();
273
342
return IsInterrupted ();
274
343
}
275
344
345
+ // ppoll/pselect prioritizes file descriptor events over a signal delivery.
346
+ // However, if the user is trying to quit ninja, we should react as fast as
347
+ // possible.
276
348
HandlePendingInterruption ();
277
349
if (IsInterrupted ())
278
350
return true ;
@@ -315,15 +387,20 @@ bool SubprocessSet::DoWork() {
315
387
}
316
388
317
389
interrupted_ = 0 ;
318
- int ret = pselect (nfds, &set, 0 , 0 , 0 , &old_mask_);
390
+ s_sigchld_received = 0 ;
391
+ int ret = pselect (nfds, (nfds > 0 ? &set : nullptr ), 0 , 0 , 0 , &old_mask_);
319
392
if (ret == -1 ) {
320
393
if (errno != EINTR) {
321
394
perror (" ninja: pselect" );
322
395
return false ;
323
396
}
397
+ CheckConsoleProcessTerminated ();
324
398
return IsInterrupted ();
325
399
}
326
400
401
+ // ppoll/pselect prioritizes file descriptor events over a signal delivery.
402
+ // However, if the user is trying to quit ninja, we should react as fast as
403
+ // possible.
327
404
HandlePendingInterruption ();
328
405
if (IsInterrupted ())
329
406
return true ;
0 commit comments