diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 10:18:55 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 10:18:55 +0100 |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/corelib/thread | |
download | qt4-tools-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz |
Long live Qt 4.5!
Diffstat (limited to 'src/corelib/thread')
27 files changed, 7491 insertions, 0 deletions
diff --git a/src/corelib/thread/qatomic.cpp b/src/corelib/thread/qatomic.cpp new file mode 100644 index 0000000000..f426feb22d --- /dev/null +++ b/src/corelib/thread/qatomic.cpp @@ -0,0 +1,1127 @@ +/**************************************************************************** +** +** 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 a combination of these operators + is \e not an atomic operation. + + \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 0000000000..4e949ee713 --- /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 0000000000..a5af6c567e --- /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 0000000000..a3b3fe714c --- /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 0000000000..51f1f79e34 --- /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 0000000000..6eb15be11c --- /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 0000000000..e15ec7e794 --- /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 0000000000..a73815dc7c --- /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 0000000000..71ecab558f --- /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 0000000000..65a3b543fe --- /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 0000000000..3b92e31dae --- /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 0000000000..809e07f950 --- /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 0000000000..c8df73808d --- /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 0000000000..a813aa6596 --- /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 0000000000..82c3607f81 --- /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 0000000000..6bc1920641 --- /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 0000000000..7f8789744e --- /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 0000000000..d9a6240b54 --- /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 0000000000..75293cee83 --- /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 0000000000..f602821339 --- /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(¤t_thread_data_key, destroy_current_thread_data); +} + +QThreadData *QThreadData::current() +{ + pthread_once(¤t_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(¤t_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, ¶m) != 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, ¶m); +#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 0000000000..27193c6211 --- /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 0000000000..35c55c1495 --- /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 0000000000..952e4b2047 --- /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 0000000000..850f76a24a --- /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 0000000000..ff7e961ca7 --- /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 0000000000..fa6610caa4 --- /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 0000000000..03f661d353 --- /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 |