Skip to content
This repository has been archived by the owner on Oct 15, 2020. It is now read-only.

Commit

Permalink
chakrashim: implement triggering GC when idle
Browse files Browse the repository at this point in the history
Added a task in prepare queue that will  schedule `IdleGC` task
if there is no idleGc task currently scheduled or if there was some
javascript script execution happened.
`IdleGC` will basically call [JsIdle](https://github.com/Microsoft/ChakraCore/wiki/JsIdle) and if it didn't succeed, it
will schedule another `IdleGC` task after (nextTick - currentTick) ms.
If this task succeeded, we don't have to do anything and next time
prepareQueue task will be executed, it will decide if it should
re-schedule `IdleGC` or not.

Also include flag `--off_idlegc` to turn idleGC feature off.

PR-URL: #77
Reviewed-By: Curtis Man <CurtisM@microsoft.com>
  • Loading branch information
kunalspathak committed Jun 15, 2016
1 parent 8641dc0 commit 1ec0729
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 14 deletions.
3 changes: 2 additions & 1 deletion deps/chakrashim/chakrashim.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

'include_dirs': [
'include',
'<(SHARED_INTERMEDIATE_DIR)'
'<(SHARED_INTERMEDIATE_DIR)',
'./../uv/include'
],
'defines': [
'BUILDING_CHAKRASHIM=1',
Expand Down
5 changes: 5 additions & 0 deletions deps/chakrashim/src/jsrtcontextshim.cc
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,11 @@ bool ContextShim::EnsureInitialized() {
return false;
}

// add idleGC callback into prepareQueue
if (IsolateShim::IsIdleGcEnabled()) {
uv_prepare_start(IsolateShim::GetCurrent()->idleGc_prepare_handle(), PrepareIdleGC);
}

return true;
}

Expand Down
25 changes: 21 additions & 4 deletions deps/chakrashim/src/jsrtisolateshim.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include <vector>
#include <algorithm>

namespace v8 {
extern bool g_disableIdleGc;
}
namespace jsrt {

/* static */ __declspec(thread) IsolateShim * IsolateShim::s_currentIsolate;
Expand All @@ -48,6 +51,11 @@ IsolateShim::~IsolateShim() {
assert(runtime == JS_INVALID_REFERENCE);
assert(this->next == nullptr);
assert(this->prevnext == nullptr);

if (IsolateShim::IsIdleGcEnabled()) {
uv_close(reinterpret_cast<uv_handle_t*>(idleGc_prepare_handle()), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(idleGc_timer_handle()), nullptr);
}
}

/* static */ v8::Isolate * IsolateShim::New(bool enableSimd) {
Expand All @@ -57,19 +65,28 @@ IsolateShim::~IsolateShim() {
return nullptr;
}

bool disableIdleGc = v8::g_disableIdleGc;
JsRuntimeHandle runtime;
JsErrorCode error =
JsCreateRuntime(static_cast<JsRuntimeAttributes>(
JsRuntimeAttributeAllowScriptInterrupt |
JsRuntimeAttributeEnableExperimentalFeatures |
(enableSimd? JsRuntimeAttributeEnableSimdjsFeature :
JsRuntimeAttributeNone)),
nullptr, &runtime);
(enableSimd ? JsRuntimeAttributeEnableSimdjsFeature :
JsRuntimeAttributeNone) |
(disableIdleGc ? JsRuntimeAttributeNone :
JsRuntimeAttributeEnableIdleProcessing)), nullptr, &runtime);
if (error != JsNoError) {
return nullptr;
}

return ToIsolate(new IsolateShim(runtime));
IsolateShim* newIsolateshim = new IsolateShim(runtime);
if (!disableIdleGc) {
uv_prepare_init(uv_default_loop(), newIsolateshim->idleGc_prepare_handle());
uv_unref(reinterpret_cast<uv_handle_t*>(newIsolateshim->idleGc_prepare_handle()));
uv_timer_init(uv_default_loop(), newIsolateshim->idleGc_timer_handle());
uv_unref(reinterpret_cast<uv_handle_t*>(newIsolateshim->idleGc_timer_handle()));
}
return ToIsolate(newIsolateshim);
}

/* static */ IsolateShim * IsolateShim::FromIsolate(v8::Isolate * isolate) {
Expand Down
45 changes: 42 additions & 3 deletions deps/chakrashim/src/jsrtisolateshim.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#include "uv.h"
#include <unordered_map>
#include <vector>

namespace v8 {

class Isolate;
class TryCatch;

extern bool g_disableIdleGc;
} // namespace v8

namespace jsrt {
Expand Down Expand Up @@ -84,7 +85,9 @@ class IsolateShim {
void DisableExecution();
bool IsExeuctionDisabled();
void EnableExecution();

static inline bool IsIdleGcEnabled() {
return !v8::g_disableIdleGc;
}

bool AddMessageListener(void * that);
void RemoveMessageListeners(void * that);
Expand All @@ -97,6 +100,38 @@ class IsolateShim {

void SetData(unsigned int slot, void* data);
void* GetData(unsigned int slot);

inline uv_prepare_t* idleGc_prepare_handle() {
return &idleGc_prepare_handle_;
}

inline uv_timer_t* idleGc_timer_handle() {
return &idleGc_timer_handle_;
}

inline bool IsJsScriptExecuted() {
return jsScriptExecuted;
}

inline void SetScriptExecuted() {
jsScriptExecuted = true;
}
inline void ResetScriptExecuted() {
jsScriptExecuted = false;
}

inline void SetIsIdleGcScheduled() {
isIdleGcScheduled = true;
}

inline void ResetIsIdleGcScheduled() {
isIdleGcScheduled = false;
}

inline bool IsIdleGcScheduled() {
return isIdleGcScheduled;
}

private:
// Construction/Destruction should go thru New/Dispose
explicit IsolateShim(JsRuntimeHandle runtime);
Expand Down Expand Up @@ -126,6 +161,10 @@ class IsolateShim {
static IsolateShim * s_isolateList;

static __declspec(thread) IsolateShim * s_currentIsolate;
};

uv_prepare_t idleGc_prepare_handle_;
uv_timer_t idleGc_timer_handle_;
bool jsScriptExecuted = false;
bool isIdleGcScheduled = false;
};
} // namespace jsrt
42 changes: 42 additions & 0 deletions deps/chakrashim/src/jsrtutils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1061,5 +1061,47 @@ JsValueRef CALLBACK CollectGarbage(
return jsrt::GetUndefined();
}

void IdleGC(uv_timer_t *timerHandler) {
unsigned int nextIdleTicks;
CHAKRA_VERIFY(JsIdle(&nextIdleTicks) == JsNoError);
DWORD currentTicks = GetTickCount();

// If idleGc completed, we don't need to schedule anything.
// simply reset the script execution flag so that idleGC
// is retriggered only when scripts are executed.
if (nextIdleTicks == UINT_MAX) {
IsolateShim::GetCurrent()->ResetScriptExecuted();
IsolateShim::GetCurrent()->ResetIsIdleGcScheduled();
return;
}

// If IdleGC didn't complete, retry doing it after diff.
if (nextIdleTicks > currentTicks) {
unsigned int diff = nextIdleTicks - currentTicks;
ScheduleIdleGcTask(diff);
} else {
IsolateShim::GetCurrent()->ResetIsIdleGcScheduled();
}
}

void PrepareIdleGC(uv_prepare_t* prepareHandler) {
// If there were no scripts executed, return
if (!IsolateShim::GetCurrent()->IsJsScriptExecuted()) {
return;
}

// If idleGC task already scheduled, return
if (IsolateShim::GetCurrent()->IsIdleGcScheduled()) {
return;
}

ScheduleIdleGcTask();
}

void ScheduleIdleGcTask(uint64_t timeoutInMilliSeconds) {
uv_timer_start(IsolateShim::GetCurrent()->idleGc_timer_handle(),
IdleGC, timeoutInMilliSeconds, 0);
IsolateShim::GetCurrent()->SetIsIdleGcScheduled();
}
} // namespace jsrt

8 changes: 8 additions & 0 deletions deps/chakrashim/src/jsrtutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#pragma once

#include "v8.h"
#include "uv.h"
#include "jsrtproxyutils.h"
#include "jsrtcontextshim.h"
#include "jsrtisolateshim.h"
Expand Down Expand Up @@ -324,6 +325,12 @@ void Unimplemented(const char * message);

void Fatal(const char * format, ...);

void ScheduleIdleGcTask(uint64_t timeoutInMilliSeconds = 1000);

void PrepareIdleGC(uv_prepare_t* prepareHandler);

void IdleGC(uv_timer_t *timerHandler);

// Arguments buffer for JsCallFunction
template <int STATIC_COUNT = 4>
class JsArguments {
Expand Down Expand Up @@ -451,5 +458,6 @@ inline JsErrorCode ValueToDoubleLikely(JsValueRef value, double* dblValue) {
return ValueToNative</*LIKELY*/true>(
JsConvertValueToNumber, JsNumberToDouble, value, dblValue);
}

} // namespace jsrt

2 changes: 2 additions & 0 deletions deps/chakrashim/src/v8function.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Local<Object> Function::NewInstance() const {
MaybeLocal<Value> Function::Call(Local<Context> context,
Handle<Value> recv, int argc,
Handle<Value> argv[]) {
IsolateShim::GetCurrent()->SetScriptExecuted();

jsrt::JsArguments<> args(argc + 1);
args[0] = *recv;

Expand Down
4 changes: 2 additions & 2 deletions deps/chakrashim/src/v8isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,13 @@ void Isolate::GetHeapStatistics(HeapStatistics *heap_statistics) {
}

size_t Isolate::NumberOfHeapSpaces() {
//Chakra doesn't expose HEAP space stats
// Chakra doesn't expose HEAP space stats
return 0;
}

bool Isolate::GetHeapSpaceStatistics(HeapSpaceStatistics* space_statistics,
size_t index) {
//Chakra doesn't expose HEAP space stats
// Chakra doesn't expose HEAP space stats
return true;
}

Expand Down
2 changes: 2 additions & 0 deletions deps/chakrashim/src/v8script.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ Local<Script> Script::Compile(Handle<String> source,
}

MaybeLocal<Value> Script::Run(Local<Context> context) {
jsrt::IsolateShim::GetCurrent()->SetScriptExecuted();

JsValueRef scriptFunction;
if (jsrt::GetProperty(this, CachedPropertyIdRef::function,
&scriptFunction) != JsNoError) {
Expand Down
16 changes: 14 additions & 2 deletions deps/chakrashim/src/v8v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ bool g_disposed = false;
bool g_exposeGC = false;
bool g_useStrict = false;
bool g_enableSimdjs = false;
bool g_disableIdleGc = false;
ArrayBuffer::Allocator* g_arrayBufferAllocator = nullptr;

const char *V8::GetVersion() {
Expand Down Expand Up @@ -83,6 +84,9 @@ static bool startsWith(const char* str, const char (&prefix)[N]) {
return strncmp(str, prefix, N - 1) == 0;
}




void V8::SetFlagsFromCommandLine(int *argc, char **argv, bool remove_flags) {
for (int i = 1; i < *argc; i++) {
// Note: Node now exits on invalid options. We may not recognize V8 flags
Expand All @@ -103,6 +107,11 @@ void V8::SetFlagsFromCommandLine(int *argc, char **argv, bool remove_flags) {
if (remove_flags) {
argv[i] = nullptr;
}
} else if (equals("--off-idlegc", arg) || equals("--off_idlegc", arg)) {
g_disableIdleGc = true;
if (remove_flags) {
argv[i] = nullptr;
}
} else if (remove_flags &&
(startsWith(
arg, "--debug") // Ignore some flags to reduce unit test noise
Expand All @@ -116,7 +125,10 @@ void V8::SetFlagsFromCommandLine(int *argc, char **argv, bool remove_flags) {
" type: bool default: false\n"
" --expose_gc (expose gc extension)\n"
" type: bool default: false\n"
" --harmony (Ignored in node running with chakracore)\n"
" --off_idlegc (turn off idle GC)\n"
" --harmony_simd (enable \"harmony simd\" (in progress))\n"
" --harmony (Other flags are ignored in node running with "
"chakracore)\n"
" --debug (Ignored in node running with chakracore)\n"
" --stack-size (Ignored in node running with chakracore)\n";
fprintf(stdout, helpText);
Expand All @@ -132,7 +144,7 @@ void V8::SetFlagsFromCommandLine(int *argc, char **argv, bool remove_flags) {

bool V8::Initialize() {
if (g_disposed) {
return false; // Can no longer Initialize if Disposed
return false; // Can no longer Initialize if Disposed
}
#ifndef NODE_ENGINE_CHAKRACORE
if (g_EnableDebug && JsStartDebugging() != JsNoError) {
Expand Down
5 changes: 4 additions & 1 deletion node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,10 @@
],
}],
['node_engine=="chakracore"', {
'dependencies': [ 'deps/chakrashim/chakrashim.gyp:chakrashim' ],
'dependencies': [
'deps/chakrashim/chakrashim.gyp:chakrashim',
'deps/uv/uv.gyp:libuv'
],
}],
],
'msvs_settings': {
Expand Down
2 changes: 1 addition & 1 deletion src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4409,9 +4409,9 @@ static void StartNodeInstance(void* arg) {
do {
v8::platform::PumpMessageLoop(default_platform, isolate);
more = uv_run(env->event_loop(), UV_RUN_ONCE);

if (more == false) {
v8::platform::PumpMessageLoop(default_platform, isolate);

EmitBeforeExit(env);

// Emit `beforeExit` if the loop became alive either after emitting
Expand Down

0 comments on commit 1ec0729

Please sign in to comment.