Conflicts: src/plugins/platforms/cocoa/qnsview.mm src/plugins/platforms/cocoa/qnsview_dragging.mm src/plugins/platforms/ios/qiosinputcontext.mm src/plugins/platforms/xcb/qxcbconnection.cpp src/plugins/platforms/xcb/qxcbconnection_xi2.cpp src/plugins/platforms/xcb/qxcbwindow.cpp src/tools/androiddeployqt/main.cpp Was moved from qttools into qtbase in 5.11. So re-apply 32398e4d here. tests/auto/corelib/global/qlogging/test/test.pro tests/auto/corelib/global/qlogging/tst_qlogging.cpp tests/auto/corelib/io/qfile/tst_qfile.cpp tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp tests/auto/corelib/thread/qthreadstorage/test/test.pro tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp tests/auto/widgets/kernel/qapplication/test/test.pro Done-with: Gatis Paeglis <gatis.paeglis@qt.io> Done-with: Mårten Nordheim <marten.nordheim@qt.io> Done-with: Oliver Wolff <oliver.wolff@qt.io> Change-Id: Id970486c5315a1718c540f00deb2633533e8fc7b
651 lines
21 KiB
C++
651 lines
21 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2017 The Qt Company Ltd.
|
|
** Copyright (C) 2018 Intel Corporation.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the QtCore module of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 3 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 3 requirements
|
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 2.0 or (at your option) the GNU General
|
|
** Public license version 3 or any later version approved by the KDE Free
|
|
** Qt Foundation. The licenses are as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qsemaphore.h"
|
|
|
|
#ifndef QT_NO_THREAD
|
|
#include "qmutex.h"
|
|
#include "qfutex_p.h"
|
|
#include "qwaitcondition.h"
|
|
#include "qdeadlinetimer.h"
|
|
#include "qdatetime.h"
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
using namespace QtFutex;
|
|
|
|
/*!
|
|
\class QSemaphore
|
|
\inmodule QtCore
|
|
\brief The QSemaphore class provides a general counting semaphore.
|
|
|
|
\threadsafe
|
|
|
|
\ingroup thread
|
|
|
|
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
|
|
\li 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.
|
|
\li 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 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{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 QSemaphoreReleaser, QMutex, QWaitCondition, QThread, {Semaphores Example}
|
|
*/
|
|
|
|
/*
|
|
QSemaphore futex operation
|
|
|
|
QSemaphore stores a 32-bit integer with the counter of currently available
|
|
tokens (value between 0 and INT_MAX). When a thread attempts to acquire n
|
|
tokens and the counter is larger than that, we perform a compare-and-swap
|
|
with the new count. If that succeeds, the acquisition worked; if not, we
|
|
loop again because the counter changed. If there were not enough tokens,
|
|
we'll perform a futex-wait.
|
|
|
|
Before we do, we set the high bit in the futex to indicate that semaphore
|
|
is contended: that is, there's a thread waiting for more tokens. On
|
|
release() for n tokens, we perform a fetch-and-add of n and then check if
|
|
that high bit was set. If it was, then we clear that bit and perform a
|
|
futex-wake on the semaphore to indicate the waiting threads can wake up and
|
|
acquire tokens. Which ones get woken up is unspecified.
|
|
|
|
If the system has the ability to wake up a precise number of threads, has
|
|
Linux's FUTEX_WAKE_OP functionality, and is 64-bit, instead of using a
|
|
single bit indicating a contended semaphore, we'll store the number of
|
|
tokens *plus* total number of waiters in the high word. Additionally, all
|
|
multi-token waiters will be waiting on that high word. So when releasing n
|
|
tokens on those systems, we tell the kernel to wake up n single-token
|
|
threads and all of the multi-token ones. Which threads get woken up is
|
|
unspecified, but it's likely single-token threads will get woken up first.
|
|
*/
|
|
|
|
#if defined(FUTEX_OP) && QT_POINTER_SIZE > 4
|
|
static Q_CONSTEXPR bool futexHasWaiterCount = true;
|
|
#else
|
|
static Q_CONSTEXPR bool futexHasWaiterCount = false;
|
|
#endif
|
|
|
|
static const quintptr futexNeedsWakeAllBit =
|
|
Q_UINT64_C(1) << (sizeof(quintptr) * CHAR_BIT - 1);
|
|
|
|
static int futexAvailCounter(quintptr v)
|
|
{
|
|
// the low 31 bits
|
|
if (futexHasWaiterCount) {
|
|
// the high bit of the low word isn't used
|
|
Q_ASSERT((v & 0x80000000U) == 0);
|
|
|
|
// so we can be a little faster
|
|
return int(unsigned(v));
|
|
}
|
|
return int(v & 0x7fffffffU);
|
|
}
|
|
|
|
static bool futexNeedsWake(quintptr v)
|
|
{
|
|
// If we're counting waiters, the number of waiters is stored in the low 31
|
|
// bits of the high word (that is, bits 32-62). If we're not, then we use
|
|
// bit 31 to indicate anyone is waiting. Either way, if any bit 31 or above
|
|
// is set, there are waiters.
|
|
return v >> 31;
|
|
}
|
|
|
|
static QBasicAtomicInteger<quint32> *futexLow32(QBasicAtomicInteger<quintptr> *ptr)
|
|
{
|
|
auto result = reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
|
|
#if Q_BYTE_ORDER == Q_BIG_ENDIAN && QT_POINTER_SIZE > 4
|
|
++result;
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
static QBasicAtomicInteger<quint32> *futexHigh32(QBasicAtomicInteger<quintptr> *ptr)
|
|
{
|
|
auto result = reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
|
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN && QT_POINTER_SIZE > 4
|
|
++result;
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
template <bool IsTimed> bool
|
|
futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValue, quintptr nn, int timeout)
|
|
{
|
|
QDeadlineTimer timer(IsTimed ? QDeadlineTimer(timeout) : QDeadlineTimer());
|
|
qint64 remainingTime = timeout * Q_INT64_C(1000) * 1000;
|
|
int n = int(unsigned(nn));
|
|
|
|
// we're called after one testAndSet, so start by waiting first
|
|
goto start_wait;
|
|
|
|
forever {
|
|
if (futexAvailCounter(curValue) >= n) {
|
|
// try to acquire
|
|
quintptr newValue = curValue - nn;
|
|
if (u.testAndSetOrdered(curValue, newValue, curValue))
|
|
return true; // succeeded!
|
|
continue;
|
|
}
|
|
|
|
// not enough tokens available, put us to wait
|
|
if (remainingTime == 0)
|
|
return false;
|
|
|
|
// indicate we're waiting
|
|
start_wait:
|
|
auto ptr = futexLow32(&u);
|
|
if (n > 1 || !futexHasWaiterCount) {
|
|
u.fetchAndOrRelaxed(futexNeedsWakeAllBit);
|
|
curValue |= futexNeedsWakeAllBit;
|
|
if (n > 1 && futexHasWaiterCount) {
|
|
ptr = futexHigh32(&u);
|
|
//curValue >>= 32; // but this is UB in 32-bit, so roundabout:
|
|
curValue = quint64(curValue) >> 32;
|
|
}
|
|
}
|
|
|
|
if (IsTimed && remainingTime > 0) {
|
|
bool timedout = !futexWait(*ptr, curValue, remainingTime);
|
|
if (timedout)
|
|
return false;
|
|
} else {
|
|
futexWait(*ptr, curValue);
|
|
}
|
|
|
|
curValue = u.loadAcquire();
|
|
if (IsTimed)
|
|
remainingTime = timer.remainingTimeNSecs();
|
|
}
|
|
}
|
|
|
|
template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u, int n, int timeout)
|
|
{
|
|
// Try to acquire without waiting (we still loop because the testAndSet
|
|
// call can fail).
|
|
quintptr nn = unsigned(n);
|
|
if (futexHasWaiterCount)
|
|
nn |= quint64(nn) << 32; // token count replicated in high word
|
|
|
|
quintptr curValue = u.loadAcquire();
|
|
while (futexAvailCounter(curValue) >= n) {
|
|
// try to acquire
|
|
quintptr newValue = curValue - nn;
|
|
if (u.testAndSetOrdered(curValue, newValue, curValue))
|
|
return true; // succeeded!
|
|
}
|
|
if (timeout == 0)
|
|
return false;
|
|
|
|
// we need to wait
|
|
quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32); // zero on 32-bit
|
|
if (futexHasWaiterCount) {
|
|
// increase the waiter count
|
|
u.fetchAndAddRelaxed(oneWaiter);
|
|
|
|
// We don't use the fetched value from above so futexWait() fails if
|
|
// it changed after the testAndSetOrdered above.
|
|
if ((quint64(curValue) >> 32) == 0x7fffffff)
|
|
return false; // overflow!
|
|
curValue += oneWaiter;
|
|
|
|
// Also adjust nn to subtract oneWaiter when we succeed in acquiring.
|
|
nn += oneWaiter;
|
|
}
|
|
|
|
if (futexSemaphoreTryAcquire_loop<IsTimed>(u, curValue, nn, timeout))
|
|
return true;
|
|
|
|
if (futexHasWaiterCount) {
|
|
// decrement the number of threads waiting
|
|
Q_ASSERT(futexHigh32(&u)->load() & 0x7fffffffU);
|
|
u.fetchAndSubRelaxed(oneWaiter);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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");
|
|
if (futexAvailable()) {
|
|
quintptr nn = unsigned(n);
|
|
if (futexHasWaiterCount)
|
|
nn |= quint64(nn) << 32; // token count replicated in high word
|
|
u.store(nn);
|
|
} else {
|
|
d = new QSemaphorePrivate(n);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Destroys the semaphore.
|
|
|
|
\warning Destroying a semaphore that is in use may result in
|
|
undefined behavior.
|
|
*/
|
|
QSemaphore::~QSemaphore()
|
|
{
|
|
if (!futexAvailable())
|
|
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");
|
|
|
|
if (futexAvailable()) {
|
|
futexSemaphoreTryAcquire<false>(u, n, -1);
|
|
return;
|
|
}
|
|
|
|
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 code/src_corelib_thread_qsemaphore.cpp 1
|
|
|
|
QSemaphoreReleaser is a \l{http://en.cppreference.com/w/cpp/language/raii}{RAII}
|
|
wrapper around this function.
|
|
|
|
\sa acquire(), available(), QSemaphoreReleaser
|
|
*/
|
|
void QSemaphore::release(int n)
|
|
{
|
|
Q_ASSERT_X(n >= 0, "QSemaphore::release", "parameter 'n' must be non-negative");
|
|
|
|
if (futexAvailable()) {
|
|
quintptr nn = unsigned(n);
|
|
if (futexHasWaiterCount)
|
|
nn |= quint64(nn) << 32; // token count replicated in high word
|
|
quintptr prevValue = u.fetchAndAddRelease(nn);
|
|
if (futexNeedsWake(prevValue)) {
|
|
#ifdef FUTEX_OP
|
|
if (!futexHasWaiterCount) {
|
|
/*
|
|
On 32-bit systems, all waiters are waiting on the same address,
|
|
so we'll wake them all and ask the kernel to clear the high bit.
|
|
|
|
atomic {
|
|
int oldval = u;
|
|
u = oldval & ~(1 << 31);
|
|
futexWake(u, INT_MAX);
|
|
if (oldval == 0) // impossible condition
|
|
futexWake(u, INT_MAX);
|
|
}
|
|
*/
|
|
quint32 op = FUTEX_OP_ANDN | FUTEX_OP_OPARG_SHIFT;
|
|
quint32 oparg = 31;
|
|
quint32 cmp = FUTEX_OP_CMP_EQ;
|
|
quint32 cmparg = 0;
|
|
futexWakeOp(u, INT_MAX, INT_MAX, u, FUTEX_OP(op, oparg, cmp, cmparg));
|
|
} else {
|
|
/*
|
|
On 64-bit systems, the single-token waiters wait on the low half
|
|
and the multi-token waiters wait on the upper half. So we ask
|
|
the kernel to wake up n single-token waiters and all multi-token
|
|
waiters (if any), then clear the multi-token wait bit.
|
|
|
|
atomic {
|
|
int oldval = *upper;
|
|
*upper = oldval & ~(1 << 31);
|
|
futexWake(lower, n);
|
|
if (oldval < 0) // sign bit set
|
|
futexWake(upper, INT_MAX);
|
|
}
|
|
*/
|
|
quint32 op = FUTEX_OP_ANDN | FUTEX_OP_OPARG_SHIFT;
|
|
quint32 oparg = 31;
|
|
quint32 cmp = FUTEX_OP_CMP_LT;
|
|
quint32 cmparg = 0;
|
|
futexWakeOp(*futexLow32(&u), n, INT_MAX, *futexHigh32(&u), FUTEX_OP(op, oparg, cmp, cmparg));
|
|
}
|
|
#else
|
|
// Unset the bit and wake everyone. There are two possibibilies
|
|
// under which a thread can set the bit between the AND and the
|
|
// futexWake:
|
|
// 1) it did see the new counter value, but it wasn't enough for
|
|
// its acquisition anyway, so it has to wait;
|
|
// 2) it did not see the new counter value, in which case its
|
|
// futexWait will fail.
|
|
u.fetchAndAndRelease(futexNeedsWakeAllBit - 1);
|
|
futexWakeAll(u);
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
|
|
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
|
|
{
|
|
if (futexAvailable())
|
|
return futexAvailCounter(u.load());
|
|
|
|
QMutexLocker locker(&d->mutex);
|
|
return d->avail;
|
|
}
|
|
|
|
/*!
|
|
Tries to acquire \c n resources guarded by the semaphore and
|
|
returns \c true on success. If available() < \a n, this call
|
|
immediately returns \c false without acquiring any resources.
|
|
|
|
Example:
|
|
|
|
\snippet 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");
|
|
|
|
if (futexAvailable())
|
|
return futexSemaphoreTryAcquire<false>(u, n, 0);
|
|
|
|
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 \c 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 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");
|
|
|
|
// We're documented to accept any negative value as "forever"
|
|
// but QDeadlineTimer only accepts -1.
|
|
timeout = qMax(timeout, -1);
|
|
|
|
if (futexAvailable())
|
|
return futexSemaphoreTryAcquire<true>(u, n, timeout);
|
|
|
|
QDeadlineTimer timer(timeout);
|
|
QMutexLocker locker(&d->mutex);
|
|
while (n > d->avail && !timer.hasExpired()) {
|
|
if (!d->cond.wait(locker.mutex(), timer))
|
|
return false;
|
|
}
|
|
if (n > d->avail)
|
|
return false;
|
|
d->avail -= n;
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
/*!
|
|
\class QSemaphoreReleaser
|
|
\brief The QSemaphoreReleaser class provides exception-safe deferral of a QSemaphore::release() call.
|
|
\since 5.10
|
|
\ingroup thread
|
|
\inmodule QtCore
|
|
|
|
\reentrant
|
|
|
|
QSemaphoreReleaser can be used wherever you would otherwise use
|
|
QSemaphore::release(). Constructing a QSemaphoreReleaser defers the
|
|
release() call on the semaphore until the QSemaphoreReleaser is
|
|
destroyed (see
|
|
\l{http://en.cppreference.com/w/cpp/language/raii}{RAII pattern}).
|
|
|
|
You can use this to reliably release a semaphore to avoid dead-lock
|
|
in the face of exceptions or early returns:
|
|
|
|
\code
|
|
// ... do something that may throw or return early
|
|
sem.release();
|
|
\endcode
|
|
|
|
If an early return is taken or an exception is thrown before the
|
|
\c{sem.release()} call is reached, the semaphore is not released,
|
|
possibly preventing the thread waiting in the corresponding
|
|
\c{sem.acquire()} call from ever continuing execution.
|
|
|
|
When using RAII instead:
|
|
|
|
\code
|
|
const QSemaphoreReleaser releaser(sem);
|
|
// ... do something that may throw or early return
|
|
// implicitly calls sem.release() here and at every other return in between
|
|
\endcode
|
|
|
|
this can no longer happen, because the compiler will make sure that
|
|
the QSemaphoreReleaser destructor is always called, and therefore
|
|
the semaphore is always released.
|
|
|
|
QSemaphoreReleaser is move-enabled and can therefore be returned
|
|
from functions to transfer responsibility for releasing a semaphore
|
|
out of a function or a scope:
|
|
|
|
\code
|
|
{ // some scope
|
|
QSemaphoreReleaser releaser; // does nothing
|
|
// ...
|
|
if (someCondition) {
|
|
releaser = QSemaphoreReleaser(sem);
|
|
// ...
|
|
}
|
|
// ...
|
|
} // conditionally calls sem.release(), depending on someCondition
|
|
\endcode
|
|
|
|
A QSemaphoreReleaser can be canceled by a call to cancel(). A canceled
|
|
semaphore releaser will no longer call QSemaphore::release() in its
|
|
destructor.
|
|
|
|
\sa QMutexLocker
|
|
*/
|
|
|
|
/*!
|
|
\fn QSemaphoreReleaser::QSemaphoreReleaser()
|
|
|
|
Default constructor. Creates a QSemaphoreReleaser that does nothing.
|
|
*/
|
|
|
|
/*!
|
|
\fn QSemaphoreReleaser::QSemaphoreReleaser(QSemaphore &sem, int n)
|
|
|
|
Constructor. Stores the arguments and calls \a{sem}.release(\a{n})
|
|
in the destructor.
|
|
*/
|
|
|
|
/*!
|
|
\fn QSemaphoreReleaser::QSemaphoreReleaser(QSemaphore *sem, int n)
|
|
|
|
Constructor. Stores the arguments and calls \a{sem}->release(\a{n})
|
|
in the destructor.
|
|
*/
|
|
|
|
/*!
|
|
\fn QSemaphoreReleaser::QSemaphoreReleaser(QSemaphoreReleaser &&other)
|
|
|
|
Move constructor. Takes over responsibility to call QSemaphore::release()
|
|
from \a other, which in turn is canceled.
|
|
|
|
\sa cancel()
|
|
*/
|
|
|
|
/*!
|
|
\fn QSemaphoreReleaser::operator=(QSemaphoreReleaser &&other)
|
|
|
|
Move assignment operator. Takes over responsibility to call QSemaphore::release()
|
|
from \a other, which in turn is canceled.
|
|
|
|
If this semaphore releaser had the responsibility to call some QSemaphore::release()
|
|
itself, it performs the call before taking over from \a other.
|
|
|
|
\sa cancel()
|
|
*/
|
|
|
|
/*!
|
|
\fn QSemaphoreReleaser::~QSemaphoreReleaser()
|
|
|
|
Unless canceled, calls QSemaphore::release() with the arguments provided
|
|
to the constructor, or by the last move assignment.
|
|
*/
|
|
|
|
/*!
|
|
\fn QSemaphoreReleaser::swap(QSemaphoreReleaser &other)
|
|
|
|
Exchanges the responsibilites of \c{*this} and \a other.
|
|
|
|
Unlike move assignment, neither of the two objects ever releases its
|
|
semaphore, if any, as a consequence of swapping.
|
|
|
|
Therefore this function is very fast and never fails.
|
|
*/
|
|
|
|
/*!
|
|
\fn QSemaphoreReleaser::semaphore() const
|
|
|
|
Returns a pointer to the QSemaphore object provided to the constructor,
|
|
or by the last move assignment, if any. Otherwise, returns \c nullptr.
|
|
*/
|
|
|
|
/*!
|
|
\fn QSemaphoreReleaser::cancel()
|
|
|
|
Cancels this QSemaphoreReleaser such that the destructor will no longer
|
|
call \c{semaphore()->release()}. Returns the value of semaphore()
|
|
before this call. After this call, semaphore() will return \c nullptr.
|
|
|
|
To enable again, assign a new QSemaphoreReleaser:
|
|
|
|
\code
|
|
releaser.cancel(); // avoid releasing old semaphore()
|
|
releaser = QSemaphoreReleaser(sem, 42);
|
|
// now will call sem.release(42) when 'releaser' is destroyed
|
|
\endcode
|
|
*/
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#endif // QT_NO_THREAD
|