]> git.lyx.org Git - lyx.git/blob - src/support/forkedcontr.C
Revert the asynchronous child process code to that of LyX 1.3.6.
[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 "support/forkedcontr.h"
18 #include "support/forkedcall.h"
19
20 #include "debug.h"
21
22 #ifdef _WIN32
23 # include <sstream>
24 # include <windows.h>
25
26 #else
27 # include <cerrno>
28 # include <cstdlib>
29 # include <unistd.h>
30 # include <sys/wait.h>
31
32 # ifndef CXX_GLOBAL_CSTD
33   using std::signal;
34   using std::strerror;
35 # endif
36 #endif
37
38 #include <boost/bind.hpp>
39
40 using boost::bind;
41
42 using std::endl;
43 using std::equal_to;
44 using std::find_if;
45
46 using std::string;
47 using std::vector;
48
49 namespace lyx {
50 namespace support {
51
52 #if defined(_WIN32)
53 string const getChildErrorMessage()
54 {
55         DWORD const error_code = ::GetLastError();
56
57         HLOCAL t_message = 0;
58         bool const ok = ::FormatMessage(
59                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
60                 FORMAT_MESSAGE_FROM_SYSTEM,
61                 0, error_code,
62                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
63                 (LPTSTR) &t_message, 0, 0
64                 ) != 0;
65
66         std::ostringstream ss;
67         ss << "LyX: Error waiting for child: " << error_code;
68
69         if (ok) {
70                 ss << ": " << (LPTSTR)t_message;
71                 ::LocalFree(t_message);
72         } else
73                 ss << ": Error unknown.";
74
75         return ss.str().c_str();
76 }
77 #endif
78
79
80 // Ensure, that only one controller exists inside process
81 ForkedcallsController & ForkedcallsController::get()
82 {
83         static ForkedcallsController singleton;
84         return singleton;
85 }
86
87
88 ForkedcallsController::ForkedcallsController()
89 {}
90
91
92 // open question: should we stop childs here?
93 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
94 // I want to print or something.
95 ForkedcallsController::~ForkedcallsController()
96 {}
97
98
99 void ForkedcallsController::addCall(ForkedProcess const & newcall)
100 {
101         forkedCalls.push_back(newcall.clone());
102 }
103
104
105 // Check the list of dead children and emit any associated signals.
106 void ForkedcallsController::handleCompletedProcesses()
107 {
108         ListType::iterator it  = forkedCalls.begin();
109         ListType::iterator end = forkedCalls.end();
110         while (it != end) {
111                 ForkedProcessPtr actCall = *it;
112                 bool remove_it = false;
113
114 #if defined(_WIN32)
115                 HANDLE const hProcess = HANDLE(actCall->pid());
116
117                 DWORD const wait_status = ::WaitForSingleObject(hProcess, 0);
118
119                 switch (wait_status) {
120                 case WAIT_TIMEOUT:
121                         // Still running
122                         break;
123                 case WAIT_OBJECT_0: {
124                         DWORD exit_code = 0;
125                         if (!GetExitCodeProcess(hProcess, &exit_code)) {
126                                 lyxerr << "GetExitCodeProcess failed waiting for child\n"
127                                        << getChildErrorMessage() << std::endl;
128                                 // Child died, so pretend it returned 1
129                                 actCall->setRetValue(1);
130                         } else {
131                                 actCall->setRetValue(exit_code);
132                         }
133                         remove_it = true;
134                         break;
135                 }
136                 case WAIT_FAILED:
137                         lyxerr << "WaitForSingleObject failed waiting for child\n"
138                                << getChildErrorMessage() << std::endl;
139                         actCall->setRetValue(1);
140                         remove_it = true;
141                         break;
142                 }
143 #else
144                 pid_t pid = actCall->pid();
145                 int stat_loc;
146                 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
147
148                 if (waitrpid == -1) {
149                         lyxerr << "LyX: Error waiting for child: "
150                                << strerror(errno) << endl;
151
152                         // Child died, so pretend it returned 1
153                         actCall->setRetValue(1);
154                         remove_it = true;
155
156                 } else if (waitrpid == 0) {
157                         // Still running. Move on to the next child.
158
159                 } else if (WIFEXITED(stat_loc)) {
160                         // Ok, the return value goes into retval.
161                         actCall->setRetValue(WEXITSTATUS(stat_loc));
162                         remove_it = true;
163
164                 } else if (WIFSIGNALED(stat_loc)) {
165                         // Child died, so pretend it returned 1
166                         actCall->setRetValue(1);
167                         remove_it = true;
168
169                 } else if (WIFSTOPPED(stat_loc)) {
170                         lyxerr << "LyX: Child (pid: " << pid
171                                << ") stopped on signal "
172                                << WSTOPSIG(stat_loc)
173                                << ". Waiting for child to finish." << endl;
174
175                 } else {
176                         lyxerr << "LyX: Something rotten happened while "
177                                 "waiting for child " << pid << endl;
178
179                         // Child died, so pretend it returned 1
180                         actCall->setRetValue(1);
181                         remove_it = true;
182                 }
183 #endif
184
185                 if (remove_it) {
186                         forkedCalls.erase(it);
187                         actCall->emitSignal();
188
189                         /* start all over: emiting the signal can result
190                          * in changing the list (Ab)
191                          */
192                         it = forkedCalls.begin();
193                 } else {
194                         ++it;
195                 }
196         }
197 }
198
199
200 ForkedcallsController::iterator
201 ForkedcallsController::find_pid(pid_t pid)
202 {
203         return find_if(forkedCalls.begin(), forkedCalls.end(),
204                        bind(equal_to<pid_t>(),
205                             bind(&Forkedcall::pid, _1),
206                             pid));
207 }
208
209
210 // Kill the process prematurely and remove it from the list
211 // within tolerance secs
212 void ForkedcallsController::kill(pid_t pid, int tolerance)
213 {
214         ListType::iterator it = find_pid(pid);
215         if (it == forkedCalls.end())
216                 return;
217
218         (*it)->kill(tolerance);
219         forkedCalls.erase(it);
220 }
221
222 } // namespace support
223 } // namespace lyx