asterinas/kernel/src/process/posix_thread/mod.rs

332 lines
11 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::{AtomicU32, Ordering};
use aster_rights::{ReadDupOp, ReadOp, WriteOp};
use ostd::sync::{RoArc, Waker};
use super::{
kill::SignalSenderIds,
signal::{
sig_disposition::SigDispositions,
sig_mask::{AtomicSigMask, SigMask, SigSet},
sig_num::SigNum,
sig_queues::SigQueues,
signals::Signal,
SigEvents, SigEventsFilter,
},
Credentials, Process,
};
use crate::{
events::Observer,
fs::{file_table::FileTable, thread_info::ThreadFsInfo},
prelude::*,
process::signal::constants::SIGCONT,
thread::{Thread, Tid},
time::{clocks::ProfClock, Timer, TimerManager},
};
mod builder;
mod exit;
pub mod futex;
mod name;
mod posix_thread_ext;
mod robust_list;
mod thread_local;
pub mod thread_table;
pub use builder::PosixThreadBuilder;
pub use exit::{do_exit, do_exit_group};
pub use name::{ThreadName, MAX_THREAD_NAME_LEN};
pub use posix_thread_ext::AsPosixThread;
pub use robust_list::RobustListHead;
pub use thread_local::{AsThreadLocal, FileTableRefMut, ThreadLocal};
pub struct PosixThread {
// Immutable part
process: Weak<Process>,
tid: Tid,
// Mutable part
name: Mutex<Option<ThreadName>>,
/// Process credentials. At the kernel level, credentials are a per-thread attribute.
credentials: Credentials,
// Files
/// File table
file_table: Mutex<Option<RoArc<FileTable>>>,
/// File system
fs: Arc<ThreadFsInfo>,
// Signal
/// Blocked signals
sig_mask: AtomicSigMask,
/// Thread-directed sigqueue
sig_queues: SigQueues,
/// The per-thread signal [`Waker`], which will be used to wake up the thread
/// when enqueuing a signal.
signalled_waker: SpinLock<Option<Arc<Waker>>>,
/// A profiling clock measures the user CPU time and kernel CPU time in the thread.
prof_clock: Arc<ProfClock>,
/// A manager that manages timers based on the user CPU time of the current thread.
virtual_timer_manager: Arc<TimerManager>,
/// A manager that manages timers based on the profiling clock of the current thread.
prof_timer_manager: Arc<TimerManager>,
/// I/O Scheduling priority value
io_priority: AtomicU32,
}
impl PosixThread {
pub fn process(&self) -> Arc<Process> {
self.process.upgrade().unwrap()
}
pub fn weak_process(&self) -> Weak<Process> {
Weak::clone(&self.process)
}
/// Returns the thread id
pub fn tid(&self) -> Tid {
self.tid
}
pub fn thread_name(&self) -> &Mutex<Option<ThreadName>> {
&self.name
}
pub fn file_table(&self) -> &Mutex<Option<RoArc<FileTable>>> {
&self.file_table
}
pub fn fs(&self) -> &Arc<ThreadFsInfo> {
&self.fs
}
/// Get the reference to the signal mask of the thread.
///
/// Note that while this function offers mutable access to the signal mask,
/// it is not sound for callers other than the current thread to modify the
/// signal mask. They may only read the signal mask.
pub fn sig_mask(&self) -> &AtomicSigMask {
&self.sig_mask
}
pub fn sig_pending(&self) -> SigSet {
self.sig_queues.sig_pending()
}
/// Returns whether the thread has some pending signals
/// that are not blocked.
pub fn has_pending(&self) -> bool {
let blocked = self.sig_mask().load(Ordering::Relaxed);
self.sig_queues.has_pending(blocked)
}
/// Returns whether the signal is blocked by the thread.
pub(in crate::process) fn has_signal_blocked(&self, signum: SigNum) -> bool {
// FIXME: Some signals cannot be blocked, even set in sig_mask.
self.sig_mask.contains(signum, Ordering::Relaxed)
}
/// Checks whether the signal can be delivered to the thread.
///
/// For a signal can be delivered to the thread, the sending thread must either
/// be privileged, or the real or effective user ID of the sending thread must equal
/// the real or saved set-user-ID of the target thread.
///
/// For SIGCONT, the sending and receiving processes should belong to the same session.
pub(in crate::process) fn check_signal_perm(
&self,
signum: Option<&SigNum>,
sender: &SignalSenderIds,
) -> Result<()> {
if sender.euid().is_root() {
return Ok(());
}
if let Some(signum) = signum
&& *signum == SIGCONT
{
let receiver_sid = self.process().sid();
if receiver_sid == sender.sid().unwrap() {
return Ok(());
}
return_errno_with_message!(
Errno::EPERM,
"sigcont requires that sender and receiver belongs to the same session"
);
}
let (receiver_ruid, receiver_suid) = {
let credentials = self.credentials();
(credentials.ruid(), credentials.suid())
};
// FIXME: further check the below code to ensure the behavior is same as Linux. According
// to man(2) kill, the real or effective user ID of the sending process must equal the
// real or saved set-user-ID of the target process.
if sender.ruid() == receiver_ruid
|| sender.ruid() == receiver_suid
|| sender.euid() == receiver_ruid
|| sender.euid() == receiver_suid
{
return Ok(());
}
return_errno_with_message!(Errno::EPERM, "sending signal to the thread is not allowed.");
}
/// Sets the input [`Waker`] as the signalled waker of this thread.
///
/// This approach can collaborate with signal-aware wait methods.
/// Once a signalled waker is set for a thread, it cannot be reset until it is cleared.
///
/// # Panics
///
/// If setting a new waker before clearing the current thread's signalled waker
/// this method will panic.
pub fn set_signalled_waker(&self, waker: Arc<Waker>) {
let mut signalled_waker = self.signalled_waker.lock();
assert!(signalled_waker.is_none());
*signalled_waker = Some(waker);
}
/// Clears the signalled waker of this thread.
pub fn clear_signalled_waker(&self) {
*self.signalled_waker.lock() = None;
}
/// Wakes up the signalled waker.
pub fn wake_signalled_waker(&self) {
if let Some(waker) = &*self.signalled_waker.lock() {
waker.wake_up();
}
}
/// Enqueues a thread-directed signal.
///
/// This method does not perform permission checks on user signals. Therefore, unless the
/// caller can ensure that there are no permission issues, this method should be used for
/// enqueue kernel signals or fault signals.
pub fn enqueue_signal(&self, signal: Box<dyn Signal>) {
let process = self.process();
let sig_dispositions = process.sig_dispositions().lock();
let signum = signal.num();
if sig_dispositions.get(signum).will_ignore(signum) {
return;
}
self.enqueue_signal_locked(signal, sig_dispositions);
}
/// Enqueues a thread-directed signal with locked dispositions.
///
/// By locking dispositions, the caller should have already checked the signal is not to be
/// ignored.
//
// FIXME: According to Linux behavior, we should enqueue ignored signals blocked by all
// threads, as a thread may change the signal handler and unblock them in the future. However,
// achieving this behavior properly without maintaining a process-wide signal queue is
// difficult. For instance, if we randomly select a thread-wide signal queue, the thread that
// modifies the signal handler and unblocks the signal may not be the same one. Consequently,
// the current implementation uses a simpler mechanism that never enqueues any ignored signals.
pub(in crate::process) fn enqueue_signal_locked(
&self,
signal: Box<dyn Signal>,
_sig_dispositions: MutexGuard<SigDispositions>,
) {
self.sig_queues.enqueue(signal);
self.wake_signalled_waker();
}
/// Returns a reference to the profiling clock of the current thread.
pub fn prof_clock(&self) -> &Arc<ProfClock> {
&self.prof_clock
}
/// Creates a timer based on the profiling CPU clock of the current thread.
pub fn create_prof_timer<F>(&self, func: F) -> Arc<Timer>
where
F: Fn() + Send + Sync + 'static,
{
self.prof_timer_manager.create_timer(func)
}
/// Creates a timer based on the user CPU clock of the current thread.
pub fn create_virtual_timer<F>(&self, func: F) -> Arc<Timer>
where
F: Fn() + Send + Sync + 'static,
{
self.virtual_timer_manager.create_timer(func)
}
/// Checks the `TimerCallback`s that are managed by the `prof_timer_manager`.
/// If any have timed out, call the corresponding callback functions.
pub fn process_expired_timers(&self) {
self.prof_timer_manager.process_expired_timers();
}
pub fn dequeue_signal(&self, mask: &SigMask) -> Option<Box<dyn Signal>> {
self.sig_queues.dequeue(mask)
}
pub fn register_sigqueue_observer(
&self,
observer: Weak<dyn Observer<SigEvents>>,
filter: SigEventsFilter,
) {
self.sig_queues.register_observer(observer, filter);
}
pub fn unregister_sigqueue_observer(&self, observer: &Weak<dyn Observer<SigEvents>>) {
self.sig_queues.unregister_observer(observer);
}
/// Gets the read-only credentials of the thread.
pub fn credentials(&self) -> Credentials<ReadOp> {
self.credentials.dup().restrict()
}
/// Gets the duplicatable read-only credentials of the thread.
pub fn credentials_dup(&self) -> Credentials<ReadDupOp> {
self.credentials.dup().restrict()
}
/// Gets the write-only credentials of the current thread.
///
/// It is illegal to mutate the credentials from a thread other than the
/// current thread. For performance reasons, this function only checks it
/// using debug assertions.
pub fn credentials_mut(&self) -> Credentials<WriteOp> {
debug_assert!(core::ptr::eq(
current_thread!().as_posix_thread().unwrap(),
self
));
self.credentials.dup().restrict()
}
/// Returns the I/O priority value of the thread.
pub fn io_priority(&self) -> &AtomicU32 {
&self.io_priority
}
}
static POSIX_TID_ALLOCATOR: AtomicU32 = AtomicU32::new(1);
/// Allocates a new tid for the new posix thread
pub fn allocate_posix_tid() -> Tid {
POSIX_TID_ALLOCATOR.fetch_add(1, Ordering::SeqCst)
}
/// Returns the last allocated tid
pub fn last_tid() -> Tid {
POSIX_TID_ALLOCATOR.load(Ordering::SeqCst) - 1
}