Changeset 190 for trunk/src


Ignore:
Timestamp:
Sep 22, 2009, 10:44:03 PM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

corelib: QProcess: Implemented child termination/death detection using SIGCHILD and an auxiliary thread.

Location:
trunk/src/corelib/io
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/corelib/io/qprocess.cpp

    r72 r190  
    437437    serial = 0;
    438438#endif
     439#ifdef Q_OS_OS2
     440    init();
     441#endif
    439442}
    440443
     
    443446QProcessPrivate::~QProcessPrivate()
    444447{
     448#ifdef Q_OS_OS2
     449    uninit();
     450#endif
    445451    if (stdinChannel.process)
    446452        stdinChannel.process->stdoutChannel.clear();
     
    744750    processError = QProcess::FailedToStart;
    745751    emit q->error(processError);
    746 #ifdef Q_OS_UNIX
     752#if defined(Q_OS_UNIX) || defined(Q_OS_OS2)
    747753    // make sure the process manager removes this entry
    748754    waitForDeadChild();
     
    801807        waitForFinished();
    802808    }
    803 #ifdef Q_OS_UNIX
     809#if defined(Q_OS_UNIX) || defined(Q_OS_OS2)
    804810    // make sure the process manager removes this entry
    805811    d->findExitCode();
  • trunk/src/corelib/io/qprocess_os2.cpp

    r181 r190  
    5151#include <ctype.h>
    5252
     53#include <qthread.h>
     54
    5355/*
    5456    Returns a human readable representation of the first \a len
     
    122124    ::fcntl(pipe[0], F_SETFD, FD_CLOEXEC);
    123125    ::fcntl(pipe[1], F_SETFD, FD_CLOEXEC);
     126}
     127
     128
     129class QProcessManager : public QThread
     130{
     131public:
     132
     133    static void addRef();
     134    static void release();
     135
     136    static void addProcess(QProcess *process);
     137    static void removeProcess(QProcess *process);
     138
     139private:
     140
     141    QProcessManager();
     142    ~QProcessManager();
     143
     144    void installSigHandler();
     145    void uninstallSigHandler();
     146    void run();
     147
     148    int refcnt;
     149    bool finish;
     150
     151    HEV eventSem;
     152    QAtomicInt eventSemGuard;
     153
     154    typedef QList<QProcess *> ProcessList;
     155    ProcessList processes;
     156
     157    void (*sa_old_sigchld_handler)(int);
     158
     159    static void sa_sigchld_handler(int signum);
     160
     161    static QProcessManager *instance;
     162    static QMutex mutex;
     163};
     164
     165// static
     166QProcessManager *QProcessManager::instance = 0;
     167QMutex QProcessManager::mutex;
     168
     169// static
     170void QProcessManager::sa_sigchld_handler(int signum)
     171{
     172#if defined (QPROCESS_DEBUG)
     173    fprintf(stderr, "*** SIGCHLD\n");
     174#endif
     175
     176    Q_ASSERT(instance);
     177
     178    // eventSemGuard is used as follows:
     179    // 0 - QProcessManager is not operational
     180    // 1 - QProcessManager is being run, eventSem is fine
     181    // 2 - another signal handler is in action
     182
     183    if (!instance->eventSemGuard.testAndSetAcquire(1, 2))
     184        return;
     185
     186    APIRET rc = DosPostEventSem(instance->eventSem);
     187    Q_ASSERT(rc == NO_ERROR);
     188
     189    instance->eventSemGuard.testAndSetRelease(2, 1);
     190
     191    if (instance->sa_old_sigchld_handler &&
     192        instance->sa_old_sigchld_handler != SIG_IGN)
     193        instance->sa_old_sigchld_handler(signum);
     194}
     195
     196// static
     197void QProcessManager::addRef()
     198{
     199    QMutexLocker locker(&mutex);
     200
     201    if (instance == 0) {
     202        instance = new QProcessManager();
     203    }
     204
     205    ++instance->refcnt;
     206}
     207
     208// static
     209void QProcessManager::release()
     210{
     211    QMutexLocker locker(&mutex);
     212
     213    Q_ASSERT(instance);
     214
     215    if (--instance->refcnt == 0) {
     216        // make sure we don't globally exist anymore before leaving the lock
     217        QProcessManager *instance = QProcessManager::instance;
     218        QProcessManager::instance = 0;
     219        // disable the signal handler
     220        while (!instance->eventSemGuard.testAndSetAcquire(1, 0))
     221            DosSleep(0);
     222        instance->uninstallSigHandler();
     223        // stop the thread
     224        instance->finish = true;
     225        locker.unlock();
     226        DosPostEventSem(instance->eventSem);
     227        instance->wait();
     228        delete instance;
     229    }
     230}
     231
     232// static
     233void QProcessManager::addProcess(QProcess *process)
     234{
     235#if defined (QPROCESS_DEBUG)
     236    qDebug() << "QProcessManager::addProcess()";
     237#endif
     238
     239    QMutexLocker locker(&mutex);
     240    Q_ASSERT(instance);
     241
     242    // lazily enable SIGCHLD handler and start the worker
     243    if (instance->eventSemGuard.testAndSetAcquire(0, 1)) {
     244        instance->installSigHandler();
     245        instance->start();
     246    }
     247
     248    instance->processes.append(process);
     249}
     250
     251// static
     252void QProcessManager::removeProcess(QProcess *process)
     253{
     254#if defined (QPROCESS_DEBUG)
     255    qDebug() << "QProcessManager::removeProcess()";
     256#endif
     257
     258    QMutexLocker locker(&mutex);
     259    Q_ASSERT(instance);
     260
     261    instance->processes.removeAll(process);
     262}
     263
     264QProcessManager::QProcessManager()
     265    : refcnt(0), finish(false), eventSem(NULLHANDLE), sa_old_sigchld_handler(0)
     266{
     267#if defined (QPROCESS_DEBUG)
     268    qDebug() << "QProcessManager::QProcessManager()";
     269#endif
     270
     271    APIRET rc = DosCreateEventSem(NULL, &eventSem, DCE_AUTORESET | DCE_POSTONE, FALSE);
     272    Q_ASSERT(rc == NO_ERROR);
     273}
     274
     275QProcessManager::~QProcessManager()
     276{
     277#if defined (QPROCESS_DEBUG)
     278    qDebug() << "QProcessManager::~QProcessManager()";
     279#endif
     280
     281    Q_ASSERT(!refcnt);
     282    Q_ASSERT(!processes.size());
     283
     284    DosCloseEventSem(eventSem);
     285}
     286
     287void QProcessManager::installSigHandler()
     288{
     289    // set up the SIGCHLD handler, which calls _q_processDied on all
     290    // known QProcessPrivate instances every time a child dies.
     291    struct sigaction oldAction;
     292    struct sigaction action;
     293    memset(&action, 0, sizeof(action));
     294    action.sa_handler = sa_sigchld_handler;
     295    action.sa_flags = SA_NOCLDSTOP;
     296    ::sigaction(SIGCHLD, &action, &oldAction);
     297    if (oldAction.sa_handler != sa_sigchld_handler)
     298    sa_old_sigchld_handler = oldAction.sa_handler;
     299}
     300
     301void QProcessManager::uninstallSigHandler()
     302{
     303    struct sigaction oldAction;
     304    struct sigaction action;
     305    memset(&action, 0, sizeof(action));
     306    action.sa_handler = sa_old_sigchld_handler;
     307    action.sa_flags = SA_NOCLDSTOP;
     308    ::sigaction(SIGCHLD, &action, &oldAction);
     309    if (oldAction.sa_handler != sa_sigchld_handler) {
     310        ::sigaction(SIGCHLD, &oldAction, 0);
     311    }
     312}
     313
     314void QProcessManager::run()
     315{
     316#if defined (QPROCESS_DEBUG)
     317    qDebug() << "QProcessManager::run() BEGIN";
     318#endif
     319
     320    // Note: the rationale behind using a worker thread so far is that
     321    // calling complex functions from a signal handler is not really a good
     322    // idea unless there is a 100% guarantee that they are reentrant. So, the
     323    // handler only posts a semaphore (I *hope* DosPostEventSem is reentrant)
     324    // and all complex work is done here asynchronously.
     325
     326    QMutexLocker locker(&mutex);
     327    APIRET arc;
     328
     329    do {
     330        locker.unlock();
     331        arc = DosWaitEventSem(eventSem, SEM_INDEFINITE_WAIT);
     332        locker.relock();
     333        if (finish)
     334            break;
     335
     336#if defined (QPROCESS_DEBUG)
     337    qDebug() << "QProcessManager::run() signaled";
     338#endif
     339        foreach (QProcess *proc, processes) {
     340            QMetaObject::invokeMethod(proc, "_q_processDied", Qt::QueuedConnection);
     341        }
     342
     343    } while (true);
     344
     345#if defined (QPROCESS_DEBUG)
     346    qDebug() << "QProcessManager::run() END";
     347#endif
     348}
     349
     350
     351void QProcessPrivate::init()
     352{
     353    QProcessManager::addRef();
     354}
     355
     356void QProcessPrivate::uninit()
     357{
     358    QProcessManager::release();
    124359}
    125360
     
    372607        return;
    373608
     609    QProcessManager::addProcess(q);
     610
    374611    // Start the process (platform dependent)
    375612    q->setProcessState(QProcess::Starting);
     
    424661                                       .arg(qPrintable(qt_error_string(errno))));
    425662        emit q->error(processError);
     663        removeProcess(q);
    426664        cleanup();
    427665        return;
     
    452690        ::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);
    453691
    454     q->setProcessState(QProcess::Running);
    455     // User can call kill()/terminate() from the stateChanged() slot
    456     // so check before proceeding
    457     if (!pid)
    458         return;
    459 
    460692    // give the process a chance to start ...
    461693    DosSleep(100);
     694
    462695    _q_startupNotification();
    463696}
     
    465698bool QProcessPrivate::processStarted()
    466699{
    467     return processState == QProcess::Running;
     700    // we don't actually wait for any notification from the child process
     701    // assuming it has been started as long as spawnvpe() returns success
     702    return processState == QProcess::Starting;
    468703}
    469704
     
    589824    Q_Q(QProcess);
    590825
    591     if (processStarted())
     826    if (processState == QProcess::Running)
    592827        return true;
    593828
     
    8201055void QProcessPrivate::findExitCode()
    8211056{
     1057    // note: this method is unconditionally called from QProcess destructor
     1058    // to make sure the process manager removes the watcher even in such a rare
     1059    // case when the process is still running after killing it and waiting
     1060    // for termination (in which case the child termination code path that
     1061    // normally calls findExitCode() won't be walked)
     1062
     1063    Q_Q(QProcess);
     1064    QProcessManager::removeProcess(q);
    8221065}
    8231066
     
    8381081#if defined QPROCESS_DEBUG
    8391082    qDebug() << "QProcessPrivate::waitForDeadChild() not dead!";
     1083    if (waitResult == -1)
     1084        qDebug() << "QProcessPrivate::waitForDeadChild()" << strerror(errno);
    8401085#endif
    8411086    return false;
  • trunk/src/corelib/io/qprocess_p.h

    r72 r190  
    195195    qint64 pipeWriterBytesToWrite() const;
    196196#endif
     197#ifdef Q_OS_OS2
     198    void init();
     199    void uninit();
     200#endif
    197201
    198202    static bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(),
Note: See TracChangeset for help on using the changeset viewer.