diff --git a/quickjs.c b/quickjs.c index 29beb4b2d..24be798b0 100644 --- a/quickjs.c +++ b/quickjs.c @@ -305,6 +305,9 @@ struct JSRuntime { void *user_opaque; void *libc_opaque; JSRuntimeFinalizerState *finalizers; + + /* Unified handler for all the engine hooks */ + JS_BOOL (*hooks)(JSRuntimeHooks type, void* opaque); }; struct JSClass { @@ -1440,7 +1443,17 @@ static void js_trigger_gc(JSRuntime *rt, size_t size) (uint64_t)rt->malloc_state.malloc_size); } #endif - JS_RunGC(rt); + //To ensure JS_RunGC cannot be executed again within callbacks, disable and restore it after. + size_t tmp_threshold = rt->malloc_gc_threshold; + rt->malloc_gc_threshold=-1; + + if((rt->hooks == NULL) || rt->hooks(JS_HOOK_GC_BEFORE,NULL)){ + JS_RunGC(rt); + if(rt->hooks != NULL)rt->hooks(JS_HOOK_GC_AFTER,NULL); + } + + rt->malloc_gc_threshold=tmp_threshold; + rt->malloc_gc_threshold = rt->malloc_state.malloc_size + (rt->malloc_state.malloc_size >> 1); } @@ -1810,6 +1823,8 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) rt->malloc_state = ms; rt->malloc_gc_threshold = 256 * 1024; + rt->hooks = NULL; + bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); init_list_head(&rt->context_list); @@ -1922,7 +1937,8 @@ void JS_SetDumpFlags(JSRuntime *rt, uint64_t flags) rt->dump_flags = flags; } -size_t JS_GetGCThreshold(JSRuntime *rt) { +size_t JS_GetGCThreshold(JSRuntime *rt) +{ return rt->malloc_gc_threshold; } @@ -1932,6 +1948,11 @@ void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) rt->malloc_gc_threshold = gc_threshold; } +void JS_SetHooksHandler(JSRuntime *rt, JS_BOOL (*fn)(JSRuntimeHooks type, void* opaque)) +{ + rt->hooks = fn; +} + #define malloc(s) malloc_is_forbidden(s) #define free(p) free_is_forbidden(p) #define realloc(p,s) realloc_is_forbidden(p,s) diff --git a/quickjs.h b/quickjs.h index 9e8b5113f..3887875c5 100644 --- a/quickjs.h +++ b/quickjs.h @@ -324,6 +324,13 @@ typedef void JSRuntimeFinalizer(JSRuntime *rt, void *arg); typedef struct JSGCObjectHeader JSGCObjectHeader; +/* Engine hooks */ +typedef enum JSRuntimeHooks{ + JS_HOOK_NONE, //Don't use, just to catch potential bugs + JS_HOOK_GC_BEFORE, //Run before a GC event takes place. No payload. Return false if the hooks opposes the GC event to take place. + JS_HOOK_GC_AFTER, //Run after a GC event took place. No payload. Return value ignored. +} JSRuntimeHooks; + JS_EXTERN JSRuntime *JS_NewRuntime(void); /* info lifetime must exceed that of rt */ JS_EXTERN void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); @@ -332,6 +339,8 @@ JS_EXTERN void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); JS_EXTERN void JS_SetDumpFlags(JSRuntime *rt, uint64_t flags); JS_EXTERN size_t JS_GetGCThreshold(JSRuntime *rt); JS_EXTERN void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); +/* register a hook dispatcher for this runtime. The handler should always return true for hooks which are not supported. */ +JS_EXTERN void JS_SetHooksHandler(JSRuntime *rt, JS_BOOL (*fn)(JSRuntimeHooks type, void* opaque)); /* use 0 to disable maximum stack size check */ JS_EXTERN void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size); /* should be called when changing thread to update the stack top value