2007-10-02 Marcus Brinkmann <marcus@g10code.de>

* kdpipeiodevice.cpp, kdpipeiodevice.moc: New versions.
	* w32-qt-io.cpp (_gpgme_io_fd2str): Print actual_fd if available.
	(_gpgme_io_dup): Only acquire a reference, do not actually dup.
	Submitted by Frank Osterfeld.
This commit is contained in:
Marcus Brinkmann 2007-10-02 15:56:37 +00:00
parent d8289000fe
commit 228ca8fab2
4 changed files with 149 additions and 39 deletions

View File

@ -1,5 +1,10 @@
2007-10-02 Marcus Brinkmann <marcus@g10code.de> 2007-10-02 Marcus Brinkmann <marcus@g10code.de>
* kdpipeiodevice.cpp, kdpipeiodevice.moc: New versions.
* w32-qt-io.cpp (_gpgme_io_fd2str): Print actual_fd if available.
(_gpgme_io_dup): Only acquire a reference, do not actually dup.
Submitted by Frank Osterfeld.
* priv-io.h, engine-gpgsm.c: Add comments. * priv-io.h, engine-gpgsm.c: Add comments.
* w32-qt-io.cpp (_gpgme_io_select): Remove code handling frozen FDs. * w32-qt-io.cpp (_gpgme_io_select): Remove code handling frozen FDs.
* w32-glib-io.c (_gpgme_io_close): Always dereference the channel, * w32-glib-io.c (_gpgme_io_close): Always dereference the channel,

View File

@ -82,6 +82,8 @@ public:
return false; return false;
} }
void notifyReadyRead();
Q_SIGNALS: Q_SIGNALS:
void readyRead(); void readyRead();
@ -96,11 +98,15 @@ public:
QWaitCondition bufferNotFullCondition; QWaitCondition bufferNotFullCondition;
QWaitCondition bufferNotEmptyCondition; QWaitCondition bufferNotEmptyCondition;
QWaitCondition hasStarted; QWaitCondition hasStarted;
QWaitCondition readyReadSentCondition;
QWaitCondition notInReadDataCondition;
bool cancel; bool cancel;
bool eof; bool eof;
bool error; bool error;
bool eofShortCut; bool eofShortCut;
int errorCode; int errorCode;
bool inReadData;
private: private:
unsigned int rptr, wptr; unsigned int rptr, wptr;
char buffer[BUFFER_SIZE+1]; // need to keep one byte free to detect empty state char buffer[BUFFER_SIZE+1]; // need to keep one byte free to detect empty state
@ -120,7 +126,8 @@ Reader::Reader( int fd_, Qt::HANDLE handle_ )
error( false ), error( false ),
eofShortCut( false ), eofShortCut( false ),
errorCode( 0 ), errorCode( 0 ),
rptr( 0 ), wptr( 0 ) rptr( 0 ), wptr( 0 ),
inReadData( false )
{ {
} }
@ -203,6 +210,9 @@ public:
bool triedToStartReader; bool triedToStartReader;
bool triedToStartWriter; bool triedToStartWriter;
public Q_SLOTS:
void emitReadyRead();
private: private:
int fd; int fd;
Qt::HANDLE handle; Qt::HANDLE handle;
@ -221,7 +231,9 @@ KDPipeIODevice::Private::Private( KDPipeIODevice * qq )
} }
KDPipeIODevice::Private::~Private() {} KDPipeIODevice::Private::~Private() {
qDebug( "KDPipeIODevice::~Private(): Destroying %p", this );
}
KDPipeIODevice::KDPipeIODevice( QObject * p ) KDPipeIODevice::KDPipeIODevice( QObject * p )
: QIODevice( p ), d( new Private( this ) ) : QIODevice( p ), d( new Private( this ) )
@ -302,6 +314,21 @@ bool KDPipeIODevice::Private::startWriterThread()
return true; return true;
} }
void KDPipeIODevice::Private::emitReadyRead()
{
static int s_counter = 0;
const int counter = s_counter++;
QPointer<Private> thisPointer( this );
qDebug( "KDPipeIODevice::Private::emitReadyRead %p, %d", this, counter );
emit q->readyRead();
if ( !thisPointer )
return;
LOCKED( reader );
reader->readyReadSentCondition.wakeAll();
qDebug( "KDPipeIODevice::Private::emitReadyRead %p leaving %d", this, counter );
}
bool KDPipeIODevice::Private::doOpen( int fd_, Qt::HANDLE handle_, OpenMode mode_ ) { bool KDPipeIODevice::Private::doOpen( int fd_, Qt::HANDLE handle_, OpenMode mode_ ) {
if ( q->isOpen() || fd_ < 0 ) if ( q->isOpen() || fd_ < 0 )
@ -323,13 +350,13 @@ bool KDPipeIODevice::Private::doOpen( int fd_, Qt::HANDLE handle_, OpenMode mode
if ( mode_ & ReadOnly ) { if ( mode_ & ReadOnly ) {
reader_.reset( new Reader( fd_, handle_ ) ); reader_.reset( new Reader( fd_, handle_ ) );
qDebug( "KDPipeIODevice::doOpen: created reader for fd %d", fd_ ); qDebug( "KDPipeIODevice::doOpen (%p): created reader (%p) for fd %d", this, reader_.get(), fd_ );
connect( reader_.get(), SIGNAL(readyRead()), q, connect( reader_.get(), SIGNAL(readyRead()), this, SLOT(emitReadyRead()),
SIGNAL(readyRead()), Qt::QueuedConnection ); Qt::QueuedConnection );
} }
if ( mode_ & WriteOnly ) { if ( mode_ & WriteOnly ) {
writer_.reset( new Writer( fd_, handle_ ) ); writer_.reset( new Writer( fd_, handle_ ) );
qDebug( "KDPipeIODevice::doOpen: created writer for fd %d", fd_ ); qDebug( "KDPipeIODevice::doOpen (%p): created writer (%p) for fd %d", this, writer_.get(), fd_ );
connect( writer_.get(), SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)), connect( writer_.get(), SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)),
Qt::QueuedConnection ); Qt::QueuedConnection );
} }
@ -412,6 +439,8 @@ bool KDPipeIODevice::waitForBytesWritten( int msecs ) { KDAB_CHECK_THIS;
if ( !w ) if ( !w )
return true; return true;
LOCKED( w ); LOCKED( w );
qDebug( "KDPipeIODevice::waitForBytesWritten (%p,w=%p): entered locked area", this, w
);
return w->bufferEmpty() || w->error || w->bufferEmptyCondition.wait( &w->mutex, msecs ) ; return w->bufferEmpty() || w->error || w->bufferEmptyCondition.wait( &w->mutex, msecs ) ;
} }
@ -428,17 +457,24 @@ bool KDPipeIODevice::waitForReadyRead( int msecs ) { KDAB_CHECK_THIS;
return r->bytesInBuffer() != 0 || r->eof || r->error || r->bufferNotEmptyCondition.wait( &r->mutex, msecs ) ; return r->bytesInBuffer() != 0 || r->eof || r->error || r->bufferNotEmptyCondition.wait( &r->mutex, msecs ) ;
} }
template <typename T>
class TemporaryValue {
public:
TemporaryValue( T& var_, const T& tv ) : var( var_ ), oldValue( var_ ) { var = tv; }
~TemporaryValue() { var = oldValue; }
private:
T& var;
const T oldValue;
};
qint64 KDPipeIODevice::readData( char * data, qint64 maxSize ) { KDAB_CHECK_THIS; qint64 KDPipeIODevice::readData( char * data, qint64 maxSize ) { KDAB_CHECK_THIS;
qDebug( "%p: KDPipeIODevice::readData: data=%p, maxSize=%lld", this, data, maxSize ); qDebug( "%p: KDPipeIODevice::readData: data=%p, maxSize=%lld", this, data, maxSize );
if ( maxSize == 0 )
return 0;
d->startReaderThread(); d->startReaderThread();
Reader * const r = d->reader; Reader * const r = d->reader;
assert( r ); assert( r );
//assert( r->isRunning() ); // wrong (might be eof, error) //assert( r->isRunning() ); // wrong (might be eof, error)
assert( data || maxSize == 0 ); assert( data || maxSize == 0 );
assert( maxSize >= 0 ); assert( maxSize >= 0 );
@ -455,10 +491,14 @@ qint64 KDPipeIODevice::readData( char * data, qint64 maxSize ) { KDAB_CHECK_THIS
if ( bytesAvailable() > 0 ) if ( bytesAvailable() > 0 )
maxSize = std::min( maxSize, bytesAvailable() ); // don't block maxSize = std::min( maxSize, bytesAvailable() ); // don't block
} }
LOCKED( r ); LOCKED( r );
if ( /* maxSize > 0 && */ r->bufferEmpty() && !r->error && !r->eof ) { // ### block on maxSize == 0? const TemporaryValue<bool> tmp( d->reader->inReadData, true );
assert( d->reader->inReadData );
while ( /* maxSize > 0 && */ r->bufferEmpty() && !r->error && !r->eof ) { // ### block on maxSize == 0?
qDebug( "%p: KDPipeIODevice::readData: waiting for bufferNotEmptyCondition", this ); qDebug( "%p: KDPipeIODevice::readData: waiting for bufferNotEmptyCondition", this );
r->readyReadSentCondition.wakeAll();
r->notInReadDataCondition.wakeAll();
r->bufferNotEmptyCondition.wait( &r->mutex ); r->bufferNotEmptyCondition.wait( &r->mutex );
} }
@ -467,6 +507,7 @@ qint64 KDPipeIODevice::readData( char * data, qint64 maxSize ) { KDAB_CHECK_THIS
// woken with an empty buffer must mean either EOF or error: // woken with an empty buffer must mean either EOF or error:
assert( r->eof || r->error ); assert( r->eof || r->error );
r->eofShortCut = true; r->eofShortCut = true;
r->notInReadDataCondition.wakeAll();
return r->eof ? 0 : -1 ; return r->eof ? 0 : -1 ;
} }
@ -474,6 +515,7 @@ qint64 KDPipeIODevice::readData( char * data, qint64 maxSize ) { KDAB_CHECK_THIS
const qint64 bytesRead = r->readData( data, maxSize ); const qint64 bytesRead = r->readData( data, maxSize );
qDebug( "%p: KDPipeIODevice::readData: read %lld bytes", this, bytesRead ); qDebug( "%p: KDPipeIODevice::readData: read %lld bytes", this, bytesRead );
qDebug( "%p (fd=%d): KDPipeIODevice::readData: %s", this, d->fd, data ); qDebug( "%p (fd=%d): KDPipeIODevice::readData: %s", this, d->fd, data );
r->notInReadDataCondition.wakeAll();
return bytesRead; return bytesRead;
} }
@ -508,9 +550,12 @@ qint64 KDPipeIODevice::writeData( const char * data, qint64 size ) { KDAB_CHECK_
LOCKED( w ); LOCKED( w );
while ( !w->error && !w->bufferEmpty() ) while ( !w->error && !w->bufferEmpty() ) {
qDebug( "%p: KDPipeIODevice::writeData: wait for empty buffer", this );
w->bufferEmptyCondition.wait( &w->mutex ); w->bufferEmptyCondition.wait( &w->mutex );
qDebug( "%p: KDPipeIODevice::writeData: empty buffer signaled", this );
}
if ( w->error ) if ( w->error )
return -1; return -1;
@ -529,10 +574,10 @@ qint64 Writer::writeData( const char * data, qint64 size ) {
numBytesInBuffer = size; numBytesInBuffer = size;
if ( !bufferEmpty() ) if ( !bufferEmpty() ) {
bufferNotEmptyCondition.wakeAll(); bufferNotEmptyCondition.wakeAll();
}
return size; return size;
} }
void KDPipeIODevice::Private::stopThreads() void KDPipeIODevice::Private::stopThreads()
@ -550,6 +595,7 @@ void KDPipeIODevice::Private::stopThreads()
r->cancel = true; r->cancel = true;
// and wake it, so it can terminate: // and wake it, so it can terminate:
r->bufferNotFullCondition.wakeAll(); r->bufferNotFullCondition.wakeAll();
r->readyReadSentCondition.wakeAll();
} }
} }
if ( Writer * & w = writer ) { if ( Writer * & w = writer ) {
@ -563,7 +609,7 @@ void KDPipeIODevice::Private::stopThreads()
} }
void KDPipeIODevice::close() { KDAB_CHECK_THIS; void KDPipeIODevice::close() { KDAB_CHECK_THIS;
qDebug( "KDPipeIODevice::close(%p)", this );
if ( !isOpen() ) if ( !isOpen() )
return; return;
@ -571,12 +617,14 @@ void KDPipeIODevice::close() { KDAB_CHECK_THIS;
emit aboutToClose(); emit aboutToClose();
d->stopThreads(); d->stopThreads();
#define waitAndDelete( t ) if ( t ) { t->wait(); delete t; t = 0; } #define waitAndDelete( t ) if ( t ) { t->wait(); QThread* t2 = t; t = 0; delete t2; }
qDebug( "KPipeIODevice::close(%p): wait and closing writer %p", this, d->writer );
waitAndDelete( d->writer ); waitAndDelete( d->writer );
qDebug( "KPipeIODevice::close(%p): wait and closing reader %p", this, d->reader );
waitAndDelete( d->reader ); waitAndDelete( d->reader );
#undef waitAndDelete #undef waitAndDelete
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
qDebug( "Closing handle" );
CloseHandle( d->handle ); CloseHandle( d->handle );
#else #else
::close( d->fd ); ::close( d->fd );
@ -598,18 +646,27 @@ void Reader::run() {
while ( true ) { while ( true ) {
while ( !cancel && bufferFull() ) { if ( !bufferFull() && !bufferEmpty() ) {
bufferNotEmptyCondition.wakeAll(); qDebug( "%p: Reader::run: buffer no longer empty, waking everyone", this );
qDebug( "%p: Reader::run: buffer is full, going to sleep", this ); notifyReadyRead();
}
while ( !cancel && bufferFull() ) {
bufferNotEmptyCondition.wakeAll();
notifyReadyRead();
if ( !bufferFull() )
break;
qDebug( "%p: Reader::run: buffer is full, going to sleep", this );
bufferNotFullCondition.wait( &mutex ); bufferNotFullCondition.wait( &mutex );
qDebug( "%p: Reader::run: woke up", this ); qDebug( "%p: Reader::run: woke up", this );
} }
if ( cancel ) { if ( cancel ) {
qDebug( "%p: Reader::run: detected cancel", this ); qDebug( "%p: Reader::run: detected cancel", this );
goto leave; goto leave;
} }
if ( rptr == wptr ) // optimize for larger chunks in case the buffer is empty if ( rptr == wptr ) // optimize for larger chunks in case the buffer is empty
rptr = wptr = 0; rptr = wptr = 0;
@ -630,9 +687,11 @@ void Reader::run() {
if ( !ok ) { if ( !ok ) {
errorCode = static_cast<int>( GetLastError() ); errorCode = static_cast<int>( GetLastError() );
if ( errorCode == ERROR_BROKEN_PIPE ) { if ( errorCode == ERROR_BROKEN_PIPE ) {
assert( numRead == 0 );
qDebug( "%p: Reader::run: got eof (broken pipe)", this ); qDebug( "%p: Reader::run: got eof (broken pipe)", this );
eof = true; eof = true;
} else { } else {
assert( numRead == 0 );
qDebug( "%p: Reader::run: got error: %s (%d)", this, strerror( errorCode ), errorCode ); qDebug( "%p: Reader::run: got error: %s (%d)", this, strerror( errorCode ), errorCode );
error = true; error = true;
} }
@ -668,16 +727,33 @@ void Reader::run() {
qDebug( "%p: Reader::run: buffer before: rptr=%4d, wptr=%4d", this, rptr, wptr ); qDebug( "%p: Reader::run: buffer before: rptr=%4d, wptr=%4d", this, rptr, wptr );
wptr = ( wptr + numRead ) % sizeof buffer; wptr = ( wptr + numRead ) % sizeof buffer;
qDebug( "%p: Reader::run: buffer after: rptr=%4d, wptr=%4d", this, rptr, wptr ); qDebug( "%p: Reader::run: buffer after: rptr=%4d, wptr=%4d", this, rptr, wptr );
if ( !bufferEmpty() ) {
qDebug( "%p: Reader::run: buffer no longer empty, waking everyone", this );
bufferNotEmptyCondition.wakeAll();
emit readyRead();
}
} }
leave: leave:
qDebug( "%p: Reader::run: terminating", this ); qDebug( "%p: Reader::run: terminating: loop while not canceled and not empty", this );
while ( !cancel && !bufferEmpty() ) {
notifyReadyRead();
}
notifyReadyRead();
qDebug( "%p: Reader::run: terminated", this );
}
void Reader::notifyReadyRead()
{
qDebug( "notifyReadyRead" );
if ( cancel )
return;
bufferNotEmptyCondition.wakeAll(); bufferNotEmptyCondition.wakeAll();
if ( inReadData ) {
qDebug( "notifyReadyRead: inReadData: waiting" );
notInReadDataCondition.wait( &mutex );
}
if ( cancel || ( !eof && !error && bufferEmpty() ) )
return;
qDebug( "readyReadData: actually emit signal" );
emit readyRead(); emit readyRead();
bufferNotEmptyCondition.wakeAll();
readyReadSentCondition.wait( &mutex );
bufferNotEmptyCondition.wakeAll();
} }
void Writer::run() { void Writer::run() {
@ -692,9 +768,11 @@ void Writer::run() {
while ( true ) { while ( true ) {
while ( !cancel && bufferEmpty() ) { while ( !cancel && bufferEmpty() ) {
bufferEmptyCondition.wakeAll(); qDebug( "%p: Writer::run: buffer is empty, wake bufferEmptyCond listeners", this );
bufferEmptyCondition.wakeAll();
emit bytesWritten( 0 );
qDebug( "%p: Writer::run: buffer is empty, going to sleep", this ); qDebug( "%p: Writer::run: buffer is empty, going to sleep", this );
bufferNotEmptyCondition.wait( &mutex ); bufferNotEmptyCondition.wait( &mutex );
qDebug( "%p: Writer::run: woke up", this ); qDebug( "%p: Writer::run: woke up", this );
} }
@ -707,10 +785,12 @@ void Writer::run() {
qDebug( "%p: Writer::run: Trying to write %u bytes", this, numBytesInBuffer ); qDebug( "%p: Writer::run: Trying to write %u bytes", this, numBytesInBuffer );
qint64 totalWritten = 0; qint64 totalWritten = 0;
do { do {
mutex.unlock(); mutex.unlock();
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
DWORD numWritten; DWORD numWritten;
qDebug( "%p (fd=%d): Writer::run: buffer before WriteFile (numBytes=%lld): %s:", this, fd, numBytesInBuffer, buffer );
qDebug( "%p (fd=%d): Writer::run: Going into WriteFile", this, fd );
if ( !WriteFile( handle, buffer + totalWritten, numBytesInBuffer - totalWritten, &numWritten, 0 ) ) { if ( !WriteFile( handle, buffer + totalWritten, numBytesInBuffer - totalWritten, &numWritten, 0 ) ) {
mutex.lock(); mutex.lock();
errorCode = static_cast<int>( GetLastError() ); errorCode = static_cast<int>( GetLastError() );
@ -732,6 +812,8 @@ void Writer::run() {
goto leave; goto leave;
} }
#endif #endif
qDebug( "%p (fd=%d): Writer::run: buffer after WriteFile (numBytes=%lld): %s:", this, fd, numBytesInBuffer,
buffer );
totalWritten += numWritten; totalWritten += numWritten;
mutex.lock(); mutex.lock();
} while ( totalWritten < numBytesInBuffer ); } while ( totalWritten < numBytesInBuffer );
@ -739,12 +821,15 @@ void Writer::run() {
qDebug( "%p: Writer::run: wrote %lld bytes", this, totalWritten ); qDebug( "%p: Writer::run: wrote %lld bytes", this, totalWritten );
numBytesInBuffer = 0; numBytesInBuffer = 0;
qDebug( "%p: Writer::run: buffer is empty, wake bufferEmptyCond listeners", this );
bufferEmptyCondition.wakeAll(); bufferEmptyCondition.wakeAll();
emit bytesWritten( totalWritten ); emit bytesWritten( totalWritten );
} }
leave: leave:
qDebug( "%p: Writer::run: terminating", this ); qDebug( "%p: Writer::run: terminating", this );
numBytesInBuffer = 0; numBytesInBuffer = 0;
qDebug( "%p: Writer::run: buffer is empty, wake bufferEmptyCond listeners", this );
bufferEmptyCondition.wakeAll(); bufferEmptyCondition.wakeAll();
emit bytesWritten( 0 ); emit bytesWritten( 0 );
} }

View File

@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
** Meta object code from reading C++ file 'kdpipeiodevice.cpp' ** Meta object code from reading C++ file 'kdpipeiodevice.cpp'
** **
** Created: Wed Sep 26 11:05:05 2007 ** Created: Mon Oct 1 16:08:44 2007
** by: The Qt Meta Object Compiler version 59 (Qt 4.3.1) ** by: The Qt Meta Object Compiler version 59 (Qt 4.3.1)
** **
** WARNING! All changes made in this file will be lost! ** WARNING! All changes made in this file will be lost!
@ -136,15 +136,18 @@ static const uint qt_meta_data_KDPipeIODevice__Private[] = {
1, // revision 1, // revision
0, // classname 0, // classname
0, 0, // classinfo 0, 0, // classinfo
0, 0, // methods 1, 10, // methods
0, 0, // properties 0, 0, // properties
0, 0, // enums/sets 0, 0, // enums/sets
// slots: signature, parameters, type, tag, flags
25, 24, 24, 24, 0x0a,
0 // eod 0 // eod
}; };
static const char qt_meta_stringdata_KDPipeIODevice__Private[] = { static const char qt_meta_stringdata_KDPipeIODevice__Private[] = {
"KDPipeIODevice::Private\0" "KDPipeIODevice::Private\0\0emitReadyRead()\0"
}; };
const QMetaObject KDPipeIODevice::Private::staticMetaObject = { const QMetaObject KDPipeIODevice::Private::staticMetaObject = {
@ -170,5 +173,11 @@ int KDPipeIODevice::Private::qt_metacall(QMetaObject::Call _c, int _id, void **_
_id = QObject::qt_metacall(_c, _id, _a); _id = QObject::qt_metacall(_c, _id, _a);
if (_id < 0) if (_id < 0)
return _id; return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
switch (_id) {
case 0: emitReadyRead(); break;
}
_id -= 1;
}
return _id; return _id;
} }

View File

@ -117,9 +117,11 @@ find_channel (int fd, int create)
BUFLEN. The printable version is the representation on the command BUFLEN. The printable version is the representation on the command
line that the child process expects. */ line that the child process expects. */
int int
_gpgme_io_fd2str (char *buf, int buflen, int fd) _gpgme_io_fd2str (char *buf, int buflen, int fd_)
{ {
return snprintf (buf, buflen, "%ld", (long) _get_osfhandle (fd)); const int actual_fd = iodevice_table[fd_] ? iodevice_table[fd_]->actual_fd : fd_;
return snprintf (buf, buflen, "%ld", (long) _get_osfhandle (actual_fd));
// return snprintf (buf, buflen, "%d", fd);
} }
@ -627,6 +629,14 @@ gpgme_get_giochannel (int fd)
int int
_gpgme_io_dup (int fd) _gpgme_io_dup (int fd)
{ {
DeviceEntry* const existing = iodevice_table[fd];
if ( existing )
existing->ref();
else
find_channel( fd, /*create=*/1 );
return fd;
#if 0
const int new_fd = _dup( fd ); const int new_fd = _dup( fd );
iodevice_table[new_fd] = iodevice_table[fd]; iodevice_table[new_fd] = iodevice_table[fd];
if ( iodevice_table[new_fd] ) if ( iodevice_table[new_fd] )
@ -634,5 +644,6 @@ _gpgme_io_dup (int fd)
else else
find_channel( new_fd, /*create=*/1 ); find_channel( new_fd, /*create=*/1 );
return new_fd; return new_fd;
#endif
} }