qt 6.6.1 original sources files (to be patched)

This commit is contained in:
kleuter
2023-12-06 14:34:14 +01:00
parent db7d18b23c
commit b752f623ec
20 changed files with 26161 additions and 0 deletions

View File

@ -0,0 +1,242 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qstandardpaths.h"
#include <qdir.h>
#include <qstringlist.h>
#ifndef QT_BOOTSTRAPPED
#include <qcoreapplication.h>
#endif
#include <qt_windows.h>
#include <shlobj.h>
#include <intshcut.h>
#include <qvarlengtharray.h>
#ifndef QT_NO_STANDARDPATHS
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
static QString convertCharArray(const wchar_t *path)
{
return QDir::fromNativeSeparators(QString::fromWCharArray(path));
}
static inline bool isGenericConfigLocation(QStandardPaths::StandardLocation type)
{
return type == QStandardPaths::GenericConfigLocation || type == QStandardPaths::GenericDataLocation;
}
static inline bool isConfigLocation(QStandardPaths::StandardLocation type)
{
return type == QStandardPaths::ConfigLocation || type == QStandardPaths::AppConfigLocation
|| type == QStandardPaths::AppDataLocation || type == QStandardPaths::AppLocalDataLocation
|| isGenericConfigLocation(type);
}
static void appendOrganizationAndApp(QString &path) // Courtesy qstandardpaths_unix.cpp
{
#ifndef QT_BOOTSTRAPPED
const QString &org = QCoreApplication::organizationName();
if (!org.isEmpty())
path += u'/' + org;
const QString &appName = QCoreApplication::applicationName();
if (!appName.isEmpty())
path += u'/' + appName;
#else // !QT_BOOTSTRAPPED
Q_UNUSED(path);
#endif
}
static inline void appendTestMode(QString &path)
{
if (QStandardPaths::isTestModeEnabled())
path += "/qttest"_L1;
}
static bool isProcessLowIntegrity()
{
// same as GetCurrentProcessToken()
const auto process_token = HANDLE(quintptr(-4));
QVarLengthArray<char,256> token_info_buf(256);
auto* token_info = reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_info_buf.data());
DWORD token_info_length = token_info_buf.size();
if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_info, token_info_length, &token_info_length)) {
// grow buffer and retry GetTokenInformation
token_info_buf.resize(token_info_length);
token_info = reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_info_buf.data());
if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_info, token_info_length, &token_info_length))
return false; // assume "normal" process
}
// The GetSidSubAuthorityCount return-code is undefined on failure, so
// there's no point in checking before dereferencing
DWORD integrity_level = *GetSidSubAuthority(token_info->Label.Sid, *GetSidSubAuthorityCount(token_info->Label.Sid) - 1);
return (integrity_level < SECURITY_MANDATORY_MEDIUM_RID);
}
// Map QStandardPaths::StandardLocation to KNOWNFOLDERID of SHGetKnownFolderPath()
static GUID writableSpecialFolderId(QStandardPaths::StandardLocation type)
{
// folders for medium & high integrity processes
static const GUID folderIds[] = {
FOLDERID_Desktop, // DesktopLocation
FOLDERID_Documents, // DocumentsLocation
FOLDERID_Fonts, // FontsLocation
FOLDERID_Programs, // ApplicationsLocation
FOLDERID_Music, // MusicLocation
FOLDERID_Videos, // MoviesLocation
FOLDERID_Pictures, // PicturesLocation
GUID(), GUID(), // TempLocation/HomeLocation
FOLDERID_LocalAppData, // AppLocalDataLocation ("Local" path)
GUID(), // CacheLocation
FOLDERID_LocalAppData, // GenericDataLocation ("Local" path)
GUID(), // RuntimeLocation
FOLDERID_LocalAppData, // ConfigLocation ("Local" path)
FOLDERID_Downloads, // DownloadLocation
GUID(), // GenericCacheLocation
FOLDERID_LocalAppData, // GenericConfigLocation ("Local" path)
FOLDERID_RoamingAppData,// AppDataLocation ("Roaming" path)
FOLDERID_LocalAppData, // AppConfigLocation ("Local" path)
FOLDERID_Public, // PublicShareLocation
FOLDERID_Templates, // TemplatesLocation
};
static_assert(sizeof(folderIds) / sizeof(folderIds[0]) == size_t(QStandardPaths::TemplatesLocation + 1));
// folders for low integrity processes
static const GUID folderIds_li[] = {
FOLDERID_Desktop, // DesktopLocation
FOLDERID_Documents, // DocumentsLocation
FOLDERID_Fonts, // FontsLocation
FOLDERID_Programs, // ApplicationsLocation
FOLDERID_Music, // MusicLocation
FOLDERID_Videos, // MoviesLocation
FOLDERID_Pictures, // PicturesLocation
GUID(), GUID(), // TempLocation/HomeLocation
FOLDERID_LocalAppDataLow,// AppLocalDataLocation ("Local" path)
GUID(), // CacheLocation
FOLDERID_LocalAppDataLow,// GenericDataLocation ("Local" path)
GUID(), // RuntimeLocation
FOLDERID_LocalAppDataLow,// ConfigLocation ("Local" path)
FOLDERID_Downloads, // DownloadLocation
GUID(), // GenericCacheLocation
FOLDERID_LocalAppDataLow,// GenericConfigLocation ("Local" path)
FOLDERID_RoamingAppData, // AppDataLocation ("Roaming" path)
FOLDERID_LocalAppDataLow,// AppConfigLocation ("Local" path)
FOLDERID_Public, // PublicShareLocation
FOLDERID_Templates, // TemplatesLocation
};
static_assert(sizeof(folderIds_li) == sizeof(folderIds));
static bool low_integrity_process = isProcessLowIntegrity();
if (size_t(type) < sizeof(folderIds) / sizeof(folderIds[0]))
return low_integrity_process ? folderIds_li[type] : folderIds[type];
return GUID();
}
// Convenience for SHGetKnownFolderPath().
static QString sHGetKnownFolderPath(const GUID &clsid)
{
QString result;
LPWSTR path;
if (Q_LIKELY(SUCCEEDED(SHGetKnownFolderPath(clsid, KF_FLAG_DONT_VERIFY, 0, &path)))) {
result = convertCharArray(path);
CoTaskMemFree(path);
}
return result;
}
QString QStandardPaths::writableLocation(StandardLocation type)
{
QString result;
switch (type) {
case CacheLocation:
// Although Microsoft has a Cache key it is a pointer to IE's cache, not a cache
// location for everyone. Most applications seem to be using a
// cache directory located in their AppData directory
result = sHGetKnownFolderPath(writableSpecialFolderId(AppLocalDataLocation));
if (!result.isEmpty()) {
appendTestMode(result);
appendOrganizationAndApp(result);
result += "/cache"_L1;
}
break;
case GenericCacheLocation:
result = sHGetKnownFolderPath(writableSpecialFolderId(GenericDataLocation));
if (!result.isEmpty()) {
appendTestMode(result);
result += "/cache"_L1;
}
break;
case RuntimeLocation:
case HomeLocation:
result = QDir::homePath();
break;
case TempLocation:
result = QDir::tempPath();
break;
default:
result = sHGetKnownFolderPath(writableSpecialFolderId(type));
if (!result.isEmpty() && isConfigLocation(type)) {
appendTestMode(result);
if (!isGenericConfigLocation(type))
appendOrganizationAndApp(result);
}
break;
}
return result;
}
#ifndef QT_BOOTSTRAPPED
extern QString qAppFileName();
#endif
QStringList QStandardPaths::standardLocations(StandardLocation type)
{
QStringList dirs;
const QString localDir = writableLocation(type);
if (!localDir.isEmpty())
dirs.append(localDir);
// type-specific handling goes here
if (isConfigLocation(type)) {
QString programData = sHGetKnownFolderPath(FOLDERID_ProgramData);
if (!programData.isEmpty()) {
if (!isGenericConfigLocation(type))
appendOrganizationAndApp(programData);
dirs.append(programData);
}
#ifndef QT_BOOTSTRAPPED
// Note: QCoreApplication::applicationDirPath(), while static, requires
// an application instance. But we might need to resolve the standard
// locations earlier than that, so we fall back to qAppFileName().
QString applicationDirPath = qApp ? QCoreApplication::applicationDirPath()
: QFileInfo(qAppFileName()).path();
dirs.append(applicationDirPath);
const QString dataDir = applicationDirPath + "/data"_L1;
dirs.append(dataDir);
if (!isGenericConfigLocation(type)) {
QString appDataDir = dataDir;
appendOrganizationAndApp(appDataDir);
if (appDataDir != dataDir)
dirs.append(appDataDir);
}
#endif // !QT_BOOTSTRAPPED
} // isConfigLocation()
return dirs;
}
QT_END_NAMESPACE
#endif // QT_NO_STANDARDPATHS

View File

@ -0,0 +1,908 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qeventdispatcher_win_p.h"
#include "qcoreapplication.h"
#include <private/qsystemlibrary_p.h>
#include "qoperatingsystemversion.h"
#include "qpair.h"
#include "qset.h"
#include "qsocketnotifier.h"
#include "qvarlengtharray.h"
#include "qelapsedtimer.h"
#include "qcoreapplication_p.h"
#include <private/qthread_p.h>
QT_BEGIN_NAMESPACE
#ifndef TIME_KILL_SYNCHRONOUS
# define TIME_KILL_SYNCHRONOUS 0x0100
#endif
#ifndef QS_RAWINPUT
# define QS_RAWINPUT 0x0400
#endif
#ifndef WM_TOUCH
# define WM_TOUCH 0x0240
#endif
#ifndef QT_NO_GESTURES
#ifndef WM_GESTURE
# define WM_GESTURE 0x0119
#endif
#ifndef WM_GESTURENOTIFY
# define WM_GESTURENOTIFY 0x011A
#endif
#endif // QT_NO_GESTURES
enum {
WM_QT_SOCKETNOTIFIER = WM_USER,
WM_QT_SENDPOSTEDEVENTS = WM_USER + 1,
WM_QT_ACTIVATENOTIFIERS = WM_USER + 2
};
enum {
SendPostedEventsTimerId = ~1u
};
class QEventDispatcherWin32Private;
#if !defined(DWORD_PTR) && !defined(Q_OS_WIN64)
#define DWORD_PTR DWORD
#endif
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
static quint64 qt_msectime()
{
using namespace std::chrono;
auto t = duration_cast<milliseconds>(steady_clock::now().time_since_epoch());
return t.count();
}
QEventDispatcherWin32Private::QEventDispatcherWin32Private()
: interrupt(false), internalHwnd(0),
sendPostedEventsTimerId(0), wakeUps(0),
activateNotifiersPosted(false)
{
}
QEventDispatcherWin32Private::~QEventDispatcherWin32Private()
{
if (internalHwnd)
DestroyWindow(internalHwnd);
}
// This function is called by a workerthread
void WINAPI QT_WIN_CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/, DWORD_PTR user, DWORD_PTR /*reserved*/, DWORD_PTR /*reserved*/)
{
if (!timerId) // sanity check
return;
auto t = reinterpret_cast<WinTimerInfo*>(user);
Q_ASSERT(t);
QCoreApplication::postEvent(t->dispatcher, new QTimerEvent(t->timerId));
}
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
if (message == WM_NCCREATE)
return true;
MSG msg;
msg.hwnd = hwnd;
msg.message = message;
msg.wParam = wp;
msg.lParam = lp;
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
qintptr result;
if (!dispatcher) {
if (message == WM_TIMER)
KillTimer(hwnd, wp);
return 0;
}
if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result))
return result;
auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
QEventDispatcherWin32Private *d = nullptr;
if (q != nullptr)
d = q->d_func();
switch (message) {
case WM_QT_SOCKETNOTIFIER: {
// socket notifier message
int type = -1;
switch (WSAGETSELECTEVENT(lp)) {
case FD_READ:
case FD_ACCEPT:
type = 0;
break;
case FD_WRITE:
case FD_CONNECT:
type = 1;
break;
case FD_OOB:
type = 2;
break;
case FD_CLOSE:
type = 3;
break;
}
if (type >= 0) {
Q_ASSERT(d != nullptr);
QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read };
QSNDict *dict = sn_vec[type];
QSockNot *sn = dict ? dict->value(wp) : 0;
if (sn == nullptr) {
d->postActivateSocketNotifiers();
} else {
Q_ASSERT(d->active_fd.contains(sn->fd));
QSockFd &sd = d->active_fd[sn->fd];
if (sd.selected) {
Q_ASSERT(sd.mask == 0);
d->doWsaAsyncSelect(sn->fd, 0);
sd.selected = false;
}
d->postActivateSocketNotifiers();
// Ignore the message if a notification with the same type was
// received previously. Suppressed message is definitely spurious.
const long eventCode = WSAGETSELECTEVENT(lp);
if ((sd.mask & eventCode) != eventCode) {
sd.mask |= eventCode;
QEvent event(type < 3 ? QEvent::SockAct : QEvent::SockClose);
QCoreApplication::sendEvent(sn->obj, &event);
}
}
}
return 0;
}
case WM_QT_ACTIVATENOTIFIERS: {
Q_ASSERT(d != nullptr);
// Postpone activation if we have unhandled socket notifier messages
// in the queue. WM_QT_ACTIVATENOTIFIERS will be posted again as a result of
// event processing.
MSG msg;
if (!PeekMessage(&msg, d->internalHwnd,
WM_QT_SOCKETNOTIFIER, WM_QT_SOCKETNOTIFIER, PM_NOREMOVE)
&& d->queuedSocketEvents.isEmpty()) {
// register all socket notifiers
for (QSFDict::iterator it = d->active_fd.begin(), end = d->active_fd.end();
it != end; ++it) {
QSockFd &sd = it.value();
if (!sd.selected) {
d->doWsaAsyncSelect(it.key(), sd.event);
// allow any event to be accepted
sd.mask = 0;
sd.selected = true;
}
}
}
d->activateNotifiersPosted = false;
return 0;
}
case WM_TIMER:
Q_ASSERT(d != nullptr);
if (wp == d->sendPostedEventsTimerId)
q->sendPostedEvents();
else
d->sendTimerEvent(wp);
return 0;
case WM_QT_SENDPOSTEDEVENTS:
Q_ASSERT(d != nullptr);
// We send posted events manually, if the window procedure was invoked
// by the foreign event loop (e.g. from the native modal dialog).
// Skip sending, if the message queue is not empty.
// sendPostedEventsTimer will deliver posted events later.
static const UINT mask = QS_ALLEVENTS;
if (HIWORD(GetQueueStatus(mask)) == 0)
q->sendPostedEvents();
else
d->startPostedEventsTimer();
return 0;
} // switch (message)
return DefWindowProc(hwnd, message, wp, lp);
}
void QEventDispatcherWin32Private::startPostedEventsTimer()
{
// we received WM_QT_SENDPOSTEDEVENTS, so allow posting it again
wakeUps.storeRelaxed(0);
if (sendPostedEventsTimerId == 0) {
// Start a timer to deliver posted events when the message queue is emptied.
sendPostedEventsTimerId = SetTimer(internalHwnd, SendPostedEventsTimerId,
USER_TIMER_MINIMUM, NULL);
}
}
// Provide class name and atom for the message window used by
// QEventDispatcherWin32Private via Q_GLOBAL_STATIC shared between threads.
struct QWindowsMessageWindowClassContext
{
QWindowsMessageWindowClassContext();
~QWindowsMessageWindowClassContext();
ATOM atom;
wchar_t *className;
};
QWindowsMessageWindowClassContext::QWindowsMessageWindowClassContext()
: atom(0), className(0)
{
// make sure that multiple Qt's can coexist in the same process
const QString qClassName = QStringLiteral("QEventDispatcherWin32_Internal_Widget")
+ QString::number(quintptr(qt_internal_proc));
className = new wchar_t[qClassName.size() + 1];
qClassName.toWCharArray(className);
className[qClassName.size()] = 0;
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = qt_internal_proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(0);
wc.hIcon = 0;
wc.hCursor = 0;
wc.hbrBackground = 0;
wc.lpszMenuName = NULL;
wc.lpszClassName = className;
atom = RegisterClass(&wc);
if (!atom) {
qErrnoWarning("%ls RegisterClass() failed", qUtf16Printable(qClassName));
delete [] className;
className = 0;
}
}
QWindowsMessageWindowClassContext::~QWindowsMessageWindowClassContext()
{
if (className) {
UnregisterClass(className, GetModuleHandle(0));
delete [] className;
}
}
Q_GLOBAL_STATIC(QWindowsMessageWindowClassContext, qWindowsMessageWindowClassContext)
static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
{
QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext();
if (!ctx->atom)
return 0;
HWND wnd = CreateWindow(ctx->className, // classname
ctx->className, // window name
0, // style
0, 0, 0, 0, // geometry
HWND_MESSAGE, // parent
0, // menu handle
GetModuleHandle(0), // application
0); // windows creation data.
if (!wnd) {
qErrnoWarning("CreateWindow() for QEventDispatcherWin32 internal window failed");
return 0;
}
SetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher));
return wnd;
}
static ULONG calculateNextTimeout(WinTimerInfo *t, quint64 currentTime)
{
uint interval = t->interval;
ULONG tolerance = TIMERV_DEFAULT_COALESCING;
switch (t->timerType) {
case Qt::PreciseTimer:
// high precision timer is based on millisecond precision
// so no adjustment is necessary
break;
case Qt::CoarseTimer:
// this timer has up to 5% coarseness
// so our boundaries are 20 ms and 20 s
// below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
// above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
if (interval >= 20000) {
t->timerType = Qt::VeryCoarseTimer;
} else if (interval <= 20) {
// no adjustment necessary
t->timerType = Qt::PreciseTimer;
break;
} else {
tolerance = interval / 20;
break;
}
Q_FALLTHROUGH();
case Qt::VeryCoarseTimer:
// the very coarse timer is based on full second precision,
// so we round to closest second (but never to zero)
tolerance = 1000;
if (interval < 1000)
interval = 1000;
else
interval = (interval + 500) / 1000 * 1000;
currentTime = currentTime / 1000 * 1000;
break;
}
t->interval = interval;
t->timeout = currentTime + interval;
return tolerance;
}
void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
{
Q_ASSERT(internalHwnd);
Q_Q(QEventDispatcherWin32);
bool ok = false;
ULONG tolerance = calculateNextTimeout(t, qt_msectime());
uint interval = t->interval;
if (interval == 0u) {
// optimization for single-shot-zero-timer
QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId));
ok = true;
} else if (tolerance == TIMERV_DEFAULT_COALESCING) {
// 3/2016: Although MSDN states timeSetEvent() is deprecated, the function
// is still deemed to be the most reliable precision timer.
t->fastTimerId = timeSetEvent(interval, 1, qt_fast_timer_proc, DWORD_PTR(t),
TIME_CALLBACK_FUNCTION | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
ok = t->fastTimerId;
}
if (!ok) {
// user normal timers for (Very)CoarseTimers, or if no more multimedia timers available
ok = SetCoalescableTimer(internalHwnd, t->timerId, interval, nullptr, tolerance);
}
if (!ok)
ok = SetTimer(internalHwnd, t->timerId, interval, nullptr);
if (!ok)
qErrnoWarning("QEventDispatcherWin32::registerTimer: Failed to create a timer");
}
void QEventDispatcherWin32Private::unregisterTimer(WinTimerInfo *t)
{
if (t->interval == 0) {
QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
} else if (t->fastTimerId != 0) {
timeKillEvent(t->fastTimerId);
QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
} else {
KillTimer(internalHwnd, t->timerId);
}
t->timerId = -1;
if (!t->inTimerEvent)
delete t;
}
void QEventDispatcherWin32Private::sendTimerEvent(int timerId)
{
WinTimerInfo *t = timerDict.value(timerId);
if (t && !t->inTimerEvent) {
// send event, but don't allow it to recurse
t->inTimerEvent = true;
// recalculate next emission
calculateNextTimeout(t, qt_msectime());
QTimerEvent e(t->timerId);
QCoreApplication::sendEvent(t->obj, &e);
// timer could have been removed
if (t->timerId == -1) {
delete t;
} else {
t->inTimerEvent = false;
}
}
}
void QEventDispatcherWin32Private::doWsaAsyncSelect(int socket, long event)
{
Q_ASSERT(internalHwnd);
// BoundsChecker may emit a warning for WSAAsyncSelect when event == 0
// This is a BoundsChecker bug and not a Qt bug
WSAAsyncSelect(socket, internalHwnd, event ? int(WM_QT_SOCKETNOTIFIER) : 0, event);
}
void QEventDispatcherWin32Private::postActivateSocketNotifiers()
{
if (!activateNotifiersPosted)
activateNotifiersPosted = PostMessage(internalHwnd, WM_QT_ACTIVATENOTIFIERS, 0, 0);
}
QEventDispatcherWin32::QEventDispatcherWin32(QObject *parent)
: QEventDispatcherWin32(*new QEventDispatcherWin32Private, parent)
{
}
QEventDispatcherWin32::QEventDispatcherWin32(QEventDispatcherWin32Private &dd, QObject *parent)
: QAbstractEventDispatcher(dd, parent)
{
Q_D(QEventDispatcherWin32);
d->internalHwnd = qt_create_internal_window(this);
}
QEventDispatcherWin32::~QEventDispatcherWin32()
{
}
static bool isUserInputMessage(UINT message)
{
return (message >= WM_KEYFIRST && message <= WM_KEYLAST)
|| (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST)
|| message == WM_MOUSEWHEEL
|| message == WM_MOUSEHWHEEL
|| message == WM_TOUCH
#ifndef QT_NO_GESTURES
|| message == WM_GESTURE
|| message == WM_GESTURENOTIFY
#endif
// Pointer input: Exclude WM_NCPOINTERUPDATE .. WM_POINTERROUTEDRELEASED
|| (message >= 0x0241 && message <= 0x0253)
|| message == WM_CLOSE;
}
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
// We don't know _when_ the interrupt occurred so we have to honor it.
const bool wasInterrupted = d->interrupt.fetchAndStoreRelaxed(false);
emit awake();
// To prevent livelocks, send posted events once per iteration.
// QCoreApplication::sendPostedEvents() takes care about recursions.
sendPostedEvents();
if (wasInterrupted)
return false;
auto threadData = d->threadData.loadRelaxed();
bool canWait;
bool retVal = false;
do {
QVarLengthArray<MSG> processedTimers;
while (!d->interrupt.loadRelaxed()) {
MSG msg;
if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
// process queued user input events
msg = d->queuedUserInputEvents.takeFirst();
} else if (!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
// process queued socket events
msg = d->queuedSocketEvents.takeFirst();
} else if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)
&& isUserInputMessage(msg.message)) {
// queue user input events for later processing
d->queuedUserInputEvents.append(msg);
continue;
}
if ((flags & QEventLoop::ExcludeSocketNotifiers)
&& (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
// queue socket events for later processing
d->queuedSocketEvents.append(msg);
continue;
}
} else if (MsgWaitForMultipleObjectsEx(0, NULL, 0, QS_ALLINPUT, MWMO_ALERTABLE)
== WAIT_OBJECT_0) {
// a new message has arrived, process it
continue;
} else {
// nothing to do, so break
break;
}
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
d->startPostedEventsTimer();
// Set result to 'true' because the message was sent by wakeUp().
retVal = true;
continue;
}
if (msg.message == WM_TIMER) {
// Skip timer event intended for use inside foreign loop.
if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId)
continue;
// avoid live-lock by keeping track of the timers we've already sent
bool found = false;
for (int i = 0; !found && i < processedTimers.count(); ++i) {
const MSG processed = processedTimers.constData()[i];
found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
}
if (found)
continue;
processedTimers.append(msg);
} else if (msg.message == WM_QUIT) {
if (QCoreApplication::instance())
QCoreApplication::instance()->quit();
return false;
}
if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
retVal = true;
}
// wait for message
canWait = (!retVal
&& !d->interrupt.loadRelaxed()
&& flags.testFlag(QEventLoop::WaitForMoreEvents)
&& threadData->canWaitLocked());
if (canWait) {
emit aboutToBlock();
MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
emit awake();
}
} while (canWait);
return retVal;
}
void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier)
{
Q_ASSERT(notifier);
int sockfd = notifier->socket();
int type = notifier->type();
#ifndef QT_NO_DEBUG
if (sockfd < 0) {
qWarning("QEventDispatcherWin32::registerSocketNotifier: invalid socket identifier");
return;
}
if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
qWarning("QEventDispatcherWin32: socket notifiers cannot be enabled from another thread");
return;
}
#endif
Q_D(QEventDispatcherWin32);
QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except };
QSNDict *dict = sn_vec[type];
if (QCoreApplication::closingDown()) // ### d->exitloop?
return; // after sn_cleanup, don't reinitialize.
if (dict->contains(sockfd)) {
const char *t[] = { "Read", "Write", "Exception" };
/* Variable "socket" below is a function pointer. */
qWarning("QSocketNotifier: Multiple socket notifiers for "
"same socket %d and type %s", sockfd, t[type]);
}
QSockNot *sn = new QSockNot;
sn->obj = notifier;
sn->fd = sockfd;
dict->insert(sn->fd, sn);
long event = 0;
if (d->sn_read.contains(sockfd))
event |= FD_READ | FD_CLOSE | FD_ACCEPT;
if (d->sn_write.contains(sockfd))
event |= FD_WRITE | FD_CONNECT;
if (d->sn_except.contains(sockfd))
event |= FD_OOB;
QSFDict::iterator it = d->active_fd.find(sockfd);
if (it != d->active_fd.end()) {
QSockFd &sd = it.value();
if (sd.selected) {
d->doWsaAsyncSelect(sockfd, 0);
sd.selected = false;
}
sd.event |= event;
} else {
// Although WSAAsyncSelect(..., 0), which is called from
// unregisterSocketNotifier(), immediately disables event message
// posting for the socket, it is possible that messages could be
// waiting in the application message queue even if the socket was
// closed. Also, some events could be implicitly re-enabled due
// to system calls. Ignore these superfluous events until all
// pending notifications have been suppressed. Next activation of
// socket notifiers will reset the mask.
d->active_fd.insert(sockfd, QSockFd(event, FD_READ | FD_CLOSE | FD_ACCEPT | FD_WRITE
| FD_CONNECT | FD_OOB));
}
d->postActivateSocketNotifiers();
}
void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier)
{
Q_ASSERT(notifier);
#ifndef QT_NO_DEBUG
int sockfd = notifier->socket();
if (sockfd < 0) {
qWarning("QEventDispatcherWin32::unregisterSocketNotifier: invalid socket identifier");
return;
}
if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
qWarning("QEventDispatcherWin32: socket notifiers cannot be disabled from another thread");
return;
}
#endif
doUnregisterSocketNotifier(notifier);
}
void QEventDispatcherWin32::doUnregisterSocketNotifier(QSocketNotifier *notifier)
{
Q_D(QEventDispatcherWin32);
int type = notifier->type();
int sockfd = notifier->socket();
Q_ASSERT(sockfd >= 0);
QSFDict::iterator it = d->active_fd.find(sockfd);
if (it != d->active_fd.end()) {
QSockFd &sd = it.value();
if (sd.selected)
d->doWsaAsyncSelect(sockfd, 0);
const long event[3] = { FD_READ | FD_CLOSE | FD_ACCEPT, FD_WRITE | FD_CONNECT, FD_OOB };
sd.event ^= event[type];
if (sd.event == 0) {
d->active_fd.erase(it);
} else if (sd.selected) {
sd.selected = false;
d->postActivateSocketNotifiers();
}
}
QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except };
QSNDict *dict = sn_vec[type];
QSockNot *sn = dict->value(sockfd);
if (!sn)
return;
dict->remove(sockfd);
delete sn;
}
void QEventDispatcherWin32::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
{
#ifndef QT_NO_DEBUG
if (timerId < 1 || interval < 0 || !object) {
qWarning("QEventDispatcherWin32::registerTimer: invalid arguments");
return;
}
if (object->thread() != thread() || thread() != QThread::currentThread()) {
qWarning("QEventDispatcherWin32::registerTimer: timers cannot be started from another thread");
return;
}
#endif
Q_D(QEventDispatcherWin32);
// exiting ... do not register new timers
// (QCoreApplication::closingDown() is set too late to be used here)
if (d->closingDown)
return;
WinTimerInfo *t = new WinTimerInfo;
t->dispatcher = this;
t->timerId = timerId;
t->interval = interval;
t->timerType = timerType;
t->obj = object;
t->inTimerEvent = false;
t->fastTimerId = 0;
d->registerTimer(t);
d->timerDict.insert(t->timerId, t); // store timers in dict
}
bool QEventDispatcherWin32::unregisterTimer(int timerId)
{
#ifndef QT_NO_DEBUG
if (timerId < 1) {
qWarning("QEventDispatcherWin32::unregisterTimer: invalid argument");
return false;
}
if (thread() != QThread::currentThread()) {
qWarning("QEventDispatcherWin32::unregisterTimer: timers cannot be stopped from another thread");
return false;
}
#endif
Q_D(QEventDispatcherWin32);
WinTimerInfo *t = d->timerDict.take(timerId);
if (!t)
return false;
d->unregisterTimer(t);
return true;
}
bool QEventDispatcherWin32::unregisterTimers(QObject *object)
{
#ifndef QT_NO_DEBUG
if (!object) {
qWarning("QEventDispatcherWin32::unregisterTimers: invalid argument");
return false;
}
if (object->thread() != thread() || thread() != QThread::currentThread()) {
qWarning("QEventDispatcherWin32::unregisterTimers: timers cannot be stopped from another thread");
return false;
}
#endif
Q_D(QEventDispatcherWin32);
if (d->timerDict.isEmpty())
return false;
auto it = d->timerDict.begin();
while (it != d->timerDict.end()) {
WinTimerInfo *t = it.value();
Q_ASSERT(t);
if (t->obj == object) {
it = d->timerDict.erase(it);
d->unregisterTimer(t);
} else {
++it;
}
}
return true;
}
QList<QEventDispatcherWin32::TimerInfo>
QEventDispatcherWin32::registeredTimers(QObject *object) const
{
#ifndef QT_NO_DEBUG
if (!object) {
qWarning("QEventDispatcherWin32:registeredTimers: invalid argument");
return QList<TimerInfo>();
}
#endif
Q_D(const QEventDispatcherWin32);
QList<TimerInfo> list;
for (WinTimerInfo *t : std::as_const(d->timerDict)) {
Q_ASSERT(t);
if (t->obj == object)
list << TimerInfo(t->timerId, t->interval, t->timerType);
}
return list;
}
int QEventDispatcherWin32::remainingTime(int timerId)
{
#ifndef QT_NO_DEBUG
if (timerId < 1) {
qWarning("QEventDispatcherWin32::remainingTime: invalid argument");
return -1;
}
#endif
Q_D(QEventDispatcherWin32);
quint64 currentTime = qt_msectime();
WinTimerInfo *t = d->timerDict.value(timerId);
if (t) {
// timer found, return time to wait
return t->timeout > currentTime ? t->timeout - currentTime : 0;
}
#ifndef QT_NO_DEBUG
qWarning("QEventDispatcherWin32::remainingTime: timer id %d not found", timerId);
#endif
return -1;
}
void QEventDispatcherWin32::wakeUp()
{
Q_D(QEventDispatcherWin32);
if (d->wakeUps.testAndSetRelaxed(0, 1)) {
// post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending
if (!PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0))
qErrnoWarning("QEventDispatcherWin32::wakeUp: Failed to post a message");
}
}
void QEventDispatcherWin32::interrupt()
{
Q_D(QEventDispatcherWin32);
d->interrupt.storeRelaxed(true);
wakeUp();
}
void QEventDispatcherWin32::startingUp()
{ }
void QEventDispatcherWin32::closingDown()
{
Q_D(QEventDispatcherWin32);
// clean up any socketnotifiers
while (!d->sn_read.isEmpty())
doUnregisterSocketNotifier((*(d->sn_read.begin()))->obj);
while (!d->sn_write.isEmpty())
doUnregisterSocketNotifier((*(d->sn_write.begin()))->obj);
while (!d->sn_except.isEmpty())
doUnregisterSocketNotifier((*(d->sn_except.begin()))->obj);
Q_ASSERT(d->active_fd.isEmpty());
// clean up any timers
for (WinTimerInfo *t : std::as_const(d->timerDict))
d->unregisterTimer(t);
d->timerDict.clear();
d->closingDown = true;
if (d->sendPostedEventsTimerId != 0)
KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
d->sendPostedEventsTimerId = 0;
}
bool QEventDispatcherWin32::event(QEvent *e)
{
Q_D(QEventDispatcherWin32);
switch (e->type()) {
case QEvent::ZeroTimerEvent: {
QZeroTimerEvent *zte = static_cast<QZeroTimerEvent*>(e);
WinTimerInfo *t = d->timerDict.value(zte->timerId());
if (t) {
t->inTimerEvent = true;
QTimerEvent te(zte->timerId());
QCoreApplication::sendEvent(t->obj, &te);
// timer could have been removed
if (t->timerId == -1) {
delete t;
} else {
if (t->interval == 0 && t->inTimerEvent) {
// post the next zero timer event as long as the timer was not restarted
QCoreApplication::postEvent(this, new QZeroTimerEvent(zte->timerId()));
}
t->inTimerEvent = false;
}
}
return true;
}
case QEvent::Timer:
d->sendTimerEvent(static_cast<const QTimerEvent*>(e)->timerId());
break;
default:
break;
}
return QAbstractEventDispatcher::event(e);
}
void QEventDispatcherWin32::sendPostedEvents()
{
Q_D(QEventDispatcherWin32);
if (d->sendPostedEventsTimerId != 0)
KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
d->sendPostedEventsTimerId = 0;
// Allow posting WM_QT_SENDPOSTEDEVENTS message.
d->wakeUps.storeRelaxed(0);
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData.loadRelaxed());
}
HWND QEventDispatcherWin32::internalHwnd()
{
return d_func()->internalHwnd;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,65 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qfunctions_win_p.h"
#include <QtCore/qdebug.h>
#include <combaseapi.h>
#include <objbase.h>
#if __has_include(<appmodel.h>)
# include <appmodel.h>
# define HAS_APPMODEL
#endif
QT_BEGIN_NAMESPACE
QComHelper::QComHelper(COINIT concurrencyModel)
{
// Avoid overhead of initializing and using obsolete technology
concurrencyModel = COINIT(concurrencyModel | COINIT_DISABLE_OLE1DDE);
m_initResult = CoInitializeEx(nullptr, concurrencyModel);
if (FAILED(m_initResult))
qErrnoWarning(m_initResult, "Failed to initialize COM library");
}
QComHelper::~QComHelper()
{
if (SUCCEEDED(m_initResult))
CoUninitialize();
}
/*!
\internal
Checks if the application has a \e{package identity}
Having a \e{package identity} is required to use many modern
Windows APIs.
https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/modernize-packaged-apps
*/
bool qt_win_hasPackageIdentity()
{
#if defined(HAS_APPMODEL)
static const bool hasPackageIdentity = []() {
UINT32 length = 0;
switch (const auto result = GetCurrentPackageFullName(&length, nullptr)) {
case ERROR_INSUFFICIENT_BUFFER:
return true;
case APPMODEL_ERROR_NO_PACKAGE:
return false;
default:
qWarning("Failed to resolve package identity (error code %ld)", result);
return false;
}
}();
return hasPackageIdentity;
#else
return false;
#endif
}
QT_END_NAMESPACE

View File

@ -0,0 +1,150 @@
// Copyright (C) 2017 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFUTEX_P_H
#define QFUTEX_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 <private/qglobal_p.h>
#include <QtCore/qtsan_impl.h>
QT_BEGIN_NAMESPACE
namespace QtDummyFutex {
constexpr inline bool futexAvailable() { return false; }
template <typename Atomic>
inline bool futexWait(Atomic &, typename Atomic::Type, int = 0)
{ Q_UNREACHABLE_RETURN(false); }
template <typename Atomic> inline void futexWakeOne(Atomic &)
{ Q_UNREACHABLE(); }
template <typename Atomic> inline void futexWakeAll(Atomic &)
{ Q_UNREACHABLE(); }
}
QT_END_NAMESPACE
#if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
// use Linux mutexes everywhere except for LSB builds
# include <sys/syscall.h>
# include <errno.h>
# include <limits.h>
# include <unistd.h>
# include <asm/unistd.h>
# include <linux/futex.h>
# define QT_ALWAYS_USE_FUTEX
// if not defined in linux/futex.h
# define FUTEX_PRIVATE_FLAG 128 // added in v2.6.22
// RISC-V does not supply __NR_futex
# ifndef __NR_futex
# define __NR_futex __NR_futex_time64
# endif
QT_BEGIN_NAMESPACE
namespace QtLinuxFutex {
constexpr inline bool futexAvailable() { return true; }
inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0,
int *addr2 = nullptr, int val3 = 0) noexcept
{
QtTsan::futexRelease(addr, addr2);
// we use __NR_futex because some libcs (like Android's bionic) don't
// provide SYS_futex etc.
int result = syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3);
QtTsan::futexAcquire(addr, addr2);
return result;
}
template <typename T> int *addr(T *ptr)
{
int *int_addr = reinterpret_cast<int *>(ptr);
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
if (sizeof(T) > sizeof(int))
int_addr++; //We want a pointer to the least significant half
#endif
return int_addr;
}
template <typename Atomic>
inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
{
_q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue));
}
template <typename Atomic>
inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout)
{
struct timespec ts;
ts.tv_sec = nstimeout / 1000 / 1000 / 1000;
ts.tv_nsec = nstimeout % (1000 * 1000 * 1000);
int r = _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue), quintptr(&ts));
return r == 0 || errno != ETIMEDOUT;
}
template <typename Atomic> inline void futexWakeOne(Atomic &futex)
{
_q_futex(addr(&futex), FUTEX_WAKE, 1);
}
template <typename Atomic> inline void futexWakeAll(Atomic &futex)
{
_q_futex(addr(&futex), FUTEX_WAKE, INT_MAX);
}
template <typename Atomic> inline
void futexWakeOp(Atomic &futex1, int wake1, int wake2, Atomic &futex2, quint32 op)
{
_q_futex(addr(&futex1), FUTEX_WAKE_OP, wake1, wake2, addr(&futex2), op);
}
}
namespace QtFutex = QtLinuxFutex;
QT_END_NAMESPACE
#elif defined(Q_OS_WIN)
# include <qt_windows.h>
QT_BEGIN_NAMESPACE
namespace QtWindowsFutex {
#define QT_ALWAYS_USE_FUTEX
constexpr inline bool futexAvailable() { return true; }
template <typename Atomic>
inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
{
QtTsan::futexRelease(&futex);
WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), INFINITE);
QtTsan::futexAcquire(&futex);
}
template <typename Atomic>
inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout)
{
BOOL r = WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), DWORD(nstimeout / 1000 / 1000));
return r || GetLastError() != ERROR_TIMEOUT;
}
template <typename Atomic> inline void futexWakeAll(Atomic &futex)
{
WakeByAddressAll(&futex);
}
template <typename Atomic> inline void futexWakeOne(Atomic &futex)
{
WakeByAddressSingle(&futex);
}
}
namespace QtFutex = QtWindowsFutex;
QT_END_NAMESPACE
#else
QT_BEGIN_NAMESPACE
namespace QtFutex = QtDummyFutex;
QT_END_NAMESPACE
#endif
#endif // QFUTEX_P_H

View File

@ -0,0 +1,922 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// Copyright (C) 2012 Olivier Goffart <ogoffart@woboq.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "global/qglobal.h"
#include "qplatformdefs.h"
#include "qmutex.h"
#include <qdebug.h>
#include "qatomic.h"
#include "qelapsedtimer.h"
#include "qfutex_p.h"
#include "qthread.h"
#include "qmutex_p.h"
#ifndef QT_ALWAYS_USE_FUTEX
#include "private/qfreelist_p.h"
#endif
QT_BEGIN_NAMESPACE
using namespace QtFutex;
static inline QMutexPrivate *dummyFutexValue()
{
return reinterpret_cast<QMutexPrivate *>(quintptr(3));
}
/*
\class QBasicMutex
\inmodule QtCore
\brief QMutex POD
\internal
\ingroup thread
- Can be used as global static object.
- Always non-recursive
- Do not use tryLock with timeout > 0, else you can have a leak (see the ~QMutex destructor)
*/
/*!
\class QMutex
\inmodule QtCore
\brief The QMutex class provides access serialization between threads.
\threadsafe
\ingroup thread
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 code/src_corelib_thread_qmutex.cpp 0
If these two methods are called in succession, the following happens:
\snippet code/src_corelib_thread_qmutex.cpp 1
If these two methods are called simultaneously from two threads then the
following sequence could result:
\snippet code/src_corelib_thread_qmutex.cpp 2
If we add a mutex, we should get the result we want:
\snippet 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().
QMutex is optimized to be fast in the non-contended case. It
will not allocate memory if there is no contention on that mutex.
It is constructed and destroyed with almost no overhead,
which means it is fine to have many mutexes as part of other classes.
\sa QRecursiveMutex, QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition
*/
/*!
\fn QMutex::QMutex()
Constructs a new mutex. The mutex is created in an unlocked state.
*/
/*! \fn QMutex::~QMutex()
Destroys the mutex.
\warning Destroying a locked mutex may result in undefined behavior.
*/
void QBasicMutex::destroyInternal(QMutexPrivate *d)
{
if (!d)
return;
if (!futexAvailable()) {
if (d != dummyLocked() && d->possiblyUnlocked.loadRelaxed() && tryLock()) {
unlock();
return;
}
}
qWarning("QMutex: destroying locked mutex");
}
/*! \fn void QMutex::lock()
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 will cause a \e dead-lock.
\sa unlock()
*/
/*! \fn bool QMutex::tryLock(int timeout)
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c 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 will cause a \e dead-lock.
\sa lock(), unlock()
*/
/*! \fn bool QMutex::tryLock(QDeadlineTimer timer)
\since 6.6
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait until \a timer expires
for the mutex to become available.
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 will cause a \e dead-lock.
\sa lock(), unlock()
*/
/*! \fn bool QMutex::tryLock()
\overload
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false.
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 will cause a \e dead-lock.
\sa lock(), unlock()
*/
/*! \fn bool QMutex::try_lock()
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false.
This function is provided for compatibility with the Standard Library
concept \c Lockable. It is equivalent to tryLock().
*/
/*! \fn template <class Rep, class Period> bool QMutex::try_lock_for(std::chrono::duration<Rep, Period> duration)
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait for at least \a duration
for the mutex to become available.
Note: Passing a negative duration as the \a duration is equivalent to
calling try_lock(). This behavior differs from tryLock().
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 will cause a \e dead-lock.
\sa lock(), unlock()
*/
/*! \fn template<class Clock, class Duration> bool QMutex::try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait at least until \a timePoint
for the mutex to become available.
Note: Passing a \a timePoint which has already passed is equivalent
to calling try_lock(). This behavior differs from tryLock().
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 will cause a \e dead-lock.
\sa lock(), unlock()
*/
/*! \fn void QMutex::unlock()
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()
*/
/*!
\class QRecursiveMutex
\inmodule QtCore
\since 5.14
\brief The QRecursiveMutex class provides access serialization between threads.
\threadsafe
\ingroup thread
The QRecursiveMutex class is a mutex, like QMutex, with which it is
API-compatible. It differs from QMutex by accepting lock() calls from
the same thread any number of times. QMutex would deadlock in this situation.
QRecursiveMutex is much more expensive to construct and operate on, so
use a plain QMutex whenever you can. Sometimes, one public function,
however, calls another public function, and they both need to lock the
same mutex. In this case, you have two options:
\list
\li Factor the code that needs mutex protection into private functions,
which assume that the mutex is held when they are called, and lock a
plain QMutex in the public functions before you call the private
implementation ones.
\li Or use a recursive mutex, so it doesn't matter that the first public
function has already locked the mutex when the second one wishes to do so.
\endlist
\sa QMutex, QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition
*/
/*! \fn QRecursiveMutex::QRecursiveMutex()
Constructs a new recursive mutex. The mutex is created in an unlocked state.
\sa lock(), unlock()
*/
/*!
Destroys the mutex.
\warning Destroying a locked mutex may result in undefined behavior.
*/
QRecursiveMutex::~QRecursiveMutex()
{
}
/*! \fn void QRecursiveMutex::lock()
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.
\sa unlock()
*/
/*!
\fn QRecursiveMutex::tryLock(int timeout)
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c 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.
\sa lock(), unlock()
*/
/*!
\since 6.6
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait until \a timeout expires
for the mutex to become available.
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.
\sa lock(), unlock()
*/
bool QRecursiveMutex::tryLock(QDeadlineTimer timeout) QT_MUTEX_LOCK_NOEXCEPT
{
unsigned tsanFlags = QtTsan::MutexWriteReentrant | QtTsan::TryLock;
QtTsan::mutexPreLock(this, tsanFlags);
Qt::HANDLE self = QThread::currentThreadId();
if (owner.loadRelaxed() == self) {
++count;
Q_ASSERT_X(count != 0, "QMutex::lock", "Overflow in recursion counter");
QtTsan::mutexPostLock(this, tsanFlags, 0);
return true;
}
bool success = true;
if (timeout.isForever()) {
mutex.lock();
} else {
success = mutex.tryLock(timeout);
}
if (success)
owner.storeRelaxed(self);
else
tsanFlags |= QtTsan::TryLockFailed;
QtTsan::mutexPostLock(this, tsanFlags, 0);
return success;
}
/*! \fn bool QRecursiveMutex::try_lock()
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false.
This function is provided for compatibility with the Standard Library
concept \c Lockable. It is equivalent to tryLock().
*/
/*! \fn template <class Rep, class Period> bool QRecursiveMutex::try_lock_for(std::chrono::duration<Rep, Period> duration)
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait for at least \a duration
for the mutex to become available.
Note: Passing a negative duration as the \a duration is equivalent to
calling try_lock(). This behavior differs from tryLock().
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.
\sa lock(), unlock()
*/
/*! \fn template<class Clock, class Duration> bool QRecursiveMutex::try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait at least until \a timePoint
for the mutex to become available.
Note: Passing a \a timePoint which has already passed is equivalent
to calling try_lock(). This behavior differs from tryLock().
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.
\sa lock(), unlock()
*/
/*!
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 QRecursiveMutex::unlock() noexcept
{
Q_ASSERT(owner.loadRelaxed() == QThread::currentThreadId());
QtTsan::mutexPreUnlock(this, 0u);
if (count > 0) {
count--;
} else {
owner.storeRelaxed(nullptr);
mutex.unlock();
}
QtTsan::mutexPostUnlock(this, 0u);
}
/*!
\class QMutexLocker
\inmodule QtCore
\brief The QMutexLocker class is a convenience class that simplifies
locking and unlocking mutexes.
\threadsafe
\ingroup thread
Locking and unlocking a QMutex or QRecursiveMutex 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 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 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 code/src_corelib_thread_qmutex.cpp 6
\sa QReadLocker, QWriteLocker, QMutex
*/
/*!
\fn template <typename Mutex> QMutexLocker<Mutex>::QMutexLocker(Mutex *mutex) noexcept
Constructs a QMutexLocker and locks \a mutex. The mutex will be
unlocked when the QMutexLocker is destroyed. If \a mutex is \nullptr,
QMutexLocker does nothing.
\sa QMutex::lock()
*/
/*!
\fn template <typename Mutex> QMutexLocker<Mutex>::QMutexLocker(QMutexLocker &&other) noexcept
\since 6.4
Move-constructs a QMutexLocker from \a other. The mutex and the
state of \a other is transferred to the newly constructed instance.
After the move, \a other will no longer be managing any mutex.
\sa QMutex::lock()
*/
/*!
\fn template <typename Mutex> QMutexLocker<Mutex> &QMutexLocker<Mutex>::operator=(QMutexLocker &&other) noexcept
\since 6.4
Move-assigns \a other onto this QMutexLocker. If this QMutexLocker
was holding a locked mutex before the assignment, the mutex will be
unlocked. The mutex and the state of \a other is then transferred
to this QMutexLocker. After the move, \a other will no longer be
managing any mutex.
\sa QMutex::lock()
*/
/*!
\fn template <typename Mutex> void QMutexLocker<Mutex>::swap(QMutexLocker &other) noexcept
\since 6.4
Swaps the mutex and the state of this QMutexLocker with \a other.
This operation is very fast and never fails.
\sa QMutex::lock()
*/
/*!
\fn template <typename Mutex> QMutexLocker<Mutex>::~QMutexLocker() noexcept
Destroys the QMutexLocker and unlocks the mutex that was locked
in the constructor.
\sa QMutex::unlock()
*/
/*!
\fn template <typename Mutex> bool QMutexLocker<Mutex>::isLocked() const noexcept
\since 6.4
Returns true if this QMutexLocker is currently locking its associated
mutex, or false otherwise.
*/
/*!
\fn template <typename Mutex> void QMutexLocker<Mutex>::unlock() noexcept
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 template <typename Mutex> void QMutexLocker<Mutex>::relock() noexcept
Relocks an unlocked mutex locker.
\sa unlock()
*/
/*!
\fn template <typename Mutex> QMutex *QMutexLocker<Mutex>::mutex() const
Returns the mutex on which the QMutexLocker is operating.
*/
/*
For a rough introduction on how this works, refer to
http://woboq.com/blog/internals-of-qmutex-in-qt5.html
which explains a slightly simplified version of it.
The differences are that here we try to work with timeout (requires the
possiblyUnlocked flag) and that we only wake one thread when unlocking
(requires maintaining the waiters count)
We also support recursive mutexes which always have a valid d_ptr.
The waiters flag represents the number of threads that are waiting or about
to wait on the mutex. There are two tricks to keep in mind:
We don't want to increment waiters after we checked no threads are waiting
(waiters == 0). That's why we atomically set the BigNumber flag on waiters when
we check waiters. Similarly, if waiters is decremented right after we checked,
the mutex would be unlocked (d->wakeUp() has (or will) be called), but there is
no thread waiting. This is only happening if there was a timeout in tryLock at the
same time as the mutex is unlocked. So when there was a timeout, we set the
possiblyUnlocked flag.
*/
/*
* QBasicMutex implementation with futexes (Linux, Windows 10)
*
* QBasicMutex contains one pointer value, which can contain one of four
* different values:
* 0x0 unlocked
* 0x1 locked, no waiters
* 0x3 locked, at least one waiter
*
* LOCKING:
*
* A starts in the 0x0 state, indicating that it's unlocked. When the first
* thread attempts to lock it, it will perform a testAndSetAcquire
* from 0x0 to 0x1. If that succeeds, the caller concludes that it
* successfully locked the mutex. That happens in fastTryLock().
*
* If that testAndSetAcquire fails, QBasicMutex::lockInternal is called.
*
* lockInternal will examine the value of the pointer. Otherwise, it will use
* futexes to sleep and wait for another thread to unlock. To do that, it needs
* to set a pointer value of 0x3, which indicates that thread is waiting. It
* does that by a simple fetchAndStoreAcquire operation.
*
* If the pointer value was 0x0, it means we succeeded in acquiring the mutex.
* For other values, it will then call FUTEX_WAIT and with an expected value of
* 0x3.
*
* If the pointer value changed before futex(2) managed to sleep, it will
* return -1 / EWOULDBLOCK, in which case we have to start over. And even if we
* are woken up directly by a FUTEX_WAKE, we need to acquire the mutex, so we
* start over again.
*
* UNLOCKING:
*
* To unlock, we need to set a value of 0x0 to indicate it's unlocked. The
* first attempt is a testAndSetRelease operation from 0x1 to 0x0. If that
* succeeds, we're done.
*
* If it fails, unlockInternal() is called. The only possibility is that the
* mutex value was 0x3, which indicates some other thread is waiting or was
* waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE.
*/
/*!
\internal helper for lock()
*/
void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT
{
if (futexAvailable()) {
// note we must set to dummyFutexValue because there could be other threads
// also waiting
while (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) != nullptr) {
// successfully set the waiting bit, now sleep
futexWait(d_ptr, dummyFutexValue());
// we got woken up, so try to acquire the mutex
}
Q_ASSERT(d_ptr.loadRelaxed());
} else {
lockInternal(-1);
}
}
/*!
\internal helper for lock(int)
*/
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
if (timeout == 0)
return false;
return lockInternal(QDeadlineTimer(timeout));
}
#endif
/*!
\internal helper for tryLock(QDeadlineTimer)
*/
bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXCEPT
{
qint64 remainingTime = deadlineTimer.remainingTimeNSecs();
if (remainingTime == 0)
return false;
if (futexAvailable()) {
if (Q_UNLIKELY(remainingTime < 0)) { // deadlineTimer.isForever()
lockInternal();
return true;
}
// The mutex is already locked, set a bit indicating we're waiting.
// Note we must set to dummyFutexValue because there could be other threads
// also waiting.
if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
return true;
Q_FOREVER {
if (!futexWait(d_ptr, dummyFutexValue(), remainingTime))
return false;
// We got woken up, so must try to acquire the mutex. We must set
// to dummyFutexValue() again because there could be other threads
// waiting.
if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
return true;
// calculate the remaining time
remainingTime = deadlineTimer.remainingTimeNSecs();
if (remainingTime <= 0)
return false;
}
}
#if !defined(QT_ALWAYS_USE_FUTEX)
while (!fastTryLock()) {
QMutexPrivate *copy = d_ptr.loadAcquire();
if (!copy) // if d is 0, the mutex is unlocked
continue;
if (copy == dummyLocked()) {
if (remainingTime == 0)
return false;
// The mutex is locked but does not have a QMutexPrivate yet.
// we need to allocate a QMutexPrivate
QMutexPrivate *newD = QMutexPrivate::allocate();
if (!d_ptr.testAndSetOrdered(dummyLocked(), newD)) {
//Either the mutex is already unlocked, or another thread already set it.
newD->deref();
continue;
}
copy = newD;
//the d->refCount is already 1 the deref will occurs when we unlock
}
QMutexPrivate *d = static_cast<QMutexPrivate *>(copy);
if (remainingTime == 0 && !d->possiblyUnlocked.loadRelaxed())
return false;
// At this point we have a pointer to a QMutexPrivate. But the other thread
// may unlock the mutex at any moment and release the QMutexPrivate to the pool.
// We will try to reference it to avoid unlock to release it to the pool to make
// sure it won't be released. But if the refcount is already 0 it has been released.
if (!d->ref())
continue; //that QMutexPrivate was already released
// We now hold a reference to the QMutexPrivate. It won't be released and re-used.
// But it is still possible that it was already re-used by another QMutex right before
// we did the ref(). So check if we still hold a pointer to the right mutex.
if (d != d_ptr.loadAcquire()) {
//Either the mutex is already unlocked, or relocked with another mutex
d->deref();
continue;
}
// In this part, we will try to increment the waiters count.
// We just need to take care of the case in which the old_waiters
// is set to the BigNumber magic value set in unlockInternal()
int old_waiters;
do {
old_waiters = d->waiters.loadAcquire();
if (old_waiters == -QMutexPrivate::BigNumber) {
// we are unlocking, and the thread that unlocks is about to change d to 0
// we try to acquire the mutex by changing to dummyLocked()
if (d_ptr.testAndSetAcquire(d, dummyLocked())) {
// Mutex acquired
d->deref();
return true;
} else {
Q_ASSERT(d != d_ptr.loadRelaxed()); //else testAndSetAcquire should have succeeded
// Mutex is likely to bo 0, we should continue the outer-loop,
// set old_waiters to the magic value of BigNumber
old_waiters = QMutexPrivate::BigNumber;
break;
}
}
} while (!d->waiters.testAndSetRelaxed(old_waiters, old_waiters + 1));
if (d != d_ptr.loadAcquire()) {
// The mutex was unlocked before we incremented waiters.
if (old_waiters != QMutexPrivate::BigNumber) {
//we did not break the previous loop
Q_ASSERT(d->waiters.loadRelaxed() >= 1);
d->waiters.deref();
}
d->deref();
continue;
}
if (d->wait(deadlineTimer)) {
// reset the possiblyUnlocked flag if needed (and deref its corresponding reference)
if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false))
d->deref();
d->derefWaiters(1);
//we got the lock. (do not deref)
Q_ASSERT(d == d_ptr.loadRelaxed());
return true;
} else {
Q_ASSERT(remainingTime >= 0);
// timed out
d->derefWaiters(1);
//There may be a race in which the mutex is unlocked right after we timed out,
// and before we deref the waiters, so maybe the mutex is actually unlocked.
// Set the possiblyUnlocked flag to indicate this possibility.
if (!d->possiblyUnlocked.testAndSetRelaxed(false, true)) {
// We keep a reference when possiblyUnlocked is true.
// but if possiblyUnlocked was already true, we don't need to keep the reference.
d->deref();
}
return false;
}
}
Q_ASSERT(d_ptr.loadRelaxed() != 0);
return true;
#else
Q_UNREACHABLE();
#endif
}
/*!
\internal
*/
void QBasicMutex::unlockInternal() noexcept
{
QMutexPrivate *copy = d_ptr.loadAcquire();
Q_ASSERT(copy); //we must be locked
Q_ASSERT(copy != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
if (futexAvailable()) {
d_ptr.storeRelease(nullptr);
return futexWakeOne(d_ptr);
}
#if !defined(QT_ALWAYS_USE_FUTEX)
QMutexPrivate *d = reinterpret_cast<QMutexPrivate *>(copy);
// If no one is waiting for the lock anymore, we should reset d to 0x0.
// Using fetchAndAdd, we atomically check that waiters was equal to 0, and add a flag
// to the waiters variable (BigNumber). That way, we avoid the race in which waiters is
// incremented right after we checked, because we won't increment waiters if is
// equal to -BigNumber
if (d->waiters.fetchAndAddRelease(-QMutexPrivate::BigNumber) == 0) {
//there is no one waiting on this mutex anymore, set the mutex as unlocked (d = 0)
if (d_ptr.testAndSetRelease(d, 0)) {
// reset the possiblyUnlocked flag if needed (and deref its corresponding reference)
if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false))
d->deref();
}
d->derefWaiters(0);
} else {
d->derefWaiters(0);
//there are thread waiting, transfer the lock.
d->wakeUp();
}
d->deref();
#else
Q_UNUSED(copy);
#endif
}
#if !defined(QT_ALWAYS_USE_FUTEX)
//The freelist management
namespace {
struct FreeListConstants : QFreeListDefaultConstants {
enum { BlockCount = 4, MaxIndex=0xffff };
static const int Sizes[BlockCount];
};
Q_CONSTINIT const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = {
16,
128,
1024,
FreeListConstants::MaxIndex - (16 + 128 + 1024)
};
typedef QFreeList<QMutexPrivate, FreeListConstants> FreeList;
// We cannot use Q_GLOBAL_STATIC because it uses QMutex
Q_CONSTINIT static FreeList freeList_;
FreeList *freelist()
{
return &freeList_;
}
}
QMutexPrivate *QMutexPrivate::allocate()
{
int i = freelist()->next();
QMutexPrivate *d = &(*freelist())[i];
d->id = i;
Q_ASSERT(d->refCount.loadRelaxed() == 0);
Q_ASSERT(!d->possiblyUnlocked.loadRelaxed());
Q_ASSERT(d->waiters.loadRelaxed() == 0);
d->refCount.storeRelaxed(1);
return d;
}
void QMutexPrivate::release()
{
Q_ASSERT(refCount.loadRelaxed() == 0);
Q_ASSERT(!possiblyUnlocked.loadRelaxed());
Q_ASSERT(waiters.loadRelaxed() == 0);
freelist()->release(id);
}
// atomically subtract "value" to the waiters, and remove the QMutexPrivate::BigNumber flag
void QMutexPrivate::derefWaiters(int value) noexcept
{
int old_waiters;
int new_waiters;
do {
old_waiters = waiters.loadRelaxed();
new_waiters = old_waiters;
if (new_waiters < 0) {
new_waiters += QMutexPrivate::BigNumber;
}
new_waiters -= value;
} while (!waiters.testAndSetRelaxed(old_waiters, new_waiters));
}
#endif
QT_END_NAMESPACE
#if defined(QT_ALWAYS_USE_FUTEX)
// nothing
#elif defined(Q_OS_DARWIN)
# include "qmutex_mac.cpp"
#else
# include "qmutex_unix.cpp"
#endif

View File

@ -0,0 +1,94 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// Copyright (C) 2012 Olivier Goffart <ogoffart@woboq.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#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 and qmutex_unix.cpp. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/private/qglobal_p.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qmutex.h>
#include <QtCore/qatomic.h>
#include <QtCore/qdeadlinetimer.h>
#include "qplatformdefs.h" // _POSIX_VERSION
#if defined(Q_OS_DARWIN)
# include <mach/semaphore.h>
#elif defined(Q_OS_UNIX)
# include <semaphore.h>
#endif
struct timespec;
QT_BEGIN_NAMESPACE
// We manipulate the pointer to this class in inline, atomic code,
// so syncqt mustn't mark them as private, so ELFVERSION:ignore-next
class QMutexPrivate
{
public:
~QMutexPrivate();
QMutexPrivate();
bool wait(QDeadlineTimer timeout = QDeadlineTimer::Forever);
void wakeUp() noexcept;
// Control the lifetime of the privates
QAtomicInt refCount;
int id;
bool ref()
{
Q_ASSERT(refCount.loadRelaxed() >= 0);
int c;
do {
c = refCount.loadRelaxed();
if (c == 0)
return false;
} while (!refCount.testAndSetRelaxed(c, c + 1));
Q_ASSERT(refCount.loadRelaxed() >= 0);
return true;
}
void deref()
{
Q_ASSERT(refCount.loadRelaxed() >= 0);
if (!refCount.deref())
release();
Q_ASSERT(refCount.loadRelaxed() >= 0);
}
void release();
static QMutexPrivate *allocate();
QAtomicInt waiters; // Number of threads waiting on this mutex. (may be offset by -BigNumber)
QAtomicInt possiblyUnlocked; /* Boolean indicating that a timed wait timed out.
When it is true, a reference is held.
It is there to avoid a race that happens if unlock happens right
when the mutex is unlocked.
*/
enum { BigNumber = 0x100000 }; //Must be bigger than the possible number of waiters (number of threads)
void derefWaiters(int value) noexcept;
//platform specific stuff
#if defined(Q_OS_DARWIN)
semaphore_t mach_semaphore;
#elif defined(Q_OS_UNIX)
sem_t semaphore;
#endif
};
QT_END_NAMESPACE
#endif // QMUTEX_P_H