Skip to content

Commit 458c34d

Browse files
author
Fabrice Bellard
committed
fixed GC logic so that a module can live after a JSContext is destroyed (#280) - update the reference count for the realm in jobs and FinalizationRegistry
1 parent 2fd48bf commit 458c34d

File tree

3 files changed

+96
-61
lines changed

3 files changed

+96
-61
lines changed

quickjs-libc.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4230,17 +4230,15 @@ static void js_std_promise_rejection_check(JSContext *ctx)
42304230
/* main loop which calls the user JS callbacks */
42314231
void js_std_loop(JSContext *ctx)
42324232
{
4233-
JSContext *ctx1;
42344233
int err;
42354234

42364235
for(;;) {
42374236
/* execute the pending jobs */
42384237
for(;;) {
4239-
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
4238+
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), NULL);
42404239
if (err <= 0) {
4241-
if (err < 0) {
4242-
js_std_dump_error(ctx1);
4243-
}
4240+
if (err < 0)
4241+
js_std_dump_error(ctx);
42444242
break;
42454243
}
42464244
}
@@ -4271,11 +4269,10 @@ JSValue js_std_await(JSContext *ctx, JSValue obj)
42714269
JS_FreeValue(ctx, obj);
42724270
break;
42734271
} else if (state == JS_PROMISE_PENDING) {
4274-
JSContext *ctx1;
42754272
int err;
4276-
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
4273+
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), NULL);
42774274
if (err < 0) {
4278-
js_std_dump_error(ctx1);
4275+
js_std_dump_error(ctx);
42794276
}
42804277
if (err == 0) {
42814278
js_std_promise_rejection_check(ctx);

quickjs.c

Lines changed: 87 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ typedef enum {
341341
JS_GC_OBJ_TYPE_VAR_REF,
342342
JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
343343
JS_GC_OBJ_TYPE_JS_CONTEXT,
344+
JS_GC_OBJ_TYPE_MODULE,
344345
} JSGCObjectTypeEnum;
345346

346347
/* header for GC objects. GC objects are C data structures with a
@@ -805,7 +806,7 @@ typedef enum {
805806
} JSModuleStatus;
806807

807808
struct JSModuleDef {
808-
JSRefCountHeader header; /* must come first, 32-bit */
809+
JSGCObjectHeader header; /* must come first */
809810
JSAtom module_name;
810811
struct list_head link;
811812

@@ -857,7 +858,7 @@ struct JSModuleDef {
857858

858859
typedef struct JSJobEntry {
859860
struct list_head link;
860-
JSContext *ctx;
861+
JSContext *realm;
861862
JSJobFunc *job_func;
862863
int argc;
863864
JSValue argv[0];
@@ -1222,7 +1223,7 @@ static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
12221223
static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
12231224
const char *input, size_t input_len,
12241225
const char *filename, int flags, int scope_idx);
1225-
static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
1226+
static void js_free_module_def(JSRuntime *rt, JSModuleDef *m);
12261227
static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
12271228
JS_MarkFunc *mark_func);
12281229
static JSValue js_import_meta(JSContext *ctx);
@@ -1782,7 +1783,7 @@ int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
17821783
e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
17831784
if (!e)
17841785
return -1;
1785-
e->ctx = ctx;
1786+
e->realm = JS_DupContext(ctx);
17861787
e->job_func = job_func;
17871788
e->argc = argc;
17881789
for(i = 0; i < argc; i++) {
@@ -1798,7 +1799,10 @@ BOOL JS_IsJobPending(JSRuntime *rt)
17981799
}
17991800

18001801
/* return < 0 if exception, 0 if no job pending, 1 if a job was
1801-
executed successfully. the context of the job is stored in '*pctx' */
1802+
executed successfully. The context of the job is stored in '*pctx'
1803+
if pctx != NULL. It may be NULL if the context was already
1804+
destroyed or if no job was pending. The 'pctx' parameter is now
1805+
absolete. */
18021806
int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
18031807
{
18041808
JSContext *ctx;
@@ -1807,15 +1811,16 @@ int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
18071811
int i, ret;
18081812

18091813
if (list_empty(&rt->job_list)) {
1810-
*pctx = NULL;
1814+
if (pctx)
1815+
*pctx = NULL;
18111816
return 0;
18121817
}
18131818

18141819
/* get the first pending job and execute it */
18151820
e = list_entry(rt->job_list.next, JSJobEntry, link);
18161821
list_del(&e->link);
1817-
ctx = e->ctx;
1818-
res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);
1822+
ctx = e->realm;
1823+
res = e->job_func(ctx, e->argc, (JSValueConst *)e->argv);
18191824
for(i = 0; i < e->argc; i++)
18201825
JS_FreeValue(ctx, e->argv[i]);
18211826
if (JS_IsException(res))
@@ -1824,7 +1829,13 @@ int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
18241829
ret = 1;
18251830
JS_FreeValue(ctx, res);
18261831
js_free(ctx, e);
1827-
*pctx = ctx;
1832+
if (pctx) {
1833+
if (ctx->header.ref_count > 1)
1834+
*pctx = ctx;
1835+
else
1836+
*pctx = NULL;
1837+
}
1838+
JS_FreeContext(ctx);
18281839
return ret;
18291840
}
18301841

@@ -1905,6 +1916,7 @@ void JS_FreeRuntime(JSRuntime *rt)
19051916
JSJobEntry *e = list_entry(el, JSJobEntry, link);
19061917
for(i = 0; i < e->argc; i++)
19071918
JS_FreeValueRT(rt, e->argv[i]);
1919+
JS_FreeContext(e->realm);
19081920
js_free_rt(rt, e);
19091921
}
19101922
init_list_head(&rt->job_list);
@@ -2180,7 +2192,13 @@ static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
21802192
JSModuleDef *m = list_entry(el, JSModuleDef, link);
21812193
if (flag == JS_FREE_MODULE_ALL ||
21822194
(flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) {
2183-
js_free_module_def(ctx, m);
2195+
/* warning: the module may be referenced elsewhere. It
2196+
could be simpler to use an array instead of a list for
2197+
'ctx->loaded_modules' */
2198+
list_del(&m->link);
2199+
m->link.prev = NULL;
2200+
m->link.next = NULL;
2201+
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
21842202
}
21852203
}
21862204
}
@@ -2198,11 +2216,9 @@ static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
21982216
int i;
21992217
struct list_head *el;
22002218

2201-
/* modules are not seen by the GC, so we directly mark the objects
2202-
referenced by each module */
22032219
list_for_each(el, &ctx->loaded_modules) {
22042220
JSModuleDef *m = list_entry(el, JSModuleDef, link);
2205-
js_mark_module_def(rt, m, mark_func);
2221+
JS_MarkValue(rt, JS_MKPTR(JS_TAG_MODULE, m), mark_func);
22062222
}
22072223

22082224
JS_MarkValue(rt, ctx->global_obj, mark_func);
@@ -5783,6 +5799,9 @@ static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
57835799
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
57845800
__async_func_free(rt, (JSAsyncFunctionState *)gp);
57855801
break;
5802+
case JS_GC_OBJ_TYPE_MODULE:
5803+
js_free_module_def(rt, (JSModuleDef *)gp);
5804+
break;
57865805
default:
57875806
abort();
57885807
}
@@ -5847,6 +5866,7 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
58475866
break;
58485867
case JS_TAG_OBJECT:
58495868
case JS_TAG_FUNCTION_BYTECODE:
5869+
case JS_TAG_MODULE:
58505870
{
58515871
JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);
58525872
if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
@@ -5859,9 +5879,6 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
58595879
}
58605880
}
58615881
break;
5862-
case JS_TAG_MODULE:
5863-
abort(); /* never freed here */
5864-
break;
58655882
case JS_TAG_BIG_INT:
58665883
{
58675884
JSBigInt *p = JS_VALUE_GET_PTR(v);
@@ -5935,6 +5952,7 @@ void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
59355952
switch(JS_VALUE_GET_TAG(val)) {
59365953
case JS_TAG_OBJECT:
59375954
case JS_TAG_FUNCTION_BYTECODE:
5955+
case JS_TAG_MODULE:
59385956
mark_func(rt, JS_VALUE_GET_PTR(val));
59395957
break;
59405958
default:
@@ -6047,6 +6065,12 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
60476065
JS_MarkContext(rt, ctx, mark_func);
60486066
}
60496067
break;
6068+
case JS_GC_OBJ_TYPE_MODULE:
6069+
{
6070+
JSModuleDef *m = (JSModuleDef *)gp;
6071+
js_mark_module_def(rt, m, mark_func);
6072+
}
6073+
break;
60506074
default:
60516075
abort();
60526076
}
@@ -6143,6 +6167,7 @@ static void gc_free_cycles(JSRuntime *rt)
61436167
case JS_GC_OBJ_TYPE_JS_OBJECT:
61446168
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
61456169
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
6170+
case JS_GC_OBJ_TYPE_MODULE:
61466171
#ifdef DUMP_GC_FREE
61476172
if (!header_done) {
61486173
printf("Freeing cycles:\n");
@@ -6165,7 +6190,8 @@ static void gc_free_cycles(JSRuntime *rt)
61656190
p = list_entry(el, JSGCObjectHeader, link);
61666191
assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
61676192
p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE ||
6168-
p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
6193+
p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION ||
6194+
p->gc_obj_type == JS_GC_OBJ_TYPE_MODULE);
61696195
if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT &&
61706196
((JSObject *)p)->weakref_count != 0) {
61716197
/* keep the object because there are weak references to it */
@@ -13747,6 +13773,9 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
1374713773
case JS_GC_OBJ_TYPE_JS_CONTEXT:
1374813774
printf("[js_context]");
1374913775
break;
13776+
case JS_GC_OBJ_TYPE_MODULE:
13777+
printf("[module]");
13778+
break;
1375013779
default:
1375113780
printf("[unknown %d]", p->gc_obj_type);
1375213781
break;
@@ -28216,7 +28245,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
2821628245
return -1;
2821728246
}
2821828247

28219-
/* 'name' is freed */
28248+
/* 'name' is freed. The module is referenced by 'ctx->loaded_modules' */
2822028249
static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
2822128250
{
2822228251
JSModuleDef *m;
@@ -28226,6 +28255,7 @@ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
2822628255
return NULL;
2822728256
}
2822828257
m->header.ref_count = 1;
28258+
add_gc_object(ctx->rt, &m->header, JS_GC_OBJ_TYPE_MODULE);
2822928259
m->module_name = name;
2823028260
m->module_ns = JS_UNDEFINED;
2823128261
m->func_obj = JS_UNDEFINED;
@@ -28267,47 +28297,56 @@ static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
2826728297
JS_MarkValue(rt, m->private_value, mark_func);
2826828298
}
2826928299

28270-
static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
28300+
static void js_free_module_def(JSRuntime *rt, JSModuleDef *m)
2827128301
{
2827228302
int i;
2827328303

28274-
JS_FreeAtom(ctx, m->module_name);
28304+
JS_FreeAtomRT(rt, m->module_name);
2827528305

2827628306
for(i = 0; i < m->req_module_entries_count; i++) {
2827728307
JSReqModuleEntry *rme = &m->req_module_entries[i];
28278-
JS_FreeAtom(ctx, rme->module_name);
28279-
JS_FreeValue(ctx, rme->attributes);
28308+
JS_FreeAtomRT(rt, rme->module_name);
28309+
JS_FreeValueRT(rt, rme->attributes);
2828028310
}
28281-
js_free(ctx, m->req_module_entries);
28311+
js_free_rt(rt, m->req_module_entries);
2828228312

2828328313
for(i = 0; i < m->export_entries_count; i++) {
2828428314
JSExportEntry *me = &m->export_entries[i];
2828528315
if (me->export_type == JS_EXPORT_TYPE_LOCAL)
28286-
free_var_ref(ctx->rt, me->u.local.var_ref);
28287-
JS_FreeAtom(ctx, me->export_name);
28288-
JS_FreeAtom(ctx, me->local_name);
28316+
free_var_ref(rt, me->u.local.var_ref);
28317+
JS_FreeAtomRT(rt, me->export_name);
28318+
JS_FreeAtomRT(rt, me->local_name);
2828928319
}
28290-
js_free(ctx, m->export_entries);
28320+
js_free_rt(rt, m->export_entries);
2829128321

28292-
js_free(ctx, m->star_export_entries);
28322+
js_free_rt(rt, m->star_export_entries);
2829328323

2829428324
for(i = 0; i < m->import_entries_count; i++) {
2829528325
JSImportEntry *mi = &m->import_entries[i];
28296-
JS_FreeAtom(ctx, mi->import_name);
28326+
JS_FreeAtomRT(rt, mi->import_name);
2829728327
}
28298-
js_free(ctx, m->import_entries);
28299-
js_free(ctx, m->async_parent_modules);
28328+
js_free_rt(rt, m->import_entries);
28329+
js_free_rt(rt, m->async_parent_modules);
2830028330

28301-
JS_FreeValue(ctx, m->module_ns);
28302-
JS_FreeValue(ctx, m->func_obj);
28303-
JS_FreeValue(ctx, m->eval_exception);
28304-
JS_FreeValue(ctx, m->meta_obj);
28305-
JS_FreeValue(ctx, m->promise);
28306-
JS_FreeValue(ctx, m->resolving_funcs[0]);
28307-
JS_FreeValue(ctx, m->resolving_funcs[1]);
28308-
JS_FreeValue(ctx, m->private_value);
28309-
list_del(&m->link);
28310-
js_free(ctx, m);
28331+
JS_FreeValueRT(rt, m->module_ns);
28332+
JS_FreeValueRT(rt, m->func_obj);
28333+
JS_FreeValueRT(rt, m->eval_exception);
28334+
JS_FreeValueRT(rt, m->meta_obj);
28335+
JS_FreeValueRT(rt, m->promise);
28336+
JS_FreeValueRT(rt, m->resolving_funcs[0]);
28337+
JS_FreeValueRT(rt, m->resolving_funcs[1]);
28338+
JS_FreeValueRT(rt, m->private_value);
28339+
/* during the GC the finalizers are called in an arbitrary
28340+
order so the module may no longer be referenced by the JSContext list */
28341+
if (m->link.next) {
28342+
list_del(&m->link);
28343+
}
28344+
remove_gc_object(&m->header);
28345+
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && m->header.ref_count != 0) {
28346+
list_add_tail(&m->header.link, &rt->gc_zero_ref_count_list);
28347+
} else {
28348+
js_free_rt(rt, m);
28349+
}
2831128350
}
2831228351

2831328352
static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
@@ -35804,7 +35843,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
3580435843
fail1:
3580535844
/* XXX: should free all the unresolved dependencies */
3580635845
if (m)
35807-
js_free_module_def(ctx, m);
35846+
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
3580835847
return JS_EXCEPTION;
3580935848
}
3581035849

@@ -37516,7 +37555,7 @@ static JSValue JS_ReadModule(BCReaderState *s)
3751637555
return obj;
3751737556
fail:
3751837557
if (m) {
37519-
js_free_module_def(ctx, m);
37558+
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
3752037559
}
3752137560
return JS_EXCEPTION;
3752237561
}
@@ -55663,7 +55702,7 @@ typedef struct JSFinRecEntry {
5566355702
typedef struct JSFinalizationRegistryData {
5566455703
JSWeakRefHeader weakref_header;
5566555704
struct list_head entries; /* list of JSFinRecEntry.link */
55666-
JSContext *ctx;
55705+
JSContext *realm;
5566755706
JSValue cb;
5566855707
} JSFinalizationRegistryData;
5566955708

@@ -55680,6 +55719,7 @@ static void js_finrec_finalizer(JSRuntime *rt, JSValue val)
5568055719
js_free_rt(rt, fre);
5568155720
}
5568255721
JS_FreeValueRT(rt, frd->cb);
55722+
JS_FreeContext(frd->realm);
5568355723
list_del(&frd->weakref_header.link);
5568455724
js_free_rt(rt, frd);
5568555725
}
@@ -55696,6 +55736,7 @@ static void js_finrec_mark(JSRuntime *rt, JSValueConst val,
5569655736
JS_MarkValue(rt, fre->held_val, mark_func);
5569755737
}
5569855738
JS_MarkValue(rt, frd->cb, mark_func);
55739+
mark_func(rt, &frd->realm->header);
5569955740
}
5570055741
}
5570155742

@@ -55721,7 +55762,7 @@ static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh)
5572155762
JSValueConst args[2];
5572255763
args[0] = frd->cb;
5572355764
args[1] = fre->held_val;
55724-
JS_EnqueueJob(frd->ctx, js_finrec_job, 2, args);
55765+
JS_EnqueueJob(frd->realm, js_finrec_job, 2, args);
5572555766

5572655767
js_weakref_free(rt, fre->target);
5572755768
js_weakref_free(rt, fre->token);
@@ -55756,7 +55797,7 @@ static JSValue js_finrec_constructor(JSContext *ctx, JSValueConst new_target,
5575655797
frd->weakref_header.weakref_type = JS_WEAKREF_TYPE_FINREC;
5575755798
list_add_tail(&frd->weakref_header.link, &ctx->rt->weakref_list);
5575855799
init_list_head(&frd->entries);
55759-
frd->ctx = ctx; /* XXX: JS_DupContext() ? */
55800+
frd->realm = JS_DupContext(ctx);
5576055801
frd->cb = JS_DupValue(ctx, cb);
5576155802
JS_SetOpaque(obj, frd);
5576255803
return obj;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy