Skip to content

Commit b07484c

Browse files
authored
store bindings now in an array for safe, fast iteration (#48212)
This lets us query and iterate the bindings without needing to hold locks over the queries, making those operations more scalable and safe to use across safepoints or concurrently. It is similar to how we already deal with specializations and datatype caches. Updates the smallintcache to additionally be more threadsafe for users, with explicit acquire and release operations.
1 parent e40d813 commit b07484c

File tree

11 files changed

+252
-213
lines changed

11 files changed

+252
-213
lines changed

src/gc-heap-snapshot.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -392,17 +392,18 @@ void _gc_heap_snapshot_record_object_edge(jl_value_t *from, jl_value_t *to, void
392392
g_snapshot->names.find_or_create_string_id(path));
393393
}
394394

395-
void _gc_heap_snapshot_record_module_to_binding(jl_module_t *module, jl_sym_t *name, jl_binding_t *binding) JL_NOTSAFEPOINT
395+
void _gc_heap_snapshot_record_module_to_binding(jl_module_t *module, jl_binding_t *binding) JL_NOTSAFEPOINT
396396
{
397+
jl_globalref_t *globalref = binding->globalref;
398+
jl_sym_t *name = globalref->name;
397399
auto from_node_idx = record_node_to_gc_snapshot((jl_value_t*)module);
398400
auto to_node_idx = record_pointer_to_gc_snapshot(binding, sizeof(jl_binding_t), jl_symbol_name(name));
399401

400402
jl_value_t *value = jl_atomic_load_relaxed(&binding->value);
401403
auto value_idx = value ? record_node_to_gc_snapshot(value) : 0;
402404
jl_value_t *ty = jl_atomic_load_relaxed(&binding->ty);
403405
auto ty_idx = ty ? record_node_to_gc_snapshot(ty) : 0;
404-
jl_value_t *globalref = (jl_value_t*)binding->globalref;
405-
auto globalref_idx = globalref ? record_node_to_gc_snapshot(globalref) : 0;
406+
auto globalref_idx = record_node_to_gc_snapshot((jl_value_t*)globalref);
406407

407408
auto &from_node = g_snapshot->nodes[from_node_idx];
408409
auto &to_node = g_snapshot->nodes[to_node_idx];

src/gc-heap-snapshot.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ void _gc_heap_snapshot_record_task_to_frame_edge(jl_task_t *from, void *to) JL_N
2020
void _gc_heap_snapshot_record_frame_to_frame_edge(jl_gcframe_t *from, jl_gcframe_t *to) JL_NOTSAFEPOINT;
2121
void _gc_heap_snapshot_record_array_edge(jl_value_t *from, jl_value_t *to, size_t index) JL_NOTSAFEPOINT;
2222
void _gc_heap_snapshot_record_object_edge(jl_value_t *from, jl_value_t *to, void* slot) JL_NOTSAFEPOINT;
23-
void _gc_heap_snapshot_record_module_to_binding(jl_module_t* module, jl_sym_t *name, jl_binding_t* binding) JL_NOTSAFEPOINT;
23+
void _gc_heap_snapshot_record_module_to_binding(jl_module_t* module, jl_binding_t* binding) JL_NOTSAFEPOINT;
2424
// Used for objects managed by GC, but which aren't exposed in the julia object, so have no
2525
// field or index. i.e. they're not reachable from julia code, but we _will_ hit them in
2626
// the GC mark phase (so we can check their type tag to get the size).
@@ -73,10 +73,10 @@ static inline void gc_heap_snapshot_record_object_edge(jl_value_t *from, jl_valu
7373
}
7474
}
7575

76-
static inline void gc_heap_snapshot_record_module_to_binding(jl_module_t* module, jl_sym_t *name, jl_binding_t* binding) JL_NOTSAFEPOINT
76+
static inline void gc_heap_snapshot_record_module_to_binding(jl_module_t* module, jl_binding_t* binding) JL_NOTSAFEPOINT
7777
{
7878
if (__unlikely(gc_heap_snapshot_enabled && prev_sweep_full)) {
79-
_gc_heap_snapshot_record_module_to_binding(module, name, binding);
79+
_gc_heap_snapshot_record_module_to_binding(module, binding);
8080
}
8181
}
8282

src/gc.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2537,22 +2537,35 @@ module_binding: {
25372537
gc_mark_binding_t *binding = gc_pop_markdata(&sp, gc_mark_binding_t);
25382538
jl_binding_t **begin = binding->begin;
25392539
jl_binding_t **end = binding->end;
2540-
for (; begin < end; begin += 2) {
2540+
for (; begin < end; begin++) {
25412541
jl_binding_t *b = *begin;
2542-
if (b == (jl_binding_t*)HT_NOTFOUND)
2542+
if (b == (jl_binding_t*)jl_nothing)
25432543
continue;
25442544
verify_parent1("module", binding->parent, begin, "binding_buff");
25452545
// Record the size used for the box for non-const bindings
2546-
gc_heap_snapshot_record_module_to_binding(binding->parent, (jl_sym_t*)begin[-1], b);
2546+
gc_heap_snapshot_record_module_to_binding(binding->parent, b);
25472547
if (gc_try_setmark((jl_value_t*)b, &binding->nptr, &tag, &bits)) {
2548-
begin += 2;
2548+
begin++;
25492549
binding->begin = begin;
25502550
gc_repush_markdata(&sp, gc_mark_binding_t);
25512551
new_obj = (jl_value_t*)b;
25522552
goto mark;
25532553
}
25542554
}
2555+
binding->begin = begin;
25552556
jl_module_t *m = binding->parent;
2557+
jl_value_t *bindings = (jl_value_t*)jl_atomic_load_relaxed(&m->bindings);
2558+
if (gc_try_setmark(bindings, &binding->nptr, &tag, &bits)) {
2559+
gc_repush_markdata(&sp, gc_mark_binding_t);
2560+
new_obj = (jl_value_t*)bindings;
2561+
goto mark;
2562+
}
2563+
jl_value_t *bindingkeyset = (jl_value_t*)jl_atomic_load_relaxed(&m->bindingkeyset);
2564+
if (gc_try_setmark(bindingkeyset, &binding->nptr, &tag, &bits)) {
2565+
gc_repush_markdata(&sp, gc_mark_binding_t);
2566+
new_obj = bindingkeyset;
2567+
goto mark;
2568+
}
25562569
int scanparent = gc_try_setmark((jl_value_t*)m->parent, &binding->nptr, &tag, &bits);
25572570
size_t nusings = m->usings.len;
25582571
if (nusings) {
@@ -2760,8 +2773,9 @@ mark: {
27602773
else if (foreign_alloc)
27612774
objprofile_count(vt, bits == GC_OLD_MARKED, sizeof(jl_module_t));
27622775
jl_module_t *m = (jl_module_t*)new_obj;
2763-
jl_binding_t **table = (jl_binding_t**)m->bindings.table;
2764-
size_t bsize = m->bindings.size;
2776+
jl_svec_t *bindings = jl_atomic_load_relaxed(&m->bindings);
2777+
jl_binding_t **table = (jl_binding_t**)jl_svec_data(bindings);
2778+
size_t bsize = jl_svec_len(bindings);
27652779
uintptr_t nptr = ((bsize + m->usings.len + 1) << 2) | (bits & GC_OLD);
27662780
gc_mark_binding_t markdata = {m, table + 1, table + bsize, nptr};
27672781
gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(module_binding),

src/gf.c

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ static jl_method_instance_t *jl_specializations_get_linfo_(jl_method_t *m JL_PRO
172172
if (!hv)
173173
i -= 1;
174174
assert(jl_svecref(specializations, i) == jl_nothing);
175-
jl_svecset(specializations, i, mi); // jl_atomic_store_release?
175+
jl_svecset(specializations, i, mi); // jl_atomic_store_relaxed?
176176
if (hv) {
177177
// TODO: fuse lookup and insert steps?
178178
jl_smallintset_insert(&m->speckeyset, (jl_value_t*)m, speccache_hash, i, specializations);
@@ -471,39 +471,38 @@ int foreach_mtable_in_module(
471471
int (*visit)(jl_methtable_t *mt, void *env),
472472
void *env)
473473
{
474-
size_t i;
475-
void **table = m->bindings.table;
476-
for (i = 0; i < m->bindings.size; i += 2) {
477-
if (table[i+1] != HT_NOTFOUND) {
478-
jl_sym_t *name = (jl_sym_t*)table[i];
479-
jl_binding_t *b = (jl_binding_t*)table[i+1];
480-
JL_GC_PROMISE_ROOTED(b);
481-
if (jl_atomic_load_relaxed(&b->owner) == b && b->constp) {
482-
jl_value_t *v = jl_atomic_load_relaxed(&b->value);
483-
if (v) {
484-
jl_value_t *uw = jl_unwrap_unionall(v);
485-
if (jl_is_datatype(uw)) {
486-
jl_typename_t *tn = ((jl_datatype_t*)uw)->name;
487-
if (tn->module == m && tn->name == name && tn->wrapper == v) {
488-
// this is the original/primary binding for the type (name/wrapper)
489-
jl_methtable_t *mt = tn->mt;
490-
if (mt != NULL && (jl_value_t*)mt != jl_nothing && mt != jl_type_type_mt && mt != jl_nonfunction_mt) {
491-
if (!visit(mt, env))
492-
return 0;
493-
}
494-
}
495-
}
496-
else if (jl_is_module(v)) {
497-
jl_module_t *child = (jl_module_t*)v;
498-
if (child != m && child->parent == m && child->name == name) {
499-
// this is the original/primary binding for the submodule
500-
if (!foreach_mtable_in_module(child, visit, env))
474+
jl_svec_t *table = jl_atomic_load_relaxed(&m->bindings);
475+
for (size_t i = 0; i < jl_svec_len(table); i++) {
476+
jl_binding_t *b = (jl_binding_t*)jl_svec_ref(table, i);
477+
if ((void*)b == jl_nothing)
478+
break;
479+
jl_sym_t *name = b->globalref->name;
480+
if (jl_atomic_load_relaxed(&b->owner) == b && b->constp) {
481+
jl_value_t *v = jl_atomic_load_relaxed(&b->value);
482+
if (v) {
483+
jl_value_t *uw = jl_unwrap_unionall(v);
484+
if (jl_is_datatype(uw)) {
485+
jl_typename_t *tn = ((jl_datatype_t*)uw)->name;
486+
if (tn->module == m && tn->name == name && tn->wrapper == v) {
487+
// this is the original/primary binding for the type (name/wrapper)
488+
jl_methtable_t *mt = tn->mt;
489+
if (mt != NULL && (jl_value_t*)mt != jl_nothing && mt != jl_type_type_mt && mt != jl_nonfunction_mt) {
490+
if (!visit(mt, env))
501491
return 0;
502492
}
503493
}
504494
}
495+
else if (jl_is_module(v)) {
496+
jl_module_t *child = (jl_module_t*)v;
497+
if (child != m && child->parent == m && child->name == name) {
498+
// this is the original/primary binding for the submodule
499+
if (!foreach_mtable_in_module(child, visit, env))
500+
return 0;
501+
}
502+
}
505503
}
506504
}
505+
table = jl_atomic_load_relaxed(&m->bindings);
507506
}
508507
return 1;
509508
}

src/init.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -910,10 +910,10 @@ static void post_boot_hooks(void)
910910
jl_init_box_caches();
911911

912912
// set module field of primitive types
913-
int i;
914-
void **table = jl_core_module->bindings.table;
915-
for (i = 1; i < jl_core_module->bindings.size; i += 2) {
916-
if (table[i] != HT_NOTFOUND) {
913+
jl_svec_t *bindings = jl_atomic_load_relaxed(&jl_core_module->bindings);
914+
jl_value_t **table = jl_svec_data(bindings);
915+
for (size_t i = 0; i < jl_svec_len(bindings); i++) {
916+
if (table[i] != jl_nothing) {
917917
jl_binding_t *b = (jl_binding_t*)table[i];
918918
jl_value_t *v = jl_atomic_load_relaxed(&b->value);
919919
if (v) {

src/julia.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -584,8 +584,9 @@ typedef struct _jl_module_t {
584584
JL_DATA_TYPE
585585
jl_sym_t *name;
586586
struct _jl_module_t *parent;
587+
_Atomic(jl_svec_t*) bindings;
588+
_Atomic(jl_array_t*) bindingkeyset; // index lookup by name into bindings
587589
// hidden fields:
588-
htable_t bindings;
589590
arraylist_t usings; // modules with all bindings potentially imported
590591
jl_uuid_t build_id;
591592
jl_uuid_t uuid;
@@ -648,12 +649,12 @@ typedef struct _jl_typemap_level_t {
648649
// contains the TypeMap for one Type
649650
typedef struct _jl_methtable_t {
650651
JL_DATA_TYPE
651-
jl_sym_t *name; // sometimes a hack used by serialization to handle kwsorter
652+
jl_sym_t *name; // sometimes used for debug printing
652653
_Atomic(jl_typemap_t*) defs;
653654
_Atomic(jl_array_t*) leafcache;
654655
_Atomic(jl_typemap_t*) cache;
655656
_Atomic(intptr_t) max_args; // max # of non-vararg arguments in a signature
656-
jl_module_t *module; // used for incremental serialization to locate original binding
657+
jl_module_t *module; // sometimes used for debug printing
657658
jl_array_t *backedges; // (sig, caller::MethodInstance) pairs
658659
jl_mutex_t writelock;
659660
uint8_t offs; // 0, or 1 to skip splitting typemap on first (function) argument
@@ -986,9 +987,10 @@ STATIC_INLINE jl_value_t *jl_svecset(
986987
{
987988
assert(jl_typeis(t,jl_simplevector_type));
988989
assert(i < jl_svec_len(t));
989-
// TODO: while svec is supposedly immutable, in practice we sometimes publish it first
990-
// and set the values lazily. Those users should be using jl_atomic_store_release here.
991-
jl_svec_data(t)[i] = (jl_value_t*)x;
990+
// while svec is supposedly immutable, in practice we sometimes publish it
991+
// first and set the values lazily. Those users occasionally might need to
992+
// instead use jl_atomic_store_release here.
993+
jl_atomic_store_relaxed((_Atomic(jl_value_t*)*)jl_svec_data(t) + i, (jl_value_t*)x);
992994
jl_gc_wb(t, x);
993995
return (jl_value_t*)x;
994996
}

0 commit comments

Comments
 (0)