]> git.lyx.org Git - lyx.git/blob - src/support/forkedcontr.C
The func.diff patch. Functors work and some tiny cleanup.
[lyx.git] / src / support / forkedcontr.C
1 /**
2  * \file forkedcontr.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup Nielsen
7  * \author Angus Leeming
8  *
9  * Full author contact details are available in file CREDITS.
10  *
11  * A class for the control of child processes launched using
12  * fork() and execvp().
13  */
14
15 #include <config.h>
16
17 #include "forkedcontr.h"
18 #include "forkedcall.h"
19 #include "lyxfunctional.h"
20 #include "debug.h"
21
22 #include "frontends/Timeout.h"
23
24 #include <boost/bind.hpp>
25
26 #include <cerrno>
27 #include <cstdlib>
28 #include <unistd.h>
29 #include <sys/wait.h>
30
31
32 using boost::bind;
33
34 using std::endl;
35 using std::find_if;
36 using std::string;
37 using std::vector;
38
39 #ifndef CXX_GLOBAL_CSTD
40 using std::strerror;
41 #endif
42
43
44 namespace lyx {
45 namespace support {
46
47 // Ensure, that only one controller exists inside process
48 ForkedcallsController & ForkedcallsController::get()
49 {
50         static ForkedcallsController singleton;
51         return singleton;
52 }
53
54
55 ForkedcallsController::ForkedcallsController()
56 {
57         timeout_ = new Timeout(100, Timeout::ONETIME);
58
59         timeout_->timeout
60                 .connect(bind(&ForkedcallsController::timer, this));
61 }
62
63
64 // open question: should we stop childs here?
65 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
66 // I want to print or something.
67 ForkedcallsController::~ForkedcallsController()
68 {
69         for (ListType::iterator it = forkedCalls.begin();
70              it != forkedCalls.end(); ++it) {
71                 delete *it;
72         }
73
74         delete timeout_;
75 }
76
77
78 void ForkedcallsController::addCall(ForkedProcess const & newcall)
79 {
80         if (!timeout_->running())
81                 timeout_->start();
82
83         forkedCalls.push_back(newcall.clone().release());
84         childrenChanged();
85 }
86
87
88 // Timer-call
89 // Check the list and, if there is a stopped child, emit the signal.
90 void ForkedcallsController::timer()
91 {
92         ListType::size_type start_size = forkedCalls.size();
93
94         ListType::iterator it  = forkedCalls.begin();
95         ListType::iterator end = forkedCalls.end();
96         while (it != end) {
97                 ForkedProcess * actCall = *it;
98
99                 pid_t pid = actCall->pid();
100                 int stat_loc;
101                 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
102                 bool remove_it = false;
103
104                 if (waitrpid == -1) {
105                         lyxerr << "LyX: Error waiting for child: "
106                                << strerror(errno) << endl;
107
108                         // Child died, so pretend it returned 1
109                         actCall->setRetValue(1);
110                         remove_it = true;
111
112                 } else if (waitrpid == 0) {
113                         // Still running. Move on to the next child.
114
115                 } else if (WIFEXITED(stat_loc)) {
116                         // Ok, the return value goes into retval.
117                         actCall->setRetValue(WEXITSTATUS(stat_loc));
118                         remove_it = true;
119
120                 } else if (WIFSIGNALED(stat_loc)) {
121                         // Child died, so pretend it returned 1
122                         actCall->setRetValue(1);
123                         remove_it = true;
124
125                 } else if (WIFSTOPPED(stat_loc)) {
126                         lyxerr << "LyX: Child (pid: " << pid
127                                << ") stopped on signal "
128                                << WSTOPSIG(stat_loc)
129                                << ". Waiting for child to finish." << endl;
130
131                 } else {
132                         lyxerr << "LyX: Something rotten happened while "
133                                 "waiting for child " << pid << endl;
134
135                         // Child died, so pretend it returned 1
136                         actCall->setRetValue(1);
137                         remove_it = true;
138                 }
139
140                 if (remove_it) {
141                         forkedCalls.erase(it);
142
143                         actCall->emitSignal();
144                         delete actCall;
145
146                         /* start all over: emiting the signal can result
147                          * in changing the list (Ab)
148                          */
149                         it = forkedCalls.begin();
150                 } else {
151                         ++it;
152                 }
153         }
154
155         if (!forkedCalls.empty() && !timeout_->running()) {
156                 timeout_->start();
157         }
158
159         if (start_size != forkedCalls.size())
160                 childrenChanged();
161 }
162
163
164 // Return a vector of the pids of all the controlled processes.
165 vector<pid_t> const ForkedcallsController::getPIDs() const
166 {
167         vector<pid_t> pids;
168
169         if (forkedCalls.empty())
170                 return pids;
171
172         pids.resize(forkedCalls.size());
173
174         vector<pid_t>::iterator vit = pids.begin();
175         for (ListType::const_iterator lit = forkedCalls.begin();
176              lit != forkedCalls.end(); ++lit, ++vit) {
177                 *vit = (*lit)->pid();
178         }
179
180         std::sort(pids.begin(), pids.end());
181         return pids;
182 }
183
184
185 // Get the command string of the process.
186 string const ForkedcallsController::getCommand(pid_t pid) const
187 {
188         ListType::const_iterator it =
189                 find_if(forkedCalls.begin(), forkedCalls.end(),
190                         lyx::compare_memfun(&Forkedcall::pid, pid));
191
192         if (it == forkedCalls.end())
193                 return string();
194
195         return (*it)->command();
196 }
197
198
199 // Kill the process prematurely and remove it from the list
200 // within tolerance secs
201 void ForkedcallsController::kill(pid_t pid, int tolerance)
202 {
203         ListType::iterator it =
204                 find_if(forkedCalls.begin(), forkedCalls.end(),
205                         lyx::compare_memfun(&Forkedcall::pid, pid));
206
207         if (it == forkedCalls.end())
208                 return;
209
210         (*it)->kill(tolerance);
211         forkedCalls.erase(it);
212
213         if (forkedCalls.empty()) {
214                 timeout_->stop();
215         }
216 }
217
218 } // namespace support
219 } // namespace lyx