-rw-r--r-- | qmake/tools/qmutex_unix.cpp | 687 |
1 files changed, 687 insertions, 0 deletions
diff --git a/qmake/tools/qmutex_unix.cpp b/qmake/tools/qmutex_unix.cpp new file mode 100644 index 0000000..c861b2d --- a/dev/null +++ b/qmake/tools/qmutex_unix.cpp @@ -0,0 +1,687 @@ +/**************************************************************************** +** $Id$ +** +** QMutex class for Unix +** +** Created : 20010725 +** +** Copyright (C) 1992-2002 Trolltech AS. All rights reserved. +** +** This file is part of the tools module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#if defined(QT_THREAD_SUPPORT) + +#include "qplatformdefs.h" + +typedef pthread_mutex_t Q_MUTEX_T; + +// POSIX threads mutex types +#if ((defined(PTHREAD_MUTEX_RECURSIVE) && defined(PTHREAD_MUTEX_DEFAULT)) || \ + defined(Q_OS_FREEBSD)) && !defined(Q_OS_UNIXWARE) && !defined(Q_OS_SOLARIS) + // POSIX 1003.1c-1995 - We love this OS +# define Q_MUTEX_SET_TYPE(a, b) pthread_mutexattr_settype((a), (b)) +# if defined(QT_CHECK_RANGE) +# define Q_NORMAL_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK +# else +# define Q_NORMAL_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT +# endif +# define Q_RECURSIVE_MUTEX_TYPE PTHREAD_MUTEX_RECURSIVE +#elif defined(MUTEX_NONRECURSIVE_NP) && defined(MUTEX_RECURSIVE_NP) +// POSIX 1003.4a pthreads draft extensions +# define Q_MUTEX_SET_TYPE(a, b) pthread_mutexattr_setkind_np((a), (b)); +# define Q_NORMAL_MUTEX_TYPE MUTEX_NONRECURSIVE_NP +# define Q_RECURSIVE_MUTEX_TYPE MUTEX_RECURSIVE_NP +#else +// Unknown mutex types - skip them +# define Q_MUTEX_SET_TYPE(a, b) +# undef Q_NORMAL_MUTEX_TYPE +# undef Q_RECURSIVE_MUTEX_TYPE +#endif + +#include "qmutex.h" +#include "qmutex_p.h" + +#include <errno.h> +#include <string.h> + + +// Private class declarations + +class QRealMutexPrivate : public QMutexPrivate { +public: + QRealMutexPrivate(bool = FALSE); + + void lock(); + void unlock(); + bool locked(); + bool trylock(); + int type() const; + + bool recursive; +}; + +#ifndef Q_RECURSIVE_MUTEX_TYPE +class QRecursiveMutexPrivate : public QMutexPrivate +{ +public: + QRecursiveMutexPrivate(); + ~QRecursiveMutexPrivate(); + + void lock(); + void unlock(); + bool locked(); + bool trylock(); + int type() const; + + int count; + unsigned long owner; + pthread_mutex_t handle2; +}; +#endif // !Q_RECURSIVE_MUTEX_TYPE + + +// Private class implementation + +// base destructor +QMutexPrivate::~QMutexPrivate() +{ + int ret = pthread_mutex_destroy(&handle); + +#ifdef QT_CHECK_RANGE + if ( ret ) + qWarning( "Mutex destroy failure: %s", strerror( ret ) ); +#endif +} + +// real mutex class +QRealMutexPrivate::QRealMutexPrivate(bool recurs) + : recursive(recurs) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + Q_MUTEX_SET_TYPE(&attr, recursive ? Q_RECURSIVE_MUTEX_TYPE : Q_NORMAL_MUTEX_TYPE); + Q_UNUSED(recursive); + int ret = pthread_mutex_init(&handle, &attr); + pthread_mutexattr_destroy(&attr); + +#ifdef QT_CHECK_RANGE + if( ret ) + qWarning( "Mutex init failure: %s", strerror( ret ) ); +#endif // QT_CHECK_RANGE +} + +void QRealMutexPrivate::lock() +{ + int ret = pthread_mutex_lock(&handle); + +#ifdef QT_CHECK_RANGE + if (ret) + qWarning("Mutex lock failure: %s", strerror(ret)); +#endif +} + +void QRealMutexPrivate::unlock() +{ + int ret = pthread_mutex_unlock(&handle); + +#ifdef QT_CHECK_RANGE + if (ret) + qWarning("Mutex unlock failure: %s", strerror(ret)); +#endif +} + +bool QRealMutexPrivate::locked() +{ + int ret = pthread_mutex_trylock(&handle); + + if (ret == EBUSY) { + return TRUE; + } else if (ret) { +#ifdef QT_CHECK_RANGE + qWarning("Mutex locktest failure: %s", strerror(ret)); +#endif + } else + pthread_mutex_unlock(&handle); + + return FALSE; +} + +bool QRealMutexPrivate::trylock() +{ + int ret = pthread_mutex_trylock(&handle); + + if (ret == EBUSY) { + return FALSE; + } else if (ret) { +#ifdef QT_CHECK_RANGE + qWarning("Mutex trylock failure: %s", strerror(ret)); +#endif + return FALSE; + } + + return TRUE; +} + +int QRealMutexPrivate::type() const +{ + return recursive ? Q_MUTEX_RECURSIVE : Q_MUTEX_NORMAL; +} + + +#ifndef Q_RECURSIVE_MUTEX_TYPE +QRecursiveMutexPrivate::QRecursiveMutexPrivate() + : count(0), owner(0) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + Q_MUTEX_SET_TYPE(&attr, Q_NORMAL_MUTEX_TYPE); + int ret = pthread_mutex_init(&handle, &attr); + pthread_mutexattr_destroy(&attr); + +# ifdef QT_CHECK_RANGE + if (ret) + qWarning( "Mutex init failure: %s", strerror(ret) ); +# endif + + pthread_mutexattr_init(&attr); + ret = pthread_mutex_init( &handle2, &attr ); + pthread_mutexattr_destroy(&attr); + +# ifdef QT_CHECK_RANGE + if (ret) + qWarning( "Mutex init failure: %s", strerror(ret) ); +# endif +} + +QRecursiveMutexPrivate::~QRecursiveMutexPrivate() +{ + int ret = pthread_mutex_destroy(&handle2); + +# ifdef QT_CHECK_RANGE + if (ret) + qWarning( "Mutex destroy failure: %s", strerror(ret) ); +# endif +} + +void QRecursiveMutexPrivate::lock() +{ + pthread_mutex_lock(&handle2); + + if (count > 0 && owner == (unsigned long) pthread_self()) { + count++; + } else { + pthread_mutex_unlock(&handle2); + pthread_mutex_lock(&handle); + pthread_mutex_lock(&handle2); + count = 1; + owner = (unsigned long) pthread_self(); + } + + pthread_mutex_unlock(&handle2); +} + +void QRecursiveMutexPrivate::unlock() +{ + pthread_mutex_lock(&handle2); + + if (owner == (unsigned long) pthread_self()) { + // do nothing if the count is already 0... to reflect the behaviour described + // in the docs + if (count && (--count) < 1) { + count = 0; + pthread_mutex_unlock(&handle); + } + } else { +#ifdef QT_CHECK_RANGE + qWarning("QMutex::unlock: unlock from different thread than locker"); + qWarning(" was locked by %d, unlock attempt from %d", + (int)owner, (int)pthread_self()); +#endif + } + + pthread_mutex_unlock(&handle2); +} + +bool QRecursiveMutexPrivate::locked() +{ + pthread_mutex_lock(&handle2); + + bool ret; + int code = pthread_mutex_trylock(&handle); + + if (code == EBUSY) { + ret = TRUE; + } else { +#ifdef QT_CHECK_RANGE + if (code) + qWarning("Mutex trylock failure: %s", strerror(code)); +#endif + + pthread_mutex_unlock(&handle); + ret = FALSE; + } + + pthread_mutex_unlock(&handle2); + + return ret; +} + +bool QRecursiveMutexPrivate::trylock() +{ + bool ret = TRUE; + + pthread_mutex_lock(&handle2); + + if ( count > 0 && owner == (unsigned long) pthread_self() ) { + count++; + } else { + int code = pthread_mutex_trylock(&handle); + + if (code == EBUSY) { + ret = FALSE; + } else if (code) { +#ifdef QT_CHECK_RANGE + qWarning("Mutex trylock failure: %s", strerror(code)); +#endif + ret = FALSE; + } else { + count = 1; + owner = (unsigned long) pthread_self(); + } + } + + pthread_mutex_unlock(&handle2); + + return ret; +} + +int QRecursiveMutexPrivate::type() const +{ + return Q_MUTEX_RECURSIVE; +} + +#endif // !Q_RECURSIVE_MUTEX_TYPE + + +/*! + \class QMutex qmutex.h + \threadsafe + \brief The QMutex class provides access serialization between threads. + + \ingroup thread + \ingroup environment + + The purpose of a QMutex is to protect an object, data structure or + section of code so that only one thread can access it at a time + (This is similar to the Java \c synchronized keyword). For + example, say there is a method which prints a message to the user + on two lines: + + \code + int number = 6; + + void method1() + { + number *= 5; + number /= 4; + } + + void method1() + { + number *= 3; + number /= 2; + } + \endcode + + If these two methods are called in succession, the following happens: + + \code + // method1() + number *= 5; // number is now 30 + number /= 4; // number is now 7 + + // method2() + number *= 3; // nubmer is now 21 + number /= 2; // number is now 10 + \endcode + + If these two methods are called simultaneously from two threads then the + following sequence could result: + + \code + // Thread 1 calls method1() + number *= 5; // number is now 30 + + // Thread 2 calls method2(). + // + // Most likely Thread 1 has been put to sleep by the operating + // system to allow Thread 2 to run. + number *= 3; // number is now 90 + number /= 2; // number is now 45 + + // Thread 1 finishes executing. + number /= 4; // number is now 11, instead of 10 + \endcode + + If we add a mutex, we should get the result we want: + + \code + QMutex mutex; + int number = 6; + + void method1() + { + mutex.lock(); + number *= 5; + number /= 4; + mutex.unlock(); + } + + void method2() + { + mutex.lock(); + number *= 3; + number /= 2; + mutex.unlock(); + } + \endcode + + Then only one thread can modify \c number at any given time and + the result is correct. This is a trivial example, of course, but + applies to any other case where things need to happen in a + particular sequence. + + When you call lock() in a thread, other threads that try to call + lock() in the same place will block until the thread that got the + lock calls unlock(). A non-blocking alternative to lock() is + tryLock(). +*/ + +/*! + Constructs a new mutex. The mutex is created in an unlocked state. + A recursive mutex is created if \a recursive is TRUE; a normal + mutex is created if \a recursive is FALSE (the default). With a + recursive mutex, a thread can lock the same mutex multiple times + and it will not be unlocked until a corresponding number of + unlock() calls have been made. +*/ +QMutex::QMutex(bool recursive) +{ +#ifndef Q_RECURSIVE_MUTEX_TYPE + if ( recursive ) + d = new QRecursiveMutexPrivate(); + else +#endif // !Q_RECURSIVE_MUTEX_TYPE + d = new QRealMutexPrivate(recursive); +} + +/*! + Destroys the mutex. + + \warning If you destroy a mutex that still holds a lock the + resultant behavior is undefined. +*/ +QMutex::~QMutex() +{ + delete d; +} + +/*! + Attempt to lock the mutex. If another thread has locked the mutex + then this call will \e block until that thread has unlocked it. + + \sa unlock(), locked() +*/ +void QMutex::lock() +{ + d->lock(); +} + +/*! + Unlocks the mutex. Attempting to unlock a mutex in a different + thread to the one that locked it results in an error. Unlocking a + mutex that is not locked results in undefined behaviour (varies + between different Operating Systems' thread implementations). + + \sa lock(), locked() +*/ +void QMutex::unlock() +{ + d->unlock(); +} + +/*! + Returns TRUE if the mutex is locked by another thread; otherwise + returns FALSE. + + \warning Due to differing implementations of recursive mutexes on + various platforms, calling this function from the same thread that + previously locked the mutex will return undefined results. + + \sa lock(), unlock() +*/ +bool QMutex::locked() +{ + return d->locked(); +} + +/*! + Attempt to lock the mutex. If the lock was obtained, this function + returns TRUE. If another thread has locked the mutex, this + function returns FALSE, instead of waiting for the mutex to become + available, i.e. it does not block. + + If the lock was obtained, the mutex must be unlocked with unlock() + before another thread can successfully lock it. + + \sa lock(), unlock(), locked() +*/ +bool QMutex::tryLock() +{ + return d->trylock(); +} + +/*! + \class QMutexLocker qmutex.h + \brief The QMutexLocker class simplifies locking and unlocking QMutexes. + + \threadsafe + + \ingroup thread + \ingroup environment + + The purpose of QMutexLocker is to simplify QMutex locking and + unlocking. Locking and unlocking a QMutex in complex functions and + statements or in exception handling code is error prone and + difficult to debug. QMutexLocker should be used in such situations + to ensure that the state of the mutex is well defined and always + locked and unlocked properly. + + QMutexLocker should be created within a function where a QMutex + needs to be locked. The mutex is locked when QMutexLocker is + created, and unlocked when QMutexLocker is destroyed. + + For example, this complex function locks a QMutex upon entering + the function and unlocks the mutex at all the exit points: + + \code + int complexFunction( int flag ) + { + mutex.lock(); + + int return_value = 0; + + switch ( flag ) { + case 0: + case 1: + { + mutex.unlock(); + return moreComplexFunction( flag ); + } + + case 2: + { + int status = anotherFunction(); + if ( status < 0 ) { + mutex.unlock(); + return -2; + } + return_value = status + flag; + break; + } + + default: + { + if ( flag > 10 ) { + mutex.unlock(); + return -1; + } + break; + } + } + + mutex.unlock(); + return return_value; + } + \endcode + + This example function will get more complicated as it is + developed, which increases the likelihood that errors will occur. + + Using QMutexLocker greatly simplifies the code, and makes it more + readable: + + \code + int complexFunction( int flag ) + { + QMutexLocker locker( &mutex ); + + int return_value = 0; + + switch ( flag ) { + case 0: + case 1: + { + return moreComplexFunction( flag ); + } + + case 2: + { + int status = anotherFunction(); + if ( status < 0 ) + return -2; + return_value = status + flag; + break; + } + + default: + { + if ( flag > 10 ) + return -1; + break; + } + } + + return return_value; + } + \endcode + + Now, the mutex will always be unlocked when the QMutexLocker + object is destroyed (when the function returns since \c locker is + an auto variable). + + The same principle applies to code that throws and catches + exceptions. An exception that is not caught in the function that + has locked the mutex has no way of unlocking the mutex before the + exception is passed up the stack to the calling function. + + QMutexLocker also provides a mutex() member function that returns + the mutex on which the QMutexLocker is operating. This is useful + for code that needs access to the mutex, such as + QWaitCondition::wait(). For example: + + \code + class SignalWaiter + { + private: + QMutexLocker locker; + + public: + SignalWaiter( QMutex *mutex ) + : locker( mutex ) + { + } + + void waitForSignal() + { + ... + ... + ... + + while ( ! signalled ) + waitcondition.wait( locker.mutex() ); + + ... + ... + ... + } + }; + \endcode + + \sa QMutex, QWaitCondition +*/ + +/*! + \fn QMutexLocker::QMutexLocker( QMutex *mutex ) + + Constructs a QMutexLocker and locks \a mutex. The mutex will be + unlocked when the QMutexLocker is destroyed. + + \sa QMutex::lock() +*/ + +/*! + \fn QMutexLocker::~QMutexLocker() + + Destroys the QMutexLocker and unlocks the mutex which was locked + in the constructor. + + \sa QMutexLocker::QMutexLocker(), QMutex::unlock() +*/ + +/*! + \fn QMutex *QMutexLocker::mutex() const + + Returns a pointer to the mutex which was locked in the + constructor. + + \sa QMutexLocker::QMutexLocker() +*/ + +#endif // QT_THREAD_SUPPORT |