Skip to content

Commit 9af39f0

Browse files
committed
fix(process): track live thread-group members
Replace the exit-time scan of every thread-group member with a shared live-task counter, matching Linux signal_struct::live semantics. Increment the counter only when a CLONE_THREAD task is published and consume exactly one token when each task enters exit, so the unique 1-to-0 transition owns group-finalization work. Keep the counter in ThreadInfo rather than SigHand because CLONE_SIGHAND can be shared by distinct thread groups. This removes quadratic exit behavior and shortens the global PID membership lock's irq-disabled critical section without changing group-exec or finalization ordering. Refs: #2153 Signed-off-by: longjin <longjin@dragonos.org>
1 parent 71bc88e commit 9af39f0

3 files changed

Lines changed: 32 additions & 38 deletions

File tree

kernel/src/process/fork.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -826,8 +826,14 @@ impl ProcessManager {
826826
// log::debug!("fork: clone_flags: {:?}", clone_flags);
827827
// 设置线程组id、组长
828828
if clone_flags.contains(CloneFlags::CLONE_THREAD) {
829-
pcb.thread.write_irqsave().group_leader =
830-
current_pcb.thread.read_irqsave().group_leader.clone();
829+
let current_thread = current_pcb.thread.read_irqsave();
830+
let group_leader = current_thread.group_leader.clone();
831+
let thread_group_live = current_thread.thread_group_live.clone();
832+
drop(current_thread);
833+
let mut child_thread = pcb.thread.write_irqsave();
834+
child_thread.group_leader = group_leader;
835+
child_thread.thread_group_live = thread_group_live;
836+
drop(child_thread);
831837
unsafe {
832838
let ptr = pcb.as_ref() as *const ProcessControlBlock as *mut ProcessControlBlock;
833839
(*ptr).tgid = current_pcb.tgid;
@@ -922,6 +928,11 @@ impl ProcessManager {
922928
let inherited_tty = current_pcb.sig_info_irqsave().tty();
923929
pcb.sig_info_mut().set_tty(inherited_tty);
924930
current_pcb.sighand().with_group_exec_check(|| {
931+
let live = pcb
932+
.threads_read_irqsave()
933+
.thread_group_live
934+
.fetch_add(1, Ordering::Relaxed);
935+
assert_ne!(live, 0, "cannot publish a thread into a dead group");
925936
pcb.attach_pid(PidType::PID);
926937
pcb.task_join_group_stop();
927938
group_leader

kernel/src/process/info.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use alloc::{
33
sync::{Arc, Weak},
44
vec::Vec,
55
};
6+
use core::sync::atomic::AtomicUsize;
67

78
use crate::{
89
arch::ipc::signal::{SigSet, Signal},
@@ -49,6 +50,12 @@ pub struct ThreadInfo {
4950
/// When the current thread is the group leader, this field stores the
5051
/// PCBs of all threads in the group.
5152
pub(super) group_tasks: Vec<Weak<ProcessControlBlock>>,
53+
/// Number of published tasks that have not entered the exit path.
54+
///
55+
/// This is shared by exactly one thread group. It must not live in
56+
/// `SigHand`, because CLONE_SIGHAND may share that object across distinct
57+
/// thread groups.
58+
pub(super) thread_group_live: Arc<AtomicUsize>,
5259
}
5360

5461
impl Default for ThreadInfo {
@@ -65,6 +72,7 @@ impl ThreadInfo {
6572
vfork_done: None,
6673
group_leader: Weak::default(),
6774
group_tasks: Vec::new(),
75+
thread_group_live: Arc::new(AtomicUsize::new(1)),
6876
}
6977
}
7078

kernel/src/process/manager/exit.rs

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -134,41 +134,6 @@ impl ProcessManager {
134134
sighand.complete_group_exec_leader_exit(current);
135135
}
136136

137-
/// Return the stable thread-group leader and whether `current`, which has
138-
/// already published EXITING, is the last live task in the group.
139-
///
140-
/// The caller must hold PID_MEMBERSHIP_LOCK across both mark_exiting() and
141-
/// this scan, so concurrent exits cannot both miss the unique last task.
142-
fn thread_group_last_live_task(
143-
current: &Arc<ProcessControlBlock>,
144-
) -> (Arc<ProcessControlBlock>, bool) {
145-
let leader = current
146-
.threads_read_irqsave()
147-
.group_leader()
148-
.unwrap_or_else(|| current.clone());
149-
let leader_threads = leader.threads_read_irqsave();
150-
let is_live = |task: &Arc<ProcessControlBlock>| {
151-
!task.flags().contains(ProcessFlags::EXITING)
152-
&& !task.is_exited()
153-
&& !task.is_zombie()
154-
&& !task.is_dead()
155-
};
156-
157-
if !Arc::ptr_eq(&leader, current) && is_live(&leader) {
158-
return (leader.clone(), false);
159-
}
160-
for weak in &leader_threads.group_tasks {
161-
let Some(task) = weak.upgrade() else {
162-
continue;
163-
};
164-
if !Arc::ptr_eq(&task, current) && is_live(&task) {
165-
return (leader.clone(), false);
166-
}
167-
}
168-
drop(leader_threads);
169-
(leader, true)
170-
}
171-
172137
fn notify_ptrace_parent(tracer: Option<(Arc<ProcessControlBlock>, i32)>) {
173138
if let Some((tracer, signal)) = tracer {
174139
if signal > 0 {
@@ -364,7 +329,17 @@ impl ProcessManager {
364329
let (group_leader, group_dead) = {
365330
let _membership_guard = crate::process::pid::pid_membership_lock();
366331
pcb.mark_exiting();
367-
Self::thread_group_last_live_task(&pcb)
332+
let thread = pcb.threads_read_irqsave();
333+
let group_leader = thread.group_leader().unwrap_or_else(|| pcb.clone());
334+
let live = thread.thread_group_live.clone();
335+
drop(thread);
336+
// Match Linux signal_struct::live: every successfully
337+
// published thread contributes once and consumes its token on
338+
// first entering do_exit(). The unique 1 -> 0 transition owns
339+
// thread-group finalization without scanning group_tasks.
340+
let previous = live.fetch_sub(1, Ordering::AcqRel);
341+
assert_ne!(previous, 0, "thread-group live count underflow");
342+
(group_leader, previous == 1)
368343
};
369344
pid = pcb.pid();
370345
if pid.is_child_reaper() {

0 commit comments

Comments
 (0)