Qt on Windows, Problems with QProcess

Leonardo Vainsencher lev9 at 013.net
Sun Feb 27 23:26:53 CET 2005


Hi.
I had the same problem reported by Joe (missing process output), using QT 3.2.3
port for cygwin. The tail end of child process output is missing almost everytime.
If the child process produces lots of output, only the last part is missing.
After several experiments with qprocess.cpp and qprocess_unix.cpp, I made a change
that has been working consistently: I replaced socketpair() calls with pipe() calls
in qprocess_unix.cpp. I've been using my patched QT version for several weeks without
any problems. I ran rudimentary regression tests, based on "process" example.
Those tests actually fail occasionally, but always due to problems with fork(), never
because of missing output.
Attached below is the qprocess_unix.cpp patch, and a modified version of "process"
example, which launches many simultaneous processes (to further stress the system)
and manages their outputs.
I don't really know who could help fixing the real problems: child process socket
missing output (which could be a cygwin/win32 issue, not a QT problem), and fork().
Any suggestions?
-leo

On 2005-02-06 1:26:39,  Ralf Habacker wrote:
>On Thursday 13 January 2005 18:51, Joe Mervini wrote:
>> Hello,
>>
>> I have been posting questions to the qt-interest list regarding a
>> problem that I have been having with qprocess.  Come to find out that
>> I've been posting to the wrong list!!!
>>
>> The problem that I am having is I have an application that uses
>> qprocess. When I compiled Qt using MinGW (gcc/g++) initially I got gdb
>> errors in libkernel32. Subsequently I downloaded the more current
>> snapshots from http://webdev.cegit.de and was able to recompile.
>> However, although I am not getting the gdb errors anymore, when I run
>> the process.exe (from the examples files) the program runs but I do not
>> get any text display in the window. Is any on this list running Qt
>> without the cygwin environment that has encountered this problem?
>>
> There is now initial io redirection support available. process should now show 
> the output (stdout, stderrr) from the called application. Redirecting stdin 
> to the called application does not work yet. 
>
> Regards
>  Ralf 

=================== qt-3.2.3-2a-src.patch ===================

diff -r -u0 qt-x11-free-3.2.3/config.cygwin-thread ../qt-x11-free-3.2.3/config.cygwin-thread
--- qt-x11-free-3.2.3/config.cygwin-thread 2005-02-17 20:30:37.078125000 -0800
+++ ../qt-x11-free-3.2.3/config.cygwin-thread 2005-02-17 20:31:05.250000000 -0800
@@ -9 +9 @@
-./configure -platform cygwin-g++-2 -fast -plugin-imgfmt-mng -thread -no-stl -system-zlib -qt-gif -system-libpng -system-libjpeg -qt-libmng -no-g++-exceptions -no-xft -no-xkb -disable-opengl $1 $2 $3
+./configure -platform cygwin-g++-2 -fast -plugin-imgfmt-mng -thread -system-zlib -qt-gif -system-libpng -system-libjpeg -qt-libmng -no-g++-exceptions -no-xft -no-xkb -disable-opengl $1 $2 $3
diff -r -u0 qt-x11-free-3.2.3/mkspecs/cygwin-g++-2/qmake.conf ../qt-x11-free-3.2.3/mkspecs/cygwin-g++-2/qmake.conf
--- qt-x11-free-3.2.3/mkspecs/cygwin-g++-2/qmake.conf 2005-02-17 20:30:37.078125000 -0800
+++ ../qt-x11-free-3.2.3/mkspecs/cygwin-g++-2/qmake.conf 2005-02-17 20:31:05.250000000 -0800
@@ -37,2 +37,2 @@
-QMAKE_INCDIR  = -I/usr/local/include
-QMAKE_LIBDIR  = -L/usr/local/lib
+QMAKE_INCDIR  =
+QMAKE_LIBDIR  =
@@ -60 +60 @@
-QMAKE_LIBS_X11  =  -lGL -lXext -lX11
+QMAKE_LIBS_X11  = -lGL -lXext -lX11
@@ -77,0 +78,2 @@
+QMAKE_COPY_FILE         = $(COPY)
+QMAKE_COPY_DIR          = $(COPY) -r
@@ -80,0 +83,4 @@
+QMAKE_STRIP             = strip
+QMAKE_STRIPFLAGS_LIB += --strip-unneeded
+QMAKE_CHK_DIR_EXISTS = test -d
+QMAKE_MKDIR  = mkdir -p
diff -r -u0 qt-x11-free-3.2.3/src/kernel/qprocess_unix.cpp ../qt-x11-free-3.2.3/src/kernel/qprocess_unix.cpp
--- qt-x11-free-3.2.3/src/kernel/qprocess_unix.cpp 2003-11-10 02:21:57.000000000 -0800
+++ ../qt-x11-free-3.2.3/src/kernel/qprocess_unix.cpp 2005-02-17 14:04:12.531250000 -0800
@@ -699,2 +699,5 @@
-#ifndef Q_OS_QNX6
-    if ( (comms & Stdin) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdin ) == -1 ) {
+    if ( (comms & Stdin) &&
+#if defined(Q_OS_CYGWIN)
+ ::pipe( sStdin ) == -1
+#elif defined(Q_OS_QNX6)
+ qnx6SocketPairReplacement(sStdin) == -1
@@ -702 +705 @@
-    if ( (comms & Stdin) && qnx6SocketPairReplacement(sStdin) == -1 ) {
+ ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdin ) == -1
@@ -703,0 +707 @@
+       ) {
@@ -706,2 +710,6 @@
-#ifndef Q_OS_QNX6
-    if ( (comms & Stderr) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStderr ) == -1 ) {
+
+    if ( (comms & Stderr) &&
+#if defined(Q_OS_CYGWIN)
+ ::pipe( sStderr ) == -1
+#elif defined(Q_OS_QNX6)
+ qnx6SocketPairReplacement(sStderr) == -1
@@ -709 +717 @@
-    if ( (comms & Stderr) && qnx6SocketPairReplacement(sStderr) == -1 ) {
+ ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStderr ) == -1
@@ -710,0 +719 @@
+       ) {
@@ -713,2 +722,6 @@
-#ifndef Q_OS_QNX6
-    if ( (comms & Stdout) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdout ) == -1 ) {
+
+    if ( (comms & Stdout) &&
+#if defined(Q_OS_CYGWIN)
+ ::pipe( sStdout ) == -1
+#elif defined(Q_OS_QNX6)
+ qnx6SocketPairReplacement(sStdout) == -1
@@ -716 +729 @@
-    if ( (comms & Stdout) && qnx6SocketPairReplacement(sStdout) == -1 ) {
+ ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdout ) == -1
@@ -717,0 +731 @@
+       ) {
@@ -922,2 +936,2 @@
- int originalFlags = fcntl(d->proc->socketStdin, F_GETFL, 0);
- fcntl(d->proc->socketStdin, F_SETFL, originalFlags | O_NONBLOCK);
+ int originalFlags = ::fcntl(d->proc->socketStdin, F_GETFL, 0);
+ ::fcntl(d->proc->socketStdin, F_SETFL, originalFlags | O_NONBLOCK);
@@ -1261 +1275 @@
- qDebug( "QProcess::socketRead(): %d bytes read from stdout (%d)",
+ qDebug( "QProcess::socketRead(): %ld bytes read from stdout (%d)",
@@ -1267 +1281 @@
- qDebug( "QProcess::socketRead(): %d bytes read from stderr (%d)",
+ qDebug( "QProcess::socketRead(): %ld bytes read from stderr (%d)",

=================== process.cpp ===================

/****************************************************************************
** $Id: qt/process.cpp   3.2.3   edited May 13 09:08 $
**
** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
**
** This file is part of an example program for Qt.  This example
** program may be used, distributed and modified without limitation.
**
** Modified by LV 2005-01-31
**
** Launch "find" to create a list of directories.
** For each directory in list, launch "find" to list files in
** the directory. Multiple "find" processes are launched
** asynchronously (in "parallel"), based on numProcs parameter.
** File lists returned by child processes are sent to stderr,
** tagged on the left with "n>> ", where n is the process slot number.
** Parameter numProcs determines the number of slots available.
*****************************************************************************/

#include <qapplication.h>
#include <qvbox.h>
#include <qsplitter.h>
#include <qtextview.h>
#include <qpushbutton.h>
#include <qmessagebox.h>
#include <qprocess.h>
#include <qtimer.h>

#include <stdlib.h>
#include <assert.h>
#include <iostream>
#include <vector>

using namespace std;

// helper class
class pxtra {
    public:
 pxtra(void) {
     proc = NULL;
     busy = false;
     ndirs = nlines = 0;
 }
 QProcess* proc;
 bool busy;
 int ndirs;
 int nlines;
};

class UicManager : public QVBox
{
    Q_OBJECT
public:
    UicManager(int,char**);
    ~UicManager(void) {}
private slots:
    void readFromStdout(void);
    void readFromStderr(void);
    void procExited(void);
private:
    void start(unsigned);
    void rdstdout(unsigned);
    void rdstderr(unsigned);
    unsigned p2xtra(const QObject*) const;
private:
    const unsigned numProcs;
    QSplitter* splitter;
    QTextView* output;
    QTextView* error;
    QPushButton* quitButton;
    QStringList dirs;
    bool dirListReady;
    vector<pxtra> procs;
};

UicManager::UicManager(int argc, char** argv) : numProcs(4)
{
    QProcess* p;
    unsigned k;
    dirListReady = false;

    splitter = new QSplitter(QSplitter::Vertical,this);
    output = new QTextView(splitter);
    error = new QTextView(splitter);
    quitButton = new QPushButton(tr("Quit"),this);
    connect(quitButton,SIGNAL(clicked()),qApp,SLOT(quit()));
    resize(640,500);

    procs.resize(numProcs);

    for (k=0 ; k < procs.size() ; k++) {
 p = procs[k].proc = new QProcess(this);
 p->setCommunication(QProcess::Stdout | QProcess::Stderr);
 connect(p,SIGNAL(readyReadStdout()),this,SLOT(readFromStdout()));
 connect(p,SIGNAL(readyReadStderr()),this,SLOT(readFromStderr()));
 connect(p,SIGNAL(processExited()),this,SLOT(procExited()));
    }
// launch initial process to build list of dir paths
    p = procs[0].proc;
    p->addArgument("find");
    for (k=1 ; k < (unsigned)argc ; k++)
 p->addArgument(argv[k]);
    p->addArgument("-type");
    p->addArgument("d");
    start(0);
}

void UicManager::start(unsigned k)
{
    if (!procs[k].proc->start()) {
 QMessageBox::critical(0,
  tr("Fatal error"),
  tr("Could not start process."),
  tr("Quit"));
 exit(-1);
    }
    procs[k].busy = true;
}

unsigned UicManager::p2xtra(const QObject* pob) const
{
    unsigned k;
    for (k=0 ; k < procs.size() ; k++)
 if (procs[k].proc==pob) return k;
    qWarning(QString().sprintf("can't find proc 0x%08x in proc array[%u]:",
  (unsigned)pob,procs.size()));
    for (k=0 ; k < procs.size() ; k++) {
 QProcess* p = procs[k].proc;
 qDebug(QString().sprintf("  proc %u: 0x%08x",k,unsigned(p)));
    }
    assert(false);
    exit(-1);
    return 0;
}

void UicManager::readFromStderr(void)
{
    const QObject* pob = sender();
    unsigned k = p2xtra(pob);
    rdstderr(k);
}

void UicManager::readFromStdout(void)
{
    const QObject* pob = sender();
    unsigned k = p2xtra(pob);
    rdstdout(k);
}

void UicManager::rdstderr(unsigned k)
{
    QProcess* p = procs[k].proc;
    QString text,sn;
    sn.setNum(k);
    sn += ">> ";
    while (p->canReadLineStderr()) {
 QString text(p->readLineStderr());
 error->append(sn+text+"\n");
    }
}

void UicManager::rdstdout(unsigned k)
{
    QProcess* p = procs[k].proc;
    QString text,sn;
    sn.setNum(k);
    sn += ">> ";
    while (p->canReadLineStdout()) {
 QString text(p->readLineStdout());
 if (dirListReady) {
     cout << sn << text << endl;
     procs[k].nlines++;
 } else {
     output->append(sn+text+" :::\n");
     dirs += text;
 }
    }
}

void UicManager::procExited(void)
{
    const QObject* pob = sender();
    unsigned k = p2xtra(pob);
    rdstdout(k);
    if (!dirListReady)
 error->append(QString().sprintf("%d dirs\n",dirs.size()));
    dirListReady = true;
    procs[k].busy = false;
    if (dirs.size() < 1) {
 // finished processing all dirs
 // wait if any proc still running
 for (k=0 ; k < procs.size() ; k++)
     if (procs[k].busy) return;
 int totdirs=0, totfiles=0;
 QString text;
 for (k=0 ; k < procs.size() ; k++) {
     text.sprintf("proc %2d processed %4d dirs, %4d files\n",
      k,procs[k].ndirs,procs[k].nlines);
     error->append(text);
     totdirs += procs[k].ndirs;
     totfiles += procs[k].nlines;
 }
 error->append(QString().sprintf("%d dirs %d files\n",
      totdirs,totfiles));
 // bye-bye in 5 secs
 QTimer::singleShot(5000,qApp,SLOT(quit()));
 error->append("exiting in 5 sec...\n");
 return;
    }
    // launch as many processes as possible
    for (k=0 ; k < procs.size() && dirs.size() ; k++) {
 if (procs[k].busy) continue;
 QProcess* p = procs[k].proc;
 QString sn;
 sn.setNum(k);
 sn += ">> ";
 p->clearArguments();
 p->addArgument("find");
 p->addArgument(dirs.first());
 p->addArgument("-maxdepth"); p->addArgument("1");
 p->addArgument("-type"); p->addArgument("f");
 procs[k].ndirs++;
 cout << sn << dirs.first() << " :::\n";
 dirs.pop_front();
 start(k);
    }
}

int main(int argc, char **argv)
{
    QApplication a(argc,argv);
    UicManager manager(argc,argv);
    a.setMainWidget(&manager);
    manager.show();
    return a.exec();
}

#include "process.moc"


More information about the kde-cygwin mailing list