From 660d0931cd9d87869ad862d735a002796d5b9fa3 Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Sat, 6 Apr 2019 17:51:20 +0800 Subject: [PATCH] Add Pthread test --- src/libos/src/lib.rs | 1 + src/libos/src/process/exit.rs | 9 +++- src/libos/src/process/thread.rs | 22 ++++++-- src/libos/src/syscall/mod.rs | 36 ++++++++++--- test/Makefile | 2 +- test/clone/main.c | 86 -------------------------------- test/{clone => pthread}/Makefile | 0 test/pthread/main.c | 41 +++++++++++++++ 8 files changed, 97 insertions(+), 100 deletions(-) delete mode 100644 test/clone/main.c rename test/{clone => pthread}/Makefile (100%) create mode 100644 test/pthread/main.c diff --git a/src/libos/src/lib.rs b/src/libos/src/lib.rs index e903df21..8b3a612d 100644 --- a/src/libos/src/lib.rs +++ b/src/libos/src/lib.rs @@ -6,6 +6,7 @@ #![feature(alloc)] #![feature(allocator_api)] #![feature(range_contains)] +#![feature(core_intrinsics)] #[macro_use] extern crate alloc; diff --git a/src/libos/src/process/exit.rs b/src/libos/src/process/exit.rs index b870e872..a7da5b33 100644 --- a/src/libos/src/process/exit.rs +++ b/src/libos/src/process/exit.rs @@ -1,4 +1,5 @@ use super::*; +use std::intrinsics::atomic_store; // TODO: make sure Processes are released eventually @@ -26,7 +27,13 @@ pub fn do_exit(exit_status: i32) { } current.children.clear(); - // Notify parent if necessary + // Notify another process, if any, that waits on ctid (see set_tid_address) + if let Some(ctid) = current.clear_child_tid { + unsafe { atomic_store(ctid, 0); } + futex_wake(ctid as *const i32, 1); + } + + // Notify the parent process if necessary let parent_ref = current.get_parent().clone(); let (mut parent, current) = { // Always lock parent before its child diff --git a/src/libos/src/process/thread.rs b/src/libos/src/process/thread.rs index 52ba10a5..8d8d925a 100644 --- a/src/libos/src/process/thread.rs +++ b/src/libos/src/process/thread.rs @@ -36,12 +36,13 @@ bitflags! { pub fn do_clone( flags: CloneFlags, stack_addr: usize, - ptid: Option<*mut i32>, - ctid: Option<*mut i32>, - new_tls: usize + ptid: Option<*mut pid_t>, + ctid: Option<*mut pid_t>, + new_tls: Option, ) -> Result { info!("clone: flags: {:?}, stack_addr: {:?}, ptid: {:?}, ctid: {:?}, new_tls: {:?}", flags, stack_addr, ptid, ctid, new_tls); + // TODO: return error for unsupported flags let current_ref = get_current(); let current = current_ref.lock().unwrap(); @@ -54,6 +55,11 @@ pub fn do_clone( Process::new(cwd, task, vm_ref, files_ref)? }; + if let Some(ctid) = ctid { + let mut new_thread = new_thread_ref.lock().unwrap(); + new_thread.clear_child_tid = Some(ctid); + } + // TODO: always get parent lock first to avoid deadlock { let parent_ref = current.parent.as_ref().unwrap(); @@ -64,11 +70,16 @@ pub fn do_clone( } process_table::put(new_thread_pid, new_thread_ref.clone()); + + if let Some(ptid) = ptid { + unsafe { *ptid = new_thread_pid; } + } + task::enqueue_task(new_thread_ref); Ok(new_thread_pid) } -fn new_thread_task(user_stack: usize, new_tls: usize) -> Result { +fn new_thread_task(user_stack: usize, new_tls: Option) -> Result { // The calling convention of Occlum clone syscall requires the user to // restore the entry point of the new thread at the top of the user stack. let user_entry = unsafe { @@ -78,7 +89,8 @@ fn new_thread_task(user_stack: usize, new_tls: usize) -> Result { Ok(Task { user_stack_addr: user_stack, user_entry_addr: user_entry, - user_fsbase_addr: new_tls, + // TODO: use 0 as the default value is not safe + user_fsbase_addr: new_tls.unwrap_or(0), ..Default::default() }) } diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 958b6f71..a0b392d8 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -87,8 +87,8 @@ pub extern "C" fn dispatch_syscall( SYS_CLONE => do_clone( arg0 as u32, arg1 as usize, - arg2 as *mut i32, - arg3 as *mut i32, + arg2 as *mut pid_t, + arg3 as *mut pid_t, arg4 as usize, ), SYS_FUTEX => do_futex( @@ -116,6 +116,11 @@ pub extern "C" fn dispatch_syscall( arg3 as i32, arg4 as usize, ), + SYS_MPROTECT => do_mprotect( + arg0 as usize, + arg1 as usize, + arg2 as u32, + ), SYS_BRK => do_brk(arg0 as usize), SYS_PIPE => do_pipe2(arg0 as *mut i32, 0), @@ -217,14 +222,14 @@ fn do_spawn( pub fn do_clone( flags: u32, stack_addr: usize, - ptid: *mut i32, - ctid: *mut i32, + ptid: *mut pid_t, + ctid: *mut pid_t, new_tls: usize, ) -> Result { let flags = CloneFlags::from_bits_truncate(flags); check_mut_ptr(stack_addr as *mut u64)?; let ptid = { - if ptid != ptr::null_mut() { + if flags.contains(CloneFlags::CLONE_PARENT_SETTID) { check_mut_ptr(ptid)?; Some(ptid) } @@ -233,7 +238,7 @@ pub fn do_clone( } }; let ctid = { - if ctid != ptr::null_mut() { + if flags.contains(CloneFlags::CLONE_CHILD_CLEARTID) { check_mut_ptr(ctid)?; Some(ctid) } @@ -241,7 +246,15 @@ pub fn do_clone( None } }; - check_mut_ptr(new_tls as *mut u64)?; + let new_tls = { + if flags.contains(CloneFlags::CLONE_SETTLS) { + check_mut_ptr(new_tls as *mut usize)?; + Some(new_tls) + } + else { + None + } + }; let child_pid = process::do_clone(flags, stack_addr, ptid, ctid, new_tls)?; @@ -494,6 +507,15 @@ fn do_mremap( Ok(ret_addr as isize) } +fn do_mprotect( + addr: usize, + len: usize, + prot: u32, +) -> Result { + // TODO: implement it + Ok(0) +} + fn do_brk(new_brk_addr: usize) -> Result { let ret_brk_addr = vm::do_brk(new_brk_addr)?; Ok(ret_brk_addr as isize) diff --git a/test/Makefile b/test/Makefile index 25cc2ab0..8d698b00 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ PROJECT_DIR := $(realpath $(CUR_DIR)/../) # Dependencies: need to be compiled but not to run by any Makefile target TEST_DEPS := dev_null # Tests: need to be compiled and run by test-% target -TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link clone tls +TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link tls pthread # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput diff --git a/test/clone/main.c b/test/clone/main.c deleted file mode 100644 index 55452497..00000000 --- a/test/clone/main.c +++ /dev/null @@ -1,86 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include - -/* - * Helper functions - */ - -static inline int a_load(volatile int* x) { - return __atomic_load_n((int*)x, __ATOMIC_SEQ_CST); -} - -static inline int a_add_fetch(volatile int* x, int a) { - return __atomic_add_fetch((int*)x, a, __ATOMIC_SEQ_CST); -} - -/* - * Futex wrapper - */ - -#define FUTEX_NUM 202 - -#define FUTEX_WAIT 0 -#define FUTEX_WAKE 1 - -// Libc does not provide a wrapper for futex, so we do it our own -static int futex(volatile int *futex_addr, int futex_op, int val) { - return (int) syscall(FUTEX_NUM, futex_addr, futex_op, val); -} - - -/* - * Child threads - */ - -#define NTHREADS 4 -#define STACK_SIZE (8 * 1024) - -volatile int num_exit_threads = 0; - -static int thread_func(void* arg) { - int* tid = arg; - //printf("tid = %d\n", *tid); - // Wake up the main thread if all child threads exit - if (a_add_fetch(&num_exit_threads, 1) == NTHREADS) { - futex(&num_exit_threads, FUTEX_WAKE, 1); - } - return 0; -} - - -int main(int argc, const char* argv[]) { - unsigned int clone_flags = CLONE_VM | CLONE_FS | CLONE_FILES | - CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | CLONE_DETACHED; - - printf("Creating %d threads...", NTHREADS); - int thread_ids[NTHREADS]; - for (int tid = 0; tid < NTHREADS; tid++) { - void* thread_stack = malloc(STACK_SIZE); - if (thread_stack == NULL) { - printf("ERROR: malloc failed for thread %d\n", tid); - return -1; - } - - thread_ids[tid] = tid; - void* thread_arg = &thread_ids[tid]; - if (clone(thread_func, thread_stack, clone_flags, thread_arg) < 0) { - printf("ERROR: clone failed for thread %d\n", tid); - return -1; - } - } - printf("done.\n"); - - printf("Waiting for %d threads to exit...", NTHREADS); - // Wait for all threads to exit - int curr_num_exit_threads; - while ((curr_num_exit_threads = a_load(&num_exit_threads)) != NTHREADS) { - futex(&num_exit_threads, FUTEX_WAIT, curr_num_exit_threads); - } - printf("done.\n"); - - return 0; -} diff --git a/test/clone/Makefile b/test/pthread/Makefile similarity index 100% rename from test/clone/Makefile rename to test/pthread/Makefile diff --git a/test/pthread/main.c b/test/pthread/main.c new file mode 100644 index 00000000..1fec331a --- /dev/null +++ b/test/pthread/main.c @@ -0,0 +1,41 @@ +#include +#include +#include + +/* + * Child threads + */ + +#define NTHREADS 4 +#define STACK_SIZE (8 * 1024) + +static void* thread_func(void* arg) { + int* tid = arg; + printf("tid = %d\n", *tid); + return NULL; +} + +int main(int argc, const char* argv[]) { + pthread_t threads[NTHREADS]; + int thread_data[NTHREADS]; + + printf("Creating %d threads...", NTHREADS); + for (int ti = 0; ti < NTHREADS; ti++) { + thread_data[ti] = ti; + if (pthread_create(&threads[ti], NULL, thread_func, &thread_data[ti]) < 0) { + printf("ERROR: pthread_create failed (ti = %d)\n", ti); + return -1; + } + } + printf("done.\n"); + + printf("Waiting for %d threads to exit...", NTHREADS); + for (int ti = 0; ti < NTHREADS; ti++) { + if (pthread_join(threads[ti], NULL) < 0) { + printf("ERROR: pthread_join failed (ti = %d)\n", ti); + return -1; + } + } + printf("done.\n"); + return 0; +}