thread_sync.c (mutex_ptr): handle mutexes held by parent threads in children

Mutexes may be held by threads which only exist in the parent
process, so their waitqueues may be populated with references
to other dead threads.  We must reset them at fork.

I am a moron for introducing this bug :<

[ruby-core:90312] [Bug #15383]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66230 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2018-12-05 18:58:45 +00:00
parent e941daa6dd
commit 818f1c65ab
2 changed files with 32 additions and 0 deletions

View File

@ -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")

View File

@ -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;
}