195 lines
4.6 KiB
C++
Executable File
195 lines
4.6 KiB
C++
Executable File
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
|
|
|
|
#include <string>
|
|
#include <Exception.hpp>
|
|
#include <noggit/AsyncLoader.h>
|
|
#include <noggit/AsyncObject.h>
|
|
#include <noggit/errorHandling.h>
|
|
#include <noggit/Log.h>
|
|
#include <util/exception_to_string.hpp>
|
|
|
|
#include <QtCore/QSettings>
|
|
|
|
#include <algorithm>
|
|
#include <list>
|
|
|
|
AsyncLoader* AsyncLoader::instance;
|
|
|
|
void AsyncLoader::setup(int threads)
|
|
{
|
|
// make sure there's always at least one thread otherwise nothing can load
|
|
instance = new AsyncLoader(std::max(1, threads));
|
|
}
|
|
|
|
bool AsyncLoader::is_loading()
|
|
{
|
|
std::lock_guard<std::mutex> const lock (_guard);
|
|
return !_currently_loading.empty();
|
|
}
|
|
|
|
void AsyncLoader::process()
|
|
{
|
|
AsyncObject* object = nullptr;
|
|
|
|
QSettings settings;
|
|
bool additional_log = settings.value("additional_file_loading_log", false).toBool();
|
|
|
|
while (!_stop)
|
|
{
|
|
{
|
|
std::unique_lock<std::mutex> lock (_guard);
|
|
|
|
_state_changed.wait
|
|
( lock
|
|
, [&]
|
|
{
|
|
return !!_stop || std::any_of ( _to_load.begin(), _to_load.end()
|
|
, [](auto const& to_load) { return !to_load.empty(); }
|
|
);
|
|
}
|
|
);
|
|
|
|
if (_stop)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (auto& to_load : _to_load)
|
|
{
|
|
if (to_load.empty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
object = to_load.front();
|
|
_currently_loading.emplace_back (object);
|
|
to_load.pop_front();
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
if (additional_log)
|
|
{
|
|
std::lock_guard<std::mutex> const lock(_guard);
|
|
LogDebug << "Loading file '" << (object->file_key().hasFilepath() ? object->file_key().filepath()
|
|
: std::to_string(object->file_key().fileDataID()))<< "'" << std::endl;
|
|
}
|
|
|
|
object->finishLoading();
|
|
|
|
if (additional_log)
|
|
{
|
|
std::lock_guard<std::mutex> const lock(_guard);
|
|
LogDebug << "Loaded file '" << (object->file_key().hasFilepath() ? object->file_key().filepath()
|
|
: std::to_string(object->file_key().fileDataID())) << "'" << std::endl;
|
|
}
|
|
|
|
{
|
|
std::lock_guard<std::mutex> const lock (_guard);
|
|
_currently_loading.remove (object);
|
|
_state_changed.notify_all();
|
|
}
|
|
}
|
|
catch (BlizzardArchive::Exceptions::FileReadFailedError const& e)
|
|
{
|
|
std::lock_guard<std::mutex> const lock(_guard);
|
|
|
|
object->error_on_loading();
|
|
// LogError << e.what() << std::endl;
|
|
|
|
if (object->is_required_when_saving())
|
|
{
|
|
_important_object_failed_loading = true;
|
|
}
|
|
|
|
_currently_loading.remove(object);
|
|
}
|
|
catch (...)
|
|
{
|
|
std::lock_guard<std::mutex> const lock(_guard);
|
|
|
|
object->error_on_loading();
|
|
std::string const reason{ util::exception_to_string(std::current_exception()) };
|
|
LogError << "Caught unknown error: " << reason << std::endl;
|
|
|
|
if (object->is_required_when_saving())
|
|
{
|
|
_important_object_failed_loading = true;
|
|
}
|
|
|
|
_currently_loading.remove(object);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AsyncLoader::queue_for_load (AsyncObject* object)
|
|
{
|
|
std::lock_guard<std::mutex> const lock (_guard);
|
|
_to_load[(size_t)object->loading_priority()].push_back (object);
|
|
_state_changed.notify_one();
|
|
}
|
|
|
|
void AsyncLoader::ensure_deletable (AsyncObject* object)
|
|
{
|
|
std::unique_lock<std::mutex> lock (_guard);
|
|
_state_changed.wait
|
|
( lock
|
|
, [&]
|
|
{
|
|
auto& to_load = _to_load[(size_t)object->loading_priority()];
|
|
auto const& it = std::find (to_load.begin(), to_load.end(), object);
|
|
|
|
// don't load it if it's just to delete it afterward
|
|
if (it != to_load.end())
|
|
{
|
|
to_load.erase(it);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return std::find (_currently_loading.begin(), _currently_loading.end(), object) == _currently_loading.end();
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
AsyncLoader::AsyncLoader(int numThreads)
|
|
: _stop (false)
|
|
{
|
|
// use half of the available threads
|
|
// unsigned int maxThreads = std::thread::hardware_concurrency() / 2;
|
|
// numThreads = maxThreads > numThreads ? maxThreads : numThreads;
|
|
|
|
for (int i = 0; i < numThreads; ++i)
|
|
{
|
|
_threads.emplace_back (&AsyncLoader::process, this);
|
|
}
|
|
}
|
|
|
|
AsyncLoader::~AsyncLoader()
|
|
{
|
|
{
|
|
std::unique_lock<std::mutex> lock(_guard);
|
|
_stop = true;
|
|
}
|
|
_state_changed.notify_all();
|
|
|
|
for (auto& thread : _threads)
|
|
{
|
|
thread.join();
|
|
}
|
|
}
|
|
|
|
bool AsyncLoader::important_object_failed_loading() const
|
|
{
|
|
return _important_object_failed_loading;
|
|
}
|
|
|
|
void AsyncLoader::reset_object_fail()
|
|
{
|
|
_important_object_failed_loading = false;
|
|
}
|