3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup Nielsen
7 * \author Angus Leeming
9 * Full author contact details are available in file CREDITS.
11 * A class for the control of child processes launched using
12 * fork() and execvp().
17 #include "support/forkedcontr.h"
18 #include "support/forkedcall.h"
33 # include <sys/wait.h>
35 # ifndef CXX_GLOBAL_CSTD
41 #include <boost/bind.hpp>
56 string const getChildErrorMessage()
58 DWORD const error_code = ::GetLastError();
61 bool const ok = ::FormatMessage(
62 FORMAT_MESSAGE_ALLOCATE_BUFFER |
63 FORMAT_MESSAGE_FROM_SYSTEM,
65 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
66 (LPTSTR) &t_message, 0, 0
69 std::ostringstream ss;
70 ss << "LyX: Error waiting for child: " << error_code;
73 ss << ": " << (LPTSTR)t_message;
74 ::LocalFree(t_message);
76 ss << ": Error unknown.";
78 return ss.str().c_str();
83 // Ensure, that only one controller exists inside process
84 ForkedcallsController & ForkedcallsController::get()
86 static ForkedcallsController singleton;
91 ForkedcallsController::ForkedcallsController()
95 // open question: should we stop childs here?
96 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
97 // I want to print or something.
98 ForkedcallsController::~ForkedcallsController()
102 void ForkedcallsController::addCall(ForkedProcess const & newcall)
104 forkedCalls.push_back(newcall.clone());
108 // Check the list of dead children and emit any associated signals.
109 void ForkedcallsController::handleCompletedProcesses()
111 ListType::iterator it = forkedCalls.begin();
112 ListType::iterator end = forkedCalls.end();
114 ForkedProcessPtr actCall = *it;
115 bool remove_it = false;
118 HANDLE const hProcess = HANDLE(actCall->pid());
120 DWORD const wait_status = ::WaitForSingleObject(hProcess, 0);
122 switch (wait_status) {
126 case WAIT_OBJECT_0: {
128 if (!GetExitCodeProcess(hProcess, &exit_code)) {
129 lyxerr << "GetExitCodeProcess failed waiting for child\n"
130 << getChildErrorMessage() << std::endl;
131 // Child died, so pretend it returned 1
132 actCall->setRetValue(1);
134 actCall->setRetValue(exit_code);
140 lyxerr << "WaitForSingleObject failed waiting for child\n"
141 << getChildErrorMessage() << std::endl;
142 actCall->setRetValue(1);
147 pid_t pid = actCall->pid();
149 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
151 if (waitrpid == -1) {
152 lyxerr << "LyX: Error waiting for child: "
153 << strerror(errno) << endl;
155 // Child died, so pretend it returned 1
156 actCall->setRetValue(1);
159 } else if (waitrpid == 0) {
160 // Still running. Move on to the next child.
162 } else if (WIFEXITED(stat_loc)) {
163 // Ok, the return value goes into retval.
164 actCall->setRetValue(WEXITSTATUS(stat_loc));
167 } else if (WIFSIGNALED(stat_loc)) {
168 // Child died, so pretend it returned 1
169 actCall->setRetValue(1);
172 } else if (WIFSTOPPED(stat_loc)) {
173 lyxerr << "LyX: Child (pid: " << pid
174 << ") stopped on signal "
175 << WSTOPSIG(stat_loc)
176 << ". Waiting for child to finish." << endl;
179 lyxerr << "LyX: Something rotten happened while "
180 "waiting for child " << pid << endl;
182 // Child died, so pretend it returned 1
183 actCall->setRetValue(1);
189 forkedCalls.erase(it);
190 actCall->emitSignal();
192 /* start all over: emiting the signal can result
193 * in changing the list (Ab)
195 it = forkedCalls.begin();
203 ForkedcallsController::iterator
204 ForkedcallsController::find_pid(pid_t pid)
206 return find_if(forkedCalls.begin(), forkedCalls.end(),
207 bind(equal_to<pid_t>(),
208 bind(&Forkedcall::pid, _1),
213 // Kill the process prematurely and remove it from the list
214 // within tolerance secs
215 void ForkedcallsController::kill(pid_t pid, int tolerance)
217 ListType::iterator it = find_pid(pid);
218 if (it == forkedCalls.end())
221 (*it)->kill(tolerance);
222 forkedCalls.erase(it);
225 } // namespace support