- Timestamp:
- Oct 9, 2006, 12:14:18 AM (19 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/qprocess.h
r41 r134 166 166 friend class QProc; 167 167 #endif 168 #if defined(Q_OS_OS2) 169 friend class QProcessMonitor; 170 #endif 168 171 169 172 #if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= -
trunk/src/kernel/qprocess_pm.cpp
r51 r134 45 45 #include "qregexp.h" 46 46 #include "qdir.h" 47 #include "qthread.h" 48 #include "qintdict.h" 49 #include "qmutex.h" 47 50 #include "private/qinternal_p.h" 48 51 #include "qt_os2.h" … … 52 55 //#define QT_QPROCESS_DEBUG 53 56 54 const HFILE HF_STDIN = 0; 55 const HFILE HF_STDOUT = 1; 56 const HFILE HF_STDERR = 2; 57 const HFILE HF_ANY = 0xFFFFFFFF; 58 59 const PID PID_NULL = ~1; 60 61 const ULONG PIPE_SIZE = 1024; 62 63 // just fake constants, for convenience 64 const int FD_PIPE_STDOUT = 1; 65 const int FD_PIPE_STDERR = 2; 57 #define HF_STDIN HFILE( 0 ) 58 #define HF_STDOUT HFILE( 1 ) 59 #define HF_STDERR HFILE( 2 ) 60 #define HF_NULL HFILE( ~0 ) 61 62 #define HP_NULL HPIPE( ~0 ) 63 #define KEY_NULL USHORT( ~0 ) 64 65 #define PID_NULL PID( ~0 ) 66 67 enum 68 { 69 PIPE_SIZE_STDIN = 65520, // max 70 PIPE_SIZE_STDOUT = 65520, // max 71 PIPE_SIZE_STDERR = 4096, 72 73 POLL_TIMER = 100, 74 75 // new pipe data notification 76 WM_U_PIPE_RDATA = WM_USER + 0, 77 WM_U_PIPE_CLOSE = WM_USER + 1, 78 }; 66 79 67 80 #if defined(QT_QPROCESS_DEBUG) 68 81 #include <stdarg.h> 82 static HFILE StdErrHandle = HF_NULL; 83 QtMsgHandler OldMsgHandler = NULL; 84 static void StdErrMsgHandler( QtMsgType type, const char *msg ) 85 { 86 if ( OldMsgHandler == NULL ) { 87 size_t len = strlen( msg ); 88 ULONG written = 0; 89 DosWrite( StdErrHandle, msg, len, &written ); 90 const char *EOL = "\n\r"; 91 DosWrite( StdErrHandle, EOL, 2, &written ); 92 } else { 93 OldMsgHandler( type, msg ); 94 } 95 } 96 #define InstallQtMsgHandler() \ 97 do { \ 98 DosDupHandle( HF_STDERR, &StdErrHandle ); \ 99 qInstallMsgHandler( StdErrMsgHandler ); \ 100 } while (0) 101 #define UninstallQtMsgHandler() \ 102 do { \ 103 qInstallMsgHandler( OldMsgHandler ); \ 104 DosClose( StdErrHandle ); \ 105 } while (0) 106 #else 107 #define InstallQtMsgHandler() do {} while (0) 108 #define UninstallQtMsgHandler() do {} while (0) 69 109 #endif 70 110 … … 77 117 { 78 118 public: 119 struct Pipe 120 { 121 Pipe () : pipe( HP_NULL ), key( KEY_NULL ), pending( 0 ) 122 , closed( false ) {} 123 HPIPE pipe; 124 USHORT key; 125 QMembuf buf; 126 // note: when QProcess is watched by QProcessMonitor, the below fields 127 // must be accessed from under the QProcessMonitor lock 128 uint pending; 129 bool closed : 1; 130 }; 131 79 132 QProcessPrivate( QProcess *proc ) 80 133 { 81 134 stdinBufRead = 0; 82 pipeStdin = HF_ANY; 83 pipeStdout = HF_ANY; 84 pipeStderr = HF_ANY; 135 pipeStdin = HP_NULL; 136 pid = PID_NULL; 85 137 exitValuesCalculated = FALSE; 86 138 87 139 lookup = new QTimer( proc ); 88 qApp->connect( lookup, SIGNAL(timeout()), 89 proc, SLOT(timeout()) ); 90 91 pid = PID_NULL; 140 qApp->connect( lookup, SIGNAL(timeout()), proc, SLOT(timeout()) ); 92 141 } 93 142 … … 102 151 delete stdinBuf.dequeue(); 103 152 } 153 stdinBufRead = 0; 104 154 closeHandles(); 105 stdinBufRead = 0; 106 pipeStdin = HF_ANY; 107 pipeStdout = HF_ANY; 108 pipeStderr = HF_ANY; 155 stdout.buf.clear(); 156 stderr.buf.clear(); 157 pid = PID_NULL; 109 158 exitValuesCalculated = FALSE; 110 111 pid = PID_NULL; 112 } 113 159 } 160 161 Pipe *findPipe( USHORT key ) 162 { 163 if ( stdout.key == key ) return &stdout; 164 if ( stderr.key == key ) return &stderr; 165 return NULL; 166 } 167 168 void closePipe( Pipe *pipe ) 169 { 170 if ( pipe->pipe != HP_NULL ) { 171 Q_ASSERT( pipe->key == KEY_NULL ); 172 DosDisConnectNPipe( pipe->pipe ); 173 DosClose( pipe->pipe ); 174 pipe->pipe = HP_NULL; 175 pipe->pending = 0; 176 pipe->closed = FALSE; 177 } 178 } 179 180 bool readPipe( Pipe *pipe ); 181 114 182 void closeHandles() 115 183 { 116 if( pipeStdin != H F_ANY) {184 if( pipeStdin != HP_NULL ) { 117 185 DosDisConnectNPipe( pipeStdin ); 118 186 DosClose( pipeStdin ); 119 pipeStdin = H F_ANY;187 pipeStdin = HP_NULL; 120 188 } 121 if( pipeStdout != HF_ANY ) { 122 // check if STDERR was redirected to STDOUT 123 if ( pipeStderr == pipeStdout ) 124 pipeStderr = HF_ANY; 125 DosDisConnectNPipe( pipeStdout ); 126 DosClose( pipeStdout ); 127 pipeStdout = HF_ANY; 128 } 129 if( pipeStderr != HF_ANY ) { 130 DosDisConnectNPipe( pipeStderr ); 131 DosClose( pipeStderr ); 132 pipeStderr = HF_ANY; 133 } 134 } 135 136 QMembuf bufStdout; 137 QMembuf bufStderr; 189 closePipe( &stdout ); 190 closePipe( &stderr ); 191 } 138 192 139 193 QPtrQueue <QByteArray> stdinBuf; 194 uint stdinBufRead; 140 195 141 196 HPIPE pipeStdin; 142 HPIPE pipeStdout; 143 HPIPE pipeStderr; 197 198 Pipe stdout; 199 Pipe stderr; 144 200 145 201 PID pid; … … 147 203 QTimer *lookup; 148 204 149 uint stdinBufRead; 150 151 bool exitValuesCalculated; 205 bool exitValuesCalculated : 1; 152 206 }; 153 207 208 class QProcessMonitor : public QPMObjectWindow 209 { 210 public: 211 class Thread : public QThread 212 { 213 public: 214 Thread( QProcessMonitor *m ) : mon( m ) {} 215 void run() { mon->monitor(); } 216 QProcessMonitor *mon; 217 }; 218 219 QProcessMonitor(); 220 ~QProcessMonitor(); 221 222 QMutex &mlock() { return lock; } 223 224 bool isOk() const { return pipeSem != 0 && thread != NULL; } 225 226 bool addProcess( QProcess *proc ); 227 void removeProcess( QProcessPrivate *d, 228 QProcessPrivate::Pipe *pipe = NULL ); 229 230 void monitor(); 231 MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 ); 232 233 private: 234 235 struct PipeStates 236 { 237 PipeStates() { 238 size = 4; 239 arr = new PIPESEMSTATE[ size ]; 240 } 241 ~PipeStates() { 242 delete[] arr; 243 } 244 void ensure( size_t sz ) { 245 // best size for sz pipes is sz * 2 (to be able to store both 246 // NPSS_RDATA & NPSS_CLOSE for every pipe) + one for NPSS_EOI 247 size_t newSize = sz * 2 + 1; 248 newSize = ((newSize + 5 - 1) / 5) * 5; 249 if ( newSize != size ) { 250 delete[] arr; 251 size = newSize; 252 arr = new PIPESEMSTATE[ size ]; 253 } 254 } 255 size_t dataSize() { return size * sizeof(PIPESEMSTATE); } 256 PIPESEMSTATE *data() { return arr; } 257 PIPESEMSTATE &operator[]( size_t i ) { 258 Q_ASSERT( i < size ); 259 return arr[i]; 260 } 261 private: 262 PIPESEMSTATE *arr; 263 size_t size; 264 }; 265 266 bool addPipe( QProcess *proc, QProcessPrivate::Pipe *pipe ); 267 void removePipe( QProcessPrivate::Pipe *pipe ); 268 269 HEV pipeSem; 270 Thread *thread; 271 bool threadRunning; 272 QIntDict<QProcess> pipeKeys; 273 PipeStates pipeStates; 274 QMutex lock; 275 }; 276 277 QProcessMonitor::QProcessMonitor() 278 : pipeSem( 0 ), thread( NULL ), threadRunning( FALSE ) 279 { 280 APIRET rc = DosCreateEventSem( NULL, &pipeSem, DC_SEM_SHARED, 0 ); 281 if ( rc != NO_ERROR ) { 282 #if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG) 283 qSystemWarning( "Failed to create a semaphore!", rc ); 284 #endif 285 return; 286 } 287 288 thread = new Thread( this ); 289 Q_ASSERT( thread != NULL ); 290 } 291 292 QProcessMonitor::~QProcessMonitor() 293 { 294 if ( thread ) { 295 lock.lock(); 296 if ( threadRunning ) { 297 threadRunning = FALSE; 298 DosPostEventSem( pipeSem ); 299 } 300 lock.unlock(); 301 thread->wait(); 302 delete thread; 303 } 304 305 if ( pipeSem ) 306 DosCloseEventSem( pipeSem ); 307 } 308 309 bool QProcessMonitor::addPipe( QProcess *proc, QProcessPrivate::Pipe *pipe ) 310 { 311 // Note: we use HPIPE as pipe keys in DosSetNPipeSem. However, HPIPE 312 // is 32-bit (ulong), while usKey in PIPESEMSTATE is 16-bit (USHORT) 313 // We unreasonably assume that the system will never assign HPIPE >= 0xFFFF, 314 // and just cast HPIPE to USHORT. There is an assertion checking this 315 // condition, so this method should simply return FALSE once our assumption 316 // breaks. 317 318 Q_ASSERT( pipe->pipe < HPIPE( KEY_NULL ) ); 319 if ( pipe->pipe >= HPIPE( KEY_NULL ) ) 320 return FALSE; 321 322 Q_ASSERT( pipe->pipe != HP_NULL && pipe->key == KEY_NULL ); 323 324 pipe->key = USHORT( pipe->pipe ); 325 APIRET rc = DosSetNPipeSem( pipe->pipe, (HSEM) pipeSem, pipe->key ); 326 if ( rc != NO_ERROR ) { 327 #if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG) 328 qSystemWarning( "Failed to set the pipe semaphore!", rc ); 329 #endif 330 return FALSE; 331 } 332 333 pipeKeys.insert( pipe->key, proc ); 334 return TRUE; 335 } 336 337 void QProcessMonitor::removePipe( QProcessPrivate::Pipe *pipe ) 338 { 339 Q_ASSERT( pipe->pipe != HP_NULL && pipe->key != KEY_NULL ); 340 341 /// @todo (r=dmik) is this really necessary to detach pipeSem? 342 DosSetNPipeSem( pipe->pipe, 0, 0 ); 343 bool ok = pipeKeys.remove( pipe->key ); 344 pipe->key = KEY_NULL; 345 Q_ASSERT( ok ); 346 Q_UNUSED( ok ); 347 } 348 349 bool QProcessMonitor::addProcess( QProcess *proc ) 350 { 351 #if defined(QT_QPROCESS_DEBUG) 352 qDebug( "QProcessMonitor::addProcess(): proc=%p d=%p", proc, proc->d ); 353 #endif 354 355 QProcessPrivate *d = proc->d; 356 357 // check if we need to monitor this process before entering the lock 358 if ( d->stdout.pipe == HP_NULL && d->stderr.pipe == HP_NULL ) 359 return TRUE; 360 361 uint newPipes = 0; 362 363 QMutexLocker locker( &lock ); 364 365 if ( d->stdout.pipe != HP_NULL ) { 366 if ( !addPipe( proc, &d->stdout ) ) 367 return FALSE; 368 ++ newPipes; 369 } 370 371 if ( d->stderr.pipe != HP_NULL ) { 372 if ( !addPipe( proc, &d->stderr ) ) { 373 removePipe( &d->stderr ); 374 return FALSE; 375 } 376 ++ newPipes; 377 } 378 379 Q_ASSERT( newPipes > 0 ); 380 381 pipeStates.ensure( pipeKeys.count() ); 382 383 // start the monitor thread if necessary 384 if ( pipeKeys.count() == newPipes ) { 385 threadRunning = TRUE; 386 thread->start(); 387 } 388 389 #if defined(QT_QPROCESS_DEBUG) 390 qDebug( "QProcessMonitor::addProcess(): added %d pipes", newPipes ); 391 #endif 392 return TRUE; 393 } 394 395 void QProcessMonitor::removeProcess( QProcessPrivate *d, 396 QProcessPrivate::Pipe *pipe /* = NULL */) 397 { 398 #if defined(QT_QPROCESS_DEBUG) 399 qDebug( "QProcessMonitor::removeProcess(): d=%p pipe=%p key=%04hX", 400 d, pipe, pipe ? pipe->key : KEY_NULL ); 401 #endif 402 403 // check if we monitor this process before entering the lock 404 if ( d->stdout.pipe == HP_NULL && d->stderr.pipe == HP_NULL ) 405 return; 406 407 lock.lock(); 408 409 if ( pipeKeys.count() == 0 ) { 410 // Nothing to do. This is an outdated general removeProcess (d, NULL) 411 // call from the QProcess destructor or from isRunning(). Just return. 412 lock.unlock(); 413 return; 414 } 415 416 if ( pipe ) { 417 removePipe( pipe ); 418 } else { 419 if ( d->stdout.pipe != HP_NULL && d->stdout.key != KEY_NULL ) 420 removePipe( &d->stdout ); 421 if ( d->stderr.pipe != HP_NULL && d->stderr.key != KEY_NULL ) 422 removePipe( &d->stderr ); 423 } 424 425 pipeStates.ensure( pipeKeys.count() ); 426 427 // stop the monitor thread if no more necessary 428 if ( pipeKeys.count() == 0 ) { 429 Q_ASSERT( threadRunning ); 430 if ( threadRunning ) { 431 threadRunning = FALSE; 432 DosPostEventSem( pipeSem ); 433 } 434 lock.unlock(); 435 thread->wait(); 436 } else { 437 lock.unlock(); 438 } 439 } 440 441 /** Monitor thread function */ 442 void QProcessMonitor::monitor() 443 { 444 #if defined(QT_QPROCESS_DEBUG) 445 qDebug( "QProcessMonitor::monitor() ENTER" ); 446 #endif 447 448 lock.lock(); 449 while( 1 ) { 450 lock.unlock(); 451 APIRET rc = DosWaitEventSem( pipeSem, SEM_INDEFINITE_WAIT ); 452 lock.lock(); 453 if ( rc != NO_ERROR ) { 454 #if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG) 455 qSystemWarning( "Failed to wait on event semaphore!", rc ); 456 #endif 457 break; 458 } 459 460 ULONG posts = 0; 461 DosResetEventSem( pipeSem, &posts ); 462 #if defined(QT_QPROCESS_DEBUG) 463 qDebug( "QProcessMonitor::monitor(): got semaphore (posts=%ld)", posts ); 464 #endif 465 466 if ( !threadRunning ) 467 break; 468 469 rc = DosQueryNPipeSemState( (HSEM) pipeSem, pipeStates.data(), 470 pipeStates.dataSize() ); 471 if ( rc != NO_ERROR ) { 472 #if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG) 473 qSystemWarning( "Failed to query pipe semaphore state!", rc ); 474 #endif 475 continue; 476 } 477 478 // In the returned information array, CLOSE and READ records for the 479 // same pipe key may be mixed. We need CLOSE messages to be posted after 480 // READ messages, so we do two passes. 481 482 int pass = 0; 483 for ( int i = 0; pass < 2; ++ i ) { 484 if ( pipeStates[i].fStatus == NPSS_EOI ) { 485 ++ pass; 486 i = -1; 487 continue; 488 } 489 if ( pass == 0 && pipeStates[i].fStatus != NPSS_RDATA ) 490 continue; 491 if ( pass == 1 && pipeStates[i].fStatus != NPSS_CLOSE ) 492 continue; 493 494 #if defined(QT_QPROCESS_DEBUG) 495 qDebug( " %d/%d: fStatus=%u fFlag=%02X usKey=%04hX usAvail=%hu", 496 pass, i, (uint) pipeStates[i].fStatus, 497 (uint) pipeStates[i].fFlag, pipeStates[i].usKey, 498 pipeStates[i].usAvail ); 499 #endif 500 QProcess *proc = pipeKeys.find( pipeStates[i].usKey ); 501 Q_ASSERT( proc ); 502 if ( !proc ) 503 continue; 504 #if defined(QT_QPROCESS_DEBUG) 505 qDebug( " proc=%p (%s/%s) d=%p", 506 proc, proc->name(), proc->className(), proc->d ); 507 #endif 508 QProcessPrivate::Pipe *pipe = proc->d->findPipe( pipeStates[i].usKey ); 509 Q_ASSERT( pipe ); 510 if ( !pipe ) 511 continue; 512 513 if ( pipeStates[i].fStatus == NPSS_RDATA ) { 514 bool wasPending = pipe->pending > 0; 515 pipe->pending = pipeStates[i].usAvail; 516 // inform the GUI thread that there is new data 517 if ( !wasPending ) 518 WinPostMsg( hwnd(), WM_U_PIPE_RDATA, MPFROMP( proc ), 519 MPFROMSHORT( pipe->key ) ); 520 } else if ( pipeStates[i].fStatus == NPSS_CLOSE ) { 521 // there may be repeated CLOSE records for the same pipe 522 // in subsequent DosQueryNPipeSemState() calls 523 if ( pipe->closed == FALSE ) { 524 pipe->closed = TRUE; 525 // inform the GUI thread that the client's pipe end 526 // was closed 527 WinPostMsg( hwnd(), WM_U_PIPE_CLOSE, MPFROMP( proc ), 528 MPFROMSHORT( pipe->key ) ); 529 } 530 } 531 } 532 } 533 lock.unlock(); 534 535 #if defined(QT_QPROCESS_DEBUG) 536 qDebug( "QProcessMonitor::monitor() LEAVE" ); 537 #endif 538 } 539 540 MRESULT QProcessMonitor::message( ULONG msg, MPARAM mp1, MPARAM mp2 ) 541 { 542 switch ( msg ) { 543 case WM_U_PIPE_RDATA: { 544 QProcess *proc = (QProcess *) PVOIDFROMMP( mp1 ); 545 USHORT key = SHORT1FROMMP( mp2 ); 546 #if defined(QT_QPROCESS_DEBUG) 547 qDebug( "QProcessMonitor::WM_U_PIPE_RDATA: proc=%p (%s/%s) d=%p " 548 "key=%04hX", 549 proc, proc->name(), proc->className(), proc->d, key ); 550 #endif 551 // check parameter validity (we can safely do it outside the lock 552 // because the GUI thread is the only that can modify pipeKeys) 553 if ( proc == pipeKeys.find( key ) ) { 554 QProcessPrivate *d = proc->d; 555 if ( d->stdout.key == key ) { 556 emit proc->readyReadStdout(); 557 } else { 558 Q_ASSERT( d->stderr.key == key ); 559 emit proc->readyReadStderr(); 560 } 561 } 562 break; 563 } 564 case WM_U_PIPE_CLOSE: { 565 QProcess *proc = (QProcess *) PVOIDFROMMP( mp1 ); 566 USHORT key = SHORT1FROMMP( mp2 ); 567 #if defined(QT_QPROCESS_DEBUG) 568 qDebug( "QProcessMonitor::WM_U_PIPE_CLOSE: proc=%p (%s/%s) d=%p " 569 "key=%04hX", 570 proc, proc->name(), proc->className(), proc->d, key ); 571 #endif 572 // check parameter validity (we can safely do it outside the lock 573 // because the GUI thread is the only that can modify pipeKeys) 574 if ( proc == pipeKeys.find( key ) ) { 575 QProcessPrivate *d = proc->d; 576 QProcessPrivate::Pipe *pipe = d->findPipe( key ); 577 Q_ASSERT( pipe ); 578 if ( pipe ) { 579 Q_ASSERT( pipe->closed ); 580 // remove the single pipe from watching 581 removeProcess( d, pipe ); 582 // close the pipe since no more necessary 583 // (pipe is not watched anymore, no need to lock access) 584 if ( pipe->pending == 0 ) 585 d->closePipe( pipe ); 586 } 587 } 588 break; 589 } 590 default: 591 break; 592 } 593 594 return FALSE; 595 } 596 597 static QProcessMonitor *processMonitor = NULL; 598 599 void QProcessMonitor_cleanup() 600 { 601 #if defined(QT_QPROCESS_DEBUG) 602 qDebug( "QProcessMonitor_cleanup()" ); 603 #endif 604 delete processMonitor; 605 processMonitor = NULL; 606 } 607 608 /** Returns true if some data was successfully read */ 609 bool QProcessPrivate::readPipe( Pipe *pipe ) 610 { 611 #if defined(QT_QPROCESS_DEBUG) 612 qDebug( "QProcessPrivate::readPipe(): this=%p key=%04hX", this, pipe->key ); 613 #endif 614 615 if ( pipe->pipe == HP_NULL ) 616 return false; 617 618 // Pipe::pending and Pipe::closed need the lock 619 QMutex *lock = NULL; 620 621 // Notes: 622 // 1. If procesMonitor is NULL, it which means we're somewhere inside 623 // the QApplication destruction procedure. 624 // 2. If pipe->key us KEY_NULL, it means GUI thread has already processed 625 // the WM_U_PIPE_CLOSE message and we're free to access fields w/o a 626 // lock. pipe->key is assigned only on the GUI thread, so it's safe 627 // to check it here w/o the lock. 628 629 if ( processMonitor && pipe->key != KEY_NULL ) 630 lock = &processMonitor->mlock(); 631 632 QByteArray *ba = NULL; 633 bool close = FALSE; 634 635 { 636 QMutexLocker locker( lock ); 637 638 if ( pipe->pending == 0 ) 639 return false; 640 641 ba = new QByteArray( pipe->pending ); 642 643 ULONG read = 0; 644 APIRET rc = DosRead( pipe->pipe, ba->data(), pipe->pending, &read ); 645 if ( rc != NO_ERROR ) { 646 delete ba; 647 #if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG) 648 qSystemWarning( "Failed to read from the pipe!", rc ); 649 #endif 650 return false; 651 } 652 653 Q_ASSERT( read == pipe->pending ); 654 if ( read < pipe->pending ) 655 ba->resize( read ); 656 657 pipe->pending -= read; 658 659 close = pipe->pending == 0 && pipe->closed; 660 } 661 662 if ( close ) { 663 // the client's end of pipe has been closed, so close our end as well 664 if ( processMonitor && pipe->key != KEY_NULL ) { 665 // WM_U_PIPE_CLOSE has been posted but not yet processed 666 processMonitor->removeProcess( this, pipe ); 667 } 668 closePipe( pipe ); 669 } 670 671 pipe->buf.append( ba ); 672 return true; 673 } 154 674 155 675 /*********************************************************************** … … 164 684 exitStat = 0; 165 685 exitNormal = FALSE; 686 687 #if defined(QT_QPROCESS_DEBUG) 688 qDebug( "~QProcess::init(): d=%p", d ); 689 #endif 166 690 } 167 691 168 692 void QProcess::reset() 169 693 { 694 // remove monitoring if the monitor is there (note that it's done before 695 // resetting QProcessPrivate which has fields protected by the 696 // QProcessMonitor lock) 697 if ( processMonitor ) 698 processMonitor->removeProcess( d ); 699 170 700 d->reset(); 171 701 exitStat = 0; 172 702 exitNormal = FALSE; 173 d->bufStdout.clear();174 d->bufStderr.clear();175 703 } 176 704 177 705 QMembuf* QProcess::membufStdout() 178 706 { 179 if( d-> pipeStdout!= 0 )180 socketRead( FD_PIPE_STDOUT);181 return &d-> bufStdout;707 if( d->stdout.pipe != 0 ) 708 d->readPipe( &d->stdout ); 709 return &d->stdout.buf; 182 710 } 183 711 184 712 QMembuf* QProcess::membufStderr() 185 713 { 186 if( d-> pipeStderr!= 0 )187 socketRead( FD_PIPE_STDERR);188 return &d-> bufStderr;714 if( d->stderr.pipe != 0 ) 715 d->readPipe( &d->stderr ); 716 return &d->stderr.buf; 189 717 } 190 718 191 719 QProcess::~QProcess() 192 720 { 721 #if defined(QT_QPROCESS_DEBUG) 722 qDebug( "~QProcess::QProcess(): d=%p", d ); 723 #endif 724 725 // remove monitoring if the monitor is there (note that it's done before 726 // resetting QProcessPrivate which has fields protected by the 727 // QProcessMonitor lock) 728 if ( processMonitor ) 729 processMonitor->removeProcess( d ); 730 193 731 delete d; 194 732 } 195 733 196 #if defined(QT_QPROCESS_DEBUG)197 static void DosPrintf( HFILE fd, const char *fmt, ... )198 {199 static char buf[ 1024 ];200 201 va_list args;202 va_start( args, fmt );203 vsnprintf( buf, sizeof(buf), fmt, args );204 va_end ( args );205 206 ULONG written = 0;207 DosWrite( fd, buf, strlen( buf ), &written );208 }209 #endif210 211 734 bool QProcess::start( QStringList *env ) 212 735 { … … 214 737 qDebug( "QProcess::start()" ); 215 738 #endif 739 216 740 reset(); 217 741 … … 444 968 // create STDIN/OUT/ERR redirection pipes 445 969 446 HFILE tmpStdin = HF_ANY, tmpStdout = HF_ANY, tmpStderr = HF_ANY; 970 if ( comms & (Stdout | Stderr) ) { 971 // lazily create the process monitor 972 if ( !processMonitor ) { 973 processMonitor = new QProcessMonitor(); 974 Q_ASSERT( processMonitor ); 975 if ( !processMonitor ) 976 return FALSE; 977 if ( !processMonitor->isOk() ) { 978 QProcessMonitor_cleanup(); 979 return FALSE; 980 } 981 qAddPostRoutine( QProcessMonitor_cleanup ); 982 } 983 } 984 985 HFILE tmpStdin = HF_NULL, tmpStdout = HF_NULL, tmpStderr = HF_NULL; 447 986 HFILE realStdin = HF_STDIN, realStdout = HF_STDOUT, realStderr = HF_STDERR; 448 987 … … 451 990 DosGetInfoBlocks( NULL, &ppib ); 452 991 992 // use the custom Qt message handler to print errors/wranings 993 // to the console when the real STDERR handle is redirected 994 InstallQtMsgHandler(); 995 453 996 if ( comms & Stdin ) { 454 HFILE clientStdin = HF_ ANY;997 HFILE clientStdin = HF_NULL; 455 998 do { 456 999 // create a Stdin pipe … … 459 1002 rc = DosCreateNPipe( pathBuf, &d->pipeStdin, 460 1003 NP_ACCESS_OUTBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1, 461 PIPE_SIZE , PIPE_SIZE, 0 );1004 PIPE_SIZE_STDIN, 0, 0 ); 462 1005 if ( rc != NO_ERROR ) 463 1006 break; … … 478 1021 } while( 0 ); 479 1022 // close the client end anyway (no more necessary) 480 if ( clientStdin != HF_ ANY)1023 if ( clientStdin != HF_NULL ) 481 1024 DosClose ( clientStdin ); 482 1025 // close all opened handles on error 483 1026 if ( rc != NO_ERROR ) { 484 1027 #if defined(QT_QPROCESS_DEBUG) 485 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr, 486 "Failed to create a STDIN redirection (%s): SYS%04ld\n\r", 487 pathBuf, rc ); 488 #endif 489 if ( tmpStdin != HF_ANY ) 1028 qDebug( "Failed to create a STDIN redirection (%s): SYS%04ld", 1029 pathBuf, rc ); 1030 #endif 1031 if ( tmpStdin != HF_NULL ) 490 1032 DosClose ( tmpStdin ); 491 1033 d->closeHandles(); 1034 UninstallQtMsgHandler(); 492 1035 return FALSE; 493 1036 } 494 1037 } 495 1038 if ( comms & Stdout ) { 496 HFILE clientStdout = HF_ ANY;1039 HFILE clientStdout = HF_NULL; 497 1040 do { 498 1041 // create a Stdout pipe 499 1042 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stdout", 500 1043 ppib->pib_ulpid, this ); 501 rc = DosCreateNPipe( pathBuf, &d-> pipeStdout,1044 rc = DosCreateNPipe( pathBuf, &d->stdout.pipe, 502 1045 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1, 503 PIPE_SIZE, PIPE_SIZE, 0 );1046 0, PIPE_SIZE_STDOUT, 0 ); 504 1047 if ( rc != NO_ERROR ) 505 1048 break; 506 DosConnectNPipe( d-> pipeStdout);1049 DosConnectNPipe( d->stdout.pipe ); 507 1050 // open a client end of the Stdout pipe 508 1051 ULONG action = 0; … … 520 1063 } while( 0 ); 521 1064 // close the client end anyway (no more necessary) 522 if ( clientStdout != HF_ ANY)1065 if ( clientStdout != HF_NULL ) 523 1066 DosClose ( clientStdout ); 524 1067 // close all opened handles on error 525 1068 if ( rc != NO_ERROR ) { 526 1069 #if defined(QT_QPROCESS_DEBUG) 527 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr, 528 "Failed to create a STDOUT redirection (%s): SYS%04ld\n\r", 529 pathBuf, rc ); 530 #endif 531 if ( tmpStdin != HF_ANY ) { 1070 qDebug( "Failed to create a STDOUT redirection (%s): SYS%04ld", 1071 pathBuf, rc ); 1072 #endif 1073 if ( tmpStdin != HF_NULL ) { 532 1074 DosDupHandle( tmpStdin, &realStdin ); 533 1075 DosClose( tmpStdin ); 534 1076 } 535 if ( tmpStdout != HF_ ANY)1077 if ( tmpStdout != HF_NULL ) 536 1078 DosClose ( tmpStdout ); 537 1079 d->closeHandles(); 1080 UninstallQtMsgHandler(); 538 1081 return FALSE; 539 1082 } 540 1083 } 541 1084 if ( (comms & Stderr) && !(comms & DupStderr) ) { 542 HFILE clientStderr = HF_ ANY;1085 HFILE clientStderr = HF_NULL; 543 1086 do { 544 1087 // create a Stderr pipe 545 1088 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stderr", 546 1089 ppib->pib_ulpid, this ); 547 rc = DosCreateNPipe( pathBuf, &d-> pipeStderr,1090 rc = DosCreateNPipe( pathBuf, &d->stderr.pipe, 548 1091 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1, 549 PIPE_SIZE, PIPE_SIZE, 0 );1092 0, PIPE_SIZE_STDERR, 0 ); 550 1093 if ( rc != NO_ERROR ) 551 1094 break; 552 DosConnectNPipe( d-> pipeStderr);1095 DosConnectNPipe( d->stderr.pipe ); 553 1096 // open a client end of the Stderr pipe 554 1097 ULONG action = 0; … … 566 1109 } while( 0 ); 567 1110 // close the client end anyway (no more necessary) 568 if ( clientStderr != HF_ ANY)1111 if ( clientStderr != HF_NULL ) 569 1112 DosClose ( clientStderr ); 570 1113 // close all opened handles on error 571 1114 if ( rc != NO_ERROR ) { 572 1115 #if defined(QT_QPROCESS_DEBUG) 573 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr, 574 "Failed to create a STDERR redirection (%s): SYS%04ld\n\r", 575 pathBuf, rc ); 576 #endif 577 if ( tmpStdin != HF_ANY ) { 1116 qDebug( "Failed to create a STDERR redirection (%s): SYS%04ld", 1117 pathBuf, rc ); 1118 #endif 1119 if ( tmpStdin != HF_NULL ) { 578 1120 DosDupHandle( tmpStdin, &realStdin ); 579 1121 DosClose( tmpStdin ); 580 1122 } 581 if ( tmpStdout != HF_ ANY) {1123 if ( tmpStdout != HF_NULL ) { 582 1124 DosDupHandle( tmpStdout, &realStdout ); 583 1125 DosClose( tmpStdout ); 584 1126 } 585 if ( tmpStderr != HF_ ANY)1127 if ( tmpStderr != HF_NULL ) 586 1128 DosClose ( tmpStderr ); 587 1129 d->closeHandles(); 1130 UninstallQtMsgHandler(); 588 1131 return FALSE; 589 1132 } 590 1133 } 591 1134 if ( comms & DupStderr ) { 592 d->pipeStderr = d->pipeStdout; 1135 // leave d->stderr.pipe equal to HP_NULL 593 1136 do { 594 1137 // save the real STDERR handle … … 602 1145 if ( rc != NO_ERROR ) { 603 1146 #if defined(QT_QPROCESS_DEBUG) 604 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr, 605 "Failed to redirect STDERR to STDOUT: SYS%04ld\n\r", 606 rc ); 607 #endif 608 if ( tmpStdin != HF_ANY ) { 1147 qDebug( "Failed to redirect STDERR to STDOUT: SYS%04ld", 1148 rc ); 1149 #endif 1150 if ( tmpStdin != HF_NULL ) { 609 1151 DosDupHandle( tmpStdin, &realStdin ); 610 1152 DosClose( tmpStdin ); 611 1153 } 612 if ( tmpStdout != HF_ ANY) {1154 if ( tmpStdout != HF_NULL ) { 613 1155 DosDupHandle( tmpStdout, &realStdout ); 614 1156 DosClose( tmpStdout ); 615 1157 } 616 if ( tmpStderr != HF_ ANY)1158 if ( tmpStderr != HF_NULL ) 617 1159 DosClose ( tmpStderr ); 618 1160 d->closeHandles(); 1161 UninstallQtMsgHandler(); 619 1162 return FALSE; 620 1163 } 621 1164 } 622 1165 1166 // add this process to the monitor 1167 if ( !processMonitor->addProcess( this ) ) { 1168 if ( tmpStdin != HF_NULL ) { 1169 DosDupHandle( tmpStdin, &realStdin ); 1170 DosClose( tmpStdin ); 1171 } 1172 if ( tmpStdout != HF_NULL ) { 1173 DosDupHandle( tmpStdout, &realStdout ); 1174 DosClose( tmpStdout ); 1175 } 1176 if ( tmpStderr != HF_NULL ) { 1177 DosDupHandle( tmpStderr, &realStderr ); 1178 DosClose ( tmpStderr ); 1179 } 1180 d->closeHandles(); 1181 UninstallQtMsgHandler(); 1182 return FALSE; 1183 } 1184 623 1185 // set the working directory 624 1186 QString curDir = QDir::currentDirPath(); 625 1187 QDir::setCurrent( workingDir.path() ); 626 1188 #if defined(QT_QPROCESS_DEBUG) 627 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr, 628 "QProcess::start(): curDir='%s', workingDir='%s'\n\r", 629 curDir.local8Bit().data(), 630 QDir::currentDirPath().local8Bit().data() ); 1189 qDebug( "QProcess::start(): curDir='%s', workingDir='%s'", 1190 curDir.local8Bit().data(), 1191 QDir::currentDirPath().local8Bit().data() ); 631 1192 #endif 632 1193 … … 642 1203 643 1204 // cancel STDIN/OUT/ERR redirections 644 if ( tmpStdin != HF_ ANY) {1205 if ( tmpStdin != HF_NULL ) { 645 1206 DosDupHandle( tmpStdin, &realStdin ); 646 1207 DosClose( tmpStdin ); 647 1208 } 648 if ( tmpStdout != HF_ ANY) {1209 if ( tmpStdout != HF_NULL ) { 649 1210 DosDupHandle( tmpStdout, &realStdout ); 650 1211 DosClose( tmpStdout ); 651 1212 } 652 if ( tmpStderr != HF_ ANY) {1213 if ( tmpStderr != HF_NULL ) { 653 1214 DosDupHandle( tmpStderr, &realStderr ); 654 1215 DosClose( tmpStderr ); 655 1216 } 1217 1218 UninstallQtMsgHandler(); 656 1219 657 1220 if ( rc != NO_ERROR ) { … … 664 1227 qSystemWarning( "Failed to start a new process!", rc ); 665 1228 #endif 1229 processMonitor->removeProcess( d ); 666 1230 d->closeHandles(); 667 1231 return FALSE; … … 671 1235 d->pid = resc.codeTerminate; 672 1236 673 if ( ioRedirection || notifyOnExit ) { 674 d->lookup->start( 100 ); 1237 // timer is not necessary for ioRedirection (we use the monitor thread) 1238 if ( /* ioRedirection || */ notifyOnExit ) { 1239 d->lookup->start( POLL_TIMER ); 675 1240 } 676 1241 … … 725 1290 if ( rc == ERROR_CHILD_NOT_COMPLETE ) 726 1291 return TRUE; 727 728 // there might be data to read 1292 729 1293 QProcess *that = (QProcess *) this; 730 that->socketRead( FD_PIPE_STDOUT ); // try stdout 731 that->socketRead( FD_PIPE_STDERR ); // try stderr 1294 1295 // There might be data to read, but WM_U_PIPE_RDATA messages won't be 1296 // converted to signals after removeProcess() is called below. Therefore, 1297 // emit signals ourselved if necessary. 1298 1299 if ( d->readPipe( &d->stdout ) ) 1300 emit that->readyReadStdout(); 1301 if ( d->readPipe( &d->stderr ) ) 1302 emit that->readyReadStderr(); 732 1303 733 1304 // compute the exit values … … 737 1308 d->exitValuesCalculated = TRUE; 738 1309 } 1310 1311 processMonitor->removeProcess( d ); 739 1312 740 1313 d->pid = PID_NULL; … … 745 1318 bool QProcess::canReadLineStdout() const 746 1319 { 747 if( d-> pipeStdout == HF_ANY)748 return d-> bufStdout.size() != 0;1320 if( d->stdout.pipe == HP_NULL ) 1321 return d->stdout.buf.size() != 0; 749 1322 750 1323 QProcess *that = (QProcess *) this; … … 754 1327 bool QProcess::canReadLineStderr() const 755 1328 { 756 if( d-> pipeStderr == HF_ANY)757 return d-> bufStderr.size() != 0;1329 if( d->stderr.pipe == HP_NULL ) 1330 return d->stderr.buf.size() != 0; 758 1331 759 1332 QProcess *that = (QProcess *) this; … … 769 1342 void QProcess::closeStdin( ) 770 1343 { 771 if ( d->pipeStdin != H F_ANY) {1344 if ( d->pipeStdin != HP_NULL ) { 772 1345 DosDisConnectNPipe( d->pipeStdin ); 773 1346 DosClose( d->pipeStdin ); 774 d->pipeStdin = HF_ANY; 775 } 776 } 777 778 void QProcess::socketRead( int fd ) 779 { 780 Q_ASSERT( fd == FD_PIPE_STDOUT || fd == FD_PIPE_STDERR ); 781 782 HPIPE pipe = HF_ANY; 783 switch( fd ) { 784 case FD_PIPE_STDOUT: pipe = d->pipeStdout; break; 785 case FD_PIPE_STDERR: pipe = d->pipeStderr; break; 786 } 787 788 if ( pipe == HF_ANY ) 789 return; 790 791 // get the number of bytes that are waiting to be read 792 ULONG read = 0; 793 AVAILDATA avail = { 0 }; 794 ULONG status = 0; 795 APIRET rc = DosPeekNPipe( pipe, NULL, 0, &read, &avail, &status ); 796 if ( rc != NO_ERROR ) { 797 #if defined(QT_QPROCESS_DEBUG) 798 qSystemWarning( "Failed to peek from the pipe!", rc ); 799 #endif 800 return; 801 } 802 803 if ( avail.cbpipe > 0 ) { 804 QMembuf *buffer; 805 if ( fd == FD_PIPE_STDOUT ) 806 buffer = &d->bufStdout; 807 else 808 buffer = &d->bufStderr; 809 810 QByteArray *ba = new QByteArray( avail.cbpipe ); 811 812 rc = DosRead( pipe, ba->data(), avail.cbpipe, &read ); 813 if ( rc != NO_ERROR ) { 814 delete ba; 815 #if defined(QT_QPROCESS_DEBUG) 816 qSystemWarning( "Failed to read from the pipe!", rc ); 817 #endif 818 return; 819 } 820 821 if ( read < avail.cbpipe ) 822 ba->resize( avail.cbpipe ); 823 824 buffer->append( ba ); 825 if ( fd == FD_PIPE_STDOUT ) 826 emit readyReadStdout(); 827 else 828 emit readyReadStderr(); 829 } 1347 d->pipeStdin = HP_NULL; 1348 } 1349 } 1350 1351 /* dummy, since MOC doesn't understand preprocessor defines */ 1352 void QProcess::socketRead( int fd ) 1353 { 830 1354 } 831 1355 832 1356 void QProcess::socketWrite( int ) 833 1357 { 834 if ( d->pipeStdin == H F_ANY)1358 if ( d->pipeStdin == HP_NULL ) 835 1359 return; 836 1360 … … 839 1363 APIRET rc = DosWrite( d->pipeStdin, 840 1364 d->stdinBuf.head()->data() + d->stdinBufRead, 841 QMIN( PIPE_SIZE, d->stdinBuf.head()->size() - d->stdinBufRead ), 1365 QMIN( PIPE_SIZE_STDIN, 1366 d->stdinBuf.head()->size() - d->stdinBufRead ), 842 1367 &written ); 843 1368 if ( rc != NO_ERROR ) { 844 #if defined(QT_ QPROCESS_DEBUG)1369 #if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG) 845 1370 qSystemWarning( "Failed to write to the pipe!", rc ); 846 1371 #endif 847 d->lookup->start( 100);1372 d->lookup->start( POLL_TIMER ); 848 1373 return; 849 1374 } … … 878 1403 socketWrite( 0 ); 879 1404 880 if ( ioRedirection ) {881 socketRead( FD_PIPE_STDOUT ); // try stdout882 socketRead( FD_PIPE_STDERR ); // try stderr883 }884 885 1405 if ( isRunning() ) { 886 1406 // enable timer again, if needed 887 if ( !d->stdinBuf.isEmpty() || ioRedirection || notifyOnExit ) 888 d->lookup->start( 100 ); 1407 // timer is not necessary for ioRedirection (we use the monitor thread) 1408 if ( !d->stdinBuf.isEmpty() || /* ioRedirection || */ notifyOnExit ) 1409 d->lookup->start( POLL_TIMER ); 889 1410 } else if ( notifyOnExit ) { 890 1411 emit processExited(); … … 899 1420 { 900 1421 ioRedirection = value; 1422 // timer is not necessary for ioRedirection (we use the monitor thread) 1423 #if 0 901 1424 if ( !ioRedirection && !notifyOnExit ) 902 1425 d->lookup->stop(); 903 1426 if ( ioRedirection ) { 904 1427 if ( isRunning() ) 905 d->lookup->start( 100 ); 906 } 1428 d->lookup->start( POLL_TIMER ); 1429 } 1430 #endif 907 1431 } 908 1432 … … 914 1438 { 915 1439 notifyOnExit = value; 916 if ( !ioRedirection && !notifyOnExit ) 1440 // timer is not necessary for ioRedirection (we use the monitor thread) 1441 if ( /* !ioRedirection && */ !notifyOnExit ) 917 1442 d->lookup->stop(); 918 1443 if ( notifyOnExit ) { 919 1444 if ( isRunning() ) 920 d->lookup->start( 100);1445 d->lookup->start( POLL_TIMER ); 921 1446 } 922 1447 }
Note:
See TracChangeset
for help on using the changeset viewer.