]> git.lyx.org Git - lyx.git/blob - src/support/forkedcontr.C
Store the forked calls in boost::shared_ptr rather than a raw pointer.
[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 #include <boost/iterator/indirect_iterator.hpp>
26
27 #include <cerrno>
28 #include <cstdlib>
29 #include <unistd.h>
30 #include <sys/wait.h>
31
32
33 using boost::bind;
34
35 using std::endl;
36 using std::find_if;
37 using std::string;
38
39 #ifndef CXX_GLOBAL_CSTD
40 using std::strerror;
41 #endif
42
43
44 namespace lyx {
45 namespace support {
46
47 // Ensure, that only one controller exists inside process
48 ForkedcallsController & ForkedcallsController::get()
49 {
50         static ForkedcallsController singleton;
51         return singleton;
52 }
53
54
55 ForkedcallsController::ForkedcallsController()
56 {
57         timeout_ = new Timeout(100, Timeout::ONETIME);
58
59         timeout_->timeout
60                 .connect(bind(&ForkedcallsController::timer, this));
61 }
62
63
64 // open question: should we stop childs here?
65 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
66 // I want to print or something.
67 ForkedcallsController::~ForkedcallsController()
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 }
80
81
82 // Timer-call
83 // Check the list and, if there is a stopped child, emit the signal.
84 void ForkedcallsController::timer()
85 {
86         ListType::iterator it  = forkedCalls.begin();
87         ListType::iterator end = forkedCalls.end();
88         while (it != end) {
89                 ForkedProcess * actCall = it->get();
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
107                 } else if (WIFEXITED(stat_loc)) {
108                         // Ok, the return value goes into retval.
109                         actCall->setRetValue(WEXITSTATUS(stat_loc));
110                         remove_it = true;
111
112                 } else if (WIFSIGNALED(stat_loc)) {
113                         // Child died, so pretend it returned 1
114                         actCall->setRetValue(1);
115                         remove_it = true;
116
117                 } else if (WIFSTOPPED(stat_loc)) {
118                         lyxerr << "LyX: Child (pid: " << pid
119                                << ") stopped on signal "
120                                << WSTOPSIG(stat_loc)
121                                << ". Waiting for child to finish." << endl;
122
123                 } else {
124                         lyxerr << "LyX: Something rotten happened while "
125                                 "waiting for child " << pid << endl;
126
127                         // Child died, so pretend it returned 1
128                         actCall->setRetValue(1);
129                         remove_it = true;
130                 }
131
132                 if (remove_it) {
133                         actCall->emitSignal();
134                         forkedCalls.erase(it);
135
136                         /* start all over: emiting the signal can result
137                          * in changing the list (Ab)
138                          */
139                         it = forkedCalls.begin();
140                 } else {
141                         ++it;
142                 }
143         }
144
145         if (!forkedCalls.empty() && !timeout_->running()) {
146                 timeout_->start();
147         }
148 }
149
150
151 // Kill the process prematurely and remove it from the list
152 // within tolerance secs
153 void ForkedcallsController::kill(pid_t pid, int tolerance)
154 {
155         typedef boost::indirect_iterator<ListType::iterator> iterator;
156
157         iterator begin = boost::make_indirect_iterator(forkedCalls.begin());
158         iterator end   = boost::make_indirect_iterator(forkedCalls.end());
159         iterator it = find_if(begin, end,
160                               lyx::compare_memfun(&Forkedcall::pid, pid));
161
162         if (it == end)
163                 return;
164
165         it->kill(tolerance);
166         forkedCalls.erase(it.base());
167
168         if (forkedCalls.empty())
169                 timeout_->stop();
170 }
171
172 } // namespace support
173 } // namespace lyx