Page MenuHomePhorge

D120.1732452582.diff
No OneTemporary

Size
16 KB
Referenced Files
None
Subscribers
None

D120.1732452582.diff

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -12,6 +12,7 @@
qt-job.cpp
${CMAKE_CURRENT_BINARY_DIR}/kazv-version.cpp
matrix-sdk.cpp
+ kazv-session-lock-guard.cpp
matrix-room.cpp
matrix-room-list.cpp
matrix-room-timeline.cpp
diff --git a/src/kazv-session-lock-guard.hpp b/src/kazv-session-lock-guard.hpp
new file mode 100644
--- /dev/null
+++ b/src/kazv-session-lock-guard.hpp
@@ -0,0 +1,21 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+#include <kazv-defs.hpp>
+#include <memory>
+#include <filesystem>
+
+class KazvSessionLockGuard
+{
+public:
+ KazvSessionLockGuard(const std::filesystem::path &sessionDir);
+ ~KazvSessionLockGuard();
+
+private:
+ struct Private;
+ std::unique_ptr<Private> m_d;
+};
diff --git a/src/kazv-session-lock-guard.cpp b/src/kazv-session-lock-guard.cpp
new file mode 100644
--- /dev/null
+++ b/src/kazv-session-lock-guard.cpp
@@ -0,0 +1,64 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <kazv-defs.hpp>
+#include <kazv-platform.hpp>
+#include <cstring>
+#if !KAZV_IS_WINDOWS
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+#include "kazv-session-lock-guard.hpp"
+#include "kazv-log.hpp"
+
+struct KazvSessionLockGuard::Private
+{
+ std::string lockFilePath;
+ int fd{-1};
+
+ void cleanup()
+ {
+#if !KAZV_IS_WINDOWS
+ qCDebug(kazvLog) << "KazvSessionLockGuard::Private::cleanup" << QString::fromStdString(lockFilePath);
+ if (fd != -1) {
+ qCDebug(kazvLog) << "unlocking session";
+ lockf(fd, F_ULOCK, 0);
+ close(fd);
+ fd = -1;
+ }
+#endif
+ }
+
+ Private(const std::filesystem::path &sessionDir)
+ : lockFilePath((sessionDir / "lock").string())
+ {
+#if !KAZV_IS_WINDOWS
+ qCDebug(kazvLog) << "KazvSessionLockGuard::Private::Private" << QString::fromStdString(lockFilePath);
+ fd = open(lockFilePath.data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ throw std::runtime_error{std::string("Cannot open lock file: ") + std::strerror(errno)};
+ }
+
+ auto res = lockf(fd, F_TLOCK, 0);
+ if (res == -1) {
+ cleanup();
+ throw std::runtime_error{std::string("Cannot grab lock file: ") + std::strerror(errno)};
+ }
+#endif
+ }
+
+ ~Private()
+ {
+ cleanup();
+ }
+};
+
+KazvSessionLockGuard::KazvSessionLockGuard(const std::filesystem::path &sessionDir)
+ : m_d(std::make_unique<Private>(sessionDir))
+{
+}
+
+KazvSessionLockGuard::~KazvSessionLockGuard() = default;
diff --git a/src/matrix-sdk.hpp b/src/matrix-sdk.hpp
--- a/src/matrix-sdk.hpp
+++ b/src/matrix-sdk.hpp
@@ -43,6 +43,7 @@
Q_OBJECT
QML_ELEMENT
+ QString m_userDataDir;
std::unique_ptr<MatrixSdkPrivate> m_d;
/// @param d A dynamically allocated d-pointer, whose ownership
@@ -78,7 +79,7 @@
private:
// Replaces the store with another one
- void emplace(std::optional<Kazv::SdkModel> model);
+ void emplace(std::optional<Kazv::SdkModel> model, std::unique_ptr<KazvSessionLockGuard> lockGuard);
Q_SIGNALS:
void trigger(Kazv::KazvEvent e);
diff --git a/src/matrix-sdk.cpp b/src/matrix-sdk.cpp
--- a/src/matrix-sdk.cpp
+++ b/src/matrix-sdk.cpp
@@ -44,6 +44,7 @@
#include "matrix-user-given-attrs-map.hpp"
#include "kazv-log.hpp"
#include "matrix-utils.hpp"
+#include "kazv-session-lock-guard.hpp"
using namespace Kazv;
// Sdk with qt event loop, identity transform and no enhancers
@@ -78,10 +79,11 @@
struct MatrixSdkPrivate
{
- MatrixSdkPrivate(MatrixSdk *q, bool testing);
- MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model);
+ MatrixSdkPrivate(MatrixSdk *q, bool testing, std::unique_ptr<KazvSessionLockGuard> lockGuard);
+ MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model, std::unique_ptr<KazvSessionLockGuard> lockGuard);
bool testing;
std::string userDataDir;
+ std::unique_ptr<KazvSessionLockGuard> lockGuard;
RandomInterface randomGenerator;
QThread *thread;
QObject *obj;
@@ -126,25 +128,25 @@
explicit CleanupHelper(std::unique_ptr<MatrixSdkPrivate> d)
: oldD(std::move(d))
{
- // After the thread's event loop is finished, we can delete the root object
- connect(oldD->thread, &QThread::finished, oldD->obj, &QObject::deleteLater);
- connect(oldD->obj, &QObject::destroyed, this, &QObject::deleteLater);
}
- std::shared_ptr<MatrixSdkPrivate> oldD;
+ std::unique_ptr<MatrixSdkPrivate> oldD;
void cleanup()
{
qCInfo(kazvLog) << "start to clean up everything";
oldD->clientOnSecondaryRoot.stopSyncing()
- .then([oldD=oldD](auto &&) {
+ .then([obj=oldD->obj, thread=oldD->thread](auto &&) {
qCDebug(kazvLog) << "stopped syncing";
- QMetaObject::invokeMethod(oldD->obj, [thread=oldD->thread]() {
+ QMetaObject::invokeMethod(obj, [thread]() {
thread->quit();
});
});
oldD->thread->wait();
oldD->thread->deleteLater();
+ // After the thread's event loop is finished, we can delete the root object
+ oldD->obj->deleteLater();
+ this->deleteLater();
qCInfo(kazvLog) << "thread is done";
}
};
@@ -178,6 +180,15 @@
return;
}
+ if (!lockGuard) {
+ try {
+ lockGuard = std::make_unique<KazvSessionLockGuard>(sessionDir);
+ } catch (const std::runtime_error &e) {
+ qCWarning(kazvLog) << "Error locking session: " << e.what();
+ return;
+ }
+ }
+
{
auto storeStream = std::ofstream(storeFile);
if (! storeStream) {
@@ -199,9 +210,10 @@
}
}
-MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing)
+MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing, std::unique_ptr<KazvSessionLockGuard> lockGuard)
: testing(testing)
, userDataDir{kazvUserDataDir().toStdString()}
+ , lockGuard(std::move(lockGuard))
, randomGenerator(QtRandAdapter{})
, thread(new QThread())
, obj(new QObject())
@@ -222,9 +234,10 @@
obj->moveToThread(thread);
}
-MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model)
+MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model, std::unique_ptr<KazvSessionLockGuard> lockGuard)
: testing(testing)
, userDataDir{kazvUserDataDir().toStdString()}
+ , lockGuard(std::move(lockGuard))
, randomGenerator(QtRandAdapter{})
, thread(new QThread())
, obj(new QObject())
@@ -247,6 +260,7 @@
MatrixSdk::MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent)
: QObject(parent)
+ , m_userDataDir(kazvUserDataDir())
, m_d(std::move(d))
{
init();
@@ -300,12 +314,12 @@
}
MatrixSdk::MatrixSdk(QObject *parent)
- : MatrixSdk(std::make_unique<MatrixSdkPrivate>(this, /* testing = */ false), parent)
+ : MatrixSdk(std::make_unique<MatrixSdkPrivate>(this, /* testing = */ false, std::unique_ptr<KazvSessionLockGuard>()), parent)
{
}
MatrixSdk::MatrixSdk(SdkModel model, bool testing, QObject *parent)
- : MatrixSdk(std::make_unique<MatrixSdkPrivate>(this, testing, std::move(model)), parent)
+ : MatrixSdk(std::make_unique<MatrixSdkPrivate>(this, testing, std::move(model), std::unique_ptr<KazvSessionLockGuard>()), parent)
{
}
@@ -371,7 +385,7 @@
return new MatrixRoomList(m_d->clientOnSecondaryRoot);
}
-void MatrixSdk::emplace(std::optional<SdkModel> model)
+void MatrixSdk::emplace(std::optional<SdkModel> model, std::unique_ptr<KazvSessionLockGuard> lockGuard)
{
auto testing = m_d->testing;
auto userDataDir = m_d->userDataDir;
@@ -381,8 +395,8 @@
}
m_d = (model.has_value()
- ? std::make_unique<MatrixSdkPrivate>(this, testing, std::move(model.value()))
- : std::make_unique<MatrixSdkPrivate>(this, testing));
+ ? std::make_unique<MatrixSdkPrivate>(this, testing, std::move(model.value()), std::move(lockGuard))
+ : std::make_unique<MatrixSdkPrivate>(this, testing, std::move(lockGuard)));
m_d->userDataDir = userDataDir;
// Re-initialize lager-qt cursors and watchable connections
@@ -436,7 +450,8 @@
bool MatrixSdk::loadSession(QString sessionName)
{
using StdPath = std::filesystem::path;
- auto loadFromSession = [this, sessionName](StdPath sessionDir) {
+ std::unique_ptr<KazvSessionLockGuard> lockGuard;
+ auto loadFromSession = [this, sessionName, &lockGuard](StdPath sessionDir) {
auto storeFile = sessionDir / "store";
auto metadataFile = sessionDir / "metadata";
@@ -488,7 +503,7 @@
qDebug() << "Error when loading session:" << QString::fromStdString(e.what());
return false;
}
- emplace(std::move(model));
+ emplace(std::move(model), std::move(lockGuard));
return true;
};
@@ -500,6 +515,12 @@
auto sessionId = parts[1].toStdString();
auto encodedUserId = encodeBase64(userId, Base64Opts::urlSafe);
auto sessionDir = userDataDir / "sessions" / encodedUserId / sessionId;
+ try {
+ lockGuard = std::make_unique<KazvSessionLockGuard>(sessionDir);
+ } catch (const std::runtime_error &e) {
+ qCWarning(kazvLog) << "Error locking session: " << e.what();
+ return false;
+ }
return loadFromSession(sessionDir);
}
qDebug(kazvLog) << "no session found for" << sessionName;
@@ -508,7 +529,7 @@
bool MatrixSdk::startNewSession()
{
- emplace(std::nullopt);
+ emplace(std::nullopt, std::unique_ptr<KazvSessionLockGuard>());
return true;
}
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -9,6 +9,10 @@
target_include_directories(kazvtestlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(kazvtestlib PUBLIC kazvprivlib libkazv::kazvtestfixtures)
+add_executable(matrix-sdk-session-loader matrix-sdk-session-loader.cpp matrix-sdk-sessions-test.cpp)
+target_compile_definitions(matrix-sdk-session-loader PRIVATE MATRIX_SDK_SESSIONS_TEST_NO_MAIN)
+target_link_libraries(matrix-sdk-session-loader Qt${QT_MAJOR_VERSION}::Test kazvtestlib)
+
ecm_add_tests(
qt-job-handler-test.cpp
qt-promise-handler-test.cpp
diff --git a/src/tests/matrix-sdk-session-loader.cpp b/src/tests/matrix-sdk-session-loader.cpp
new file mode 100644
--- /dev/null
+++ b/src/tests/matrix-sdk-session-loader.cpp
@@ -0,0 +1,26 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <kazv-defs.hpp>
+#include <iostream>
+#include <QString>
+#include "matrix-sdk-sessions-test.hpp"
+
+int main([[maybe_unused]] int argc, char *argv[])
+{
+ std::string userDataDir = argv[1];
+ std::string sessionName = argv[2];
+ auto sdk = MatrixSdkSessionsTest::makeMatrixSdkImpl(userDataDir);
+ auto res = sdk->loadSession(QString::fromStdString(sessionName));
+ if (!res) {
+ std::cout << "cannot load session" << std::endl;
+ return 1;
+ }
+
+ std::cout << "loaded session" << std::endl;
+ while (1) {}
+ return 0;
+}
diff --git a/src/tests/matrix-sdk-sessions-test.hpp b/src/tests/matrix-sdk-sessions-test.hpp
new file mode 100644
--- /dev/null
+++ b/src/tests/matrix-sdk-sessions-test.hpp
@@ -0,0 +1,47 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+#include <kazv-defs.hpp>
+#include <memory>
+#include <QObject>
+#include <matrix-sdk.hpp>
+
+class MatrixSdkSessionsTest : public QObject
+{
+ Q_OBJECT
+
+private:
+ std::string m_userDataDir;
+
+ void clearDir();
+ void createSession(std::string userId, std::string deviceId);
+
+public:
+ template<class ...Args>
+ static std::unique_ptr<MatrixSdk> makeMatrixSdkImpl(std::string userDataDir, Args &&...args)
+ {
+ std::unique_ptr<MatrixSdk> sdk(new MatrixSdk(std::forward<Args>(args)...));
+ sdk->setUserDataDir(userDataDir);
+ return sdk;
+ }
+
+ template<class ...Args>
+ std::unique_ptr<MatrixSdk> makeMatrixSdk(Args &&...args)
+ {
+ return makeMatrixSdkImpl(m_userDataDir, std::forward<Args>(args)...);
+ }
+
+private Q_SLOTS:
+ void initTestCase();
+ void init();
+ void cleanup();
+
+ void testListSessions();
+ void testListLegacySessions();
+ void testLoadSession();
+ void testSessionLock();
+};
diff --git a/src/tests/matrix-sdk-sessions-test.cpp b/src/tests/matrix-sdk-sessions-test.cpp
--- a/src/tests/matrix-sdk-sessions-test.cpp
+++ b/src/tests/matrix-sdk-sessions-test.cpp
@@ -5,47 +5,22 @@
*/
#include <kazv-defs.hpp>
+#include <kazv-platform.hpp>
#include <memory>
#include <filesystem>
#include <fstream>
#include <QObject>
#include <QtTest>
+#include <QProcess>
+#include <QCoreApplication>
#include <crypto/base64.hpp>
-#include <matrix-sdk.hpp>
#include "test-temp.hpp"
+#include "matrix-sdk-sessions-test.hpp"
using namespace Kazv;
namespace Fs = std::filesystem;
using StdPath = Fs::path;
-class MatrixSdkSessionsTest : public QObject
-{
- Q_OBJECT
-
-private:
- std::string m_userDataDir;
-
- void clearDir();
- void createSession(std::string userId, std::string deviceId);
-
- template<class ...Args>
- std::unique_ptr<MatrixSdk> makeMatrixSdk(Args &&...args)
- {
- std::unique_ptr<MatrixSdk> sdk(new MatrixSdk(std::forward<Args>(args)...));
- sdk->setUserDataDir(m_userDataDir);
- return sdk;
- }
-
-private Q_SLOTS:
- void initTestCase();
- void init();
- void cleanup();
-
- void testListSessions();
- void testListLegacySessions();
- void testLoadSession();
-};
-
void MatrixSdkSessionsTest::initTestCase()
{
auto dir = StdPath(kazvTestTempDir()) / "sessions-test";
@@ -70,8 +45,13 @@
SdkModel model;
model.client.userId = userId;
model.client.deviceId = deviceId;
- auto sdk = makeMatrixSdk(model, /* testing = */ false);
- sdk->serializeToFile();
+ {
+ auto sdk = makeMatrixSdk(model, /* testing = */ false);
+ sdk->serializeToFile();
+ }
+ // Not ideal, but this is the only way to wait for
+ // MatrixSdkPravite to be destroyed.
+ QTest::qWait(100);
}
void MatrixSdkSessionsTest::testListSessions()
@@ -117,15 +97,44 @@
void MatrixSdkSessionsTest::testLoadSession()
{
- createSession("@mew:example.com", "device1");
+ createSession("@mew:example.com", "device4");
auto sdk = makeMatrixSdk();
- auto res = sdk->loadSession("@mew:example.com/device1");
+ auto res = sdk->loadSession("@mew:example.com/device4");
QVERIFY(res);
QCOMPARE(sdk->userId(), QStringLiteral("@mew:example.com"));
- QCOMPARE(sdk->deviceId(), QStringLiteral("device1"));
+ QCOMPARE(sdk->deviceId(), QStringLiteral("device4"));
}
-QTEST_MAIN(MatrixSdkSessionsTest)
+void MatrixSdkSessionsTest::testSessionLock()
+{
+#if KAZV_IS_WINDOWS
+ QSKIP("Skipping because session lock is not yet supported on Windows");
+#else
+ createSession("@mew:example.com", "device5");
+
+ auto program = QCoreApplication::applicationDirPath() + QStringLiteral("/matrix-sdk-session-loader");
+ QProcess proc1;
+ QStringList args{
+ QString::fromStdString(m_userDataDir),
+ QStringLiteral("@mew:example.com/device5"),
+ };
+ proc1.start(program, args);
+ proc1.waitForReadyRead();
+ auto line = proc1.readLine();
+ QCOMPARE(line, QByteArray("loaded session\n"));
+
+ QProcess proc2;
+ proc2.start(program, args);
+ auto res = proc2.waitForFinished();
+ QVERIFY(res);
+ QCOMPARE(proc2.exitStatus(), QProcess::NormalExit);
+ QCOMPARE(proc2.exitCode(), 1);
-#include "matrix-sdk-sessions-test.moc"
+ proc1.kill();
+#endif
+}
+
+#ifndef MATRIX_SDK_SESSIONS_TEST_NO_MAIN
+QTEST_MAIN(MatrixSdkSessionsTest)
+#endif

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 24, 4:49 AM (20 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39587
Default Alt Text
D120.1732452582.diff (16 KB)

Event Timeline