summaryrefslogtreecommitdiff
path: root/variable.c
diff options
context:
space:
mode:
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c252
1 files changed, 174 insertions, 78 deletions
diff --git a/variable.c b/variable.c
index 6de7b476e3..cdc9efe954 100644
--- a/variable.c
+++ b/variable.c
@@ -25,6 +25,7 @@
static struct rb_id_table *rb_global_tbl;
static ID autoload, classpath, tmp_classpath, classid;
+static VALUE autoload_featuremap; /* feature => autoload_i */
static void check_before_mod_set(VALUE, ID, VALUE, const char *);
static void setup_const_entry(rb_const_entry_t *, VALUE, VALUE, rb_const_flag_t);
@@ -1842,31 +1843,53 @@ autoload_data(VALUE mod, ID id)
return (VALUE)val;
}
+struct autoload_const {
+ struct list_node cnode; /* <=> autoload_data_i.constants */
+ VALUE mod;
+ VALUE ad; /* autoload_data_i */
+ VALUE value;
+ ID id;
+ int safe_level;
+ rb_const_flag_t flag;
+};
+
/* always on stack, no need to mark */
struct autoload_state {
- struct autoload_data_i *ele;
- VALUE mod;
+ struct autoload_const *ac;
VALUE result;
- ID id;
VALUE thread;
struct list_node waitq;
};
struct autoload_data_i {
VALUE feature;
- int safe_level;
- rb_const_flag_t flag;
- VALUE value;
struct autoload_state *state; /* points to on-stack struct */
rb_serial_t fork_gen;
+ struct list_head constants; /* <=> autoload_const.cnode */
};
static void
autoload_i_mark(void *ptr)
{
struct autoload_data_i *p = ptr;
+
rb_gc_mark(p->feature);
- rb_gc_mark(p->value);
+
+ /* allow GC to free us if no modules refer to this via autoload_const.ad */
+ if (list_empty(&p->constants)) {
+ rb_hash_delete(autoload_featuremap, p->feature);
+ }
+}
+
+static void
+autoload_i_free(void *ptr)
+{
+ struct autoload_data_i *p = ptr;
+
+ /* we may leak some memory at VM shutdown time, no big deal */
+ if (list_empty(&p->constants)) {
+ xfree(p);
+ }
}
static size_t
@@ -1877,20 +1900,53 @@ autoload_i_memsize(const void *ptr)
static const rb_data_type_t autoload_data_i_type = {
"autoload_i",
- {autoload_i_mark, RUBY_TYPED_DEFAULT_FREE, autoload_i_memsize,},
+ {autoload_i_mark, autoload_i_free, autoload_i_memsize,},
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static void
+autoload_c_mark(void *ptr)
+{
+ struct autoload_const *ac = ptr;
+
+ rb_gc_mark(ac->mod);
+ rb_gc_mark(ac->ad);
+ rb_gc_mark(ac->value);
+}
+
+static void
+autoload_c_free(void *ptr)
+{
+ struct autoload_const *ac = ptr;
+ list_del(&ac->cnode);
+ xfree(ac);
+}
+
+static size_t
+autoload_c_memsize(const void *ptr)
+{
+ return sizeof(struct autoload_const);
+}
+
+static const rb_data_type_t autoload_const_type = {
+ "autoload_const",
+ {autoload_c_mark, autoload_c_free, autoload_c_memsize,},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
static struct autoload_data_i *
-get_autoload_data(VALUE av)
+get_autoload_data(VALUE acv, struct autoload_const **acp)
{
- struct autoload_data_i *ele = rb_check_typeddata(av, &autoload_data_i_type);
+ struct autoload_const *ac = rb_check_typeddata(acv, &autoload_const_type);
+ struct autoload_data_i *ele;
+ ele = rb_check_typeddata(ac->ad, &autoload_data_i_type);
/* do not reach across stack for ->state after forking: */
if (ele && ele->state && ele->fork_gen != GET_VM()->fork_gen) {
ele->state = 0;
ele->fork_gen = 0;
}
+ if (acp) *acp = ac;
return ele;
}
@@ -1940,17 +1996,42 @@ rb_autoload_str(VALUE mod, ID id, VALUE file)
DATA_PTR(av) = tbl = st_init_numtable();
}
- ad = TypedData_Make_Struct(0, struct autoload_data_i, &autoload_data_i_type, ele);
if (OBJ_TAINTED(file)) {
file = rb_str_dup(file);
FL_UNSET(file, FL_TAINT);
}
- ele->feature = rb_fstring(file);
- ele->safe_level = rb_safe_level();
- ele->value = Qundef;
- ele->state = 0;
- ele->flag = CONST_PUBLIC;
- st_insert(tbl, (st_data_t)id, (st_data_t)ad);
+ file = rb_fstring(file);
+ if (!autoload_featuremap) {
+ autoload_featuremap = rb_hash_new_compare_by_id();
+ rb_obj_hide(autoload_featuremap);
+ rb_gc_register_mark_object(autoload_featuremap);
+ }
+ ad = rb_hash_aref(autoload_featuremap, file);
+ if (NIL_P(ad)) {
+ ad = TypedData_Make_Struct(0, struct autoload_data_i,
+ &autoload_data_i_type, ele);
+ ele->feature = file;
+ ele->state = 0;
+ list_head_init(&ele->constants);
+ rb_hash_aset(autoload_featuremap, file, ad);
+ }
+ else {
+ ele = rb_check_typeddata(ad, &autoload_data_i_type);
+ }
+ {
+ VALUE acv;
+ struct autoload_const *ac;
+ acv = TypedData_Make_Struct(0, struct autoload_const,
+ &autoload_const_type, ac);
+ ac->mod = mod;
+ ac->id = id;
+ ac->value = Qundef;
+ ac->safe_level = rb_safe_level();
+ ac->flag = CONST_PUBLIC;
+ ac->ad = ad;
+ list_add_tail(&ele->constants, &ac->cnode);
+ st_insert(tbl, (st_data_t)id, (st_data_t)acv);
+ }
}
static void
@@ -1960,8 +2041,22 @@ autoload_delete(VALUE mod, ID id)
if (st_lookup(RCLASS_IV_TBL(mod), (st_data_t)autoload, &val)) {
struct st_table *tbl = check_autoload_table((VALUE)val);
+ struct autoload_data_i *ele;
+ struct autoload_const *ac;
st_delete(tbl, &n, &load);
+ ele = get_autoload_data((VALUE)load, &ac);
+ VM_ASSERT(ele);
+ if (ele) {
+ VM_ASSERT(!list_empty(&ele->constants));
+ }
+
+ /*
+ * we must delete here to avoid "already initialized" warnings
+ * with parallel autoload. Using list_del_init here so list_del
+ * works in autoload_c_free
+ */
+ list_del_init(&ac->cnode);
if (tbl->num_entries == 0) {
n = autoload;
@@ -1987,12 +2082,13 @@ reset_safe(VALUE safe)
static VALUE
check_autoload_required(VALUE mod, ID id, const char **loadingpath)
{
- VALUE file, load;
+ VALUE file;
+ VALUE load = autoload_data(mod, id);
struct autoload_data_i *ele;
const char *loading;
int safe;
- if (!(load = autoload_data(mod, id)) || !(ele = get_autoload_data(load))) {
+ if (!load || !(ele = get_autoload_data(load, 0))) {
return 0;
}
file = ele->feature;
@@ -2027,19 +2123,21 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath)
MJIT_FUNC_EXPORTED int
rb_autoloading_value(VALUE mod, ID id, VALUE* value, rb_const_flag_t *flag)
{
- VALUE load;
+ VALUE load = autoload_data(mod, id);
struct autoload_data_i *ele;
+ struct autoload_const *ac;
- if (!(load = autoload_data(mod, id)) || !(ele = get_autoload_data(load))) {
- return 0;
+ if (!load || !(ele = get_autoload_data(load, &ac))) {
+ return 0;
}
+
if (ele->state && ele->state->thread == rb_thread_current()) {
- if (ele->value != Qundef) {
+ if (ac->value != Qundef) {
if (value) {
- *value = ele->value;
+ *value = ac->value;
}
if (flag) {
- *flag = ele->flag;
+ *flag = ac->flag;
}
return 1;
}
@@ -2058,23 +2156,16 @@ autoload_defined_p(VALUE mod, ID id)
return !rb_autoloading_value(mod, id, NULL, NULL);
}
-struct autoload_const_set_args {
- VALUE mod;
- ID id;
- VALUE value;
- rb_const_flag_t flag;
-};
-
-static void const_tbl_update(struct autoload_const_set_args *);
+static void const_tbl_update(struct autoload_const *);
static VALUE
autoload_const_set(VALUE arg)
{
- struct autoload_const_set_args* args = (struct autoload_const_set_args *)arg;
- VALUE klass = args->mod;
- ID id = args->id;
- check_before_mod_set(klass, id, args->value, "constant");
- const_tbl_update(args);
+ struct autoload_const *ac = (struct autoload_const *)arg;
+ VALUE klass = ac->mod;
+ ID id = ac->id;
+ check_before_mod_set(klass, id, ac->value, "constant");
+ const_tbl_update(ac);
return 0; /* ignored */
}
@@ -2082,10 +2173,13 @@ static VALUE
autoload_require(VALUE arg)
{
struct autoload_state *state = (struct autoload_state *)arg;
+ struct autoload_const *ac = state->ac;
+ struct autoload_data_i *ele;
+ ele = rb_check_typeddata(ac->ad, &autoload_data_i_type);
/* this may release GVL and switch threads: */
state->result = rb_funcall(rb_vm_top_self(), rb_intern("require"), 1,
- state->ele->feature);
+ ele->feature);
return state->result;
}
@@ -2095,26 +2189,27 @@ autoload_reset(VALUE arg)
{
struct autoload_state *state = (struct autoload_state *)arg;
int need_wakeups = 0;
+ struct autoload_const *ac = state->ac;
+ struct autoload_data_i *ele;
- if (state->ele->state == state) {
+ ele = rb_check_typeddata(ac->ad, &autoload_data_i_type);
+ if (ele->state == state) {
need_wakeups = 1;
- state->ele->state = 0;
- state->ele->fork_gen = 0;
+ ele->state = 0;
+ ele->fork_gen = 0;
}
/* At the last, move a value defined in autoload to constant table */
- if (RTEST(state->result) && state->ele->value != Qundef) {
- int safe_backup;
- struct autoload_const_set_args args;
-
- args.mod = state->mod;
- args.id = state->id;
- args.value = state->ele->value;
- args.flag = state->ele->flag;
- safe_backup = rb_safe_level();
- rb_set_safe_level_force(state->ele->safe_level);
- rb_ensure(autoload_const_set, (VALUE)&args,
- reset_safe, (VALUE)safe_backup);
+ if (RTEST(state->result)) {
+ struct autoload_const *next;
+ int safe_backup = rb_safe_level();
+
+ list_for_each_safe(&ele->constants, ac, next, cnode) {
+ if (ac->value != Qundef) {
+ rb_ensure(autoload_const_set, (VALUE)ac,
+ reset_safe, (VALUE)safe_backup);
+ }
+ }
}
/* wakeup any waiters we had */
@@ -2172,6 +2267,7 @@ rb_autoload_load(VALUE mod, ID id)
VALUE load, result;
const char *loading = 0, *src;
struct autoload_data_i *ele;
+ struct autoload_const *ac;
struct autoload_state state;
if (!autoload_defined_p(mod, id)) return Qfalse;
@@ -2181,13 +2277,10 @@ rb_autoload_load(VALUE mod, ID id)
if (src && loading && strcmp(src, loading) == 0) return Qfalse;
/* set ele->state for a marker of autoloading thread */
- if (!(ele = get_autoload_data(load))) {
+ if (!(ele = get_autoload_data(load, &ac))) {
return Qfalse;
}
-
- state.ele = ele;
- state.mod = mod;
- state.id = id;
+ state.ac = ac;
state.thread = rb_thread_current();
if (!ele->state) {
ele->state = &state;
@@ -2229,7 +2322,7 @@ rb_autoload_p(VALUE mod, ID id)
}
load = check_autoload_required(mod, id, 0);
if (!load) return Qnil;
- return (ele = get_autoload_data(load)) ? ele->feature : Qnil;
+ return (ele = get_autoload_data(load, 0)) ? ele->feature : Qnil;
}
MJIT_FUNC_EXPORTED void
@@ -2610,12 +2703,12 @@ rb_const_set(VALUE klass, ID id, VALUE val)
setup_const_entry(ce, klass, val, CONST_PUBLIC);
}
else {
- struct autoload_const_set_args args;
- args.mod = klass;
- args.id = id;
- args.value = val;
- args.flag = CONST_PUBLIC;
- const_tbl_update(&args);
+ struct autoload_const ac;
+ ac.mod = klass;
+ ac.id = id;
+ ac.value = val;
+ ac.flag = CONST_PUBLIC;
+ const_tbl_update(&ac);
}
/*
* Resolve and cache class name immediately to resolve ambiguity
@@ -2647,12 +2740,12 @@ rb_const_set(VALUE klass, ID id, VALUE val)
}
static struct autoload_data_i *
-current_autoload_data(VALUE mod, ID id)
+current_autoload_data(VALUE mod, ID id, struct autoload_const **acp)
{
struct autoload_data_i *ele;
VALUE load = autoload_data(mod, id);
if (!load) return 0;
- ele = get_autoload_data(load);
+ ele = get_autoload_data(load, acp);
if (!ele) return 0;
/* for autoloading thread, keep the defined value to autoloading storage */
if (ele->state && (ele->state->thread == rb_thread_current())) {
@@ -2662,25 +2755,25 @@ current_autoload_data(VALUE mod, ID id)
}
static void
-const_tbl_update(struct autoload_const_set_args *args)
+const_tbl_update(struct autoload_const *ac)
{
VALUE value;
- VALUE klass = args->mod;
- VALUE val = args->value;
- ID id = args->id;
+ VALUE klass = ac->mod;
+ VALUE val = ac->value;
+ ID id = ac->id;
struct rb_id_table *tbl = RCLASS_CONST_TBL(klass);
- rb_const_flag_t visibility = args->flag;
+ rb_const_flag_t visibility = ac->flag;
rb_const_entry_t *ce;
if (rb_id_table_lookup(tbl, id, &value)) {
ce = (rb_const_entry_t *)value;
if (ce->value == Qundef) {
- struct autoload_data_i *ele = current_autoload_data(klass, id);
+ struct autoload_data_i *ele = current_autoload_data(klass, id, &ac);
if (ele) {
rb_clear_constant_cache();
- ele->value = val; /* autoload_i is non-WB-protected */
+ ac->value = val; /* autoload_i is non-WB-protected */
return;
}
/* otherwise, allow to override */
@@ -2753,6 +2846,7 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv,
}
for (i = 0; i < argc; i++) {
+ struct autoload_const *ac;
VALUE val = argv[i];
id = rb_check_id(&val);
if (!id) {
@@ -2767,10 +2861,12 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv,
ce->flag &= ~mask;
ce->flag |= flag;
if (ce->value == Qundef) {
- struct autoload_data_i *ele = current_autoload_data(mod, id);
+ struct autoload_data_i *ele;
+
+ ele = current_autoload_data(mod, id, &ac);
if (ele) {
- ele->flag &= ~mask;
- ele->flag |= flag;
+ ac->flag &= ~mask;
+ ac->flag |= flag;
}
}
}