summaryrefslogtreecommitdiff
path: root/process.c
diff options
context:
space:
mode:
authorJean Boussier <byroot@ruby-lang.org>2023-02-16 19:36:31 +0100
committerJean Boussier <jean.boussier@gmail.com>2023-03-20 08:21:23 +0000
commit1db8951d3a8be6a756c9d3d3b87231997b301985 (patch)
tree450ed624fb3b8afc145ef2fd3e85258f64b08bf3 /process.c
parent70ea58bd5ba42b26ff39f51079af0efa422ac036 (diff)
downloadruby-1db8951d3a8be6a756c9d3d3b87231997b301985.tar.gz
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.
Diffstat (limited to 'process.c')
-rw-r--r--process.c24
1 files changed, 21 insertions, 3 deletions
diff --git a/process.c b/process.c
index ca09357d17..9f2a7b9337 100644
--- a/process.c
+++ b/process.c
@@ -359,6 +359,8 @@ static ID id_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC;
#endif
static ID id_hertz;
+static VALUE cached_pid = Qnil;
+
/* execv and execl are async-signal-safe since SUSv4 (POSIX.1-2008, XPG7) */
#if defined(__sun) && !defined(_XPG7) /* Solaris 10, 9, ... */
#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
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 */
if (pid >= 0) { /* fork succeed */
- if (pid == 0) rb_thread_atfork();
+ if (pid == 0) rb_process_atfork();
return pid;
}
@@ -6832,7 +6850,7 @@ rb_daemon(int nochdir, int noclose)
before_fork_ruby();
err = daemon(nochdir, noclose);
after_fork_ruby();
- rb_thread_atfork();
+ rb_process_atfork();
#else
int n;