summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread')
-rw-r--r--src/corelib/thread/qatomic.cpp1126
-rw-r--r--src/corelib/thread/qatomic.h227
-rw-r--r--src/corelib/thread/qbasicatomic.h210
-rw-r--r--src/corelib/thread/qmutex.cpp515
-rw-r--r--src/corelib/thread/qmutex.h193
-rw-r--r--src/corelib/thread/qmutex_p.h88
-rw-r--r--src/corelib/thread/qmutex_unix.cpp113
-rw-r--r--src/corelib/thread/qmutex_win.cpp81
-rw-r--r--src/corelib/thread/qmutexpool.cpp165
-rw-r--r--src/corelib/thread/qmutexpool_p.h85
-rw-r--r--src/corelib/thread/qorderedmutexlocker_p.h119
-rw-r--r--src/corelib/thread/qreadwritelock.cpp584
-rw-r--r--src/corelib/thread/qreadwritelock.h244
-rw-r--r--src/corelib/thread/qreadwritelock_p.h87
-rw-r--r--src/corelib/thread/qsemaphore.cpp235
-rw-r--r--src/corelib/thread/qsemaphore.h83
-rw-r--r--src/corelib/thread/qthread.cpp730
-rw-r--r--src/corelib/thread/qthread.h163
-rw-r--r--src/corelib/thread/qthread_p.h215
-rw-r--r--src/corelib/thread/qthread_unix.cpp562
-rw-r--r--src/corelib/thread/qthread_win.cpp620
-rw-r--r--src/corelib/thread/qthreadstorage.cpp320
-rw-r--r--src/corelib/thread/qthreadstorage.h157
-rw-r--r--src/corelib/thread/qwaitcondition.h105
-rw-r--r--src/corelib/thread/qwaitcondition_unix.cpp193
-rw-r--r--src/corelib/thread/qwaitcondition_win.cpp237
-rw-r--r--src/corelib/thread/thread.pri33
27 files changed, 7490 insertions, 0 deletions
diff --git a/src/corelib/thread/qatomic.cpp b/src/corelib/thread/qatomic.cpp
new file mode 100644
index 0000000..44c4482
--- /dev/null
+++ b/src/corelib/thread/qatomic.cpp
@@ -0,0 +1,1126 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QAtomicInt
+ \brief The QAtomicInt class provides platform-independent atomic operations on integers.
+ \since 4.4
+
+ \ingroup thread
+
+ For atomic operations on pointers, see the QAtomicPointer class.
+
+ An complex operation that completes without interruption is said
+ to be \e atomic. The QAtomicInt class provides atomic reference
+ counting, test-and-set, fetch-and-store, and fetch-and-add for
+ integers.
+
+ \section1 Non-atomic convenience operators
+
+ For convenience, QAtomicInt provides integer comparison, cast, and
+ assignment operators. Note that these operators are \e not atomic.
+
+ \section1 The Atomic API
+
+ \section2 Reference counting
+
+ The ref() and deref() functions provide an efficient reference
+ counting API. The return value of these functions are used to
+ indicate when the last reference has been released. These
+ functions allow you to implement your own implicitly shared
+ classes.
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qatomic.cpp 0
+
+ \section2 Memory ordering
+
+ QAtomicInt provides several implementations of the atomic
+ test-and-set, fetch-and-store, and fetch-and-add functions. Each
+ implementation defines a memory ordering semantic that describes
+ how memory accesses surrounding the atomic instruction are
+ executed by the processor. Since many modern architectures allow
+ out-of-order execution and memory ordering, using the correct
+ semantic is necessary to ensure that your application functions
+ properly on all processors.
+
+ \list
+
+ \o Relaxed - memory ordering is unspecified, leaving the compiler
+ and processor to freely reorder memory accesses.
+
+ \o Acquire - memory access following the atomic operation (in
+ program order) may not be re-ordered before the atomic operation.
+
+ \o Release - memory access before the atomic operation (in program
+ order) may not be re-ordered after the atomic operation.
+
+ \o Ordered - the same Acquire and Release semantics combined.
+
+ \endlist
+
+ \section2 Test-and-set
+
+ If the current value of the QAtomicInt is an expected value, the
+ test-and-set functions assign a new value to the QAtomicInt and
+ return true. If values are \a not the same, these functions do
+ nothing and return false. This operation equates to the following
+ code:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qatomic.cpp 1
+
+ There are 4 test-and-set functions: testAndSetRelaxed(),
+ testAndSetAcquire(), testAndSetRelease(), and
+ testAndSetOrdered(). See above for an explanation of the different
+ memory ordering semantics.
+
+ \section2 Fetch-and-store
+
+ The atomic fetch-and-store functions read the current value of the
+ QAtomicInt and then assign a new value, returning the original
+ value. This operation equates to the following code:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qatomic.cpp 2
+
+ There are 4 fetch-and-store functions: fetchAndStoreRelaxed(),
+ fetchAndStoreAcquire(), fetchAndStoreRelease(), and
+ fetchAndStoreOrdered(). See above for an explanation of the
+ different memory ordering semantics.
+
+ \section2 Fetch-and-add
+
+ The atomic fetch-and-add functions read the current value of the
+ QAtomicInt and then add the given value to the current value,
+ returning the original value. This operation equates to the
+ following code:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qatomic.cpp 3
+
+ There are 4 fetch-and-add functions: fetchAndAddRelaxed(),
+ fetchAndAddAcquire(), fetchAndAddRelease(), and
+ fetchAndAddOrdered(). See above for an explanation of the
+ different memory ordering semantics.
+
+ \section1 Feature Tests for the Atomic API
+
+ Providing a platform-independent atomic API that works on all
+ processors is challenging. The API provided by QAtomicInt is
+ guaranteed to work atomically on all processors. However, since
+ not all processors implement support for every operation provided
+ by QAtomicInt, it is necessary to expose information about the
+ processor.
+
+ You can check at compile time which features are supported on your
+ hardware using various macros. These will tell you if your
+ hardware always, sometimes, or does not support a particular
+ operation. The macros have the form
+ Q_ATOMIC_INT_\e{OPERATION}_IS_\e{HOW}_NATIVE. \e{OPERATION}
+ is one of REFERENCE_COUNTING, TEST_AND_SET,
+ FETCH_AND_STORE, or FETCH_AND_ADD, and \e{HOW} is one of
+ ALWAYS, SOMETIMES, or NOT. There will always be exactly one
+ defined macro per operation. For example, if
+ Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE is defined,
+ neither Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE nor
+ Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE will be defined.
+
+ An operation that completes in constant time is said to be
+ wait-free. Such operations are not implemented using locks or
+ loops of any kind. For atomic operations that are always
+ supported, and that are wait-free, Qt defines the
+ Q_ATOMIC_INT_\e{OPERATION}_IS_WAIT_FREE in addition to the
+ Q_ATOMIC_INT_\e{OPERATION}_IS_ALWAYS_NATIVE.
+
+ In cases where an atomic operation is only supported in newer
+ generations of the processor, QAtomicInt also provides a way to
+ check at runtime what your hardware supports with the
+ isReferenceCountingNative(), isTestAndSetNative(),
+ isFetchAndStoreNative(), and isFetchAndAddNative()
+ functions. Wait-free implementations can be detected using the
+ isReferenceCountingWaitFree(), isTestAndSetWaitFree(),
+ isFetchAndStoreWaitFree(), and isFetchAndAddWaitFree() functions.
+
+ Below is a complete list of all feature macros for QAtomicInt:
+
+ \list
+
+ \o Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
+ \o Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE
+ \o Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE
+ \o Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE
+
+ \o Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE
+ \o Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE
+ \o Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE
+ \o Q_ATOMIC_INT_TEST_AND_SET_IS_WAIT_FREE
+
+ \o Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE
+ \o Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
+ \o Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE
+ \o Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE
+
+ \o Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE
+ \o Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
+ \o Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE
+ \o Q_ATOMIC_INT_FETCH_AND_ADD_IS_WAIT_FREE
+
+ \endlist
+
+ \sa QAtomicPointer
+*/
+
+/*! \fn QAtomicInt::QAtomicInt(int value)
+
+ Constructs a QAtomicInt with the given \a value.
+*/
+
+/*! \fn QAtomicInt::QAtomicInt(const QAtomicInt &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*! \fn QAtomicInt &QAtomicInt::operator=(int value)
+
+ Assigns the \a value to this QAtomicInt and returns a reference to
+ this QAtomicInt.
+*/
+
+/*! \fn QAtomicInt &QAtomicInt::operator=(const QAtomicInt &other)
+
+ Assigns \a other to this QAtomicInt and returns a reference to
+ this QAtomicInt.
+*/
+
+/*! \fn bool QAtomicInt::operator==(int value) const
+
+ Returns true if the \a value is equal to the value in this
+ QAtomicInt; otherwise returns false.
+*/
+
+/*! \fn bool QAtomicInt::operator!=(int value) const
+
+ Returns true if the value of this QAtomicInt is not equal to \a
+ value; otherwise returns false.
+*/
+
+/*! \fn bool QAtomicInt::operator!() const
+
+ Returns true is the value of this QAtomicInt is zero; otherwise
+ returns false.
+*/
+
+/*! \fn QAtomicInt::operator int() const
+
+ Returns the value stored by the QAtomicInt object as an integer.
+*/
+
+/*! \fn bool QAtomicInt::isReferenceCountingNative()
+
+ Returns true if reference counting is implemented using atomic
+ processor instructions, false otherwise.
+*/
+
+/*! \fn bool QAtomicInt::isReferenceCountingWaitFree()
+
+ Returns true if atomic reference counting is wait-free, false
+ otherwise.
+*/
+
+/*! \fn bool QAtomicInt::ref()
+ Atomically increments the value of this QAtomicInt. Returns true
+ if the new value is non-zero, false otherwise.
+
+ This function uses \e ordered \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before and after the atomic operation (in program order)
+ may not be re-ordered.
+
+ \sa deref()
+*/
+
+/*! \fn bool QAtomicInt::deref()
+ Atomically decrements the value of this QAtomicInt. Returns true
+ if the new value is non-zero, false otherwise.
+
+ This function uses \e ordered \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before and after the atomic operation (in program order)
+ may not be re-ordered.
+
+ \sa ref()
+*/
+
+/*! \fn bool QAtomicInt::isTestAndSetNative()
+
+ Returns true if test-and-set is implemented using atomic processor
+ instructions, false otherwise.
+*/
+
+/*! \fn bool QAtomicInt::isTestAndSetWaitFree()
+
+ Returns true if atomic test-and-set is wait-free, false otherwise.
+*/
+
+/*! \fn bool QAtomicInt::testAndSetRelaxed(int expectedValue, int newValue)
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicInt is the \a expectedValue,
+ the test-and-set functions assign the \a newValue to this
+ QAtomicInt and return true. If the values are \e not the same,
+ this function does nothing and returns false.
+
+ This function uses \e relaxed \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, leaving the compiler and
+ processor to freely reorder memory accesses.
+*/
+
+/*! \fn bool QAtomicInt::testAndSetAcquire(int expectedValue, int newValue)
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicInt is the \a expectedValue,
+ the test-and-set functions assign the \a newValue to this
+ QAtomicInt and return true. If the values are \e not the same,
+ this function does nothing and returns false.
+
+ This function uses \e acquire \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access following the atomic operation (in program order) may not
+ be re-ordered before the atomic operation.
+*/
+
+/*! \fn bool QAtomicInt::testAndSetRelease(int expectedValue, int newValue)
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicInt is the \a expectedValue,
+ the test-and-set functions assign the \a newValue to this
+ QAtomicInt and return true. If the values are \e not the same,
+ this function does nothing and returns false.
+
+ This function uses \e release \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before the atomic operation (in program order) may not be
+ re-ordered after the atomic operation.
+*/
+
+/*! \fn bool QAtomicInt::testAndSetOrdered(int expectedValue, int newValue)
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicInt is the \a expectedValue,
+ the test-and-set functions assign the \a newValue to this
+ QAtomicInt and return true. If the values are \e not the same,
+ this function does nothing and returns false.
+
+ This function uses \e ordered \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before and after the atomic operation (in program order)
+ may not be re-ordered.
+*/
+
+/*! \fn bool QAtomicInt::isFetchAndStoreNative()
+
+ Returns true if fetch-and-store is implemented using atomic
+ processor instructions, false otherwise.
+*/
+
+/*! \fn bool QAtomicInt::isFetchAndStoreWaitFree()
+
+ Returns true if atomic fetch-and-store is wait-free, false
+ otherwise.
+*/
+
+/*! \fn int QAtomicInt::fetchAndStoreRelaxed(int newValue)
+
+ Atomic fetch-and-store.
+
+ Reads the current value of this QAtomicInt and then assigns it the
+ \a newValue, returning the original value.
+
+ This function uses \e relaxed \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, leaving the compiler and
+ processor to freely reorder memory accesses.
+*/
+
+/*! \fn int QAtomicInt::fetchAndStoreAcquire(int newValue)
+
+ Atomic fetch-and-store.
+
+ Reads the current value of this QAtomicInt and then assigns it the
+ \a newValue, returning the original value.
+
+ This function uses \e acquire \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access following the atomic operation (in program order) may not
+ be re-ordered before the atomic operation.
+*/
+
+/*! \fn int QAtomicInt::fetchAndStoreRelease(int newValue)
+
+ Atomic fetch-and-store.
+
+ Reads the current value of this QAtomicInt and then assigns it the
+ \a newValue, returning the original value.
+
+ This function uses \e release \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before the atomic operation (in program order) may not be
+ re-ordered after the atomic operation.
+*/
+
+/*! \fn int QAtomicInt::fetchAndStoreOrdered(int newValue)
+
+ Atomic fetch-and-store.
+
+ Reads the current value of this QAtomicInt and then assigns it the
+ \a newValue, returning the original value.
+
+ This function uses \e ordered \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before and after the atomic operation (in program order)
+ may not be re-ordered.
+*/
+
+/*! \fn bool QAtomicInt::isFetchAndAddNative()
+
+ Returns true if fetch-and-add is implemented using atomic
+ processor instructions, false otherwise.
+*/
+
+/*! \fn bool QAtomicInt::isFetchAndAddWaitFree()
+
+ Returns true if atomic fetch-and-add is wait-free, false
+ otherwise.
+*/
+
+/*! \fn int QAtomicInt::fetchAndAddRelaxed(int valueToAdd)
+
+ Atomic fetch-and-add.
+
+ Reads the current value of this QAtomicInt and then adds
+ \a valueToAdd to the current value, returning the original value.
+
+ This function uses \e relaxed \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, leaving the compiler and
+ processor to freely reorder memory accesses.
+*/
+
+/*! \fn int QAtomicInt::fetchAndAddAcquire(int valueToAdd)
+
+ Atomic fetch-and-add.
+
+ Reads the current value of this QAtomicInt and then adds
+ \a valueToAdd to the current value, returning the original value.
+
+ This function uses \e acquire \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access following the atomic operation (in program order) may not
+ be re-ordered before the atomic operation.
+*/
+
+/*! \fn int QAtomicInt::fetchAndAddRelease(int valueToAdd)
+
+ Atomic fetch-and-add.
+
+ Reads the current value of this QAtomicInt and then adds
+ \a valueToAdd to the current value, returning the original value.
+
+ This function uses \e release \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before the atomic operation (in program order) may not be
+ re-ordered after the atomic operation.
+*/
+
+/*! \fn int QAtomicInt::fetchAndAddOrdered(int valueToAdd)
+
+ Atomic fetch-and-add.
+
+ Reads the current value of this QAtomicInt and then adds
+ \a valueToAdd to the current value, returning the original value.
+
+ This function uses \e ordered \l {QAtomicInt#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before and after the atomic operation (in program order)
+ may not be re-ordered.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined if and only if all generations of your
+ processor support atomic reference counting.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined when only certain generations of the
+ processor support atomic reference counting. Use the
+ QAtomicInt::isReferenceCountingNative() function to check what
+ your processor supports.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined when the hardware does not support atomic
+ reference counting.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE
+ \relates QAtomicInt
+
+ This macro is defined together with
+ Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE to indicate that
+ the reference counting is wait-free.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined if and only if your processor supports
+ atomic test-and-set on integers.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined when only certain generations of the
+ processor support atomic test-and-set on integers. Use the
+ QAtomicInt::isTestAndSetNative() function to check what your
+ processor supports.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined when the hardware does not support atomic
+ test-and-set on integers.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_TEST_AND_SET_IS_WAIT_FREE
+ \relates QAtomicInt
+
+ This macro is defined together with
+ Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE to indicate that the
+ atomic test-and-set on integers is wait-free.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined if and only if your processor supports
+ atomic fetch-and-store on integers.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined when only certain generations of the
+ processor support atomic fetch-and-store on integers. Use the
+ QAtomicInt::isFetchAndStoreNative() function to check what your
+ processor supports.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined when the hardware does not support atomic
+ fetch-and-store on integers.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE
+ \relates QAtomicInt
+
+ This macro is defined together with
+ Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE to indicate that the
+ atomic fetch-and-store on integers is wait-free.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined if and only if your processor supports
+ atomic fetch-and-add on integers.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined when only certain generations of the
+ processor support atomic fetch-and-add on integers. Use the
+ QAtomicInt::isFetchAndAddNative() function to check what your
+ processor supports.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE
+ \relates QAtomicInt
+
+ This macro is defined when the hardware does not support atomic
+ fetch-and-add on integers.
+*/
+
+/*!
+ \macro Q_ATOMIC_INT_FETCH_AND_ADD_IS_WAIT_FREE
+ \relates QAtomicInt
+
+ This macro is defined together with
+ Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE to indicate that the
+ atomic fetch-and-add on integers is wait-free.
+*/
+
+
+
+
+/*!
+ \class QAtomicPointer
+ \brief The QAtomicPointer class is a template class that provides platform-independent atomic operations on pointers.
+ \since 4.4
+
+ \ingroup thread
+
+ For atomic operations on integers, see the QAtomicInt class.
+
+ An complex operation that completes without interruption is said
+ to be \e atomic. The QAtomicPointer class provides atomic
+ test-and-set, fetch-and-store, and fetch-and-add for pointers.
+
+ \section1 Non-atomic convenience operators
+
+ For convenience, QAtomicPointer provides pointer comparison, cast,
+ dereference, and assignment operators. Note that these operators
+ are \e not atomic.
+
+ \section1 The Atomic API
+
+ \section2 Memory ordering
+
+ QAtomicPointer provides several implementations of the atomic
+ test-and-set, fetch-and-store, and fetch-and-add functions. Each
+ implementation defines a memory ordering semantic that describes
+ how memory accesses surrounding the atomic instruction are
+ executed by the processor. Since many modern architectures allow
+ out-of-order execution and memory ordering, using the correct
+ semantic is necessary to ensure that your application functions
+ properly on all processors.
+
+ \list
+
+ \o Relaxed - memory ordering is unspecified, leaving the compiler
+ and processor to freely reorder memory accesses.
+
+ \o Acquire - memory access following the atomic operation (in
+ program order) may not be re-ordered before the atomic operation.
+
+ \o Release - memory access before the atomic operation (in program
+ order) may not be re-ordered after the atomic operation.
+
+ \o Ordered - the same Acquire and Release semantics combined.
+
+ \endlist
+
+ \section2 Test-and-set
+
+ If the current value of the QAtomicPointer is an expected value,
+ the test-and-set functions assign a new value to the
+ QAtomicPointer and return true. If values are \a not the same,
+ these functions do nothing and return false. This operation
+ equates to the following code:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qatomic.cpp 4
+
+ There are 4 test-and-set functions: testAndSetRelaxed(),
+ testAndSetAcquire(), testAndSetRelease(), and
+ testAndSetOrdered(). See above for an explanation of the different
+ memory ordering semantics.
+
+ \section2 Fetch-and-store
+
+ The atomic fetch-and-store functions read the current value of the
+ QAtomicPointer and then assign a new value, returning the original
+ value. This operation equates to the following code:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qatomic.cpp 5
+
+ There are 4 fetch-and-store functions: fetchAndStoreRelaxed(),
+ fetchAndStoreAcquire(), fetchAndStoreRelease(), and
+ fetchAndStoreOrdered(). See above for an explanation of the
+ different memory ordering semantics.
+
+ \section2 Fetch-and-add
+
+ The atomic fetch-and-add functions read the current value of the
+ QAtomicPointer and then add the given value to the current value,
+ returning the original value. This operation equates to the
+ following code:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qatomic.cpp 6
+
+ There are 4 fetch-and-add functions: fetchAndAddRelaxed(),
+ fetchAndAddAcquire(), fetchAndAddRelease(), and
+ fetchAndAddOrdered(). See above for an explanation of the
+ different memory ordering semantics.
+
+ \section1 Feature Tests for the Atomic API
+
+ Providing a platform-independent atomic API that works on all
+ processors is challenging. The API provided by QAtomicPointer is
+ guaranteed to work atomically on all processors. However, since
+ not all processors implement support for every operation provided
+ by QAtomicPointer, it is necessary to expose information about the
+ processor.
+
+ You can check at compile time which features are supported on your
+ hardware using various macros. These will tell you if your
+ hardware always, sometimes, or does not support a particular
+ operation. The macros have the form
+ Q_ATOMIC_POINTER_\e{OPERATION}_IS_\e{HOW}_NATIVE. \e{OPERATION} is
+ one of TEST_AND_SET, FETCH_AND_STORE, or FETCH_AND_ADD, and
+ \e{HOW} is one of ALWAYS, SOMETIMES, or NOT. There will always be
+ exactly one defined macro per operation. For example, if
+ Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE is defined, neither
+ Q_ATOMIC_POINTER_TEST_AND_SET_IS_SOMETIMES_NATIVE nor
+ Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE will be defined.
+
+ An operation that completes in constant time is said to be
+ wait-free. Such operations are not implemented using locks or
+ loops of any kind. For atomic operations that are always
+ supported, and that are wait-free, Qt defines the
+ Q_ATOMIC_POINTER_\e{OPERATION}_IS_WAIT_FREE in addition to the
+ Q_ATOMIC_POINTER_\e{OPERATION}_IS_ALWAYS_NATIVE.
+
+ In cases where an atomic operation is only supported in newer
+ generations of the processor, QAtomicPointer also provides a way
+ to check at runtime what your hardware supports with the
+ isTestAndSetNative(), isFetchAndStoreNative(), and
+ isFetchAndAddNative() functions. Wait-free implementations can be
+ detected using the isTestAndSetWaitFree(),
+ isFetchAndStoreWaitFree(), and isFetchAndAddWaitFree() functions.
+
+ Below is a complete list of all feature macros for QAtomicPointer:
+
+ \list
+
+ \o Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE
+ \o Q_ATOMIC_POINTER_TEST_AND_SET_IS_SOMETIMES_NATIVE
+ \o Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE
+ \o Q_ATOMIC_POINTER_TEST_AND_SET_IS_WAIT_FREE
+
+ \o Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE
+ \o Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
+ \o Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE
+ \o Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_WAIT_FREE
+
+ \o Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE
+ \o Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
+ \o Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE
+ \o Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_WAIT_FREE
+
+ \endlist
+
+ \sa QAtomicInt
+*/
+
+/*! \fn QAtomicPointer::QAtomicPointer(T *value)
+
+ Constructs a QAtomicPointer with the given \a value.
+*/
+
+/*! \fn QAtomicPointer::QAtomicPointer(const QAtomicPointer<T> &other)
+
+ Constructs a copy of \a other.
+*/
+
+/*! \fn QAtomicPointer<T> &QAtomicPointer::operator=(T *value)
+
+ Assigns the \a value to this QAtomicPointer and returns a
+ reference to this QAtomicPointer.
+*/
+
+/*! \fn QAtomicPointer<T> &QAtomicPointer::operator=(const QAtomicPointer<T> &other)
+
+ Assigns \a other to this QAtomicPointer and returns a reference to
+ this QAtomicPointer.
+*/
+
+/*! \fn bool QAtomicPointer::operator==(T *value) const
+
+ Returns true if the \a value is equal to the value in this
+ QAtomicPointer; otherwise returns false.
+*/
+
+/*! \fn bool QAtomicPointer::operator!=(T *value) const
+
+ Returns true if the value of this QAtomicPointer is not equal to
+ \a value; otherwise returns false.
+*/
+
+/*! \fn bool QAtomicPointer::operator!() const
+
+ Returns true is the current value of this QAtomicPointer is zero;
+ otherwise returns false.
+*/
+
+/*! \fn QAtomicPointer::operator T *() const
+
+ Returns the current pointer value stored by this QAtomicPointer
+ object.
+*/
+
+/*! \fn T *QAtomicPointer::operator->() const
+
+*/
+
+/*! \fn bool QAtomicPointer::isTestAndSetNative()
+
+ Returns true if test-and-set is implemented using atomic processor
+ instructions, false otherwise.
+*/
+
+/*! \fn bool QAtomicPointer::isTestAndSetWaitFree()
+
+ Returns true if atomic test-and-set is wait-free, false otherwise.
+*/
+
+/*! \fn bool QAtomicPointer::testAndSetRelaxed(T *expectedValue, T *newValue)
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicPointer is the \a expectedValue,
+ the test-and-set functions assign the \a newValue to this
+ QAtomicPointer and return true. If the values are \e not the same,
+ this function does nothing and returns false.
+
+ This function uses \e relaxed \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, leaving the compiler and
+ processor to freely reorder memory accesses.
+*/
+
+/*! \fn bool QAtomicPointer::testAndSetAcquire(T *expectedValue, T *newValue)
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicPointer is the \a expectedValue,
+ the test-and-set functions assign the \a newValue to this
+ QAtomicPointer and return true. If the values are \e not the same,
+ this function does nothing and returns false.
+
+ This function uses \e acquire \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access following the atomic operation (in program order) may not
+ be re-ordered before the atomic operation.
+*/
+
+/*! \fn bool QAtomicPointer::testAndSetRelease(T *expectedValue, T *newValue)
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicPointer is the \a expectedValue,
+ the test-and-set functions assign the \a newValue to this
+ QAtomicPointer and return true. If the values are \e not the same,
+ this function does nothing and returns false.
+
+ This function uses \e release \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before the atomic operation (in program order) may not be
+ re-ordered after the atomic operation.
+*/
+
+/*! \fn bool QAtomicPointer::testAndSetOrdered(T *expectedValue, T *newValue)
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicPointer is the \a expectedValue,
+ the test-and-set functions assign the \a newValue to this
+ QAtomicPointer and return true. If the values are \e not the same,
+ this function does nothing and returns false.
+
+ This function uses \e ordered \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before and after the atomic operation (in program order)
+ may not be re-ordered.
+*/
+
+/*! \fn bool QAtomicPointer::isFetchAndStoreNative()
+
+ Returns true if fetch-and-store is implemented using atomic
+ processor instructions, false otherwise.
+*/
+
+/*! \fn bool QAtomicPointer::isFetchAndStoreWaitFree()
+
+ Returns true if atomic fetch-and-store is wait-free, false
+ otherwise.
+*/
+
+/*! \fn T *QAtomicPointer::fetchAndStoreRelaxed(T *newValue)
+
+ Atomic fetch-and-store.
+
+ Reads the current value of this QAtomicPointer and then assigns it the
+ \a newValue, returning the original value.
+
+ This function uses \e relaxed \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, leaving the compiler and
+ processor to freely reorder memory accesses.
+*/
+
+/*! \fn T *QAtomicPointer::fetchAndStoreAcquire(T *newValue)
+
+ Atomic fetch-and-store.
+
+ Reads the current value of this QAtomicPointer and then assigns it the
+ \a newValue, returning the original value.
+
+ This function uses \e acquire \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access following the atomic operation (in program order) may not
+ be re-ordered before the atomic operation.
+*/
+
+/*! \fn T *QAtomicPointer::fetchAndStoreRelease(T *newValue)
+
+ Atomic fetch-and-store.
+
+ Reads the current value of this QAtomicPointer and then assigns it the
+ \a newValue, returning the original value.
+
+ This function uses \e release \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before the atomic operation (in program order) may not be
+ re-ordered after the atomic operation.
+*/
+
+/*! \fn T *QAtomicPointer::fetchAndStoreOrdered(T *newValue)
+
+ Atomic fetch-and-store.
+
+ Reads the current value of this QAtomicPointer and then assigns it the
+ \a newValue, returning the original value.
+
+ This function uses \e ordered \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before and after the atomic operation (in program order)
+ may not be re-ordered.
+*/
+
+/*! \fn bool QAtomicPointer::isFetchAndAddNative()
+
+ Returns true if fetch-and-add is implemented using atomic
+ processor instructions, false otherwise.
+*/
+
+/*! \fn bool QAtomicPointer::isFetchAndAddWaitFree()
+
+ Returns true if atomic fetch-and-add is wait-free, false
+ otherwise.
+*/
+
+/*! \fn T *QAtomicPointer::fetchAndAddRelaxed(qptrdiff valueToAdd)
+
+ Atomic fetch-and-add.
+
+ Reads the current value of this QAtomicPointer and then adds
+ \a valueToAdd to the current value, returning the original value.
+
+ This function uses \e relaxed \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, leaving the compiler and
+ processor to freely reorder memory accesses.
+*/
+
+/*! \fn T *QAtomicPointer::fetchAndAddAcquire(qptrdiff valueToAdd)
+
+ Atomic fetch-and-add.
+
+ Reads the current value of this QAtomicPointer and then adds
+ \a valueToAdd to the current value, returning the original value.
+
+ This function uses \e acquire \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access following the atomic operation (in program order) may not
+ be re-ordered before the atomic operation.
+*/
+
+/*! \fn T *QAtomicPointer::fetchAndAddRelease(qptrdiff valueToAdd)
+
+ Atomic fetch-and-add.
+
+ Reads the current value of this QAtomicPointer and then adds
+ \a valueToAdd to the current value, returning the original value.
+
+ This function uses \e release \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before the atomic operation (in program order) may not be
+ re-ordered after the atomic operation.
+*/
+
+/*! \fn T *QAtomicPointer::fetchAndAddOrdered(qptrdiff valueToAdd)
+
+ Atomic fetch-and-add.
+
+ Reads the current value of this QAtomicPointer and then adds
+ \a valueToAdd to the current value, returning the original value.
+
+ This function uses \e ordered \l {QAtomicPointer#Memory
+ ordering}{memory ordering} semantics, which ensures that memory
+ access before and after the atomic operation (in program order)
+ may not be re-ordered.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE
+ \relates QAtomicPointer
+
+ This macro is defined if and only if your processor supports
+ atomic test-and-set on pointers.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_TEST_AND_SET_IS_SOMETIMES_NATIVE
+ \relates QAtomicPointer
+
+ This macro is defined when only certain generations of the
+ processor support atomic test-and-set on pointers. Use the
+ QAtomicPointer::isTestAndSetNative() function to check what your
+ processor supports.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE
+ \relates QAtomicPointer
+
+ This macro is defined when the hardware does not support atomic
+ test-and-set on pointers.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_TEST_AND_SET_IS_WAIT_FREE
+ \relates QAtomicPointer
+
+ This macro is defined together with
+ Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE to indicate that
+ the atomic test-and-set on pointers is wait-free.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE
+ \relates QAtomicPointer
+
+ This macro is defined if and only if your processor supports
+ atomic fetch-and-store on pointers.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
+ \relates QAtomicPointer
+
+ This macro is defined when only certain generations of the
+ processor support atomic fetch-and-store on pointers. Use the
+ QAtomicPointer::isFetchAndStoreNative() function to check what
+ your processor supports.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE
+ \relates QAtomicPointer
+
+ This macro is defined when the hardware does not support atomic
+ fetch-and-store on pointers.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_WAIT_FREE
+ \relates QAtomicPointer
+
+ This macro is defined together with
+ Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE to indicate that
+ the atomic fetch-and-store on pointers is wait-free.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE
+ \relates QAtomicPointer
+
+ This macro is defined if and only if your processor supports
+ atomic fetch-and-add on pointers.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
+ \relates QAtomicPointer
+
+ This macro is defined when only certain generations of the
+ processor support atomic fetch-and-add on pointers. Use the
+ QAtomicPointer::isFetchAndAddNative() function to check what your
+ processor supports.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE
+ \relates QAtomicPointer
+
+ This macro is defined when the hardware does not support atomic
+ fetch-and-add on pointers.
+*/
+
+/*!
+ \macro Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_WAIT_FREE
+ \relates QAtomicPointer
+
+ This macro is defined together with
+ Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE to indicate that
+ the atomic fetch-and-add on pointers is wait-free.
+*/
diff --git a/src/corelib/thread/qatomic.h b/src/corelib/thread/qatomic.h
new file mode 100644
index 0000000..4e949ee
--- /dev/null
+++ b/src/corelib/thread/qatomic.h
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QATOMIC_H
+#define QATOMIC_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qbasicatomic.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+// High-level atomic integer operations
+class Q_CORE_EXPORT QAtomicInt : public QBasicAtomicInt
+{
+public:
+ inline QAtomicInt(int value = 0)
+ {
+#ifdef QT_ARCH_PARISC
+ this->_q_lock[0] = this->_q_lock[1] = this->_q_lock[2] = this->_q_lock[3] = -1;
+#endif
+ _q_value = value;
+ }
+ inline QAtomicInt(const QAtomicInt &other)
+ {
+#ifdef QT_ARCH_PARISC
+ this->_q_lock[0] = this->_q_lock[1] = this->_q_lock[2] = this->_q_lock[3] = -1;
+#endif
+ _q_value = other._q_value;
+ }
+
+ inline QAtomicInt &operator=(int value)
+ {
+ (void) QBasicAtomicInt::operator=(value);
+ return *this;
+ }
+
+ inline QAtomicInt &operator=(const QAtomicInt &other)
+ {
+ (void) QBasicAtomicInt::operator=(other);
+ return *this;
+ }
+
+#ifdef qdoc
+ bool operator==(int value) const;
+ bool operator!=(int value) const;
+ bool operator!() const;
+ operator int() const;
+
+ static bool isReferenceCountingNative();
+ static bool isReferenceCountingWaitFree();
+
+ bool ref();
+ bool deref();
+
+ static bool isTestAndSetNative();
+ static bool isTestAndSetWaitFree();
+
+ bool testAndSetRelaxed(int expectedValue, int newValue);
+ bool testAndSetAcquire(int expectedValue, int newValue);
+ bool testAndSetRelease(int expectedValue, int newValue);
+ bool testAndSetOrdered(int expectedValue, int newValue);
+
+ static bool isFetchAndStoreNative();
+ static bool isFetchAndStoreWaitFree();
+
+ int fetchAndStoreRelaxed(int newValue);
+ int fetchAndStoreAcquire(int newValue);
+ int fetchAndStoreRelease(int newValue);
+ int fetchAndStoreOrdered(int newValue);
+
+ static bool isFetchAndAddNative();
+ static bool isFetchAndAddWaitFree();
+
+ int fetchAndAddRelaxed(int valueToAdd);
+ int fetchAndAddAcquire(int valueToAdd);
+ int fetchAndAddRelease(int valueToAdd);
+ int fetchAndAddOrdered(int valueToAdd);
+#endif
+};
+
+// High-level atomic pointer operations
+template <typename T>
+class QAtomicPointer : public QBasicAtomicPointer<T>
+{
+public:
+ inline QAtomicPointer(T *value = 0)
+ {
+#ifdef QT_ARCH_PARISC
+ this->_q_lock[0] = this->_q_lock[1] = this->_q_lock[2] = this->_q_lock[3] = -1;
+#endif
+ QBasicAtomicPointer<T>::_q_value = value;
+ }
+ inline QAtomicPointer(const QAtomicPointer<T> &other)
+ {
+#ifdef QT_ARCH_PARISC
+ this->_q_lock[0] = this->_q_lock[1] = this->_q_lock[2] = this->_q_lock[3] = -1;
+#endif
+ QBasicAtomicPointer<T>::_q_value = other._q_value;
+ }
+
+ inline QAtomicPointer<T> &operator=(T *value)
+ {
+ (void) QBasicAtomicPointer<T>::operator=(value);
+ return *this;
+ }
+
+ inline QAtomicPointer<T> &operator=(const QAtomicPointer<T> &other)
+ {
+ (void) QBasicAtomicPointer<T>::operator=(other);
+ return *this;
+ }
+
+#ifdef qdoc
+ bool operator==(T *value) const;
+ bool operator!=(T *value) const;
+ bool operator!() const;
+ operator T *() const;
+ T *operator->() const;
+
+ static bool isTestAndSetNative();
+ static bool isTestAndSetWaitFree();
+
+ bool testAndSetRelaxed(T *expectedValue, T *newValue);
+ bool testAndSetAcquire(T *expectedValue, T *newValue);
+ bool testAndSetRelease(T *expectedValue, T *newValue);
+ bool testAndSetOrdered(T *expectedValue, T *newValue);
+
+ static bool isFetchAndStoreNative();
+ static bool isFetchAndStoreWaitFree();
+
+ T *fetchAndStoreRelaxed(T *newValue);
+ T *fetchAndStoreAcquire(T *newValue);
+ T *fetchAndStoreRelease(T *newValue);
+ T *fetchAndStoreOrdered(T *newValue);
+
+ static bool isFetchAndAddNative();
+ static bool isFetchAndAddWaitFree();
+
+ T *fetchAndAddRelaxed(qptrdiff valueToAdd);
+ T *fetchAndAddAcquire(qptrdiff valueToAdd);
+ T *fetchAndAddRelease(qptrdiff valueToAdd);
+ T *fetchAndAddOrdered(qptrdiff valueToAdd);
+#endif
+};
+
+/*!
+ This is a helper for the assignment operators of implicitly
+ shared classes. Your assignment operator should look like this:
+
+ \snippet doc/src/snippets/code/src.corelib.thread.qatomic.h 0
+*/
+template <typename T>
+inline void qAtomicAssign(T *&d, T *x)
+{
+ if (d == x)
+ return;
+ x->ref.ref();
+ if (!d->ref.deref())
+ delete d;
+ d = x;
+}
+
+/*!
+ This is a helper for the detach method of implicitly shared
+ classes. Your private class needs a copy constructor which copies
+ the members and sets the refcount to 1. After that, your detach
+ function should look like this:
+
+ \snippet doc/src/snippets/code/src.corelib.thread.qatomic.h 1
+*/
+template <typename T>
+inline void qAtomicDetach(T *&d)
+{
+ if (d->ref == 1)
+ return;
+ T *x = d;
+ d = new T(*d);
+ if (!x->ref.deref())
+ delete x;
+}
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif // QATOMIC_H
diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h
new file mode 100644
index 0000000..a5af6c5
--- /dev/null
+++ b/src/corelib/thread/qbasicatomic.h
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBASICATOMIC_H
+#define QBASICATOMIC_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class Q_CORE_EXPORT QBasicAtomicInt
+{
+public:
+#ifdef QT_ARCH_PARISC
+ int _q_lock[4];
+#endif
+ volatile int _q_value;
+
+ // Non-atomic API
+ inline bool operator==(int value) const
+ {
+ return _q_value == value;
+ }
+
+ inline bool operator!=(int value) const
+ {
+ return _q_value != value;
+ }
+
+ inline bool operator!() const
+ {
+ return _q_value == 0;
+ }
+
+ inline operator int() const
+ {
+ return _q_value;
+ }
+
+ inline QBasicAtomicInt &operator=(int value)
+ {
+#ifdef QT_ARCH_PARISC
+ this->_q_lock[0] = this->_q_lock[1] = this->_q_lock[2] = this->_q_lock[3] = -1;
+#endif
+ _q_value = value;
+ return *this;
+ }
+
+ // Atomic API, implemented in qatomic_XXX.h
+
+ static bool isReferenceCountingNative();
+ static bool isReferenceCountingWaitFree();
+
+ bool ref();
+ bool deref();
+
+ static bool isTestAndSetNative();
+ static bool isTestAndSetWaitFree();
+
+ bool testAndSetRelaxed(int expectedValue, int newValue);
+ bool testAndSetAcquire(int expectedValue, int newValue);
+ bool testAndSetRelease(int expectedValue, int newValue);
+ bool testAndSetOrdered(int expectedValue, int newValue);
+
+ static bool isFetchAndStoreNative();
+ static bool isFetchAndStoreWaitFree();
+
+ int fetchAndStoreRelaxed(int newValue);
+ int fetchAndStoreAcquire(int newValue);
+ int fetchAndStoreRelease(int newValue);
+ int fetchAndStoreOrdered(int newValue);
+
+ static bool isFetchAndAddNative();
+ static bool isFetchAndAddWaitFree();
+
+ int fetchAndAddRelaxed(int valueToAdd);
+ int fetchAndAddAcquire(int valueToAdd);
+ int fetchAndAddRelease(int valueToAdd);
+ int fetchAndAddOrdered(int valueToAdd);
+};
+
+template <typename T>
+class QBasicAtomicPointer
+{
+public:
+#ifdef QT_ARCH_PARISC
+ int _q_lock[4];
+#endif
+ T * volatile _q_value;
+
+ // Non-atomic API
+ inline bool operator==(T *value) const
+ {
+ return _q_value == value;
+ }
+
+ inline bool operator!=(T *value) const
+ {
+ return !operator==(value);
+ }
+
+ inline bool operator!() const
+ {
+ return operator==(0);
+ }
+
+ inline operator T *() const
+ {
+ return _q_value;
+ }
+
+ inline T *operator->() const
+ {
+ return _q_value;
+ }
+
+ inline QBasicAtomicPointer<T> &operator=(T *value)
+ {
+#ifdef QT_ARCH_PARISC
+ this->_q_lock[0] = this->_q_lock[1] = this->_q_lock[2] = this->_q_lock[3] = -1;
+#endif
+ _q_value = value;
+ return *this;
+ }
+
+ // Atomic API, implemented in qatomic_XXX.h
+
+ static bool isTestAndSetNative();
+ static bool isTestAndSetWaitFree();
+
+ bool testAndSetRelaxed(T *expectedValue, T *newValue);
+ bool testAndSetAcquire(T *expectedValue, T *newValue);
+ bool testAndSetRelease(T *expectedValue, T *newValue);
+ bool testAndSetOrdered(T *expectedValue, T *newValue);
+
+ static bool isFetchAndStoreNative();
+ static bool isFetchAndStoreWaitFree();
+
+ T *fetchAndStoreRelaxed(T *newValue);
+ T *fetchAndStoreAcquire(T *newValue);
+ T *fetchAndStoreRelease(T *newValue);
+ T *fetchAndStoreOrdered(T *newValue);
+
+ static bool isFetchAndAddNative();
+ static bool isFetchAndAddWaitFree();
+
+ T *fetchAndAddRelaxed(qptrdiff valueToAdd);
+ T *fetchAndAddAcquire(qptrdiff valueToAdd);
+ T *fetchAndAddRelease(qptrdiff valueToAdd);
+ T *fetchAndAddOrdered(qptrdiff valueToAdd);
+};
+
+#ifdef QT_ARCH_PARISC
+# define Q_BASIC_ATOMIC_INITIALIZER(a) {{-1,-1,-1,-1},(a)}
+#else
+# define Q_BASIC_ATOMIC_INITIALIZER(a) { (a) }
+#endif
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#if defined(QT_MOC) || defined(QT_BUILD_QMAKE) || defined(QT_RCC) || defined(QT_UIC) || defined(QT_BOOTSTRAPPED)
+# include <QtCore/qatomic_bootstrap.h>
+#else
+# include <QtCore/qatomic_arch.h>
+#endif
+
+#endif // QBASIC_ATOMIC
diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp
new file mode 100644
index 0000000..a3b3fe7
--- /dev/null
+++ b/src/corelib/thread/qmutex.cpp
@@ -0,0 +1,515 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qmutex.h"
+
+#ifndef QT_NO_THREAD
+#include "qatomic.h"
+#include "qthread.h"
+#include "qmutex_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QMutex
+ \brief The QMutex class provides access serialization between threads.
+
+ \threadsafe
+
+ \ingroup thread
+ \ingroup environment
+ \mainclass
+
+ 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). It is
+ usually best to use a mutex with a QMutexLocker since this makes
+ it easy to ensure that locking and unlocking are performed
+ consistently.
+
+ For example, say there is a method that prints a message to the
+ user on two lines:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 0
+
+ If these two methods are called in succession, the following happens:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 1
+
+ If these two methods are called simultaneously from two threads then the
+ following sequence could result:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 2
+
+ If we add a mutex, we should get the result we want:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 3
+
+ 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().
+
+ \sa QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition
+*/
+
+/*!
+ \enum QMutex::RecursionMode
+
+ \value Recursive In this mode, a thread can lock the same mutex
+ multiple times and the mutex won't be unlocked
+ until a corresponding number of unlock() calls
+ have been made.
+
+ \value NonRecursive In this mode, a thread may only lock a mutex
+ once.
+
+ \sa QMutex()
+*/
+
+/*!
+ Constructs a new mutex. The mutex is created in an unlocked state.
+
+ If \a mode is QMutex::Recursive, a thread can lock the same mutex
+ multiple times and the mutex won't be unlocked until a
+ corresponding number of unlock() calls have been made. The
+ default is QMutex::NonRecursive.
+
+ \sa lock(), unlock()
+*/
+QMutex::QMutex(RecursionMode mode)
+ : d(new QMutexPrivate(mode))
+{ }
+
+/*!
+ Destroys the mutex.
+
+ \warning Destroying a locked mutex may result in undefined behavior.
+*/
+QMutex::~QMutex()
+{ delete d; }
+
+/*!
+ Locks the mutex. If another thread has locked the mutex then this
+ call will block until that thread has unlocked it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread is allowed if this mutex is a
+ \l{QMutex::Recursive}{recursive mutex}. If this mutex is a
+ \l{QMutex::NonRecursive}{non-recursive mutex}, this function will
+ \e dead-lock when the mutex is locked recursively.
+
+ \sa unlock()
+*/
+void QMutex::lock()
+{
+ Qt::HANDLE self;
+
+ if (d->recursive) {
+ self = QThread::currentThreadId();
+ if (d->owner == self) {
+ ++d->count;
+ Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflow in recursion counter");
+ return;
+ }
+
+ bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0;
+ if (!isLocked) {
+#ifndef QT_NO_DEBUG
+ if (d->owner == self)
+ qWarning("QMutex::lock: Deadlock detected in thread %ld",
+ long(d->owner));
+#endif
+
+ // didn't get the lock, wait for it
+ isLocked = d->wait();
+ Q_ASSERT_X(isLocked, "QMutex::lock",
+ "Internal error, infinite wait has timed out.");
+
+ // don't need to wait for the lock anymore
+ d->contenders.deref();
+ }
+
+ d->owner = self;
+ ++d->count;
+ Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflow in recursion counter");
+ return;
+ }
+
+#ifndef QT_NO_DEBUG
+ self = QThread::currentThreadId();
+#endif
+
+ bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1);
+ if (!isLocked) {
+ int spinCount = 0;
+ int lastSpinCount = d->lastSpinCount;
+
+ enum { AdditionalSpins = 20, SpinCountPenalizationDivisor = 4 };
+ const int maximumSpinCount = lastSpinCount + AdditionalSpins;
+
+ do {
+ if (spinCount++ > maximumSpinCount) {
+ // puts("spinning useless, sleeping");
+ isLocked = d->contenders.fetchAndAddAcquire(1) == 0;
+ if (!isLocked) {
+#ifndef QT_NO_DEBUG
+ if (d->owner == self)
+ qWarning("QMutex::lock: Deadlock detected in thread %ld",
+ long(d->owner));
+#endif
+
+ // didn't get the lock, wait for it
+ isLocked = d->wait();
+ Q_ASSERT_X(isLocked, "QMutex::lock",
+ "Internal error, infinite wait has timed out.");
+
+ // don't need to wait for the lock anymore
+ d->contenders.deref();
+ }
+ // decrease the lastSpinCount since we didn't actually get the lock by spinning
+ spinCount = -d->lastSpinCount / SpinCountPenalizationDivisor;
+ break;
+ }
+
+ isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1);
+ } while (!isLocked);
+
+ // adjust the last spin lock count
+ lastSpinCount = d->lastSpinCount;
+ d->lastSpinCount = spinCount >= 0
+ ? qMax(lastSpinCount, spinCount)
+ : lastSpinCount + spinCount;
+ }
+
+#ifndef QT_NO_DEBUG
+ d->owner = self;
+#endif
+}
+
+/*!
+ Attempts to lock the mutex. If the lock was obtained, this function
+ returns true. If another thread has locked the mutex, this
+ function returns false immediately.
+
+ If the lock was obtained, the mutex must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread is allowed if this mutex is a
+ \l{QMutex::Recursive}{recursive mutex}. If this mutex is a
+ \l{QMutex::NonRecursive}{non-recursive mutex}, this function will
+ \e always return false when attempting to lock the mutex
+ recursively.
+
+ \sa lock(), unlock()
+*/
+bool QMutex::tryLock()
+{
+ Qt::HANDLE self;
+
+ if (d->recursive) {
+ self = QThread::currentThreadId();
+ if (d->owner == self) {
+ ++d->count;
+ Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
+ return true;
+ }
+
+ bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1);
+ if (!isLocked) {
+ // some other thread has the mutex locked, or we tried to
+ // recursively lock an non-recursive mutex
+ return isLocked;
+ }
+
+ d->owner = self;
+ ++d->count;
+ Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
+ return isLocked;
+ }
+
+#ifndef QT_NO_DEBUG
+ self = QThread::currentThreadId();
+#endif
+ bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1);
+ if (!isLocked) {
+ // some other thread has the mutex locked, or we tried to
+ // recursively lock an non-recursive mutex
+ return isLocked;
+ }
+#ifndef QT_NO_DEBUG
+ d->owner = self;
+#endif
+ return isLocked;
+}
+
+/*! \overload
+
+ Attempts to lock the mutex. This function returns true if the lock
+ was obtained; otherwise it returns false. If another thread has
+ locked the mutex, this function will wait for at most \a timeout
+ milliseconds for the mutex to become available.
+
+ Note: Passing a negative number as the \a timeout is equivalent to
+ calling lock(), i.e. this function will wait forever until mutex
+ can be locked if \a timeout is negative.
+
+ If the lock was obtained, the mutex must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread is allowed if this mutex is a
+ \l{QMutex::Recursive}{recursive mutex}. If this mutex is a
+ \l{QMutex::NonRecursive}{non-recursive mutex}, this function will
+ \e always return false when attempting to lock the mutex
+ recursively.
+
+ \sa lock(), unlock()
+*/
+bool QMutex::tryLock(int timeout)
+{
+ Qt::HANDLE self;
+
+ if (d->recursive) {
+ self = QThread::currentThreadId();
+ if (d->owner == self) {
+ ++d->count;
+ Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
+ return true;
+ }
+
+ bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0;
+ if (!isLocked) {
+ // didn't get the lock, wait for it
+ isLocked = d->wait(timeout);
+
+ // don't need to wait for the lock anymore
+ d->contenders.deref();
+ if (!isLocked)
+ return false;
+ }
+
+ d->owner = self;
+ ++d->count;
+ Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
+ return true;
+ }
+
+#ifndef QT_NO_DEBUG
+ self = QThread::currentThreadId();
+#endif
+ bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0;
+ if (!isLocked) {
+ // didn't get the lock, wait for it
+ isLocked = d->wait(timeout);
+
+ // don't need to wait for the lock anymore
+ d->contenders.deref();
+ if (!isLocked)
+ return false;
+ }
+#ifndef QT_NO_DEBUG
+ d->owner = self;
+#endif
+ return true;
+}
+
+
+/*!
+ 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 behavior.
+
+ \sa lock()
+*/
+void QMutex::unlock()
+{
+ Q_ASSERT_X(d->owner == QThread::currentThreadId(), "QMutex::unlock()",
+ "A mutex must be unlocked in the same thread that locked it.");
+
+ if (d->recursive) {
+ if (!--d->count) {
+ d->owner = 0;
+ if (!d->contenders.testAndSetRelease(1, 0))
+ d->wakeUp();
+ }
+ } else {
+#ifndef QT_NO_DEBUG
+ d->owner = 0;
+#endif
+ if (!d->contenders.testAndSetRelease(1, 0))
+ d->wakeUp();
+ }
+}
+
+/*!
+ \fn bool QMutex::locked()
+
+ Returns true if the mutex is locked by another thread; otherwise
+ returns false.
+
+ It is generally a bad idea to use this function, because code
+ that uses it has a race condition. Use tryLock() and unlock()
+ instead.
+
+ \oldcode
+ bool isLocked = mutex.locked();
+ \newcode
+ bool isLocked = true;
+ if (mutex.tryLock()) {
+ mutex.unlock();
+ isLocked = false;
+ }
+ \endcode
+*/
+
+/*!
+ \class QMutexLocker
+ \brief The QMutexLocker class is a convenience class that simplifies
+ locking and unlocking mutexes.
+
+ \threadsafe
+
+ \ingroup thread
+ \ingroup environment
+
+ Locking and unlocking a QMutex in complex functions and
+ statements or in exception handling code is error-prone and
+ difficult to debug. QMutexLocker can be used in such situations
+ to ensure that the state of the mutex is always well-defined.
+
+ QMutexLocker should be created within a function where a
+ QMutex needs to be locked. The mutex is locked when QMutexLocker
+ is created. You can unlock and relock the mutex with \c unlock()
+ and \c relock(). If locked, the mutex will be unlocked when the
+ QMutexLocker is destroyed.
+
+ For example, this complex function locks a QMutex upon entering
+ the function and unlocks the mutex at all the exit points:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 4
+
+ 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:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 5
+
+ 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 \c 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:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 6
+
+ \sa QReadLocker, QWriteLocker, QMutex
+*/
+
+/*!
+ \fn QMutexLocker::QMutexLocker(QMutex *mutex)
+
+ Constructs a QMutexLocker and locks \a mutex. The mutex will be
+ unlocked when the QMutexLocker is destroyed. If \a mutex is zero,
+ QMutexLocker does nothing.
+
+ \sa QMutex::lock()
+*/
+
+/*!
+ \fn QMutexLocker::~QMutexLocker()
+
+ Destroys the QMutexLocker and unlocks the mutex that was locked
+ in the constructor.
+
+ \sa QMutex::unlock()
+*/
+
+/*!
+ \fn QMutex *QMutexLocker::mutex() const
+
+ Returns a pointer to the mutex that was locked in the
+ constructor.
+*/
+
+/*!
+ \fn void QMutexLocker::unlock()
+
+ Unlocks this mutex locker. You can use \c relock() to lock
+ it again. It does not need to be locked when destroyed.
+
+ \sa relock()
+*/
+
+/*!
+ \fn void QMutexLocker::relock()
+
+ Relocks an unlocked mutex locker.
+
+ \sa unlock()
+*/
+
+/*!
+ \fn QMutex::QMutex(bool recursive)
+
+ Use the constructor that takes a RecursionMode parameter instead.
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_THREAD
diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h
new file mode 100644
index 0000000..51f1f79
--- /dev/null
+++ b/src/corelib/thread/qmutex.h
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMUTEX_H
+#define QMUTEX_H
+
+#include <QtCore/qglobal.h>
+#include <new>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_THREAD
+
+class QMutexPrivate;
+
+class Q_CORE_EXPORT QMutex
+{
+ friend class QWaitCondition;
+ friend class QWaitConditionPrivate;
+
+public:
+ enum RecursionMode { NonRecursive, Recursive };
+
+ explicit QMutex(RecursionMode mode = NonRecursive);
+ ~QMutex();
+
+ void lock();
+ bool tryLock();
+ bool tryLock(int timeout);
+ void unlock();
+
+#if defined(QT3_SUPPORT)
+ inline QT3_SUPPORT bool locked()
+ {
+ if (!tryLock())
+ return true;
+ unlock();
+ return false;
+ }
+ inline QT3_SUPPORT_CONSTRUCTOR QMutex(bool recursive)
+ {
+ new (this) QMutex(recursive ? Recursive : NonRecursive);
+ }
+#endif
+
+private:
+ Q_DISABLE_COPY(QMutex)
+
+ QMutexPrivate *d;
+};
+
+class Q_CORE_EXPORT QMutexLocker
+{
+public:
+ inline explicit QMutexLocker(QMutex *m)
+ : mtx(m)
+ {
+ Q_ASSERT_X((val & quintptr(1u)) == quintptr(0),
+ "QMutexLocker", "QMutex pointer is misaligned");
+ relock();
+ }
+ inline ~QMutexLocker() { unlock(); }
+
+ inline void unlock()
+ {
+ if (mtx) {
+ if ((val & quintptr(1u)) == quintptr(1u)) {
+ val &= ~quintptr(1u);
+ mtx->unlock();
+ }
+ }
+ }
+
+ inline void relock()
+ {
+ if (mtx) {
+ if ((val & quintptr(1u)) == quintptr(0u)) {
+ mtx->lock();
+ val |= quintptr(1u);
+ }
+ }
+ }
+
+#if defined(Q_CC_MSVC)
+#pragma warning( push )
+#pragma warning( disable : 4312 ) // ignoring the warning from /Wp64
+#endif
+
+ inline QMutex *mutex() const
+ {
+ return reinterpret_cast<QMutex *>(val & ~quintptr(1u));
+ }
+
+#if defined(Q_CC_MSVC)
+#pragma warning( pop )
+#endif
+
+private:
+ Q_DISABLE_COPY(QMutexLocker)
+
+ union {
+ QMutex *mtx;
+ quintptr val;
+ };
+};
+
+#else // QT_NO_THREAD
+
+
+class Q_CORE_EXPORT QMutex
+{
+public:
+ enum RecursionMode { NonRecursive, Recursive };
+
+ inline explicit QMutex(RecursionMode mode = NonRecursive) { Q_UNUSED(mode); }
+ inline ~QMutex() {}
+
+ static inline void lock() {}
+ static inline bool tryLock() { return true; }
+ static inline bool tryLock(int timeout) { Q_UNUSED(timeout); return true; }
+ static void unlock() {}
+
+#if defined(QT3_SUPPORT)
+ static inline QT3_SUPPORT bool locked() { return false; }
+#endif
+
+private:
+ Q_DISABLE_COPY(QMutex)
+};
+
+class Q_CORE_EXPORT QMutexLocker
+{
+public:
+ inline explicit QMutexLocker(QMutex *) {}
+ inline ~QMutexLocker() {}
+
+ static inline void unlock() {}
+ static void relock() {}
+ static inline QMutex *mutex() { return 0; }
+
+private:
+ Q_DISABLE_COPY(QMutexLocker)
+};
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMUTEX_H
diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h
new file mode 100644
index 0000000..6eb15be
--- /dev/null
+++ b/src/corelib/thread/qmutex_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMUTEX_P_H
+#define QMUTEX_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qmutex.cpp, qmutex_unix.cpp, and qmutex_win.cpp. This header
+// file may change from version to version without notice, or even be
+// removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qnamespace.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMutexPrivate {
+public:
+ QMutexPrivate(QMutex::RecursionMode mode);
+ ~QMutexPrivate();
+
+ ulong self();
+ bool wait(int timeout = -1);
+ void wakeUp();
+
+ const bool recursive;
+ QAtomicInt contenders;
+ volatile int lastSpinCount;
+ Qt::HANDLE owner;
+ uint count;
+
+#if defined(Q_OS_UNIX)
+ volatile bool wakeup;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+#elif defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ HANDLE event;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QMUTEX_P_H
diff --git a/src/corelib/thread/qmutex_unix.cpp b/src/corelib/thread/qmutex_unix.cpp
new file mode 100644
index 0000000..e15ec7e
--- /dev/null
+++ b/src/corelib/thread/qmutex_unix.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qmutex.h"
+#include "qstring.h"
+
+#ifndef QT_NO_THREAD
+#include "qatomic.h"
+#include "qmutex_p.h"
+
+#include <errno.h>
+
+QT_BEGIN_NAMESPACE
+
+static void report_error(int code, const char *where, const char *what)
+{
+ if (code != 0)
+ qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code)));
+}
+
+
+QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode)
+ : recursive(mode == QMutex::Recursive), contenders(0), lastSpinCount(0), owner(0), count(0), wakeup(false)
+{
+ report_error(pthread_mutex_init(&mutex, NULL), "QMutex", "mutex init");
+ report_error(pthread_cond_init(&cond, NULL), "QMutex", "cv init");
+}
+
+QMutexPrivate::~QMutexPrivate()
+{
+ report_error(pthread_cond_destroy(&cond), "QMutex", "cv destroy");
+ report_error(pthread_mutex_destroy(&mutex), "QMutex", "mutex destroy");
+}
+
+bool QMutexPrivate::wait(int timeout)
+{
+ report_error(pthread_mutex_lock(&mutex), "QMutex::lock", "mutex lock");
+ int errorCode = 0;
+ while (!wakeup) {
+ if (timeout < 0) {
+ errorCode = pthread_cond_wait(&cond, &mutex);
+ } else {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ timespec ti;
+ ti.tv_nsec = (tv.tv_usec + (timeout % 1000) * 1000) * 1000;
+ ti.tv_sec = tv.tv_sec + (timeout / 1000) + (ti.tv_nsec / 1000000000);
+ ti.tv_nsec %= 1000000000;
+
+ errorCode = pthread_cond_timedwait(&cond, &mutex, &ti);
+ }
+ if (errorCode) {
+ if (errorCode == ETIMEDOUT)
+ break;
+ report_error(errorCode, "QMutex::lock()", "cv wait");
+ }
+ }
+ wakeup = false;
+ report_error(pthread_mutex_unlock(&mutex), "QMutex::lock", "mutex unlock");
+ return errorCode == 0;
+}
+
+void QMutexPrivate::wakeUp()
+{
+ report_error(pthread_mutex_lock(&mutex), "QMutex::unlock", "mutex lock");
+ wakeup = true;
+ report_error(pthread_cond_signal(&cond), "QMutex::unlock", "cv signal");
+ report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock");
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_THREAD
diff --git a/src/corelib/thread/qmutex_win.cpp b/src/corelib/thread/qmutex_win.cpp
new file mode 100644
index 0000000..a73815d
--- /dev/null
+++ b/src/corelib/thread/qmutex_win.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <windows.h>
+
+#include "qmutex.h"
+#include <qatomic.h>
+#include "qmutex_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode)
+ : recursive(mode == QMutex::Recursive), contenders(0), lastSpinCount(0), owner(0), count(0)
+{
+ if (QSysInfo::WindowsVersion == 0) {
+ // mutex was created before initializing WindowsVersion. this
+ // can happen when creating the resource file engine handler,
+ // for example. try again with just the A version
+#ifdef Q_OS_WINCE
+ event = CreateEventW(0, FALSE, FALSE, 0);
+#else
+ event = CreateEventA(0, FALSE, FALSE, 0);
+#endif
+ } else {
+ event = QT_WA_INLINE(CreateEventW(0, FALSE, FALSE, 0),
+ CreateEventA(0, FALSE, FALSE, 0));
+ }
+ if (!event)
+ qWarning("QMutexPrivate::QMutexPrivate: Cannot create event");
+}
+
+QMutexPrivate::~QMutexPrivate()
+{ CloseHandle(event); }
+
+bool QMutexPrivate::wait(int timeout)
+{
+ return WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0;
+}
+
+void QMutexPrivate::wakeUp()
+{ SetEvent(event); }
+
+QT_END_NAMESPACE
diff --git a/src/corelib/thread/qmutexpool.cpp b/src/corelib/thread/qmutexpool.cpp
new file mode 100644
index 0000000..71ecab5
--- /dev/null
+++ b/src/corelib/thread/qmutexpool.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qatomic.h"
+#include "qmutexpool_p.h"
+
+#ifndef QT_NO_THREAD
+
+QT_BEGIN_NAMESPACE
+
+// qt_global_mutexpool is here for backwards compatability only,
+// use QMutexpool::instance() in new clode.
+Q_CORE_EXPORT QMutexPool *qt_global_mutexpool = 0;
+Q_GLOBAL_STATIC_WITH_ARGS(QMutexPool, globalMutexPool, (true))
+
+/*!
+ \class QMutexPool
+ \brief The QMutexPool class provides a pool of QMutex objects.
+
+ \internal
+
+ \ingroup thread
+
+ QMutexPool is a convenience class that provides access to a fixed
+ number of QMutex objects.
+
+ Typical use of a QMutexPool is in situations where it is not
+ possible or feasible to use one QMutex for every protected object.
+ The mutex pool will return a mutex based on the address of the
+ object that needs protection.
+
+ For example, consider this simple class:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qmutexpool.cpp 0
+
+ Adding a QMutex member to the Number class does not make sense,
+ because it is so small. However, in order to ensure that access to
+ each Number is protected, you need to use a mutex. In this case, a
+ QMutexPool would be ideal.
+
+ Code to calculate the square of a number would then look something
+ like this:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qmutexpool.cpp 1
+
+ This function will safely calculate the square of a number, since
+ it uses a mutex from a QMutexPool. The mutex is locked and
+ unlocked automatically by the QMutexLocker class. See the
+ QMutexLocker documentation for more details.
+*/
+
+/*!
+ Constructs a QMutexPool, reserving space for \a size QMutexes. If
+ \a recursive is true, all QMutexes in the pool will be recursive
+ mutexes; otherwise they will all be non-recursive (the default).
+
+ The QMutexes are created when needed, and deleted when the
+ QMutexPool is destructed.
+*/
+QMutexPool::QMutexPool(bool recursive, int size)
+ : count(size), recurs(recursive)
+{
+ mutexes = new QMutex*[count];
+ for (int index = 0; index < count; ++index) {
+ mutexes[index] = 0;
+ }
+}
+
+/*!
+ Destructs a QMutexPool. All QMutexes that were created by the pool
+ are deleted.
+*/
+QMutexPool::~QMutexPool()
+{
+ QMutexLocker locker(&mutex);
+ for (int index = 0; index < count; ++index) {
+ delete mutexes[index];
+ mutexes[index] = 0;
+ }
+ delete [] mutexes;
+ mutexes = 0;
+}
+
+/*!
+ Returns the global QMutexPool instance.
+*/
+QMutexPool *QMutexPool::instance()
+{
+ return globalMutexPool();
+}
+
+/*!
+ Returns a QMutex from the pool. QMutexPool uses the value \a address
+ to determine which mutex is returned from the pool.
+*/
+QMutex *QMutexPool::get(const void *address)
+{
+ Q_ASSERT_X(address != 0, "QMutexPool::get()", "'address' argument cannot be zero");
+ int index = int((quintptr(address) >> (sizeof(address) >> 1)) % count);
+
+ if (!mutexes[index]) {
+ // mutex not created, create one
+
+ QMutexLocker locker(&mutex);
+ // we need to check once again that the mutex hasn't been created, since
+ // 2 threads could be trying to create a mutex at the same index...
+ if (!mutexes[index])
+ mutexes[index] = new QMutex(recurs ? QMutex::Recursive : QMutex::NonRecursive);
+ }
+
+ return mutexes[index];
+}
+
+/*!
+ Returns a QMutex from the global mutex pool.
+*/
+QMutex *QMutexPool::globalInstanceGet(const void *address)
+{
+ QMutexPool * const globalInstance = globalMutexPool();
+ if (globalInstance == 0)
+ return 0;
+ return globalInstance->get(address);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_THREAD
diff --git a/src/corelib/thread/qmutexpool_p.h b/src/corelib/thread/qmutexpool_p.h
new file mode 100644
index 0000000..65a3b54
--- /dev/null
+++ b/src/corelib/thread/qmutexpool_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMUTEXPOOL_P_H
+#define QMUTEXPOOL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of QSettings. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qmutex.h"
+
+#ifndef QT_NO_THREAD
+
+QT_BEGIN_NAMESPACE
+
+class Q_CORE_EXPORT QMutexPool
+{
+public:
+ explicit QMutexPool(bool recursive = false, int size = 128);
+ ~QMutexPool();
+
+ QMutex *get(const void *address);
+ static QMutexPool *instance();
+ static QMutex *globalInstanceGet(const void *address);
+
+private:
+ QMutex mutex;
+ QMutex **mutexes;
+ int count;
+ bool recurs;
+};
+
+extern Q_CORE_EXPORT QMutexPool *qt_global_mutexpool;
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_THREAD
+
+#endif // QMUTEXPOOL_P_H
diff --git a/src/corelib/thread/qorderedmutexlocker_p.h b/src/corelib/thread/qorderedmutexlocker_p.h
new file mode 100644
index 0000000..3b92e31
--- /dev/null
+++ b/src/corelib/thread/qorderedmutexlocker_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QORDEREDMUTEXLOCKER_P_H
+#define QORDEREDMUTEXLOCKER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QMutex;
+
+/*
+ Locks 2 mutexes in a defined order, avoiding a recursive lock if
+ we're trying to lock the same mutex twice.
+*/
+class QOrderedMutexLocker
+{
+public:
+ QOrderedMutexLocker(QMutex *m1, QMutex *m2)
+ : mtx1((m1 == m2) ? m1 : (m1 < m2 ? m1 : m2)),
+ mtx2((m1 == m2) ? 0 : (m1 < m2 ? m2 : m1)),
+ locked(false)
+ {
+ relock();
+ }
+ ~QOrderedMutexLocker()
+ {
+ unlock();
+ }
+
+ void relock()
+ {
+ if (!locked) {
+ if (mtx1) mtx1->lock();
+ if (mtx2) mtx2->lock();
+ locked = true;
+ }
+ }
+
+ void unlock()
+ {
+ if (locked) {
+ if (mtx1) mtx1->unlock();
+ if (mtx2) mtx2->unlock();
+ locked = false;
+ }
+ }
+
+ static bool relock(QMutex *mtx1, QMutex *mtx2)
+ {
+ // mtx1 is already locked, mtx2 not... do we need to unlock and relock?
+ if (mtx1 == mtx2)
+ return false;
+ if (mtx1 < mtx2) {
+ mtx2->lock();
+ return true;
+ }
+ mtx1->unlock();
+ mtx2->lock();
+ mtx1->lock();
+ return true;
+ }
+
+private:
+ QMutex *mtx1, *mtx2;
+ bool locked;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp
new file mode 100644
index 0000000..809e07f
--- /dev/null
+++ b/src/corelib/thread/qreadwritelock.cpp
@@ -0,0 +1,584 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qreadwritelock.h"
+
+#ifndef QT_NO_THREAD
+#include "qmutex.h"
+#include "qthread.h"
+#include "qwaitcondition.h"
+
+#include "qreadwritelock_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*! \class QReadWriteLock
+ \brief The QReadWriteLock class provides read-write locking.
+
+ \threadsafe
+
+ \ingroup thread
+ \ingroup environment
+
+ A read-write lock is a synchronization tool for protecting
+ resources that can be accessed for reading and writing. This type
+ of lock is useful if you want to allow multiple threads to have
+ simultaneous read-only access, but as soon as one thread wants to
+ write to the resource, all other threads must be blocked until
+ the writing is complete.
+
+ In many cases, QReadWriteLock is a direct competitor to QMutex.
+ QReadWriteLock is a good choice if there are many concurrent
+ reads and writing occurs infrequently.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 0
+
+ To ensure that writers aren't blocked forever by readers, readers
+ attempting to obtain a lock will not succeed if there is a blocked
+ writer waiting for access, even if the lock is currently only
+ accessed by other readers. Also, if the lock is accessed by a
+ writer and another writer comes in, that writer will have
+ priority over any readers that might also be waiting.
+
+ Like QMutex, a QReadWriteLock can be recursively locked by the
+ same thread when constructed in
+ \l{QReadWriteLock::RecursionMode}recursive mode}. In such cases,
+ unlock() must be called the same number of times lockForWrite() or
+ lockForRead() was called. Note that the lock type cannot be
+ changed when trying to lock recursively, i.e. it is not possible
+ to lock for reading in a thread that already has locked for
+ writing (and vice versa).
+
+ \sa QReadLocker, QWriteLocker, QMutex, QSemaphore
+*/
+
+/*!
+ \enum QReadWriteLock::RecursionMode
+ \since 4.4
+
+ \value Recursive In this mode, a thread can lock the same
+ QReadWriteLock multiple times and the mutex won't be unlocked
+ until a corresponding number of unlock() calls have been made.
+
+ \value NonRecursive In this mode, a thread may only lock a
+ QReadWriteLock once.
+
+ \sa QReadWriteLock()
+*/
+
+/*!
+ Constructs a QReadWriteLock object in NonRecursive mode.
+
+ \sa lockForRead(), lockForWrite()
+*/
+QReadWriteLock::QReadWriteLock()
+ :d(new QReadWriteLockPrivate(NonRecursive))
+{ }
+
+/*!
+ \since 4.4
+
+ Constructs a QReadWriteLock object in the given \a recursionMode.
+
+ \sa lockForRead(), lockForWrite(), RecursionMode
+*/
+QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
+ : d(new QReadWriteLockPrivate(recursionMode))
+{ }
+
+/*!
+ Destroys the QReadWriteLock object.
+
+ \warning Destroying a read-write lock that is in use may result
+ in undefined behavior.
+*/
+QReadWriteLock::~QReadWriteLock()
+{
+ delete d;
+}
+
+/*!
+ Locks the lock for reading. This function will block the current
+ thread if any thread (including the current) has locked for
+ writing.
+
+ \sa unlock() lockForWrite() tryLockForRead()
+*/
+void QReadWriteLock::lockForRead()
+{
+ QMutexLocker lock(&d->mutex);
+
+ Qt::HANDLE self = 0;
+ if (d->recursive) {
+ self = QThread::currentThreadId();
+
+ QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
+ if (it != d->currentReaders.end()) {
+ ++it.value();
+ ++d->accessCount;
+ Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::lockForRead()",
+ "Overflow in lock counter");
+ return;
+ }
+ }
+
+ while (d->accessCount < 0 || d->waitingWriters) {
+ ++d->waitingReaders;
+ d->readerWait.wait(&d->mutex);
+ --d->waitingReaders;
+ }
+ if (d->recursive)
+ d->currentReaders.insert(self, 1);
+
+ ++d->accessCount;
+ Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::lockForRead()", "Overflow in lock counter");
+}
+
+/*!
+ Attempts to lock for reading. If the lock was obtained, this
+ function returns true, otherwise it returns false instead of
+ waiting for the lock to become available, i.e. it does not block.
+
+ The lock attempt will fail if another thread has locked for
+ writing.
+
+ If the lock was obtained, the lock must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ \sa unlock() lockForRead()
+*/
+bool QReadWriteLock::tryLockForRead()
+{
+ QMutexLocker lock(&d->mutex);
+
+ Qt::HANDLE self = 0;
+ if (d->recursive) {
+ self = QThread::currentThreadId();
+
+ QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
+ if (it != d->currentReaders.end()) {
+ ++it.value();
+ ++d->accessCount;
+ Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()",
+ "Overflow in lock counter");
+ return true;
+ }
+ }
+
+ if (d->accessCount < 0)
+ return false;
+ if (d->recursive)
+ d->currentReaders.insert(self, 1);
+
+ ++d->accessCount;
+ Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()", "Overflow in lock counter");
+
+ return true;
+}
+
+/*! \overload
+
+ Attempts to lock for reading. This function returns true if the
+ lock was obtained; otherwise it returns false. If another thread
+ has locked for writing, this function will wait for at most \a
+ timeout milliseconds for the lock to become available.
+
+ Note: Passing a negative number as the \a timeout is equivalent to
+ calling lockForRead(), i.e. this function will wait forever until
+ lock can be locked for reading when \a timeout is negative.
+
+ If the lock was obtained, the lock must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ \sa unlock() lockForRead()
+*/
+bool QReadWriteLock::tryLockForRead(int timeout)
+{
+ QMutexLocker lock(&d->mutex);
+
+ Qt::HANDLE self = 0;
+ if (d->recursive) {
+ self = QThread::currentThreadId();
+
+ QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
+ if (it != d->currentReaders.end()) {
+ ++it.value();
+ ++d->accessCount;
+ Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()",
+ "Overflow in lock counter");
+ return true;
+ }
+ }
+
+ while (d->accessCount < 0 || d->waitingWriters) {
+ ++d->waitingReaders;
+ bool success = d->readerWait.wait(&d->mutex, timeout < 0 ? ULONG_MAX : timeout);
+ --d->waitingReaders;
+ if (!success)
+ return false;
+ }
+ if (d->recursive)
+ d->currentReaders.insert(self, 1);
+
+ ++d->accessCount;
+ Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()", "Overflow in lock counter");
+
+ return true;
+}
+
+ /*!
+ Locks the lock for writing. This function will block the current
+ thread if another thread has locked for reading or writing.
+
+ \sa unlock() lockForRead() tryLockForWrite()
+ */
+void QReadWriteLock::lockForWrite()
+{
+ QMutexLocker lock(&d->mutex);
+
+ Qt::HANDLE self = 0;
+ if (d->recursive) {
+ self = QThread::currentThreadId();
+
+ if (d->currentWriter == self) {
+ --d->accessCount;
+ Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()",
+ "Overflow in lock counter");
+ return;
+ }
+ }
+
+ while (d->accessCount != 0) {
+ ++d->waitingWriters;
+ d->writerWait.wait(&d->mutex);
+ --d->waitingWriters;
+ }
+ if (d->recursive)
+ d->currentWriter = self;
+
+ --d->accessCount;
+ Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()", "Overflow in lock counter");
+}
+
+/*!
+ Attempts to lock for writing. If the lock was obtained, this
+ function returns true; otherwise, it returns false immediately.
+
+ The lock attempt will fail if another thread has locked for
+ reading or writing.
+
+ If the lock was obtained, the lock must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ \sa unlock() lockForWrite()
+*/
+bool QReadWriteLock::tryLockForWrite()
+{
+ QMutexLocker lock(&d->mutex);
+
+ Qt::HANDLE self = 0;
+ if (d->recursive) {
+ self = QThread::currentThreadId();
+
+ if (d->currentWriter == self) {
+ --d->accessCount;
+ Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()",
+ "Overflow in lock counter");
+ return true;
+ }
+ }
+
+ if (d->accessCount != 0)
+ return false;
+ if (d->recursive)
+ d->currentWriter = self;
+
+ --d->accessCount;
+ Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::tryLockForWrite()",
+ "Overflow in lock counter");
+
+ return true;
+}
+
+/*! \overload
+
+ Attempts to lock for writing. This function returns true if the
+ lock was obtained; otherwise it returns false. If another thread
+ has locked for reading or writing, this function will wait for at
+ most \a timeout milliseconds for the lock to become available.
+
+ Note: Passing a negative number as the \a timeout is equivalent to
+ calling lockForWrite(), i.e. this function will wait forever until
+ lock can be locked for writing when \a timeout is negative.
+
+ If the lock was obtained, the lock must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ \sa unlock() lockForWrite()
+*/
+bool QReadWriteLock::tryLockForWrite(int timeout)
+{
+ QMutexLocker lock(&d->mutex);
+
+ Qt::HANDLE self = 0;
+ if (d->recursive) {
+ self = QThread::currentThreadId();
+
+ if (d->currentWriter == self) {
+ --d->accessCount;
+ Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()",
+ "Overflow in lock counter");
+ return true;
+ }
+ }
+
+ while (d->accessCount != 0) {
+ ++d->waitingWriters;
+ bool success = d->writerWait.wait(&d->mutex, timeout < 0 ? ULONG_MAX : timeout);
+ --d->waitingWriters;
+
+ if (!success)
+ return false;
+ }
+ if (d->recursive)
+ d->currentWriter = self;
+
+ --d->accessCount;
+ Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::tryLockForWrite()",
+ "Overflow in lock counter");
+
+ return true;
+}
+
+/*!
+ Unlocks the lock.
+
+ Attempting to unlock a lock that is not locked is an error, and will result
+ in program termination.
+
+ \sa lockForRead() lockForWrite() tryLockForRead() tryLockForWrite()
+*/
+void QReadWriteLock::unlock()
+{
+ QMutexLocker lock(&d->mutex);
+
+ Q_ASSERT_X(d->accessCount != 0, "QReadWriteLock::unlock()", "Cannot unlock an unlocked lock");
+
+ bool unlocked = false;
+ if (d->accessCount > 0) {
+ // releasing a read lock
+ if (d->recursive) {
+ Qt::HANDLE self = QThread::currentThreadId();
+ QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
+ if (it != d->currentReaders.end()) {
+ if (--it.value() <= 0)
+ d->currentReaders.erase(it);
+ }
+ }
+
+ unlocked = --d->accessCount == 0;
+ } else if (d->accessCount < 0 && ++d->accessCount == 0) {
+ // released a write lock
+ unlocked = true;
+ d->currentWriter = 0;
+ }
+
+ if (unlocked) {
+ if (d->waitingWriters) {
+ d->writerWait.wakeOne();
+ } else if (d->waitingReaders) {
+ d->readerWait.wakeAll();
+ }
+ }
+}
+
+/*!
+ \class QReadLocker
+ \brief The QReadLocker class is a convenience class that
+ simplifies locking and unlocking read-write locks for read access.
+
+ \threadsafe
+
+ \ingroup thread
+ \ingroup environment
+
+ The purpose of QReadLocker (and QWriteLocker) is to simplify
+ QReadWriteLock locking and unlocking. Locking and unlocking
+ statements or in exception handling code is error-prone and
+ difficult to debug. QReadLocker can be used in such situations
+ to ensure that the state of the lock is always well-defined.
+
+ Here's an example that uses QReadLocker to lock and unlock a
+ read-write lock for reading:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 1
+
+ It is equivalent to the following code:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 2
+
+ The QMutexLocker documentation shows examples where the use of a
+ locker object greatly simplifies programming.
+
+ \sa QWriteLocker, QReadWriteLock
+*/
+
+/*!
+ \fn QReadLocker::QReadLocker(QReadWriteLock *lock)
+
+ Constructs a QReadLocker and locks \a lock for reading. The lock
+ will be unlocked when the QReadLocker is destroyed. If \c lock is
+ zero, QReadLocker does nothing.
+
+ \sa QReadWriteLock::lockForRead()
+*/
+
+/*!
+ \fn QReadLocker::~QReadLocker()
+
+ Destroys the QReadLocker and unlocks the lock that was passed to
+ the constructor.
+
+ \sa QReadWriteLock::unlock()
+*/
+
+/*!
+ \fn void QReadLocker::unlock()
+
+ Unlocks the lock associated with this locker.
+
+ \sa QReadWriteLock::unlock()
+*/
+
+/*!
+ \fn void QReadLocker::relock()
+
+ Relocks an unlocked lock.
+
+ \sa unlock()
+*/
+
+/*!
+ \fn QReadWriteLock *QReadLocker::readWriteLock() const
+
+ Returns a pointer to the read-write lock that was passed
+ to the constructor.
+*/
+
+/*!
+ \class QWriteLocker
+ \brief The QWriteLocker class is a convenience class that
+ simplifies locking and unlocking read-write locks for write access.
+
+ \threadsafe
+
+ \ingroup thread
+ \ingroup environment
+
+ The purpose of QWriteLocker (and QReadLocker is to simplify
+ QReadWriteLock locking and unlocking. Locking and unlocking
+ statements or in exception handling code is error-prone and
+ difficult to debug. QWriteLocker can be used in such situations
+ to ensure that the state of the lock is always well-defined.
+
+ Here's an example that uses QWriteLocker to lock and unlock a
+ read-write lock for writing:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 3
+
+ It is equivalent to the following code:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 4
+
+ The QMutexLocker documentation shows examples where the use of a
+ locker object greatly simplifies programming.
+
+ \sa QReadLocker, QReadWriteLock
+*/
+
+/*!
+ \fn QWriteLocker::QWriteLocker(QReadWriteLock *lock)
+
+ Constructs a QWriteLocker and locks \a lock for writing. The lock
+ will be unlocked when the QWriteLocker is destroyed. If \c lock is
+ zero, QWriteLocker does nothing.
+
+ \sa QReadWriteLock::lockForWrite()
+*/
+
+/*!
+ \fn QWriteLocker::~QWriteLocker()
+
+ Destroys the QWriteLocker and unlocks the lock that was passed to
+ the constructor.
+
+ \sa QReadWriteLock::unlock()
+*/
+
+/*!
+ \fn void QWriteLocker::unlock()
+
+ Unlocks the lock associated with this locker.
+
+ \sa QReadWriteLock::unlock()
+*/
+
+/*!
+ \fn void QWriteLocker::relock()
+
+ Relocks an unlocked lock.
+
+ \sa unlock()
+*/
+
+/*!
+ \fn QReadWriteLock *QWriteLocker::readWriteLock() const
+
+ Returns a pointer to the read-write lock that was passed
+ to the constructor.
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_THREAD
diff --git a/src/corelib/thread/qreadwritelock.h b/src/corelib/thread/qreadwritelock.h
new file mode 100644
index 0000000..c8df738
--- /dev/null
+++ b/src/corelib/thread/qreadwritelock.h
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QREADWRITELOCK_H
+#define QREADWRITELOCK_H
+
+#include <QtCore/qglobal.h>
+#include <limits.h> // ### Qt 5: remove
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_THREAD
+
+struct QReadWriteLockPrivate;
+
+class Q_CORE_EXPORT QReadWriteLock
+{
+public:
+ enum RecursionMode { NonRecursive, Recursive };
+
+ QReadWriteLock(); // ### Qt 5: merge with below
+ QReadWriteLock(RecursionMode recursionMode);
+ ~QReadWriteLock();
+
+ void lockForRead();
+ bool tryLockForRead();
+ bool tryLockForRead(int timeout);
+
+ void lockForWrite();
+ bool tryLockForWrite();
+ bool tryLockForWrite(int timeout);
+
+ void unlock();
+
+private:
+ Q_DISABLE_COPY(QReadWriteLock)
+ QReadWriteLockPrivate *d;
+
+ friend class QWaitCondition;
+};
+
+#if defined(Q_CC_MSVC)
+#pragma warning( push )
+#pragma warning( disable : 4312 ) // ignoring the warning from /Wp64
+#endif
+
+class Q_CORE_EXPORT QReadLocker
+{
+public:
+ inline QReadLocker(QReadWriteLock *readWriteLock);
+
+ inline ~QReadLocker()
+ { unlock(); }
+
+ inline void unlock()
+ {
+ if (q_lock) {
+ if ((q_val & quintptr(1u)) == quintptr(1u)) {
+ q_val &= ~quintptr(1u);
+ q_lock->unlock();
+ }
+ }
+ }
+
+ inline void relock()
+ {
+ if (q_lock) {
+ if ((q_val & quintptr(1u)) == quintptr(0u)) {
+ q_lock->lockForRead();
+ q_val |= quintptr(1u);
+ }
+ }
+ }
+
+ inline QReadWriteLock *readWriteLock() const
+ { return reinterpret_cast<QReadWriteLock *>(q_val & ~quintptr(1u)); }
+
+private:
+ Q_DISABLE_COPY(QReadLocker)
+ union {
+ QReadWriteLock *q_lock;
+ quintptr q_val;
+ };
+};
+
+inline QReadLocker::QReadLocker(QReadWriteLock *areadWriteLock)
+ : q_lock(areadWriteLock)
+{
+ Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0),
+ "QReadLocker", "QReadWriteLock pointer is misaligned");
+ relock();
+}
+
+class Q_CORE_EXPORT QWriteLocker
+{
+public:
+ inline QWriteLocker(QReadWriteLock *readWriteLock);
+
+ inline ~QWriteLocker()
+ { unlock(); }
+
+ inline void unlock()
+ {
+ if (q_lock) {
+ if ((q_val & quintptr(1u)) == quintptr(1u)) {
+ q_val &= ~quintptr(1u);
+ q_lock->unlock();
+ }
+ }
+ }
+
+ inline void relock()
+ {
+ if (q_lock) {
+ if ((q_val & quintptr(1u)) == quintptr(0u)) {
+ q_lock->lockForWrite();
+ q_val |= quintptr(1u);
+ }
+ }
+ }
+
+ inline QReadWriteLock *readWriteLock() const
+ { return reinterpret_cast<QReadWriteLock *>(q_val & ~quintptr(1u)); }
+
+
+private:
+ Q_DISABLE_COPY(QWriteLocker)
+ union{
+ QReadWriteLock *q_lock;
+ quintptr q_val;
+ };
+};
+
+inline QWriteLocker::QWriteLocker(QReadWriteLock *areadWriteLock)
+ : q_lock(areadWriteLock)
+{
+ Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0),
+ "QWriteLocker", "QReadWriteLock pointer is misaligned");
+ relock();
+}
+
+#if defined(Q_CC_MSVC)
+#pragma warning( pop )
+#endif
+
+#else // QT_NO_THREAD
+
+class Q_CORE_EXPORT QReadWriteLock
+{
+public:
+ inline explicit QReadWriteLock() { }
+ inline ~QReadWriteLock() { }
+
+ static inline void lockForRead() { }
+ static inline bool tryLockForRead() { return true; }
+ static inline bool tryLockForRead(int timeout) { Q_UNUSED(timeout); return true; }
+
+ static inline void lockForWrite() { }
+ static inline bool tryLockForWrite() { return true; }
+ static inline bool tryLockForWrite(int timeout) { Q_UNUSED(timeout); return true; }
+
+ static inline void unlock() { }
+
+private:
+ Q_DISABLE_COPY(QReadWriteLock)
+};
+
+class Q_CORE_EXPORT QReadLocker
+{
+public:
+ inline QReadLocker(QReadWriteLock *) { }
+ inline ~QReadLocker() { }
+
+ static inline void unlock() { }
+ static inline void relock() { }
+ static inline QReadWriteLock *readWriteLock() { return 0; }
+
+private:
+ Q_DISABLE_COPY(QReadLocker)
+};
+
+class Q_CORE_EXPORT QWriteLocker
+{
+public:
+ inline explicit QWriteLocker(QReadWriteLock *) { }
+ inline ~QWriteLocker() { }
+
+ static inline void unlock() { }
+ static inline void relock() { }
+ static inline QReadWriteLock *readWriteLock() { return 0; }
+
+private:
+ Q_DISABLE_COPY(QWriteLocker)
+};
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QREADWRITELOCK_H
diff --git a/src/corelib/thread/qreadwritelock_p.h b/src/corelib/thread/qreadwritelock_p.h
new file mode 100644
index 0000000..a813aa6
--- /dev/null
+++ b/src/corelib/thread/qreadwritelock_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QREADWRITELOCK_P_H
+#define QREADWRITELOCK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the implementation. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qhash.h>
+
+#ifndef QT_NO_THREAD
+
+QT_BEGIN_NAMESPACE
+
+struct QReadWriteLockPrivate
+{
+ QReadWriteLockPrivate(QReadWriteLock::RecursionMode recursionMode)
+ : accessCount(0), waitingReaders(0), waitingWriters(0),
+ recursive(recursionMode == QReadWriteLock::Recursive), currentWriter(0)
+ { }
+
+ QMutex mutex;
+ QWaitCondition readerWait;
+ QWaitCondition writerWait;
+
+ int accessCount;
+ int waitingReaders;
+ int waitingWriters;
+
+ bool recursive;
+ Qt::HANDLE currentWriter;
+ QHash<Qt::HANDLE, int> currentReaders;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_THREAD
+
+#endif // QREADWRITELOCK_P_H
diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp
new file mode 100644
index 0000000..82c3607
--- /dev/null
+++ b/src/corelib/thread/qsemaphore.cpp
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsemaphore.h"
+
+#ifndef QT_NO_THREAD
+#include "qmutex.h"
+#include "qwaitcondition.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSemaphore
+ \brief The QSemaphore class provides a general counting semaphore.
+
+ \threadsafe
+
+ \ingroup thread
+ \ingroup environment
+
+ A semaphore is a generalization of a mutex. While a mutex can
+ only be locked once, it's possible to acquire a semaphore
+ multiple times. Semaphores are typically used to protect a
+ certain number of identical resources.
+
+ Semaphores support two fundamental operations, acquire() and
+ release():
+
+ \list
+ \o acquire(\e{n}) tries to acquire \e n resources. If there aren't
+ that many resources available, the call will block until this
+ is the case.
+ \o release(\e{n}) releases \e n resources.
+ \endlist
+
+ There's also a tryAcquire() function that returns immediately if
+ it cannot acquire the resources, and an available() function that
+ returns the number of available resources at any time.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qsemaphore.cpp 0
+
+ A typical application of semaphores is for controlling access to
+ a circular buffer shared by a producer thread and a consumer
+ thread. The \l{threads/semaphores}{Semaphores} example shows how
+ to use QSemaphore to solve that problem.
+
+ A non-computing example of a semaphore would be dining at a
+ restaurant. A semaphore is initialized with the number of chairs
+ in the restaurant. As people arrive, they want a seat. As seats
+ are filled, available() is decremented. As people leave, the
+ available() is incremented, allowing more people to enter. If a
+ party of 10 people want to be seated, but there are only 9 seats,
+ those 10 people will wait, but a party of 4 people would be
+ seated (taking the available seats to 5, making the party of 10
+ people wait longer).
+
+ \sa QMutex, QWaitCondition, QThread, {Semaphores Example}
+*/
+
+class QSemaphorePrivate {
+public:
+ inline QSemaphorePrivate(int n) : avail(n) { }
+
+ QMutex mutex;
+ QWaitCondition cond;
+
+ int avail;
+};
+
+/*!
+ Creates a new semaphore and initializes the number of resources
+ it guards to \a n (by default, 0).
+
+ \sa release(), available()
+*/
+QSemaphore::QSemaphore(int n)
+{
+ Q_ASSERT_X(n >= 0, "QSemaphore", "parameter 'n' must be non-negative");
+ d = new QSemaphorePrivate(n);
+}
+
+/*!
+ Destroys the semaphore.
+
+ \warning Destroying a semaphore that is in use may result in
+ undefined behavior.
+*/
+QSemaphore::~QSemaphore()
+{ delete d; }
+
+/*!
+ Tries to acquire \c n resources guarded by the semaphore. If \a n
+ > available(), this call will block until enough resources are
+ available.
+
+ \sa release(), available(), tryAcquire()
+*/
+void QSemaphore::acquire(int n)
+{
+ Q_ASSERT_X(n >= 0, "QSemaphore::acquire", "parameter 'n' must be non-negative");
+ QMutexLocker locker(&d->mutex);
+ while (n > d->avail)
+ d->cond.wait(locker.mutex());
+ d->avail -= n;
+}
+
+/*!
+ Releases \a n resources guarded by the semaphore.
+
+ This function can be used to "create" resources as well. For
+ example:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qsemaphore.cpp 1
+
+ \sa acquire(), available()
+*/
+void QSemaphore::release(int n)
+{
+ Q_ASSERT_X(n >= 0, "QSemaphore::release", "parameter 'n' must be non-negative");
+ QMutexLocker locker(&d->mutex);
+ d->avail += n;
+ d->cond.wakeAll();
+}
+
+/*!
+ Returns the number of resources currently available to the
+ semaphore. This number can never be negative.
+
+ \sa acquire(), release()
+*/
+int QSemaphore::available() const
+{
+ QMutexLocker locker(&d->mutex);
+ return d->avail;
+}
+
+/*!
+ Tries to acquire \c n resources guarded by the semaphore and
+ returns true on success. If available() < \a n, this call
+ immediately returns false without acquiring any resources.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qsemaphore.cpp 2
+
+ \sa acquire()
+*/
+bool QSemaphore::tryAcquire(int n)
+{
+ Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative");
+ QMutexLocker locker(&d->mutex);
+ if (n > d->avail)
+ return false;
+ d->avail -= n;
+ return true;
+}
+
+/*!
+ Tries to acquire \c n resources guarded by the semaphore and
+ returns true on success. If available() < \a n, this call will
+ wait for at most \a timeout milliseconds for resources to become
+ available.
+
+ Note: Passing a negative number as the \a timeout is equivalent to
+ calling acquire(), i.e. this function will wait forever for
+ resources to become available if \a timeout is negative.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qsemaphore.cpp 3
+
+ \sa acquire()
+*/
+bool QSemaphore::tryAcquire(int n, int timeout)
+{
+ Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative");
+ QMutexLocker locker(&d->mutex);
+ if (timeout < 0) {
+ while (n > d->avail)
+ d->cond.wait(locker.mutex());
+ } else {
+ while (n > d->avail) {
+ if (!d->cond.wait(locker.mutex(), timeout))
+ return false;
+ }
+ }
+ d->avail -= n;
+ return true;
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_THREAD
diff --git a/src/corelib/thread/qsemaphore.h b/src/corelib/thread/qsemaphore.h
new file mode 100644
index 0000000..6bc1920
--- /dev/null
+++ b/src/corelib/thread/qsemaphore.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSEMAPHORE_H
+#define QSEMAPHORE_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_THREAD
+
+class QSemaphorePrivate;
+
+class Q_CORE_EXPORT QSemaphore
+{
+public:
+ explicit QSemaphore(int n = 0);
+ ~QSemaphore();
+
+ void acquire(int n = 1);
+ bool tryAcquire(int n = 1);
+ bool tryAcquire(int n, int timeout);
+
+ void release(int n = 1);
+
+ int available() const;
+
+private:
+ Q_DISABLE_COPY(QSemaphore)
+
+ QSemaphorePrivate *d;
+};
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSEMAPHORE_H
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp
new file mode 100644
index 0000000..7f87897
--- /dev/null
+++ b/src/corelib/thread/qthread.cpp
@@ -0,0 +1,730 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qthread.h"
+#include "qthreadstorage.h"
+#include "qmutex.h"
+#include "qmutexpool_p.h"
+#include "qreadwritelock.h"
+#include "qabstracteventdispatcher.h"
+
+#include <qeventloop.h>
+#include <qhash.h>
+
+#include "qthread_p.h"
+#include "private/qcoreapplication_p.h"
+
+/*
+#ifdef Q_OS_WIN32
+# include "qt_windows.h"
+#else
+# include <unistd.h>
+# include <netinet/in.h>
+# include <sys/utsname.h>
+# include <sys/socket.h>
+*/
+/*
+# elif defined(Q_OS_HPUX)
+# include <sys/pstat.h>
+# elif defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_MAC)
+# include <sys/sysctl.h>
+# endif
+#endif
+*/
+
+QT_BEGIN_NAMESPACE
+
+/*
+ QThreadData
+*/
+
+QThreadData::QThreadData(int initialRefCount)
+ : _ref(initialRefCount), thread(0),
+ quitNow(false), loopLevel(0), eventDispatcher(0), canWait(true)
+{
+ // fprintf(stderr, "QThreadData %p created\n", this);
+}
+
+QThreadData::~QThreadData()
+{
+ Q_ASSERT(_ref == 0);
+
+ // In the odd case that Qt is running on a secondary thread, the main
+ // thread instance will have been dereffed asunder because of the deref in
+ // QThreadData::current() and the deref in the pthread_destroy. To avoid
+ // crashing during QCoreApplicationData's global static cleanup we need to
+ // safeguard the main thread here.. This fix is a bit crude, but it solves
+ // the problem...
+ if (this->thread == QCoreApplicationPrivate::theMainThread) {
+ QCoreApplicationPrivate::theMainThread = 0;
+ }
+
+ QThread *t = thread;
+ thread = 0;
+ delete t;
+
+ for (int i = 0; i < postEventList.size(); ++i) {
+ const QPostEvent &pe = postEventList.at(i);
+ if (pe.event) {
+ --pe.receiver->d_func()->postedEvents;
+ pe.event->posted = false;
+ delete pe.event;
+ }
+ }
+
+ // fprintf(stderr, "QThreadData %p destroyed\n", this);
+}
+
+QThreadData *QThreadData::get2(QThread *thread)
+{
+ Q_ASSERT_X(thread != 0, "QThread", "internal error");
+ return thread->d_func()->data;
+}
+
+void QThreadData::ref()
+{
+#ifndef QT_NO_THREAD
+ (void) _ref.ref();
+ Q_ASSERT(_ref != 0);
+#endif
+}
+
+void QThreadData::deref()
+{
+#ifndef QT_NO_THREAD
+ if (!_ref.deref())
+ delete this;
+#endif
+}
+
+
+#ifndef QT_NO_THREAD
+/*
+ QThreadPrivate
+*/
+
+QThreadPrivate::QThreadPrivate(QThreadData *d)
+ : QObjectPrivate(), running(false), finished(false), terminated(false),
+ stackSize(0), priority(QThread::InheritPriority), data(d)
+{
+#if defined (Q_OS_UNIX)
+ thread_id = 0;
+#elif defined (Q_WS_WIN)
+ handle = 0;
+ id = 0;
+ waiters = 0;
+ terminationEnabled = true;
+ terminatePending = false;
+#endif
+
+ if (!data)
+ data = new QThreadData;
+}
+
+QThreadPrivate::~QThreadPrivate()
+{
+ data->deref();
+}
+
+/*
+ QAdoptedThread
+*/
+
+QAdoptedThread::QAdoptedThread(QThreadData *data)
+ : QThread(*new QThreadPrivate(data))
+{
+ // thread should be running and not finished for the lifetime
+ // of the application (even if QCoreApplication goes away)
+ d_func()->running = true;
+ d_func()->finished = false;
+ init();
+
+ // fprintf(stderr, "new QAdoptedThread = %p\n", this);
+}
+
+QAdoptedThread::~QAdoptedThread()
+{
+ QThreadPrivate::finish(this);
+
+ // fprintf(stderr, "~QAdoptedThread = %p\n", this);
+}
+
+QThread *QAdoptedThread::createThreadForAdoption()
+{
+ QThread *t = new QAdoptedThread(0);
+ t->moveToThread(t);
+ return t;
+}
+
+void QAdoptedThread::run()
+{
+ // this function should never be called
+ qFatal("QAdoptedThread::run(): Internal error, this implementation should never be called.");
+}
+
+/*!
+ \class QThread
+ \brief The QThread class provides platform-independent threads.
+
+ \ingroup thread
+ \ingroup environment
+ \mainclass
+
+ A QThread represents a separate thread of control within the
+ program; it shares data with all the other threads within the
+ process but executes independently in the way that a separate
+ program does on a multitasking operating system. Instead of
+ starting in \c main(), QThreads begin executing in run(). By
+ default, run() starts the event loop by calling exec() (see
+ below). To create your own threads, subclass QThread and
+ reimplement run(). For example:
+
+ \snippet doc/src/snippets/code/src_corelib_thread_qthread.cpp 0
+
+ This will create a QTcpSocket in the thread and then execute the
+ thread's event loop. Use the start() method to begin execution.
+ Execution ends when you return from run(), just as an application
+ does when it leaves main(). QThread will notifiy you via a signal
+ when the thread is started(), finished(), and terminated(), or
+ you can use isFinished() and isRunning() to query the state of
+ the thread. Use wait() to block until the thread has finished
+ execution.
+
+ Each thread gets its own stack from the operating system. The
+ operating system also determines the default size of the stack.
+ You can use setStackSize() to set a custom stack size.
+
+ Each QThread can have its own event loop. You can start the event
+ loop by calling exec(); you can stop it by calling exit() or
+ quit(). Having an event loop in a thread makes it possible to
+ connect signals from other threads to slots in this thread, using
+ a mechanism called \l{Qt::QueuedConnection}{queued
+ connections}. It also makes it possible to use classes that
+ require the event loop, such as QTimer and QTcpSocket, in the
+ thread. Note, however, that it is not possible to use any widget
+ classes in the thread.
+
+ In extreme cases, you may want to forcibly terminate() an
+ executing thread. However, doing so is dangerous and discouraged.
+ Please read the documentation for terminate() and
+ setTerminationEnabled() for detailed information.
+
+ The static functions currentThreadId() and currentThread() return
+ identifiers for the currently executing thread. The former
+ returns a platform specific ID for the thread; the latter returns
+ a QThread pointer.
+
+ QThread also provides platform independent sleep functions in
+ varying resolutions. Use sleep() for full second resolution,
+ msleep() for millisecond resolution, and usleep() for microsecond
+ resolution.
+
+ \sa {Thread Support in Qt}, QThreadStorage, QMutex, QSemaphore, QWaitCondition,
+ {Mandelbrot Example}, {Semaphores Example}, {Wait Conditions Example}
+*/
+
+/*!
+ \fn Qt::HANDLE QThread::currentThreadId()
+
+ Returns the thread handle of the currently executing thread.
+
+ \warning The handle returned by this function is used for internal
+ purposes and should not be used in any application code. On
+ Windows, the returned value is a pseudo-handle for the current
+ thread that cannot be used for numerical comparison.
+*/
+
+/*!
+ \fn int QThread::idealThreadCount()
+
+ Returns the ideal number of threads that can be run on the system. This is done querying
+ the number of processor cores, both real and logical, in the system. This function returns -1
+ if the number of processor cores could not be detected.
+*/
+
+/*!
+ \fn void QThread::yieldCurrentThread()
+
+ Yields execution of the current thread to another runnable thread,
+ if any. Note that the operating system decides to which thread to
+ switch.
+*/
+
+/*!
+ \fn void QThread::start(Priority priority)
+
+ Begins execution of the thread by calling run(), which should be
+ reimplemented in a QThread subclass to contain your code. The
+ operating system will schedule the thread according to the \a
+ priority parameter. If the thread is already running, this
+ function does nothing.
+
+ \sa run(), terminate()
+*/
+
+/*!
+ \fn void QThread::started()
+
+ This signal is emitted when the thread starts executing.
+
+ \sa finished(), terminated()
+*/
+
+/*!
+ \fn void QThread::finished()
+
+ This signal is emitted when the thread has finished executing.
+
+ \sa started(), terminated()
+*/
+
+/*!
+ \fn void QThread::terminated()
+
+ This signal is emitted when the thread is terminated.
+
+ \sa started(), finished()
+*/
+
+/*!
+ \enum QThread::Priority
+
+ This enum type indicates how the operating system should schedule
+ newly created threads.
+
+ \value IdlePriority scheduled only when no other threads are
+ running.
+
+ \value LowestPriority scheduled less often than LowPriority.
+ \value LowPriority scheduled less often than NormalPriority.
+
+ \value NormalPriority the default priority of the operating
+ system.
+
+ \value HighPriority scheduled more often than NormalPriority.
+ \value HighestPriority scheduled more often than HighPriority.
+
+ \value TimeCriticalPriority scheduled as often as possible.
+
+ \value InheritPriority use the same priority as the creating
+ thread. This is the default.
+*/
+
+/*!
+ Returns a pointer to a QThread which represents the currently
+ executing thread.
+*/
+QThread *QThread::currentThread()
+{
+ QThreadData *data = QThreadData::current();
+ Q_ASSERT(data != 0);
+ return data->thread;
+}
+
+/*!
+ Constructs a new thread with the given \a parent. The thread does
+ not begin executing until start() is called.
+
+ \sa start()
+*/
+QThread::QThread(QObject *parent)
+ : QObject(*(new QThreadPrivate), parent)
+{
+ Q_D(QThread);
+ // fprintf(stderr, "QThreadData %p created for thread %p\n", d->data, this);
+ d->data->thread = this;
+}
+
+/*! \internal
+ */
+QThread::QThread(QThreadPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+ Q_D(QThread);
+ // fprintf(stderr, "QThreadData %p taken from private data for thread %p\n", d->data, this);
+ d->data->thread = this;
+}
+
+/*!
+ Destroys the thread.
+
+ Note that deleting a QThread object will not stop the execution
+ of the thread it represents. Deleting a running QThread (i.e.
+ isFinished() returns false) will probably result in a program
+ crash. You can wait() on a thread to make sure that it has
+ finished.
+*/
+QThread::~QThread()
+{
+ Q_D(QThread);
+ {
+ QMutexLocker locker(&d->mutex);
+ if (d->running && !d->finished)
+ qWarning("QThread: Destroyed while thread is still running");
+
+ d->data->thread = 0;
+ }
+}
+
+/*!
+ Returns true if the thread is finished; otherwise returns false.
+
+ \sa isRunning()
+*/
+bool QThread::isFinished() const
+{
+ Q_D(const QThread);
+ QMutexLocker locker(&d->mutex);
+ return d->finished;
+}
+
+/*!
+ Returns true if the thread is running; otherwise returns false.
+
+ \sa isFinished()
+*/
+bool QThread::isRunning() const
+{
+ Q_D(const QThread);
+ QMutexLocker locker(&d->mutex);
+ return d->running;
+}
+
+/*!
+ Sets the maximum stack size for the thread to \a stackSize. If \a
+ stackSize is greater than zero, the maximum stack size is set to
+ \a stackSize bytes, otherwise the maximum stack size is
+ automatically determined by the operating system.
+
+ \warning Most operating systems place minimum and maximum limits
+ on thread stack sizes. The thread will fail to start if the stack
+ size is outside these limits.
+
+ \sa stackSize()
+*/
+void QThread::setStackSize(uint stackSize)
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+ Q_ASSERT_X(!d->running, "QThread::setStackSize",
+ "cannot change stack size while the thread is running");
+ d->stackSize = stackSize;
+}
+
+/*!
+ Returns the maximum stack size for the thread (if set with
+ setStackSize()); otherwise returns zero.
+
+ \sa setStackSize()
+*/
+uint QThread::stackSize() const
+{
+ Q_D(const QThread);
+ QMutexLocker locker(&d->mutex);
+ return d->stackSize;
+}
+
+/*!
+ Enters the event loop and waits until exit() is called, returning the value
+ that was passed to exit(). The value returned is 0 if exit() is called via
+ quit().
+
+ It is necessary to call this function to start event handling.
+
+ \sa quit(), exit()
+*/
+int QThread::exec()
+{
+ Q_D(QThread);
+ d->mutex.lock();
+ d->data->quitNow = false;
+ QEventLoop eventLoop;
+ d->mutex.unlock();
+ int returnCode = eventLoop.exec();
+ return returnCode;
+}
+
+/*!
+ Tells the thread's event loop to exit with a return code.
+
+ After calling this function, the thread leaves the event loop and
+ returns from the call to QEventLoop::exec(). The
+ QEventLoop::exec() function returns \a returnCode.
+
+ By convention, a \a returnCode of 0 means success, any non-zero value
+ indicates an error.
+
+ Note that unlike the C library function of the same name, this
+ function \e does return to the caller -- it is event processing
+ that stops.
+
+ This function does nothing if the thread does not have an event
+ loop.
+
+ \sa quit() QEventLoop
+*/
+void QThread::exit(int returnCode)
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+ d->data->quitNow = true;
+ for (int i = 0; i < d->data->eventLoops.size(); ++i) {
+ QEventLoop *eventLoop = d->data->eventLoops.at(i);
+ eventLoop->exit(returnCode);
+ }
+}
+
+/*!
+ Tells the thread's event loop to exit with return code 0 (success).
+ Equivalent to calling QThread::exit(0).
+
+ This function does nothing if the thread does not have an event
+ loop.
+
+ \sa exit() QEventLoop
+*/
+void QThread::quit()
+{ exit(); }
+
+/*!
+ The starting point for the thread. After calling start(), the
+ newly created thread calls this function. The default
+ implementation simply calls exec().
+
+ You can reimplemented this function to do other useful
+ work. Returning from this method will end the execution of the
+ thread.
+
+ \sa start() wait()
+*/
+void QThread::run()
+{
+ (void) exec();
+}
+
+/*! \internal
+ Initializes the QThread system.
+*/
+#if defined (Q_OS_WIN)
+void qt_create_tls();
+#endif
+
+void QThread::initialize()
+{
+ if (qt_global_mutexpool)
+ return;
+ qt_global_mutexpool = QMutexPool::instance();
+
+#if defined (Q_OS_WIN)
+ qt_create_tls();
+#endif
+}
+
+
+/*! \internal
+ Cleans up the QThread system.
+*/
+void QThread::cleanup()
+{
+ qt_global_mutexpool = 0;
+}
+
+/*!
+ \fn bool QThread::finished() const
+
+ Use isFinished() instead.
+*/
+
+/*!
+ \fn bool QThread::running() const
+
+ Use isRunning() instead.
+*/
+
+/*! \fn void QThread::setPriority(Priority priority)
+ \since 4.1
+
+ This function sets the \a priority for a running thread. If the
+ thread is not running, this function does nothing and returns
+ immediately. Use start() to start a thread with a specific
+ priority.
+
+ The \a priority argument can be any value in the \c
+ QThread::Priority enum except for \c InheritPriorty.
+
+ \sa Priority priority() start()
+*/
+
+/*!
+ \since 4.1
+
+ Returns the priority for a running thread. If the thread is not
+ running, this function returns \c InheritPriority.
+
+ \sa Priority setPriority() start()
+*/
+QThread::Priority QThread::priority() const
+{
+ Q_D(const QThread);
+ QMutexLocker locker(&d->mutex);
+ return d->priority;
+}
+
+/*!
+ \fn void QThread::sleep(unsigned long secs)
+
+ Forces the current thread to sleep for \a secs seconds.
+
+ \sa msleep(), usleep()
+*/
+
+/*!
+ \fn void QThread::msleep(unsigned long msecs)
+
+ Causes the current thread to sleep for \a msecs milliseconds.
+
+ \sa sleep(), usleep()
+*/
+
+/*!
+ \fn void QThread::usleep(unsigned long usecs)
+
+ Causes the current thread to sleep for \a usecs microseconds.
+
+ \sa sleep(), msleep()
+*/
+
+/*!
+ \fn void QThread::terminate()
+
+ Terminates the execution of the thread. The thread may or may not
+ be terminated immediately, depending on the operating systems
+ scheduling policies. Use QThread::wait() after terminate() for
+ synchronous termination.
+
+ When the thread is terminated, all threads waiting for the thread
+ to finish will be woken up.
+
+ \warning This function is dangerous and its use is discouraged.
+ The thread can be terminate at any point in its code path.
+ Threads can be terminated while modifying data. There is no
+ chance for the thread to cleanup after itself, unlock any held
+ mutexes, etc. In short, use this function only if absolutely
+ necessary.
+
+ Termination can be explicitly enabled or disabled by calling
+ QThread::setTerminationEnabled(). Calling this function while
+ termination is disabled results in the termination being
+ deferred, until termination is re-enabled. See the documentation
+ of QThread::setTerminationEnabled() for more information.
+
+ \sa setTerminationEnabled()
+*/
+
+/*!
+ \fn bool QThread::wait(unsigned long time)
+
+ Blocks the thread until either of these conditions is met:
+
+ \list
+ \o The thread associated with this QThread object has finished
+ execution (i.e. when it returns from \l{run()}). This function
+ will return true if the thread has finished. It also returns
+ true if the thread has not been started yet.
+ \o \a time milliseconds has elapsed. If \a time is ULONG_MAX (the
+ default), then the wait will never timeout (the thread must
+ return from \l{run()}). This function will return false if the
+ wait timed out.
+ \endlist
+
+ This provides similar functionality to the POSIX \c
+ pthread_join() function.
+
+ \sa sleep(), terminate()
+*/
+
+/*!
+ \fn void QThread::setTerminationEnabled(bool enabled)
+
+ Enables or disables termination of the current thread based on the
+ \a enabled parameter. The thread must have been started by
+ QThread.
+
+ When \a enabled is false, termination is disabled. Future calls
+ to QThread::terminate() will return immediately without effect.
+ Instead, the termination is deferred until termination is enabled.
+
+ When \a enabled is true, termination is enabled. Future calls to
+ QThread::terminate() will terminate the thread normally. If
+ termination has been deferred (i.e. QThread::terminate() was
+ called with termination disabled), this function will terminate
+ the calling thread \e immediately. Note that this function will
+ not return in this case.
+
+ \sa terminate()
+*/
+
+#else // QT_NO_THREAD
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <private/qcoreapplication_p.h>
+QT_END_INCLUDE_NAMESPACE
+
+Q_GLOBAL_STATIC_WITH_ARGS(QThreadData, staticThreadData, (0));
+QThread* QThread::instance = 0;
+
+QThread::QThread() : QObject(*new QThreadPrivate, (QObject*)0)
+{
+ Q_D(QThread);
+ d->data->thread = this;
+ QCoreApplicationPrivate::theMainThread = this;
+}
+
+QThreadData* QThreadData::current()
+{
+ if (QThread::instance)
+ return QThread::instance->d_func()->data;
+ return staticThreadData();
+}
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h
new file mode 100644
index 0000000..d9a6240
--- /dev/null
+++ b/src/corelib/thread/qthread.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTHREAD_H
+#define QTHREAD_H
+
+#include <QtCore/qobject.h>
+
+#include <limits.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QThreadData;
+class QThreadPrivate;
+
+#ifndef QT_NO_THREAD
+class Q_CORE_EXPORT QThread : public QObject
+{
+public:
+ static Qt::HANDLE currentThreadId();
+ static QThread *currentThread();
+ static int idealThreadCount();
+ static void yieldCurrentThread();
+
+ explicit QThread(QObject *parent = 0);
+ ~QThread();
+
+ enum Priority {
+ IdlePriority,
+
+ LowestPriority,
+ LowPriority,
+ NormalPriority,
+ HighPriority,
+ HighestPriority,
+
+ TimeCriticalPriority,
+
+ InheritPriority
+ };
+
+ void setPriority(Priority priority);
+ Priority priority() const;
+
+ bool isFinished() const;
+ bool isRunning() const;
+
+ void setStackSize(uint stackSize);
+ uint stackSize() const;
+
+ void exit(int retcode = 0);
+
+public Q_SLOTS:
+ void start(Priority = InheritPriority);
+ void terminate();
+ void quit();
+
+public:
+ // default argument causes thread to block indefinately
+ bool wait(unsigned long time = ULONG_MAX);
+
+Q_SIGNALS:
+ void started();
+ void finished();
+ void terminated();
+
+protected:
+ virtual void run();
+ int exec();
+
+ static void setTerminationEnabled(bool enabled = true);
+
+ static void sleep(unsigned long);
+ static void msleep(unsigned long);
+ static void usleep(unsigned long);
+
+#ifdef QT3_SUPPORT
+public:
+ inline QT3_SUPPORT bool finished() const { return isFinished(); }
+ inline QT3_SUPPORT bool running() const { return isRunning(); }
+#endif
+
+protected:
+ QThread(QThreadPrivate &dd, QObject *parent = 0);
+
+private:
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QThread)
+
+ static void initialize();
+ static void cleanup();
+
+ friend class QCoreApplication;
+ friend class QThreadData;
+};
+
+#else // QT_NO_THREAD
+
+class Q_CORE_EXPORT QThread : public QObject
+{
+public:
+ static Qt::HANDLE currentThreadId() { return Qt::HANDLE(currentThread()); }
+ static QThread* currentThread()
+ { if (!instance) instance = new QThread(); return instance; }
+
+private:
+ QThread();
+ static QThread *instance;
+
+ friend class QCoreApplication;
+ friend class QThreadData;
+ Q_DECLARE_PRIVATE(QThread)
+};
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTHREAD_H
diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h
new file mode 100644
index 0000000..75293ce
--- /dev/null
+++ b/src/corelib/thread/qthread_p.h
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTHREAD_P_H
+#define QTHREAD_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include "qplatformdefs.h"
+#include "QtCore/qthread.h"
+#include "QtCore/qmutex.h"
+#include "QtCore/qstack.h"
+#include "QtCore/qwaitcondition.h"
+#include "QtCore/qmap.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractEventDispatcher;
+class QEventLoop;
+
+class QPostEvent
+{
+public:
+ QObject *receiver;
+ QEvent *event;
+ int priority;
+ inline QPostEvent()
+ : receiver(0), event(0), priority(0)
+ { }
+ inline QPostEvent(QObject *r, QEvent *e, int p)
+ : receiver(r), event(e), priority(p)
+ { }
+};
+inline bool operator<(int priority, const QPostEvent &pe)
+{
+ return pe.priority < priority;
+}
+inline bool operator<(const QPostEvent &pe, int priority)
+{
+ return priority < pe.priority;
+}
+
+class QPostEventList : public QList<QPostEvent>
+{
+public:
+ // recursion == recursion count for sendPostedEvents()
+ int recursion;
+
+ // sendOffset == the current event to start sending
+ int startOffset;
+ // insertionOffset == set by sendPostedEvents to tell postEvent() where to start insertions
+ int insertionOffset;
+
+ QMutex mutex;
+
+ inline QPostEventList()
+ : QList<QPostEvent>(), recursion(0), startOffset(0), insertionOffset(0)
+ { }
+};
+
+class Q_CORE_EXPORT QThreadData
+{
+ QAtomicInt _ref;
+
+public:
+ QThreadData(int initialRefCount = 1);
+ ~QThreadData();
+
+ static QThreadData *current();
+ static QThreadData *get2(QThread *thread);
+
+ void ref();
+ void deref();
+
+ QThread *thread;
+ bool quitNow;
+ int loopLevel;
+ QAbstractEventDispatcher *eventDispatcher;
+ QStack<QEventLoop *> eventLoops;
+ QPostEventList postEventList;
+ bool canWait;
+ QMap<int, void *> tls;
+
+ QMutex mutex;
+};
+
+#ifndef QT_NO_THREAD
+class QThreadPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QThread)
+
+public:
+ QThreadPrivate(QThreadData *d = 0);
+ ~QThreadPrivate();
+
+ mutable QMutex mutex;
+
+ bool running;
+ bool finished;
+ bool terminated;
+
+ uint stackSize;
+ QThread::Priority priority;
+
+ static QThread *threadForId(int id);
+
+#ifdef Q_OS_UNIX
+ pthread_t thread_id;
+ QWaitCondition thread_done;
+
+ static void *start(void *arg);
+ static void finish(void *arg);
+#endif
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ HANDLE handle;
+ unsigned int id;
+ int waiters;
+ bool terminationEnabled, terminatePending;
+
+ static unsigned int __stdcall start(void *);
+ static void finish(void *, bool lockAnyway=true);
+#endif // Q_OS_WIN32
+
+ QThreadData *data;
+
+ static void createEventDispatcher(QThreadData *data);
+};
+
+// thread wrapper for the main() thread
+class QAdoptedThread : public QThread
+{
+ Q_DECLARE_PRIVATE(QThread)
+
+public:
+ QAdoptedThread(QThreadData *data = 0);
+ ~QAdoptedThread();
+ void init();
+
+ static QThread *createThreadForAdoption();
+private:
+ void run();
+};
+
+#else // QT_NO_THREAD
+
+class QThreadPrivate : public QObjectPrivate
+{
+public:
+ QThreadPrivate() : data(QThreadData::current()) {}
+ ~QThreadPrivate() { }
+
+ QThreadData *data;
+
+ static void setCurrentThread(QThread*) {}
+ static QThread *threadForId(int) { return QThread::currentThread(); }
+ static void createEventDispatcher(QThreadData *data);
+
+ Q_DECLARE_PUBLIC(QThread)
+};
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
+
+#endif // QTHREAD_P_H
diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp
new file mode 100644
index 0000000..f602821
--- /dev/null
+++ b/src/corelib/thread/qthread_unix.cpp
@@ -0,0 +1,562 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qthread.h"
+
+#include "qplatformdefs.h"
+
+#include <private/qcoreapplication_p.h>
+#if !defined(QT_NO_GLIB)
+# include "../kernel/qeventdispatcher_glib_p.h"
+#endif
+#include <private/qeventdispatcher_unix_p.h>
+
+#include "qthreadstorage.h"
+
+#include "qthread_p.h"
+
+#include "qdebug.h"
+
+#include <sched.h>
+#include <errno.h>
+
+#ifdef Q_OS_BSD4
+#include <sys/sysctl.h>
+#endif
+
+#if defined(Q_OS_MAC)
+# ifdef qDebug
+# define old_qDebug qDebug
+# undef qDebug
+# endif
+# include <CoreServices/CoreServices.h>
+
+# ifdef old_qDebug
+# undef qDebug
+# define qDebug QT_NO_QDEBUG_MACRO
+# undef old_qDebug
+# endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_THREAD
+
+static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT;
+static pthread_key_t current_thread_data_key;
+
+static void destroy_current_thread_data(void *p)
+{
+ // POSIX says the value in our key is set to zero before calling
+ // this destructor function, so we need to set it back to the
+ // right value...
+ pthread_setspecific(current_thread_data_key, p);
+ reinterpret_cast<QThreadData *>(p)->deref();
+ // ... but we must reset it to zero before returning so we aren't
+ // called again (POSIX allows implementations to call destructor
+ // functions repeatedly until all values are zero)
+ pthread_setspecific(current_thread_data_key, 0);
+}
+
+static void create_current_thread_data_key()
+{
+ pthread_key_create(&current_thread_data_key, destroy_current_thread_data);
+}
+
+QThreadData *QThreadData::current()
+{
+ pthread_once(&current_thread_data_once, create_current_thread_data_key);
+
+ QThreadData *data = reinterpret_cast<QThreadData *>(pthread_getspecific(current_thread_data_key));
+ if (!data) {
+ void *a;
+ if (QInternal::activateCallbacks(QInternal::AdoptCurrentThread, &a)) {
+ QThread *adopted = static_cast<QThread*>(a);
+ Q_ASSERT(adopted);
+ data = QThreadData::get2(adopted);
+ pthread_setspecific(current_thread_data_key, data);
+ adopted->d_func()->running = true;
+ adopted->d_func()->finished = false;
+ static_cast<QAdoptedThread *>(adopted)->init();
+ } else {
+ data = new QThreadData;
+ pthread_setspecific(current_thread_data_key, data);
+ data->thread = new QAdoptedThread(data);
+ data->deref();
+ }
+ if (!QCoreApplicationPrivate::theMainThread)
+ QCoreApplicationPrivate::theMainThread = data->thread;
+ }
+ return data;
+}
+
+
+void QAdoptedThread::init()
+{
+ d_func()->thread_id = pthread_self();
+}
+
+/*
+ QThreadPrivate
+*/
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+typedef void*(*QtThreadCallback)(void*);
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+#endif // QT_NO_THREAD
+
+void QThreadPrivate::createEventDispatcher(QThreadData *data)
+{
+#if !defined(QT_NO_GLIB)
+ if (qgetenv("QT_NO_GLIB").isEmpty()
+ && qgetenv("QT_NO_THREADED_GLIB").isEmpty()
+ && QEventDispatcherGlib::versionSupported())
+ data->eventDispatcher = new QEventDispatcherGlib;
+ else
+#endif
+ data->eventDispatcher = new QEventDispatcherUNIX;
+ data->eventDispatcher->startingUp();
+}
+
+#ifndef QT_NO_THREAD
+
+void *QThreadPrivate::start(void *arg)
+{
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ pthread_cleanup_push(QThreadPrivate::finish, arg);
+
+ QThread *thr = reinterpret_cast<QThread *>(arg);
+ QThreadData *data = QThreadData::get2(thr);
+
+ pthread_once(&current_thread_data_once, create_current_thread_data_key);
+ pthread_setspecific(current_thread_data_key, data);
+
+ data->ref();
+ data->quitNow = false;
+
+ // ### TODO: allow the user to create a custom event dispatcher
+ if (QCoreApplication::instance())
+ createEventDispatcher(data);
+
+ emit thr->started();
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+ thr->run();
+
+ pthread_cleanup_pop(1);
+ return 0;
+}
+
+void QThreadPrivate::finish(void *arg)
+{
+ QThread *thr = reinterpret_cast<QThread *>(arg);
+ QThreadPrivate *d = thr->d_func();
+ QMutexLocker locker(&d->mutex);
+
+ d->priority = QThread::InheritPriority;
+ d->running = false;
+ d->finished = true;
+ if (d->terminated)
+ emit thr->terminated();
+ d->terminated = false;
+ emit thr->finished();
+
+ if (d->data->eventDispatcher) {
+ d->data->eventDispatcher->closingDown();
+ QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher;
+ d->data->eventDispatcher = 0;
+ delete eventDispatcher;
+ }
+
+ void *data = &d->data->tls;
+ QThreadStorageData::finish((void **)data);
+
+ d->thread_id = 0;
+ d->thread_done.wakeAll();
+}
+
+
+
+
+/**************************************************************************
+ ** QThread
+ *************************************************************************/
+
+Qt::HANDLE QThread::currentThreadId()
+{
+ // requires a C cast here otherwise we run into trouble on AIX
+ return (Qt::HANDLE)pthread_self();
+}
+
+#if defined(QT_LINUXBASE) && !defined(_SC_NPROCESSORS_ONLN)
+// LSB doesn't define _SC_NPROCESSORS_ONLN.
+# define _SC_NPROCESSORS_ONLN 84
+#endif
+
+int QThread::idealThreadCount()
+{
+ int cores = -1;
+
+#if defined(Q_OS_MAC)
+ // Mac OS X
+ cores = MPProcessorsScheduled();
+#elif defined(Q_OS_HPUX)
+ // HP-UX
+ struct pst_dynamic psd;
+ if (pstat_getdynamic(&psd, sizeof(psd), 1, 0) == -1) {
+ perror("pstat_getdynamic");
+ cores = -1;
+ } else {
+ cores = (int)psd.psd_proc_cnt;
+ }
+#elif defined(Q_OS_BSD4)
+ // FreeBSD, OpenBSD, NetBSD, BSD/OS
+ size_t len = sizeof(cores);
+ int mib[2];
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ if (sysctl(mib, 2, &cores, &len, NULL, 0) != 0) {
+ perror("sysctl");
+ cores = -1;
+ }
+#elif defined(Q_OS_IRIX)
+ // IRIX
+ cores = (int)sysconf(_SC_NPROC_ONLN);
+#elif defined(Q_OS_INTEGRITY)
+ // ### TODO - how to get the amound of CPUs on INTEGRITY?
+#else
+ // the rest: Linux, Solaris, AIX, Tru64
+ cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+
+ return cores;
+}
+
+void QThread::yieldCurrentThread()
+{
+ sched_yield();
+}
+
+/* \internal
+ helper function to do thread sleeps, since usleep()/nanosleep()
+ aren't reliable enough (in terms of behavior and availability)
+*/
+static void thread_sleep(struct timespec *ti)
+{
+ pthread_mutex_t mtx;
+ pthread_cond_t cnd;
+
+ pthread_mutex_init(&mtx, 0);
+ pthread_cond_init(&cnd, 0);
+
+ pthread_mutex_lock(&mtx);
+ (void) pthread_cond_timedwait(&cnd, &mtx, ti);
+ pthread_mutex_unlock(&mtx);
+
+ pthread_cond_destroy(&cnd);
+ pthread_mutex_destroy(&mtx);
+}
+
+void QThread::sleep(unsigned long secs)
+{
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ struct timespec ti;
+ ti.tv_sec = tv.tv_sec + secs;
+ ti.tv_nsec = (tv.tv_usec * 1000);
+ thread_sleep(&ti);
+}
+
+void QThread::msleep(unsigned long msecs)
+{
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ struct timespec ti;
+
+ ti.tv_nsec = (tv.tv_usec + (msecs % 1000) * 1000) * 1000;
+ ti.tv_sec = tv.tv_sec + (msecs / 1000) + (ti.tv_nsec / 1000000000);
+ ti.tv_nsec %= 1000000000;
+ thread_sleep(&ti);
+}
+
+void QThread::usleep(unsigned long usecs)
+{
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ struct timespec ti;
+
+ ti.tv_nsec = (tv.tv_usec + (usecs % 1000000)) * 1000;
+ ti.tv_sec = tv.tv_sec + (usecs / 1000000) + (ti.tv_nsec / 1000000000);
+ ti.tv_nsec %= 1000000000;
+ thread_sleep(&ti);
+}
+
+void QThread::start(Priority priority)
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+ if (d->running)
+ return;
+
+ d->running = true;
+ d->finished = false;
+ d->terminated = false;
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ d->priority = priority;
+
+#if defined(Q_OS_DARWIN) || !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0)
+ switch (priority) {
+ case InheritPriority:
+ {
+ pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+ break;
+ }
+
+ default:
+ {
+ int sched_policy;
+ if (pthread_attr_getschedpolicy(&attr, &sched_policy) != 0) {
+ // failed to get the scheduling policy, don't bother
+ // setting the priority
+ qWarning("QThread::start: Cannot determine default scheduler policy");
+ break;
+ }
+
+ int prio_min = sched_get_priority_min(sched_policy);
+ int prio_max = sched_get_priority_max(sched_policy);
+ if (prio_min == -1 || prio_max == -1) {
+ // failed to get the scheduling parameters, don't
+ // bother setting the priority
+ qWarning("QThread::start: Cannot determine scheduler priority range");
+ break;
+ }
+
+ int prio;
+ switch (priority) {
+ case IdlePriority:
+ prio = prio_min;
+ break;
+
+ case TimeCriticalPriority:
+ prio = prio_max;
+ break;
+
+ default:
+ // crudely scale our priority enum values to the prio_min/prio_max
+ prio = (priority * (prio_max - prio_min) / TimeCriticalPriority) + prio_min;
+ prio = qMax(prio_min, qMin(prio_max, prio));
+ break;
+ }
+
+ sched_param sp;
+ sp.sched_priority = prio;
+
+ if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0
+ || pthread_attr_setschedpolicy(&attr, sched_policy) != 0
+ || pthread_attr_setschedparam(&attr, &sp) != 0) {
+ // could not set scheduling hints, fallback to inheriting them
+ pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+ }
+ break;
+ }
+ }
+#endif // _POSIX_THREAD_PRIORITY_SCHEDULING
+
+ if (d->stackSize > 0) {
+#if defined(_POSIX_THREAD_ATTR_STACKSIZE) && (_POSIX_THREAD_ATTR_STACKSIZE-0 > 0)
+ int code = pthread_attr_setstacksize(&attr, d->stackSize);
+#else
+ int code = ENOSYS; // stack size not supported, automatically fail
+#endif // _POSIX_THREAD_ATTR_STACKSIZE
+
+ if (code) {
+ qWarning("QThread::start: Thread stack size error: %s",
+ qPrintable(qt_error_string(code)));
+
+ // we failed to set the stacksize, and as the documentation states,
+ // the thread will fail to run...
+ d->running = false;
+ d->finished = false;
+ return;
+ }
+ }
+
+ int code =
+ pthread_create(&d->thread_id, &attr, QThreadPrivate::start, this);
+ if (code == EPERM) {
+ // caller does not have permission to set the scheduling
+ // parameters/policy
+ pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+ code =
+ pthread_create(&d->thread_id, &attr, QThreadPrivate::start, this);
+ }
+
+ pthread_attr_destroy(&attr);
+
+ if (code) {
+ qWarning("QThread::start: Thread creation error: %s", qPrintable(qt_error_string(code)));
+
+ d->running = false;
+ d->finished = false;
+ d->thread_id = 0;
+ }
+}
+
+void QThread::terminate()
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+
+ if (!d->thread_id)
+ return;
+
+ int code = pthread_cancel(d->thread_id);
+ if (code) {
+ qWarning("QThread::start: Thread termination error: %s",
+ qPrintable(qt_error_string((code))));
+ } else {
+ d->terminated = true;
+ }
+}
+
+bool QThread::wait(unsigned long time)
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+
+ if (d->thread_id == pthread_self()) {
+ qWarning("QThread::wait: Thread tried to wait on itself");
+ return false;
+ }
+
+ if (d->finished || !d->running)
+ return true;
+
+ while (d->running) {
+ if (!d->thread_done.wait(locker.mutex(), time))
+ return false;
+ }
+ return true;
+}
+
+void QThread::setTerminationEnabled(bool enabled)
+{
+ Q_ASSERT_X(currentThread() != 0, "QThread::setTerminationEnabled()",
+ "Current thread was not started with QThread.");
+ pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, NULL);
+ if (enabled)
+ pthread_testcancel();
+}
+
+void QThread::setPriority(Priority priority)
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+ if (!d->running) {
+ qWarning("QThread::setPriority: Cannot set priority, thread is not running");
+ return;
+ }
+
+ d->priority = priority;
+
+ // copied from start() with a few modifications:
+
+#if defined(Q_OS_DARWIN) || !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0)
+ int sched_policy;
+ sched_param param;
+
+ if (pthread_getschedparam(d->thread_id, &sched_policy, &param) != 0) {
+ // failed to get the scheduling policy, don't bother setting
+ // the priority
+ qWarning("QThread::setPriority: Cannot get scheduler parameters");
+ return;
+ }
+
+ int prio_min = sched_get_priority_min(sched_policy);
+ int prio_max = sched_get_priority_max(sched_policy);
+ if (prio_min == -1 || prio_max == -1) {
+ // failed to get the scheduling parameters, don't
+ // bother setting the priority
+ qWarning("QThread::setPriority: Cannot determine scheduler priority range");
+ return;
+ }
+
+ int prio;
+ switch (priority) {
+ case InheritPriority:
+ qWarning("QThread::setPriority: Argument cannot be InheritPriority");
+ return;
+
+ case IdlePriority:
+ prio = prio_min;
+ break;
+
+ case TimeCriticalPriority:
+ prio = prio_max;
+ break;
+
+ default:
+ // crudely scale our priority enum values to the prio_min/prio_max
+ prio = (priority * (prio_max - prio_min) / TimeCriticalPriority) + prio_min;
+ prio = qMax(prio_min, qMin(prio_max, prio));
+ break;
+ }
+
+ param.sched_priority = prio;
+ pthread_setschedparam(d->thread_id, sched_policy, &param);
+#endif
+}
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
+
diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp
new file mode 100644
index 0000000..27193c6
--- /dev/null
+++ b/src/corelib/thread/qthread_win.cpp
@@ -0,0 +1,620 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define WINVER 0x0500
+#define _WIN32_WINNT 0x0400
+
+
+#include "qthread.h"
+#include "qthread_p.h"
+#include "qthreadstorage.h"
+#include "qmutex.h"
+
+#include <qcoreapplication.h>
+#include <qpointer.h>
+
+#include <private/qcoreapplication_p.h>
+#include <private/qeventdispatcher_win_p.h>
+
+#include <windows.h>
+
+
+#ifndef Q_OS_WINCE
+#ifndef _MT
+#define _MT
+#endif
+#include <process.h>
+#else
+#include "qfunctions_wince.h"
+#endif
+
+#ifndef QT_NO_THREAD
+QT_BEGIN_NAMESPACE
+
+void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread);
+void qt_adopted_thread_watcher_function(void *);
+
+static DWORD qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
+void qt_create_tls()
+{
+ if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
+ return;
+ static QMutex mutex;
+ QMutexLocker locker(&mutex);
+ qt_current_thread_data_tls_index = TlsAlloc();
+}
+
+static void qt_free_tls()
+{
+ if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES) {
+ TlsFree(qt_current_thread_data_tls_index);
+ qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES;
+ }
+}
+Q_DESTRUCTOR_FUNCTION(qt_free_tls)
+
+/*
+ QThreadData
+*/
+QThreadData *QThreadData::current()
+{
+ qt_create_tls();
+ QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index));
+ if (!threadData) {
+ QThread *adopted = 0;
+ if (QInternal::activateCallbacks(QInternal::AdoptCurrentThread, (void **) &adopted)) {
+ Q_ASSERT(adopted);
+ threadData = QThreadData::get2(adopted);
+ TlsSetValue(qt_current_thread_data_tls_index, threadData);
+ adopted->d_func()->running = true;
+ adopted->d_func()->finished = false;
+ static_cast<QAdoptedThread *>(adopted)->init();
+ } else {
+ threadData = new QThreadData;
+ // This needs to be called prior to new AdoptedThread() to
+ // avoid recursion.
+ TlsSetValue(qt_current_thread_data_tls_index, threadData);
+ threadData->thread = new QAdoptedThread(threadData);
+ threadData->deref();
+ }
+
+ if (!QCoreApplicationPrivate::theMainThread) {
+ QCoreApplicationPrivate::theMainThread = threadData->thread;
+ } else {
+ HANDLE realHandle = INVALID_HANDLE_VALUE;
+#if !defined(Q_OS_WINCE) || (defined(_WIN32_WCE) && (_WIN32_WCE>=0x600))
+ DuplicateHandle(GetCurrentProcess(),
+ GetCurrentThread(),
+ GetCurrentProcess(),
+ &realHandle,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+#else
+ realHandle = (HANDLE)GetCurrentThreadId();
+#endif
+ qt_watch_adopted_thread(realHandle, threadData->thread);
+ }
+ }
+ return threadData;
+}
+
+void QAdoptedThread::init()
+{
+ d_func()->handle = GetCurrentThread();
+ d_func()->id = GetCurrentThreadId();
+}
+
+static QVector<HANDLE> qt_adopted_thread_handles;
+static QVector<QThread *> qt_adopted_qthreads;
+static QMutex qt_adopted_thread_watcher_mutex;
+static HANDLE qt_adopted_thread_watcher_handle = 0;
+static HANDLE qt_adopted_thread_wakeup = 0;
+
+/*! \internal
+ Adds an adopted thread to the list of threads that Qt watches to make sure
+ the thread data is properly cleaned up. This function starts the watcher
+ thread if necessary.
+*/
+void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread)
+{
+ QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
+ qt_adopted_thread_handles.append(adoptedThreadHandle);
+ qt_adopted_qthreads.append(qthread);
+
+ // Start watcher thread if it is not already running.
+ if (qt_adopted_thread_watcher_handle == 0) {
+ if (qt_adopted_thread_wakeup == 0) {
+ qt_adopted_thread_wakeup = QT_WA_INLINE(CreateEventW(0, false, false, 0),
+ CreateEventA(0, false, false, 0));
+ qt_adopted_thread_handles.prepend(qt_adopted_thread_wakeup);
+ }
+
+ qt_adopted_thread_watcher_handle =
+ (HANDLE)_beginthread(qt_adopted_thread_watcher_function, 0, NULL);
+ } else {
+ SetEvent(qt_adopted_thread_wakeup);
+ }
+}
+
+/*! \internal
+ This function loops and waits for native adopted threads to finish.
+ When this happens it derefs the QThreadData for the adopted thread
+ to make sure it gets cleaned up properly.
+*/
+void qt_adopted_thread_watcher_function(void *)
+{
+ forever {
+ qt_adopted_thread_watcher_mutex.lock();
+
+ if (qt_adopted_thread_handles.count() == 1) {
+ qt_adopted_thread_watcher_handle = 0;
+ qt_adopted_thread_watcher_mutex.unlock();
+ break;
+ }
+
+ QVector<HANDLE> handlesCopy = qt_adopted_thread_handles;
+ qt_adopted_thread_watcher_mutex.unlock();
+
+ DWORD ret = WAIT_TIMEOUT;
+ int loops = (handlesCopy.count() / MAXIMUM_WAIT_OBJECTS) + 1, offset, count;
+ if (loops == 1) {
+ // no need to loop, no timeout
+ offset = 0;
+ count = handlesCopy.count();
+ ret = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, INFINITE);
+ } else {
+ int loop = 0;
+ do {
+ offset = loop * MAXIMUM_WAIT_OBJECTS;
+ count = qMin(handlesCopy.count() - offset, MAXIMUM_WAIT_OBJECTS);
+ ret = WaitForMultipleObjects(count, handlesCopy.constData() + offset, false, 100);
+ loop = (loop + 1) % loops;
+ } while (ret == WAIT_TIMEOUT);
+ }
+
+ if (ret == WAIT_FAILED || !(ret >= WAIT_OBJECT_0 && ret < WAIT_OBJECT_0 + uint(count))) {
+ qWarning("QThread internal error while waiting for adopted threads: %d", int(GetLastError()));
+ continue;
+ }
+
+ const int handleIndex = offset + ret - WAIT_OBJECT_0;
+ if (handleIndex == 0){
+ // New handle to watch was added.
+ continue;
+ } else {
+// printf("(qt) - qt_adopted_thread_watcher_function... called\n");
+ const int qthreadIndex = handleIndex - 1;
+ QThreadData::get2(qt_adopted_qthreads.at(qthreadIndex))->deref();
+#if !defined(Q_OS_WINCE) || (defined(_WIN32_WCE) && (_WIN32_WCE>=0x600))
+ CloseHandle(qt_adopted_thread_handles.at(handleIndex));
+#endif
+ QMutexLocker lock(&qt_adopted_thread_watcher_mutex);
+ qt_adopted_thread_handles.remove(handleIndex);
+ qt_adopted_qthreads.remove(qthreadIndex);
+ }
+ }
+}
+
+#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINCE)
+
+#ifndef Q_OS_WIN64
+# define ULONG_PTR DWORD
+#endif
+
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in user addr space)
+ HANDLE dwThreadID; // thread ID (-1=caller thread)
+ DWORD dwFlags; // reserved for future use, must be zero
+} THREADNAME_INFO;
+
+void qt_set_thread_name(HANDLE threadId, LPCSTR threadName)
+{
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = threadName;
+ info.dwThreadID = threadId;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR*)&info);
+ }
+ __except (EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+}
+#endif // !QT_NO_DEBUG && Q_CC_MSVC && !Q_OS_WINCE
+
+/**************************************************************************
+ ** QThreadPrivate
+ *************************************************************************/
+
+#endif // QT_NO_THREAD
+
+void QThreadPrivate::createEventDispatcher(QThreadData *data)
+{
+ data->eventDispatcher = new QEventDispatcherWin32;
+ data->eventDispatcher->startingUp();
+}
+
+#ifndef QT_NO_THREAD
+
+unsigned int __stdcall QThreadPrivate::start(void *arg)
+{
+ QThread *thr = reinterpret_cast<QThread *>(arg);
+ QThreadData *data = QThreadData::get2(thr);
+
+ qt_create_tls();
+ TlsSetValue(qt_current_thread_data_tls_index, data);
+
+ QThread::setTerminationEnabled(false);
+
+ data->quitNow = false;
+ // ### TODO: allow the user to create a custom event dispatcher
+ if (QCoreApplication::instance())
+ createEventDispatcher(data);
+
+#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINCE)
+ // sets the name of the current thread.
+ QByteArray objectName = thr->objectName().toLocal8Bit();
+ qt_set_thread_name((HANDLE)-1,
+ objectName.isEmpty() ?
+ thr->metaObject()->className() : objectName.constData());
+#endif
+
+ emit thr->started();
+ QThread::setTerminationEnabled(true);
+ thr->run();
+
+ finish(arg);
+ return 0;
+}
+
+void QThreadPrivate::finish(void *arg, bool lockAnyway)
+{
+ QThread *thr = reinterpret_cast<QThread *>(arg);
+ QThreadPrivate *d = thr->d_func();
+
+ if (lockAnyway)
+ d->mutex.lock();
+ d->priority = QThread::InheritPriority;
+ d->running = false;
+ d->finished = true;
+ if (d->terminated)
+ emit thr->terminated();
+ d->terminated = false;
+ emit thr->finished();
+
+ if (d->data->eventDispatcher) {
+ d->data->eventDispatcher->closingDown();
+ QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher;
+ d->data->eventDispatcher = 0;
+ delete eventDispatcher;
+ }
+
+ QThreadStorageData::finish(reinterpret_cast<void **>(&d->data->tls));
+
+ if (!d->waiters) {
+ CloseHandle(d->handle);
+ d->handle = 0;
+ }
+
+ d->id = 0;
+
+ if (lockAnyway)
+ d->mutex.unlock();
+}
+
+/**************************************************************************
+ ** QThread
+ *************************************************************************/
+
+Qt::HANDLE QThread::currentThreadId()
+{
+ return (Qt::HANDLE)GetCurrentThreadId();
+}
+
+int QThread::idealThreadCount()
+{
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ return sysinfo.dwNumberOfProcessors;
+}
+
+void QThread::yieldCurrentThread()
+{
+#ifndef Q_OS_WINCE
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)
+ SwitchToThread();
+ else
+#endif
+ ::Sleep(0);
+}
+
+void QThread::sleep(unsigned long secs)
+{
+ ::Sleep(secs * 1000);
+}
+
+void QThread::msleep(unsigned long msecs)
+{
+ ::Sleep(msecs);
+}
+
+void QThread::usleep(unsigned long usecs)
+{
+ ::Sleep((usecs / 1000) + 1);
+}
+
+
+void QThread::start(Priority priority)
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+
+ if (d->running)
+ return;
+
+ d->running = true;
+ d->finished = false;
+ d->terminated = false;
+
+ /*
+ NOTE: we create the thread in the suspended state, set the
+ priority and then resume the thread.
+
+ since threads are created with normal priority by default, we
+ could get into a case where a thread (with priority less than
+ NormalPriority) tries to create a new thread (also with priority
+ less than NormalPriority), but the newly created thread preempts
+ its 'parent' and runs at normal priority.
+ */
+ d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
+ this, CREATE_SUSPENDED, &(d->id));
+
+ if (!d->handle) {
+ qErrnoWarning(errno, "QThread::start: Failed to create thread");
+ d->running = false;
+ d->finished = true;
+ return;
+ }
+
+ // Since Win 9x will have problems if the priority is idle or time critical
+ // we have to use the closest one instead
+ int prio;
+ d->priority = priority;
+ switch (d->priority) {
+ case IdlePriority:
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) {
+ prio = THREAD_PRIORITY_LOWEST;
+ } else {
+ prio = THREAD_PRIORITY_IDLE;
+ }
+ break;
+
+ case LowestPriority:
+ prio = THREAD_PRIORITY_LOWEST;
+ break;
+
+ case LowPriority:
+ prio = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+
+ case NormalPriority:
+ prio = THREAD_PRIORITY_NORMAL;
+ break;
+
+ case HighPriority:
+ prio = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+
+ case HighestPriority:
+ prio = THREAD_PRIORITY_HIGHEST;
+ break;
+
+ case TimeCriticalPriority:
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) {
+ prio = THREAD_PRIORITY_HIGHEST;
+ } else {
+ prio = THREAD_PRIORITY_TIME_CRITICAL;
+ }
+ break;
+
+ case InheritPriority:
+ default:
+ prio = GetThreadPriority(GetCurrentThread());
+ break;
+ }
+
+ if (!SetThreadPriority(d->handle, prio)) {
+ qErrnoWarning("QThread::start: Failed to set thread priority");
+ }
+
+ if (ResumeThread(d->handle) == (DWORD) -1) {
+ qErrnoWarning("QThread::start: Failed to resume new thread");
+ }
+}
+
+void QThread::terminate()
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+ if (!d->running)
+ return;
+ if (!d->terminationEnabled) {
+ d->terminatePending = true;
+ return;
+ }
+ TerminateThread(d->handle, 0);
+ d->terminated = true;
+ QThreadPrivate::finish(this, false);
+}
+
+bool QThread::wait(unsigned long time)
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+
+ if (d->id == GetCurrentThreadId()) {
+ qWarning("QThread::wait: Thread tried to wait on itself");
+ return false;
+ }
+ if (d->finished || !d->running)
+ return true;
+
+ ++d->waiters;
+ locker.mutex()->unlock();
+
+ bool ret = false;
+ switch (WaitForSingleObject(d->handle, time)) {
+ case WAIT_OBJECT_0:
+ ret = true;
+ break;
+ case WAIT_FAILED:
+ qErrnoWarning("QThread::wait: Thread wait failure");
+ break;
+ case WAIT_ABANDONED:
+ case WAIT_TIMEOUT:
+ default:
+ break;
+ }
+
+ locker.mutex()->lock();
+ --d->waiters;
+
+ if (ret && !d->finished) {
+ // thread was terminated by someone else
+ d->terminated = true;
+ QThreadPrivate::finish(this, false);
+ }
+
+ if (d->finished && !d->waiters) {
+ CloseHandle(d->handle);
+ d->handle = 0;
+ }
+
+ return ret;
+}
+
+void QThread::setTerminationEnabled(bool enabled)
+{
+ QThread *thr = currentThread();
+ Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()",
+ "Current thread was not started with QThread.");
+ QThreadPrivate *d = thr->d_func();
+ QMutexLocker locker(&d->mutex);
+ d->terminationEnabled = enabled;
+ if (enabled && d->terminatePending) {
+ d->terminated = true;
+ QThreadPrivate::finish(thr, false);
+ locker.unlock(); // don't leave the mutex locked!
+ _endthreadex(0);
+ }
+}
+
+void QThread::setPriority(Priority priority)
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+ if (!d->running) {
+ qWarning("QThread::setPriority: Cannot set priority, thread is not running");
+ return;
+ }
+
+ // copied from start() with a few modifications:
+
+ // Since Win 9x will have problems if the priority is idle or time critical
+ // we have to use the closest one instead
+ int prio;
+ d->priority = priority;
+ switch (d->priority) {
+ case IdlePriority:
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) {
+ prio = THREAD_PRIORITY_LOWEST;
+ } else {
+ prio = THREAD_PRIORITY_IDLE;
+ }
+ break;
+
+ case LowestPriority:
+ prio = THREAD_PRIORITY_LOWEST;
+ break;
+
+ case LowPriority:
+ prio = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+
+ case NormalPriority:
+ prio = THREAD_PRIORITY_NORMAL;
+ break;
+
+ case HighPriority:
+ prio = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+
+ case HighestPriority:
+ prio = THREAD_PRIORITY_HIGHEST;
+ break;
+
+ case TimeCriticalPriority:
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) {
+ prio = THREAD_PRIORITY_HIGHEST;
+ } else {
+ prio = THREAD_PRIORITY_TIME_CRITICAL;
+ }
+ break;
+
+ case InheritPriority:
+ default:
+ qWarning("QThread::setPriority: Argument cannot be InheritPriority");
+ return;
+ }
+
+ if (!SetThreadPriority(d->handle, prio)) {
+ qErrnoWarning("QThread::setPriority: Failed to set thread priority");
+ }
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_THREAD
diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp
new file mode 100644
index 0000000..35c55c1
--- /dev/null
+++ b/src/corelib/thread/qthreadstorage.cpp
@@ -0,0 +1,320 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qthreadstorage.h"
+
+#ifndef QT_NO_THREAD
+#include "qthread.h"
+#include "qthread_p.h"
+#include "qmutex.h"
+
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+// #define THREADSTORAGE_DEBUG
+#ifdef THREADSTORAGE_DEBUG
+# define DEBUG_MSG qtsDebug
+
+# include <stdio.h>
+# include <stdarg.h>
+void qtsDebug(const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+
+ fprintf(stderr, "QThreadStorage: ");
+ vfprintf(stderr, fmt, va);
+ fprintf(stderr, "\n");
+
+ va_end(va);
+}
+#else
+# define DEBUG_MSG if(false)qDebug
+#endif
+
+static QBasicAtomicInt idCounter = Q_BASIC_ATOMIC_INITIALIZER(INT_MAX);
+Q_GLOBAL_STATIC(QMutex, mutex)
+typedef QMap<int, void (*)(void *)> DestructorMap;
+Q_GLOBAL_STATIC(DestructorMap, destructors)
+
+QThreadStorageData::QThreadStorageData(void (*func)(void *))
+ : id(idCounter.fetchAndAddRelaxed(-1))
+{
+ QMutexLocker locker(mutex());
+ destructors()->insert(id, func);
+
+ DEBUG_MSG("QThreadStorageData: Allocated id %d, destructor %p", id, func);
+}
+
+QThreadStorageData::~QThreadStorageData()
+{
+ QMutexLocker locker(mutex());
+ if (destructors())
+ destructors()->remove(id);
+
+ DEBUG_MSG("QThreadStorageData: Released id %d", id);
+}
+
+void **QThreadStorageData::get() const
+{
+ QThreadData *data = QThreadData::current();
+ if (!data) {
+ qWarning("QThreadStorage::get: QThreadStorage can only be used with threads started with QThread");
+ return 0;
+ }
+ QMap<int, void *>::iterator it = data->tls.find(id);
+ DEBUG_MSG("QThreadStorageData: Returning storage %d, data %p, for thread %p",
+ id,
+ it != data->tls.end() ? it.value() : 0,
+ data->thread);
+ return it != data->tls.end() && it.value() != 0 ? &it.value() : 0;
+}
+
+void **QThreadStorageData::set(void *p)
+{
+ QThreadData *data = QThreadData::current();
+ if (!data) {
+ qWarning("QThreadStorage::set: QThreadStorage can only be used with threads started with QThread");
+ return 0;
+ }
+
+ QMap<int, void *>::iterator it = data->tls.find(id);
+ if (it != data->tls.end()) {
+ // delete any previous data
+ if (it.value() != 0) {
+ DEBUG_MSG("QThreadStorageData: Deleting previous storage %d, data %p, for thread %p",
+ id,
+ it.value(),
+ data->thread);
+
+ void *q = it.value();
+ it.value() = 0;
+
+ mutex()->lock();
+ void (*destructor)(void *) = destructors()->value(id);
+ mutex()->unlock();
+
+ destructor(q);
+ }
+
+ // store new data
+ it.value() = p;
+ DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread, p);
+ } else {
+ it = data->tls.insert(id, p);
+ DEBUG_MSG("QThreadStorageData: Inserted storage %d, data %p, for thread %p", id, p, data->thread);
+ }
+
+ return &it.value();
+}
+
+void QThreadStorageData::finish(void **p)
+{
+ QMap<int, void *> *tls = reinterpret_cast<QMap<int, void *> *>(p);
+ if (!tls || tls->isEmpty() || !mutex())
+ return; // nothing to do
+
+ DEBUG_MSG("QThreadStorageData: Destroying storage for thread %p", QThread::currentThread());
+
+ QMap<int, void *>::iterator it = tls->begin();
+ while (it != tls->end()) {
+ int id = it.key();
+ void *q = it.value();
+ it.value() = 0;
+ ++it;
+
+ if (!q) {
+ // data already deleted
+ continue;
+ }
+
+ mutex()->lock();
+ void (*destructor)(void *) = destructors()->value(id);
+ mutex()->unlock();
+
+ if (!destructor) {
+ if (QThread::currentThread())
+ qWarning("QThreadStorage: Thread %p exited after QThreadStorage %d destroyed",
+ QThread::currentThread(), id);
+ continue;
+ }
+ destructor(q);
+ }
+ tls->clear();
+}
+
+/*!
+ \class QThreadStorage
+ \brief The QThreadStorage class provides per-thread data storage.
+
+ \threadsafe
+
+ \ingroup thread
+ \ingroup environment
+ \mainclass
+
+ QThreadStorage is a template class that provides per-thread data
+ storage.
+
+ \e{Note that due to compiler limitations, QThreadStorage can only
+ store pointers.}
+
+ The setLocalData() function stores a single thread-specific value
+ for the calling thread. The data can be accessed later using
+ localData(). QThreadStorage takes ownership of the data (which
+ must be created on the heap with \c new) and deletes it when the
+ thread exits, either normally or via termination.
+
+ The hasLocalData() function allows the programmer to determine if
+ data has previously been set using the setLocalData() function.
+ This is also useful for lazy initializiation.
+
+ For example, the following code uses QThreadStorage to store a
+ single cache for each thread that calls the cacheObject() and
+ removeFromCache() functions. The cache is automatically
+ deleted when the calling thread exits.
+
+ \snippet doc/src/snippets/threads/threads.cpp 7
+ \snippet doc/src/snippets/threads/threads.cpp 8
+ \snippet doc/src/snippets/threads/threads.cpp 9
+
+ \section1 Caveats
+
+ \list
+
+ \o As noted above, QThreadStorage can only store pointers due to
+ compiler limitations.
+
+ \o The QThreadStorage destructor does not delete per-thread data.
+ QThreadStorage only deletes per-thread data when the thread exits
+ or when setLocalData() is called multiple times.
+
+ \o QThreadStorage can be used to store data for the \c main()
+ thread. QThreadStorage deletes all data set for the \c main()
+ thread when QApplication is destroyed, regardless of whether or
+ not the \c main() thread has actually finished.
+
+ \endlist
+
+ \sa QThread
+*/
+
+/*!
+ \fn QThreadStorage::QThreadStorage()
+
+ Constructs a new per-thread data storage object.
+*/
+
+/*!
+ \fn QThreadStorage::~QThreadStorage()
+
+ Destroys the per-thread data storage object.
+
+ Note: The per-thread data stored is not deleted. Any data left
+ in QThreadStorage is leaked. Make sure that all threads using
+ QThreadStorage have exited before deleting the QThreadStorage.
+
+ \sa hasLocalData()
+*/
+
+/*!
+ \fn bool QThreadStorage::hasLocalData() const
+
+ Returns true if the calling thread has non-zero data available;
+ otherwise returns false.
+
+ \sa localData()
+*/
+
+/*!
+ \fn T &QThreadStorage::localData()
+
+ Returns a reference to the data that was set by the calling
+ thread.
+
+ Note: QThreadStorage can only store pointers. This function
+ returns a reference to the pointer that was set by the calling
+ thread. The value of this reference is 0 if no data was set by
+ the calling thread,
+
+ \sa hasLocalData()
+*/
+
+/*!
+ \fn const T QThreadStorage::localData() const
+ \overload
+
+ Returns a copy of the data that was set by the calling thread.
+
+ Note: QThreadStorage can only store pointers. This function
+ returns a pointer to the data that was set by the calling thread.
+ If no data was set by the calling thread, this function returns 0.
+
+ \sa hasLocalData()
+*/
+
+/*!
+ \fn void QThreadStorage::setLocalData(T data)
+
+ Sets the local data for the calling thread to \a data. It can be
+ accessed later using the localData() functions.
+
+ If \a data is 0, this function deletes the previous data (if
+ any) and returns immediately.
+
+ If \a data is non-zero, QThreadStorage takes ownership of the \a
+ data and deletes it automatically either when the thread exits
+ (either normally or via termination) or when setLocalData() is
+ called again.
+
+ Note: QThreadStorage can only store pointers. The \a data
+ argument must be either a pointer to an object created on the heap
+ (i.e. using \c new) or 0. You should not delete \a data
+ yourself; QThreadStorage takes ownership and will delete the \a
+ data itself.
+
+ \sa localData(), hasLocalData()
+*/
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
diff --git a/src/corelib/thread/qthreadstorage.h b/src/corelib/thread/qthreadstorage.h
new file mode 100644
index 0000000..952e4b2
--- /dev/null
+++ b/src/corelib/thread/qthreadstorage.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTHREADSTORAGE_H
+#define QTHREADSTORAGE_H
+
+#include <QtCore/qglobal.h>
+
+#ifndef QT_NO_THREAD
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class Q_CORE_EXPORT QThreadStorageData
+{
+public:
+ explicit QThreadStorageData(void (*func)(void *));
+ ~QThreadStorageData();
+
+ void** get() const;
+ void** set(void* p);
+
+ static void finish(void**);
+ int id;
+};
+
+#if !defined(QT_MOC_CPP)
+// MOC_SKIP_BEGIN
+
+// pointer specialization
+template <typename T>
+inline
+T *&qThreadStorage_localData(QThreadStorageData &d, T **)
+{
+ void **v = d.get();
+ if (!v) v = d.set(0);
+ return *(reinterpret_cast<T**>(v));
+}
+
+template <typename T>
+inline
+T *qThreadStorage_localData_const(const QThreadStorageData &d, T **)
+{
+ void **v = d.get();
+ return v ? *(reinterpret_cast<T**>(v)) : 0;
+}
+
+template <typename T>
+inline
+void qThreadStorage_setLocalData(QThreadStorageData &d, T **t)
+{ (void) d.set(*t); }
+
+#ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION
+
+// value-based specialization
+template <typename T>
+inline
+T &qThreadStorage_localData(QThreadStorageData &d, T *)
+{
+ void **v = d.get();
+ if (!v) v = d.set(new T());
+ return *(reinterpret_cast<T*>(*v));
+}
+
+template <typename T>
+inline
+T qThreadStorage_localData_const(const QThreadStorageData &d, T *)
+{
+ void **v = d.get();
+ return v ? *(reinterpret_cast<T*>(*v)) : T();
+}
+
+template <typename T>
+inline
+void qThreadStorage_setLocalData(QThreadStorageData &d, T *t)
+{ (void) d.set(new T(*t)); }
+
+#endif // QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION
+
+// MOC_SKIP_END
+#endif
+
+template <class T>
+class QThreadStorage
+{
+private:
+ QThreadStorageData d;
+
+ Q_DISABLE_COPY(QThreadStorage)
+
+ static inline void deleteData(void *x)
+ { delete static_cast<T>(x); }
+
+public:
+ inline QThreadStorage() : d(deleteData) { }
+ inline ~QThreadStorage() { }
+
+ inline bool hasLocalData() const
+ { return d.get() != 0; }
+
+ inline T& localData()
+ { return qThreadStorage_localData(d, reinterpret_cast<T*>(0)); }
+ inline T localData() const
+ { return qThreadStorage_localData_const(d, reinterpret_cast<T*>(0)); }
+
+ inline void setLocalData(T t)
+ { qThreadStorage_setLocalData(d, &t); }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_THREAD
+
+#endif // QTHREADSTORAGE_H
diff --git a/src/corelib/thread/qwaitcondition.h b/src/corelib/thread/qwaitcondition.h
new file mode 100644
index 0000000..850f76a
--- /dev/null
+++ b/src/corelib/thread/qwaitcondition.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAITCONDITION_H
+#define QWAITCONDITION_H
+
+#include <QtCore/qglobal.h>
+
+#include <limits.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_THREAD
+
+class QWaitConditionPrivate;
+class QMutex;
+class QReadWriteLock;
+
+class Q_CORE_EXPORT QWaitCondition
+{
+public:
+ QWaitCondition();
+ ~QWaitCondition();
+
+ bool wait(QMutex *mutex, unsigned long time = ULONG_MAX);
+ bool wait(QReadWriteLock *readWriteLock, unsigned long time = ULONG_MAX);
+
+ void wakeOne();
+ void wakeAll();
+
+private:
+ Q_DISABLE_COPY(QWaitCondition)
+
+ QWaitConditionPrivate * d;
+};
+
+#else
+
+class QMutex;
+class Q_CORE_EXPORT QWaitCondition
+{
+public:
+ QWaitCondition() {}
+ ~QWaitCondition() {}
+
+ bool wait(QMutex *mutex, unsigned long time = ULONG_MAX)
+ {
+ Q_UNUSED(mutex);
+ Q_UNUSED(time);
+ return true;
+ }
+
+ void wakeOne() {}
+ void wakeAll() {}
+};
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWAITCONDITION_H
diff --git a/src/corelib/thread/qwaitcondition_unix.cpp b/src/corelib/thread/qwaitcondition_unix.cpp
new file mode 100644
index 0000000..ff7e961
--- /dev/null
+++ b/src/corelib/thread/qwaitcondition_unix.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qwaitcondition.h"
+#include "qmutex.h"
+#include "qreadwritelock.h"
+#include "qatomic.h"
+#include "qstring.h"
+
+#include "qmutex_p.h"
+#include "qreadwritelock_p.h"
+
+#include <errno.h>
+
+#ifndef QT_NO_THREAD
+
+QT_BEGIN_NAMESPACE
+
+static void report_error(int code, const char *where, const char *what)
+{
+ if (code != 0)
+ qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code)));
+}
+
+
+
+struct QWaitConditionPrivate {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int waiters;
+ int wakeups;
+
+ bool wait(unsigned long time)
+ {
+ int code;
+ forever {
+ if (time != ULONG_MAX) {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ timespec ti;
+ ti.tv_nsec = (tv.tv_usec + (time % 1000) * 1000) * 1000;
+ ti.tv_sec = tv.tv_sec + (time / 1000) + (ti.tv_nsec / 1000000000);
+ ti.tv_nsec %= 1000000000;
+
+ code = pthread_cond_timedwait(&cond, &mutex, &ti);
+ } else {
+ code = pthread_cond_wait(&cond, &mutex);
+ }
+ if (code == 0 && wakeups == 0) {
+ // many vendors warn of spurios wakeups from
+ // pthread_cond_wait(), especially after signal delivery,
+ // even though POSIX doesn't allow for it... sigh
+ continue;
+ }
+ break;
+ }
+
+ Q_ASSERT_X(waiters > 0, "QWaitCondition::wait", "internal error (waiters)");
+ --waiters;
+ if (code == 0) {
+ Q_ASSERT_X(wakeups > 0, "QWaitCondition::wait", "internal error (wakeups)");
+ --wakeups;
+ }
+ report_error(pthread_mutex_unlock(&mutex), "QWaitCondition::wait()", "mutex unlock");
+
+ if (code && code != ETIMEDOUT)
+ report_error(code, "QWaitCondition::wait()", "cv wait");
+
+ return (code == 0);
+ }
+};
+
+
+QWaitCondition::QWaitCondition()
+{
+ d = new QWaitConditionPrivate;
+ report_error(pthread_mutex_init(&d->mutex, NULL), "QWaitCondition", "mutex init");
+ report_error(pthread_cond_init(&d->cond, NULL), "QWaitCondition", "cv init");
+ d->waiters = d->wakeups = 0;
+}
+
+
+QWaitCondition::~QWaitCondition()
+{
+ report_error(pthread_cond_destroy(&d->cond), "QWaitCondition", "cv destroy");
+ report_error(pthread_mutex_destroy(&d->mutex), "QWaitCondition", "mutex destroy");
+ delete d;
+}
+
+void QWaitCondition::wakeOne()
+{
+ report_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wakeOne()", "mutex lock");
+ d->wakeups = qMin(d->wakeups + 1, d->waiters);
+ report_error(pthread_cond_signal(&d->cond), "QWaitCondition::wakeOne()", "cv signal");
+ report_error(pthread_mutex_unlock(&d->mutex), "QWaitCondition::wakeOne()", "mutex unlock");
+}
+
+void QWaitCondition::wakeAll()
+{
+ report_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wakeAll()", "mutex lock");
+ d->wakeups = d->waiters;
+ report_error(pthread_cond_broadcast(&d->cond), "QWaitCondition::wakeAll()", "cv broadcast");
+ report_error(pthread_mutex_unlock(&d->mutex), "QWaitCondition::wakeAll()", "mutex unlock");
+}
+
+bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
+{
+ if (! mutex)
+ return false;
+ if (mutex->d->recursive) {
+ qWarning("QWaitCondition: cannot wait on recursive mutexes");
+ return false;
+ }
+
+ report_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wait()", "mutex lock");
+ ++d->waiters;
+ mutex->unlock();
+
+ bool returnValue = d->wait(time);
+
+ mutex->lock();
+
+ return returnValue;
+}
+
+bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
+{
+ if (!readWriteLock || readWriteLock->d->accessCount == 0)
+ return false;
+ if (readWriteLock->d->accessCount < -1) {
+ qWarning("QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()");
+ return false;
+ }
+
+ report_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wait()", "mutex lock");
+ ++d->waiters;
+
+ int previousAccessCount = readWriteLock->d->accessCount;
+ readWriteLock->unlock();
+
+ bool returnValue = d->wait(time);
+
+ if (previousAccessCount < 0)
+ readWriteLock->lockForWrite();
+ else
+ readWriteLock->lockForRead();
+
+ return returnValue;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_THREAD
diff --git a/src/corelib/thread/qwaitcondition_win.cpp b/src/corelib/thread/qwaitcondition_win.cpp
new file mode 100644
index 0000000..fa6610c
--- /dev/null
+++ b/src/corelib/thread/qwaitcondition_win.cpp
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaitcondition.h"
+#include "qnamespace.h"
+#include "qmutex.h"
+#include "qreadwritelock.h"
+#include "qlist.h"
+#include "qalgorithms.h"
+#include "qt_windows.h"
+
+#ifndef QT_NO_THREAD
+
+#define Q_MUTEX_T void*
+#include <private/qmutex_p.h>
+#include <private/qreadwritelock_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//***********************************************************************
+// QWaitConditionPrivate
+// **********************************************************************
+
+class QWaitConditionEvent
+{
+public:
+ inline QWaitConditionEvent() : priority(0), wokenUp(false)
+ {
+ QT_WA ({
+ event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ }, {
+ event = CreateEventA(NULL, TRUE, FALSE, NULL);
+ });
+ }
+ inline ~QWaitConditionEvent() { CloseHandle(event); }
+ int priority;
+ bool wokenUp;
+ HANDLE event;
+};
+
+typedef QList<QWaitConditionEvent *> EventQueue;
+
+class QWaitConditionPrivate
+{
+public:
+ QMutex mtx;
+ EventQueue queue;
+ EventQueue freeQueue;
+
+ QWaitConditionEvent *pre();
+ bool wait(QWaitConditionEvent *wce, unsigned long time);
+ void post(QWaitConditionEvent *wce, bool ret);
+};
+
+QWaitConditionEvent *QWaitConditionPrivate::pre()
+{
+ mtx.lock();
+ QWaitConditionEvent *wce =
+ freeQueue.isEmpty() ? new QWaitConditionEvent : freeQueue.takeFirst();
+ wce->priority = GetThreadPriority(GetCurrentThread());
+ wce->wokenUp = false;
+
+ // insert 'wce' into the queue (sorted by priority)
+ int index = 0;
+ for (; index < queue.size(); ++index) {
+ QWaitConditionEvent *current = queue.at(index);
+ if (current->priority < wce->priority)
+ break;
+ }
+ queue.insert(index, wce);
+ mtx.unlock();
+
+ return wce;
+}
+
+bool QWaitConditionPrivate::wait(QWaitConditionEvent *wce, unsigned long time)
+{
+ // wait for the event
+ bool ret = false;
+ switch (WaitForSingleObject(wce->event, time)) {
+ default: break;
+
+ case WAIT_OBJECT_0:
+ ret = true;
+ break;
+ }
+ return ret;
+}
+
+void QWaitConditionPrivate::post(QWaitConditionEvent *wce, bool ret)
+{
+ mtx.lock();
+
+ // remove 'wce' from the queue
+ queue.removeAll(wce);
+ ResetEvent(wce->event);
+ freeQueue.append(wce);
+
+ // wakeups delivered after the timeout should be forwarded to the next waiter
+ if (!ret && wce->wokenUp && !queue.isEmpty()) {
+ QWaitConditionEvent *other = queue.first();
+ SetEvent(other->event);
+ other->wokenUp = true;
+ }
+
+ mtx.unlock();
+}
+
+//***********************************************************************
+// QWaitCondition implementation
+//***********************************************************************
+
+QWaitCondition::QWaitCondition()
+{
+ d = new QWaitConditionPrivate;
+}
+
+QWaitCondition::~QWaitCondition()
+{
+ if (!d->queue.isEmpty()) {
+ qWarning("QWaitCondition: Destroyed while threads are still waiting");
+ qDeleteAll(d->queue);
+ }
+
+ qDeleteAll(d->freeQueue);
+ delete d;
+}
+
+bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
+{
+ if (!mutex)
+ return false;
+ if (mutex->d->recursive) {
+ qWarning("QWaitCondition::wait: Cannot wait on recursive mutexes");
+ return false;
+ }
+
+ QWaitConditionEvent *wce = d->pre();
+ mutex->unlock();
+
+ bool returnValue = d->wait(wce, time);
+
+ mutex->lock();
+ d->post(wce, returnValue);
+
+ return returnValue;
+}
+
+bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
+{
+ if (!readWriteLock || readWriteLock->d->accessCount == 0)
+ return false;
+ if (readWriteLock->d->accessCount < -1) {
+ qWarning("QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()");
+ return false;
+ }
+
+ QWaitConditionEvent *wce = d->pre();
+ int previousAccessCount = readWriteLock->d->accessCount;
+ readWriteLock->unlock();
+
+ bool returnValue = d->wait(wce, time);
+
+ if (previousAccessCount < 0)
+ readWriteLock->lockForWrite();
+ else
+ readWriteLock->lockForRead();
+ d->post(wce, returnValue);
+
+ return returnValue;
+}
+
+void QWaitCondition::wakeOne()
+{
+ // wake up the first waiting thread in the queue
+ QMutexLocker locker(&d->mtx);
+ for (int i = 0; i < d->queue.size(); ++i) {
+ QWaitConditionEvent *current = d->queue.at(i);
+ if (current->wokenUp)
+ continue;
+ SetEvent(current->event);
+ current->wokenUp = true;
+ break;
+ }
+}
+
+void QWaitCondition::wakeAll()
+{
+ // wake up the all threads in the queue
+ QMutexLocker locker(&d->mtx);
+ for (int i = 0; i < d->queue.size(); ++i) {
+ QWaitConditionEvent *current = d->queue.at(i);
+ SetEvent(current->event);
+ current->wokenUp = true;
+ }
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_THREAD
diff --git a/src/corelib/thread/thread.pri b/src/corelib/thread/thread.pri
new file mode 100644
index 0000000..03f661d
--- /dev/null
+++ b/src/corelib/thread/thread.pri
@@ -0,0 +1,33 @@
+# Qt core thread module
+
+# public headers
+HEADERS += thread/qmutex.h \
+ thread/qreadwritelock.h \
+ thread/qsemaphore.h \
+ thread/qthread.h \
+ thread/qthreadstorage.h \
+ thread/qwaitcondition.h \
+ thread/qatomic.h
+
+# private headers
+HEADERS += thread/qmutex_p.h \
+ thread/qmutexpool_p.h \
+ thread/qorderedmutexlocker_p.h \
+ thread/qreadwritelock_p.h \
+ thread/qthread_p.h
+
+SOURCES += thread/qatomic.cpp \
+ thread/qmutex.cpp \
+ thread/qreadwritelock.cpp \
+ thread/qmutexpool.cpp \
+ thread/qsemaphore.cpp \
+ thread/qthread.cpp \
+ thread/qthreadstorage.cpp
+
+unix:SOURCES += thread/qmutex_unix.cpp \
+ thread/qthread_unix.cpp \
+ thread/qwaitcondition_unix.cpp
+
+win32:SOURCES += thread/qmutex_win.cpp \
+ thread/qthread_win.cpp \
+ thread/qwaitcondition_win.cpp