Changeset 197 for trunk/src/corelib
- Timestamp:
- Oct 3, 2009, 2:34:36 AM (16 years ago)
- Location:
- trunk/src/corelib/io
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/corelib/io/qprocess.cpp
r190 r197 421 421 notifier = 0; 422 422 pipeWriter = 0; 423 423 424 childStartedPipe[0] = INVALID_Q_PIPE; 424 425 childStartedPipe[1] = INVALID_Q_PIPE; 425 426 deathPipe[0] = INVALID_Q_PIPE; 426 427 deathPipe[1] = INVALID_Q_PIPE; 428 427 429 exitCode = 0; 428 430 crashed = false; … … 510 512 destroyPipe(stderrChannel.pipe); 511 513 destroyPipe(stdinChannel.pipe); 514 512 515 destroyPipe(childStartedPipe); 513 516 destroyPipe(deathPipe); 517 514 518 #ifdef Q_OS_UNIX 515 519 serial = 0; -
trunk/src/corelib/io/qprocess_os2.cpp
r195 r197 97 97 #include <qfile.h> 98 98 #include <qfileinfo.h> 99 #include <qlist.h> 100 #include <qmap.h> 99 #include <qhash.h> 101 100 #include <qmutex.h> 102 #include <qsemaphore.h>103 #include <qsocketnotifier.h>104 101 #include <qdir.h> 105 102 106 # include <errno.h>107 # include <sys/filio.h>108 # include <sys/socket.h> // socketpair103 # 104 # 105 # 109 106 110 107 QT_BEGIN_NAMESPACE 111 108 112 static void qt_create_pipe(int *pipe) 113 { 114 if (pipe[0] != -1) 115 ::close(pipe[0]); 116 if (pipe[1] != -1) 117 ::close(pipe[1]); 118 // Note: with kLIBC 0.6.3 and earlier (GCC 3.3.5 CSD3), fds created with 119 // pipe() cannot be used in select(), so use socketpair() instead 120 if (::socketpair(AF_OS2, SOCK_STREAM, 0, pipe) != 0) { 121 qWarning("QProcessPrivate::createPipe: ::socketpair() failed with '%s'", 122 qPrintable(qt_error_string(errno))); 123 } 124 ::fcntl(pipe[0], F_SETFD, FD_CLOEXEC); 125 ::fcntl(pipe[1], F_SETFD, FD_CLOEXEC); 126 } 127 109 enum 110 { 111 PIPE_SIZE_STDIN = 65520, // max 112 PIPE_SIZE_STDOUT = 65520, // max 113 PIPE_SIZE_STDERR = 4096, 114 }; 128 115 129 116 class QProcessManager : public QThread 130 117 { 131 118 public: 119 120 132 121 133 122 static void addRef(); 134 123 static void release(); 135 124 136 static void addProcess(QProcess *process); 137 static void removeProcess(QProcess *process); 125 static USHORT addProcess(QProcess *process); 126 static void removeProcess(USHORT procKey); 127 128 static QMutex *mutex() { return &mtx; } 138 129 139 130 private: … … 151 142 HEV eventSem; 152 143 QAtomicInt eventSemGuard; 153 154 typedef QList<QProcess *> ProcessList; 144 QAtomicInt deathFlag; 145 146 typedef QHash<USHORT, QProcess *> ProcessList; 155 147 ProcessList processes; 148 156 149 157 150 void (*sa_old_sigchld_handler)(int); … … 159 152 static void sa_sigchld_handler(int signum); 160 153 154 155 156 157 158 159 160 161 162 161 163 static QProcessManager *instance; 162 static QMutex m utex;164 static QMutex mx; 163 165 }; 164 166 165 167 // static 166 168 QProcessManager *QProcessManager::instance = 0; 167 QMutex QProcessManager::m utex;169 QMutex QProcessManager::mx; 168 170 169 171 // static … … 184 186 return; 185 187 186 APIRET rc = DosPostEventSem(instance->eventSem); 187 Q_ASSERT(rc == NO_ERROR); 188 // set deathEvent to 1 and post the semaphore if not already done so 189 if (instance->deathFlag.testAndSetRelaxed(0, 1)) { 190 APIRET rc = DosPostEventSem(instance->eventSem); 191 Q_ASSERT(rc == NO_ERROR || rc == ERROR_ALREADY_POSTED); 192 } 188 193 189 194 instance->eventSemGuard.testAndSetRelease(2, 1); … … 197 202 void QProcessManager::addRef() 198 203 { 199 QMutexLocker locker( &mutex);204 QMutexLocker locker(); 200 205 201 206 if (instance == 0) { … … 209 214 void QProcessManager::release() 210 215 { 211 QMutexLocker locker( &mutex);216 QMutexLocker locker(); 212 217 213 218 Q_ASSERT(instance); … … 234 239 235 240 // static 236 voidQProcessManager::addProcess(QProcess *process)237 { 238 #if defined (QPROCESS_DEBUG) 239 qDebug( ) << "QProcessManager::addProcess()";240 #endif 241 242 QMutexLocker locker( &mutex);241 QProcessManager::addProcess(QProcess *process) 242 { 243 #if defined (QPROCESS_DEBUG) 244 qDebug(; 245 #endif 246 247 QMutexLocker locker(); 243 248 Q_ASSERT(instance); 244 249 … … 249 254 } 250 255 251 instance->processes.append(process); 256 USHORT procKey = instance->lastProcKey + 1; 257 if (procKey > MaxProcKey) { 258 // limit reached, find an unused number 259 procKey = 0; 260 while (++procKey <= MaxProcKey && 261 instance->processes.contains(procKey)); 262 Q_ASSERT(procKey <= MaxProcKey); 263 if (procKey > MaxProcKey) { 264 // oops, no more free keys! 265 process->setErrorString(QString("Internal error: Too many processes")); 266 return InvalidProcKey; 267 } 268 } else { 269 instance->lastProcKey = procKey; 270 } 271 272 // attach the semahpore to the pipes of the process 273 APIRET arc = NO_ERROR; 274 QProcessPrivate *d = process->d_func(); 275 if (d->stdinChannel.pipe.server != HPIPE(~0)) { 276 arc = DosSetNPipeSem(d->stdinChannel.pipe.server, (HSEM)instance->eventSem, 277 toPipeKey(procKey, QProcessPrivate::InPipe)); 278 } 279 if (arc == NO_ERROR && d->stdoutChannel.pipe.server != HPIPE(~0)) { 280 arc = DosSetNPipeSem(d->stdoutChannel.pipe.server, (HSEM)instance->eventSem, 281 toPipeKey(procKey, QProcessPrivate::OutPipe)); 282 } 283 if (arc == NO_ERROR && d->stderrChannel.pipe.server != HPIPE(~0)) { 284 arc = DosSetNPipeSem(d->stderrChannel.pipe.server, (HSEM)instance->eventSem, 285 toPipeKey(procKey, QProcessPrivate::ErrPipe)); 286 } 287 if (arc != NO_ERROR) { 288 process->setErrorString(QProcess::tr("Internal error: DOS error %1") 289 .arg(arc)); 290 if (procKey == instance->lastProcKey) 291 --instance->lastProcKey; 292 return InvalidProcKey; 293 } 294 295 instance->processes[procKey] = process; 296 297 return procKey; 252 298 } 253 299 254 300 // static 255 void QProcessManager::removeProcess(QProcess *process) 256 { 257 #if defined (QPROCESS_DEBUG) 258 qDebug() << "QProcessManager::removeProcess()"; 259 #endif 260 261 QMutexLocker locker(&mutex); 301 void QProcessManager::removeProcess(USHORT procKey) 302 { 303 QMutexLocker locker(mutex()); 262 304 Q_ASSERT(instance); 263 305 264 instance->processes.removeAll(process); 306 Q_ASSERT(instance->processes.contains(procKey)); 307 QProcess *process = instance->processes[procKey]; 308 309 #if defined (QPROCESS_DEBUG) 310 qDebug("QProcessManager::removeProcess(%p)", process); 311 #endif 312 313 // to guarantee that the given procKey may be reused, we must close all 314 // pipes in order to ensure that we won't get late NPSS_CLOSE for the 315 // removed process that may be already associated with a different process 316 QProcessPrivate *d = process->d_func(); 317 d->destroyPipe(d->stdinChannel.pipe); 318 d->destroyPipe(d->stdoutChannel.pipe); 319 d->destroyPipe(d->stderrChannel.pipe); 320 321 instance->processes.remove(procKey); 322 323 // small optimization: released the highest key 324 if (procKey == instance->lastProcKey) 325 --instance->lastProcKey; 265 326 } 266 327 … … 272 333 #endif 273 334 274 APIRET rc = DosCreateEventSem(NULL, &eventSem, DCE_AUTORESET | DCE_POSTONE, FALSE); 335 APIRET rc = DosCreateEventSem(NULL, &eventSem, 336 DC_SEM_SHARED | DCE_AUTORESET | DCE_POSTONE, 337 FALSE); 275 338 Q_ASSERT(rc == NO_ERROR); 339 340 276 341 } 277 342 … … 327 392 // and all complex work is done here asynchronously. 328 393 329 QMutexLocker locker( &mutex);394 QMutexLocker locker(); 330 395 APIRET arc; 396 397 398 331 399 332 400 do { … … 334 402 qDosNI(arc = DosWaitEventSem(eventSem, SEM_INDEFINITE_WAIT)); 335 403 locker.relock(); 404 336 405 if (finish) 337 406 break; 338 407 339 #if defined (QPROCESS_DEBUG) 340 qDebug() << "QProcessManager::run() signaled"; 341 #endif 342 foreach (QProcess *proc, processes) { 408 if (instance->deathFlag.testAndSetRelaxed(1, 0)) { 409 #if defined (QPROCESS_DEBUG) 410 qDebug() << "QProcessManager::run(): child death signaled"; 411 #endif 412 foreach (QProcess *proc, processes) { 413 QProcessPrivate::WaitMode mode = (QProcessPrivate::WaitMode) 414 proc->d_func()->waitMode.fetchAndStoreRelaxed(QProcessPrivate::SigChild); 415 switch (mode) { 416 case QProcessPrivate::Semaphore: 417 DosPostEventSem(proc->d_func()->waitSem); 418 break; 419 case QProcessPrivate::Async: 420 case QProcessPrivate::SigChild: 421 QMetaObject::invokeMethod(proc, "_q_processDied", Qt::QueuedConnection); 422 break; 423 } 424 } 425 } 426 427 #if defined (QPROCESS_DEBUG) 428 qDebug() << "QProcessManager::run(): checking pipes"; 429 #endif 430 431 // make sure the state array is big enough. The best size for sz pipes 432 // is sz * 2 (to be able to store both NPSS_RDATA/NPSS_WSPACE and 433 // NPSS_CLOSE for every pipe) + one for NPSS_EOI 434 int bestSize = processes.size() * 3 * 2 + 1; 435 if (pipeStates.size() < bestSize) 436 pipeStates.resize(bestSize); 437 438 arc = DosQueryNPipeSemState((HSEM)eventSem, pipeStates.data(), 439 pipeStates.size() * sizeof(PIPESEMSTATE)); 440 if (arc != NO_ERROR) { 441 qWarning("QProcessManager::run: DosQueryNPipeSemState returned %lu", arc); 442 continue; 443 } 444 445 // In the returned information array, CLOSE and READ records for the 446 // same pipe key may be mixed. We need CLOSE messages to be posted after 447 // READ messages, so we do two passes. 448 449 int pass = 0; 450 for (int i = 0; pass < 2; ++i) { 451 BYTE status = pipeStates[i].fStatus; 452 if (status == NPSS_EOI) { 453 ++ pass; 454 i = -1; 455 continue; 456 } 457 if (pass == 0 && status != NPSS_RDATA && status != NPSS_WSPACE) 458 continue; 459 if (pass == 1 && status != NPSS_CLOSE) 460 continue; 461 #if defined(QPROCESS_DEBUG) 462 qDebug(" %d/%d: fStatus %u fFlag f%02X usKey %04hX usAvail %hu", 463 pass, i, (uint) pipeStates[i].fStatus, 464 (uint) pipeStates[i].fFlag, pipeStates[i].usKey, 465 pipeStates[i].usAvail); 466 #endif 467 int procKey = toProcKey(pipeStates[i].usKey); 468 QProcessPrivate::PipeType type = toPipeType(pipeStates[i].usKey); 469 470 QProcess *proc = processes[procKey]; 471 Q_ASSERT(proc); 472 #if defined(QPROCESS_DEBUG) 473 qDebug(" process %p", proc); 474 #endif 475 QProcessPrivate *d = proc->d_func(); 476 477 if (status == NPSS_CLOSE) { 478 // nothing to do but notify the object; it should close the 479 // pipe if it sees that it has been notified but there is no 480 // data to read, or if it fails to write to a (closed) pipe 481 } else { 482 d->pipeData[type].bytes = pipeStates[i].usAvail; 483 } 484 485 if (d->pipeData[type].isNew) { 486 // the object didn't process the previous notification yet; 487 // there is no point to send a new one 488 continue; 489 } 490 491 d->pipeData[type].isNew = true; 492 493 // signal the process object 343 494 QProcessPrivate::WaitMode mode = (QProcessPrivate::WaitMode) 344 proc->d_func()->waitMode.fetchAndStore Acquire(QProcessPrivate::SigChild);495 proc->d_func()->waitMode.fetchAndStore(QProcessPrivate::SigChild); 345 496 switch (mode) { 346 case QProcessPrivate::Select:347 ::so_cancel(proc->d_func()->maxSelectFd);348 break;349 497 case QProcessPrivate::Semaphore: 350 498 DosPostEventSem(proc->d_func()->waitSem); … … 352 500 case QProcessPrivate::Async: 353 501 case QProcessPrivate::SigChild: 354 QMetaObject::invokeMethod(proc, "_q_processDied", Qt::QueuedConnection); 502 const char *method; 503 switch(type) { 504 case QProcessPrivate::InPipe: 505 Q_ASSERT(status == NPSS_CLOSE || status == NPSS_WSPACE); 506 method = "_q_canWrite"; 507 break; 508 case QProcessPrivate::OutPipe: 509 Q_ASSERT(status == NPSS_CLOSE || status == NPSS_RDATA); 510 method = "_q_canReadStandardOutput"; 511 break; 512 case QProcessPrivate::ErrPipe: 513 Q_ASSERT(status == NPSS_CLOSE || status == NPSS_RDATA); 514 method = "_q_canReadStandardError"; 515 break; 516 } 517 QMetaObject::invokeMethod(proc, method, Qt::QueuedConnection); 355 518 break; 356 519 } … … 367 530 { 368 531 waitMode = Async; 369 maxSelectFd = -1;370 532 waitSem = NULLHANDLE; 533 534 535 536 537 538 371 539 QProcessManager::addRef(); 372 540 } … … 375 543 { 376 544 QProcessManager::release(); 545 377 546 if (waitSem != NULLHANDLE) 378 547 DosCloseEventSem(waitSem); … … 387 556 } 388 557 389 void QProcessPrivate::destroyPipe(int *pipe) 390 { 391 if (pipe[1] != -1) { 392 ::close(pipe[1]); 393 pipe[1] = -1; 394 } 395 if (pipe[0] != -1) { 396 ::close(pipe[0]); 397 pipe[0] = -1; 558 bool QProcessPrivate::createPipe(PipeType type, Channel::Pipe &pipe) 559 { 560 APIRET rc; 561 char pathBuf[CCHMAXPATH]; 562 563 // we need the process identifier to guarantee pipe name unicity 564 PPIB ppib = NULL; 565 DosGetInfoBlocks(NULL, &ppib); 566 567 switch (type) { 568 case InPipe: 569 // create our end of the pipe 570 sprintf(pathBuf, "\\pipe\\Qt4\\%08lX\\QProcess\\%p\\Stdin", 571 ppib->pib_ulpid, this->q_func()); 572 rc = DosCreateNPipe(pathBuf, &pipe.server, 573 NP_ACCESS_OUTBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1, 574 PIPE_SIZE_STDIN, 0, 0); 575 if (rc == NO_ERROR) { 576 DosConnectNPipe(pipe.server); 577 // open the client end of the pipe 578 ULONG action = 0; 579 rc = DosOpen(pathBuf, &pipe.client, &action, 0, FILE_NORMAL, FILE_OPEN, 580 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYREADWRITE | 581 OPEN_FLAGS_NOINHERIT, (PEAOP2)NULL); 582 } 583 break; 584 case OutPipe: 585 case ErrPipe: 586 // create our end of the pipe 587 sprintf(pathBuf, "\\pipe\\Qt4\\%08lX\\QProcess\\%p\\%s", 588 ppib->pib_ulpid, this->q_func(), type == OutPipe ? "Stdout" : "Stderr"); 589 rc = DosCreateNPipe(pathBuf, &pipe.server, 590 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1, 591 0, type == OutPipe ? PIPE_SIZE_STDOUT : PIPE_SIZE_STDERR, 0); 592 if (rc == NO_ERROR) { 593 DosConnectNPipe(pipe.server); 594 // open the client end of the pipe 595 ULONG action = 0; 596 rc = DosOpen(pathBuf, &pipe.client, &action, 0, FILE_NORMAL, FILE_OPEN, 597 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE | 598 OPEN_FLAGS_NOINHERIT, (PEAOP2)NULL); 599 } 600 break; 601 } 602 603 if (rc != NO_ERROR) 604 qWarning("QProcessPrivate::createPipe: DosCreateNPipe or DosOpen " 605 "returned %lu for PipeType(%d)", rc, type); 606 607 return rc == NO_ERROR; 608 } 609 610 void QProcessPrivate::destroyPipe(Channel::Pipe &pipe) 611 { 612 if (pipe.client != ~HFILE(~0)) { 613 DosClose(pipe.client); 614 pipe.client = HFILE(~0); 615 } 616 if (pipe.server != HPIPE(~0)) { 617 DosDisConnectNPipe(pipe.server); 618 DosClose(pipe.server); 619 pipe.server = HPIPE(~0); 398 620 } 399 621 } … … 408 630 Q_Q(QProcess); 409 631 632 633 634 410 635 if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) { 411 channel.pipe[0] = -1;412 channel.pipe[1] = -1;413 636 return true; 414 637 } … … 416 639 if (channel.type == Channel::Normal) { 417 640 // we're piping this channel to our own process 418 qt_create_pipe(channel.pipe); 419 420 // create the socket notifiers 421 if (threadData->eventDispatcher) { 422 if (&channel == &stdinChannel) { 423 channel.notifier = new QSocketNotifier(channel.pipe[1], 424 QSocketNotifier::Write, q); 425 channel.notifier->setEnabled(false); 426 QObject::connect(channel.notifier, SIGNAL(activated(int)), 427 q, SLOT(_q_canWrite())); 428 } else { 429 channel.notifier = new QSocketNotifier(channel.pipe[0], 430 QSocketNotifier::Read, q); 431 const char *receiver; 432 if (&channel == &stdoutChannel) 433 receiver = SLOT(_q_canReadStandardOutput()); 434 else 435 receiver = SLOT(_q_canReadStandardError()); 436 QObject::connect(channel.notifier, SIGNAL(activated(int)), 437 q, receiver); 438 } 439 } 440 441 return true; 641 PipeType type; 642 if (&channel == &stdinChannel) 643 type = InPipe; 644 else if (&channel == &stdoutChannel) 645 type = OutPipe; 646 else if (&channel == &stderrChannel) 647 type = ErrPipe; 648 else 649 Q_ASSERT(false); 650 return createPipe(type, channel.pipe); 442 651 } else if (channel.type == Channel::Redirect) { 443 652 // we're redirecting the channel to/from a file 444 653 QByteArray fname = QFile::encodeName(channel.file); 445 654 655 446 656 if (&channel == &stdinChannel) { 447 657 // try to open in read-only mode 448 channel.pipe[1] = -1; 449 if ( (channel.pipe[0] = ::open(fname, O_RDONLY)) != -1) 450 return true; // success 658 ULONG action = 0; 659 rc = DosOpen(fname, &channel.pipe.client, &action, 0, FILE_NORMAL, FILE_OPEN, 660 OPEN_ACCESS_READONLY | OPEN_FLAGS_NOINHERIT, (PEAOP2)NULL); 661 662 if (rc == NO_ERROR) 663 return true; // success 451 664 452 665 q->setErrorString(QProcess::tr("Could not open input redirection for reading")); 453 666 } else { 454 int mode = O_WRONLY | O_CREAT;667 int mode = ; 455 668 if (channel.append) 456 mode |= O_APPEND;669 mode |= ; 457 670 else 458 mode |= O_TRUNC; 459 460 channel.pipe[0] = -1; 461 if ( (channel.pipe[1] = ::open(fname, mode, 0666)) != -1) 671 mode |= FILE_TRUNCATE; 672 ULONG action = 0; 673 rc = DosOpen(fname, &channel.pipe.client, &action, 0, FILE_NORMAL, mode, 674 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYWRITE | 675 OPEN_FLAGS_NOINHERIT, (PEAOP2)NULL); 676 677 if (rc == NO_ERROR && channel.append) { 678 ULONG actual = 0; 679 rc = DosSetFilePtr(channel.pipe.client, 0, FILE_END, &actual); 680 } 681 if (rc == NO_ERROR) 462 682 return true; // success 463 683 … … 492 712 } 493 713 494 if (source->pipe [1] != INVALID_Q_PIPE || sink->pipe[0] != INVALID_Q_PIPE) {714 if (source->pipe) { 495 715 // already created, do nothing 716 717 496 718 return true; 497 719 } else { 498 Q_ASSERT(source->pipe [0] == INVALID_Q_PIPE && source->pipe[1] == INVALID_Q_PIPE);499 Q_ASSERT(sink->pipe [0] == INVALID_Q_PIPE && sink->pipe[1] == INVALID_Q_PIPE);500 501 Q_PIPE pipe[2] = { -1, -1 };502 qt_create_pipe(pipe);503 sink->pipe [0] = pipe[0];504 source->pipe [1] = pipe[1];720 Q_ASSERT(source->pipe); 721 Q_ASSERT(sink->pipe); 722 723 ; 724 pipe); 725 sink->pipe; 726 source->pipe; 505 727 506 728 return true; … … 635 857 return; 636 858 637 QProcessManager::addProcess(q); 859 procKey = QProcessManager::addProcess(q); 860 861 if (procKey == QProcessManager::InvalidProcKey) { 862 // setErrorString() is called inside addProcess() 863 Q_ASSERT(!q->errorString().isEmpty()); 864 processError = QProcess::FailedToStart; 865 emit q->error(processError); 866 cleanup(); 867 return; 868 } 638 869 639 870 // Start the process (platform dependent) 871 640 872 q->setProcessState(QProcess::Starting); 641 873 642 // save & copy the stdin socket 643 int stdin_copy = ::dup(fileno(stdin)); 644 ::dup2(stdinChannel.pipe[0], fileno(stdin)); 645 646 // save & copy the stdout and stderr if asked to 647 int stdout_copy = -1, stderr_copy = -1; 648 if (processChannelMode != QProcess::ForwardedChannels) { 649 stdout_copy = ::dup(fileno(stdout)); 650 ::dup2(stdoutChannel.pipe[1], fileno(stdout)); 651 652 // merge stdout and stderr if asked to 653 stderr_copy = ::dup(fileno(stderr)); 654 if (processChannelMode == QProcess::MergedChannels) { 655 ::dup2(fileno(stdout), fileno(stderr)); 656 } else { 874 APIRET arc; 875 876 QString error; 877 HFILE tmpStdin = HFILE(~0), tmpStdout = HFILE(~0), tmpStderr = HFILE(~0); 878 HFILE realStdin = HF_STDIN, realStdout = HF_STDOUT, realStderr = HF_STDERR; 879 880 do { 881 // save & copy the stdin handle 882 if ((arc = DosDupHandle(realStdin, &tmpStdin)) == NO_ERROR) { 883 arc = DosDupHandle(stdinChannel.pipe.client, &realStdin); 884 } 885 if (arc != NO_ERROR) { 886 #if defined (QPROCESS_DEBUG) 887 qDebug("QProcessPrivate::startProcess: DosDupHandle for STDIN " 888 "returned %lu", arc); 889 #endif 890 break; 891 } 892 // save & copy the stdout and stderr handles if asked to 893 if (processChannelMode != QProcess::ForwardedChannels) { 894 // save & copy the stdout handle 895 if ((arc = DosDupHandle(realStdout, &tmpStdout)) == NO_ERROR) { 896 arc = DosDupHandle(stdoutChannel.pipe.client, &realStdout); 897 } 898 if (arc != NO_ERROR) { 899 #if defined (QPROCESS_DEBUG) 900 qDebug("QProcessPrivate::startProcess: DosDupHandle for STDOUT " 901 "returned %lu", arc); 902 #endif 903 break; 904 } 905 // qDebug() uses STDERR so only redirect if !QPROCESS_DEBUG 657 906 #if !defined (QPROCESS_DEBUG) 658 // don't redirect it as qDebug() uses it 659 ::dup2(stderrChannel.pipe[1], fileno(stderr)); 660 #endif 661 } 662 } 663 664 int pid = qt_startProcess(program, arguments, workingDirectory, 907 if ((arc = DosDupHandle(realStderr, &tmpStderr)) == NO_ERROR) { 908 // merge stdout and stderr if asked to 909 if (processChannelMode == QProcess::MergedChannels) { 910 arc = DosDupHandle(stdoutChannel.pipe.client, &realStderr); 911 } else { 912 arc = DosDupHandle(stderrChannel.pipe.client, &realStderr); 913 } 914 } 915 if (arc != NO_ERROR) 916 break; 917 #endif 918 } 919 920 } while (false); 921 922 int pid = -1; 923 if (arc == NO_ERROR) 924 pid = qt_startProcess(program, arguments, workingDirectory, 665 925 &environment); 666 926 667 // restore stdin/stdout/stderr668 if ( stdin_copy != -1) {669 ::dup2(stdin_copy, fileno(stdin));670 ::close(stdin_copy);671 } 672 if ( stdout_copy != -1) {673 ::dup2(stdout_copy, fileno(stdout));674 ::close(stdout_copy);675 } 676 if ( stderr_copy != -1) {677 ::dup2(stderr_copy, fileno(stderr));678 ::close(stderr_copy);679 } 680 681 if ( pid == -1) {927 // 928 if () { 929 ); 930 ); 931 } 932 if () { 933 ); 934 ); 935 } 936 if () { 937 ); 938 ); 939 } 940 941 if (pid == -1) { 682 942 // Cleanup, report error and return 683 #if defined (QPROCESS_DEBUG)684 qDebug("spawnvpe failed: %s", qPrintable(qt_error_string(errno)));685 #endif686 943 q->setProcessState(QProcess::NotRunning); 687 944 processError = QProcess::FailedToStart; 688 q->setErrorString(QProcess::tr("Process failed to start: %1") 689 .arg(qPrintable(qt_error_string(errno)))); 945 if (arc != NO_ERROR) { 946 // handle duplication failed 947 q->setErrorString(QProcess::tr("Process failed to start: %1") 948 .arg(QString("DOS error %1").arg(arc))); 949 } else { 950 #if defined (QPROCESS_DEBUG) 951 qDebug("spawnvpe failed: %s", qPrintable(qt_error_string(errno))); 952 #endif 953 q->setErrorString(QProcess::tr("Process failed to start: %1") 954 .arg(qPrintable(qt_error_string(errno)))); 955 } 690 956 emit q->error(processError); 691 QProcessManager::removeProcess(q); 957 QProcessManager::removeProcess(procKey); 958 procKey = QProcessManager::InvalidProcKey; 692 959 cleanup(); 693 960 return; … … 696 963 this->pid = Q_PID(pid); 697 964 698 unsigned long nonBlock = 1; 699 700 // close the ends we don't use and make all pipes non-blocking 701 if (stdinChannel.pipe[0] != -1) { 702 ::close(stdinChannel.pipe[0]); 703 stdinChannel.pipe[0] = -1; 704 } 705 if (stdinChannel.pipe[1] != -1) 706 ::ioctl(stdinChannel.pipe[1], FIONBIO, &nonBlock); 707 708 if (stdoutChannel.pipe[1] != -1) { 709 ::close(stdoutChannel.pipe[1]); 710 stdoutChannel.pipe[1] = -1; 711 } 712 if (stdoutChannel.pipe[0] != -1) 713 ::ioctl(stdoutChannel.pipe[0], FIONBIO, &nonBlock); 714 715 if (stderrChannel.pipe[1] != -1) { 716 ::close(stderrChannel.pipe[1]); 717 stderrChannel.pipe[1] = -1; 718 } 719 if (stderrChannel.pipe[0] != -1) 720 ::ioctl(stderrChannel.pipe[0], FIONBIO, &nonBlock); 965 // close the client ends (they are no more necessary) 966 if (stdinChannel.pipe.client != HFILE(~0)) { 967 DosClose(stdinChannel.pipe.client); 968 stdinChannel.pipe.client = HFILE(~0); 969 } 970 if (stdoutChannel.pipe.client != HFILE(~0)) { 971 DosClose(stdoutChannel.pipe.client); 972 stdoutChannel.pipe.client = HFILE(~0); 973 } 974 if (stderrChannel.pipe.client != HFILE(~0)) { 975 DosClose(stderrChannel.pipe.client); 976 stderrChannel.pipe.client = HFILE(~0); 977 } 721 978 722 979 // give the process a chance to start ... … … 735 992 qint64 QProcessPrivate::bytesAvailableFromStdout() const 736 993 { 737 size_t nbytes = 0;738 994 qint64 available = 0; 739 if (::ioctl(stdoutChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0) 740 available = (qint64) *((int *) &nbytes); 995 { 996 QMutexLocker lock(QProcessManager::mutex()); 997 available = pipeData[OutPipe].bytes; 998 // accept new messages from QProcessManager 999 const_cast<QProcessPrivate*>(this)->pipeData[OutPipe].isNew = false; 1000 } 741 1001 #if defined (QPROCESS_DEBUG) 742 1002 qDebug("QProcessPrivate::bytesAvailableFromStdout() == %lld", available); … … 747 1007 qint64 QProcessPrivate::bytesAvailableFromStderr() const 748 1008 { 749 size_t nbytes = 0;750 1009 qint64 available = 0; 751 if (::ioctl(stderrChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0) 752 available = (qint64) *((int *) &nbytes); 1010 { 1011 QMutexLocker lock(QProcessManager::mutex()); 1012 available = pipeData[ErrPipe].bytes; 1013 // accept new messages from QProcessManager 1014 const_cast<QProcessPrivate*>(this)->pipeData[ErrPipe].isNew = false; 1015 } 753 1016 #if defined (QPROCESS_DEBUG) 754 1017 qDebug("QProcessPrivate::bytesAvailableFromStderr() == %lld", available); … … 759 1022 qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen) 760 1023 { 761 qint64 bytesRead = ::read(stdoutChannel.pipe[0], data, maxlen); 1024 ULONG actual = 0; 1025 APIRET arc = DosRead(stdoutChannel.pipe.server, data, maxlen, &actual); 1026 1027 // decrement the counter to make sure bytesAvailableFromStdout() will return 1028 // a correct number before QProcessManager::run() updates it 1029 QMutexLocker lock(QProcessManager::mutex()); 1030 if (actual <= (ULONG) pipeData[OutPipe].bytes) 1031 pipeData[OutPipe].bytes -= (USHORT) actual; 1032 else 1033 pipeData[OutPipe].bytes = 0; 1034 1035 qint64 bytesRead = arc == NO_ERROR ? (qint64)actual : -1; 1036 762 1037 #if defined QPROCESS_DEBUG 763 1038 qDebug("QProcessPrivate::readFromStdout(%p \"%s\", %lld) == %lld", … … 769 1044 qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen) 770 1045 { 771 qint64 bytesRead = ::read(stderrChannel.pipe[0], data, maxlen); 1046 ULONG actual = 0; 1047 APIRET arc = DosRead(stderrChannel.pipe.server, data, maxlen, &actual); 1048 1049 // decrement the counter to make sure bytesAvailableFromStderr() will return 1050 // a correct number before QProcessManager::run() updates it 1051 QMutexLocker lock(QProcessManager::mutex()); 1052 if (actual <= (ULONG) pipeData[ErrPipe].bytes) 1053 pipeData[ErrPipe].bytes -= (USHORT) actual; 1054 else 1055 pipeData[ErrPipe].bytes = 0; 1056 1057 qint64 bytesRead = arc == NO_ERROR ? (qint64)actual : -1; 1058 772 1059 #if defined QPROCESS_DEBUG 773 1060 qDebug("QProcessPrivate::readFromStderr(%p \"%s\", %lld) == %lld", … … 779 1066 qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen) 780 1067 { 781 qint64 written = ::write(stdinChannel.pipe[1], data, maxlen); 1068 ULONG actual = 0; 1069 APIRET arc = DosWrite(stdinChannel.pipe.server, data, maxlen, &actual); 1070 1071 qint64 written = arc == NO_ERROR ? (qint64)actual : -1; 1072 1073 // accept new messages from QProcessManager 1074 QMutexLocker lock(QProcessManager::mutex()); 1075 const_cast<QProcessPrivate*>(this)->pipeData[InPipe].isNew = false; 1076 782 1077 #if defined QPROCESS_DEBUG 783 1078 qDebug("QProcessPrivate::writeToStdin(%p \"%s\", %lld) == %lld", … … 840 1135 } 841 1136 842 static int qt_select(int maxfd, fd_set *fdread, fd_set *fdwrite, int timeout)843 {844 struct timeval tv;845 tv.tv_sec = timeout / 1000;846 tv.tv_usec = (timeout % 1000) * 1000;847 848 int ret = select(maxfd, fdread, fdwrite, 0, timeout < 0 ? 0 : &tv);849 return ret;850 }851 852 1137 bool QProcessPrivate::waitForStarted(int msecs) 853 1138 { … … 875 1160 stopWatch.start(); 876 1161 1162 1163 1164 877 1165 forever { 878 fd_set fdread;879 fd_set fdwrite;880 881 FD_ZERO(&fdread);882 FD_ZERO(&fdwrite);883 884 int maxFd = 0;885 if (stdoutChannel.pipe[0] != -1) {886 FD_SET(stdoutChannel.pipe[0], &fdread);887 maxFd = qMax(maxFd, stdoutChannel.pipe[0]);888 }889 if (stderrChannel.pipe[0] != -1) {890 FD_SET(stderrChannel.pipe[0], &fdread);891 maxFd = qMax(maxFd, stderrChannel.pipe[0]);892 }893 894 if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1) {895 FD_SET(stdinChannel.pipe[1], &fdwrite);896 maxFd = qMax(maxFd, stdinChannel.pipe[1]);897 }898 899 WaitMode mode;900 if (maxFd >= 0) {901 mode = Select;902 maxSelectFd = maxFd;903 } else {904 mode = Semaphore;905 ensureWaitSem();906 }907 908 1166 bool timedOut = false, failed = false; 909 910 if (waitMode.testAndSetRelease(Async, mode)) { 1167 if (waitMode.testAndSetRelaxed(Async, Semaphore)) { 911 1168 int timeout = qt_timeout_value(msecs, stopWatch.elapsed()); 912 if (maxFd >= 0) { 913 int ret = qt_select(maxFd + 1, &fdread, &fdwrite, timeout); 914 if (ret < 0 && errno != EINTR) { 915 Q_ASSERT(errno == EINTR); 1169 qDosNI(arc = DosWaitEventSem(waitSem, (ULONG)timeout)); 1170 if (arc == ERROR_TIMEOUT) { 1171 timedOut = true; 1172 } else if (arc != NO_ERROR) { 1173 Q_ASSERT(arc == NO_ERROR); 1174 failed = true; 1175 } else { 1176 QMutexLocker lock(QProcessManager::mutex()); 1177 1178 bool readyReadEmitted = false; 1179 if (pipeData[OutPipe].bytes) { 1180 bool canRead = _q_canReadStandardOutput(); 1181 if (processChannel == QProcess::StandardOutput && canRead) 1182 readyReadEmitted = true; 1183 } 1184 if (pipeData[ErrPipe].bytes) { 1185 bool canRead = _q_canReadStandardError(); 1186 if (processChannel == QProcess::StandardError && canRead) 1187 readyReadEmitted = true; 1188 } 1189 if (readyReadEmitted) { 1190 waitMode.fetchAndStoreRelaxed(Async); 1191 return true; 1192 } 1193 1194 if (pipeData[InPipe].bytes) 1195 _q_canWrite(); 1196 1197 if (!pid) 916 1198 failed = true; 917 } else if (ret == 0) {918 timedOut = true;919 } else if (ret >= 0) {920 bool readyReadEmitted = false;921 if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) {922 bool canRead = _q_canReadStandardOutput();923 if (processChannel == QProcess::StandardOutput && canRead)924 readyReadEmitted = true;925 }926 if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) {927 bool canRead = _q_canReadStandardError();928 if (processChannel == QProcess::StandardError && canRead)929 readyReadEmitted = true;930 }931 if (readyReadEmitted) {932 waitMode.fetchAndStoreRelaxed(Async);933 return true;934 }935 936 if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite))937 _q_canWrite();938 939 if (!pid)940 failed = true;941 }942 } else {943 APIRET arc;944 qDosNI(arc = DosWaitEventSem(waitSem, (ULONG)timeout));945 if (arc == ERROR_TIMEOUT) {946 timedOut = true;947 } else if (arc != NO_ERROR) {948 Q_ASSERT(arc == NO_ERROR);949 failed = true;950 }951 1199 } 952 1200 } else { … … 980 1228 stopWatch.start(); 981 1229 1230 1231 1232 982 1233 while (!writeBuffer.isEmpty()) { 983 fd_set fdread;984 fd_set fdwrite;985 986 FD_ZERO(&fdread);987 FD_ZERO(&fdwrite);988 989 int maxFd = 0;990 if (stdoutChannel.pipe[0] != -1) {991 FD_SET(stdoutChannel.pipe[0], &fdread);992 maxFd = qMax(maxFd, stdoutChannel.pipe[0]);993 }994 if (stderrChannel.pipe[0] != -1) {995 FD_SET(stderrChannel.pipe[0], &fdread);996 maxFd = qMax(maxFd, stderrChannel.pipe[0]);997 }998 999 if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1) {1000 FD_SET(stdinChannel.pipe[1], &fdwrite);1001 maxFd = qMax(maxFd, stdinChannel.pipe[1]);1002 }1003 1004 WaitMode mode;1005 if (maxFd >= 0) {1006 mode = Select;1007 maxSelectFd = maxFd;1008 } else {1009 mode = Semaphore;1010 ensureWaitSem();1011 }1012 1013 1234 bool timedOut = false, failed = false; 1014 1015 if (waitMode.testAndSetRelease(Async, mode)) { 1235 if (waitMode.testAndSetRelaxed(Async, Semaphore)) { 1016 1236 int timeout = qt_timeout_value(msecs, stopWatch.elapsed()); 1017 if (maxFd >= 0) { 1018 int ret = qt_select(maxFd + 1, &fdread, &fdwrite, timeout); 1019 if (ret < 0 && errno != EINTR) { 1020 Q_ASSERT(errno == EINTR); 1237 qDosNI(arc = DosWaitEventSem(waitSem, (ULONG)timeout)); 1238 if (arc == ERROR_TIMEOUT) { 1239 timedOut = true; 1240 } else if (arc != NO_ERROR) { 1241 Q_ASSERT(arc == NO_ERROR); 1242 failed = true; 1243 } else { 1244 QMutexLocker lock(QProcessManager::mutex()); 1245 1246 if (pipeData[InPipe].bytes) { 1247 waitMode.fetchAndStoreRelaxed(Async); 1248 return _q_canWrite(); 1249 } 1250 1251 if (pipeData[OutPipe].bytes) 1252 _q_canReadStandardOutput(); 1253 1254 if (pipeData[ErrPipe].bytes) 1255 _q_canReadStandardError(); 1256 1257 if (!pid) 1021 1258 failed = true; 1022 } else if (ret == 0) {1023 timedOut = true;1024 } else if (ret >= 0) {1025 if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite))1026 return _q_canWrite();1027 1028 if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread))1029 _q_canReadStandardOutput();1030 1031 if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread))1032 _q_canReadStandardError();1033 1034 if (!pid)1035 failed = true;1036 }1037 } else {1038 APIRET arc;1039 qDosNI(arc = DosWaitEventSem(waitSem, (ULONG)timeout));1040 if (arc == ERROR_TIMEOUT) {1041 timedOut = true;1042 } else if (arc != NO_ERROR) {1043 Q_ASSERT(arc == NO_ERROR);1044 failed = true;1045 }1046 1259 } 1047 1260 } else { … … 1076 1289 stopWatch.start(); 1077 1290 1291 1292 1293 1078 1294 forever { 1079 fd_set fdread;1080 fd_set fdwrite;1081 1082 FD_ZERO(&fdread);1083 FD_ZERO(&fdwrite);1084 1085 int maxFd = -1;1086 if (stdoutChannel.pipe[0] != -1) {1087 FD_SET(stdoutChannel.pipe[0], &fdread);1088 maxFd = qMax(maxFd, stdoutChannel.pipe[0]);1089 }1090 if (stderrChannel.pipe[0] != -1) {1091 FD_SET(stderrChannel.pipe[0], &fdread);1092 maxFd = qMax(maxFd, stderrChannel.pipe[0]);1093 }1094 1095 if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1) {1096 FD_SET(stdinChannel.pipe[1], &fdwrite);1097 maxFd = qMax(maxFd, stdinChannel.pipe[1]);1098 }1099 1100 WaitMode mode;1101 if (maxFd >= 0) {1102 mode = Select;1103 maxSelectFd = maxFd;1104 } else {1105 mode = Semaphore;1106 ensureWaitSem();1107 }1108 1109 1295 bool timedOut = false, failed = false; 1110 1111 if (waitMode.testAndSetRelease(Async, mode)) { 1296 if (waitMode.testAndSetRelaxed(Async, Semaphore)) { 1112 1297 int timeout = qt_timeout_value(msecs, stopWatch.elapsed()); 1113 if (maxFd >= 0) { 1114 int ret = qt_select(maxFd + 1, &fdread, &fdwrite, timeout); 1115 if (ret < 0 && errno != EINTR) { 1116 Q_ASSERT(errno == EINTR); 1117 failed = true; 1118 } else if (ret == 0) { 1119 timedOut = true; 1120 } else if (ret >= 0) { 1121 if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) 1122 _q_canWrite(); 1123 1124 if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) 1125 _q_canReadStandardOutput(); 1126 1127 if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) 1128 _q_canReadStandardError(); 1129 1130 if (!pid) { 1131 waitMode.fetchAndStoreRelaxed(Async); 1132 return true; 1133 } 1134 } 1298 qDosNI(arc = DosWaitEventSem(waitSem, (ULONG)timeout)); 1299 if (arc == ERROR_TIMEOUT) { 1300 timedOut = true; 1301 } else if (arc != NO_ERROR) { 1302 Q_ASSERT(arc == NO_ERROR); 1303 failed = true; 1135 1304 } else { 1136 APIRET arc; 1137 qDosNI(arc = DosWaitEventSem(waitSem, (ULONG)timeout)); 1138 if (arc == ERROR_TIMEOUT) { 1139 timedOut = true; 1140 } else if (arc != NO_ERROR) { 1141 Q_ASSERT(arc == NO_ERROR); 1142 failed = true; 1305 QMutexLocker lock(QProcessManager::mutex()); 1306 1307 if (pipeData[InPipe].bytes) 1308 _q_canWrite(); 1309 1310 if (pipeData[OutPipe].bytes) 1311 _q_canReadStandardOutput(); 1312 1313 if (pipeData[ErrPipe].bytes) 1314 _q_canReadStandardError(); 1315 1316 if (!pid) { 1317 waitMode.fetchAndStoreRelaxed(Async); 1318 return true; 1143 1319 } 1144 1320 } … … 1165 1341 bool QProcessPrivate::waitForWrite(int msecs) 1166 1342 { 1167 fd_set fdwrite; 1168 FD_ZERO(&fdwrite); 1169 FD_SET(stdinChannel.pipe[1], &fdwrite); 1343 // ### this function isn't actually used in OS/2 and Unix code paths 1344 1345 APIRET arc; 1346 ensureWaitSem(); 1170 1347 1171 1348 bool ret = false; 1172 maxSelectFd = stdinChannel.pipe[1]; 1173 if (waitMode.testAndSetRelease(Async, Select)) { 1174 ret = qt_select(stdinChannel.pipe[1] + 1, 0, &fdwrite, 1175 msecs < 0 ? 0 : msecs) == 1; 1349 if (waitMode.testAndSetRelaxed(Async, Semaphore)) { 1350 qDosNI(arc = DosWaitEventSem(waitSem, (ULONG)msecs)); 1351 if (arc == NO_ERROR) { 1352 QMutexLocker lock(QProcessManager::mutex()); 1353 ret = pipeData[InPipe].bytes; 1354 } 1176 1355 } 1177 1356 waitMode.fetchAndStoreRelaxed(Async); … … 1187 1366 // normally calls findExitCode() won't be walked) 1188 1367 1189 Q_Q(QProcess); 1190 QProcessManager::removeProcess(q); 1368 if (procKey != QProcessManager::InvalidProcKey) { 1369 QProcessManager::removeProcess(procKey); 1370 procKey = QProcessManager::InvalidProcKey; 1371 } 1191 1372 } 1192 1373 -
trunk/src/corelib/io/qprocess_p.h
r192 r197 59 59 #include "private/qiodevice_p.h" 60 60 61 #if def Q_OS_WIN61 #if 62 62 #include "QtCore/qt_windows.h" 63 63 typedef HANDLE Q_PIPE; 64 64 #define INVALID_Q_PIPE INVALID_HANDLE_VALUE 65 66 65 67 #else 66 68 typedef int Q_PIPE; 67 69 #define INVALID_Q_PIPE -1 68 #endif69 70 #ifdef Q_OS_OS271 #include "QtCore/qt_os2.h"72 70 #endif 73 71 … … 97 95 Channel() : process(0), notifier(0), type(Normal), closed(false), append(false) 98 96 { 97 98 99 100 99 101 pipe[0] = INVALID_Q_PIPE; 100 102 pipe[1] = INVALID_Q_PIPE; 103 101 104 } 102 105 … … 128 131 QProcessPrivate *process; 129 132 QSocketNotifier *notifier; 133 134 135 136 137 138 139 130 140 Q_PIPE pipe[2]; 141 131 142 132 143 unsigned type : 2; … … 172 183 QRingBuffer writeBuffer; 173 184 185 186 187 188 189 174 190 Q_PIPE childStartedPipe[2]; 175 191 Q_PIPE deathPipe[2]; 176 192 void destroyPipe(Q_PIPE pipe[2]); 193 177 194 178 195 QSocketNotifier *startupSocketNotifier; … … 215 232 #endif 216 233 #ifdef Q_OS_OS2 217 enum WaitMode { Async, Se lect, Semaphore, SigChild };234 enum WaitMode { Async, Semaphore, SigChild }; 218 235 QAtomicInt waitMode; 219 int maxSelectFd;220 236 HEV waitSem; 237 238 239 240 241 242 221 243 #endif 222 244
Note:
See TracChangeset
for help on using the changeset viewer.