Add Pthread test

This commit is contained in:
Tate, Hongliang Tian 2019-04-06 17:51:20 +08:00 committed by Tate Tian
parent 13e4a898dd
commit 660d0931cd
8 changed files with 97 additions and 100 deletions

@ -6,6 +6,7 @@
#![feature(alloc)] #![feature(alloc)]
#![feature(allocator_api)] #![feature(allocator_api)]
#![feature(range_contains)] #![feature(range_contains)]
#![feature(core_intrinsics)]
#[macro_use] #[macro_use]
extern crate alloc; extern crate alloc;

@ -1,4 +1,5 @@
use super::*; use super::*;
use std::intrinsics::atomic_store;
// TODO: make sure Processes are released eventually // TODO: make sure Processes are released eventually
@ -26,7 +27,13 @@ pub fn do_exit(exit_status: i32) {
} }
current.children.clear(); 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 parent_ref = current.get_parent().clone();
let (mut parent, current) = { let (mut parent, current) = {
// Always lock parent before its child // Always lock parent before its child

@ -36,12 +36,13 @@ bitflags! {
pub fn do_clone( pub fn do_clone(
flags: CloneFlags, flags: CloneFlags,
stack_addr: usize, stack_addr: usize,
ptid: Option<*mut i32>, ptid: Option<*mut pid_t>,
ctid: Option<*mut i32>, ctid: Option<*mut pid_t>,
new_tls: usize new_tls: Option<usize>,
) -> Result<pid_t, Error> { ) -> Result<pid_t, Error> {
info!("clone: flags: {:?}, stack_addr: {:?}, ptid: {:?}, ctid: {:?}, new_tls: {:?}", info!("clone: flags: {:?}, stack_addr: {:?}, ptid: {:?}, ctid: {:?}, new_tls: {:?}",
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_ref = get_current();
let current = current_ref.lock().unwrap(); let current = current_ref.lock().unwrap();
@ -54,6 +55,11 @@ pub fn do_clone(
Process::new(cwd, task, vm_ref, files_ref)? 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 // TODO: always get parent lock first to avoid deadlock
{ {
let parent_ref = current.parent.as_ref().unwrap(); 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()); 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); task::enqueue_task(new_thread_ref);
Ok(new_thread_pid) Ok(new_thread_pid)
} }
fn new_thread_task(user_stack: usize, new_tls: usize) -> Result<Task, Error> { fn new_thread_task(user_stack: usize, new_tls: Option<usize>) -> Result<Task, Error> {
// The calling convention of Occlum clone syscall requires the user to // 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. // restore the entry point of the new thread at the top of the user stack.
let user_entry = unsafe { let user_entry = unsafe {
@ -78,7 +89,8 @@ fn new_thread_task(user_stack: usize, new_tls: usize) -> Result<Task, Error> {
Ok(Task { Ok(Task {
user_stack_addr: user_stack, user_stack_addr: user_stack,
user_entry_addr: user_entry, 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() ..Default::default()
}) })
} }

@ -87,8 +87,8 @@ pub extern "C" fn dispatch_syscall(
SYS_CLONE => do_clone( SYS_CLONE => do_clone(
arg0 as u32, arg0 as u32,
arg1 as usize, arg1 as usize,
arg2 as *mut i32, arg2 as *mut pid_t,
arg3 as *mut i32, arg3 as *mut pid_t,
arg4 as usize, arg4 as usize,
), ),
SYS_FUTEX => do_futex( SYS_FUTEX => do_futex(
@ -116,6 +116,11 @@ pub extern "C" fn dispatch_syscall(
arg3 as i32, arg3 as i32,
arg4 as usize, arg4 as usize,
), ),
SYS_MPROTECT => do_mprotect(
arg0 as usize,
arg1 as usize,
arg2 as u32,
),
SYS_BRK => do_brk(arg0 as usize), SYS_BRK => do_brk(arg0 as usize),
SYS_PIPE => do_pipe2(arg0 as *mut i32, 0), SYS_PIPE => do_pipe2(arg0 as *mut i32, 0),
@ -217,14 +222,14 @@ fn do_spawn(
pub fn do_clone( pub fn do_clone(
flags: u32, flags: u32,
stack_addr: usize, stack_addr: usize,
ptid: *mut i32, ptid: *mut pid_t,
ctid: *mut i32, ctid: *mut pid_t,
new_tls: usize, new_tls: usize,
) -> Result<isize, Error> { ) -> Result<isize, Error> {
let flags = CloneFlags::from_bits_truncate(flags); let flags = CloneFlags::from_bits_truncate(flags);
check_mut_ptr(stack_addr as *mut u64)?; check_mut_ptr(stack_addr as *mut u64)?;
let ptid = { let ptid = {
if ptid != ptr::null_mut() { if flags.contains(CloneFlags::CLONE_PARENT_SETTID) {
check_mut_ptr(ptid)?; check_mut_ptr(ptid)?;
Some(ptid) Some(ptid)
} }
@ -233,7 +238,7 @@ pub fn do_clone(
} }
}; };
let ctid = { let ctid = {
if ctid != ptr::null_mut() { if flags.contains(CloneFlags::CLONE_CHILD_CLEARTID) {
check_mut_ptr(ctid)?; check_mut_ptr(ctid)?;
Some(ctid) Some(ctid)
} }
@ -241,7 +246,15 @@ pub fn do_clone(
None 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)?; 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) Ok(ret_addr as isize)
} }
fn do_mprotect(
addr: usize,
len: usize,
prot: u32,
) -> Result<isize, Error> {
// TODO: implement it
Ok(0)
}
fn do_brk(new_brk_addr: usize) -> Result<isize, Error> { fn do_brk(new_brk_addr: usize) -> Result<isize, Error> {
let ret_brk_addr = vm::do_brk(new_brk_addr)?; let ret_brk_addr = vm::do_brk(new_brk_addr)?;
Ok(ret_brk_addr as isize) Ok(ret_brk_addr as isize)

@ -4,7 +4,7 @@ PROJECT_DIR := $(realpath $(CUR_DIR)/../)
# Dependencies: need to be compiled but not to run by any Makefile target # Dependencies: need to be compiled but not to run by any Makefile target
TEST_DEPS := dev_null TEST_DEPS := dev_null
# Tests: need to be compiled and run by test-% target # 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 # Benchmarks: need to be compiled and run by bench-% target
BENCHES := spawn_and_exit_latency pipe_throughput BENCHES := spawn_and_exit_latency pipe_throughput

@ -1,86 +0,0 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <unistd.h>
#include <sys/syscall.h>
/*
* 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;
}

41
test/pthread/main.c Normal file

@ -0,0 +1,41 @@
#include <sys/types.h>
#include <pthread.h>
#include <stdio.h>
/*
* 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;
}