blob: 30b356ef0d0f254573ef014a619c1f72a862c8fd [file] [edit]
// Copyright 2026 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
use foreign_box::ForeignBox;
use pw_status::{Error, Result};
use syscall_defs::ExitStatus;
use crate::Kernel;
use crate::object::{KernelObject, ObjectBase, Signals};
use crate::scheduler::thread::{ProcessHandle, Thread, ThreadHandle};
use crate::scheduler::{SchedulerState, TryJoinResult};
use crate::sync::spinlock::{SpinLock, SpinLockGuard};
enum State<K: Kernel> {
Stopped(ForeignBox<Thread<K>>),
Running(ThreadHandle<K>),
Empty,
}
impl<K: Kernel> State<K> {
fn take_thread(&mut self) -> Option<ForeignBox<Thread<K>>> {
match core::mem::replace(self, State::Empty) {
State::Stopped(thread) => Some(thread),
state => {
*self = state;
None
}
}
}
}
pub struct ThreadObject<K: Kernel> {
base: ObjectBase<K>,
state: SpinLock<K, State<K>>,
}
impl<K: Kernel> ThreadObject<K> {
#[must_use]
pub const fn new(thread: ForeignBox<Thread<K>>) -> Self {
Self {
base: ObjectBase::new(Signals::no_active()),
state: SpinLock::new(State::Stopped(thread)),
}
}
pub const fn new_empty() -> Self {
Self {
base: ObjectBase::new(Signals::no_active()),
state: SpinLock::new(State::Empty),
}
}
pub fn set_thread(&self, kernel: K, thread: ForeignBox<Thread<K>>) -> Result<()> {
let mut state = self.state.lock(kernel);
match *state {
State::Empty => {
*state = State::Stopped(thread);
Ok(())
}
_ => Err(Error::AlreadyExists),
}
}
pub fn start(
&self,
kernel: K,
process_handle: ProcessHandle<K>,
initial_pc: usize,
initial_sp: usize,
args: (usize, usize, usize),
) -> Result<()> {
let mut state = self.state.lock(kernel);
let Some(mut thread) = state.take_thread() else {
return Err(Error::AlreadyExists);
};
// SAFETY: `ThreadObject` is a kernel object that is always managed
// within a `ForeignRcState` (e.g., when wrapped in `ForeignRc` or
// `ForeignBox` for the object table). Thus, the `&self` reference
// points to storage contained inside a `ForeignRcState`.
let self_rc = unsafe { foreign_box::ForeignRcState::create_ref_from_inner(self) };
thread.object = Some(self_rc.clone());
unsafe {
thread.initialize_non_priv_thread(
kernel,
process_handle,
initial_pc,
initial_sp,
args,
)?;
}
let thread_handle = crate::scheduler::start_thread(kernel, thread);
*state = State::Running(thread_handle);
Ok(())
}
pub fn terminate(&self, kernel: K) -> Result<()> {
let mut thread_handle = {
let state = self.state.lock(kernel);
match &*state {
State::Running(thread_handle) => thread_handle.clone(),
State::Stopped(_) => return Err(Error::FailedPrecondition),
State::Empty => return Err(Error::Internal),
}
};
// Call terminate without holding the state lock
thread_handle.terminate(kernel, ExitStatus::TerminatedBySyscall)
}
pub fn join_locked<'a>(
&self,
kernel: K,
sched: SpinLockGuard<'a, K, SchedulerState<K>>,
) -> (SpinLockGuard<'a, K, SchedulerState<K>>, Result<ExitStatus>) {
let thread_handle = {
let mut state = self.state.lock(kernel);
match core::mem::replace(&mut *state, State::Empty) {
State::Running(t) => t,
old_state => {
*state = old_state;
return (sched, Err(Error::FailedPrecondition));
}
}
};
let res = thread_handle.try_join_locked(kernel, sched);
match res {
(sched, crate::scheduler::TryJoinResult::Joined(thread, status)) => {
let mut state = self.state.lock(kernel);
*state = State::Stopped(thread);
(sched, Ok(status))
}
(sched, crate::scheduler::TryJoinResult::Wait(thread_handle)) => {
let mut state = self.state.lock(kernel);
*state = State::Running(thread_handle);
(sched, Err(Error::Unavailable))
}
(
sched,
TryJoinResult::Err {
error,
thread: thread_handle,
},
) => {
let mut state = self.state.lock(kernel);
*state = State::Running(thread_handle);
(sched, Err(error))
}
}
}
pub(crate) fn signal_locked<'a, F: Fn(Signals) -> Signals>(
&self,
kernel: K,
sched: SpinLockGuard<'a, K, SchedulerState<K>>,
update_fn: F,
) -> SpinLockGuard<'a, K, SchedulerState<K>> {
self.base.signal_locked(kernel, sched, update_fn)
}
}
impl<K: Kernel> KernelObject<K> for ThreadObject<K> {
fn base(&self) -> Option<&ObjectBase<K>> {
Some(&self.base)
}
fn object_wait(
&self,
kernel: K,
signal_mask: Signals,
deadline: time::Instant<K::Clock>,
) -> Result<crate::object::WaitReturn> {
self.base.wait_until(kernel, signal_mask, deadline)
}
fn thread_start(&self, kernel: K, initial_pc: usize, initial_sp: usize) -> Result<()> {
let process = kernel.get_scheduler().lock(kernel).current_process_handle();
self.start(kernel, process.clone(), initial_pc, initial_sp, (0, 0, 0))
}
fn task_terminate(&self, kernel: K) -> Result<()> {
self.terminate(kernel)
}
fn task_join(&self, kernel: K) -> Result<ExitStatus> {
let sched = kernel.get_scheduler().lock(kernel);
let (_, res) = self.join_locked(kernel, sched);
res
}
}