forked from mirrors/0ad
CMapGeneratorWorker thread safety issue. Fixes #783.
VFS access moved to main thread, where random map scripts are preloaded and stored prior to use by the worker thread. Adds LoadGlobalScript to ScriptInterface, for evaluating script content in the global scope. This was SVN commit r9261.
This commit is contained in:
@@ -19,10 +19,11 @@
|
||||
|
||||
#include "MapGenerator.h"
|
||||
|
||||
#include "lib/file/vfs/vfs_path.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/utf8.h"
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
|
||||
// TODO: what's a good default? perhaps based on map size
|
||||
#define RMS_RUNTIME_SIZE 96 * 1024 * 1024
|
||||
|
||||
@@ -48,6 +49,9 @@ void CMapGeneratorWorker::Initialize(const VfsPath& scriptFile, const std::strin
|
||||
m_ScriptPath = scriptFile;
|
||||
m_Settings = settings;
|
||||
|
||||
// Preload random map and library scripts
|
||||
fs_util::ForEachFile(g_VFS, L"maps/random/", PreloadScript, (uintptr_t)this, L"*.js", fs_util::DIR_RECURSIVE);
|
||||
|
||||
// Launch the worker thread
|
||||
int ret = pthread_create(&m_WorkerThread, NULL, &RunThread, this);
|
||||
debug_assert(ret == 0);
|
||||
@@ -96,7 +100,7 @@ bool CMapGeneratorWorker::Run()
|
||||
CScriptValRooted settingsVal = m_ScriptInterface->ParseJSON(m_Settings);
|
||||
if (settingsVal.undefined())
|
||||
{
|
||||
LOGERROR(L"CMapGeneratorWorker::Initialize: Failed to parse settings");
|
||||
LOGERROR(L"CMapGeneratorWorker::Run: Failed to parse settings");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -104,7 +108,7 @@ bool CMapGeneratorWorker::Run()
|
||||
uint32 seed;
|
||||
if (!m_ScriptInterface->GetProperty(settingsVal.get(), "Seed", seed))
|
||||
{ // No seed specified
|
||||
LOGWARNING(L"CMapGeneratorWorker::Initialize: No seed value specified - using 0");
|
||||
LOGWARNING(L"CMapGeneratorWorker::Run: No seed value specified - using 0");
|
||||
seed = 0;
|
||||
}
|
||||
|
||||
@@ -113,19 +117,24 @@ bool CMapGeneratorWorker::Run()
|
||||
// Copy settings to global variable
|
||||
if (!m_ScriptInterface->SetProperty(m_ScriptInterface->GetGlobalObject(), "g_MapSettings", settingsVal))
|
||||
{
|
||||
LOGERROR(L"CMapGeneratorWorker::Initialize: Failed to define g_MapSettings");
|
||||
LOGERROR(L"CMapGeneratorWorker::Run: Failed to define g_MapSettings");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load RMS
|
||||
LOGMESSAGE(L"Loading RMS '%ls'", m_ScriptPath.string().c_str());
|
||||
if (!m_ScriptInterface->LoadGlobalScriptFile(m_ScriptPath))
|
||||
// Find RMS file and run
|
||||
ScriptFilesMap::iterator it = m_ScriptFiles.find(m_ScriptPath);
|
||||
if (it != m_ScriptFiles.end())
|
||||
{
|
||||
LOGERROR(L"CMapGeneratorWorker::Initialize: Failed to load RMS '%ls'", m_ScriptPath.string().c_str());
|
||||
return false;
|
||||
LOGMESSAGE(L"CMapGeneratorWorker::Run: Loading RMS '%ls'", m_ScriptPath.string().c_str());
|
||||
// Note: we're not really accessing the file here
|
||||
if (m_ScriptInterface->LoadGlobalScript(m_ScriptPath, it->second))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
LOGERROR(L"CMapGeneratorWorker::Run: Failed to load RMS '%ls'", m_ScriptPath.string().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
int CMapGeneratorWorker::GetProgress()
|
||||
@@ -183,38 +192,64 @@ bool CMapGeneratorWorker::LoadScripts(const std::wstring& libraryName)
|
||||
// Mark this as loaded, to prevent it recursively loading itself
|
||||
m_LoadedLibraries.insert(libraryName);
|
||||
|
||||
VfsPath path = L"maps/random/" + libraryName + L"/";
|
||||
VfsPaths pathnames;
|
||||
LOGMESSAGE(L"Loading '%ls' library", libraryName.c_str());
|
||||
|
||||
// Load all scripts in mapgen directory
|
||||
LibError ret = fs_util::GetPathnames(g_VFS, path, L"*.js", pathnames);
|
||||
if (ret == INFO::OK)
|
||||
std::wstring libraryPath = L"maps/random/" + libraryName + L"/";
|
||||
|
||||
// Iterate preloaded script map, running library scripts
|
||||
for (ScriptFilesMap::iterator it = m_ScriptFiles.begin(); it != m_ScriptFiles.end(); ++it)
|
||||
{
|
||||
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
|
||||
std::wstring path = it->first.string();
|
||||
if (path.find(libraryPath) == 0)
|
||||
{
|
||||
LOGMESSAGE(L"Loading map generator script '%ls'", it->string().c_str());
|
||||
|
||||
if (!m_ScriptInterface->LoadGlobalScriptFile(*it))
|
||||
// This script is part of the library, so load it
|
||||
// Note: we're not really accessing the file here
|
||||
if (m_ScriptInterface->LoadGlobalScript(path, it->second))
|
||||
{
|
||||
LOGERROR(L"Failed to load script '%ls'", it->string().c_str());
|
||||
LOGMESSAGE(L"Successfully loaded library script '%ls'", path.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Script failed to run
|
||||
LOGERROR(L"Failed loading library script '%ls'", path.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some error reading directory
|
||||
wchar_t error[200];
|
||||
LOGERROR(L"Error reading scripts in directory '%ls': %hs", path.string().c_str(), error_description_r(ret, error, ARRAY_SIZE(error)));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LibError CMapGeneratorWorker::PreloadScript(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), const uintptr_t cbData)
|
||||
{
|
||||
CMapGeneratorWorker* self = (CMapGeneratorWorker*)cbData;
|
||||
|
||||
if (!VfsFileExists(g_VFS, pathname))
|
||||
{
|
||||
// This should never happen
|
||||
LOGERROR(L"CMapGeneratorWorker::PreloadScript: File '%ls' does not exist", pathname.string().c_str());
|
||||
return ERR::VFS_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
CVFSFile file;
|
||||
|
||||
PSRETURN ret = file.Load(g_VFS, pathname);
|
||||
if (ret != PSRETURN_OK)
|
||||
{
|
||||
LOGERROR(L"CMapGeneratorWorker::PreloadScript: Failed to load file '%ls', error=%hs", pathname.string().c_str(), GetErrorString(ret));
|
||||
return ERR::FAIL;
|
||||
}
|
||||
|
||||
std::string content(file.GetBuffer(), file.GetBuffer() + file.GetBufferSize());
|
||||
|
||||
self->m_ScriptFiles.insert(std::make_pair(pathname, wstring_from_utf8(content)));
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
CMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker())
|
||||
{
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user