3 * Copyright 2001 The LyX Team
6 * \author Asger Alstrup Nielsen
7 * \author Angus Leeming
9 * A class for the control of child processes launched using
10 * fork() and execvp().
16 #pragma implementation
19 #include "forkedcontr.h"
20 #include "forkedcall.h"
21 #include "lyxfunctional.h"
24 #include "frontends/Timeout.h"
26 #include <boost/bind.hpp>
37 #ifndef CXX_GLOBAL_CSTD
41 // Ensure, that only one controller exists inside process
42 ForkedcallsController & ForkedcallsController::get()
44 static ForkedcallsController singleton;
49 ForkedcallsController::ForkedcallsController()
51 timeout_ = new Timeout(100, Timeout::ONETIME);
54 .connect(boost::bind(&ForkedcallsController::timer, this));
58 // open question: should we stop childs here?
59 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
60 // I want to print or something.
61 ForkedcallsController::~ForkedcallsController()
63 for (ListType::iterator it = forkedCalls.begin();
64 it != forkedCalls.end(); ++it) {
72 // Add child process information to the list of controlled processes
73 void ForkedcallsController::addCall(Forkedcall const &newcall)
75 if (!timeout_->running())
78 Forkedcall * call = new Forkedcall(newcall);
79 forkedCalls.push_back(call);
85 // Check the list and, if there is a stopped child, emit the signal.
86 void ForkedcallsController::timer()
88 ListType::size_type start_size = forkedCalls.size();
90 for (ListType::iterator it = forkedCalls.begin();
91 it != forkedCalls.end(); ++it) {
92 Forkedcall * actCall = *it;
94 pid_t pid = actCall->pid();
96 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
97 bool remove_it = false;
100 lyxerr << "LyX: Error waiting for child: "
101 << strerror(errno) << endl;
103 // Child died, so pretend it returned 1
104 actCall->setRetValue(1);
107 } else if (waitrpid == 0) {
108 // Still running. Move on to the next child.
111 } else if (WIFEXITED(stat_loc)) {
112 // Ok, the return value goes into retval.
113 actCall->setRetValue(WEXITSTATUS(stat_loc));
116 } else if (WIFSIGNALED(stat_loc)) {
117 // Child died, so pretend it returned 1
118 actCall->setRetValue(1);
121 } else if (WIFSTOPPED(stat_loc)) {
122 lyxerr << "LyX: Child (pid: " << pid
123 << ") stopped on signal "
124 << WSTOPSIG(stat_loc)
125 << ". Waiting for child to finish." << endl;
128 lyxerr << "LyX: Something rotten happened while "
129 "waiting for child " << pid << endl;
131 // Child died, so pretend it returned 1
132 actCall->setRetValue(1);
137 // Emit signal and remove the item from the list
138 actCall->emitSignal();
140 // erase returns the next iterator, so decrement it
141 // to continue the loop.
142 ListType::iterator prev = it;
144 forkedCalls.erase(it);
149 if (!forkedCalls.empty()) {
153 if (start_size != forkedCalls.size())
158 // Return a vector of the pids of all the controlled processes.
159 vector<pid_t> const ForkedcallsController::getPIDs() const
163 if (forkedCalls.empty())
166 pids.resize(forkedCalls.size());
168 vector<pid_t>::iterator vit = pids.begin();
169 for (ListType::const_iterator lit = forkedCalls.begin();
170 lit != forkedCalls.end(); ++lit, ++vit) {
171 *vit = (*lit)->pid();
174 std::sort(pids.begin(), pids.end());
179 // Get the command string of the process.
180 string const ForkedcallsController::getCommand(pid_t pid) const
182 ListType::const_iterator it =
183 find_if(forkedCalls.begin(), forkedCalls.end(),
184 lyx::compare_memfun(&Forkedcall::pid, pid));
186 if (it == forkedCalls.end())
189 return (*it)->command();
193 // Kill the process prematurely and remove it from the list
194 // within tolerance secs
195 void ForkedcallsController::kill(pid_t pid, int tolerance)
197 ListType::iterator it =
198 find_if(forkedCalls.begin(), forkedCalls.end(),
199 lyx::compare_memfun(&Forkedcall::pid, pid));
201 if (it == forkedCalls.end())
204 (*it)->kill(tolerance);
205 forkedCalls.erase(it);
207 if (forkedCalls.empty()) {