diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index c7e4835359eb331736137f60a5da18fd0ade027a..6145c2c79d2b2005966e8181b05c8acb02170df0 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 3530a5ca32d7d8615f51b28ef808e3dadecc46b7..6ec43980308ff813fcc09ea149205e22f3f4e904 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/expected/unistd/fork.stdout b/tests/expected/unistd/fork.stdout new file mode 100644 index 0000000000000000000000000000000000000000..98f331c9de0fe27f463332645cd91657cb3862e0 --- /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 0000000000000000000000000000000000000000..eb9b84ac2da1a61a5fe3bcb86b13fb1e42330c28 --- /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); +}