wasm: add platform qsettings
Since the backend is async, the settings will not be ready to read/write instantly as on other platforms, but only be ready after the filesystem has been synced to the sandbox. This takes at least 250 to 500 ms. The QSettings status() or isWritable() can be used to discern when the settings are ready for use. This also fixes a crash in threaded wasm Task-number: QTBUG-70002 Fixes: QTBUG-63923 Fixes: QTBUG-79650 Change-Id: If24c6ada1b91b2a565ed6733da74972c3027f622 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
parent
f2cf5f5417
commit
a3f62d7ead
@ -136,6 +136,7 @@ qtConfig(settings) {
|
||||
} else: darwin:!nacl {
|
||||
SOURCES += io/qsettings_mac.cpp
|
||||
}
|
||||
wasm : SOURCES += io/qsettings_wasm.cpp
|
||||
}
|
||||
|
||||
win32 {
|
||||
|
@ -76,10 +76,6 @@
|
||||
# include <ioLib.h>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WASM
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -295,7 +291,7 @@ after_loop:
|
||||
|
||||
// see also qsettings_win.cpp, qsettings_winrt.cpp and qsettings_mac.cpp
|
||||
|
||||
#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
|
||||
#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_WASM)
|
||||
QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
|
||||
const QString &organization, const QString &application)
|
||||
{
|
||||
@ -1185,7 +1181,9 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format,
|
||||
confFiles.append(QConfFile::fromName(systemPath.path + orgFile, false));
|
||||
}
|
||||
|
||||
#ifndef Q_OS_WASM // wasm needs to delay access until after file sync
|
||||
initAccess();
|
||||
#endif
|
||||
}
|
||||
|
||||
QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName,
|
||||
@ -1548,13 +1546,6 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
|
||||
perms |= QFile::ReadGroup | QFile::ReadOther;
|
||||
QFile(confFile->name).setPermissions(perms);
|
||||
}
|
||||
#ifdef Q_OS_WASM
|
||||
EM_ASM(
|
||||
// Sync sandbox filesystem to persistent database filesystem. See QTBUG-70002
|
||||
FS.syncfs(false, function(err) {
|
||||
});
|
||||
);
|
||||
#endif
|
||||
} else {
|
||||
setStatus(QSettings::AccessError);
|
||||
}
|
||||
|
@ -57,6 +57,10 @@
|
||||
#include "QtCore/qiodevice.h"
|
||||
#include "QtCore/qstack.h"
|
||||
#include "QtCore/qstringlist.h"
|
||||
|
||||
#include <QtCore/qvariant.h>
|
||||
#include "qsettings.h"
|
||||
|
||||
#ifndef QT_NO_QOBJECT
|
||||
#include "private/qobject_p.h"
|
||||
#endif
|
||||
@ -253,6 +257,10 @@ protected:
|
||||
mutable QSettings::Status status;
|
||||
};
|
||||
|
||||
#ifdef Q_OS_WASM
|
||||
class QWasmSettingsPrivate;
|
||||
#endif
|
||||
|
||||
class QConfFileSettingsPrivate : public QSettingsPrivate
|
||||
{
|
||||
public:
|
||||
@ -281,7 +289,7 @@ public:
|
||||
|
||||
private:
|
||||
void initFormat();
|
||||
void initAccess();
|
||||
virtual void initAccess();
|
||||
void syncConfFile(QConfFile *confFile);
|
||||
bool writeIniFile(QIODevice &device, const ParsedSettingsMap &map);
|
||||
#ifdef Q_OS_MAC
|
||||
@ -297,6 +305,9 @@ private:
|
||||
QString extension;
|
||||
Qt::CaseSensitivity caseSensitivity;
|
||||
int nextPosition;
|
||||
#ifdef Q_OS_WASM
|
||||
friend class QWasmSettingsPrivate;
|
||||
#endif
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
259
src/corelib/io/qsettings_wasm.cpp
Normal file
259
src/corelib/io/qsettings_wasm.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qsettings.h"
|
||||
#ifndef QT_NO_SETTINGS
|
||||
|
||||
#include "qsettings_p.h"
|
||||
#ifndef QT_NO_QOBJECT
|
||||
#include "qcoreapplication.h"
|
||||
#include <QFile>
|
||||
#endif // QT_NO_QOBJECT
|
||||
#include <QDebug>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <emscripten.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static bool isReadReady = false;
|
||||
|
||||
class QWasmSettingsPrivate : public QConfFileSettingsPrivate
|
||||
{
|
||||
public:
|
||||
QWasmSettingsPrivate(QSettings::Scope scope, const QString &organization,
|
||||
const QString &application);
|
||||
~QWasmSettingsPrivate();
|
||||
|
||||
bool get(const QString &key, QVariant *value) const override;
|
||||
QStringList children(const QString &prefix, ChildSpec spec) const override;
|
||||
void clear() override;
|
||||
void sync() override;
|
||||
void flush() override;
|
||||
bool isWritable() const override;
|
||||
|
||||
void syncToLocal(const char *data, int size);
|
||||
void loadLocal(const QByteArray &filename);
|
||||
void setReady();
|
||||
void initAccess() override;
|
||||
|
||||
private:
|
||||
QString databaseName;
|
||||
QString id;
|
||||
};
|
||||
|
||||
static void QWasmSettingsPrivate_onLoad(void *userData, void *dataPtr, int size)
|
||||
{
|
||||
QWasmSettingsPrivate *wasm = reinterpret_cast<QWasmSettingsPrivate *>(userData);
|
||||
|
||||
QFile file(wasm->fileName());
|
||||
QFileInfo fileInfo(wasm->fileName());
|
||||
QDir dir(fileInfo.path());
|
||||
if (!dir.exists())
|
||||
dir.mkpath(fileInfo.path());
|
||||
|
||||
if (file.open(QFile::WriteOnly)) {
|
||||
file.write(reinterpret_cast<char *>(dataPtr), size);
|
||||
file.close();
|
||||
wasm->setReady();
|
||||
}
|
||||
}
|
||||
|
||||
static void QWasmSettingsPrivate_onError(void *userData)
|
||||
{
|
||||
QWasmSettingsPrivate *wasm = reinterpret_cast<QWasmSettingsPrivate *>(userData);
|
||||
if (wasm)
|
||||
wasm->setStatus(QSettings::AccessError);
|
||||
}
|
||||
|
||||
static void QWasmSettingsPrivate_onStore(void *userData)
|
||||
{
|
||||
QWasmSettingsPrivate *wasm = reinterpret_cast<QWasmSettingsPrivate *>(userData);
|
||||
if (wasm)
|
||||
wasm->setStatus(QSettings::NoError);
|
||||
}
|
||||
|
||||
static void QWasmSettingsPrivate_onCheck(void *userData, int exists)
|
||||
{
|
||||
QWasmSettingsPrivate *wasm = reinterpret_cast<QWasmSettingsPrivate *>(userData);
|
||||
if (wasm) {
|
||||
if (exists)
|
||||
wasm->loadLocal(wasm->fileName().toLocal8Bit());
|
||||
else
|
||||
wasm->setReady();
|
||||
}
|
||||
}
|
||||
|
||||
QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format,
|
||||
QSettings::Scope scope,
|
||||
const QString &organization,
|
||||
const QString &application)
|
||||
{
|
||||
Q_UNUSED(format)
|
||||
if (organization == QLatin1String("Qt"))
|
||||
{
|
||||
QString organizationDomain = QCoreApplication::organizationDomain();
|
||||
QString applicationName = QCoreApplication::applicationName();
|
||||
|
||||
QSettingsPrivate *newSettings;
|
||||
newSettings = new QWasmSettingsPrivate(scope, organizationDomain, applicationName);
|
||||
|
||||
newSettings->beginGroupOrArray(QSettingsGroup(normalizedKey(organization)));
|
||||
if (!application.isEmpty())
|
||||
newSettings->beginGroupOrArray(QSettingsGroup(normalizedKey(application)));
|
||||
|
||||
return newSettings;
|
||||
}
|
||||
return new QWasmSettingsPrivate(scope, organization, application);
|
||||
}
|
||||
|
||||
QWasmSettingsPrivate::QWasmSettingsPrivate(QSettings::Scope scope, const QString &organization,
|
||||
const QString &application)
|
||||
: QConfFileSettingsPrivate(QSettings::NativeFormat, scope, organization, application)
|
||||
{
|
||||
setStatus(QSettings::AccessError); // access error until sandbox gets loaded
|
||||
databaseName = organization;
|
||||
id = application;
|
||||
|
||||
emscripten_idb_async_exists("/home/web_user",
|
||||
fileName().toLocal8Bit(),
|
||||
reinterpret_cast<void*>(this),
|
||||
QWasmSettingsPrivate_onCheck,
|
||||
QWasmSettingsPrivate_onError);
|
||||
}
|
||||
|
||||
QWasmSettingsPrivate::~QWasmSettingsPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
void QWasmSettingsPrivate::initAccess()
|
||||
{
|
||||
if (isReadReady)
|
||||
QConfFileSettingsPrivate::initAccess();
|
||||
}
|
||||
|
||||
bool QWasmSettingsPrivate::get(const QString &key, QVariant *value) const
|
||||
{
|
||||
if (isReadReady)
|
||||
return QConfFileSettingsPrivate::get(key, value);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList QWasmSettingsPrivate::children(const QString &prefix, ChildSpec spec) const
|
||||
{
|
||||
return QConfFileSettingsPrivate::children(prefix, spec);
|
||||
}
|
||||
|
||||
void QWasmSettingsPrivate::clear()
|
||||
{
|
||||
QConfFileSettingsPrivate::clear();
|
||||
emscripten_idb_async_delete("/home/web_user",
|
||||
fileName().toLocal8Bit(),
|
||||
reinterpret_cast<void*>(this),
|
||||
QWasmSettingsPrivate_onStore,
|
||||
QWasmSettingsPrivate_onError);
|
||||
}
|
||||
|
||||
void QWasmSettingsPrivate::sync()
|
||||
{
|
||||
QConfFileSettingsPrivate::sync();
|
||||
|
||||
QFile file(fileName());
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
QByteArray dataPointer = file.readAll();
|
||||
|
||||
emscripten_idb_async_store("/home/web_user",
|
||||
fileName().toLocal8Bit(),
|
||||
reinterpret_cast<void *>(dataPointer.data()),
|
||||
dataPointer.length(),
|
||||
reinterpret_cast<void*>(this),
|
||||
QWasmSettingsPrivate_onStore,
|
||||
QWasmSettingsPrivate_onError);
|
||||
}
|
||||
}
|
||||
|
||||
void QWasmSettingsPrivate::flush()
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
bool QWasmSettingsPrivate::isWritable() const
|
||||
{
|
||||
return isReadReady && QConfFileSettingsPrivate::isWritable();
|
||||
}
|
||||
|
||||
void QWasmSettingsPrivate::syncToLocal(const char *data, int size)
|
||||
{
|
||||
QFile file(fileName());
|
||||
|
||||
if (file.open(QFile::WriteOnly)) {
|
||||
file.write(data, size + 1);
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
emscripten_idb_async_store("/home/web_user",
|
||||
fileName().toLocal8Bit(),
|
||||
reinterpret_cast<void *>(data.data()),
|
||||
data.length(),
|
||||
reinterpret_cast<void*>(this),
|
||||
QWasmSettingsPrivate_onStore,
|
||||
QWasmSettingsPrivate_onError);
|
||||
setReady();
|
||||
}
|
||||
}
|
||||
|
||||
void QWasmSettingsPrivate::loadLocal(const QByteArray &filename)
|
||||
{
|
||||
emscripten_idb_async_load("/home/web_user",
|
||||
filename.data(),
|
||||
reinterpret_cast<void*>(this),
|
||||
QWasmSettingsPrivate_onLoad,
|
||||
QWasmSettingsPrivate_onError);
|
||||
}
|
||||
|
||||
void QWasmSettingsPrivate::setReady()
|
||||
{
|
||||
isReadReady = true;
|
||||
setStatus(QSettings::NoError);
|
||||
QConfFileSettingsPrivate::initAccess();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
#endif // QT_NO_SETTINGS
|
@ -121,7 +121,6 @@
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WASM
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/val.h>
|
||||
#endif
|
||||
|
||||
@ -480,13 +479,6 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint
|
||||
|
||||
QCoreApplicationPrivate::~QCoreApplicationPrivate()
|
||||
{
|
||||
#ifdef Q_OS_WASM
|
||||
EM_ASM(
|
||||
// unmount persistent directory as IDBFS
|
||||
// see also QTBUG-70002
|
||||
FS.unmount('/home/web_user');
|
||||
);
|
||||
#endif
|
||||
#ifndef QT_NO_QOBJECT
|
||||
cleanupThreadData();
|
||||
#endif
|
||||
@ -780,17 +772,8 @@ void QCoreApplicationPrivate::init()
|
||||
Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
|
||||
QCoreApplication::self = q;
|
||||
|
||||
#ifdef Q_OS_WASM
|
||||
EM_ASM(
|
||||
// mount and sync persistent filesystem to sandbox
|
||||
FS.mount(IDBFS, {}, '/home/web_user');
|
||||
FS.syncfs(true, function(err) {
|
||||
if (err)
|
||||
Module.print(err);
|
||||
});
|
||||
);
|
||||
|
||||
#if QT_CONFIG(thread)
|
||||
#ifdef Q_OS_WASM
|
||||
QThreadPrivate::idealThreadCount = emscripten::val::global("navigator")["hardwareConcurrency"].as<int>();
|
||||
#endif
|
||||
#endif
|
||||
|
@ -1696,13 +1696,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate()
|
||||
qt_gl_set_global_share_context(0);
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_WASM
|
||||
EM_ASM(
|
||||
// unmount persistent directory as IDBFS
|
||||
// see QTBUG-70002
|
||||
FS.unmount('/home/web_user');
|
||||
);
|
||||
#endif
|
||||
|
||||
platform_integration->destroy();
|
||||
|
||||
delete platform_theme;
|
||||
|
Loading…
Reference in New Issue
Block a user