зеркало из https://github.com/github/ruby.git
Cache `Process.pid`
[Feature #19443] It's not uncommon for database client and similar network libraries to protect themselves from Process.fork by regularly checking Process.pid 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:
Родитель
70ea58bd5b
Коммит
1db8951d3a
24
process.c
24
process.c
|
@ -359,6 +359,8 @@ static ID id_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC;
|
||||||
#endif
|
#endif
|
||||||
static ID id_hertz;
|
static ID id_hertz;
|
||||||
|
|
||||||
|
static VALUE cached_pid = Qnil;
|
||||||
|
|
||||||
/* execv and execl are async-signal-safe since SUSv4 (POSIX.1-2008, XPG7) */
|
/* execv and execl are async-signal-safe since SUSv4 (POSIX.1-2008, XPG7) */
|
||||||
#if defined(__sun) && !defined(_XPG7) /* Solaris 10, 9, ... */
|
#if defined(__sun) && !defined(_XPG7) /* Solaris 10, 9, ... */
|
||||||
#define execv(path, argv) (rb_async_bug_errno("unreachable: async-signal-unsafe execv() is called", 0))
|
#define execv(path, argv) (rb_async_bug_errno("unreachable: async-signal-unsafe execv() is called", 0))
|
||||||
|
@ -497,7 +499,23 @@ parent_redirect_close(int fd)
|
||||||
static VALUE
|
static VALUE
|
||||||
get_pid(void)
|
get_pid(void)
|
||||||
{
|
{
|
||||||
return PIDT2NUM(getpid());
|
if (UNLIKELY(NIL_P(cached_pid))) {
|
||||||
|
cached_pid = PIDT2NUM(getpid());
|
||||||
|
}
|
||||||
|
return cached_pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clear_pid_cache(void)
|
||||||
|
{
|
||||||
|
cached_pid = Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
rb_process_atfork(void)
|
||||||
|
{
|
||||||
|
clear_pid_cache();
|
||||||
|
rb_thread_atfork(); /* calls mjit_resume() */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4059,7 +4077,7 @@ rb_fork_ruby2(struct rb_process_status *status)
|
||||||
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_thread_atfork();
|
if (pid == 0) rb_process_atfork();
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6832,7 +6850,7 @@ rb_daemon(int nochdir, int noclose)
|
||||||
before_fork_ruby();
|
before_fork_ruby();
|
||||||
err = daemon(nochdir, noclose);
|
err = daemon(nochdir, noclose);
|
||||||
after_fork_ruby();
|
after_fork_ruby();
|
||||||
rb_thread_atfork();
|
rb_process_atfork();
|
||||||
#else
|
#else
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
|
|
|
@ -2586,6 +2586,26 @@ EOS
|
||||||
end
|
end
|
||||||
end if Process.respond_to?(:_fork)
|
end if Process.respond_to?(:_fork)
|
||||||
|
|
||||||
|
def test__fork_pid_cache
|
||||||
|
parent_pid = Process.pid
|
||||||
|
r, w = IO.pipe
|
||||||
|
pid = Process._fork
|
||||||
|
if pid == 0
|
||||||
|
begin
|
||||||
|
r.close
|
||||||
|
w << "ok: #{Process.pid}"
|
||||||
|
w.close
|
||||||
|
ensure
|
||||||
|
exit!
|
||||||
|
end
|
||||||
|
else
|
||||||
|
w.close
|
||||||
|
assert_equal("ok: #{pid}", r.read)
|
||||||
|
r.close
|
||||||
|
Process.waitpid(pid)
|
||||||
|
end
|
||||||
|
end if Process.respond_to?(:_fork)
|
||||||
|
|
||||||
def test__fork_hook
|
def test__fork_hook
|
||||||
%w(fork Process.fork).each do |method|
|
%w(fork Process.fork).each do |method|
|
||||||
feature17795 = '[ruby-core:103400] [Feature #17795]'
|
feature17795 = '[ruby-core:103400] [Feature #17795]'
|
||||||
|
|
Загрузка…
Ссылка в новой задаче