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