impl sys_futex. fix clear_child_tid for thread exit

master
WangRunji 6 years ago
parent 84b2af3cdb
commit 491353acfd

@ -21,6 +21,9 @@ use super::abi::{self, ProcInitInfo};
pub struct Thread {
pub context: Context,
pub kstack: KernelStack,
/// Kernel performs futex wake when thread exits.
/// Ref: [http://man7.org/linux/man-pages/man2/set_tid_address.2.html]
pub clear_child_tid: usize,
pub proc: Arc<Mutex<Process>>,
}
@ -57,6 +60,7 @@ pub struct Process {
pub memory_set: MemorySet,
pub files: BTreeMap<usize, FileLike>,
pub cwd: String,
futexes: BTreeMap<usize, Arc<Condvar>>,
}
/// Let `rcore_thread` can switch between our `Thread`
@ -74,10 +78,12 @@ impl Thread {
Box::new(Thread {
context: Context::null(),
kstack: KernelStack::new(),
clear_child_tid: 0,
proc: Arc::new(Mutex::new(Process {
memory_set: MemorySet::new(),
files: BTreeMap::default(),
cwd: String::from("/"),
futexes: BTreeMap::default(),
})),
})
}
@ -89,10 +95,12 @@ impl Thread {
Box::new(Thread {
context: unsafe { Context::new_kernel_thread(entry, arg, kstack.top(), memory_set.token()) },
kstack,
clear_child_tid: 0,
proc: Arc::new(Mutex::new(Process {
memory_set,
files: BTreeMap::default(),
cwd: String::from("/"),
futexes: BTreeMap::default(),
})),
})
}
@ -174,10 +182,12 @@ impl Thread {
entry_addr, ustack_top, kstack.top(), is32, memory_set.token())
},
kstack,
clear_child_tid: 0,
proc: Arc::new(Mutex::new(Process {
memory_set,
files,
cwd: String::from("/"),
futexes: BTreeMap::default(),
})),
})
}
@ -216,21 +226,24 @@ impl Thread {
Box::new(Thread {
context: unsafe { Context::new_fork(tf, kstack.top(), memory_set.token()) },
kstack,
clear_child_tid: 0,
proc: Arc::new(Mutex::new(Process {
memory_set,
files,
cwd,
futexes: BTreeMap::default(),
})),
})
}
/// Create a new thread in the same process.
pub fn clone(&self, tf: &TrapFrame, stack_top: usize, tls: usize) -> Box<Thread> {
pub fn clone(&self, tf: &TrapFrame, stack_top: usize, tls: usize, clear_child_tid: usize) -> Box<Thread> {
let kstack = KernelStack::new();
let token = self.proc.lock().memory_set.token();
Box::new(Thread {
context: unsafe { Context::new_clone(tf, stack_top, kstack.top(), token, tls) },
kstack,
clear_child_tid,
proc: self.proc.clone(),
})
}
@ -240,6 +253,12 @@ impl Process {
pub fn get_free_inode(&self) -> usize {
(0..).find(|i| !self.files.contains_key(i)).unwrap()
}
pub fn get_futex(&mut self, uaddr: usize) -> Arc<Condvar> {
if !self.futexes.contains_key(&uaddr) {
self.futexes.insert(uaddr, Arc::new(Condvar::new()));
}
self.futexes.get(&uaddr).unwrap().clone()
}
}

@ -45,6 +45,20 @@ impl Condvar {
t.unpark();
}
}
/// Notify up to `n` waiters.
/// Return the number of waiters that were woken up.
pub fn notify_n(&self, mut n: usize) -> usize {
let mut count = 0;
while count < n {
if let Some(t) = self.wait_queue.lock().pop_front() {
t.unpark();
count += 1;
} else {
break;
}
}
count
}
pub fn _clear(&self) {
self.wait_queue.lock().clear();
}

@ -1,14 +0,0 @@
use super::*;
pub fn sys_arch_prctl(code: i32, addr: usize, tf: &mut TrapFrame) -> SysResult {
const ARCH_SET_FS: i32 = 0x1002;
match code {
#[cfg(target_arch = "x86_64")]
ARCH_SET_FS => {
info!("sys_arch_prctl: set FS to {:#x}", addr);
tf.fsbase = addr;
Ok(0)
}
_ => Err(SysError::EINVAL),
}
}

@ -1,5 +1,22 @@
use super::*;
use core::mem::size_of;
use core::sync::atomic::{AtomicI32, Ordering};
use alloc::collections::btree_map::BTreeMap;
use crate::sync::Condvar;
use crate::sync::SpinNoIrqLock as Mutex;
pub fn sys_arch_prctl(code: i32, addr: usize, tf: &mut TrapFrame) -> SysResult {
const ARCH_SET_FS: i32 = 0x1002;
match code {
#[cfg(target_arch = "x86_64")]
ARCH_SET_FS => {
info!("sys_arch_prctl: set FS to {:#x}", addr);
tf.fsbase = addr;
Ok(0)
}
_ => Err(SysError::EINVAL),
}
}
pub fn sys_uname(buf: *mut u8) -> SysResult {
info!("sched_uname: buf: {:?}", buf);
@ -46,6 +63,52 @@ pub fn sys_sysinfo(sys_info: *mut SysInfo) -> SysResult {
Ok(0)
}
pub fn sys_futex(uaddr: usize, op: u32, val: i32, timeout: *const TimeSpec) -> SysResult {
info!("futex: [{}] uaddr: {:#x}, op: {:#x}, val: {}, timeout_ptr: {:?}",
thread::current().id(), uaddr, op, val, timeout);
// if op & OP_PRIVATE == 0 {
// unimplemented!("futex only support process-private");
// return Err(SysError::ENOSYS);
// }
if uaddr % size_of::<u32>() != 0 {
return Err(SysError::EINVAL);
}
// FIXME: check uaddr, see sys_mprotect
// process().memory_set.check_mut_ptr(uaddr)?;
let atomic = unsafe { &mut *(uaddr as *mut AtomicI32) };
let timeout = if timeout.is_null() {
None
} else {
process().memory_set.check_ptr(timeout)?;
Some(unsafe { *timeout })
};
const OP_WAIT: u32 = 0;
const OP_WAKE: u32 = 1;
const OP_PRIVATE: u32 = 128;
let queue = process().get_futex(uaddr);
match op & 0xf {
OP_WAIT => {
if atomic.load(Ordering::Acquire) != val {
return Err(SysError::EAGAIN);
}
// FIXME: support timeout
queue._wait();
Ok(0)
}
OP_WAKE => {
let woken_up_count = queue.notify_n(val as usize);
Ok(woken_up_count)
}
_ => {
warn!("unsupported futex operation: {}", op);
Err(SysError::ENOSYS)
},
}
}
#[repr(C)]
#[derive(Debug, Default)]
pub struct SysInfo {

@ -19,7 +19,6 @@ use self::fs::*;
use self::mem::*;
use self::proc::*;
use self::time::*;
use self::ctrl::*;
use self::net::*;
use self::misc::*;
@ -27,7 +26,6 @@ mod fs;
mod mem;
mod proc;
mod time;
mod ctrl;
mod net;
mod misc;
@ -76,7 +74,7 @@ pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> isize {
052 => sys_getpeername(args[0], args[1] as *mut SockaddrIn, args[2] as *mut u32),
054 => sys_setsockopt(args[0], args[1], args[2], args[3] as *const u8, args[4]),
055 => sys_getsockopt(args[0], args[1], args[2], args[3] as *mut u8, args[4] as *mut u32),
056 => sys_clone(args[0], args[1], args[2] as *mut usize, args[3] as *mut usize, args[4], tf),
056 => sys_clone(args[0], args[1], args[2] as *mut u32, args[3] as *mut u32, args[4], tf),
057 => sys_fork(tf),
// use fork for vfork
058 => sys_fork(tf),
@ -109,6 +107,7 @@ pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> isize {
// 169 => sys_reboot(),
186 => sys_gettid(),
201 => sys_time(args[0] as *mut u64),
202 => sys_futex(args[0], args[1] as u32, args[2] as i32, args[3] as *const TimeSpec),
204 => sys_sched_getaffinity(args[0], args[1], args[2] as *mut u32),
217 => sys_getdents64(args[0], args[1] as *mut LinuxDirent64, args[2]),
228 => sys_clock_gettime(args[0], args[1] as *mut TimeSpec),

@ -14,7 +14,7 @@ pub fn sys_fork(tf: &TrapFrame) -> SysResult {
/// The new thread's stack pointer will be set to `newsp`.
/// The child tid will be stored at both `parent_tid` and `child_tid`.
/// This is partially implemented for musl only.
pub fn sys_clone(flags: usize, newsp: usize, parent_tid: *mut usize, child_tid: *mut usize, newtls: usize, tf: &TrapFrame) -> SysResult {
pub fn sys_clone(flags: usize, newsp: usize, parent_tid: *mut u32, child_tid: *mut u32, newtls: usize, tf: &TrapFrame) -> SysResult {
info!("clone: flags: {:#x}, newsp: {:#x}, parent_tid: {:?}, child_tid: {:?}, newtls: {:#x}",
flags, newsp, parent_tid, child_tid, newtls);
if flags != 0x7d0f00 {
@ -27,14 +27,13 @@ pub fn sys_clone(flags: usize, newsp: usize, parent_tid: *mut usize, child_tid:
// proc.memory_set.check_mut_ptr(parent_tid)?;
// proc.memory_set.check_mut_ptr(child_tid)?;
}
// FIXME: tf.rip => tf.ip() for all arch
let new_thread = current_thread().clone(tf, newsp, newtls);
let new_thread = current_thread().clone(tf, newsp, newtls, child_tid as usize);
// FIXME: parent pid
let tid = processor().manager().add(new_thread, thread::current().id());
info!("clone: {} -> {}", thread::current().id(), tid);
unsafe {
parent_tid.write(tid);
child_tid.write(tid);
parent_tid.write(tid as u32);
child_tid.write(tid as u32);
}
Ok(tid)
}
@ -184,6 +183,17 @@ pub fn sys_exit(exit_code: isize) -> ! {
let pid = thread::current().id();
info!("exit: {}, code: {}", pid, exit_code);
// perform futex wake 1
// ref: http://man7.org/linux/man-pages/man2/set_tid_address.2.html
// FIXME: do it in all possible ways a thread can exit
// it has memory access so we can't move it to Thread::drop?
let clear_child_tid = current_thread().clear_child_tid;
if clear_child_tid != 0 {
unsafe { (clear_child_tid as *mut u32).write(0); }
let queue = process().get_futex(clear_child_tid);
queue.notify_one();
}
processor().manager().exit(pid, exit_code as usize);
processor().yield_now();
unreachable!();

Loading…
Cancel
Save