8000 Free rb_native_thread memory at fork · ruby/ruby@0ba2a48 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0ba2a48

Browse files
committed
Free rb_native_thread memory at fork
We never freed any resources of rb_native_thread at fork because it would cause it to hang. This is because it called rb_native_cond_destroy for condition variables. We can't call rb_native_cond_destroy here because according to the specs of pthread_cond_destroy: Attempting to destroy a condition variable upon which other threads are currently blocked results in undefined behavior. Specifically, glibc's pthread_cond_destroy waits on all the other listeners. Since after forking all the threads are dead, the condition variable's listeners will never wake up, so it will hang forever. This commit changes it to only free the memory and none of the condition variables.
1 parent 9520129 commit 0ba2a48

File tree

2 files changed

+23
-8
lines changed

2 files changed

+23
-8
lines changed

thread.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -519,12 +519,8 @@ thread_cleanup_func(void *th_ptr, int atfork)
519519
th->locking_mutex = Qfalse;
520520
thread_cleanup_func_before_exec(th_ptr);
521521

522-
/*
523-
* Unfortunately, we can't release native threading resource at fork
524-
* because libc may have unstable locking state therefore touching
525-
* a threading resource may cause a deadlock.
526-
*/
527522
if (atfork) {
523+
native_thread_destroy_atfork(th->nt);
528524
th->nt = NULL;
529525
return;
530526
}

thread_pthread.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,27 @@ native_thread_assign(struct rb_native_thread *nt, rb_thread_t *th)
18161816
th->nt = nt;
18171817
}
18181818

1819+
static void
1820+
native_thread_destroy_atfork(struct rb_native_thread *nt)
1821+
{
1822+
if (nt) {
1823+
/* We can't call rb_native_cond_destroy here because according to the
1824+
* specs of pthread_cond_destroy:
1825+
*
1826+
* Attempting to destroy a condition variable upon which other threads
1827+
* are currently blocked results in undefined behavior.
1828+
*
1829+
* Specifically, glibc's pthread_cond_destroy waits on all the other
1830+
* listeners. Since after forking all the threads are dead, the condition
1831+
* variable's listeners will never wake up, so it will hang forever.
1832+
*/
1833+
1834+
RB_ALTSTACK_FREE(nt->altstack);
1835+
ruby_xfree(nt->nt_context);
1836+
ruby_xfree(nt);
1837+
}
1838+
}
1839+
18191840
static void
18201841
native_thread_destroy(struct rb_native_thread *nt)
18211842
{
@@ -1826,9 +1847,7 @@ native_thread_destroy(struct rb_native_thread *nt)
18261847
rb_native_cond_destroy(&nt->cond.intr);
18271848
}
18281849

1829-
RB_ALTSTACK_FREE(nt->altstack);
1830-
ruby_xfree(nt->nt_context);
1831-
ruby_xfree(nt);
1850+
native_thread_destroy_atfork(nt);
18321851
}
18331852
}
18341853

0 commit comments

Comments
 (0)
0