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:
+129
-39
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user