2007-09-07 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / kdpipeiodevice.cpp
1 /*
2   Copyright (C) 2007 Klarälvdalens Datakonsult AB
3
4   KDPipeIODevice is free software; you can redistribute it and/or
5   modify it under the terms of the GNU Library General Public
6   License as published by the Free Software Foundation; either
7   version 2 of the License, or (at your option) any later version.
8
9   KDPipeIODevice is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU Library General Public License for more details.
13
14   You should have received a copy of the GNU Library General Public License
15   along with KDPipeIODevice; see the file COPYING.LIB.  If not, write to the
16   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17   Boston, MA 02110-1301, USA.
18 */
19
20 #include "kdpipeiodevice.h"
21
22 #include <QtCore>
23
24 #include <cassert>
25 #include <memory>
26 #include <algorithm>
27
28 #ifdef Q_OS_WIN32
29 # define NOMINMAX
30 # include <windows.h>
31 # include <io.h>
32 #else
33 # include <unistd.h>
34 # include <errno.h>
35 #endif
36
37 #ifndef KDAB_CHECK_THIS
38 # define KDAB_CHECK_CTOR (void)1
39 # define KDAB_CHECK_DTOR KDAB_CHECK_CTOR
40 # define KDAB_CHECK_THIS KDAB_CHECK_CTOR
41 #endif
42
43 #define LOCKED( d ) const QMutexLocker locker( &d->mutex )
44 #define synchronized( d ) if ( int i = 0 ) {} else for ( const QMutexLocker locker( &d->mutex ) ; !i ; ++i )
45
46 const unsigned int BUFFER_SIZE = 4096;
47 const bool ALLOW_QIODEVICE_BUFFERING = true;
48
49 // comment to get trace output:
50 #define qDebug if(1){}else qDebug
51
52 namespace {
53 class Reader : public QThread {
54     Q_OBJECT
55 public:
56     Reader( int fd, Qt::HANDLE handle );
57     ~Reader();
58
59     qint64 readData( char * data, qint64 maxSize );
60
61     unsigned int bytesInBuffer() const {
62         return ( wptr + sizeof buffer - rptr ) % sizeof buffer ;
63     }
64
65     bool bufferFull() const {
66         return bytesInBuffer() == sizeof buffer - 1;
67     }
68
69     bool bufferEmpty() const {
70         return bytesInBuffer() == 0;
71     }
72
73     bool bufferContains( char ch ) {
74         const unsigned int bib = bytesInBuffer();
75         for ( unsigned int i = rptr ; i < rptr + bib ; ++i )
76             if ( buffer[i%sizeof buffer] == ch )
77                 return true;
78         return false;
79     }
80         
81 Q_SIGNALS:
82     void readyRead();
83
84 protected:
85     /* reimp */ void run();
86
87 private:
88     int fd;
89     Qt::HANDLE handle;
90 public:
91     QMutex mutex;
92     QWaitCondition bufferNotFullCondition;
93     QWaitCondition bufferNotEmptyCondition;
94     QWaitCondition hasStarted;
95     bool cancel;
96     bool eof;
97     bool error;
98     bool eofShortCut;
99     int errorCode;
100 private:
101     unsigned int rptr, wptr;
102     char buffer[BUFFER_SIZE+1]; // need to keep one byte free to detect empty state
103 };
104
105
106 Reader::Reader( int fd_, Qt::HANDLE handle_ )
107     : QThread(),
108       fd( fd_ ),
109       handle( handle_ ),
110       mutex(),
111       bufferNotFullCondition(),
112       bufferNotEmptyCondition(),
113       hasStarted(),
114       cancel( false ),
115       eof( false ),
116       error( false ),
117       eofShortCut( false ),
118       errorCode( 0 ),
119       rptr( 0 ), wptr( 0 )
120 {
121     
122 }
123
124 Reader::~Reader() {}
125
126
127 class Writer : public QThread {
128     Q_OBJECT
129 public:
130     Writer( int fd, Qt::HANDLE handle );
131     ~Writer();
132
133     qint64 writeData( const char * data, qint64 size );
134
135     unsigned int bytesInBuffer() const { return numBytesInBuffer; }
136
137     bool bufferFull() const {
138         return numBytesInBuffer == sizeof buffer;
139     }
140
141     bool bufferEmpty() const {
142         return numBytesInBuffer == 0;
143     }
144
145 Q_SIGNALS:
146     void bytesWritten( qint64 );
147
148 protected:
149     /* reimp */ void run();
150
151 private:
152     int fd;
153     Qt::HANDLE handle;
154 public:
155     QMutex mutex;
156     QWaitCondition bufferEmptyCondition;
157     QWaitCondition bufferNotEmptyCondition;
158     QWaitCondition hasStarted;
159     bool cancel;
160     bool error;
161     int errorCode;
162 private:
163     unsigned int numBytesInBuffer;
164     char buffer[BUFFER_SIZE];
165 };
166 }
167
168 Writer::Writer( int fd_, Qt::HANDLE handle_ )
169     : QThread(),
170       fd( fd_ ),
171       handle( handle_ ),
172       mutex(),
173       bufferEmptyCondition(),
174       bufferNotEmptyCondition(),
175       hasStarted(),
176       cancel( false ),
177       error( false ),
178       errorCode( 0 ),
179       numBytesInBuffer( 0 )
180 {
181
182 }
183
184 Writer::~Writer() {}
185
186
187 class KDPipeIODevice::Private {
188     friend class ::KDPipeIODevice;
189     KDPipeIODevice * const q;
190 public:
191     explicit Private( KDPipeIODevice * qq );
192     ~Private();
193
194     bool doOpen( int, Qt::HANDLE, OpenMode );
195
196 private:
197     int fd;
198     Qt::HANDLE handle;
199     Reader * reader;
200     Writer * writer;
201 };
202
203 KDPipeIODevice::Private::Private( KDPipeIODevice * qq )
204     : q( qq ),
205       fd( -1 ),
206       handle( 0 ),
207       reader( 0 ),
208       writer( 0 )
209 {
210
211 }
212
213
214 KDPipeIODevice::Private::~Private() {}
215
216
217 KDPipeIODevice::KDPipeIODevice( QObject * p )
218     : QIODevice( p ), d( new Private( this ) )
219 {
220     KDAB_CHECK_CTOR;
221 }
222
223 KDPipeIODevice::KDPipeIODevice( int fd, OpenMode mode, QObject * p )
224     : QIODevice( p ), d( new Private( this ) )
225 {
226     KDAB_CHECK_CTOR;
227     open( fd, mode );
228 }
229
230 KDPipeIODevice::KDPipeIODevice( Qt::HANDLE handle, OpenMode mode, QObject * p )
231     : QIODevice( p ), d( new Private( this ) )
232 {
233     KDAB_CHECK_CTOR;
234     open( handle, mode );
235 }
236
237 KDPipeIODevice::~KDPipeIODevice() { KDAB_CHECK_DTOR;
238     if ( isOpen() )
239         close();
240     delete d; d = 0;
241 }
242
243
244 bool KDPipeIODevice::open( int fd, OpenMode mode ) { KDAB_CHECK_THIS;
245
246 #ifdef Q_OS_WIN32
247     return d->doOpen( fd, (HANDLE)_get_osfhandle( fd ), mode );
248 #else
249     return d->doOpen( fd, 0, mode );
250 #endif
251
252 }
253
254 bool KDPipeIODevice::open( Qt::HANDLE h, OpenMode mode ) { KDAB_CHECK_THIS;
255
256 #ifdef Q_OS_WIN32
257     return d->doOpen( 0, h, mode );
258 #else
259     Q_UNUSED( h );
260     Q_UNUSED( mode );
261     assert( !"KDPipeIODevice::open( Qt::HANDLE, OpenMode ) should never be called except on Windows." );
262 #endif
263
264 }
265
266 bool KDPipeIODevice::Private::doOpen( int fd_, Qt::HANDLE handle_, OpenMode mode_ ) {
267
268     if ( q->isOpen() || fd_ < 0 )
269         return false;
270
271 #ifdef Q_OS_WIN32
272     if ( !handle_ )
273         return false;
274 #endif
275
276     if ( !(mode_ & ReadWrite) )
277         return false; // need to have at least read -or- write
278
279     fd = fd_;
280     handle = handle_;
281
282     std::auto_ptr<Reader> reader_;
283     std::auto_ptr<Writer> writer_;
284
285     if ( mode_ & ReadOnly ) {
286         reader_.reset( new Reader( fd_, handle_ ) );
287         LOCKED( reader_ );
288         reader_->start( QThread::HighestPriority );
289         if ( !reader_->hasStarted.wait( &reader_->mutex, 1000 ) )
290             return false;
291         connect( reader_.get(), SIGNAL(readyRead()), q, SIGNAL(readyRead()), Qt::QueuedConnection );
292     }
293     if ( mode_ & WriteOnly ) {
294         writer_.reset( new Writer( fd_, handle_ ) );
295         LOCKED( writer_ );
296         writer_->start( QThread::HighestPriority );
297         if ( !writer_->hasStarted.wait( &writer_->mutex, 1000 ) )
298             return false;
299         connect( writer_.get(), SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)), Qt::QueuedConnection );
300     }
301
302     // commit to *this:
303     fd = fd_;
304     handle = handle_;
305     reader = reader_.release();
306     writer = writer_.release();
307
308     q->setOpenMode( mode_|Unbuffered );
309
310     return true;
311 }
312
313 int KDPipeIODevice::descriptor() const { KDAB_CHECK_THIS;
314     return d->fd;
315 }
316
317 Qt::HANDLE KDPipeIODevice::handle() const { KDAB_CHECK_THIS;
318     return d->handle;
319 }
320
321 qint64 KDPipeIODevice::bytesAvailable() const { KDAB_CHECK_THIS;
322     const qint64 base = QIODevice::bytesAvailable();
323     if ( d->reader )
324         synchronized( d->reader ) return base + d->reader->bytesInBuffer();
325     return base;
326 }
327
328 qint64 KDPipeIODevice::bytesToWrite() const { KDAB_CHECK_THIS;
329     const qint64 base = QIODevice::bytesToWrite();
330     if ( d->writer )
331         synchronized( d->writer ) return base + d->writer->bytesInBuffer();
332     return base;
333 }
334
335 bool KDPipeIODevice::canReadLine() const { KDAB_CHECK_THIS;
336     if ( QIODevice::canReadLine() )
337         return true;
338     if ( d->reader )
339         synchronized( d->reader ) return d->reader->bufferContains( '\n' );
340     return true;
341 }
342
343 bool KDPipeIODevice::isSequential() const {
344     return true;
345 }
346
347 bool KDPipeIODevice::atEnd() const { KDAB_CHECK_THIS;
348     if ( !QIODevice::atEnd() ) {
349         qDebug( "KDPipeIODevice::atEnd returns false since QIODevice::atEnd does (with bytesAvailable=%ld)", static_cast<long>(bytesAvailable()) );
350         return false;
351     }
352     if ( !isOpen() )
353         return true;
354     if ( d->reader->eofShortCut )
355         return true;
356     LOCKED( d->reader );
357     const bool eof = ( d->reader->error || d->reader->eof ) && d->reader->bufferEmpty();
358     if ( !eof ) {
359         if ( !d->reader->error && !d->reader->eof )
360             qDebug( "KDPipeIODevice::atEnd returns false since !reader->error && !reader->eof" );
361         if ( !d->reader->bufferEmpty() )
362             qDebug( "KDPipeIODevice::atEnd returns false since !reader->bufferEmpty()" );
363     }
364     return eof;
365 }
366
367 bool KDPipeIODevice::waitForBytesWritten( int msecs ) { KDAB_CHECK_THIS;
368     Writer * const w = d->writer;
369     if ( !w )
370         return true;
371     LOCKED( w );
372     return w->bufferEmpty() || w->error || w->bufferEmptyCondition.wait( &w->mutex, msecs ) ;
373 }
374
375 bool KDPipeIODevice::waitForReadyRead( int msecs ) { KDAB_CHECK_THIS;
376     if ( ALLOW_QIODEVICE_BUFFERING ) {
377         if ( bytesAvailable() > 0 )
378             return true;
379     }
380     Reader * const r = d->reader;
381     if ( !r || r->eofShortCut )
382         return true;
383     LOCKED( r );
384     return r->bytesInBuffer() != 0 || r->eof || r->error || r->bufferNotEmptyCondition.wait( &r->mutex, msecs ) ;
385 }
386
387 qint64 KDPipeIODevice::readData( char * data, qint64 maxSize ) { KDAB_CHECK_THIS;
388
389     qDebug( "KDPipeIODevice::readData: data=%p, maxSize=%lld", data, maxSize );
390
391     Reader * const r = d->reader;
392
393     assert( r );
394     //assert( r->isRunning() ); // wrong (might be eof, error)
395     assert( data || maxSize == 0 );
396     assert( maxSize >= 0 );
397
398     if ( r->eofShortCut ) {
399         qDebug( "KDPipeIODevice::readData: hit eofShortCut, returning 0" );
400         return 0;
401     }
402
403     if ( maxSize < 0 )
404         maxSize = 0;
405
406     if ( ALLOW_QIODEVICE_BUFFERING ) {
407         if ( bytesAvailable() > 0 )
408             maxSize = std::min( maxSize, bytesAvailable() ); // don't block
409     }
410
411     LOCKED( r );
412     if ( /* maxSize > 0 && */ r->bufferEmpty() && !r->error && !r->eof ) { // ### block on maxSize == 0?
413         qDebug( "KDPipeIODevice::readData: waiting for bufferNotEmptyCondition" );
414         r->bufferNotEmptyCondition.wait( &r->mutex );
415     }
416
417     if ( r->bufferEmpty() ) {
418         qDebug( "KDPipeIODevice::readData: got empty buffer, signal eof" );
419         // woken with an empty buffer must mean either EOF or error:
420         assert( r->eof || r->error );
421         r->eofShortCut = true;
422         return r->eof ? 0 : -1 ;
423     }
424
425     qDebug( "KDPipeIODevice::readData: got bufferNotEmptyCondition, trying to read %lld bytes", maxSize );
426     const qint64 bytesRead = r->readData( data, maxSize );
427     qDebug( "KDPipeIODevice::readData: read %lld bytes", bytesRead );
428     return bytesRead;
429 }
430
431 qint64 Reader::readData( char * data, qint64 maxSize ) {
432
433     qint64 numRead = rptr < wptr ? wptr - rptr : sizeof buffer - rptr ;
434     if ( numRead > maxSize )
435         numRead = maxSize;
436
437     qDebug( "KDPipeIODevice::readData: data=%p, maxSize=%lld; rptr=%u, wptr=%u (bytesInBuffer=%u); -> numRead=%lld",
438             data, maxSize, rptr, wptr, bytesInBuffer(), numRead );
439
440     std::memcpy( data, buffer + rptr, numRead );
441
442     rptr = ( rptr + numRead ) % sizeof buffer ;
443
444     if ( !bufferFull() ) {
445         qDebug( "KDPipeIODevice::readData: signal bufferNotFullCondition" );
446         bufferNotFullCondition.wakeAll();
447     }
448
449     return numRead;
450 }
451
452 qint64 KDPipeIODevice::writeData( const char * data, qint64 size ) { KDAB_CHECK_THIS;
453
454     Writer * const w = d->writer;
455
456     assert( w );
457     assert( w->error || w->isRunning() );
458     assert( data || size == 0 );
459     assert( size >= 0 );
460
461     LOCKED( w );
462
463     while ( !w->error && !w->bufferEmpty() )
464         w->bufferEmptyCondition.wait( &w->mutex );
465
466     if ( w->error )
467         return -1;
468
469     assert( w->bufferEmpty() );
470
471     return w->writeData( data, size );
472 }
473
474 qint64 Writer::writeData( const char * data, qint64 size ) {
475
476     assert( bufferEmpty() );
477
478     if ( size > static_cast<qint64>( sizeof buffer ) )
479         size = sizeof buffer;
480
481     std::memcpy( buffer, data, size );
482     
483     numBytesInBuffer = size;
484
485     if ( !bufferEmpty() )
486         bufferNotEmptyCondition.wakeAll();
487
488     return size;
489 }
490
491 void KDPipeIODevice::close() { KDAB_CHECK_THIS;
492
493     if ( !isOpen() )
494         return;
495
496     // tell clients we're about to close:
497     emit aboutToClose();
498
499     if ( d->writer && bytesToWrite() > 0 )
500         waitForBytesWritten( -1 );
501
502     assert( bytesToWrite() == 0 );
503
504     if ( Reader * & r = d->reader ) {
505         synchronized( r ) {
506             // tell thread to cancel:
507             r->cancel = true;
508             // and wake it, so it can terminate:
509             r->bufferNotFullCondition.wakeAll();
510         }
511         r->wait();
512         delete r; r = 0;
513     }
514     if ( Writer * & w = d->writer ) {
515         synchronized( w ) {
516             // tell thread to cancel:
517             w->cancel = true;
518             // and wake it, so it can terminate:
519             w->bufferNotEmptyCondition.wakeAll();
520         }
521         w->wait();
522         delete w; w = 0;
523     }
524
525 #ifdef Q_OS_WIN32
526     CloseHandle( d->handle );
527 #else
528     ::close( d->fd );
529 #endif
530
531     setOpenMode( NotOpen );
532     d->fd = -1;
533     d->handle = 0;
534 }
535
536 void Reader::run() {
537
538     LOCKED( this );
539
540     // too bad QThread doesn't have that itself; a signal isn't enough
541     hasStarted.wakeAll();
542
543     qDebug( "Reader::run: started" );
544
545     while ( true ) {
546
547         while ( !cancel && bufferFull() ) {
548             bufferNotEmptyCondition.wakeAll();
549             qDebug( "Reader::run: buffer is full, going to sleep" );
550             bufferNotFullCondition.wait( &mutex );
551             qDebug( "Reader::run: woke up" );
552         }
553
554         if ( cancel ) {
555             qDebug( "Reader::run: detected cancel" );
556             goto leave;
557         }
558
559         if ( rptr == wptr ) // optimize for larger chunks in case the buffer is empty
560             rptr = wptr = 0;
561
562         unsigned int numBytes = ( rptr + sizeof buffer - wptr - 1 ) % sizeof buffer;
563         if ( numBytes > sizeof buffer - wptr )
564             numBytes = sizeof buffer - wptr;
565
566         qDebug( "Reader::run: rptr=%d, wptr=%d -> numBytes=%d", rptr, wptr, numBytes );
567
568         assert( numBytes > 0 );
569
570         qDebug( "Reader::run: trying to read %d bytes", numBytes );
571 #ifdef Q_OS_WIN32
572         DWORD numRead;
573         mutex.unlock();
574         const bool ok = ReadFile( handle, buffer + wptr, numBytes, &numRead, 0 );
575         mutex.lock();
576         if ( !ok ) {
577             errorCode = static_cast<int>( GetLastError() );
578             if ( errorCode == ERROR_BROKEN_PIPE ) {
579                 qDebug( "Reader::run: got eof" );
580                 eof = true;
581             } else {
582                 qDebug( "Reader::run: got error: %d", errorCode );
583                 error = true;
584             }
585             goto leave;
586         }
587 #else
588         qint64 numRead;
589         mutex.unlock();
590         do {
591             numRead = ::read( fd, buffer + wptr, numBytes );
592         } while ( numRead == -1 && errno == EINTR );
593         mutex.lock();
594
595         if ( numRead < 0 ) {
596             errorCode = errno;
597             error = true;
598             qDebug( "Reader::run: got error: %d", errorCode );
599             goto leave;
600         }
601 #endif
602         qDebug( "Reader::run: read %ld bytes", static_cast<long>(numRead) );
603         if ( numRead == 0 ) {
604             qDebug( "Reader::run: eof detected" );
605             eof = true;
606             goto leave;
607         }
608
609         if ( cancel ) {
610             qDebug( "Reader::run: detected cancel" );
611             goto leave;
612         }
613         qDebug( "Reader::run: buffer before: rptr=%4d, wptr=%4d", rptr, wptr );
614         wptr = ( wptr + numRead ) % sizeof buffer;
615         qDebug( "Reader::run: buffer after:  rptr=%4d, wptr=%4d", rptr, wptr );
616         if ( !bufferEmpty() ) {
617             qDebug( "Reader::run: buffer no longer empty, waking everyone" );
618             bufferNotEmptyCondition.wakeAll();
619             emit readyRead();
620         }
621     }
622  leave:
623     qDebug( "Reader::run: terminating" );
624     bufferNotEmptyCondition.wakeAll();
625     emit readyRead();
626 }
627
628 void Writer::run() {
629
630     LOCKED( this );
631
632     // too bad QThread doesn't have that itself; a signal isn't enough
633     hasStarted.wakeAll();
634
635     qDebug( "Writer::run: started" );
636
637     while ( true ) {
638
639         while ( !cancel && bufferEmpty() ) {
640             bufferEmptyCondition.wakeAll();
641             qDebug( "Writer::run: buffer is empty, going to sleep" );
642             bufferNotEmptyCondition.wait( &mutex );
643             qDebug( "Writer::run: woke up" );
644         }
645
646         if ( cancel ) {
647             qDebug( "Writer::run: detected cancel" );
648             goto leave;
649         }
650
651         assert( numBytesInBuffer > 0 );
652
653         qDebug( "Writer::run: Trying to write %u bytes", numBytesInBuffer );
654         qint64 totalWritten = 0;
655         do { 
656             mutex.unlock();
657 #ifdef Q_OS_WIN32
658             DWORD numWritten;
659             if ( !WriteFile( handle, buffer + totalWritten, numBytesInBuffer - totalWritten, &numWritten, 0 ) ) {
660                 mutex.lock();
661                 errorCode = static_cast<int>( GetLastError() );
662                 qDebug( "Writer::run: got error code: %d", errorCode );
663                 error = true;
664                 goto leave;
665             }
666 #else
667             qint64 numWritten;
668             do {
669                 numWritten = ::write( fd, buffer + totalWritten, numBytesInBuffer - totalWritten );
670             } while ( numWritten == -1 && errno == EINTR );
671
672             if ( numWritten < 0 ) {
673                 mutex.lock();
674                 errorCode = errno;
675                 qDebug( "Writer::run: got error code: %d", errorCode );
676                 error = true;
677                 goto leave;
678             }
679 #endif
680             totalWritten += numWritten;
681             mutex.lock();
682         } while ( totalWritten < numBytesInBuffer );
683
684         qDebug( "Writer::run: wrote %lld bytes", totalWritten );
685
686         numBytesInBuffer = 0;
687         bufferEmptyCondition.wakeAll();
688         emit bytesWritten( totalWritten );
689     }
690  leave:
691     qDebug( "Writer::run: terminating" );
692     numBytesInBuffer = 0;
693     bufferEmptyCondition.wakeAll();
694     emit bytesWritten( 0 );
695 }
696
697 // static 
698 std::pair<KDPipeIODevice*,KDPipeIODevice*> KDPipeIODevice::makePairOfConnectedPipes() {
699     KDPipeIODevice * read = 0;
700     KDPipeIODevice * write = 0;
701 #ifdef Q_OS_WIN32
702     HANDLE rh;
703     HANDLE wh;
704     SECURITY_ATTRIBUTES sa;
705     memset( &sa, 0, sizeof(sa) );
706     sa.nLength = sizeof(sa);
707     sa.bInheritHandle = TRUE;
708         if ( CreatePipe( &rh, &wh, &sa, BUFFER_SIZE ) ) {
709         read = new KDPipeIODevice;
710         read->open( rh, ReadOnly );
711         write = new KDPipeIODevice;
712         write->open( wh, WriteOnly );
713     }
714 #else
715     int fds[2];
716     if ( pipe( fds ) == 0 ) {
717         read = new KDPipeIODevice;
718         read->open( fds[0], ReadOnly );
719         write = new KDPipeIODevice;
720         write->open( fds[1], WriteOnly );
721     }
722 #endif
723     return std::make_pair( read, write );
724 }
725
726 #ifdef KDAB_DEFINE_CHECKS
727 KDAB_DEFINE_CHECKS( KDPipeIODevice ) {
728     if ( !isOpen() ) {
729         assert( openMode() == NotOpen );
730         assert( !d->reader );
731         assert( !d->writer );
732 #ifdef Q_OS_WIN32
733         assert( !d->handle );
734 #else
735         assert( d->fd < 0 );
736 #endif
737     } else {
738         assert( openMode() != NotOpen );
739         assert( openMode() & ReadWrite );
740         if ( openMode() & ReadOnly ) {
741             assert( d->reader );
742             synchronized( d->reader )
743                 assert( d->reader->eof || d->reader->error || d->reader->isRunning() );
744         }
745         if ( openMode() & WriteOnly ) {
746             assert( d->writer );
747             synchronized( d->writer )
748                 assert( d->writer->error || d->writer->isRunning() );
749         }
750 #ifdef Q_OS_WIN32
751         assert( d->handle );
752 #else
753         assert( d->fd >= 0 );
754 #endif
755     }
756 }
757 #endif // KDAB_DEFINE_CHECKS
758
759 #include "moc_kdpipeiodevice.cpp"
760 #include "kdpipeiodevice.moc"