diff options
| author | BoiledElectricity <james.dellamorte@gmail.com> | 2026-06-16 16:20:02 +0200 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2026-06-16 16:20:02 +0200 |
| commit | ce14fc91fb84c51650f57c059396756e1fc3c4cf (patch) | |
| tree | fe4de221202edf705f3b5383054bbf0b79b7d4bc /src/qt_common | |
| parent | c27266c4cd5feabd01c8de89573c229a0ed5991d (diff) | |
[game_list] fix crash and flashing from the directory watcher (#4099)
So the game list watcher could rebuild the content providers
(CreateFactories) while the populate worker was still scanning them, and
the worker would walk a torn-down RegisteredCache and segfault in
OpenFileOrDirectoryConcat. Fixed by stopping and joining the worker
before rebuilding.
On macOS the watcher also re-armed itself every populate. Re-adding the
same paths makes QFileSystemWatcher re-emit directoryChanged (the
FSEvent comes in async, so the blockSignals guard misses it), so it just
kept refreshing and the list flashed forever. Now it only re-arms when
the watched dirs actually changed.
Also null-guarded OpenFileOrDirectoryConcat so a torn-down cache cant
null-deref there.
Related: https://github.com/eden-emulator/Issue-Reports/issues/336
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4099
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Diffstat (limited to 'src/qt_common')
| -rw-r--r-- | src/qt_common/game_list/model.cpp | 8 | ||||
| -rw-r--r-- | src/qt_common/game_list/model.h | 4 |
2 files changed, 12 insertions, 0 deletions
diff --git a/src/qt_common/game_list/model.cpp b/src/qt_common/game_list/model.cpp index 2f8deaa0c5..91ec80c525 100644 --- a/src/qt_common/game_list/model.cpp +++ b/src/qt_common/game_list/model.cpp @@ -57,6 +57,12 @@ void GameListModel::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) { QThreadPool::globalInstance()->start(current_worker.get()); } +void GameListModel::StopWorker() { + // ~GameListWorker sets stop_requested and blocks until run() finishes, so this returns only + // once the worker is no longer touching the content providers. + current_worker.reset(); +} + void GameListModel::WorkerEvent() { current_worker->ProcessEvents(this); } @@ -202,6 +208,7 @@ void GameListModel::RefreshGameDirectory() { if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); + StopWorker(); QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs); PopulateAsync(UISettings::values.game_dirs); } @@ -210,6 +217,7 @@ void GameListModel::RefreshGameDirectory() { void GameListModel::RefreshExternalContent() { if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { LOG_INFO(Frontend, "External content directory changed. Clearing metadata cache."); + StopWorker(); QtCommon::Game::ResetMetadata(false); QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs); PopulateAsync(UISettings::values.game_dirs); diff --git a/src/qt_common/game_list/model.h b/src/qt_common/game_list/model.h index d0a116beb6..ce1fcc87b7 100644 --- a/src/qt_common/game_list/model.h +++ b/src/qt_common/game_list/model.h @@ -52,6 +52,10 @@ public: void DonePopulating(const QStringList& watch_list); void PopulateAsync(QVector<UISettings::GameDir>& game_dirs); + // Stops and joins the running populate worker, if any. Must be called before rebuilding the + // content providers (CreateFactories), otherwise the worker keeps scanning a cache that is + // being torn down underneath it. + void StopWorker(); void WorkerEvent(); bool IsEmpty() const; |
