Commit bc45b43a authored by Gabriel Ivăncescu's avatar Gabriel Ivăncescu Committed by Alexandre Julliard

jscript: Make the garbage collector thread-wide rather than per-ctx.

parent 39c7950a
...@@ -783,6 +783,7 @@ HRESULT gc_run(script_ctx_t *ctx) ...@@ -783,6 +783,7 @@ HRESULT gc_run(script_ctx_t *ctx)
struct chunk *next; struct chunk *next;
LONG ref[1020]; LONG ref[1020];
} *head, *chunk; } *head, *chunk;
struct thread_data *thread_data = ctx->thread_data;
jsdisp_t *obj, *obj2, *link, *link2; jsdisp_t *obj, *obj2, *link, *link2;
dispex_prop_t *prop, *props_end; dispex_prop_t *prop, *props_end;
struct gc_ctx gc_ctx = { 0 }; struct gc_ctx gc_ctx = { 0 };
...@@ -791,7 +792,7 @@ HRESULT gc_run(script_ctx_t *ctx) ...@@ -791,7 +792,7 @@ HRESULT gc_run(script_ctx_t *ctx)
struct list *iter; struct list *iter;
/* Prevent recursive calls from side-effects during unlinking (e.g. CollectGarbage from host object's Release) */ /* Prevent recursive calls from side-effects during unlinking (e.g. CollectGarbage from host object's Release) */
if(ctx->gc_is_unlinking) if(thread_data->gc_is_unlinking)
return S_OK; return S_OK;
if(!(head = malloc(sizeof(*head)))) if(!(head = malloc(sizeof(*head))))
...@@ -800,7 +801,7 @@ HRESULT gc_run(script_ctx_t *ctx) ...@@ -800,7 +801,7 @@ HRESULT gc_run(script_ctx_t *ctx)
chunk = head; chunk = head;
/* 1. Save actual refcounts and decrease them speculatively as-if we unlinked the objects */ /* 1. Save actual refcounts and decrease them speculatively as-if we unlinked the objects */
LIST_FOR_EACH_ENTRY(obj, &ctx->objects, jsdisp_t, entry) { LIST_FOR_EACH_ENTRY(obj, &thread_data->objects, jsdisp_t, entry) {
if(chunk_idx == ARRAY_SIZE(chunk->ref)) { if(chunk_idx == ARRAY_SIZE(chunk->ref)) {
if(!(chunk->next = malloc(sizeof(*chunk)))) { if(!(chunk->next = malloc(sizeof(*chunk)))) {
do { do {
...@@ -815,7 +816,7 @@ HRESULT gc_run(script_ctx_t *ctx) ...@@ -815,7 +816,7 @@ HRESULT gc_run(script_ctx_t *ctx)
} }
chunk->ref[chunk_idx++] = obj->ref; chunk->ref[chunk_idx++] = obj->ref;
} }
LIST_FOR_EACH_ENTRY(obj, &ctx->objects, jsdisp_t, entry) { LIST_FOR_EACH_ENTRY(obj, &thread_data->objects, jsdisp_t, entry) {
for(prop = obj->props, props_end = prop + obj->prop_cnt; prop < props_end; prop++) { for(prop = obj->props, props_end = prop + obj->prop_cnt; prop < props_end; prop++) {
switch(prop->type) { switch(prop->type) {
case PROP_JSVAL: case PROP_JSVAL:
...@@ -841,7 +842,7 @@ HRESULT gc_run(script_ctx_t *ctx) ...@@ -841,7 +842,7 @@ HRESULT gc_run(script_ctx_t *ctx)
} }
/* 2. Clear mark on objects with non-zero "external refcount" and all objects accessible from them */ /* 2. Clear mark on objects with non-zero "external refcount" and all objects accessible from them */
LIST_FOR_EACH_ENTRY(obj, &ctx->objects, jsdisp_t, entry) { LIST_FOR_EACH_ENTRY(obj, &thread_data->objects, jsdisp_t, entry) {
if(!obj->ref || !obj->gc_marked) if(!obj->ref || !obj->gc_marked)
continue; continue;
...@@ -899,7 +900,7 @@ HRESULT gc_run(script_ctx_t *ctx) ...@@ -899,7 +900,7 @@ HRESULT gc_run(script_ctx_t *ctx)
/* For weak refs, traverse paths accessible from it via the WeakMaps, if the WeakMaps are alive at this point. /* For weak refs, traverse paths accessible from it via the WeakMaps, if the WeakMaps are alive at this point.
We need both the key and the WeakMap for the entry to actually be accessible (and thus traversed). */ We need both the key and the WeakMap for the entry to actually be accessible (and thus traversed). */
if(obj2->has_weak_refs) { if(obj2->has_weak_refs) {
struct list *list = &RB_ENTRY_VALUE(rb_get(&ctx->weak_refs, obj2), struct weak_refs_entry, entry)->list; struct list *list = &RB_ENTRY_VALUE(rb_get(&thread_data->weak_refs, obj2), struct weak_refs_entry, entry)->list;
struct weakmap_entry *entry; struct weakmap_entry *entry;
LIST_FOR_EACH_ENTRY(entry, list, struct weakmap_entry, weak_refs_entry) { LIST_FOR_EACH_ENTRY(entry, list, struct weakmap_entry, weak_refs_entry) {
...@@ -926,7 +927,7 @@ HRESULT gc_run(script_ctx_t *ctx) ...@@ -926,7 +927,7 @@ HRESULT gc_run(script_ctx_t *ctx)
/* Restore */ /* Restore */
chunk = head; chunk_idx = 0; chunk = head; chunk_idx = 0;
LIST_FOR_EACH_ENTRY(obj, &ctx->objects, jsdisp_t, entry) { LIST_FOR_EACH_ENTRY(obj, &thread_data->objects, jsdisp_t, entry) {
obj->ref = chunk->ref[chunk_idx++]; obj->ref = chunk->ref[chunk_idx++];
if(chunk_idx == ARRAY_SIZE(chunk->ref)) { if(chunk_idx == ARRAY_SIZE(chunk->ref)) {
struct chunk *next = chunk->next; struct chunk *next = chunk->next;
...@@ -940,13 +941,13 @@ HRESULT gc_run(script_ctx_t *ctx) ...@@ -940,13 +941,13 @@ HRESULT gc_run(script_ctx_t *ctx)
return hres; return hres;
/* 3. Remove all the links from the marked objects, since they are dangling */ /* 3. Remove all the links from the marked objects, since they are dangling */
ctx->gc_is_unlinking = TRUE; thread_data->gc_is_unlinking = TRUE;
iter = list_head(&ctx->objects); iter = list_head(&thread_data->objects);
while(iter) { while(iter) {
obj = LIST_ENTRY(iter, jsdisp_t, entry); obj = LIST_ENTRY(iter, jsdisp_t, entry);
if(!obj->gc_marked) { if(!obj->gc_marked) {
iter = list_next(&ctx->objects, iter); iter = list_next(&thread_data->objects, iter);
continue; continue;
} }
...@@ -956,12 +957,12 @@ HRESULT gc_run(script_ctx_t *ctx) ...@@ -956,12 +957,12 @@ HRESULT gc_run(script_ctx_t *ctx)
/* Releasing unlinked object should not delete any other object, /* Releasing unlinked object should not delete any other object,
so we can safely obtain the next pointer now */ so we can safely obtain the next pointer now */
iter = list_next(&ctx->objects, iter); iter = list_next(&thread_data->objects, iter);
jsdisp_release(obj); jsdisp_release(obj);
} }
ctx->gc_is_unlinking = FALSE; thread_data->gc_is_unlinking = FALSE;
ctx->gc_last_tick = GetTickCount(); thread_data->gc_last_tick = GetTickCount();
return S_OK; return S_OK;
} }
...@@ -2174,7 +2175,7 @@ HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *b ...@@ -2174,7 +2175,7 @@ HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *b
unsigned i; unsigned i;
/* FIXME: Use better heuristics to decide when to run the GC */ /* FIXME: Use better heuristics to decide when to run the GC */
if(GetTickCount() - ctx->gc_last_tick > 30000) if(GetTickCount() - ctx->thread_data->gc_last_tick > 30000)
gc_run(ctx); gc_run(ctx);
TRACE("%p (%p)\n", dispex, prototype); TRACE("%p (%p)\n", dispex, prototype);
...@@ -2201,7 +2202,7 @@ HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *b ...@@ -2201,7 +2202,7 @@ HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *b
script_addref(ctx); script_addref(ctx);
dispex->ctx = ctx; dispex->ctx = ctx;
list_add_tail(&ctx->objects, &dispex->entry); list_add_tail(&ctx->thread_data->objects, &dispex->entry);
return S_OK; return S_OK;
} }
...@@ -2241,7 +2242,7 @@ void jsdisp_free(jsdisp_t *obj) ...@@ -2241,7 +2242,7 @@ void jsdisp_free(jsdisp_t *obj)
TRACE("(%p)\n", obj); TRACE("(%p)\n", obj);
if(obj->has_weak_refs) { if(obj->has_weak_refs) {
struct list *list = &RB_ENTRY_VALUE(rb_get(&obj->ctx->weak_refs, obj), struct weak_refs_entry, entry)->list; struct list *list = &RB_ENTRY_VALUE(rb_get(&obj->ctx->thread_data->weak_refs, obj), struct weak_refs_entry, entry)->list;
do { do {
remove_weakmap_entry(LIST_ENTRY(list->next, struct weakmap_entry, weak_refs_entry)); remove_weakmap_entry(LIST_ENTRY(list->next, struct weakmap_entry, weak_refs_entry));
} while(obj->has_weak_refs); } while(obj->has_weak_refs);
......
...@@ -88,6 +88,7 @@ void script_release(script_ctx_t *ctx) ...@@ -88,6 +88,7 @@ void script_release(script_ctx_t *ctx)
ctx->jscaller->ctx = NULL; ctx->jscaller->ctx = NULL;
IServiceProvider_Release(&ctx->jscaller->IServiceProvider_iface); IServiceProvider_Release(&ctx->jscaller->IServiceProvider_iface);
release_thread_data(ctx->thread_data);
free(ctx); free(ctx);
} }
...@@ -719,13 +720,6 @@ static ULONG WINAPI JScript_Release(IActiveScript *iface) ...@@ -719,13 +720,6 @@ static ULONG WINAPI JScript_Release(IActiveScript *iface)
return ref; return ref;
} }
static int weak_refs_compare(const void *key, const struct rb_entry *entry)
{
const struct weak_refs_entry *weak_refs_entry = RB_ENTRY_VALUE(entry, const struct weak_refs_entry, entry);
ULONG_PTR a = (ULONG_PTR)key, b = (ULONG_PTR)LIST_ENTRY(weak_refs_entry->list.next, struct weakmap_entry, weak_refs_entry)->key;
return (a > b) - (a < b);
}
static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface, static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface,
IActiveScriptSite *pass) IActiveScriptSite *pass)
{ {
...@@ -764,8 +758,6 @@ static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface, ...@@ -764,8 +758,6 @@ static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface,
ctx->html_mode = This->html_mode; ctx->html_mode = This->html_mode;
ctx->acc = jsval_undefined(); ctx->acc = jsval_undefined();
list_init(&ctx->named_items); list_init(&ctx->named_items);
list_init(&ctx->objects);
rb_init(&ctx->weak_refs, weak_refs_compare);
heap_pool_init(&ctx->tmp_heap); heap_pool_init(&ctx->tmp_heap);
hres = create_jscaller(ctx); hres = create_jscaller(ctx);
...@@ -774,6 +766,8 @@ static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface, ...@@ -774,6 +766,8 @@ static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface,
return hres; return hres;
} }
thread_data->ref++;
ctx->thread_data = thread_data;
ctx->last_match = jsstr_empty(); ctx->last_match = jsstr_empty();
This->ctx = ctx; This->ctx = ctx;
......
...@@ -132,6 +132,12 @@ HRESULT builtin_set_const(script_ctx_t*,jsdisp_t*,jsval_t); ...@@ -132,6 +132,12 @@ HRESULT builtin_set_const(script_ctx_t*,jsdisp_t*,jsval_t);
struct thread_data { struct thread_data {
LONG ref; LONG ref;
LONG thread_id; LONG thread_id;
BOOL gc_is_unlinking;
DWORD gc_last_tick;
struct list objects;
struct rb_tree weak_refs;
}; };
struct thread_data *get_thread_data(void); struct thread_data *get_thread_data(void);
...@@ -383,10 +389,9 @@ struct _script_ctx_t { ...@@ -383,10 +389,9 @@ struct _script_ctx_t {
SCRIPTSTATE state; SCRIPTSTATE state;
IActiveScript *active_script; IActiveScript *active_script;
struct thread_data *thread_data;
struct _call_frame_t *call_ctx; struct _call_frame_t *call_ctx;
struct list named_items; struct list named_items;
struct list objects;
struct rb_tree weak_refs;
IActiveScriptSite *site; IActiveScriptSite *site;
IInternetHostSecurityManager *secmgr; IInternetHostSecurityManager *secmgr;
DWORD safeopt; DWORD safeopt;
...@@ -399,9 +404,6 @@ struct _script_ctx_t { ...@@ -399,9 +404,6 @@ struct _script_ctx_t {
heap_pool_t tmp_heap; heap_pool_t tmp_heap;
BOOL gc_is_unlinking;
DWORD gc_last_tick;
jsval_t *stack; jsval_t *stack;
unsigned stack_top; unsigned stack_top;
jsval_t acc; jsval_t acc;
......
...@@ -40,6 +40,13 @@ HINSTANCE jscript_hinstance; ...@@ -40,6 +40,13 @@ HINSTANCE jscript_hinstance;
static DWORD jscript_tls; static DWORD jscript_tls;
static ITypeInfo *dispatch_typeinfo; static ITypeInfo *dispatch_typeinfo;
static int weak_refs_compare(const void *key, const struct rb_entry *entry)
{
const struct weak_refs_entry *weak_refs_entry = RB_ENTRY_VALUE(entry, const struct weak_refs_entry, entry);
ULONG_PTR a = (ULONG_PTR)key, b = (ULONG_PTR)LIST_ENTRY(weak_refs_entry->list.next, struct weakmap_entry, weak_refs_entry)->key;
return (a > b) - (a < b);
}
struct thread_data *get_thread_data(void) struct thread_data *get_thread_data(void)
{ {
struct thread_data *thread_data = TlsGetValue(jscript_tls); struct thread_data *thread_data = TlsGetValue(jscript_tls);
...@@ -49,6 +56,8 @@ struct thread_data *get_thread_data(void) ...@@ -49,6 +56,8 @@ struct thread_data *get_thread_data(void)
if(!thread_data) if(!thread_data)
return NULL; return NULL;
thread_data->thread_id = GetCurrentThreadId(); thread_data->thread_id = GetCurrentThreadId();
list_init(&thread_data->objects);
rb_init(&thread_data->weak_refs, weak_refs_compare);
TlsSetValue(jscript_tls, thread_data); TlsSetValue(jscript_tls, thread_data);
} }
......
...@@ -658,7 +658,7 @@ void remove_weakmap_entry(struct weakmap_entry *entry) ...@@ -658,7 +658,7 @@ void remove_weakmap_entry(struct weakmap_entry *entry)
else { else {
struct weak_refs_entry *weak_refs_entry = LIST_ENTRY(next, struct weak_refs_entry, list); struct weak_refs_entry *weak_refs_entry = LIST_ENTRY(next, struct weak_refs_entry, list);
entry->key->has_weak_refs = FALSE; entry->key->has_weak_refs = FALSE;
rb_remove(&entry->key->ctx->weak_refs, &weak_refs_entry->entry); rb_remove(&entry->key->ctx->thread_data->weak_refs, &weak_refs_entry->entry);
free(weak_refs_entry); free(weak_refs_entry);
} }
rb_remove(&weakmap->map, &entry->entry); rb_remove(&weakmap->map, &entry->entry);
...@@ -771,14 +771,14 @@ static HRESULT WeakMap_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigne ...@@ -771,14 +771,14 @@ static HRESULT WeakMap_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigne
} }
if(key->has_weak_refs) if(key->has_weak_refs)
weak_refs_entry = RB_ENTRY_VALUE(rb_get(&ctx->weak_refs, key), struct weak_refs_entry, entry); weak_refs_entry = RB_ENTRY_VALUE(rb_get(&ctx->thread_data->weak_refs, key), struct weak_refs_entry, entry);
else { else {
if(!(weak_refs_entry = malloc(sizeof(*weak_refs_entry)))) { if(!(weak_refs_entry = malloc(sizeof(*weak_refs_entry)))) {
jsval_release(entry->value); jsval_release(entry->value);
free(entry); free(entry);
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
} }
rb_put(&ctx->weak_refs, key, &weak_refs_entry->entry); rb_put(&ctx->thread_data->weak_refs, key, &weak_refs_entry->entry);
list_init(&weak_refs_entry->list); list_init(&weak_refs_entry->list);
key->has_weak_refs = TRUE; key->has_weak_refs = TRUE;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment