Ninja
subprocess.h
Go to the documentation of this file.
1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef NINJA_SUBPROCESS_H_
16 #define NINJA_SUBPROCESS_H_
17 
18 #include <string>
19 #include <vector>
20 #include <queue>
21 
22 #ifdef _WIN32
23 #include <windows.h>
24 #else
25 #include <signal.h>
26 #endif
27 
28 // ppoll() exists on FreeBSD, but only on newer versions.
29 #ifdef __FreeBSD__
30 # include <sys/param.h>
31 # if defined USE_PPOLL && __FreeBSD_version < 1002000
32 # undef USE_PPOLL
33 # endif
34 #endif
35 
36 #include "exit_status.h"
37 
38 /// Subprocess wraps a single async subprocess. It is entirely
39 /// passive: it expects the caller to notify it when its fds are ready
40 /// for reading, as well as call Finish() to reap the child once done()
41 /// is true.
42 struct Subprocess {
43  ~Subprocess();
44 
45  /// Returns ExitSuccess on successful process exit, ExitInterrupted if
46  /// the process was interrupted, ExitFailure if it otherwise failed.
48 
49  bool Done() const;
50 
51  const std::string& GetOutput() const;
52 
53  private:
54  Subprocess(bool use_console);
55  bool Start(struct SubprocessSet* set, const std::string& command);
56  void OnPipeReady();
57 
58  std::string buf_;
59 
60 #ifdef _WIN32
61  /// Set up pipe_ as the parent-side pipe of the subprocess; return the
62  /// other end of the pipe, usable in the child process.
63  HANDLE SetupPipe(HANDLE ioport);
64 
65  HANDLE child_;
66  HANDLE pipe_;
67  OVERLAPPED overlapped_;
68  char overlapped_buf_[4 << 10];
69  bool is_reading_;
70 #else
71  /// The file descriptor that will be used in ppoll/pselect() for this process,
72  /// if any. Otherwise -1.
73  /// In non-console mode, this is the read-side of a pipe that was created
74  /// specifically for this subprocess. The write-side of the pipe is given to
75  /// the subprocess as combined stdout and stderr.
76  /// In console mode no pipe is created: fd_ is -1, and process termination is
77  /// detected using the SIGCHLD signal and waitpid(WNOHANG).
78  int fd_;
79  /// PID of the subprocess. Set to -1 when the subprocess is reaped.
80  pid_t pid_;
81  /// In POSIX platforms it is necessary to use waitpid(WNOHANG) to know whether
82  /// a certain subprocess has finished. This is done for terminal subprocesses.
83  /// However, this also causes the subprocess to be reaped before Finish() is
84  /// called, so we need to store the ExitStatus so that a later Finish()
85  /// invocation can return it.
87 
88  /// Call waitpid() on the subprocess with the provided options and update the
89  /// pid_ and exit_status_ fields.
90  /// Return a boolean indicating whether the subprocess has indeed terminated.
91  bool TryFinish(int waitpid_options);
92 #endif
94 
95  friend struct SubprocessSet;
96 };
97 
98 /// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
99 /// DoWork() waits for any state change in subprocesses; finished_
100 /// is a queue of subprocesses as they finish.
102  SubprocessSet();
103  ~SubprocessSet();
104 
105  Subprocess* Add(const std::string& command, bool use_console = false);
106  bool DoWork();
108  void Clear();
109 
110  std::vector<Subprocess*> running_;
111  std::queue<Subprocess*> finished_;
112 
113 #ifdef _WIN32
114  static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType);
115  static HANDLE ioport_;
116 #else
117  static void SetInterruptedFlag(int signum);
118  static void SigChldHandler(int signo, siginfo_t* info, void* context);
119 
120  /// Store the signal number that causes the interruption.
121  /// 0 if not interruption.
122  static volatile sig_atomic_t interrupted_;
123  /// Whether ninja should quit. Set on SIGINT, SIGTERM or SIGHUP reception.
124  static bool IsInterrupted() { return interrupted_ != 0; }
125  static void HandlePendingInterruption();
126 
127  /// Initialized to 0 before ppoll/pselect().
128  /// Filled to 1 by SIGCHLD handler when a child process terminates.
129  static volatile sig_atomic_t s_sigchld_received;
131 
132  struct sigaction old_int_act_;
133  struct sigaction old_term_act_;
134  struct sigaction old_hup_act_;
135  struct sigaction old_chld_act_;
136  sigset_t old_mask_;
137 #endif
138 };
139 
140 #endif // NINJA_SUBPROCESS_H_
ExitStatus
Definition: exit_status.h:27
SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
Definition: subprocess.h:101
void CheckConsoleProcessTerminated()
std::queue< Subprocess * > finished_
Definition: subprocess.h:111
static void HandlePendingInterruption()
Subprocess * NextFinished()
static volatile sig_atomic_t interrupted_
Store the signal number that causes the interruption.
Definition: subprocess.h:122
static volatile sig_atomic_t s_sigchld_received
Initialized to 0 before ppoll/pselect().
Definition: subprocess.h:129
struct sigaction old_hup_act_
Definition: subprocess.h:134
static void SetInterruptedFlag(int signum)
sigset_t old_mask_
Definition: subprocess.h:136
struct sigaction old_chld_act_
Definition: subprocess.h:135
std::vector< Subprocess * > running_
Definition: subprocess.h:110
struct sigaction old_int_act_
Definition: subprocess.h:132
struct sigaction old_term_act_
Definition: subprocess.h:133
static void SigChldHandler(int signo, siginfo_t *info, void *context)
static bool IsInterrupted()
Whether ninja should quit. Set on SIGINT, SIGTERM or SIGHUP reception.
Definition: subprocess.h:124
Subprocess * Add(const std::string &command, bool use_console=false)
Subprocess wraps a single async subprocess.
Definition: subprocess.h:42
bool Start(struct SubprocessSet *set, const std::string &command)
bool TryFinish(int waitpid_options)
Call waitpid() on the subprocess with the provided options and update the pid_ and exit_status_ field...
bool Done() const
ExitStatus exit_status_
In POSIX platforms it is necessary to use waitpid(WNOHANG) to know whether a certain subprocess has f...
Definition: subprocess.h:86
Subprocess(bool use_console)
int fd_
The file descriptor that will be used in ppoll/pselect() for this process, if any.
Definition: subprocess.h:78
bool use_console_
Definition: subprocess.h:93
ExitStatus Finish()
Returns ExitSuccess on successful process exit, ExitInterrupted if the process was interrupted,...
const std::string & GetOutput() const
std::string buf_
Definition: subprocess.h:58
pid_t pid_
PID of the subprocess. Set to -1 when the subprocess is reaped.
Definition: subprocess.h:80