mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Proof of Concept: Allow to prevent fork from happening in known fork unsafe API
[Feature #20590] For better of for worse, fork(2) remain the primary provider of parallelism in Ruby programs. Even though it's frowned uppon in many circles, and a lot of literature will simply state that only async-signal safe APIs are safe to use after `fork()`, in practice most APIs work well as long as you are careful about not forking while another thread is holding a pthread mutex. One of the APIs that is known cause fork safety issues is `getaddrinfo`. If you fork while another thread is inside `getaddrinfo`, a mutex may be left locked in the child, with no way to unlock it. I think we could reduce the impact of these problem by preventing in for the most notorious and common cases, by locking around `fork(2)` and known unsafe APIs with a read-write lock.
This commit is contained in:
parent
2e5680d304
commit
63cbe3f6ac
Notes:
git
2024-09-05 09:44:04 +00:00
6 changed files with 82 additions and 2 deletions
|
@ -3346,6 +3346,52 @@ native_sleep(rb_thread_t *th, rb_hrtime_t *rel)
|
|||
RUBY_DEBUG_LOG("wakeup");
|
||||
}
|
||||
|
||||
// fork read-write lock (only for pthread)
|
||||
static pthread_rwlock_t rb_thread_fork_rw_lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
|
||||
void
|
||||
rb_thread_release_fork_lock(void)
|
||||
{
|
||||
int r;
|
||||
if ((r = pthread_rwlock_unlock(&rb_thread_fork_rw_lock))) {
|
||||
rb_bug_errno("pthread_rwlock_unlock", r);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rb_thread_reset_fork_lock(void)
|
||||
{
|
||||
int r;
|
||||
if ((r = pthread_rwlock_destroy(&rb_thread_fork_rw_lock))) {
|
||||
rb_bug_errno("pthread_rwlock_destroy", r);
|
||||
}
|
||||
|
||||
if ((r = pthread_rwlock_init(&rb_thread_fork_rw_lock, NULL))) {
|
||||
rb_bug_errno("pthread_rwlock_init", r);
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
rb_thread_prevent_fork(void *(*func)(void *), void *data)
|
||||
{
|
||||
int r;
|
||||
if ((r = pthread_rwlock_rdlock(&rb_thread_fork_rw_lock))) {
|
||||
rb_bug_errno("pthread_rwlock_rdlock", r);
|
||||
}
|
||||
void *result = func(data);
|
||||
rb_thread_release_fork_lock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
rb_thread_acquire_fork_lock(void)
|
||||
{
|
||||
int r;
|
||||
if ((r = pthread_rwlock_wrlock(&rb_thread_fork_rw_lock))) {
|
||||
rb_bug_errno("pthread_rwlock_wrlock", r);
|
||||
}
|
||||
}
|
||||
|
||||
// thread internal event hooks (only for pthread)
|
||||
|
||||
struct rb_internal_thread_event_hook {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue