-
Notifications
You must be signed in to change notification settings - Fork 120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
QJS hooks and GC hooks #747
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please make the style match the surroundings, here and elsewhere. |
||
size_t tmp_threshold = rt->malloc_gc_threshold; | ||
rt->malloc_gc_threshold=-1; | ||
|
||
if((rt->hooks == NULL) || rt->hooks(JS_HOOK_GC_BEFORE,NULL)){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reason for not running GC if the before handler returns FALSE? There is an API to disable GC already, what's the use case here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The hook could change the threshold, but you are then re-setting it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but while functionally similar they have different applications. Setting the GC on/off in specific critical regions can get a bit tricky with async/await. Not only that, but doing so would mask that a request was done, preventing the application from saying "oh yes, I had to delay it, I am now in a safe region, let's clean up things". Ensuring the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds very theoretical ;-) Why can't the mechanism that knows if we are safe or not enable / disable GC while in a critical region? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on the application requirements:
Having the option to skip gc events while tracking them enables option 3 which is better than option 2 for a good class of real time applications. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for elaborating! Looks like option 3 could work as follows:
Wouldn't that work? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the time the app can check if it is ok to run a GC event, it is already in the body of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before can stop GC from running by disabling it. So before get hit, but because you disable it it doesn't run. Then when you want GC the app can enable it and run it. What wouldn't work? |
||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd call this gc_handlers, gc_hooks or gc_callbacks, just hooks feels wrong. |
||
|
||
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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why take an opaque if you are not doing anything with it? An opaque would make sense if you support multiple callbacks, so each could get their own. |
||
{ | ||
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) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why this needs to exist. You want to avoid 0? Make JS_HOOK_GC_BEFORE start at 1? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a habit of mine. I usually keep 0 as an error value for enums, and I usually give them a mnemonic in place of starting with 1. It helps when testing debug builds where memory is initialized to 0. |
||
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. */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The API suggests one could register multiple hooks, but you really cannot. The finalizers API can, on the contrary. Now, if you add the ability to register multiple handlers, then the whole boolean return thing doesn't work... |
||
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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have something similar for finalizers, how is this generic? It only deals with GC. I'd call it GC hooks.
Alternatively we could fold finalizers in there too, but I'm not too thrilled about it.