KDE3Support
k3processcontroller.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at) 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "k3processcontroller.h" 00021 #include "k3process.h" 00022 00023 #include <config.h> 00024 00025 #include <sys/time.h> 00026 #include <sys/types.h> 00027 #include <sys/wait.h> 00028 #include <unistd.h> 00029 #include <errno.h> 00030 #include <fcntl.h> 00031 #include <stdio.h> 00032 #include <stdlib.h> 00033 00034 #include <QtCore/QSocketNotifier> 00035 00036 class K3ProcessController::Private 00037 { 00038 public: 00039 Private() 00040 : needcheck( false ), 00041 notifier( 0 ) 00042 { 00043 } 00044 00045 ~Private() 00046 { 00047 delete notifier; 00048 } 00049 00050 int fd[2]; 00051 bool needcheck; 00052 QSocketNotifier *notifier; 00053 QList<K3Process*> kProcessList; 00054 QList<int> unixProcessList; 00055 static struct sigaction oldChildHandlerData; 00056 static bool handlerSet; 00057 static int refCount; 00058 static K3ProcessController* instance; 00059 }; 00060 00061 K3ProcessController *K3ProcessController::Private::instance = 0; 00062 int K3ProcessController::Private::refCount = 0; 00063 00064 void K3ProcessController::ref() 00065 { 00066 if ( !Private::refCount ) { 00067 Private::instance = new K3ProcessController; 00068 setupHandlers(); 00069 } 00070 Private::refCount++; 00071 } 00072 00073 void K3ProcessController::deref() 00074 { 00075 Private::refCount--; 00076 if( !Private::refCount ) { 00077 resetHandlers(); 00078 delete Private::instance; 00079 Private::instance = 0; 00080 } 00081 } 00082 00083 K3ProcessController* K3ProcessController::instance() 00084 { 00085 /* 00086 * there were no safety guards in previous revisions, is that ok? 00087 if ( !Private::instance ) { 00088 ref(); 00089 } 00090 */ 00091 00092 return Private::instance; 00093 } 00094 00095 K3ProcessController::K3ProcessController() 00096 : d( new Private ) 00097 { 00098 if( pipe( d->fd ) ) 00099 { 00100 perror( "pipe" ); 00101 abort(); 00102 } 00103 00104 fcntl( d->fd[0], F_SETFL, O_NONBLOCK ); // in case slotDoHousekeeping is called without polling first 00105 fcntl( d->fd[1], F_SETFL, O_NONBLOCK ); // in case it fills up 00106 fcntl( d->fd[0], F_SETFD, FD_CLOEXEC ); 00107 fcntl( d->fd[1], F_SETFD, FD_CLOEXEC ); 00108 00109 d->notifier = new QSocketNotifier( d->fd[0], QSocketNotifier::Read ); 00110 d->notifier->setEnabled( true ); 00111 QObject::connect( d->notifier, SIGNAL(activated(int)), 00112 SLOT(slotDoHousekeeping())); 00113 } 00114 00115 K3ProcessController::~K3ProcessController() 00116 { 00117 #ifndef Q_OS_MAC 00118 /* not sure why, but this is causing lockups */ 00119 close( d->fd[0] ); 00120 close( d->fd[1] ); 00121 #else 00122 #warning FIXME: why does close() freeze up destruction? 00123 #endif 00124 00125 delete d; 00126 } 00127 00128 00129 extern "C" { 00130 static void theReaper( int num ) 00131 { 00132 K3ProcessController::theSigCHLDHandler( num ); 00133 } 00134 } 00135 00136 #ifdef Q_OS_UNIX 00137 struct sigaction K3ProcessController::Private::oldChildHandlerData; 00138 #endif 00139 bool K3ProcessController::Private::handlerSet = false; 00140 00141 void K3ProcessController::setupHandlers() 00142 { 00143 if( Private::handlerSet ) 00144 return; 00145 Private::handlerSet = true; 00146 00147 #ifdef Q_OS_UNIX 00148 struct sigaction act; 00149 sigemptyset( &act.sa_mask ); 00150 00151 act.sa_handler = SIG_IGN; 00152 act.sa_flags = 0; 00153 sigaction( SIGPIPE, &act, 0L ); 00154 00155 act.sa_handler = theReaper; 00156 act.sa_flags = SA_NOCLDSTOP; 00157 // CC: take care of SunOS which automatically restarts interrupted system 00158 // calls (and thus does not have SA_RESTART) 00159 #ifdef SA_RESTART 00160 act.sa_flags |= SA_RESTART; 00161 #endif 00162 sigaction( SIGCHLD, &act, &Private::oldChildHandlerData ); 00163 00164 sigaddset( &act.sa_mask, SIGCHLD ); 00165 // Make sure we don't block this signal. gdb tends to do that :-( 00166 sigprocmask( SIG_UNBLOCK, &act.sa_mask, 0 ); 00167 #else 00168 //TODO: win32 00169 #endif 00170 } 00171 00172 void K3ProcessController::resetHandlers() 00173 { 00174 if( !Private::handlerSet ) 00175 return; 00176 Private::handlerSet = false; 00177 00178 #ifdef Q_OS_UNIX 00179 sigset_t mask, omask; 00180 sigemptyset( &mask ); 00181 sigaddset( &mask, SIGCHLD ); 00182 sigprocmask( SIG_BLOCK, &mask, &omask ); 00183 00184 struct sigaction act; 00185 sigaction( SIGCHLD, &Private::oldChildHandlerData, &act ); 00186 if (act.sa_handler != theReaper) { 00187 sigaction( SIGCHLD, &act, 0 ); 00188 Private::handlerSet = true; 00189 } 00190 00191 sigprocmask( SIG_SETMASK, &omask, 0 ); 00192 #else 00193 //TODO: win32 00194 #endif 00195 // there should be no problem with SIGPIPE staying SIG_IGN 00196 } 00197 00198 // the pipe is needed to sync the child reaping with our event processing, 00199 // as otherwise there are race conditions, locking requirements, and things 00200 // generally get harder 00201 void K3ProcessController::theSigCHLDHandler( int arg ) 00202 { 00203 int saved_errno = errno; 00204 00205 char dummy = 0; 00206 ::write( instance()->d->fd[1], &dummy, 1 ); 00207 00208 #ifdef Q_OS_UNIX 00209 if ( Private::oldChildHandlerData.sa_handler != SIG_IGN && 00210 Private::oldChildHandlerData.sa_handler != SIG_DFL ) { 00211 Private::oldChildHandlerData.sa_handler( arg ); // call the old handler 00212 } 00213 #else 00214 //TODO: win32 00215 #endif 00216 00217 errno = saved_errno; 00218 } 00219 00220 int K3ProcessController::notifierFd() const 00221 { 00222 return d->fd[0]; 00223 } 00224 00225 void K3ProcessController::unscheduleCheck() 00226 { 00227 char dummy[16]; // somewhat bigger - just in case several have queued up 00228 if( ::read( d->fd[0], dummy, sizeof(dummy) ) > 0 ) 00229 d->needcheck = true; 00230 } 00231 00232 void 00233 K3ProcessController::rescheduleCheck() 00234 { 00235 if( d->needcheck ) 00236 { 00237 d->needcheck = false; 00238 char dummy = 0; 00239 ::write( d->fd[1], &dummy, 1 ); 00240 } 00241 } 00242 00243 void K3ProcessController::slotDoHousekeeping() 00244 { 00245 char dummy[16]; // somewhat bigger - just in case several have queued up 00246 ::read( d->fd[0], dummy, sizeof(dummy) ); 00247 00248 int status; 00249 again: 00250 QList<K3Process*>::iterator it( d->kProcessList.begin() ); 00251 QList<K3Process*>::iterator eit( d->kProcessList.end() ); 00252 while( it != eit ) 00253 { 00254 K3Process *prc = *it; 00255 if( prc->runs && waitpid( prc->pid_, &status, WNOHANG ) > 0 ) 00256 { 00257 prc->processHasExited( status ); 00258 // the callback can nuke the whole process list and even 'this' 00259 if (!instance()) 00260 return; 00261 goto again; 00262 } 00263 ++it; 00264 } 00265 QList<int>::iterator uit( d->unixProcessList.begin() ); 00266 QList<int>::iterator ueit( d->unixProcessList.end() ); 00267 while( uit != ueit ) 00268 { 00269 if( waitpid( *uit, 0, WNOHANG ) > 0 ) 00270 { 00271 uit = d->unixProcessList.erase( uit ); 00272 deref(); // counterpart to addProcess, can invalidate 'this' 00273 } else 00274 ++uit; 00275 } 00276 } 00277 00278 bool K3ProcessController::waitForProcessExit( int timeout ) 00279 { 00280 #ifdef Q_OS_UNIX 00281 for(;;) 00282 { 00283 struct timeval tv, *tvp; 00284 if (timeout < 0) 00285 tvp = 0; 00286 else 00287 { 00288 tv.tv_sec = timeout; 00289 tv.tv_usec = 0; 00290 tvp = &tv; 00291 } 00292 00293 fd_set fds; 00294 FD_ZERO( &fds ); 00295 FD_SET( d->fd[0], &fds ); 00296 00297 switch( select( d->fd[0]+1, &fds, 0, 0, tvp ) ) 00298 { 00299 case -1: 00300 if( errno == EINTR ) 00301 continue; 00302 // fall through; should never happen 00303 case 0: 00304 return false; 00305 default: 00306 slotDoHousekeeping(); 00307 return true; 00308 } 00309 } 00310 #else 00311 //TODO: win32 00312 return false; 00313 #endif 00314 } 00315 00316 void K3ProcessController::addKProcess( K3Process* p ) 00317 { 00318 d->kProcessList.append( p ); 00319 } 00320 00321 void K3ProcessController::removeKProcess( K3Process* p ) 00322 { 00323 d->kProcessList.removeAll( p ); 00324 } 00325 00326 void K3ProcessController::addProcess( int pid ) 00327 { 00328 d->unixProcessList.append( pid ); 00329 ref(); // make sure we stay around when the K3Process goes away 00330 } 00331 00332 #include "k3processcontroller.moc"
KDE 4.6 API Reference