]> git.lyx.org Git - lyx.git/blob - src/support/forkedcontr.C
Alfredo's patches to the forkedcall controller and to the graphics loader.
[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 using std::vector;
32 using std::endl;
33 using std::find_if;
34
35 #ifndef CXX_GLOBAL_CSTD
36 using std::strerror;
37 #endif
38
39 // Ensure, that only one controller exists inside process
40 ForkedcallsController & ForkedcallsController::get()
41 {
42         static ForkedcallsController singleton;
43         return singleton;
44 }
45
46
47 ForkedcallsController::ForkedcallsController()
48 {
49         timeout_ = new Timeout(100, Timeout::ONETIME);
50
51         timeout_->timeout
52                 .connect(boost::bind(&ForkedcallsController::timer, this));
53 }
54
55
56 // open question: should we stop childs here?
57 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
58 // I want to print or something.
59 ForkedcallsController::~ForkedcallsController()
60 {
61         for (ListType::iterator it = forkedCalls.begin();
62              it != forkedCalls.end(); ++it) {
63                 delete *it;
64         }
65
66         delete timeout_;
67 }
68
69
70 void ForkedcallsController::addCall(ForkedProcess const & newcall)
71 {
72         if (!timeout_->running())
73                 timeout_->start();
74
75         forkedCalls.push_back(newcall.clone());
76         childrenChanged();
77 }
78
79
80 // Timer-call
81 // Check the list and, if there is a stopped child, emit the signal.
82 void ForkedcallsController::timer()
83 {
84         ListType::size_type start_size = forkedCalls.size();
85
86         ListType::iterator it  = forkedCalls.begin();
87         ListType::iterator end = forkedCalls.end();
88         while (it != end) {
89                 ForkedProcess * actCall = *it;
90
91                 pid_t pid = actCall->pid();
92                 int stat_loc;
93                 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
94                 bool remove_it = false;
95
96                 if (waitrpid == -1) {
97                         lyxerr << "LyX: Error waiting for child: "
98                                << strerror(errno) << endl;
99
100                         // Child died, so pretend it returned 1
101                         actCall->setRetValue(1);
102                         remove_it = true;
103
104                 } else if (waitrpid == 0) {
105                         // Still running. Move on to the next child.
106                         continue;
107
108                 } else if (WIFEXITED(stat_loc)) {
109                         // Ok, the return value goes into retval.
110                         actCall->setRetValue(WEXITSTATUS(stat_loc));
111                         remove_it = true;
112
113                 } else if (WIFSIGNALED(stat_loc)) {
114                         // Child died, so pretend it returned 1
115                         actCall->setRetValue(1);
116                         remove_it = true;
117
118                 } else if (WIFSTOPPED(stat_loc)) {
119                         lyxerr << "LyX: Child (pid: " << pid
120                                << ") stopped on signal "
121                                << WSTOPSIG(stat_loc)
122                                << ". Waiting for child to finish." << endl;
123
124                 } else {
125                         lyxerr << "LyX: Something rotten happened while "
126                                 "waiting for child " << pid << endl;
127
128                         // Child died, so pretend it returned 1
129                         actCall->setRetValue(1);
130                         remove_it = true;
131                 }
132
133                 if (remove_it) {
134                         forkedCalls.erase(it);
135
136                         actCall->emitSignal();
137                         delete actCall;
138
139                         /* start all over: emiting the signal can result
140                          * in changing the list (Ab)
141                          */
142                         it = forkedCalls.begin();
143                 } else {
144                         ++it;
145                 }
146         }
147
148         if (!forkedCalls.empty() && !timeout_->running()) {
149                 timeout_->start();
150         }
151
152         if (start_size != forkedCalls.size())
153                 childrenChanged();
154 }
155
156
157 // Return a vector of the pids of all the controlled processes.
158 vector<pid_t> const ForkedcallsController::getPIDs() const
159 {
160         vector<pid_t> pids;
161
162         if (forkedCalls.empty())
163                 return pids;
164
165         pids.resize(forkedCalls.size());
166
167         vector<pid_t>::iterator vit = pids.begin();
168         for (ListType::const_iterator lit = forkedCalls.begin();
169              lit != forkedCalls.end(); ++lit, ++vit) {
170                 *vit = (*lit)->pid();
171         }
172
173         std::sort(pids.begin(), pids.end());
174         return pids;
175 }
176
177
178 // Get the command string of the process.
179 string const ForkedcallsController::getCommand(pid_t pid) const
180 {
181         ListType::const_iterator it =
182                 find_if(forkedCalls.begin(), forkedCalls.end(),
183                         lyx::compare_memfun(&Forkedcall::pid, pid));
184
185         if (it == forkedCalls.end())
186                 return string();
187
188         return (*it)->command();
189 }
190
191
192 // Kill the process prematurely and remove it from the list
193 // within tolerance secs
194 void ForkedcallsController::kill(pid_t pid, int tolerance)
195 {
196         ListType::iterator it =
197                 find_if(forkedCalls.begin(), forkedCalls.end(),
198                         lyx::compare_memfun(&Forkedcall::pid, pid));
199
200         if (it == forkedCalls.end())
201                 return;
202
203         (*it)->kill(tolerance);
204         forkedCalls.erase(it);
205
206         if (forkedCalls.empty()) {
207                 timeout_->stop();
208         }
209 }