summaryrefslogtreecommitdiffstats
path: root/src/tools/qmutex_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/qmutex_unix.cpp')
-rw-r--r--src/tools/qmutex_unix.cpp695
1 files changed, 695 insertions, 0 deletions
diff --git a/src/tools/qmutex_unix.cpp b/src/tools/qmutex_unix.cpp
new file mode 100644
index 000000000..8e25f4228
--- /dev/null
+++ b/src/tools/qmutex_unix.cpp
@@ -0,0 +1,695 @@
+/****************************************************************************
+**
+** TQMutex class for Unix
+**
+** Created : 20010725
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the tools module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#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) && \
+ !defined(Q_OS_MAC)
+// 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 TQRealMutexPrivate : public TQMutexPrivate {
+public:
+ TQRealMutexPrivate(bool = FALSE);
+
+ void lock();
+ void unlock();
+ bool locked();
+ bool trylock();
+ int type() const;
+
+ bool recursive;
+};
+
+#ifndef Q_RECURSIVE_MUTEX_TYPE
+class TQRecursiveMutexPrivate : public TQMutexPrivate
+{
+public:
+ TQRecursiveMutexPrivate();
+ ~TQRecursiveMutexPrivate();
+
+ 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
+TQMutexPrivate::~TQMutexPrivate()
+{
+ int ret = pthread_mutex_destroy(&handle);
+
+#ifdef QT_CHECK_RANGE
+ if ( ret )
+ qWarning( "Mutex destroy failure: %s", strerror( ret ) );
+#endif
+}
+
+// real mutex class
+TQRealMutexPrivate::TQRealMutexPrivate(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 TQRealMutexPrivate::lock()
+{
+ int ret = pthread_mutex_lock(&handle);
+
+#ifdef QT_CHECK_RANGE
+ if (ret)
+ qWarning("Mutex lock failure: %s", strerror(ret));
+#endif
+}
+
+void TQRealMutexPrivate::unlock()
+{
+ int ret = pthread_mutex_unlock(&handle);
+
+#ifdef QT_CHECK_RANGE
+ if (ret)
+ qWarning("Mutex unlock failure: %s", strerror(ret));
+#endif
+}
+
+bool TQRealMutexPrivate::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 TQRealMutexPrivate::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 TQRealMutexPrivate::type() const
+{
+ return recursive ? Q_MUTEX_RECURSIVE : Q_MUTEX_NORMAL;
+}
+
+
+#ifndef Q_RECURSIVE_MUTEX_TYPE
+TQRecursiveMutexPrivate::TQRecursiveMutexPrivate()
+ : 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
+}
+
+TQRecursiveMutexPrivate::~TQRecursiveMutexPrivate()
+{
+ int ret = pthread_mutex_destroy(&handle2);
+
+# ifdef QT_CHECK_RANGE
+ if (ret)
+ qWarning( "Mutex destroy failure: %s", strerror(ret) );
+# endif
+}
+
+void TQRecursiveMutexPrivate::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 TQRecursiveMutexPrivate::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("TQMutex::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 TQRecursiveMutexPrivate::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 TQRecursiveMutexPrivate::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 TQRecursiveMutexPrivate::type() const
+{
+ return Q_MUTEX_RECURSIVE;
+}
+
+#endif // !Q_RECURSIVE_MUTEX_TYPE
+
+
+/*!
+ \class TQMutex qmutex.h
+ \threadsafe
+ \brief The TQMutex class provides access serialization between threads.
+
+ \ingroup thread
+ \ingroup environment
+
+ The purpose of a TQMutex 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 method2()
+ {
+ 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
+ TQMutex 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.
+*/
+TQMutex::TQMutex(bool recursive)
+{
+#ifndef Q_RECURSIVE_MUTEX_TYPE
+ if ( recursive )
+ d = new TQRecursiveMutexPrivate();
+ else
+#endif // !Q_RECURSIVE_MUTEX_TYPE
+ d = new TQRealMutexPrivate(recursive);
+}
+
+/*!
+ Destroys the mutex.
+
+ \warning If you destroy a mutex that still holds a lock the
+ resultant behavior is undefined.
+*/
+TQMutex::~TQMutex()
+{
+ 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 TQMutex::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 TQMutex::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 TQMutex::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 TQMutex::tryLock()
+{
+ return d->trylock();
+}
+
+/*!
+ \class TQMutexLocker qmutex.h
+ \brief The TQMutexLocker class simplifies locking and unlocking TQMutexes.
+
+ \threadsafe
+
+ \ingroup thread
+ \ingroup environment
+
+ The purpose of TQMutexLocker is to simplify TQMutex locking and
+ unlocking. Locking and unlocking a TQMutex in complex functions and
+ statements or in exception handling code is error prone and
+ difficult to debug. TQMutexLocker should be used in such situations
+ to ensure that the state of the mutex is well defined and always
+ locked and unlocked properly.
+
+ TQMutexLocker should be created within a function where a TQMutex
+ needs to be locked. The mutex is locked when TQMutexLocker is
+ created, and unlocked when TQMutexLocker is destroyed.
+
+ For example, this complex function locks a TQMutex 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 TQMutexLocker greatly simplifies the code, and makes it more
+ readable:
+
+ \code
+ int complexFunction( int flag )
+ {
+ TQMutexLocker 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 TQMutexLocker
+ object is destroyed (when the function returns since \c locker is
+ an auto variable). Note that the mutex will be unlocked after
+ the call to moreComplexFunction() in this example, avoiding
+ possible bugs caused by unlocking the mutex too early, as in
+ the first example.
+
+ 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.
+
+ TQMutexLocker also provides a mutex() member function that returns
+ the mutex on which the TQMutexLocker is operating. This is useful
+ for code that needs access to the mutex, such as
+ TQWaitCondition::wait(). For example:
+
+ \code
+ class SignalWaiter
+ {
+ private:
+ TQMutexLocker locker;
+
+ public:
+ SignalWaiter( TQMutex *mutex )
+ : locker( mutex )
+ {
+ }
+
+ void waitForSignal()
+ {
+ ...
+ ...
+ ...
+
+ while ( ! signalled )
+ waitcondition.wait( locker.mutex() );
+
+ ...
+ ...
+ ...
+ }
+ };
+ \endcode
+
+ \sa TQMutex, TQWaitCondition
+*/
+
+/*!
+ \fn TQMutexLocker::TQMutexLocker( TQMutex *mutex )
+
+ Constructs a TQMutexLocker and locks \a mutex. The mutex will be
+ unlocked when the TQMutexLocker is destroyed. If \a mutex is zero,
+ TQMutexLocker does nothing.
+
+ \sa TQMutex::lock()
+*/
+
+/*!
+ \fn TQMutexLocker::~TQMutexLocker()
+
+ Destroys the TQMutexLocker and unlocks the mutex which was locked
+ in the constructor.
+
+ \sa TQMutexLocker::TQMutexLocker(), TQMutex::unlock()
+*/
+
+/*!
+ \fn TQMutex *TQMutexLocker::mutex() const
+
+ Returns a pointer to the mutex which was locked in the
+ constructor.
+
+ \sa TQMutexLocker::TQMutexLocker()
+*/
+
+#endif // QT_THREAD_SUPPORT