fix futex based thread joining

This commit is contained in:
Ashwin Naren
2025-12-21 17:02:19 -08:00
parent e77742e029
commit 8bf0a72bce
4 changed files with 51 additions and 17 deletions

View File

@@ -1,3 +1,4 @@
use crate::memory::uaccess::copy_to_user;
use crate::{
process::{TASK_LIST, Task, TaskState},
sched::{self, current_task},
@@ -44,8 +45,8 @@ bitflags! {
pub async fn sys_clone(
flags: u32,
newsp: UA,
_arent_tidptr: UA,
_child_tidptr: UA,
parent_tidptr: UA,
child_tidptr: UA,
tls: usize,
) -> Result<usize> {
let flags = CloneFlags::from_bits_truncate(flags);
@@ -146,6 +147,11 @@ pub async fn sys_clone(
state: Arc::new(SpinLock::new(TaskState::Runnable)),
last_run: SpinLock::new(None),
robust_list: SpinLock::new(None),
child_tid_ptr: SpinLock::new(if !child_tidptr.is_null() {
Some(child_tidptr.cast::<u32>())
} else {
None
}),
}
};
@@ -157,5 +163,13 @@ pub async fn sys_clone(
sched::insert_task(Arc::new(new_task));
// Honour CLONE_*SETTID semantics for the parent and (shared-VM) child.
if flags.contains(CloneFlags::CLONE_PARENT_SETTID) && !parent_tidptr.is_null() {
copy_to_user(parent_tidptr.cast::<u32>(), tid.value() as u32).await?;
}
if flags.contains(CloneFlags::CLONE_CHILD_SETTID) && !child_tidptr.is_null() {
copy_to_user(child_tidptr.cast::<u32>(), tid.value() as u32).await?;
}
Ok(tid.value() as _)
}

View File

@@ -1,3 +1,4 @@
use crate::process::threading::futex_wake_addr;
use crate::sched::current_task;
use alloc::vec::Vec;
use libkernel::error::Result;
@@ -101,6 +102,17 @@ pub fn sys_exit_group(exit_code: usize) -> Result<usize> {
pub fn sys_exit(exit_code: usize) -> Result<usize> {
let task = current_task();
// Honour CLONE_CHILD_CLEARTID: clear the user TID word and futex-wake any waiters.
if let Some(ptr) = task.child_tid_ptr.lock_save_irq().take() {
unsafe {
(ptr.value() as *mut u32).write_volatile(0);
}
// Wake any thread waiting on this futex.
futex_wake_addr(ptr, 1);
}
let process = Arc::clone(&task.process);
let mut thread_lock = process.threads.lock_save_irq();

View File

@@ -181,6 +181,7 @@ pub struct Task {
pub last_run: SpinLock<Option<Instant>>,
pub state: Arc<SpinLock<TaskState>>,
pub robust_list: SpinLock<Option<TUA<RobustListHead>>>,
pub child_tid_ptr: SpinLock<Option<TUA<u32>>>,
}
impl Task {
@@ -214,6 +215,7 @@ impl Task {
fd_table: Arc::new(SpinLock::new(FileDescriptorTable::new())),
last_run: SpinLock::new(None),
robust_list: SpinLock::new(None),
child_tid_ptr: SpinLock::new(None),
}
}
@@ -241,6 +243,7 @@ impl Task {
)),
last_run: SpinLock::new(None),
robust_list: SpinLock::new(None),
child_tid_ptr: SpinLock::new(None),
}
}

View File

@@ -74,6 +74,25 @@ const FUTEX_WAIT_BITSET: i32 = 9;
const FUTEX_WAKE_BITSET: i32 = 10;
const FUTEX_PRIVATE_FLAG: i32 = 128;
/// Wake up to `nr` waiters sleeping on the futex word located at `uaddr`.
/// Returns the number of tasks actually woken.
pub fn futex_wake_addr(uaddr: TUA<u32>, nr: usize) -> usize {
let mut woke = 0;
if let Some(table) = FUTEX_TABLE.get()
&& let Some(waitq_arc) = table.lock_save_irq().get(&uaddr).cloned()
{
let mut waitq = waitq_arc.lock_save_irq();
for _ in 0..nr {
waitq.wakeups = waitq.wakeups.saturating_add(1);
waitq.wakers.wake_one();
woke += 1;
}
}
woke
}
pub async fn sys_futex(
uaddr: TUA<u32>,
op: i32,
@@ -132,21 +151,7 @@ pub async fn sys_futex(
FUTEX_WAKE | FUTEX_WAKE_BITSET => {
let nr_wake = val as usize;
let mut woke = 0;
if let Some(table) = FUTEX_TABLE.get()
&& let Some(waitq_arc) = table.lock_save_irq().get(&uaddr).cloned()
{
let mut waitq = waitq_arc.lock_save_irq();
for _ in 0..nr_wake {
// Record a pending wake-up and attempt to wake a single waiter.
waitq.wakeups = waitq.wakeups.saturating_add(1);
waitq.wakers.wake_one();
woke += 1;
}
}
Ok(woke)
Ok(futex_wake_addr(uaddr, nr_wake))
}
_ => Err(KernelError::NotSupported),