Skip to content

Commit 6a1af97

Browse files
async gamelists writes
1 parent cfefa1e commit 6a1af97

3 files changed

Lines changed: 60 additions & 7 deletions

File tree

es-app/src/Gamelist.cpp

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#include "Gamelist.h"
22

3+
#include <algorithm>
34
#include <chrono>
5+
#include <fstream>
6+
#include <future>
7+
#include <mutex>
8+
#include <sstream>
49

510
#include "utils/FileSystemUtil.h"
611
#include "FileData.h"
@@ -9,6 +14,11 @@
914
#include "Settings.h"
1015
#include "SystemData.h"
1116
#include <pugixml.hpp>
17+
#include <unordered_map>
18+
19+
// Async gamelist write infrastructure
20+
static std::mutex sGamelistWriteMutex;
21+
static std::vector<std::future<void>> sGamelistPendingWrites;
1222

1323
FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType type)
1424
{
@@ -325,22 +335,59 @@ void updateGamelist(SystemData* system)
325335
// now write the file
326336

327337
if (numUpdated > 0) {
328-
const auto startTs = std::chrono::system_clock::now();
329-
330338
//make sure the folders leading up to this path exist (or the write will fail)
331339
std::string xmlWritePath(system->getGamelistPath(true));
332340
Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath));
333341

334342
LOG(LogInfo) << "Added/Updated " << numUpdated << " entities in '" << xmlReadPath << "'";
335343

336-
if (!doc.save_file(xmlWritePath.c_str())) {
337-
LOG(LogError) << "Error saving gamelist.xml to \"" << xmlWritePath << "\" (for system " << system->getName() << ")!";
338-
}
344+
// Serialize the XML document to a string on the main thread,
345+
// then write the string to file on a background thread to
346+
// avoid blocking the UI on slow NAS I/O.
347+
std::stringstream ss;
348+
doc.save(ss);
349+
std::string xmlContent = ss.str();
350+
std::string sysName = system->getName();
351+
352+
{
353+
// Clean up finished futures
354+
std::lock_guard<std::mutex> lock(sGamelistWriteMutex);
355+
sGamelistPendingWrites.erase(
356+
std::remove_if(sGamelistPendingWrites.begin(), sGamelistPendingWrites.end(),
357+
[](std::future<void>& f) {
358+
return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
359+
}),
360+
sGamelistPendingWrites.end());
361+
362+
sGamelistPendingWrites.push_back(std::async(std::launch::async,
363+
[xmlContent, xmlWritePath, sysName]() {
364+
const auto startTs = std::chrono::system_clock::now();
365+
366+
std::ofstream outFile(xmlWritePath, std::ios::out | std::ios::trunc);
367+
if (outFile.is_open()) {
368+
outFile << xmlContent;
369+
outFile.close();
370+
} else {
371+
LOG(LogError) << "Error saving gamelist.xml to \"" << xmlWritePath << "\" (for system " << sysName << ")!";
372+
}
339373

340-
const auto endTs = std::chrono::system_clock::now();
341-
LOG(LogInfo) << "Saved gamelist.xml for system \"" << system->getName() << "\" in " << std::chrono::duration_cast<std::chrono::milliseconds>(endTs - startTs).count() << " ms";
374+
const auto endTs = std::chrono::system_clock::now();
375+
LOG(LogInfo) << "Saved gamelist.xml for system \"" << sysName << "\" in " << std::chrono::duration_cast<std::chrono::milliseconds>(endTs - startTs).count() << " ms";
376+
}));
377+
}
342378
}
343379
}else{
344380
LOG(LogError) << "Found no root folder for system \"" << system->getName() << "\"!";
345381
}
346382
}
383+
384+
void waitForGamelistWrites()
385+
{
386+
std::lock_guard<std::mutex> lock(sGamelistWriteMutex);
387+
for (auto& f : sGamelistPendingWrites)
388+
{
389+
if (f.valid())
390+
f.wait();
391+
}
392+
sGamelistPendingWrites.clear();
393+
}

es-app/src/Gamelist.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ void parseGamelist(SystemData* system);
1010
// Writes currently loaded metadata for a SystemData to gamelist.xml.
1111
void updateGamelist(SystemData* system);
1212

13+
// Blocks until all pending async gamelist writes have completed.
14+
// Must be called before process exit to avoid data loss.
15+
void waitForGamelistWrites();
16+
1317
#endif // ES_APP_GAME_LIST_H

es-app/src/main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "views/ViewController.h"
99
#include "CollectionSystemManager.h"
1010
#include "EmulationStation.h"
11+
#include "Gamelist.h"
1112
#include "InputManager.h"
1213
#include "Log.h"
1314
#include "MameNames.h"
@@ -484,6 +485,7 @@ int main(int argc, char* argv[])
484485

485486
MameNames::deinit();
486487
CollectionSystemManager::deinit();
488+
waitForGamelistWrites();
487489
SystemData::deleteSystems();
488490

489491
// call this ONLY when linking with FreeImage as a static library

0 commit comments

Comments
 (0)