mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
variable.c: fix thread + fork errors in autoload
This is fairly non-intrusive bugfix to prevent children from trying to reach into thread stacks of the parent. I will probably reuse this idea and redo r62934, too (same bug). * vm_core.h (typedef struct rb_vm_struct): add fork_gen counter * thread.c (rb_thread_atfork_internal): increment fork_gen * variable.c (struct autoload_data_i): store fork_gen * variable.c (check_autoload_data): remove (replaced with get_...) * variable.c (get_autoload_data): check fork_gen when retrieving * variable.c (check_autoload_required): use get_autoload_data * variable.c (rb_autoloading_value): ditto * variable.c (rb_autoload_p): ditto * variable.c (current_autoload_data): ditto * variable.c (autoload_reset): reset fork_gen, adjust indent * variable.c (rb_autoload_load): set fork_gen when setting state * test/ruby/test_autoload.rb (test_autoload_fork): new test [ruby-core:86410] [Bug #14634] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63210 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
5b9bb50088
commit
b456eab2ea
4 changed files with 51 additions and 9 deletions
|
@ -285,6 +285,32 @@ p Foo::Bar
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_autoload_fork
|
||||||
|
EnvUtil.default_warning do
|
||||||
|
Tempfile.create(['autoload', '.rb']) {|file|
|
||||||
|
file.puts 'sleep 0.3; class AutoloadTest; end'
|
||||||
|
file.close
|
||||||
|
add_autoload(file.path)
|
||||||
|
begin
|
||||||
|
thrs = []
|
||||||
|
3.times do
|
||||||
|
thrs << Thread.new { AutoloadTest; nil }
|
||||||
|
thrs << Thread.new { fork { AutoloadTest } }
|
||||||
|
end
|
||||||
|
thrs.each(&:join)
|
||||||
|
thrs.each do |th|
|
||||||
|
pid = th.value or next
|
||||||
|
_, status = Process.waitpid2(pid)
|
||||||
|
assert_predicate status, :success?
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
remove_autoload_constant
|
||||||
|
assert_nil $!, '[ruby-core:86410] [Bug #14634]'
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end if Process.respond_to?(:fork)
|
||||||
|
|
||||||
def add_autoload(path)
|
def add_autoload(path)
|
||||||
(@autoload_paths ||= []) << path
|
(@autoload_paths ||= []) << path
|
||||||
::Object.class_eval {autoload(:AutoloadTest, path)}
|
::Object.class_eval {autoload(:AutoloadTest, path)}
|
||||||
|
|
1
thread.c
1
thread.c
|
@ -4216,6 +4216,7 @@ rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const r
|
||||||
}
|
}
|
||||||
rb_vm_living_threads_init(vm);
|
rb_vm_living_threads_init(vm);
|
||||||
rb_vm_living_threads_insert(vm, th);
|
rb_vm_living_threads_insert(vm, th);
|
||||||
|
vm->fork_gen++;
|
||||||
rb_thread_sync_reset_all();
|
rb_thread_sync_reset_all();
|
||||||
|
|
||||||
vm->sleeper = 0;
|
vm->sleeper = 0;
|
||||||
|
|
28
variable.c
28
variable.c
|
@ -21,6 +21,7 @@
|
||||||
#include "ccan/list/list.h"
|
#include "ccan/list/list.h"
|
||||||
#include "id_table.h"
|
#include "id_table.h"
|
||||||
#include "debug_counter.h"
|
#include "debug_counter.h"
|
||||||
|
#include "vm_core.h"
|
||||||
|
|
||||||
static struct rb_id_table *rb_global_tbl;
|
static struct rb_id_table *rb_global_tbl;
|
||||||
static ID autoload, classpath, tmp_classpath, classid;
|
static ID autoload, classpath, tmp_classpath, classid;
|
||||||
|
@ -1857,6 +1858,7 @@ struct autoload_data_i {
|
||||||
rb_const_flag_t flag;
|
rb_const_flag_t flag;
|
||||||
VALUE value;
|
VALUE value;
|
||||||
struct autoload_state *state; /* points to on-stack struct */
|
struct autoload_state *state; /* points to on-stack struct */
|
||||||
|
rb_serial_t fork_gen;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1879,8 +1881,18 @@ static const rb_data_type_t autoload_data_i_type = {
|
||||||
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
||||||
};
|
};
|
||||||
|
|
||||||
#define check_autoload_data(av) \
|
static struct autoload_data_i *
|
||||||
(struct autoload_data_i *)rb_check_typeddata((av), &autoload_data_i_type)
|
get_autoload_data(VALUE av)
|
||||||
|
{
|
||||||
|
struct autoload_data_i *ele = rb_check_typeddata(av, &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;
|
||||||
|
}
|
||||||
|
return ele;
|
||||||
|
}
|
||||||
|
|
||||||
RUBY_FUNC_EXPORTED void
|
RUBY_FUNC_EXPORTED void
|
||||||
rb_autoload(VALUE mod, ID id, const char *file)
|
rb_autoload(VALUE mod, ID id, const char *file)
|
||||||
|
@ -1980,7 +1992,7 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath)
|
||||||
const char *loading;
|
const char *loading;
|
||||||
int safe;
|
int safe;
|
||||||
|
|
||||||
if (!(load = autoload_data(mod, id)) || !(ele = check_autoload_data(load))) {
|
if (!(load = autoload_data(mod, id)) || !(ele = get_autoload_data(load))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
file = ele->feature;
|
file = ele->feature;
|
||||||
|
@ -2018,7 +2030,7 @@ rb_autoloading_value(VALUE mod, ID id, VALUE* value, rb_const_flag_t *flag)
|
||||||
VALUE load;
|
VALUE load;
|
||||||
struct autoload_data_i *ele;
|
struct autoload_data_i *ele;
|
||||||
|
|
||||||
if (!(load = autoload_data(mod, id)) || !(ele = check_autoload_data(load))) {
|
if (!(load = autoload_data(mod, id)) || !(ele = get_autoload_data(load))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (ele->state && ele->state->thread == rb_thread_current()) {
|
if (ele->state && ele->state->thread == rb_thread_current()) {
|
||||||
|
@ -2087,6 +2099,7 @@ autoload_reset(VALUE arg)
|
||||||
if (state->ele->state == state) {
|
if (state->ele->state == state) {
|
||||||
need_wakeups = 1;
|
need_wakeups = 1;
|
||||||
state->ele->state = 0;
|
state->ele->state = 0;
|
||||||
|
state->ele->fork_gen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* At the last, move a value defined in autoload to constant table */
|
/* At the last, move a value defined in autoload to constant table */
|
||||||
|
@ -2168,7 +2181,7 @@ rb_autoload_load(VALUE mod, ID id)
|
||||||
if (src && loading && strcmp(src, loading) == 0) return Qfalse;
|
if (src && loading && strcmp(src, loading) == 0) return Qfalse;
|
||||||
|
|
||||||
/* set ele->state for a marker of autoloading thread */
|
/* set ele->state for a marker of autoloading thread */
|
||||||
if (!(ele = check_autoload_data(load))) {
|
if (!(ele = get_autoload_data(load))) {
|
||||||
return Qfalse;
|
return Qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2178,6 +2191,7 @@ rb_autoload_load(VALUE mod, ID id)
|
||||||
state.thread = rb_thread_current();
|
state.thread = rb_thread_current();
|
||||||
if (!ele->state) {
|
if (!ele->state) {
|
||||||
ele->state = &state;
|
ele->state = &state;
|
||||||
|
ele->fork_gen = GET_VM()->fork_gen;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* autoload_reset will wake up any threads added to this
|
* autoload_reset will wake up any threads added to this
|
||||||
|
@ -2215,7 +2229,7 @@ rb_autoload_p(VALUE mod, ID id)
|
||||||
}
|
}
|
||||||
load = check_autoload_required(mod, id, 0);
|
load = check_autoload_required(mod, id, 0);
|
||||||
if (!load) return Qnil;
|
if (!load) return Qnil;
|
||||||
return (ele = check_autoload_data(load)) ? ele->feature : Qnil;
|
return (ele = get_autoload_data(load)) ? ele->feature : Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
MJIT_FUNC_EXPORTED void
|
MJIT_FUNC_EXPORTED void
|
||||||
|
@ -2638,7 +2652,7 @@ current_autoload_data(VALUE mod, ID id)
|
||||||
struct autoload_data_i *ele;
|
struct autoload_data_i *ele;
|
||||||
VALUE load = autoload_data(mod, id);
|
VALUE load = autoload_data(mod, id);
|
||||||
if (!load) return 0;
|
if (!load) return 0;
|
||||||
ele = check_autoload_data(load);
|
ele = get_autoload_data(load);
|
||||||
if (!ele) return 0;
|
if (!ele) return 0;
|
||||||
/* for autoloading thread, keep the defined value to autoloading storage */
|
/* for autoloading thread, keep the defined value to autoloading storage */
|
||||||
if (ele->state && (ele->state->thread == rb_thread_current())) {
|
if (ele->state && (ele->state->thread == rb_thread_current())) {
|
||||||
|
|
|
@ -539,6 +539,7 @@ typedef struct rb_vm_struct {
|
||||||
struct rb_thread_struct *main_thread;
|
struct rb_thread_struct *main_thread;
|
||||||
struct rb_thread_struct *running_thread;
|
struct rb_thread_struct *running_thread;
|
||||||
|
|
||||||
|
rb_serial_t fork_gen;
|
||||||
struct list_head waiting_fds; /* <=> struct waiting_fd */
|
struct list_head waiting_fds; /* <=> struct waiting_fd */
|
||||||
struct list_head living_threads;
|
struct list_head living_threads;
|
||||||
VALUE thgroup_default;
|
VALUE thgroup_default;
|
||||||
|
|
Loading…
Add table
Reference in a new issue