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"
30 # include <sys/wait.h>
32 # ifndef CXX_GLOBAL_CSTD
38 #include <boost/bind.hpp>
53 string const getChildErrorMessage()
55 DWORD const error_code = ::GetLastError();
58 bool const ok = ::FormatMessage(
59 FORMAT_MESSAGE_ALLOCATE_BUFFER |
60 FORMAT_MESSAGE_FROM_SYSTEM,
62 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
63 (LPTSTR) &t_message, 0, 0
66 std::ostringstream ss;
67 ss << "LyX: Error waiting for child: " << error_code;
70 ss << ": " << (LPTSTR)t_message;
71 ::LocalFree(t_message);
73 ss << ": Error unknown.";
75 return ss.str().c_str();
80 // Ensure, that only one controller exists inside process
81 ForkedcallsController & ForkedcallsController::get()
83 static ForkedcallsController singleton;
88 ForkedcallsController::ForkedcallsController()
92 // open question: should we stop childs here?
93 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
94 // I want to print or something.
95 ForkedcallsController::~ForkedcallsController()
99 void ForkedcallsController::addCall(ForkedProcess const & newcall)
101 forkedCalls.push_back(newcall.clone());
105 // Check the list of dead children and emit any associated signals.
106 void ForkedcallsController::handleCompletedProcesses()
108 ListType::iterator it = forkedCalls.begin();
109 ListType::iterator end = forkedCalls.end();
111 ForkedProcessPtr actCall = *it;
112 bool remove_it = false;
115 HANDLE const hProcess = HANDLE(actCall->pid());
117 DWORD const wait_status = ::WaitForSingleObject(hProcess, 0);
119 switch (wait_status) {
123 case WAIT_OBJECT_0: {
125 if (!GetExitCodeProcess(hProcess, &exit_code)) {
126 lyxerr << "GetExitCodeProcess failed waiting for child\n"
127 << getChildErrorMessage() << std::endl;
128 // Child died, so pretend it returned 1
129 actCall->setRetValue(1);
131 actCall->setRetValue(exit_code);
137 lyxerr << "WaitForSingleObject failed waiting for child\n"
138 << getChildErrorMessage() << std::endl;
139 actCall->setRetValue(1);
144 pid_t pid = actCall->pid();
146 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
148 if (waitrpid == -1) {
149 lyxerr << "LyX: Error waiting for child: "
150 << strerror(errno) << endl;
152 // Child died, so pretend it returned 1
153 actCall->setRetValue(1);
156 } else if (waitrpid == 0) {
157 // Still running. Move on to the next child.
159 } else if (WIFEXITED(stat_loc)) {
160 // Ok, the return value goes into retval.
161 actCall->setRetValue(WEXITSTATUS(stat_loc));
164 } else if (WIFSIGNALED(stat_loc)) {
165 // Child died, so pretend it returned 1
166 actCall->setRetValue(1);
169 } else if (WIFSTOPPED(stat_loc)) {
170 lyxerr << "LyX: Child (pid: " << pid
171 << ") stopped on signal "
172 << WSTOPSIG(stat_loc)
173 << ". Waiting for child to finish." << endl;
176 lyxerr << "LyX: Something rotten happened while "
177 "waiting for child " << pid << endl;
179 // Child died, so pretend it returned 1
180 actCall->setRetValue(1);
186 forkedCalls.erase(it);
187 actCall->emitSignal();
189 /* start all over: emiting the signal can result
190 * in changing the list (Ab)
192 it = forkedCalls.begin();
200 ForkedcallsController::iterator
201 ForkedcallsController::find_pid(pid_t pid)
203 return find_if(forkedCalls.begin(), forkedCalls.end(),
204 bind(equal_to<pid_t>(),
205 bind(&Forkedcall::pid, _1),
210 // Kill the process prematurely and remove it from the list
211 // within tolerance secs
212 void ForkedcallsController::kill(pid_t pid, int tolerance)
214 ListType::iterator it = find_pid(pid);
215 if (it == forkedCalls.end())
218 (*it)->kill(tolerance);
219 forkedCalls.erase(it);
222 } // namespace support