Add Pthread test
This commit is contained in:
parent
13e4a898dd
commit
660d0931cd
@ -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
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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user