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