summaryrefslogtreecommitdiff
path: root/variable.c
diff options
context:
space:
mode:
authornagachika <nagachika@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-01-23 14:14:25 +0000
committernagachika <nagachika@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-01-23 14:14:25 +0000
commit63d9ab33fe419167382fc03be2c00413a78c05cb (patch)
tree3fd057c62fa4c81bbc7e1520d13976c1fbdd4930 /variable.c
parente42dcdb5152f05fc6ccc3f438b344f2d88d8b88f (diff)
merge revision(s) 62934,63210,63215,63309: [Backport #14634]
thread_sync.c: avoid reaching across stacks of dead threads rb_ensure is insufficient cleanup for fork and we must reinitialize all waitqueues in the child process. Unfortunately this increases the footprint of ConditionVariable, Queue and SizedQueue by 8 bytes on 32-bit (16 bytes on 64-bit). [ruby-core:86316] [Bug #14634] 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] thread_sync: redo r62934 to use fork_gen Instead of maintaining linked-lists to store all rb_queue/rb_szqueue/rb_condvar structs; store only a fork_gen serial number to simplify management of these items. This reduces initialization costs and avoids the up-front cost of resetting all Queue/SizedQueue/ConditionVariable objects at fork while saving 8 bytes per-structure on 64-bit. There are no savings on 32-bit. * thread.c (rb_thread_atfork_internal): remove rb_thread_sync_reset_all call * thread_sync.c (rb_thread_sync_reset_all): remove * thread_sync.c (queue_live): remove * thread_sync.c (queue_free): remove * thread_sync.c (struct rb_queue): s/live/fork_gen/ * thread_sync.c (queue_data_type): use default free * thread_sync.c (queue_alloc): remove list_add * thread_sync.c (queue_fork_check): new function * thread_sync.c (queue_ptr): call queue_fork_check * thread_sync.c (szqueue_free): remove * thread_sync.c (szqueue_data_type): use default free * thread_sync.c (szqueue_alloc): remove list_add * thread_sync.c (szqueue_ptr): check fork_gen via queue_fork_check * thread_sync.c (struct rb_condvar): s/live/fork_gen/ * thread_sync.c (condvar_free): remove * thread_sync.c (cv_data_type): use default free * thread_sync.c (condvar_ptr): check fork_gen * thread_sync.c (condvar_alloc): remove list_add [ruby-core:86316] [Bug #14634] thread_sync.c (condvar_ptr): reset fork_gen after forking Otherwise the condition variable waiter list will always be empty, which is wrong :x [Bug #14725] [Bug #14634] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_5@66912 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/variable.c b/variable.c
index f700303d2f..bfb6fa2edd 100644
--- a/variable.c
+++ b/variable.c
@@ -20,6 +20,7 @@
#include "ccan/list/list.h"
#include "id_table.h"
#include "debug_counter.h"
+#include "vm_core.h"
struct rb_id_table *rb_global_tbl;
static ID autoload, classpath, tmp_classpath, classid;
@@ -1859,6 +1860,7 @@ struct autoload_data_i {
rb_const_flag_t flag;
VALUE value;
struct autoload_state *state; /* points to on-stack struct */
+ rb_serial_t fork_gen;
};
static void
@@ -1881,8 +1883,18 @@ static const rb_data_type_t autoload_data_i_type = {
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
-#define check_autoload_data(av) \
- (struct autoload_data_i *)rb_check_typeddata((av), &autoload_data_i_type)
+static struct autoload_data_i *
+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
rb_autoload(VALUE mod, ID id, const char *file)
@@ -1982,7 +1994,7 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath)
const char *loading;
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;
}
file = ele->feature;
@@ -2020,7 +2032,7 @@ rb_autoloading_value(VALUE mod, ID id, VALUE* value, rb_const_flag_t *flag)
VALUE load;
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;
}
if (ele->state && ele->state->thread == rb_thread_current()) {
@@ -2087,8 +2099,9 @@ autoload_reset(VALUE arg)
int need_wakeups = 0;
if (state->ele->state == state) {
- need_wakeups = 1;
- state->ele->state = 0;
+ need_wakeups = 1;
+ state->ele->state = 0;
+ state->ele->fork_gen = 0;
}
/* At the last, move a value defined in autoload to constant table */
@@ -2170,7 +2183,7 @@ 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 = check_autoload_data(load))) {
+ if (!(ele = get_autoload_data(load))) {
return Qfalse;
}
@@ -2180,6 +2193,7 @@ rb_autoload_load(VALUE mod, ID id)
state.thread = rb_thread_current();
if (!ele->state) {
ele->state = &state;
+ ele->fork_gen = GET_VM()->fork_gen;
/*
* autoload_reset will wake up any threads added to this
@@ -2217,7 +2231,7 @@ rb_autoload_p(VALUE mod, ID id)
}
load = check_autoload_required(mod, id, 0);
if (!load) return Qnil;
- return (ele = check_autoload_data(load)) ? ele->feature : Qnil;
+ return (ele = get_autoload_data(load)) ? ele->feature : Qnil;
}
void
@@ -2646,7 +2660,7 @@ current_autoload_data(VALUE mod, ID id)
struct autoload_data_i *ele;
VALUE load = autoload_data(mod, id);
if (!load) return 0;
- ele = check_autoload_data(load);
+ ele = get_autoload_data(load);
if (!ele) return 0;
/* for autoloading thread, keep the defined value to autoloading storage */
if (ele->state && (ele->state->thread == rb_thread_current())) {