Mercurial > octave
comparison lib/k3processcontroller.cpp @ 15628:e67d0d06c18b
Forked from QTermWidget.
author | Jacob Dawid <jacob.dawid@googlemail.com> |
---|---|
date | Mon, 23 Jan 2012 12:22:13 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 15628:e67d0d06c18b |
---|---|
1 /* This file is part of the KDE libraries | |
2 Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at) | |
3 | |
4 Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 | |
5 | |
6 This library is free software; you can redistribute it and/or | |
7 modify it under the terms of the GNU Library General Public | |
8 License as published by the Free Software Foundation; either | |
9 version 2 of the License, or (at your option) any later version. | |
10 | |
11 This library is distributed in the hope that it will be useful, | |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 Library General Public License for more details. | |
15 | |
16 You should have received a copy of the GNU Library General Public License | |
17 along with this library; see the file COPYING.LIB. If not, write to | |
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
19 Boston, MA 02110-1301, USA. | |
20 */ | |
21 | |
22 #include "k3processcontroller.h" | |
23 #include "k3process.h" | |
24 | |
25 //#include <config.h> | |
26 | |
27 #include <sys/time.h> | |
28 #include <sys/types.h> | |
29 #include <sys/wait.h> | |
30 #include <unistd.h> | |
31 #include <errno.h> | |
32 #include <fcntl.h> | |
33 #include <stdio.h> | |
34 #include <stdlib.h> | |
35 | |
36 #include <QtCore/QSocketNotifier> | |
37 #include <QDebug> | |
38 | |
39 class K3ProcessController::Private | |
40 { | |
41 public: | |
42 Private() | |
43 : needcheck( false ), | |
44 notifier( 0 ) | |
45 { | |
46 } | |
47 | |
48 ~Private() | |
49 { | |
50 delete notifier; | |
51 } | |
52 | |
53 int fd[2]; | |
54 bool needcheck; | |
55 QSocketNotifier *notifier; | |
56 QList<K3Process*> kProcessList; | |
57 QList<int> unixProcessList; | |
58 static struct sigaction oldChildHandlerData; | |
59 static bool handlerSet; | |
60 static int refCount; | |
61 static K3ProcessController* instance; | |
62 }; | |
63 | |
64 K3ProcessController *K3ProcessController::Private::instance = 0; | |
65 int K3ProcessController::Private::refCount = 0; | |
66 | |
67 void K3ProcessController::ref() | |
68 { | |
69 if ( !Private::refCount ) { | |
70 Private::instance = new K3ProcessController; | |
71 setupHandlers(); | |
72 } | |
73 Private::refCount++; | |
74 } | |
75 | |
76 void K3ProcessController::deref() | |
77 { | |
78 Private::refCount--; | |
79 if( !Private::refCount ) { | |
80 resetHandlers(); | |
81 delete Private::instance; | |
82 Private::instance = 0; | |
83 } | |
84 } | |
85 | |
86 K3ProcessController* K3ProcessController::instance() | |
87 { | |
88 /* | |
89 * there were no safety guards in previous revisions, is that ok? | |
90 if ( !Private::instance ) { | |
91 ref(); | |
92 } | |
93 */ | |
94 | |
95 return Private::instance; | |
96 } | |
97 | |
98 K3ProcessController::K3ProcessController() | |
99 : d( new Private ) | |
100 { | |
101 if( pipe( d->fd ) ) | |
102 { | |
103 perror( "pipe" ); | |
104 abort(); | |
105 } | |
106 | |
107 fcntl( d->fd[0], F_SETFL, O_NONBLOCK ); // in case slotDoHousekeeping is called without polling first | |
108 fcntl( d->fd[1], F_SETFL, O_NONBLOCK ); // in case it fills up | |
109 fcntl( d->fd[0], F_SETFD, FD_CLOEXEC ); | |
110 fcntl( d->fd[1], F_SETFD, FD_CLOEXEC ); | |
111 | |
112 d->notifier = new QSocketNotifier( d->fd[0], QSocketNotifier::Read ); | |
113 d->notifier->setEnabled( true ); | |
114 QObject::connect( d->notifier, SIGNAL(activated(int)), | |
115 SLOT(slotDoHousekeeping())); | |
116 } | |
117 | |
118 K3ProcessController::~K3ProcessController() | |
119 { | |
120 #ifndef Q_OS_MAC | |
121 /* not sure why, but this is causing lockups */ | |
122 close( d->fd[0] ); | |
123 close( d->fd[1] ); | |
124 #else | |
125 #warning FIXME: why does close() freeze up destruction? | |
126 #endif | |
127 | |
128 delete d; | |
129 } | |
130 | |
131 | |
132 extern "C" { | |
133 static void theReaper( int num ) | |
134 { | |
135 K3ProcessController::theSigCHLDHandler( num ); | |
136 } | |
137 } | |
138 | |
139 #ifdef Q_OS_UNIX | |
140 struct sigaction K3ProcessController::Private::oldChildHandlerData; | |
141 #endif | |
142 bool K3ProcessController::Private::handlerSet = false; | |
143 | |
144 void K3ProcessController::setupHandlers() | |
145 { | |
146 if( Private::handlerSet ) | |
147 return; | |
148 Private::handlerSet = true; | |
149 | |
150 #ifdef Q_OS_UNIX | |
151 struct sigaction act; | |
152 sigemptyset( &act.sa_mask ); | |
153 | |
154 act.sa_handler = SIG_IGN; | |
155 act.sa_flags = 0; | |
156 sigaction( SIGPIPE, &act, 0L ); | |
157 | |
158 act.sa_handler = theReaper; | |
159 act.sa_flags = SA_NOCLDSTOP; | |
160 // CC: take care of SunOS which automatically restarts interrupted system | |
161 // calls (and thus does not have SA_RESTART) | |
162 #ifdef SA_RESTART | |
163 act.sa_flags |= SA_RESTART; | |
164 #endif | |
165 sigaction( SIGCHLD, &act, &Private::oldChildHandlerData ); | |
166 | |
167 sigaddset( &act.sa_mask, SIGCHLD ); | |
168 // Make sure we don't block this signal. gdb tends to do that :-( | |
169 sigprocmask( SIG_UNBLOCK, &act.sa_mask, 0 ); | |
170 #else | |
171 //TODO: win32 | |
172 #endif | |
173 } | |
174 | |
175 void K3ProcessController::resetHandlers() | |
176 { | |
177 if( !Private::handlerSet ) | |
178 return; | |
179 Private::handlerSet = false; | |
180 | |
181 #ifdef Q_OS_UNIX | |
182 sigset_t mask, omask; | |
183 sigemptyset( &mask ); | |
184 sigaddset( &mask, SIGCHLD ); | |
185 sigprocmask( SIG_BLOCK, &mask, &omask ); | |
186 | |
187 struct sigaction act; | |
188 sigaction( SIGCHLD, &Private::oldChildHandlerData, &act ); | |
189 if (act.sa_handler != theReaper) { | |
190 sigaction( SIGCHLD, &act, 0 ); | |
191 Private::handlerSet = true; | |
192 } | |
193 | |
194 sigprocmask( SIG_SETMASK, &omask, 0 ); | |
195 #else | |
196 //TODO: win32 | |
197 #endif | |
198 // there should be no problem with SIGPIPE staying SIG_IGN | |
199 } | |
200 | |
201 // the pipe is needed to sync the child reaping with our event processing, | |
202 // as otherwise there are race conditions, locking requirements, and things | |
203 // generally get harder | |
204 void K3ProcessController::theSigCHLDHandler( int arg ) | |
205 { | |
206 int saved_errno = errno; | |
207 | |
208 char dummy = 0; | |
209 ssize_t result = ::write( instance()->d->fd[1], &dummy, 1 ); | |
210 if (result < 0) { | |
211 qDebug() << "Write failed with the error code " << result << endl; | |
212 } | |
213 | |
214 #ifdef Q_OS_UNIX | |
215 if ( Private::oldChildHandlerData.sa_handler != SIG_IGN && | |
216 Private::oldChildHandlerData.sa_handler != SIG_DFL ) { | |
217 Private::oldChildHandlerData.sa_handler( arg ); // call the old handler | |
218 } | |
219 #else | |
220 //TODO: win32 | |
221 #endif | |
222 | |
223 errno = saved_errno; | |
224 } | |
225 | |
226 int K3ProcessController::notifierFd() const | |
227 { | |
228 return d->fd[0]; | |
229 } | |
230 | |
231 void K3ProcessController::unscheduleCheck() | |
232 { | |
233 char dummy[16]; // somewhat bigger - just in case several have queued up | |
234 if( ::read( d->fd[0], dummy, sizeof(dummy) ) > 0 ) | |
235 d->needcheck = true; | |
236 } | |
237 | |
238 void | |
239 K3ProcessController::rescheduleCheck() | |
240 { | |
241 if( d->needcheck ) | |
242 { | |
243 d->needcheck = false; | |
244 char dummy = 0; | |
245 ssize_t result = ::write( d->fd[1], &dummy, 1 ); | |
246 if (result < 0) { | |
247 qDebug() << "Write failed with the error code " << result << endl; | |
248 } | |
249 | |
250 } | |
251 } | |
252 | |
253 void K3ProcessController::slotDoHousekeeping() | |
254 { | |
255 char dummy[16]; // somewhat bigger - just in case several have queued up | |
256 ssize_t result = ::read( d->fd[0], dummy, sizeof(dummy) ); | |
257 if (result < 0) { | |
258 qDebug() << "Write failed with the error code " << result << endl; | |
259 } | |
260 | |
261 int status; | |
262 again: | |
263 QList<K3Process*>::iterator it( d->kProcessList.begin() ); | |
264 QList<K3Process*>::iterator eit( d->kProcessList.end() ); | |
265 while( it != eit ) | |
266 { | |
267 K3Process *prc = *it; | |
268 if( prc->runs && waitpid( prc->pid_, &status, WNOHANG ) > 0 ) | |
269 { | |
270 prc->processHasExited( status ); | |
271 // the callback can nuke the whole process list and even 'this' | |
272 if (!instance()) | |
273 return; | |
274 goto again; | |
275 } | |
276 ++it; | |
277 } | |
278 QList<int>::iterator uit( d->unixProcessList.begin() ); | |
279 QList<int>::iterator ueit( d->unixProcessList.end() ); | |
280 while( uit != ueit ) | |
281 { | |
282 if( waitpid( *uit, 0, WNOHANG ) > 0 ) | |
283 { | |
284 uit = d->unixProcessList.erase( uit ); | |
285 deref(); // counterpart to addProcess, can invalidate 'this' | |
286 } else | |
287 ++uit; | |
288 } | |
289 } | |
290 | |
291 bool K3ProcessController::waitForProcessExit( int timeout ) | |
292 { | |
293 #ifdef Q_OS_UNIX | |
294 for(;;) | |
295 { | |
296 struct timeval tv, *tvp; | |
297 if (timeout < 0) | |
298 tvp = 0; | |
299 else | |
300 { | |
301 tv.tv_sec = timeout; | |
302 tv.tv_usec = 0; | |
303 tvp = &tv; | |
304 } | |
305 | |
306 fd_set fds; | |
307 FD_ZERO( &fds ); | |
308 FD_SET( d->fd[0], &fds ); | |
309 | |
310 switch( select( d->fd[0]+1, &fds, 0, 0, tvp ) ) | |
311 { | |
312 case -1: | |
313 if( errno == EINTR ) | |
314 continue; | |
315 // fall through; should never happen | |
316 case 0: | |
317 return false; | |
318 default: | |
319 slotDoHousekeeping(); | |
320 return true; | |
321 } | |
322 } | |
323 #else | |
324 //TODO: win32 | |
325 return false; | |
326 #endif | |
327 } | |
328 | |
329 void K3ProcessController::addKProcess( K3Process* p ) | |
330 { | |
331 d->kProcessList.append( p ); | |
332 } | |
333 | |
334 void K3ProcessController::removeKProcess( K3Process* p ) | |
335 { | |
336 d->kProcessList.removeAll( p ); | |
337 } | |
338 | |
339 void K3ProcessController::addProcess( int pid ) | |
340 { | |
341 d->unixProcessList.append( pid ); | |
342 ref(); // make sure we stay around when the K3Process goes away | |
343 } | |
344 | |
345 //#include "moc_k3processcontroller.cpp" |