Add the new QBasicAtomicXXX implementation - no backends yet

The new implementation is API- and ABI-compatible with the old
implementation, provided that QBasicAtomicInt isn't used as an
argument in a function call or the return value: now, QBasicAtomicInt
is a typedef to QBasicAtomicInteger<int>.

The new design is based on CRTP: the QGenericAtomicOps template class
takes as a template parameter the derived class itself. This way, we
implement a "poor man's virtual" without a virtual table and
everything is inline.

QGenericAtomicOps implements most of the atomics code that is repeated
in many classes all over:

 * Acquire semantics are obtained by placing an acquire barrier after
   the Relaxed operation
 * Release semantics are obtained by placing a release barrier before
   the Relaxed operation
 * Ordered semantics are obtained by placing an ordered barrier before
   the Relaxed operation (either way would be fine)
 * fetchAndStoreRelaxed and fetchAndAddRelaxed are implemented on top
   of testAndSetRelaxed
 * ref and deref are implemented on top of fetchAndAddRelaxed

It also adds load, loadAcquire, store and storeRelease: the default
implementations of loadAcquire and storeRelease operate on a volatile
variable and add barriers. There are no direct operators for accessing
the value.

Each architecture-specific implementation can override any of the
functions or the memory barrier functions. It must implement at least
the testAndSetRelaxed function.

In addition, by specialising one template class, the implementations
can allow QBasicAtomicInteger for additional types (of different
sizes). At the very least, int, unsigned and pointers must be supported.

Change-Id: I6da647e225bb330d3cfc16f84d0e7849dff85ec7
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
This commit is contained in:
Thiago Macieira 2011-07-31 17:40:40 -03:00 committed by Qt by Nokia
parent f188d8a005
commit 1f843ca39e
5 changed files with 449 additions and 3 deletions

View File

@ -44,7 +44,7 @@
#ifndef QATOMIC_H
#define QATOMIC_H
#include <QtCore/qoldbasicatomic.h>
#include <QtCore/qbasicatomic.h>
QT_BEGIN_HEADER

View File

@ -0,0 +1,207 @@
/****************************************************************************
**
** Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.1, 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.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QBASICATOMIC_H
#define QBASICATOMIC_H
#include <QtCore/qglobal.h>
# define QT_OLD_ATOMICS
#ifdef QT_OLD_ATOMICS
# include "qoldbasicatomic.h"
# undef QT_OLD_ATOMICS
#else
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
QT_MODULE(Core)
#if 0
#pragma qt_no_master_include
#pragma qt_sync_stop_processing
#endif
// New atomics
template <typename T>
struct QBasicAtomicInteger
{
typedef QAtomicOps<T> Ops;
// static check that this is a valid integer
typedef char PermittedIntegerType[QAtomicIntegerTraits<T>::IsInteger ? 1 : -1];
typename Ops::Type _q_value;
// Non-atomic API
T load() const { return Ops::load(_q_value); }
void store(T newValue) { Ops::store(_q_value, newValue); }
// Atomic API, implemented in qatomic_XXX.h
T loadAcquire() { return Ops::loadAcquire(_q_value); }
void storeRelease(T newValue) { Ops::storeRelease(_q_value, newValue); }
static bool isReferenceCountingNative() { return Ops::isReferenceCountingNative(); }
static bool isReferenceCountingWaitFree() { return Ops::isReferenceCountingWaitFree(); }
bool ref() { return Ops::ref(_q_value); }
bool deref() { return Ops::deref(_q_value); }
static bool isTestAndSetNative() { return Ops::isTestAndSetNative(); }
static bool isTestAndSetWaitFree() { return Ops::isTestAndSetWaitFree(); }
bool testAndSetRelaxed(T expectedValue, T newValue)
{ return Ops::testAndSetRelaxed(_q_value, expectedValue, newValue); }
bool testAndSetAcquire(T expectedValue, T newValue)
{ return Ops::testAndSetAcquire(_q_value, expectedValue, newValue); }
bool testAndSetRelease(T expectedValue, T newValue)
{ return Ops::testAndSetRelease(_q_value, expectedValue, newValue); }
bool testAndSetOrdered(T expectedValue, T newValue)
{ return Ops::testAndSetOrdered(_q_value, expectedValue, newValue); }
static bool isFetchAndStoreNative() { return Ops::isFetchAndStoreNative(); }
static bool isFetchAndStoreWaitFree() { return Ops::isFetchAndStoreWaitFree(); }
T fetchAndStoreRelaxed(T newValue)
{ return Ops::fetchAndStoreRelaxed(_q_value, newValue); }
T fetchAndStoreAcquire(T newValue)
{ return Ops::fetchAndStoreAcquire(_q_value, newValue); }
T fetchAndStoreRelease(T newValue)
{ return Ops::fetchAndStoreRelease(_q_value, newValue); }
T fetchAndStoreOrdered(T newValue)
{ return Ops::fetchAndStoreOrdered(_q_value, newValue); }
static bool isFetchAndAddNative() { return Ops::isFetchAndAddNative(); }
static bool isFetchAndAddWaitFree() { return Ops::isFetchAndAddWaitFree(); }
T fetchAndAddRelaxed(T valueToAdd)
{ return Ops::fetchAndAddRelaxed(_q_value, valueToAdd); }
T fetchAndAddAcquire(T valueToAdd)
{ return Ops::fetchAndAddAcquire(_q_value, valueToAdd); }
T fetchAndAddRelease(T valueToAdd)
{ return Ops::fetchAndAddRelease(_q_value, valueToAdd); }
T fetchAndAddOrdered(T valueToAdd)
{ return Ops::fetchAndAddOrdered(_q_value, valueToAdd); }
#if defined(Q_COMPILER_CONSTEXPR) && defined(Q_COMPILER_DEFAULT_DELETE_MEMBERS)
QBasicAtomicInteger() = default;
constexpr QBasicAtomicInteger(T value) : _q_value(value) {}
QBasicAtomicInteger(const QBasicAtomicInteger &) = delete;
QBasicAtomicInteger &operator=(const QBasicAtomicInteger &) = delete;
QBasicAtomicInteger &operator=(const QBasicAtomicInteger &) volatile = delete;
#endif
};
typedef QBasicAtomicInteger<int> QBasicAtomicInt;
template <typename X>
struct QBasicAtomicPointer
{
typedef X *Type;
typedef QAtomicOps<Type> Ops;
typedef typename Ops::Type AtomicType;
AtomicType _q_value;
// Non-atomic API
Type load() const { return _q_value; }
void store(Type newValue) { _q_value = newValue; }
// Atomic API, implemented in qatomic_XXX.h
Type loadAcquire() { return Ops::loadAcquire(_q_value); }
void storeRelease(Type newValue) { Ops::storeRelease(_q_value, newValue); }
static bool isTestAndSetNative() { return Ops::isTestAndSetNative(); }
static bool isTestAndSetWaitFree() { return Ops::isTestAndSetWaitFree(); }
bool testAndSetRelaxed(Type expectedValue, Type newValue)
{ return Ops::testAndSetRelaxed(_q_value, expectedValue, newValue); }
bool testAndSetAcquire(Type expectedValue, Type newValue)
{ return Ops::testAndSetAcquire(_q_value, expectedValue, newValue); }
bool testAndSetRelease(Type expectedValue, Type newValue)
{ return Ops::testAndSetRelease(_q_value, expectedValue, newValue); }
bool testAndSetOrdered(Type expectedValue, Type newValue)
{ return Ops::testAndSetOrdered(_q_value, expectedValue, newValue); }
static bool isFetchAndStoreNative() { return Ops::isFetchAndStoreNative(); }
static bool isFetchAndStoreWaitFree() { return Ops::isFetchAndStoreWaitFree(); }
Type fetchAndStoreRelaxed(Type newValue)
{ return Ops::fetchAndStoreRelaxed(_q_value, newValue); }
Type fetchAndStoreAcquire(Type newValue)
{ return Ops::fetchAndStoreAcquire(_q_value, newValue); }
Type fetchAndStoreRelease(Type newValue)
{ return Ops::fetchAndStoreRelease(_q_value, newValue); }
Type fetchAndStoreOrdered(Type newValue)
{ return Ops::fetchAndStoreOrdered(_q_value, newValue); }
static bool isFetchAndAddNative() { return Ops::isFetchAndAddNative(); }
static bool isFetchAndAddWaitFree() { return Ops::isFetchAndAddWaitFree(); }
Type fetchAndAddRelaxed(qptrdiff valueToAdd)
{ return Ops::fetchAndAddRelaxed(_q_value, valueToAdd); }
Type fetchAndAddAcquire(qptrdiff valueToAdd)
{ return Ops::fetchAndAddAcquire(_q_value, valueToAdd); }
Type fetchAndAddRelease(qptrdiff valueToAdd)
{ return Ops::fetchAndAddRelease(_q_value, valueToAdd); }
Type fetchAndAddOrdered(qptrdiff valueToAdd)
{ return Ops::fetchAndAddOrdered(_q_value, valueToAdd); }
#if defined(Q_COMPILER_CONSTEXPR) && defined(Q_COMPILER_DEFAULT_DELETE_MEMBERS)
QBasicAtomicPointer() = default;
constexpr QBasicAtomicPointer(Type value) : _q_value(value) {}
QBasicAtomicPointer(const QBasicAtomicPointer &) = delete;
QBasicAtomicPointer &operator=(const QBasicAtomicPointer &) = delete;
QBasicAtomicPointer &operator=(const QBasicAtomicPointer &) volatile = delete;
#endif
};
#ifndef Q_BASIC_ATOMIC_INITIALIZER
# define Q_BASIC_ATOMIC_INITIALIZER(a) { (a) }
#endif
QT_END_NAMESPACE
QT_END_HEADER
#endif // QT_OLD_ATOMICS
#endif // QBASIC_ATOMIC

View File

@ -0,0 +1,232 @@
/****************************************************************************
**
** Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.1, 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.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QGENERICATOMIC_H
#define QGENERICATOMIC_H
#include <QtCore/qglobal.h>
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
#if 0
#pragma qt_sync_stop_processing
#endif
#ifdef Q_CC_GNU
// lowercase is fine, we'll undef it below
#define always_inline __attribute__((always_inline, gnu_inline))
#else
#define always_inline
#endif
template<typename T> struct QAtomicIntegerTraits { enum { IsInteger = 0 }; };
template <typename T> struct QAtomicAdditiveType
{
typedef T AdditiveT;
static const int AddScale = 1;
};
template <typename T> struct QAtomicAdditiveType<T *>
{
typedef qptrdiff AdditiveT;
static const int AddScale = sizeof(T);
};
// not really atomic...
template <typename BaseClass> struct QGenericAtomicOps
{
template <typename T> struct AtomicType { typedef T Type; typedef T *PointerType; };
static void acquireMemoryFence() { BaseClass::orderedMemoryFence(); }
static void releaseMemoryFence() { BaseClass::orderedMemoryFence(); }
static void orderedMemoryFence() { }
template <typename T> static inline always_inline
T load(T &_q_value)
{
return _q_value;
}
template <typename T> static inline always_inline
void store(T &_q_value, T newValue)
{
_q_value = newValue;
}
template <typename T> static inline always_inline
T loadAcquire(T &_q_value)
{
T tmp = *static_cast<volatile T *>(&_q_value);
BaseClass::acquireMemoryFence();
return tmp;
}
template <typename T> static inline always_inline
void storeRelease(T &_q_value, T newValue)
{
BaseClass::releaseMemoryFence();
*static_cast<volatile T *>(&_q_value) = newValue;
}
static inline bool isReferenceCountingNative()
{ return BaseClass::isFetchAndAddNative(); }
static inline bool isReferenceCountingWaitFree()
{ return BaseClass::isFetchAndAddWaitFree(); }
template <typename T> static inline always_inline
bool ref(T &_q_value)
{
return BaseClass::fetchAndAddRelaxed(_q_value, 1) != T(-1);
}
template <typename T> static inline always_inline
bool deref(T &_q_value)
{
return BaseClass::fetchAndAddRelaxed(_q_value, -1) != 1;
}
#if 0
// These functions have no default implementation
// Archictectures must implement them
static inline bool isTestAndSetNative();
static inline bool isTestAndSetWaitFree();
template <typename T> static inline
bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue);
#endif
template <typename T> static inline always_inline
bool testAndSetAcquire(T &_q_value, T expectedValue, T newValue)
{
bool tmp = BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue);
BaseClass::acquireMemoryFence();
return tmp;
}
template <typename T> static inline always_inline
bool testAndSetRelease(T &_q_value, T expectedValue, T newValue)
{
BaseClass::releaseMemoryFence();
return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue);
}
template <typename T> static inline always_inline
bool testAndSetOrdered(T &_q_value, T expectedValue, T newValue)
{
BaseClass::orderedMemoryFence();
return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue);
}
static inline bool isFetchAndStoreNative() { return false; }
static inline bool isFetchAndStoreWaitFree() { return false; }
template <typename T> static inline always_inline
T fetchAndStoreRelaxed(T &_q_value, T newValue)
{
// implement fetchAndStore on top of testAndSet
forever {
register T tmp = load(_q_value);
if (BaseClass::testAndSetRelaxed(_q_value, tmp, newValue))
return tmp;
}
}
template <typename T> static inline always_inline
T fetchAndStoreAcquire(T &_q_value, T newValue)
{
T tmp = BaseClass::fetchAndStoreRelaxed(_q_value, newValue);
BaseClass::acquireMemoryFence();
return tmp;
}
template <typename T> static inline always_inline
T fetchAndStoreRelease(T &_q_value, T newValue)
{
BaseClass::releaseMemoryFence();
return BaseClass::fetchAndStoreRelaxed(_q_value, newValue);
}
template <typename T> static inline always_inline
T fetchAndStoreOrdered(T &_q_value, T newValue)
{
BaseClass::orderedMemoryFence();
return BaseClass::fetchAndStoreRelaxed(_q_value, newValue);
}
static inline bool isFetchAndAddNative() { return false; }
static inline bool isFetchAndAddWaitFree() { return false; }
template <typename T> static inline always_inline
T fetchAndAddRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd)
{
// implement fetchAndAdd on top of testAndSet
forever {
register T tmp = BaseClass::load(_q_value);
if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp + valueToAdd)))
return tmp;
}
}
template <typename T> static inline always_inline
T fetchAndAddAcquire(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd)
{
T tmp = BaseClass::fetchAndAddRelaxed(_q_value, valueToAdd);
BaseClass::acquireMemoryFence();
return tmp;
}
template <typename T> static inline always_inline
T fetchAndAddRelease(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd)
{
BaseClass::releaseMemoryFence();
return BaseClass::fetchAndAddRelaxed(_q_value, valueToAdd);
}
template <typename T> static inline always_inline
T fetchAndAddOrdered(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd)
{
BaseClass::orderedMemoryFence();
return BaseClass::fetchAndAddRelaxed(_q_value, valueToAdd);
}
};
#undef always_inline
QT_END_NAMESPACE
QT_END_HEADER
#endif // QGENERICATOMIC_H

View File

@ -39,8 +39,8 @@
**
****************************************************************************/
#ifndef QBASICATOMIC_H
#define QBASICATOMIC_H
#ifndef QOLDBASICATOMIC_H
#define QOLDBASICATOMIC_H
#include <QtCore/qglobal.h>
@ -50,6 +50,11 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Core)
#if 0
#pragma qt_no_master_include
#pragma qt_sync_stop_processing
#endif
class Q_CORE_EXPORT QBasicAtomicInt
{
public:

View File

@ -8,6 +8,8 @@ HEADERS += thread/qmutex.h \
thread/qthreadstorage.h \
thread/qwaitcondition.h \
thread/qatomic.h \
thread/qbasicatomic.h \
thread/qgenericatomic.h \
thread/qoldbasicatomic.h
# private headers