зеркало из https://github.com/github/ruby.git
thread_pthread.c: Use a `fork_gen` to protect against fork instead of getpid()
[Feature #19443] Until recently most libc would cache `getpid()` so this was a cheap check to make. However as of glibc version 2.25 the PID cache is removed and calls to getpid() always invoke the actual system call which significantly degrades the performance of existing applications. The reason glibc removed the cache is that some libraries were bypassing fork(2) by issuing system calls themselves, causing stale cache issues. That isn't a concern for Ruby as bypassing MRI's primitive for forking would render the VM unusable, so we can safely cache the PID.
This commit is contained in:
Родитель
a9146bd81e
Коммит
3563e1383f
19
process.c
19
process.c
|
@ -511,13 +511,6 @@ clear_pid_cache(void)
|
||||||
cached_pid = Qnil;
|
cached_pid = Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
|
||||||
rb_process_atfork(void)
|
|
||||||
{
|
|
||||||
clear_pid_cache();
|
|
||||||
rb_thread_atfork(); /* calls mjit_resume() */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* Process.pid -> integer
|
* Process.pid -> integer
|
||||||
|
@ -1564,9 +1557,13 @@ before_fork_ruby(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
after_fork_ruby(void)
|
after_fork_ruby(rb_pid_t pid)
|
||||||
{
|
{
|
||||||
rb_threadptr_pending_interrupt_clear(GET_THREAD());
|
rb_threadptr_pending_interrupt_clear(GET_THREAD());
|
||||||
|
if (pid == 0) {
|
||||||
|
clear_pid_cache();
|
||||||
|
rb_thread_atfork();
|
||||||
|
}
|
||||||
after_exec();
|
after_exec();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -4073,11 +4070,10 @@ rb_fork_ruby2(struct rb_process_status *status)
|
||||||
status->pid = pid;
|
status->pid = pid;
|
||||||
status->error = err;
|
status->error = err;
|
||||||
}
|
}
|
||||||
after_fork_ruby();
|
after_fork_ruby(pid);
|
||||||
disable_child_handler_fork_parent(&old); /* yes, bad name */
|
disable_child_handler_fork_parent(&old); /* yes, bad name */
|
||||||
|
|
||||||
if (pid >= 0) { /* fork succeed */
|
if (pid >= 0) { /* fork succeed */
|
||||||
if (pid == 0) rb_process_atfork();
|
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6849,8 +6845,7 @@ rb_daemon(int nochdir, int noclose)
|
||||||
#ifdef HAVE_DAEMON
|
#ifdef HAVE_DAEMON
|
||||||
before_fork_ruby();
|
before_fork_ruby();
|
||||||
err = daemon(nochdir, noclose);
|
err = daemon(nochdir, noclose);
|
||||||
after_fork_ruby();
|
after_fork_ruby(0);
|
||||||
rb_process_atfork();
|
|
||||||
#else
|
#else
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,8 @@ struct rb_internal_thread_event_hook {
|
||||||
static rb_internal_thread_event_hook_t *rb_internal_thread_event_hooks = NULL;
|
static rb_internal_thread_event_hook_t *rb_internal_thread_event_hooks = NULL;
|
||||||
static pthread_rwlock_t rb_internal_thread_event_hooks_rw_lock = PTHREAD_RWLOCK_INITIALIZER;
|
static pthread_rwlock_t rb_internal_thread_event_hooks_rw_lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||||
|
|
||||||
|
static rb_serial_t current_fork_gen = 1; /* We can't use GET_VM()->fork_gen */
|
||||||
|
|
||||||
#define RB_INTERNAL_THREAD_HOOK(event) if (rb_internal_thread_event_hooks) { rb_thread_execute_hooks(event); }
|
#define RB_INTERNAL_THREAD_HOOK(event) if (rb_internal_thread_event_hooks) { rb_thread_execute_hooks(event); }
|
||||||
|
|
||||||
rb_internal_thread_event_hook_t *
|
rb_internal_thread_event_hook_t *
|
||||||
|
@ -203,7 +205,7 @@ enum rtimer_state {
|
||||||
static const struct itimerspec zero;
|
static const struct itimerspec zero;
|
||||||
static struct {
|
static struct {
|
||||||
rb_atomic_t state_; /* rtimer_state */
|
rb_atomic_t state_; /* rtimer_state */
|
||||||
rb_pid_t owner;
|
rb_serial_t fork_gen;
|
||||||
timer_t timerid;
|
timer_t timerid;
|
||||||
} timer_posix = {
|
} timer_posix = {
|
||||||
/* .state = */ RTIMER_DEAD,
|
/* .state = */ RTIMER_DEAD,
|
||||||
|
@ -253,7 +255,7 @@ static void *timer_pthread_fn(void *);
|
||||||
static struct {
|
static struct {
|
||||||
int low[2];
|
int low[2];
|
||||||
rb_atomic_t armed; /* boolean */
|
rb_atomic_t armed; /* boolean */
|
||||||
rb_pid_t owner;
|
rb_serial_t fork_gen;
|
||||||
pthread_t thid;
|
pthread_t thid;
|
||||||
} timer_pthread = {
|
} timer_pthread = {
|
||||||
{ -1, -1 },
|
{ -1, -1 },
|
||||||
|
@ -268,7 +270,7 @@ static void threadptr_trap_interrupt(rb_thread_t *);
|
||||||
static void ubf_wakeup_all_threads(void);
|
static void ubf_wakeup_all_threads(void);
|
||||||
static int ubf_threads_empty(void);
|
static int ubf_threads_empty(void);
|
||||||
|
|
||||||
#define TIMER_THREAD_CREATED_P() (signal_self_pipe.owner_process == getpid())
|
#define TIMER_THREAD_CREATED_P() (signal_self_pipe.fork_gen == current_fork_gen)
|
||||||
|
|
||||||
/* for testing, and in case we come across a platform w/o pipes: */
|
/* for testing, and in case we come across a platform w/o pipes: */
|
||||||
#define BUSY_WAIT_SIGNALS (0)
|
#define BUSY_WAIT_SIGNALS (0)
|
||||||
|
@ -533,6 +535,7 @@ static void thread_cache_reset(void);
|
||||||
static void
|
static void
|
||||||
thread_sched_atfork(struct rb_thread_sched *sched)
|
thread_sched_atfork(struct rb_thread_sched *sched)
|
||||||
{
|
{
|
||||||
|
current_fork_gen++;
|
||||||
thread_cache_reset();
|
thread_cache_reset();
|
||||||
rb_thread_sched_init(sched);
|
rb_thread_sched_init(sched);
|
||||||
thread_sched_to_running(sched, GET_THREAD());
|
thread_sched_to_running(sched, GET_THREAD());
|
||||||
|
@ -1560,7 +1563,7 @@ static struct {
|
||||||
int ub_main[2]; /* unblock main thread from native_ppoll_sleep */
|
int ub_main[2]; /* unblock main thread from native_ppoll_sleep */
|
||||||
|
|
||||||
/* volatile for signal handler use: */
|
/* volatile for signal handler use: */
|
||||||
volatile rb_pid_t owner_process;
|
volatile rb_serial_t fork_gen;
|
||||||
} signal_self_pipe = {
|
} signal_self_pipe = {
|
||||||
{-1, -1},
|
{-1, -1},
|
||||||
{-1, -1},
|
{-1, -1},
|
||||||
|
@ -1605,10 +1608,10 @@ rb_thread_wakeup_timer_thread_fd(int fd)
|
||||||
* process could not react to the original signal in time.
|
* process could not react to the original signal in time.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ubf_timer_arm(rb_pid_t current) /* async signal safe */
|
ubf_timer_arm(rb_serial_t fork_gen) /* async signal safe */
|
||||||
{
|
{
|
||||||
#if UBF_TIMER == UBF_TIMER_POSIX
|
#if UBF_TIMER == UBF_TIMER_POSIX
|
||||||
if ((!current || timer_posix.owner == current) &&
|
if ((!fork_gen || timer_posix.fork_gen == fork_gen) &&
|
||||||
timer_state_cas(RTIMER_DISARM, RTIMER_ARMING) == RTIMER_DISARM) {
|
timer_state_cas(RTIMER_DISARM, RTIMER_ARMING) == RTIMER_DISARM) {
|
||||||
struct itimerspec it;
|
struct itimerspec it;
|
||||||
|
|
||||||
|
@ -1642,7 +1645,7 @@ ubf_timer_arm(rb_pid_t current) /* async signal safe */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elif UBF_TIMER == UBF_TIMER_PTHREAD
|
#elif UBF_TIMER == UBF_TIMER_PTHREAD
|
||||||
if (!current || current == timer_pthread.owner) {
|
if (!fork_gen || fork_gen == timer_pthread.fork_gen) {
|
||||||
if (ATOMIC_EXCHANGE(timer_pthread.armed, 1) == 0)
|
if (ATOMIC_EXCHANGE(timer_pthread.armed, 1) == 0)
|
||||||
rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
|
rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
|
||||||
}
|
}
|
||||||
|
@ -1652,8 +1655,6 @@ ubf_timer_arm(rb_pid_t current) /* async signal safe */
|
||||||
void
|
void
|
||||||
rb_thread_wakeup_timer_thread(int sig)
|
rb_thread_wakeup_timer_thread(int sig)
|
||||||
{
|
{
|
||||||
rb_pid_t current;
|
|
||||||
|
|
||||||
/* non-sighandler path */
|
/* non-sighandler path */
|
||||||
if (sig <= 0) {
|
if (sig <= 0) {
|
||||||
rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
|
rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
|
||||||
|
@ -1664,8 +1665,7 @@ rb_thread_wakeup_timer_thread(int sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* must be safe inside sighandler, so no mutex */
|
/* must be safe inside sighandler, so no mutex */
|
||||||
current = getpid();
|
if (signal_self_pipe.fork_gen == current_fork_gen) {
|
||||||
if (signal_self_pipe.owner_process == current) {
|
|
||||||
rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
|
rb_thread_wakeup_timer_thread_fd(signal_self_pipe.normal[1]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1690,7 +1690,7 @@ rb_thread_wakeup_timer_thread(int sig)
|
||||||
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
RUBY_VM_SET_TRAP_INTERRUPT(ec);
|
RUBY_VM_SET_TRAP_INTERRUPT(ec);
|
||||||
ubf_timer_arm(current);
|
ubf_timer_arm(current_fork_gen);
|
||||||
|
|
||||||
/* some ubfs can interrupt single-threaded process directly */
|
/* some ubfs can interrupt single-threaded process directly */
|
||||||
if (vm->ubf_async_safe && mth->unblock.func) {
|
if (vm->ubf_async_safe && mth->unblock.func) {
|
||||||
|
@ -1904,11 +1904,11 @@ ubf_timer_invalidate(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ubf_timer_pthread_create(rb_pid_t current)
|
ubf_timer_pthread_create(rb_serial_t fork_gen)
|
||||||
{
|
{
|
||||||
#if UBF_TIMER == UBF_TIMER_PTHREAD
|
#if UBF_TIMER == UBF_TIMER_PTHREAD
|
||||||
int err;
|
int err;
|
||||||
if (timer_pthread.owner == current)
|
if (timer_pthread.fork_gen == fork_gen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (setup_communication_pipe_internal(timer_pthread.low) < 0)
|
if (setup_communication_pipe_internal(timer_pthread.low) < 0)
|
||||||
|
@ -1916,7 +1916,7 @@ ubf_timer_pthread_create(rb_pid_t current)
|
||||||
|
|
||||||
err = pthread_create(&timer_pthread.thid, 0, timer_pthread_fn, GET_VM());
|
err = pthread_create(&timer_pthread.thid, 0, timer_pthread_fn, GET_VM());
|
||||||
if (!err)
|
if (!err)
|
||||||
timer_pthread.owner = current;
|
timer_pthread.fork_gen = fork_gen;
|
||||||
else
|
else
|
||||||
rb_warn("pthread_create failed for timer: %s, signals racy",
|
rb_warn("pthread_create failed for timer: %s, signals racy",
|
||||||
strerror(err));
|
strerror(err));
|
||||||
|
@ -1924,7 +1924,7 @@ ubf_timer_pthread_create(rb_pid_t current)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ubf_timer_create(rb_pid_t current)
|
ubf_timer_create(rb_serial_t fork_gen)
|
||||||
{
|
{
|
||||||
#if UBF_TIMER == UBF_TIMER_POSIX
|
#if UBF_TIMER == UBF_TIMER_POSIX
|
||||||
# if defined(__sun)
|
# if defined(__sun)
|
||||||
|
@ -1945,24 +1945,23 @@ ubf_timer_create(rb_pid_t current)
|
||||||
if (prev != RTIMER_DEAD) {
|
if (prev != RTIMER_DEAD) {
|
||||||
rb_bug("timer_posix was not dead: %u\n", (unsigned)prev);
|
rb_bug("timer_posix was not dead: %u\n", (unsigned)prev);
|
||||||
}
|
}
|
||||||
timer_posix.owner = current;
|
timer_posix.fork_gen = fork_gen;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_warn("timer_create failed: %s, signals racy", strerror(errno));
|
rb_warn("timer_create failed: %s, signals racy", strerror(errno));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (UBF_TIMER == UBF_TIMER_PTHREAD)
|
if (UBF_TIMER == UBF_TIMER_PTHREAD)
|
||||||
ubf_timer_pthread_create(current);
|
ubf_timer_pthread_create(fork_gen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rb_thread_create_timer_thread(void)
|
rb_thread_create_timer_thread(void)
|
||||||
{
|
{
|
||||||
/* we only create the pipe, and lazy-spawn */
|
/* we only create the pipe, and lazy-spawn */
|
||||||
rb_pid_t current = getpid();
|
rb_serial_t fork_gen = signal_self_pipe.fork_gen;
|
||||||
rb_pid_t owner = signal_self_pipe.owner_process;
|
|
||||||
|
|
||||||
if (owner && owner != current) {
|
if (fork_gen && fork_gen != current_fork_gen) {
|
||||||
CLOSE_INVALIDATE_PAIR(signal_self_pipe.normal);
|
CLOSE_INVALIDATE_PAIR(signal_self_pipe.normal);
|
||||||
CLOSE_INVALIDATE_PAIR(signal_self_pipe.ub_main);
|
CLOSE_INVALIDATE_PAIR(signal_self_pipe.ub_main);
|
||||||
ubf_timer_invalidate();
|
ubf_timer_invalidate();
|
||||||
|
@ -1971,11 +1970,11 @@ rb_thread_create_timer_thread(void)
|
||||||
if (setup_communication_pipe_internal(signal_self_pipe.normal) < 0) return;
|
if (setup_communication_pipe_internal(signal_self_pipe.normal) < 0) return;
|
||||||
if (setup_communication_pipe_internal(signal_self_pipe.ub_main) < 0) return;
|
if (setup_communication_pipe_internal(signal_self_pipe.ub_main) < 0) return;
|
||||||
|
|
||||||
ubf_timer_create(current);
|
ubf_timer_create(current_fork_gen);
|
||||||
if (owner != current) {
|
if (fork_gen != current_fork_gen) {
|
||||||
/* validate pipe on this process */
|
/* validate pipe on this process */
|
||||||
sigwait_th = THREAD_INVALID;
|
sigwait_th = THREAD_INVALID;
|
||||||
signal_self_pipe.owner_process = current;
|
signal_self_pipe.fork_gen = current_fork_gen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1985,7 +1984,7 @@ ubf_timer_disarm(void)
|
||||||
#if UBF_TIMER == UBF_TIMER_POSIX
|
#if UBF_TIMER == UBF_TIMER_POSIX
|
||||||
rb_atomic_t prev;
|
rb_atomic_t prev;
|
||||||
|
|
||||||
if (timer_posix.owner && timer_posix.owner != getpid()) return;
|
if (timer_posix.fork_gen && timer_posix.fork_gen != current_fork_gen) return;
|
||||||
prev = timer_state_cas(RTIMER_ARMED, RTIMER_DISARM);
|
prev = timer_state_cas(RTIMER_ARMED, RTIMER_DISARM);
|
||||||
switch (prev) {
|
switch (prev) {
|
||||||
case RTIMER_DISARM: return; /* likely */
|
case RTIMER_DISARM: return; /* likely */
|
||||||
|
@ -2018,7 +2017,7 @@ static void
|
||||||
ubf_timer_destroy(void)
|
ubf_timer_destroy(void)
|
||||||
{
|
{
|
||||||
#if UBF_TIMER == UBF_TIMER_POSIX
|
#if UBF_TIMER == UBF_TIMER_POSIX
|
||||||
if (timer_posix.owner == getpid()) {
|
if (timer_posix.fork_gen == current_fork_gen) {
|
||||||
rb_atomic_t expect = RTIMER_DISARM;
|
rb_atomic_t expect = RTIMER_DISARM;
|
||||||
size_t i, max = 10000000;
|
size_t i, max = 10000000;
|
||||||
|
|
||||||
|
@ -2055,7 +2054,7 @@ done:
|
||||||
#elif UBF_TIMER == UBF_TIMER_PTHREAD
|
#elif UBF_TIMER == UBF_TIMER_PTHREAD
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
timer_pthread.owner = 0;
|
timer_pthread.fork_gen = 0;
|
||||||
ubf_timer_disarm();
|
ubf_timer_disarm();
|
||||||
rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
|
rb_thread_wakeup_timer_thread_fd(timer_pthread.low[1]);
|
||||||
err = pthread_join(timer_pthread.thid, 0);
|
err = pthread_join(timer_pthread.thid, 0);
|
||||||
|
@ -2136,15 +2135,15 @@ rb_reserved_fd_p(int fd)
|
||||||
|
|
||||||
#if UBF_TIMER == UBF_TIMER_PTHREAD
|
#if UBF_TIMER == UBF_TIMER_PTHREAD
|
||||||
if (fd == timer_pthread.low[0] || fd == timer_pthread.low[1])
|
if (fd == timer_pthread.low[0] || fd == timer_pthread.low[1])
|
||||||
goto check_pid;
|
goto check_fork_gen;
|
||||||
#endif
|
#endif
|
||||||
if (fd == signal_self_pipe.normal[0] || fd == signal_self_pipe.normal[1])
|
if (fd == signal_self_pipe.normal[0] || fd == signal_self_pipe.normal[1])
|
||||||
goto check_pid;
|
goto check_fork_gen;
|
||||||
if (fd == signal_self_pipe.ub_main[0] || fd == signal_self_pipe.ub_main[1])
|
if (fd == signal_self_pipe.ub_main[0] || fd == signal_self_pipe.ub_main[1])
|
||||||
goto check_pid;
|
goto check_fork_gen;
|
||||||
return 0;
|
return 0;
|
||||||
check_pid:
|
check_fork_gen:
|
||||||
if (signal_self_pipe.owner_process == getpid()) /* async-signal-safe */
|
if (signal_self_pipe.fork_gen == current_fork_gen) /* async-signal-safe */
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2159,7 +2158,7 @@ int
|
||||||
rb_sigwait_fd_get(const rb_thread_t *th)
|
rb_sigwait_fd_get(const rb_thread_t *th)
|
||||||
{
|
{
|
||||||
if (signal_self_pipe.normal[0] >= 0) {
|
if (signal_self_pipe.normal[0] >= 0) {
|
||||||
VM_ASSERT(signal_self_pipe.owner_process == getpid());
|
VM_ASSERT(signal_self_pipe.fork_gen == current_fork_gen);
|
||||||
/*
|
/*
|
||||||
* no need to keep firing the timer if any thread is sleeping
|
* no need to keep firing the timer if any thread is sleeping
|
||||||
* on the signal self-pipe
|
* on the signal self-pipe
|
||||||
|
|
Загрузка…
Ссылка в новой задаче