Making it possible to set V8 runtime flags

Only caveat: such flags can only be set before the V8 platform gets initialized.

Comes in handy to deactivate for example V8's aggressive use of `pthread` which
somehow seems to not play too nice with Unicorn:

```
MiniRacer::Platform.set_flag! '--noconcurrent_recompilation'
MiniRacer::Platform.set_flag! '--noconcurrent_sweeping'
```

Also protecting the platform initialization with a mutex, not sure
how/why current concurrent tests never seem to hit bugs without that mutex.

Added just one test on the new `MiniRacer::Platform.set_flag!` method, and
unsure how to make other tests without making tests run in order (bad),
given that flags can only be set before anything else is done.
This commit is contained in:
Jean Rouge 2016-06-22 11:32:58 -07:00
parent bddbdcd284
commit 42475db467
3 changed files with 52 additions and 4 deletions

View File

@ -6,6 +6,7 @@
#include <ruby/encoding.h>
#include <pthread.h>
#include <unistd.h>
#include <mutex>
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);
}
}

View File

@ -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

View File

@ -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