mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-03 15:55:27 +08:00
qt 6.6.1 original sources files (to be patched)
This commit is contained in:
242
qtbase/src/corelib/io/qstandardpaths_win.cpp
Normal file
242
qtbase/src/corelib/io/qstandardpaths_win.cpp
Normal 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
|
908
qtbase/src/corelib/kernel/qeventdispatcher_win.cpp
Normal file
908
qtbase/src/corelib/kernel/qeventdispatcher_win.cpp
Normal 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
|
65
qtbase/src/corelib/kernel/qfunctions_win.cpp
Normal file
65
qtbase/src/corelib/kernel/qfunctions_win.cpp
Normal 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
|
150
qtbase/src/corelib/thread/qfutex_p.h
Normal file
150
qtbase/src/corelib/thread/qfutex_p.h
Normal 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
|
922
qtbase/src/corelib/thread/qmutex.cpp
Normal file
922
qtbase/src/corelib/thread/qmutex.cpp
Normal 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
|
94
qtbase/src/corelib/thread/qmutex_p.h
Normal file
94
qtbase/src/corelib/thread/qmutex_p.h
Normal 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
|
Reference in New Issue
Block a user