mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-04 16:25:27 +08:00
qt 6.5.1 original
This commit is contained in:
14
tests/auto/corelib/thread/qthreadonce/CMakeLists.txt
Normal file
14
tests/auto/corelib/thread/qthreadonce/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qthreadonce Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qthreadonce
|
||||
SOURCES
|
||||
qthreadonce.cpp
|
||||
tst_qthreadonce.cpp
|
||||
LIBRARIES
|
||||
Qt::TestPrivate
|
||||
)
|
80
tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
Normal file
80
tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include "qplatformdefs.h"
|
||||
#include "qthreadonce.h"
|
||||
|
||||
#include "qmutex.h"
|
||||
|
||||
Q_GLOBAL_STATIC(QRecursiveMutex, onceInitializationMutex)
|
||||
|
||||
enum QOnceExtra {
|
||||
MustRunCode = 0x01,
|
||||
MustUnlockMutex = 0x02
|
||||
};
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Initialize the Q_ONCE structure.
|
||||
|
||||
Q_ONCE consists of two variables:
|
||||
- a static POD QOnceControl::ControlVariable (it's a QBasicAtomicInt)
|
||||
- an automatic QOnceControl that controls the former
|
||||
|
||||
The POD is initialized to 0.
|
||||
|
||||
When QOnceControl's constructor starts, it'll lock the global
|
||||
initialization mutex. It'll then check if it's the first to up
|
||||
the control variable and will take note.
|
||||
|
||||
The QOnceControl's destructor will unlock the global
|
||||
initialization mutex.
|
||||
*/
|
||||
QOnceControl::QOnceControl(QBasicAtomicInt *control)
|
||||
{
|
||||
d = 0;
|
||||
gv = control;
|
||||
// check if code has already run once
|
||||
if (gv->loadAcquire() == 2) {
|
||||
// uncontended case: it has already initialized
|
||||
// no waiting
|
||||
return;
|
||||
}
|
||||
|
||||
// acquire the path
|
||||
onceInitializationMutex()->lock();
|
||||
extra = MustUnlockMutex;
|
||||
|
||||
if (gv->testAndSetAcquire(0, 1)) {
|
||||
// path acquired, we're the first
|
||||
extra |= MustRunCode;
|
||||
}
|
||||
}
|
||||
|
||||
QOnceControl::~QOnceControl()
|
||||
{
|
||||
if (mustRunCode())
|
||||
// code wasn't run!
|
||||
gv->testAndSetRelease(1, 0);
|
||||
else
|
||||
gv->testAndSetRelease(1, 2);
|
||||
if (extra & MustUnlockMutex)
|
||||
onceInitializationMutex()->unlock();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Returns true if the initialization code must be run.
|
||||
|
||||
Obviously, the initialization code must be run only once...
|
||||
*/
|
||||
bool QOnceControl::mustRunCode()
|
||||
{
|
||||
return extra & MustRunCode;
|
||||
}
|
||||
|
||||
void QOnceControl::done()
|
||||
{
|
||||
extra &= ~MustRunCode;
|
||||
}
|
67
tests/auto/corelib/thread/qthreadonce/qthreadonce.h
Normal file
67
tests/auto/corelib/thread/qthreadonce/qthreadonce.h
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#ifndef QTHREADONCE_H
|
||||
#define QTHREADONCE_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qatomic.h>
|
||||
|
||||
|
||||
class QOnceControl
|
||||
{
|
||||
public:
|
||||
QOnceControl(QBasicAtomicInt *);
|
||||
~QOnceControl();
|
||||
|
||||
bool mustRunCode();
|
||||
void done();
|
||||
|
||||
private:
|
||||
QBasicAtomicInt *gv;
|
||||
union {
|
||||
qint32 extra;
|
||||
void *d;
|
||||
};
|
||||
};
|
||||
|
||||
#define Q_ONCE_GV_NAME2(prefix, line) prefix ## line
|
||||
#define Q_ONCE_GV_NAME(prefix, line) Q_ONCE_GV_NAME2(prefix, line)
|
||||
#define Q_ONCE_GV Q_ONCE_GV_NAME(_q_once_, __LINE__)
|
||||
|
||||
#define Q_ONCE \
|
||||
static QBasicAtomicInt Q_ONCE_GV = Q_BASIC_ATOMIC_INITIALIZER(0); \
|
||||
if (0){} else \
|
||||
for (QOnceControl _control_(&Q_ONCE_GV); _control_.mustRunCode(); _control_.done())
|
||||
|
||||
template<typename T>
|
||||
class QSingleton
|
||||
{
|
||||
// this is a POD-like class
|
||||
struct Destructor
|
||||
{
|
||||
T *&pointer;
|
||||
Destructor(T *&ptr) : pointer(ptr) {}
|
||||
~Destructor() { delete pointer; }
|
||||
};
|
||||
|
||||
public:
|
||||
T *_q_value;
|
||||
QBasicAtomicInt _q_guard;
|
||||
|
||||
inline T *value()
|
||||
{
|
||||
for (QOnceControl control(&_q_guard); control.mustRunCode(); control.done()) {
|
||||
_q_value = new T();
|
||||
static Destructor cleanup(_q_value);
|
||||
}
|
||||
return _q_value;
|
||||
}
|
||||
|
||||
inline T& operator*() { return *value(); }
|
||||
inline T* operator->() { return value(); }
|
||||
inline operator T*() { return value(); }
|
||||
};
|
||||
|
||||
#endif
|
203
tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
Normal file
203
tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <QSemaphore>
|
||||
|
||||
#include <qcoreapplication.h>
|
||||
#include <qmutex.h>
|
||||
#include <qthread.h>
|
||||
#include <qwaitcondition.h>
|
||||
#include "qthreadonce.h"
|
||||
|
||||
#include <QtTest/private/qemulationdetector_p.h>
|
||||
|
||||
class tst_QThreadOnce : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void sameThread();
|
||||
void sameThread_data();
|
||||
void multipleThreads();
|
||||
|
||||
void nesting();
|
||||
void reentering();
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
void exception();
|
||||
#endif
|
||||
};
|
||||
|
||||
void tst_QThreadOnce::initTestCase()
|
||||
{
|
||||
if (QTestPrivate::isRunningArmOnX86())
|
||||
QSKIP("Flaky on QEMU, QTBUG-94737");
|
||||
}
|
||||
|
||||
class SingletonObject: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static int runCount;
|
||||
SingletonObject() { val.storeRelaxed(42); ++runCount; }
|
||||
~SingletonObject() { }
|
||||
|
||||
QBasicAtomicInt val;
|
||||
};
|
||||
|
||||
class IncrementThread: public QThread
|
||||
{
|
||||
public:
|
||||
static QBasicAtomicInt runCount;
|
||||
static QSingleton<SingletonObject> singleton;
|
||||
QSemaphore &sem1, &sem2;
|
||||
int &var;
|
||||
|
||||
IncrementThread(QSemaphore *psem1, QSemaphore *psem2, int *pvar, QObject *parent)
|
||||
: QThread(parent), sem1(*psem1), sem2(*psem2), var(*pvar)
|
||||
{ start(); }
|
||||
|
||||
~IncrementThread() { wait(); }
|
||||
|
||||
protected:
|
||||
void run() override
|
||||
{
|
||||
sem2.release();
|
||||
sem1.acquire(); // synchronize
|
||||
|
||||
Q_ONCE {
|
||||
++var;
|
||||
}
|
||||
runCount.ref();
|
||||
singleton->val.ref();
|
||||
}
|
||||
};
|
||||
int SingletonObject::runCount = 0;
|
||||
QBasicAtomicInt IncrementThread::runCount = Q_BASIC_ATOMIC_INITIALIZER(0);
|
||||
QSingleton<SingletonObject> IncrementThread::singleton;
|
||||
|
||||
void tst_QThreadOnce::sameThread_data()
|
||||
{
|
||||
SingletonObject::runCount = 0;
|
||||
QTest::addColumn<int>("expectedValue");
|
||||
|
||||
QTest::newRow("first") << 42;
|
||||
QTest::newRow("second") << 43;
|
||||
}
|
||||
|
||||
void tst_QThreadOnce::sameThread()
|
||||
{
|
||||
static int controlVariable = 0;
|
||||
Q_ONCE {
|
||||
QCOMPARE(controlVariable, 0);
|
||||
++controlVariable;
|
||||
}
|
||||
QCOMPARE(controlVariable, 1);
|
||||
|
||||
static QSingleton<SingletonObject> s;
|
||||
QTEST(int(s->val.loadRelaxed()), "expectedValue");
|
||||
s->val.ref();
|
||||
|
||||
QCOMPARE(SingletonObject::runCount, 1);
|
||||
}
|
||||
|
||||
void tst_QThreadOnce::multipleThreads()
|
||||
{
|
||||
#if defined(Q_OS_VXWORKS)
|
||||
const int NumberOfThreads = 20;
|
||||
#else
|
||||
const int NumberOfThreads = 100;
|
||||
#endif
|
||||
int controlVariable = 0;
|
||||
QSemaphore sem1, sem2(NumberOfThreads);
|
||||
|
||||
QObject *parent = new QObject;
|
||||
for (int i = 0; i < NumberOfThreads; ++i)
|
||||
new IncrementThread(&sem1, &sem2, &controlVariable, parent);
|
||||
|
||||
QCOMPARE(controlVariable, 0); // nothing must have set them yet
|
||||
SingletonObject::runCount = 0;
|
||||
IncrementThread::runCount.storeRelaxed(0);
|
||||
|
||||
// wait for all of them to be ready
|
||||
sem2.acquire(NumberOfThreads);
|
||||
// unleash the threads
|
||||
sem1.release(NumberOfThreads);
|
||||
|
||||
// wait for all of them to terminate:
|
||||
delete parent;
|
||||
|
||||
QCOMPARE(controlVariable, 1);
|
||||
QCOMPARE(int(IncrementThread::runCount.loadRelaxed()), NumberOfThreads);
|
||||
QCOMPARE(SingletonObject::runCount, 1);
|
||||
}
|
||||
|
||||
void tst_QThreadOnce::nesting()
|
||||
{
|
||||
int variable = 0;
|
||||
Q_ONCE {
|
||||
Q_ONCE {
|
||||
++variable;
|
||||
}
|
||||
}
|
||||
|
||||
QCOMPARE(variable, 1);
|
||||
}
|
||||
|
||||
static void reentrant(int control, int &counter)
|
||||
{
|
||||
Q_ONCE {
|
||||
if (counter)
|
||||
reentrant(--control, counter);
|
||||
++counter;
|
||||
}
|
||||
static QSingleton<SingletonObject> s;
|
||||
s->val.ref();
|
||||
}
|
||||
|
||||
void tst_QThreadOnce::reentering()
|
||||
{
|
||||
const int WantedRecursions = 5;
|
||||
int count = 0;
|
||||
SingletonObject::runCount = 0;
|
||||
reentrant(WantedRecursions, count);
|
||||
|
||||
// reentrancy is undefined behavior:
|
||||
QVERIFY(count == 1 || count == WantedRecursions);
|
||||
QCOMPARE(SingletonObject::runCount, 1);
|
||||
}
|
||||
|
||||
#if !defined(QT_NO_EXCEPTIONS)
|
||||
static void exception_helper(int &val)
|
||||
{
|
||||
Q_ONCE {
|
||||
if (val++ == 0) throw 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
void tst_QThreadOnce::exception()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
try {
|
||||
exception_helper(count);
|
||||
} catch (...) {
|
||||
// nothing
|
||||
}
|
||||
QCOMPARE(count, 1);
|
||||
|
||||
try {
|
||||
exception_helper(count);
|
||||
} catch (...) {
|
||||
QVERIFY2(false, "Exception shouldn't have been thrown...");
|
||||
}
|
||||
QCOMPARE(count, 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
QTEST_MAIN(tst_QThreadOnce)
|
||||
#include "tst_qthreadonce.moc"
|
Reference in New Issue
Block a user