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