aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--test/ruby/test_thread.rb21
-rw-r--r--thread_sync.c11
2 files changed, 32 insertions, 0 deletions
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index 2b120b4a17..78d6d82e23 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -1239,6 +1239,27 @@ q.pop
end
end if Process.respond_to?(:fork)
+ def test_fork_while_parent_locked
+ skip 'needs fork' unless Process.respond_to?(:fork)
+ m = Thread::Mutex.new
+ failures = 0
+ run = true
+ thrs = 50.times.map do
+ Thread.new do
+ while run
+ pid = fork { m.synchronize {} }
+ m.synchronize {}
+ _, st = Process.waitpid2(pid)
+ m.synchronize { failures += 1 } unless st.success?
+ end
+ end
+ end
+ sleep 0.5
+ run = false
+ thrs.each(&:join)
+ assert_equal 0, failures, '[ruby-core:90312] [Bug #15383]'
+ end
+
def test_subclass_no_initialize
t = Module.new do
break eval("class C\u{30b9 30ec 30c3 30c9} < Thread; self; end")
diff --git a/thread_sync.c b/thread_sync.c
index b79db1fee3..e478a96b88 100644
--- a/thread_sync.c
+++ b/thread_sync.c
@@ -45,6 +45,7 @@ typedef struct rb_mutex_struct {
rb_thread_t *th;
struct rb_mutex_struct *next_mutex;
struct list_head waitq; /* protected by GVL */
+ rb_serial_t fork_gen;
} rb_mutex_t;
#if defined(HAVE_WORKING_FORK)
@@ -121,8 +122,18 @@ static rb_mutex_t *
mutex_ptr(VALUE obj)
{
rb_mutex_t *mutex;
+ rb_serial_t fork_gen = GET_VM()->fork_gen;
TypedData_Get_Struct(obj, rb_mutex_t, &mutex_data_type, mutex);
+
+ if (mutex->fork_gen != fork_gen) {
+ /* forked children can't reach into parent thread stacks */
+ mutex->fork_gen = fork_gen;
+ list_head_init(&mutex->waitq);
+ mutex->next_mutex = 0;
+ mutex->th = 0;
+ }
+
return mutex;
}