Skip to content

Commit

Permalink
feat: turn off quickjs GC at page loading phase. (#515)
Browse files Browse the repository at this point in the history
QuickJS GC seriously affects performance during the initialization
phase. This PR delays the execution of GC until after the page
initialization is complete.


<img width="1432" alt="image"
src="https://github.com/openwebf/webf/assets/4409743/9602892f-7a99-4459-b5c8-76d7dac97feb">

---------

Co-authored-by: openwebf-bot <[email protected]>
  • Loading branch information
andycall and openwebf-bot authored Nov 8, 2023
1 parent 6fb0d13 commit 399a656
Show file tree
Hide file tree
Showing 10 changed files with 45 additions and 3 deletions.
6 changes: 6 additions & 0 deletions bridge/core/dom/events/event_target.cc
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,12 @@ NativeValue EventTarget::HandleDispatchEventFromDart(int32_t argc, const NativeV
Event* event = EventFactory::Create(GetExecutingContext(), event_type, raw_event);
assert(event->target() != nullptr);
assert(event->currentTarget() != nullptr);

auto* window = DynamicTo<Window>(event->target());
if (window != nullptr && event->type() == event_type_names::kload) {
window->OnLoadEventFired();
}

ExceptionState exception_state;
event->SetTrusted(false);
event->SetEventPhase(Event::kAtTarget);
Expand Down
11 changes: 11 additions & 0 deletions bridge/core/executing_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ ExecutingContext::ExecutingContext(DartIsolateContext* dart_isolate_context,
JSContext* ctx = script_state_.ctx();
global_object_ = JS_GetGlobalObject(script_state_.ctx());

// Turn off quickjs GC to avoid performance issue at loading status.
// When the `load` event fired in window, the GC will turn on.
JS_TurnOffGC(script_state_.runtime());
JS_SetContextOpaque(ctx, this);
JS_SetHostPromiseRejectionTracker(script_state_.runtime(), promiseRejectTracker, nullptr);

Expand Down Expand Up @@ -332,6 +335,14 @@ void ExecutingContext::FlushUICommand() {
}
}

void ExecutingContext::TurnOnJavaScriptGC() {
JS_TurnOnGC(script_state_.runtime());
}

void ExecutingContext::TurnOffJavaScriptGC() {
JS_TurnOffGC(script_state_.runtime());
}

void ExecutingContext::DispatchErrorEvent(ErrorEvent* error_event) {
if (in_dispatch_error_event_) {
return;
Expand Down
3 changes: 3 additions & 0 deletions bridge/core/executing_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ class ExecutingContext {
// Force dart side to execute the pending ui commands.
void FlushUICommand();

void TurnOnJavaScriptGC();
void TurnOffJavaScriptGC();

void DispatchErrorEvent(ErrorEvent* error_event);
void DispatchErrorEventInterval(ErrorEvent* error_event);
void ReportErrorEvent(ErrorEvent* error_event);
Expand Down
4 changes: 4 additions & 0 deletions bridge/core/frame/window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ void Window::cancelAnimationFrame(double request_id, ExceptionState& exception_s
GetExecutingContext()->document()->CancelAnimationFrame(static_cast<uint32_t>(request_id), exception_state);
}

void Window::OnLoadEventFired() {
GetExecutingContext()->TurnOnJavaScriptGC();
}

bool Window::IsWindowOrWorkerGlobalScope() const {
return true;
}
Expand Down
1 change: 1 addition & 0 deletions bridge/core/frame/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Window : public EventTargetWithInlineData {
double requestAnimationFrame(const std::shared_ptr<QJSFunction>& callback, ExceptionState& exceptionState);
void cancelAnimationFrame(double request_id, ExceptionState& exception_state);

void OnLoadEventFired();
bool IsWindowOrWorkerGlobalScope() const override;

void Trace(GCVisitor* visitor) const override;
Expand Down
2 changes: 2 additions & 0 deletions bridge/third_party/quickjs/include/quickjs/quickjs.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ JSRuntime *JS_NewRuntime(void);
void JS_SetRuntimeInfo(JSRuntime *rt, const char *info);
void JS_SetMemoryLimit(JSRuntime *rt, size_t limit);
void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold);
void JS_TurnOffGC(JSRuntime *rt);
void JS_TurnOnGC(JSRuntime *rt);
/* use 0 to disable maximum stack size check */
void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size);
/* should be called when changing thread to update the stack top value
Expand Down
11 changes: 11 additions & 0 deletions bridge/third_party/quickjs/src/core/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,9 @@ void gc_free_cycles(JSRuntime* rt) {
}

void JS_RunGC(JSRuntime* rt) {
/* Turn off the GC running for some special reasons. */
if (rt->gc_off) return;

/* decrement the reference of the children of each object. mark =
1 after this pass. */
gc_decref(rt);
Expand All @@ -803,6 +806,14 @@ void JS_RunGC(JSRuntime* rt) {
gc_free_cycles(rt);
}

void JS_TurnOffGC(JSRuntime *rt) {
rt->gc_off = TRUE;
}

void JS_TurnOnGC(JSRuntime *rt) {
rt->gc_off = FALSE;
}

/* Return false if not an object or if the object has already been
freed (zombie objects are visible in finalizers when freeing
cycles). */
Expand Down
5 changes: 3 additions & 2 deletions bridge/third_party/quickjs/src/core/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -3005,7 +3005,8 @@ JSRuntime* JS_NewRuntime2(const JSMallocFunctions* mf, void* opaque) {
rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
}
rt->malloc_state = ms;
rt->malloc_gc_threshold = 256 * 1024;
rt->malloc_gc_threshold = 64 * 1024 * 1024; // 64 MB as a start
rt->gc_off = FALSE;

#ifdef CONFIG_BIGNUM
bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
Expand Down Expand Up @@ -3155,4 +3156,4 @@ JSValue JS_EvalFunctionInternal(JSContext* ctx, JSValue fun_obj, JSValueConst th

JSValue JS_EvalFunction(JSContext* ctx, JSValue fun_obj) {
return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
}
}
1 change: 1 addition & 0 deletions bridge/third_party/quickjs/src/core/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ struct JSRuntime {
struct list_head gc_zero_ref_count_list;
struct list_head tmp_obj_list; /* used during GC */
JSGCPhaseEnum gc_phase : 8;
BOOL gc_off: 8;
size_t malloc_gc_threshold;
#ifdef DUMP_LEAKS
struct list_head string_list; /* list of JSString.link */
Expand Down
4 changes: 3 additions & 1 deletion webf/lib/src/dom/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ class Window extends EventTarget {

Window(BindingContext? context, this.document)
: screen = Screen(context!.contextId, document.controller.ownerFlutterView, document.controller.view),
super(context);
super(context) {
BindingBridge.listenEvent(this, 'load');
}

@override
EventTarget? get parentEventTarget => null;
Expand Down

0 comments on commit 399a656

Please sign in to comment.