core: rewrite panic sites as explicit match+panic

Replace implicit unwrap()/expect() in the lock-ordered core with explicit
match arms. Lock-poison sites use one uniform message
("smarm: <lock> lock poisoned (core corrupt): {e}"); invariant sites panic
with a descriptive message naming the violated invariant. No behaviour
change: each rewrite preserves the prior panic-on-bad-arm semantics. Also
clears the accompanying clippy hygiene in these files (redundant_closure,
len_without_is_empty, too_many_arguments, unnecessary_sort_by,
missing_safety_doc, nonminimal_bool/unnecessary_unwrap).
This commit is contained in:
smarm-agent
2026-06-20 17:47:33 +00:00
parent 531571bfa5
commit a875fa8285
11 changed files with 387 additions and 114 deletions
+129 -39
View File
@@ -39,7 +39,10 @@ pub(crate) fn with_runtime<R>(f: impl FnOnce(&Arc<RuntimeInner>) -> R) -> R {
let prev = crate::preempt::PREEMPTION_ENABLED.with(|c| c.replace(false));
let result = RUNTIME.with(|r| {
let b = r.borrow();
let inner = b.as_ref().expect("smarm: not inside Runtime::run()");
let inner = match b.as_ref() {
Some(inner) => inner,
None => panic!("smarm: not inside Runtime::run()"),
};
f(inner)
});
crate::preempt::PREEMPTION_ENABLED.with(|c| c.set(prev));
@@ -51,7 +54,7 @@ pub(crate) fn with_runtime<R>(f: impl FnOnce(&Arc<RuntimeInner>) -> R) -> R {
/// Same preemption gate as [`with_runtime`]; same reasons.
pub(crate) fn try_with_runtime<R>(f: impl FnOnce(&Arc<RuntimeInner>) -> R) -> Option<R> {
let prev = crate::preempt::PREEMPTION_ENABLED.with(|c| c.replace(false));
let result = RUNTIME.with(|r| r.borrow().as_ref().map(|inner| f(inner)));
let result = RUNTIME.with(|r| r.borrow().as_ref().map(f));
crate::preempt::PREEMPTION_ENABLED.with(|c| c.set(prev));
result
}
@@ -76,15 +79,20 @@ impl JoinHandle {
pub fn join(mut self) -> Result<(), JoinError> {
use crate::actor::Outcome;
let me = current_pid().expect("join() called outside an actor");
let me = match current_pid() {
Some(pid) => pid,
None => panic!("join() called outside an actor"),
};
loop {
// Check-Done-or-register-waiter is atomic under the target's cold
// lock; finalize publishes Done and takes the waiter list under
// the same lock, so we either see the outcome or are woken.
let outcome = with_runtime(|inner| {
let slot = inner.slot_at(self.pid)
.expect("join: pid index out of range");
let slot = match inner.slot_at(self.pid) {
Some(slot) => slot,
None => panic!("join: pid index out of range: {:?}", self.pid),
};
let mut cold = slot.cold.lock();
match slot.status_for(self.pid) {
// Our outstanding handle pins the slot: it cannot be
@@ -93,7 +101,10 @@ impl JoinHandle {
panic!("join: target slot has been reused")
}
crate::slot_state::Status::Done => {
Some(cold.outcome.take().expect("Done slot must have outcome"))
Some(match cold.outcome.take() {
Some(outcome) => outcome,
None => panic!("Done slot must have outcome"),
})
}
crate::slot_state::Status::Live => {
// begin_wait is lock-free, legal under the cold lock;
@@ -181,8 +192,10 @@ pub fn spawn_under<A>(supervisor: Pid<A>, f: impl FnOnce() + Send + 'static) ->
// syscall and no allocation ever stalls another scheduler thread.
let stack = with_runtime(|inner| inner.stack_pool.lock().pop())
.unwrap_or_else(|| {
crate::stack::Stack::new(crate::runtime::ACTOR_STACK_SIZE)
.expect("stack allocation failed")
match crate::stack::Stack::new(crate::runtime::ACTOR_STACK_SIZE) {
Ok(stack) => stack,
Err(e) => panic!("stack allocation failed: {e}"),
}
});
let sp = init_actor_stack(stack.top(), crate::actor::trampoline);
let closure: crate::runtime::Closure = Box::new(f);
@@ -225,7 +238,10 @@ pub fn spawn_addr<A: crate::pid::Addressable>(
use crate::context::init_actor_stack;
pub fn self_pid() -> Pid {
current_pid().expect("self_pid() called outside an actor")
match current_pid() {
Some(pid) => pid,
None => panic!("self_pid() called outside an actor"),
}
}
// ---------------------------------------------------------------------------
@@ -282,7 +298,10 @@ pub(crate) fn unpark_at(pid: Pid, epoch: u32) {
/// waker. Lock-free (one CAS on the own slot word), so it is legal under
/// any lock, including a Channel-class lock.
pub(crate) fn begin_wait() -> u32 {
let me = current_pid().expect("begin_wait() called outside an actor");
let me = match current_pid() {
Some(pid) => pid,
None => panic!("begin_wait() called outside an actor"),
};
with_runtime(|inner| inner.begin_wait(me))
}
@@ -294,7 +313,10 @@ pub(crate) fn begin_wait() -> u32 {
/// self-clean at their wakers' failed CAS; nothing can fault the actor's
/// next one-shot park.
pub(crate) fn retire_wait() {
let me = current_pid().expect("retire_wait() called outside an actor");
let me = match current_pid() {
Some(pid) => pid,
None => panic!("retire_wait() called outside an actor"),
};
with_runtime(|inner| inner.retire_wait(me));
// A request_stop that fired before the clear had its notification
// eaten, but it set the stop flag first — observe it here and unwind.
@@ -365,11 +387,19 @@ impl Drop for NoPreempt {
// ---------------------------------------------------------------------------
pub fn sleep(duration: std::time::Duration) {
let me = current_pid().expect("sleep() called outside an actor");
let me = match current_pid() {
Some(pid) => pid,
None => panic!("sleep() called outside an actor"),
};
let _np = NoPreempt::enter();
let epoch = begin_wait();
let deadline = crate::timer::deadline_from_now(duration);
with_runtime(|inner| inner.timers.lock().unwrap().insert_sleep(deadline, me, epoch));
with_runtime(|inner| {
match inner.timers.lock() {
Ok(mut timers) => timers.insert_sleep(deadline, me, epoch),
Err(e) => panic!("smarm: timers lock poisoned (core corrupt): {e}"),
}
});
park_current();
}
@@ -380,11 +410,14 @@ pub fn insert_wait_timer(
epoch: u32,
) {
with_runtime(|inner| {
inner.timers.lock().unwrap().insert(
deadline,
pid,
crate::timer::Reason::WaitTimeout { target, epoch },
);
match inner.timers.lock() {
Ok(mut timers) => timers.insert(
deadline,
pid,
crate::timer::Reason::WaitTimeout { target, epoch },
),
Err(e) => panic!("smarm: timers lock poisoned (core corrupt): {e}"),
}
});
}
@@ -412,7 +445,12 @@ pub fn send_after<A: crate::pid::Addressable>(
let fire = Box::new(move || {
let _ = crate::registry::send_to(dest, msg);
});
with_runtime(|inner| inner.timers.lock().unwrap().insert_send(deadline, dest.erase(), fire))
with_runtime(|inner| {
match inner.timers.lock() {
Ok(mut timers) => timers.insert_send(deadline, dest.erase(), fire),
Err(e) => panic!("smarm: timers lock poisoned (core corrupt): {e}"),
}
})
}
/// Deliver `msg` to whichever actor holds the name `dest` at fire time
@@ -429,7 +467,12 @@ pub fn send_after_named<M: Send + 'static>(
let fire = Box::new(move || {
let _ = crate::registry::send(dest, msg);
});
with_runtime(|inner| inner.timers.lock().unwrap().insert_send(deadline, armed_by, fire))
with_runtime(|inner| {
match inner.timers.lock() {
Ok(mut timers) => timers.insert_send(deadline, armed_by, fire),
Err(e) => panic!("smarm: timers lock poisoned (core corrupt): {e}"),
}
})
}
/// Deliver `msg` onto a channel the caller owns after `after`, rather than to a
@@ -453,14 +496,24 @@ pub(crate) fn send_after_to<T: Send + 'static>(
let fire = Box::new(move || {
let _ = tx.send(msg);
});
with_runtime(|inner| inner.timers.lock().unwrap().insert_send(deadline, armed_by, fire))
with_runtime(|inner| {
match inner.timers.lock() {
Ok(mut timers) => timers.insert_send(deadline, armed_by, fire),
Err(e) => panic!("smarm: timers lock poisoned (core corrupt): {e}"),
}
})
}
/// Cancel a timer armed by [`send_after`] / [`send_after_named`]. Returns
/// `true` if it was still pending (delivery now prevented), `false` if it had
/// already fired or been cancelled.
pub fn cancel_timer(id: crate::timer::TimerId) -> bool {
with_runtime(|inner| inner.timers.lock().unwrap().cancel(id))
with_runtime(|inner| {
match inner.timers.lock() {
Ok(mut timers) => timers.cancel(id),
Err(e) => panic!("smarm: timers lock poisoned (core corrupt): {e}"),
}
})
}
// ---------------------------------------------------------------------------
@@ -472,7 +525,10 @@ where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
let me = current_pid().expect("block_on_io() called outside an actor");
let me = match current_pid() {
Some(pid) => pid,
None => panic!("block_on_io() called outside an actor"),
};
let work: Box<dyn FnOnce() -> crate::io::IoResult + Send> = Box::new(move || {
let v: T = f();
Ok(Box::new(v) as Box<dyn std::any::Any + Send>)
@@ -481,24 +537,37 @@ where
let _np = NoPreempt::enter();
let epoch = begin_wait();
with_runtime(|inner| {
let mut io = inner.io.lock().unwrap();
io.as_mut().expect("io thread not started").submit(me, epoch, work);
let mut io = match inner.io.lock() {
Ok(io) => io,
Err(e) => panic!("smarm: io lock poisoned (core corrupt): {e}"),
};
match io.as_mut() {
Some(io) => io.submit(me, epoch, work),
None => panic!("io thread not started"),
}
});
park_current();
}
let result = with_runtime(|inner| {
let slot = inner.slot_at(me).expect("block_on_io: own slot vanished");
let slot = match inner.slot_at(me) {
Some(slot) => slot,
None => panic!("block_on_io: own slot vanished"),
};
let mut cold = slot.cold.lock();
debug_assert_eq!(
slot.generation(), me.generation(),
"block_on_io: own slot reused mid-park"
);
cold.pending_io_result
.take()
.expect("block_on_io: resumed without a result")
match cold.pending_io_result.take() {
Some(result) => result,
None => panic!("block_on_io: resumed without a result"),
}
});
match result {
Ok(any) => *any.downcast::<T>().expect("block_on_io: type mismatch"),
Ok(any) => match any.downcast::<T>() {
Ok(typed) => *typed,
Err(_) => panic!("block_on_io: type mismatch"),
},
Err(payload) => std::panic::resume_unwind(payload),
}
}
@@ -512,12 +581,21 @@ pub fn wait_writable(fd: std::os::fd::RawFd) -> std::io::Result<()> {
}
fn wait_fd(fd: std::os::fd::RawFd, readable: bool, writable: bool) -> std::io::Result<()> {
let me = current_pid().expect("wait_*() called outside an actor");
let me = match current_pid() {
Some(pid) => pid,
None => panic!("wait_*() called outside an actor"),
};
let _np = NoPreempt::enter();
let epoch = begin_wait();
with_runtime(|inner| {
let mut io = inner.io.lock().unwrap();
io.as_mut().expect("io thread not started").epoll_register(fd, me, epoch, readable, writable)
let mut io = match inner.io.lock() {
Ok(io) => io,
Err(e) => panic!("smarm: io lock poisoned (core corrupt): {e}"),
};
match io.as_mut() {
Some(io) => io.epoll_register(fd, me, epoch, readable, writable),
None => panic!("io thread not started"),
}
})?;
// If a terminal stop unwinds us out of the park below, the registration
@@ -535,7 +613,10 @@ fn wait_fd(fd: std::os::fd::RawFd, readable: bool, writable: bool) -> std::io::R
impl Drop for Dereg {
fn drop(&mut self) {
with_runtime(|inner| {
let mut io = inner.io.lock().unwrap();
let mut io = match inner.io.lock() {
Ok(io) => io,
Err(e) => panic!("smarm: io lock poisoned (core corrupt): {e}"),
};
if let Some(io) = io.as_mut() {
if io.waiters.get(&self.fd) == Some(&(self.me, self.epoch)) {
io.waiters.remove(&self.fd);
@@ -597,10 +678,16 @@ impl crate::channel::Selectable for FdArm {
return Ok(false);
}
with_runtime(|inner| {
let mut io = inner.io.lock().unwrap();
io.as_mut()
.expect("io thread not started")
.epoll_register(self.fd, pid, epoch, self.readable, self.writable)
let mut io = match inner.io.lock() {
Ok(io) => io,
Err(e) => panic!("smarm: io lock poisoned (core corrupt): {e}"),
};
match io.as_mut() {
Some(io) => {
io.epoll_register(self.fd, pid, epoch, self.readable, self.writable)
}
None => panic!("io thread not started"),
}
})?;
Ok(true)
}
@@ -621,7 +708,10 @@ impl crate::channel::Selectable for FdArm {
/// actor's fresh registration; in that case touch nothing.
fn sel_unregister(&self, pid: Pid, epoch: u32) {
with_runtime(|inner| {
let mut io = inner.io.lock().unwrap();
let mut io = match inner.io.lock() {
Ok(io) => io,
Err(e) => panic!("smarm: io lock poisoned (core corrupt): {e}"),
};
if let Some(io) = io.as_mut() {
if io.waiters.get(&self.fd) == Some(&(pid, epoch)) {
io.waiters.remove(&self.fd);