diff options
Diffstat (limited to 'src/tsthread')
-rw-r--r-- | src/tsthread/Makefile.am | 12 | ||||
-rw-r--r-- | src/tsthread/tsthread.cpp | 194 | ||||
-rw-r--r-- | src/tsthread/tsthread.h | 387 | ||||
-rw-r--r-- | src/tsthread/tswaitcondition.cpp | 60 | ||||
-rw-r--r-- | src/tsthread/tswaitcondition.h | 72 |
5 files changed, 725 insertions, 0 deletions
diff --git a/src/tsthread/Makefile.am b/src/tsthread/Makefile.am new file mode 100644 index 0000000..cd0ed32 --- /dev/null +++ b/src/tsthread/Makefile.am @@ -0,0 +1,12 @@ +INCLUDES = -I$(srcdir) -I$(srcdir)/.. $(all_includes) + +noinst_LTLIBRARIES = libtsthread.la + +libtsthread_la_SOURCES = \ + tsthread.cpp \ + tswaitcondition.cpp + +noinst_HEADERS = \ + tsthread.h + +METASOURCES = AUTO diff --git a/src/tsthread/tsthread.cpp b/src/tsthread/tsthread.cpp new file mode 100644 index 0000000..16b4538 --- /dev/null +++ b/src/tsthread/tsthread.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** + + Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +****************************************************************************/ + +#include "tsthread.h" + +#include <qapplication.h> +#include <qmetaobject.h> +#include <kdebug.h> +#include <qguardedptr.h> + +#include <assert.h> + +#ifdef TS_QTHREADSTORAGE +QThreadStorage< TSThread** >* TSThread::current_thread; +#else +TSCurrentThread* TSThread::current_thread; +#endif +TSThread* TSThread::main_thread = NULL; + +class TSMainThread + : public TSThread + { + protected: + virtual void run() { assert( false ); } + }; + +TSThread::Helper::Helper( TSThread* parent ) + : thread( parent ) + { + assert( parent ); + } + +void TSThread::Helper::run() + { + thread->executeThread(); + } + + +TSThread::TSThread() + : thread( this ) + , cancelling( false ) + , emit_pending( false ) + , cancel_mutex( NULL ) + , cancel_cond( NULL ) + , deleted_flag( NULL ) + { + } + +TSThread::~TSThread() + { + if( deleted_flag != NULL ) + *deleted_flag = true; + } + +void TSThread::start() + { + if( current_thread == NULL ) + initCurrentThread(); + thread.start(); + } + +void TSThread::cancel() + { + QMutexLocker lock( &mutex ); + cancelling = true; + if( cancel_mutex != NULL ) + { + QMutexLocker lock( cancel_mutex ); + cancel_cond->wakeAll(); + } + } + +void TSThread::wait( unsigned long time ) + { + thread.wait( time ); + } + +bool TSThread::finished() const + { + return thread.finished(); + } + +bool TSThread::running() const + { + return thread.running(); + } + +void TSThread::initCurrentThread() + { +#ifdef TS_QTHREADSTORAGE + current_thread = new QThreadStorage< TSThread** >(); + typedef TSThread* TSThreadPtr; + // set pointer for main thread (this must be main thread) + current_thread->setLocalData( new TSThreadPtr( NULL )); // TODO NULL ? +#else + current_thread = new TSCurrentThread(); + main_thread = new TSMainThread; + // set pointer for main thread (this must be main thread) + current_thread->setLocalData( main_thread ); +#endif + } + +void TSThread::executeThread() + { +#ifdef TS_QTHREADSTORAGE + // store dynamically allocated pointer, so that + // QThreadStorage deletes the pointer and not TSThread + typedef TSThread* TSThreadPtr; + current_thread->setLocalData( new TSThreadPtr( this )); +#else + current_thread->setLocalData( this ); +#endif + run(); + postSignal( this, NULL ); // = terminated() + } + +void TSThread::postSignal( QObject* obj, const char* signal ) + { + assert( currentThread() == this ); + qApp->postEvent( this, new SignalEvent( signal, obj, NULL )); + } + +void TSThread::emitSignalInternal( QObject* obj, const char* signal, QUObject* o ) + { + assert( currentThread() == this ); + QMutexLocker locker( &signal_mutex ); + emit_pending = true; + qApp->postEvent( this, new SignalEvent( signal, obj, o )); + while( emit_pending ) + signal_cond.wait( &signal_mutex ); + } + +void TSThread::emitCancellableSignalInternal( QObject* obj, const char* signal, QUObject* o ) + { + assert( currentThread() == this ); + // can't use this->mutex, because its locking will be triggered by TSWaitCondition + QMutexLocker locker( &signal_mutex ); + emit_pending = true; + qApp->postEvent( this, new SignalEvent( signal, obj, o )); + while( emit_pending && !testCancel()) + signal_cond.cancellableWait( &signal_mutex ); + emit_pending = false; // in case of cancel + } + +void TSThread::customEvent( QCustomEvent* ev ) + { + SignalEvent* e = static_cast< SignalEvent* >( ev ); + if( e->signal.isEmpty()) // = terminated() + { // threadExecute() finishes before the actual thread terminates + if( !finished()) + wait(); + emit terminated(); + return; + } + bool deleted = false; + deleted_flag = &deleted; // this is like QGuardedPtr for self, but faster + int signal_id = e->object->metaObject()->findSignal( normalizeSignalSlot( e->signal ).data() + 1, true ); + if( signal_id >= 0 ) + e->object->qt_emit( signal_id, e->args ); + else + kdWarning() << "Cannot emit signal \"" << e->signal << "\"." << endl; + if( deleted ) // some slot deleted 'this' + return; + deleted_flag = NULL; + QMutexLocker locker( &signal_mutex ); + if( emit_pending ) + { + emit_pending = false; + signal_cond.wakeOne(); + } + } + +#include "tsthread.moc" diff --git a/src/tsthread/tsthread.h b/src/tsthread/tsthread.h new file mode 100644 index 0000000..5b98890 --- /dev/null +++ b/src/tsthread/tsthread.h @@ -0,0 +1,387 @@ +/**************************************************************************** + + Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +****************************************************************************/ + +#ifndef TSTHREAD_H +#define TSTHREAD_H + +#include <qobject.h> +#include <qthread.h> +#include <qwaitcondition.h> +#include <qdeepcopy.h> +#include <qmutex.h> +#include <private/qucomextra_p.h> + +#ifdef TS_QTHREADSTORAGE +#include <qthreadstorage.h> +#else +#include <pthread.h> +#endif + +#include "tswaitcondition.h" + +// how difficult ... +template< typename T > +T TSDeepCopy( const T& t ) +{ + return QDeepCopy< T >( t ); +} + +class TSCurrentThread; + +/** + Thread class, internally based on QThread, which intentionally doesn't have + dangerous crap like QThread::terminate() and intentionally has useful features + like emitting signals to the main thread, currentThread() or support + for cancelling. + */ +class TSThread + : public QObject + { + Q_OBJECT + public: + TSThread(); + virtual ~TSThread(); + /** + * Starts the thread. + * @see QThread::start() + */ + void start(); + /** + * Waits for the thread to finish. + * @see QThread::wait() + */ + void wait( unsigned long time = ULONG_MAX ); + /** + * Returns true if the thread has finished. + * @see QThread::finished() + */ + bool finished() const; + /** + * Returns true if the thread is running. + * @see QThread::running() + */ + bool running() const; + /** + * Sends the thread a request to terminate. + * The thread must check for cancellation using testCancel() + * and finish execution (return from run()). + * @see testCancel() + */ + void cancel(); + // TODO suspend + resume? + /** + * Returns true if a request to terminate is pending. + * @see cancel() + */ + bool testCancel() const; + /** + * Returns pointer to the current thread, i.e. thread from which + * this function is called. + */ + static TSThread* currentThread(); + /** + * Returns a pointer to the main thread. Mostly useful for currentThread(). + */ + static TSThread* mainThread(); + /** + * Emits the specified signal in the main thread. This function returns + * only after all slots connected to the signal have been executed (i.e. + * it works like normal signal). The signal can have one pointer argument, + * which can be used for communication in either direction. QObject::sender() + * in slots is valid. + * Example: + * \code + * emitSignal( this, SIGNAL( result( int* )), &result_data ); + * \endcode + * @see postSignal + * @see emitCancellableSignal + */ + void emitSignal( QObject* obj, const char* signal ); + template< typename T1 > + void emitSignal( QObject* obj, const char* signal, const T1& p1 ); + template< typename T1, typename T2 > + void emitSignal( QObject* obj, const char* signal, const T1& p1, const T2& p2 ); + /** + * This function works like emitSignal(), but additionally acts as a cancellation + * point, i.e. calling cancel() on the thread causes premature return. + * @see emitSignal + * @see postSignal + */ + void emitCancellableSignal( QObject* obj, const char* signal ); + template< typename T1 > + void emitCancellableSignal( QObject* obj, const char* signal, const T1& p1 ); + template< typename T1, typename T2 > + void emitCancellableSignal( QObject* obj, const char* signal, const T1& p1, const T2& p2 ); + /** + * Posts (i.e. it is not executed immediatelly like normal signals) + * a signal to be emitted in the main thread. The signal cannot + * have any parameters, use your TSThread derived class instance + * data members instead. QObject::sender() in slots is valid, unless + * the thread instance is destroyed before the signal is processed. + * @see emitSignal + */ + void postSignal( QObject* obj, const char* signal ); // is emitted _always_ in main thread + protected: + /** + * The code to be executed in the started thread. + * @see QThread::run() + */ + virtual void run() = 0; + signals: + /** + * Emitted after the thread is terminated. + */ + void terminated(); // is emitted _always_ in main thread + protected: + /** + * @internal + */ + void customEvent( QCustomEvent* e ); + private: + class SignalEvent + : public QCustomEvent + { + public: + SignalEvent( const char* sig, QObject* obj, QUObject* o ) + : QCustomEvent( QEvent::User ), signal( sig ), object( obj ), args( o ) + { + } + const QCString signal; + QObject* object; + QUObject* args; + }; + class Helper + : public QThread + { + public: + Helper( TSThread* parent ); + protected: + virtual void run(); + private: + TSThread* thread; + }; + void executeThread(); + static void initCurrentThread(); + bool setCancelData( QMutex*m, QWaitCondition* c ); + void setSignalData( QUObject* o, int i ); + void setSignalData( QUObject* o, const QImage& i ); + void setSignalData( QUObject* o, const QString& s ); + void setSignalData( QUObject* o, bool b ); + void setSignalData( QUObject* o, const QColor& c ); + void setSignalData( QUObject* o, const char* s ); + void setSignalData( QUObject* o, const QSize& ); + void emitSignalInternal( QObject* obj, const char* signal, QUObject* o ); + void emitCancellableSignalInternal( QObject* obj, const char* signal, QUObject* o ); + friend class Helper; + friend class TSWaitCondition; + Helper thread; + bool cancelling; + bool emit_pending; + mutable QMutex mutex; + QMutex signal_mutex; + TSWaitCondition signal_cond; + QMutex* cancel_mutex; + QWaitCondition* cancel_cond; + bool* deleted_flag; +#ifdef TS_QTHREADSTORAGE + static QThreadStorage< TSThread** >* current_thread; +#else + static TSCurrentThread* current_thread; +#endif + static TSThread* main_thread; + private: + TSThread( const TSThread& ); + TSThread& operator=( const TSThread& ); + }; + +#ifndef TS_QTHREADSTORAGE +/** + * @internal + */ +class TSCurrentThread + { + public: + TSCurrentThread(); + ~TSCurrentThread(); + TSThread* localData() const; + void setLocalData( TSThread* t ); + private: + pthread_key_t key; + }; + + +inline TSCurrentThread::TSCurrentThread() + { + pthread_key_create( &key, NULL ); + } + +inline TSCurrentThread::~TSCurrentThread() + { + pthread_key_delete( key ); + } + +inline void TSCurrentThread::setLocalData( TSThread* t ) + { + pthread_setspecific( key, t ); + } + +inline TSThread* TSCurrentThread::localData() const + { + return static_cast< TSThread* >( pthread_getspecific( key )); + } +#endif + +inline +bool TSThread::testCancel() const + { + QMutexLocker lock( &mutex ); + return cancelling; + } + +#include <kdebug.h> +inline +bool TSThread::setCancelData( QMutex* m, QWaitCondition* c ) + { + QMutexLocker lock( &mutex ); + if( cancelling && m != NULL ) + return false; + cancel_mutex = m; + cancel_cond = c; + return true; + } + +inline +TSThread* TSThread::currentThread() + { + if( current_thread == NULL ) + initCurrentThread(); +#ifdef TS_QTHREADSTORAGE + return *current_thread->localData(); +#else + return current_thread->localData(); +#endif + } + +inline +TSThread* TSThread::mainThread() + { + return main_thread; + } + +inline +void TSThread::setSignalData( QUObject* o, int i ) + { + static_QUType_int.set( o, i ); + } + +inline +void TSThread::setSignalData( QUObject* o, const QImage& i ) + { + static_QUType_varptr.set( o, &i ); + } + +inline +void TSThread::setSignalData( QUObject* o, const QString& s ) + { + static_QUType_QString.set( o, s ); + } + +inline +void TSThread::setSignalData( QUObject* o, bool b ) + { + static_QUType_bool.set( o, b ); + } + +inline +void TSThread::setSignalData( QUObject* o, const QColor& c ) + { + static_QUType_varptr.set( o, &c ); + } + +inline +void TSThread::setSignalData( QUObject* o, const char* s ) + { + static_QUType_charstar.set( o, s ); + } + +inline +void TSThread::setSignalData( QUObject* o, const QSize& s ) + { + static_QUType_varptr.set( o, &s ); + } + +inline +void TSThread::emitSignal( QObject* obj, const char* signal ) + { + QUObject o[ 1 ]; + emitSignalInternal( obj, signal, o ); + } + +template< typename T1 > +inline +void TSThread::emitSignal( QObject* obj, const char* signal, const T1& p1 ) + { + QUObject o[ 2 ]; + setSignalData( o + 1, p1 ); + emitSignalInternal( obj, signal, o ); + } + +template< typename T1, typename T2 > +inline +void TSThread::emitSignal( QObject* obj, const char* signal, const T1& p1, const T2& p2 ) + { + QUObject o[ 3 ]; + setSignalData( o + 1, p1 ); + setSignalData( o + 2, p2 ); + emitSignalInternal( obj, signal, o ); + } + +inline +void TSThread::emitCancellableSignal( QObject* obj, const char* signal ) + { + QUObject o[ 1 ]; + emitCancellableSignalInternal( obj, signal, o ); + } + +template< typename T1 > +inline +void TSThread::emitCancellableSignal( QObject* obj, const char* signal, const T1& p1 ) + { + QUObject o[ 2 ]; + setSignalData( o + 1, p1 ); + emitCancellableSignalInternal( obj, signal, o ); + } + +template< typename T1, typename T2 > +inline +void TSThread::emitCancellableSignal( QObject* obj, const char* signal, const T1& p1, const T2& p2 ) + { + QUObject o[ 3 ]; + setSignalData( o + 1, p1 ); + setSignalData( o + 2, p2 ); + emitCancellableSignalInternal( obj, signal, o ); + } + + +#endif diff --git a/src/tsthread/tswaitcondition.cpp b/src/tsthread/tswaitcondition.cpp new file mode 100644 index 0000000..e11e989 --- /dev/null +++ b/src/tsthread/tswaitcondition.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** + + Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +****************************************************************************/ + +#include "tswaitcondition.h" + +#include "tsthread.h" + +bool TSWaitCondition::wait( QMutex* m, unsigned long time ) + { + return cond.wait( m, time ); // TODO? + } + +bool TSWaitCondition::cancellableWait( QMutex* m, unsigned long time ) + { + mutex.lock(); + if( !TSThread::currentThread()->setCancelData( &mutex, &cond )) + { + mutex.unlock(); + return false; + } + m->unlock(); + bool ret = cond.wait( &mutex, time ); + TSThread::currentThread()->setCancelData( NULL, NULL ); + mutex.unlock(); + m->lock(); + return ret; + } + +void TSWaitCondition::wakeOne() + { + QMutexLocker locker( &mutex ); + cond.wakeOne(); + } + +void TSWaitCondition::wakeAll() + { + QMutexLocker locker( &mutex ); + cond.wakeAll(); + } diff --git a/src/tsthread/tswaitcondition.h b/src/tsthread/tswaitcondition.h new file mode 100644 index 0000000..09f9cab --- /dev/null +++ b/src/tsthread/tswaitcondition.h @@ -0,0 +1,72 @@ +/**************************************************************************** + + Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +****************************************************************************/ + +#ifndef TSWAITCONDITION_H +#define TSWAITCONDITION_H + +#include <qmutex.h> +#include <qwaitcondition.h> + +class TSWaitCondition + { + public: + TSWaitCondition(); + ~TSWaitCondition(); + bool wait( QMutex* mutex, unsigned long time = ULONG_MAX ); + bool wait( QMutex& mutex, unsigned long time = ULONG_MAX ); + bool cancellableWait( QMutex* mutex, unsigned long time = ULONG_MAX ); + bool cancellableWait( QMutex& mutex, unsigned long time = ULONG_MAX ); + void wakeOne(); + void wakeAll(); + private: + QMutex mutex; + QWaitCondition cond; + private: + TSWaitCondition( const TSWaitCondition& ); + TSWaitCondition& operator=( const TSWaitCondition& ); + }; + +inline +TSWaitCondition::TSWaitCondition() + { + } + +inline +TSWaitCondition::~TSWaitCondition() + { + } + +inline +bool TSWaitCondition::wait( QMutex& mutex, unsigned long time ) + { + return wait( &mutex, time ); + } + +inline +bool TSWaitCondition::cancellableWait( QMutex& mutex, unsigned long time ) + { + return cancellableWait( &mutex, time ); + } + +#endif |