diff --git a/ext/mini_racer_extension/mini_racer_extension.cc b/ext/mini_racer_extension/mini_racer_extension.cc index 9b56fc1..9a9bf26 100644 --- a/ext/mini_racer_extension/mini_racer_extension.cc +++ b/ext/mini_racer_extension/mini_racer_extension.cc @@ -6,6 +6,7 @@ #include #include #include +#include using namespace v8; @@ -52,18 +53,48 @@ static VALUE rb_eParseError; static VALUE rb_eScriptRuntimeError; static VALUE rb_cJavaScriptFunction; static VALUE rb_eSnapshotError; +static VALUE rb_ePlatformAlreadyInitializedError; static VALUE rb_cDateTime = Qnil; static Platform* current_platform = NULL; +static std::mutex platform_lock; + +static VALUE rb_platform_set_flag(VALUE _klass, VALUE flag_as_str) { + bool platform_already_initialized = false; + + platform_lock.lock(); + + if (current_platform == NULL) { + V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str)); + } else { + platform_already_initialized = true; + } + + platform_lock.unlock(); + + // important to raise outside of the lock + if (platform_already_initialized) { + rb_raise(rb_ePlatformAlreadyInitializedError, "The V8 platform is already initialized"); + } + + return Qnil; +} static void init_v8() { + // no need to wait for the lock if already initialized + if (current_platform != NULL) return; + + platform_lock.lock(); + if (current_platform == NULL) { - V8::InitializeICU(); - current_platform = platform::CreateDefaultPlatform(); - V8::InitializePlatform(current_platform); - V8::Initialize(); + V8::InitializeICU(); + current_platform = platform::CreateDefaultPlatform(); + V8::InitializePlatform(current_platform); + V8::Initialize(); } + + platform_lock.unlock(); } void* breaker(void *d) { @@ -720,6 +751,7 @@ extern "C" { VALUE rb_mMiniRacer = rb_define_module("MiniRacer"); VALUE rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject); VALUE rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject); + VALUE rb_cPlatform = rb_define_class_under(rb_mMiniRacer, "Platform", rb_cObject); VALUE rb_eEvalError = rb_define_class_under(rb_mMiniRacer, "EvalError", rb_eStandardError); rb_eScriptTerminatedError = rb_define_class_under(rb_mMiniRacer, "ScriptTerminatedError", rb_eEvalError); @@ -727,6 +759,7 @@ extern "C" { rb_eScriptRuntimeError = rb_define_class_under(rb_mMiniRacer, "RuntimeError", rb_eEvalError); rb_cJavaScriptFunction = rb_define_class_under(rb_mMiniRacer, "JavaScriptFunction", rb_cObject); rb_eSnapshotError = rb_define_class_under(rb_mMiniRacer, "SnapshotError", rb_eStandardError); + rb_ePlatformAlreadyInitializedError = rb_define_class_under(rb_mMiniRacer, "PlatformAlreadyInitialized", rb_eStandardError); VALUE rb_cExternalFunction = rb_define_class_under(rb_cContext, "ExternalFunction", rb_cObject); rb_define_method(rb_cContext, "stop", (VALUE(*)(...))&rb_context_stop, 0); @@ -741,6 +774,8 @@ extern "C" { rb_define_method(rb_cSnapshot, "size", (VALUE(*)(...))&rb_snapshot_size, 0); rb_define_method(rb_cSnapshot, "warmup", (VALUE(*)(...))&rb_snapshot_warmup, 1); rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1); + + rb_define_singleton_method(rb_cPlatform, "set_flag!", (VALUE(*)(...))&rb_platform_set_flag, 1); } } diff --git a/lib/mini_racer.rb b/lib/mini_racer.rb index 2cecc25..17bf278 100644 --- a/lib/mini_racer.rb +++ b/lib/mini_racer.rb @@ -8,6 +8,7 @@ module MiniRacer class ScriptTerminatedError < EvalError; end class ParseError < EvalError; end class SnapshotError < StandardError; end + class PlatformAlreadyInitialized < StandardError; end class RuntimeError < EvalError def initialize(message) @@ -39,6 +40,9 @@ module MiniRacer end end + # `::set_flag!` is defined in the C class + class Platform; end + # eval is defined in the C class class Context diff --git a/test/mini_racer_test.rb b/test/mini_racer_test.rb index 2a43b70..2e6d8c0 100644 --- a/test/mini_racer_test.rb +++ b/test/mini_racer_test.rb @@ -357,4 +357,13 @@ raise FooError, "I like foos" assert_equal 1, context.eval("Math.sin") end + + def test_platform_set_flag_raises_an_exception_if_already_initialized + # makes sure it's initialized + MiniRacer::Snapshot.new + + assert_raises(MiniRacer::PlatformAlreadyInitialized) do + MiniRacer::Platform.set_flag!("--noconcurrent_recompilation") + end + end end