diff --git a/ext/mini_racer_extension/mini_racer_extension.cc b/ext/mini_racer_extension/mini_racer_extension.cc index 0a21174..fc8219c 100644 --- a/ext/mini_racer_extension/mini_racer_extension.cc +++ b/ext/mini_racer_extension/mini_racer_extension.cc @@ -128,11 +128,59 @@ typedef struct { size_t max_memory; } FunctionCall; -enum IsolateFlags { - IN_GVL, - DO_TERMINATE, - MEM_SOFTLIMIT_VALUE, - MEM_SOFTLIMIT_REACHED, +class IsolateData { +public: + enum Flag { + // first flags are bitfield + // max count: sizeof(uintptr_t) * 8 + IN_GVL, // whether we are inside of ruby gvl or not + DO_TERMINATE, // terminate as soon as possible + MEM_SOFTLIMIT_REACHED, // we've hit the memory soft limit + MEM_SOFTLIMIT_VALUE, + }; + + static uintptr_t Get(Isolate *isolate, Flag flag) { + Bitfield u = { reinterpret_cast(isolate->GetData(0)) }; + switch (flag) { + case IN_GVL: return u.IN_GVL; + case DO_TERMINATE: return u.DO_TERMINATE; + case MEM_SOFTLIMIT_REACHED: return u.MEM_SOFTLIMIT_REACHED; + case MEM_SOFTLIMIT_VALUE: return u.MEM_SOFTLIMIT_VALUE << 10; + } + } + + static void Set(Isolate *isolate, Flag flag, uintptr_t value) { + Bitfield u = { reinterpret_cast(isolate->GetData(0)) }; + switch (flag) { + case IN_GVL: u.IN_GVL = value; break; + case DO_TERMINATE: u.DO_TERMINATE = value; break; + case MEM_SOFTLIMIT_REACHED: u.MEM_SOFTLIMIT_REACHED = value; break; + // drop least significant 10 bits 'store memory amount in kb' + case MEM_SOFTLIMIT_VALUE: u.MEM_SOFTLIMIT_VALUE = value >> 10; break; + } + isolate->SetData(0, reinterpret_cast(u.dataPtr)); + } + +private: + struct Bitfield { + // WARNING: this would explode on platforms below 64 bit ptrs + // compiler will fail here, making it clear for them. + // Additionally, using the other part of the union to reinterpret the + // memory is undefined behavior according to spec, but is / has been stable + // across major compilers for decades. + static_assert(sizeof(uintptr_t) >= sizeof(uint64_t), "mini_racer not supported on this platform. ptr size must be at least 64 bit."); + union { + uint64_t dataPtr: 64; + // order in this struct matters. For cpu performance keep larger subobjects + // aligned on their boundaries (8 16 32), try not to straddle + struct { + size_t MEM_SOFTLIMIT_VALUE:22; + bool IN_GVL:1; + bool DO_TERMINATE:1; + bool MEM_SOFTLIMIT_REACHED:1; + }; + }; + }; }; static VALUE rb_cContext; @@ -205,16 +253,18 @@ static void init_v8() { } static void gc_callback(Isolate *isolate, GCType type, GCCallbackFlags flags) { - if((bool)isolate->GetData(MEM_SOFTLIMIT_REACHED)) return; + if (IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_REACHED)) { + return; + } - size_t softlimit = *(size_t*) isolate->GetData(MEM_SOFTLIMIT_VALUE); + size_t softlimit = IsolateData::Get(isolate, IsolateData::MEM_SOFTLIMIT_VALUE); HeapStatistics stats; isolate->GetHeapStatistics(&stats); size_t used = stats.used_heap_size(); if(used > softlimit) { - isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)true); + IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, true); isolate->TerminateExecution(); } } @@ -326,14 +376,10 @@ nogvl_context_eval(void* arg) { Context::Scope context_scope(context); v8::ScriptOrigin *origin = NULL; - // in gvl flag - isolate->SetData(IN_GVL, (void*)false); - // terminate ASAP - isolate->SetData(DO_TERMINATE, (void*)false); - // Memory softlimit - isolate->SetData(MEM_SOFTLIMIT_VALUE, (void*)false); - // Memory softlimit hit flag - isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false); + IsolateData::Set(isolate, IsolateData::IN_GVL, false); + IsolateData::Set(isolate, IsolateData::DO_TERMINATE, false); + IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_VALUE, 0); + IsolateData::Set(isolate, IsolateData::MEM_SOFTLIMIT_REACHED, false); MaybeLocal