From e35f22b3dfe8b2727ccf3fb94430c104ef0753cd Mon Sep 17 00:00:00 2001 From: jD91mZM2 <me@krake.one> Date: Sun, 28 Apr 2019 17:14:45 +0200 Subject: [PATCH] WIP: pthread_atfork WIP mainly because we *should* use thread locals, but #[thread_local] causes segfaults. --- src/header/unistd/mod.rs | 43 +++++++++++++++++++++++++++---- tests/Makefile | 1 + tests/expected/unistd/fork.stderr | 0 tests/expected/unistd/fork.stdout | 3 +++ tests/unistd/fork.c | 29 +++++++++++++++++++++ 5 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 tests/expected/unistd/fork.stderr create mode 100644 tests/expected/unistd/fork.stdout create mode 100644 tests/unistd/fork.c diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index c7e483535..6145c2c79 100644 --- a/src/header/unistd/mod.rs +++ b/src/header/unistd/mod.rs @@ -2,6 +2,7 @@ use core::{mem, ptr, slice}; +use alloc::collections::LinkedList; use c_str::CStr; use header::errno; use header::limits; @@ -10,9 +11,9 @@ use header::sys_ioctl; use header::sys_time; use header::termios; use header::time::timespec; -use platform; use platform::types::*; use platform::{Pal, Sys}; +use platform; pub use self::brk::*; pub use self::getopt::*; @@ -42,6 +43,17 @@ pub const STDIN_FILENO: c_int = 0; pub const STDOUT_FILENO: c_int = 1; pub const STDERR_FILENO: c_int = 2; +#[thread_local] +pub static mut fork_hooks_static: Option<[LinkedList<extern "C" fn()>; 3]> = None; + +unsafe fn init_fork_hooks<'a>() -> &'a mut [LinkedList<extern "C" fn()>; 3] { + // Transmute the lifetime so we can return here. Should be safe as + // long as one does not access the original fork_hooks. + mem::transmute(fork_hooks_static.get_or_insert_with(|| { + [LinkedList::new(), LinkedList::new(), LinkedList::new()] + })) +} + #[no_mangle] pub extern "C" fn _exit(status: c_int) { Sys::exit(status) @@ -214,7 +226,21 @@ pub extern "C" fn fdatasync(fildes: c_int) -> c_int { #[no_mangle] pub extern "C" fn fork() -> pid_t { - Sys::fork() + let fork_hooks = unsafe { init_fork_hooks() }; + for prepare in &fork_hooks[0] { + prepare(); + } + let pid = Sys::fork(); + if pid == 0 { + for child in &fork_hooks[2] { + child(); + } + } else if pid != -1 { + for parent in &fork_hooks[1] { + parent(); + } + } + pid } #[no_mangle] @@ -456,9 +482,16 @@ pub extern "C" fn pthread_atfork( parent: Option<extern "C" fn()>, child: Option<extern "C" fn()>, ) -> c_int { - // TODO: WIP implementation available in "atfork" branch. It's - // segfaulting at the thread-local stuff, both in Unix and Redox. - // unimplemented!(); + let fork_hooks = unsafe { init_fork_hooks() }; + if let Some(prepare) = prepare { + fork_hooks[0].push_back(prepare); + } + if let Some(parent) = parent { + fork_hooks[1].push_back(parent); + } + if let Some(child) = child { + fork_hooks[2].push_back(child); + } 0 } diff --git a/tests/Makefile b/tests/Makefile index 3530a5ca3..6ec439803 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -77,6 +77,7 @@ EXPECT_NAMES=\ unistd/dup \ unistd/exec \ unistd/fchdir \ + unistd/fork \ unistd/fsync \ unistd/ftruncate \ unistd/getopt \ diff --git a/tests/expected/unistd/fork.stderr b/tests/expected/unistd/fork.stderr new file mode 100644 index 000000000..e69de29bb diff --git a/tests/expected/unistd/fork.stdout b/tests/expected/unistd/fork.stdout new file mode 100644 index 000000000..98f331c9d --- /dev/null +++ b/tests/expected/unistd/fork.stdout @@ -0,0 +1,3 @@ +Hello from prepare +Hello from child +Hello from parent diff --git a/tests/unistd/fork.c b/tests/unistd/fork.c new file mode 100644 index 000000000..eb9b84ac2 --- /dev/null +++ b/tests/unistd/fork.c @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "test_helpers.h" + +void prepare() { + puts("Hello from prepare"); +} +void parent() { + // Make sure we print in the right order and also don't exit + // before the fork does. + int us_status = usleep(1000); + ERROR_IF(usleep, us_status, == -1); + UNEXP_IF(usleep, us_status, != 0); + + puts("Hello from parent"); +} +void child() { + puts("Hello from child"); +} + +int main(void) { + int status = pthread_atfork(prepare, parent, child); + ERROR_IF(pthread_atfork, status, == -1); + + int pid = fork(); + ERROR_IF(fork, pid, == -1); +} -- GitLab