1
0
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:
historic_bruno
2011-04-15 05:23:51 +00:00
parent 225346ffc9
commit 481f570a0e
4 changed files with 102 additions and 29 deletions
+62 -27
View File
@@ -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())
{
}