mirror of
https://github.com/systemd/systemd.git
synced 2025-01-13 17:18:18 +03:00
process-util: add getpid_cached() as a caching wrapper for getpid()
Let's make getpid() fast again.
This commit is contained in:
parent
f7a2b4213c
commit
5c30a6d2b8
@ -922,6 +922,68 @@ int ioprio_parse_priority(const char *s, int *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The cached PID, possible values:
|
||||
*
|
||||
* == UNSET [0] → cache not initialized yet
|
||||
* == BUSY [-1] → some thread is initializing it at the moment
|
||||
* any other → the cached PID
|
||||
*/
|
||||
|
||||
#define CACHED_PID_UNSET ((pid_t) 0)
|
||||
#define CACHED_PID_BUSY ((pid_t) -1)
|
||||
|
||||
static pid_t cached_pid = CACHED_PID_UNSET;
|
||||
|
||||
static void reset_cached_pid(void) {
|
||||
/* Invoked in the child after a fork(), i.e. at the first moment the PID changed */
|
||||
cached_pid = CACHED_PID_UNSET;
|
||||
}
|
||||
|
||||
/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc
|
||||
* headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against
|
||||
* libpthread, as it is part of glibc anyway. */
|
||||
extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void * __dso_handle);
|
||||
extern void* __dso_handle __attribute__ ((__weak__));
|
||||
|
||||
pid_t getpid_cached(void) {
|
||||
pid_t current_value;
|
||||
|
||||
/* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a
|
||||
* system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally
|
||||
* cached. Starting with 2.24 getpid() started to become prohibitively expensive when used for detecting when
|
||||
* objects were used across fork()s. With this caching the old behaviour is somewhat restored.
|
||||
*
|
||||
* https://bugzilla.redhat.com/show_bug.cgi?id=1443976
|
||||
* https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=1d2bc2eae969543b89850e35e532f3144122d80a
|
||||
*/
|
||||
|
||||
current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY);
|
||||
|
||||
switch (current_value) {
|
||||
|
||||
case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */
|
||||
pid_t new_pid;
|
||||
|
||||
new_pid = getpid();
|
||||
|
||||
if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) {
|
||||
/* OOM? Let's try again later */
|
||||
cached_pid = CACHED_PID_UNSET;
|
||||
return new_pid;
|
||||
}
|
||||
|
||||
cached_pid = new_pid;
|
||||
return new_pid;
|
||||
}
|
||||
|
||||
case CACHED_PID_BUSY: /* Somebody else is currently initializing */
|
||||
return getpid();
|
||||
|
||||
default: /* Properly initialized */
|
||||
return current_value;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const ioprio_class_table[] = {
|
||||
[IOPRIO_CLASS_NONE] = "none",
|
||||
[IOPRIO_CLASS_RT] = "realtime",
|
||||
|
@ -119,3 +119,5 @@ static inline bool ioprio_priority_is_valid(int i) {
|
||||
}
|
||||
|
||||
int ioprio_parse_priority(const char *s, int *ret);
|
||||
|
||||
pid_t getpid_cached(void);
|
||||
|
@ -410,6 +410,61 @@ static void test_rename_process(void) {
|
||||
test_rename_process_one("1234567", 1); /* should always fit */
|
||||
}
|
||||
|
||||
static void test_getpid_cached(void) {
|
||||
siginfo_t si;
|
||||
pid_t a, b, c, d, e, f, child;
|
||||
|
||||
a = raw_getpid();
|
||||
b = getpid_cached();
|
||||
c = getpid();
|
||||
|
||||
assert_se(a == b && a == c);
|
||||
|
||||
child = fork();
|
||||
assert_se(child >= 0);
|
||||
|
||||
if (child == 0) {
|
||||
/* In child */
|
||||
a = raw_getpid();
|
||||
b = getpid_cached();
|
||||
c = getpid();
|
||||
|
||||
assert_se(a == b && a == c);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
d = raw_getpid();
|
||||
e = getpid_cached();
|
||||
f = getpid();
|
||||
|
||||
assert_se(a == d && a == e && a == f);
|
||||
|
||||
assert_se(wait_for_terminate(child, &si) >= 0);
|
||||
assert_se(si.si_status == 0);
|
||||
assert_se(si.si_code == CLD_EXITED);
|
||||
}
|
||||
|
||||
#define MEASURE_ITERATIONS (10000000LLU)
|
||||
|
||||
static void test_getpid_measure(void) {
|
||||
unsigned long long i;
|
||||
usec_t t, q;
|
||||
|
||||
t = now(CLOCK_MONOTONIC);
|
||||
for (i = 0; i < MEASURE_ITERATIONS; i++)
|
||||
(void) getpid();
|
||||
q = now(CLOCK_MONOTONIC) - t;
|
||||
|
||||
log_info(" glibc getpid(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
|
||||
|
||||
t = now(CLOCK_MONOTONIC);
|
||||
for (i = 0; i < MEASURE_ITERATIONS; i++)
|
||||
(void) getpid_cached();
|
||||
q = now(CLOCK_MONOTONIC) - t;
|
||||
|
||||
log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
@ -434,6 +489,8 @@ int main(int argc, char *argv[]) {
|
||||
test_personality();
|
||||
test_get_process_cmdline_harder();
|
||||
test_rename_process();
|
||||
test_getpid_cached();
|
||||
test_getpid_measure();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user