Index: source/graphics/MapGenerator.cpp =================================================================== --- source/graphics/MapGenerator.cpp +++ source/graphics/MapGenerator.cpp @@ -39,7 +39,7 @@ #include #include -// TODO: what's a good default? perhaps based on map size +// TODO: Maybe this should be optimized depending on the map size. #define RMS_RUNTIME_SIZE 96 * 1024 * 1024 extern bool IsQuitRequested(); @@ -89,7 +89,7 @@ debug_SetThreadName("MapGenerator"); g_Profiler2.RegisterCurrentThread("MapGenerator"); - shared_ptr mapgenRuntime = ScriptInterface::CreateRuntime(g_ScriptRuntime, RMS_RUNTIME_SIZE); + shared_ptr mapgenRuntime = ScriptRuntime::CreateRuntime(RMS_RUNTIME_SIZE); // Enable the script to be aborted JS_SetInterruptCallback(mapgenRuntime->m_rt, MapGeneratorInterruptCallback); Index: source/network/NetServer.cpp =================================================================== --- source/network/NetServer.cpp +++ source/network/NetServer.cpp @@ -388,7 +388,8 @@ // To avoid the need for JS_SetContextThread, we create and use and destroy // the script interface entirely within this network thread - m_ScriptInterface = new ScriptInterface("Engine", "Net server", ScriptInterface::CreateRuntime(g_ScriptRuntime)); + shared_ptr netServerRuntime = ScriptRuntime::CreateRuntime(); + m_ScriptInterface = new ScriptInterface("Engine", "Net server", netServerRuntime); m_GameAttributes.init(m_ScriptInterface->GetJSRuntime(), JS::UndefinedValue()); while (true) Index: source/ps/GameSetup/GameSetup.cpp =================================================================== --- source/ps/GameSetup/GameSetup.cpp +++ source/ps/GameSetup/GameSetup.cpp @@ -109,7 +109,7 @@ bool g_DoRenderLogger = true; bool g_DoRenderCursor = true; -shared_ptr g_ScriptRuntime; +thread_local shared_ptr g_ScriptRuntime; static const int SANE_TEX_QUALITY_DEFAULT = 5; // keep in sync with code @@ -895,7 +895,7 @@ // their own threads and also their own runtimes. const int runtimeSize = 384 * 1024 * 1024; const int heapGrowthBytesGCTrigger = 20 * 1024 * 1024; - g_ScriptRuntime = ScriptInterface::CreateRuntime(shared_ptr(), runtimeSize, heapGrowthBytesGCTrigger); + g_ScriptRuntime = ScriptRuntime::CreateRuntime(runtimeSize, heapGrowthBytesGCTrigger); Mod::CacheEnabledModVersions(g_ScriptRuntime); Index: source/ps/Replay.cpp =================================================================== --- source/ps/Replay.cpp +++ source/ps/Replay.cpp @@ -208,7 +208,7 @@ const int runtimeSize = 384 * 1024 * 1024; const int heapGrowthBytesGCTrigger = 20 * 1024 * 1024; - g_ScriptRuntime = ScriptInterface::CreateRuntime(shared_ptr(), runtimeSize, heapGrowthBytesGCTrigger); + g_ScriptRuntime = ScriptRuntime::CreateRuntime(runtimeSize, heapGrowthBytesGCTrigger); Mod::CacheEnabledModVersions(g_ScriptRuntime); Index: source/scriptinterface/NativeWrapperDefns.h =================================================================== --- source/scriptinterface/NativeWrapperDefns.h +++ source/scriptinterface/NativeWrapperDefns.h @@ -183,8 +183,7 @@ JS::AutoValueVector argv(cx); argv.resize(sizeof...(Ts)); AssignOrToJSValHelper<0>(cx, argv, params...); - bool ok = CallFunction_(val, name, argv, &jsRet); - if (!ok) + if (!CallFunction_(val, name, argv, &jsRet)) return false; return FromJSVal(cx, jsRet, ret); } Index: source/scriptinterface/ScriptInterface.h =================================================================== --- source/scriptinterface/ScriptInterface.h +++ source/scriptinterface/ScriptInterface.h @@ -48,15 +48,13 @@ // but as large as necessary for all wrapped functions) #define SCRIPT_INTERFACE_MAX_ARGS 8 -// TODO: what's a good default? -#define DEFAULT_RUNTIME_SIZE 16 * 1024 * 1024 -#define DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER 2 * 1024 *1024 - struct ScriptInterface_impl; class ScriptRuntime; -extern shared_ptr g_ScriptRuntime; +// Using a global object for the runtime is a workaround until Simulation, AI, etc, +// use their own threads and also their own runtimes. +extern thread_local shared_ptr g_ScriptRuntime; /** @@ -73,17 +71,6 @@ public: - /** - * Returns a runtime, which can used to initialise any number of - * ScriptInterfaces contexts. Values created in one context may be used - * in any other context from the same runtime (but not any other runtime). - * Each runtime should only ever be used on a single thread. - * @param runtimeSize Maximum size in bytes of the new runtime - */ - static shared_ptr CreateRuntime(shared_ptr parentRuntime = shared_ptr(), int runtimeSize = DEFAULT_RUNTIME_SIZE, - int heapGrowthBytesGCTrigger = DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER); - - /** * Constructor. * @param nativeScopeName Name of global object that functions (via RegisterFunction) will @@ -442,7 +429,7 @@ void Register(const char* name, JSNative fptr, size_t nargs) const; // Take care to keep this declaration before heap rooted members. Destructors of heap rooted - // members have to be called before the runtime destructor. + // members have to be called before the custom destructor of ScriptInterface_impl. std::unique_ptr m; boost::rand48* m_rng; Index: source/scriptinterface/ScriptInterface.cpp =================================================================== --- source/scriptinterface/ScriptInterface.cpp +++ source/scriptinterface/ScriptInterface.cpp @@ -64,7 +64,7 @@ JSContext* m_cx; JS::PersistentRootedObject m_glob; // global scope object - JSCompartment* m_comp; + JSCompartment* m_formerCompartment; boost::rand48* m_rng; JS::PersistentRootedObject m_nativeScope; // native function scope object }; @@ -333,8 +333,6 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const shared_ptr& runtime) : m_runtime(runtime), m_glob(runtime->m_rt), m_nativeScope(runtime->m_rt) { - bool ok; - m_cx = JS_NewContext(m_runtime->m_rt, STACK_CHUNK_SIZE); ENSURE(m_cx); @@ -362,9 +360,8 @@ JSAutoRequest rq(m_cx); JS::RootedObject globalRootedVal(m_cx, JS_NewGlobalObject(m_cx, &global_class, NULL, JS::OnNewGlobalHookOption::FireOnNewGlobalHook, opt)); - m_comp = JS_EnterCompartment(m_cx, globalRootedVal); - ok = JS_InitStandardClasses(m_cx, globalRootedVal); - ENSURE(ok); + m_formerCompartment = JS_EnterCompartment(m_cx, globalRootedVal); + ENSURE(JS_InitStandardClasses(m_cx, globalRootedVal)); m_glob = globalRootedVal.get(); JS_DefineProperty(m_cx, m_glob, "global", globalRootedVal, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); @@ -390,7 +387,7 @@ m_runtime->UnRegisterContext(m_cx); { JSAutoRequest rq(m_cx); - JS_LeaveCompartment(m_cx, m_comp); + JS_LeaveCompartment(m_cx, m_formerCompartment); } JS_DestroyContext(m_cx); } @@ -563,9 +560,7 @@ if (!JS_HasProperty(m->m_cx, obj, name, &found) || !found) return false; - bool ok = JS_CallFunctionName(m->m_cx, obj, name, argv, ret); - - return ok; + return JS_CallFunctionName(m->m_cx, obj, name, argv, ret); } bool ScriptInterface::CreateObject_(JSContext* cx, JS::MutableHandleObject object) @@ -848,11 +843,6 @@ return JS_CallFunction(m->m_cx, nullptr, func, JS::HandleValueArray::empty(), &rval); } -shared_ptr ScriptInterface::CreateRuntime(shared_ptr parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger) -{ - return shared_ptr(new ScriptRuntime(parentRuntime, runtimeSize, heapGrowthBytesGCTrigger)); -} - bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code) const { JSAutoRequest rq(m->m_cx); Index: source/scriptinterface/ScriptRuntime.h =================================================================== --- source/scriptinterface/ScriptRuntime.h +++ source/scriptinterface/ScriptRuntime.h @@ -25,6 +25,10 @@ #define STACK_CHUNK_SIZE 8192 +// Those are minimal defaults. The runtime for the main game is larger and GCs upon a larger growth. +#define DEFAULT_RUNTIME_SIZE 16 * 1024 * 1024 +#define DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER 2 * 1024 * 1024 + /** * Abstraction around a SpiderMonkey JSRuntime. * Each ScriptRuntime can be used to initialize several ScriptInterface @@ -38,9 +42,21 @@ class ScriptRuntime { public: - ScriptRuntime(shared_ptr parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger); + ScriptRuntime(int runtimeSize, int heapGrowthBytesGCTrigger); ~ScriptRuntime(); + /** + * Returns a runtime, which can used to initialise any number of + * ScriptInterfaces contexts. Values created in one context may be used + * in any other context from the same runtime (but not any other runtime). + * Each runtime should only ever be used on a single thread. + * @param runtimeSize Maximum size in bytes of the new runtime + * @param heapGrowthBytesGCTrigger Size in bytes of cumulated allocations after which a GC will be triggered + */ + static shared_ptr CreateRuntime( + int runtimeSize = DEFAULT_RUNTIME_SIZE, + int heapGrowthBytesGCTrigger = DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER); + /** * MaybeIncrementalRuntimeGC tries to determine whether a runtime-wide garbage collection would free up enough memory to * be worth the amount of time it would take. It does this with our own logic and NOT some predefined JSAPI logic because Index: source/scriptinterface/ScriptRuntime.cpp =================================================================== --- source/scriptinterface/ScriptRuntime.cpp +++ source/scriptinterface/ScriptRuntime.cpp @@ -89,7 +89,12 @@ #endif } -ScriptRuntime::ScriptRuntime(shared_ptr parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger): +shared_ptr ScriptRuntime::CreateRuntime(int runtimeSize, int heapGrowthBytesGCTrigger) +{ + return shared_ptr(new ScriptRuntime(runtimeSize, heapGrowthBytesGCTrigger)); +} + +ScriptRuntime::ScriptRuntime(int runtimeSize, int heapGrowthBytesGCTrigger): m_LastGCBytes(0), m_LastGCCheck(0.0f), m_HeapGrowthBytesGCTrigger(heapGrowthBytesGCTrigger), @@ -97,8 +102,7 @@ { ENSURE(ScriptEngine::IsInitialised() && "The ScriptEngine must be initialized before constructing any ScriptRuntimes!"); - JSRuntime* parentJSRuntime = parentRuntime ? parentRuntime->m_rt : nullptr; - m_rt = JS_NewRuntime(runtimeSize, JS::DefaultNurseryBytes, parentJSRuntime); + m_rt = JS_NewRuntime(runtimeSize, JS::DefaultNurseryBytes, nullptr); ENSURE(m_rt); // TODO: error handling JS::SetGCSliceCallback(m_rt, GCSliceCallbackHook); Index: source/test_setup.cpp =================================================================== --- source/test_setup.cpp +++ source/test_setup.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -37,7 +37,7 @@ #include "lib/sysdep/sysdep.h" #include "ps/Profiler2.h" #include "scriptinterface/ScriptEngine.h" -#include "scriptinterface/ScriptInterface.h" +#include "scriptinterface/ScriptRuntime.h" class LeakReporter : public CxxTest::GlobalFixture { @@ -80,7 +80,7 @@ g_Profiler2.Initialise(); m_ScriptEngine = new ScriptEngine; - g_ScriptRuntime = ScriptInterface::CreateRuntime(); + g_ScriptRuntime = ScriptRuntime::CreateRuntime(); return true; }