author | simon <simon> | 2002-12-10 11:07:46 (UTC) |
---|---|---|
committer | simon <simon> | 2002-12-10 11:07:46 (UTC) |
commit | f468c3bc69655aefa0ec5783405355dd3dde4afc (patch) (side-by-side diff) | |
tree | b358fb36cb4547a0bf2c100573e4846fbc32211c | |
parent | 448426777d9e35826898791d03b4bc71335427ff (diff) | |
download | opie-f468c3bc69655aefa0ec5783405355dd3dde4afc.zip opie-f468c3bc69655aefa0ec5783405355dd3dde4afc.tar.gz opie-f468c3bc69655aefa0ec5783405355dd3dde4afc.tar.bz2 |
- added ThreadUtil::Thread class
-rw-r--r-- | noncore/multimedia/opieplayer2/threadutil.cpp | 99 | ||||
-rw-r--r-- | noncore/multimedia/opieplayer2/threadutil.h | 28 |
2 files changed, 127 insertions, 0 deletions
diff --git a/noncore/multimedia/opieplayer2/threadutil.cpp b/noncore/multimedia/opieplayer2/threadutil.cpp index 4055c74..5687f42 100644 --- a/noncore/multimedia/opieplayer2/threadutil.cpp +++ b/noncore/multimedia/opieplayer2/threadutil.cpp @@ -1,284 +1,383 @@ /* This file is part of the KDE project Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "threadutil.h" #include <qsocketnotifier.h> #include <pthread.h> #include <assert.h> #include <unistd.h> +#include <errno.h> using namespace ThreadUtil; struct Mutex::Data { Data() { pthread_mutex_init( &mutex, 0 ); } ~Data() { pthread_mutex_destroy( &mutex ); } pthread_mutex_t mutex; }; Mutex::Mutex() : d( new Data ) { } Mutex::~Mutex() { delete d; } void Mutex::lock() { pthread_mutex_lock( &d->mutex ); } void Mutex::unlock() { pthread_mutex_unlock( &d->mutex ); } bool Mutex::tryLock() { return pthread_mutex_trylock( &d->mutex ) == 0; } bool Mutex::isLocked() { if ( !tryLock() ) return true; unlock(); return false; } struct WaitCondition::Data { Data() { int result = pthread_cond_init( &waitCondition, 0 ); assert( result == 0 ); } ~Data() { pthread_cond_destroy( &waitCondition ); } pthread_cond_t waitCondition; }; WaitCondition::WaitCondition() : d( new Data ) { } WaitCondition::~WaitCondition() { delete d; } bool WaitCondition::wait() { Mutex m; m.lock(); return wait( m ); } bool WaitCondition::wait( Mutex &mutex ) { return pthread_cond_wait( &d->waitCondition, &mutex.d->mutex ); } void WaitCondition::wakeOne() { pthread_cond_signal( &d->waitCondition ); } void WaitCondition::wakeAll() { pthread_cond_broadcast( &d->waitCondition ); } +struct Thread::Data +{ + Data() : isRunning( false ) + {} + + pthread_t self; + Mutex guard; + bool isRunning; + + WaitCondition finishCondition; +}; + +extern "C" +{ + +void _threadutil_terminate_thread( void *arg ) +{ + Thread *thr = ( Thread* )arg; + + assert( thr ); + + AutoLock locker( thr->d->guard ); + thr->d->isRunning = false; + thr->d->finishCondition.wakeAll(); +} + +void *_threadutil_start_thread( void *arg ) +{ + Thread *thr = ( Thread* )arg; + + pthread_cleanup_push( _threadutil_terminate_thread, thr ); + + thr->d->isRunning = true; + thr->run(); + + pthread_cleanup_pop( true ); + + Thread::exit(); + return 0; // never reached +} + +} + +Thread::Thread() + : d( new Data ) +{ +} + +Thread::~Thread() +{ + assert( d->isRunning == false ); + delete d; +} + +void Thread::start() +{ + AutoLock lock( d->guard ); + + if ( d->isRunning ) { + qDebug( "ThreadUtil::Thread::start() called for running thread." ); + return; + } + + pthread_attr_t attributes; + pthread_attr_init( &attributes ); + pthread_attr_setscope( &attributes, PTHREAD_SCOPE_SYSTEM ); + int err = pthread_create( &d->self, &attributes, _threadutil_start_thread, ( void* )this ); + if ( err != 0 ) { + qDebug( "ThreadUtil::Thread::start() : can't create thread: %s", strerror( err ) ); + pthread_attr_destroy( &attributes ); + return; + } + pthread_attr_destroy( &attributes ); +} + +void Thread::terminate() +{ + AutoLock lock( d->guard ); + if ( !d->isRunning ) + return; + + pthread_cancel( d->self ); +} + +bool Thread::wait() +{ + AutoLock lock( d->guard ); + if ( !d->isRunning ) + return true; + + return d->finishCondition.wait( d->guard ); +} + +void Thread::exit() +{ + pthread_exit( 0 ); +} + OnewayNotifier::OnewayNotifier() { int fds[ 2 ]; pipe( fds ); m_readFd = fds[ 0 ]; m_writeFd = fds[ 1 ]; m_notifier = new QSocketNotifier( m_readFd, QSocketNotifier::Read ); connect( m_notifier, SIGNAL( activated( int ) ), this, SLOT( wakeUp() ) ); } OnewayNotifier::~OnewayNotifier() { delete m_notifier; ::close( m_readFd ); ::close( m_writeFd ); } void OnewayNotifier::notify() { const char c = 42; ::write( m_writeFd, &c, 1 ); } void OnewayNotifier::wakeUp() { char c = 0; if ( ::read( m_readFd, &c, 1 ) != 1 ) return; emit awake(); } ChannelMessage::ChannelMessage( int type ) : m_type( type ), m_isCall( false ), m_replied( false ), m_inEventHandler( false ) { } ChannelMessage::~ChannelMessage() { if ( m_guard.isLocked() ) m_guard.unlock(); } void ChannelMessage::reply() { if ( !m_isCall ) { qDebug( "ChannelMessage::reply() - can't reply oneway message!" ); return; } if ( m_inEventHandler ) { m_replied = true; return; } m_condition.wakeOne(); m_guard.unlock(); } struct Channel::Private { Private() { ownerThread = pthread_self(); } pthread_t ownerThread; }; Channel::Channel( QObject *parent, const char *name ) : QObject( parent, name ), d( new Private ) { connect( &m_notifier, SIGNAL( awake() ), this, SLOT( deliver() ) ); } Channel::~Channel() { delete d; } void Channel::send( ChannelMessage *message, SendType type ) { if ( type == WaitForReply ) { message->m_guard.lock(); message->m_isCall = true; } m_pendingMessagesGuard.lock(); m_pendingMessages << MsgEnvelope( type, message ); m_pendingMessagesGuard.unlock(); if ( d->ownerThread == pthread_self() ) { assert( type != WaitForReply ); deliver(); } else m_notifier.notify(); //QThread::postEvent( this, new QCustomEvent( QEvent::User, envelope ) ); if ( type == WaitForReply ) { message->m_condition.wait( message->m_guard ); message->m_guard.unlock(); } } void Channel::deliver() { AutoLock lock( m_pendingMessagesGuard ); while ( !m_pendingMessages.isEmpty() ) { MsgEnvelope envelope = m_pendingMessages.first(); m_pendingMessages.remove( m_pendingMessages.begin() ); m_pendingMessagesGuard.unlock(); deliverOne( envelope ); m_pendingMessagesGuard.lock(); } } void Channel::deliverOne( const MsgEnvelope &envelope ) { ChannelMessage *msg = envelope.msg; assert( msg ); if ( envelope.type == WaitForReply ) { msg->m_guard.lock(); msg->m_inEventHandler = true; } receiveMessage( msg, envelope.type ); if ( envelope.type == WaitForReply ) { msg->m_inEventHandler = false; if ( msg->m_replied ) { msg->m_condition.wakeOne(); // this is a bit tricky. we unlock only when we reply. // reply() does an unlock as well. msg->m_guard.unlock(); } } } /* vim: et sw=4 ts=4 */ diff --git a/noncore/multimedia/opieplayer2/threadutil.h b/noncore/multimedia/opieplayer2/threadutil.h index 5cc4cdc..b537cc1 100644 --- a/noncore/multimedia/opieplayer2/threadutil.h +++ b/noncore/multimedia/opieplayer2/threadutil.h @@ -1,176 +1,204 @@ /* This file is part of the KDE project Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef THREADUTIL_H #define THREADUTIL_H #include <qvaluelist.h> #include <qobject.h> #include <qguardedptr.h> class QSocketNotifier; +extern "C" +{ + void *_threadutil_start_thread( void* ); + void _threadutil_terminate_thread( void* ); +} + namespace ThreadUtil { class Mutex { friend class WaitCondition; public: Mutex(); ~Mutex(); void lock(); void unlock(); bool tryLock(); bool isLocked(); private: struct Data; Data *d; Mutex( const Mutex & ); Mutex &operator=( const Mutex & ); }; class AutoLock { public: AutoLock( Mutex &mutex ) : m_mutex( mutex ) { m_mutex.lock(); } ~AutoLock() { m_mutex.unlock(); } Mutex *operator &() const { return &m_mutex; } private: Mutex &m_mutex; }; class WaitCondition { public: WaitCondition(); ~WaitCondition(); bool wait(); bool wait( Mutex &mutex ); void wakeOne(); void wakeAll(); private: struct Data; Data *d; WaitCondition( const WaitCondition & ); WaitCondition &operator=( const WaitCondition & ); }; + class Thread + { + friend void *::_threadutil_start_thread( void* ); + friend void ::_threadutil_terminate_thread( void* ); + public: + Thread(); + virtual ~Thread(); + + void start(); + void terminate(); + + bool wait(); + + static void exit(); + protected: + virtual void run() = 0; + + private: + struct Data; + Data *d; + }; + class OnewayNotifier : public QObject { Q_OBJECT public: OnewayNotifier(); ~OnewayNotifier(); void notify(); signals: void awake(); private slots: void wakeUp(); private: int m_readFd; int m_writeFd; QSocketNotifier *m_notifier; }; class Channel; class ChannelMessage { friend class Channel; public: ChannelMessage( int type = -1 ); virtual ~ChannelMessage(); int type() const { return m_type; } void reply(); private: ChannelMessage( const ChannelMessage & ); ChannelMessage &operator=( const ChannelMessage ); int m_type; bool m_isCall : 1; bool m_replied : 1; bool m_inEventHandler : 1; Mutex m_guard; WaitCondition m_condition; QGuardedPtr<Channel> m_channel; }; class Channel : public QObject { Q_OBJECT public: enum SendType { OneWay, WaitForReply }; Channel( QObject *parent = 0, const char *name = 0 ); virtual ~Channel(); void send( ChannelMessage *message, SendType type ); protected: virtual void receiveMessage( ChannelMessage *message, SendType type ) = 0; private slots: void deliver(); private: OnewayNotifier m_notifier; struct MsgEnvelope { MsgEnvelope() : type( OneWay ), msg( 0 ) {} MsgEnvelope( SendType _type , ChannelMessage *_msg ) : type( _type ), msg( _msg ) {} SendType type; ChannelMessage *msg; }; void deliverOne( const MsgEnvelope &envelope ); typedef QValueList<MsgEnvelope> MsgEnvelopeList; MsgEnvelopeList m_pendingMessages; Mutex m_pendingMessagesGuard; struct Private; Private *d; }; } #endif // THREADUTIL_H /* vim: et sw=4 ts=4 */ |