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