use std::sync::Arc;
pub(crate) const QUARTZ_EVERY: u32 = 16;
pub(crate) const DRIVE_BOUND: u64 = 1_000_000;
#[derive(Clone, Debug)]
pub(crate) struct Tuning {
pub(crate) driver_interval: u32,
pub(crate) drive_bound: u64,
pub(crate) inbox_interval: u32,
pub(crate) max_io_events: usize,
pub(crate) lifo: bool,
pub(crate) hooks: ThreadHooks,
pub(crate) blocking: Option<Arc<crate::sync::internal::AtomicUsize>>,
#[cfg(ring)]
pub(crate) zero_copy_min: usize,
pub(crate) record_latencies: bool,
#[cfg(feature = "io-stats")]
pub(crate) count_io: bool,
}
impl Default for Tuning {
fn default() -> Self {
Self {
driver_interval: super::shard::DRIVER_INTERVAL,
drive_bound: DRIVE_BOUND,
inbox_interval: super::shard::INBOX_INTERVAL,
max_io_events: usize::MAX,
lifo: true,
hooks: ThreadHooks::default(),
blocking: None,
#[cfg(ring)]
zero_copy_min: crate::io::source::ring::SEND_ZC_MIN,
record_latencies: false,
#[cfg(feature = "io-stats")]
count_io: false,
}
}
}
pub(crate) type Hook = Arc<dyn Fn() + Send + Sync>;
#[derive(Clone, Default)]
pub(crate) struct ThreadHooks {
pub(crate) start: Option<Hook>,
pub(crate) stop: Option<Hook>,
pub(crate) park: Option<Hook>,
pub(crate) unpark: Option<Hook>,
}
impl ThreadHooks {
pub(crate) fn started(&self) -> Stopping {
if let Some(start) = &self.start {
call_caught(start);
}
Stopping(self.stop.clone())
}
#[inline(always)]
pub(crate) fn parked(&self) {
if let Some(park) = &self.park {
call_caught(park);
}
}
#[inline(always)]
pub(crate) fn unparked(&self) {
if let Some(unpark) = &self.unpark {
call_caught(unpark);
}
}
}
#[cold]
#[inline(never)]
fn call_caught(hook: &Hook) {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| hook()));
}
#[cold]
#[inline(never)]
pub(crate) fn name_caught(name: &(dyn Fn() -> String + Send + Sync), fallback: &str) -> String {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(name))
.map_or_else(|_| fallback.to_string(), spawnable)
}
pub(crate) fn spawnable(mut name: String) -> String {
if let Some(at) = name.find('\0') {
name.truncate(at);
}
name
}
pub(crate) struct Stopping(Option<Hook>);
impl Drop for Stopping {
fn drop(&mut self) {
if let Some(stop) = &self.0 {
call_caught(stop);
}
}
}
impl core::fmt::Debug for ThreadHooks {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ThreadHooks")
.field("start", &self.start.is_some())
.field("stop", &self.stop.is_some())
.field("park", &self.park.is_some())
.field("unpark", &self.unpark.is_some())
.finish()
}
}
#[derive(Debug)]
pub struct TaskMeta<'a> {
id: crate::task::Id,
name: Option<Arc<str>>,
_task: core::marker::PhantomData<&'a ()>,
}
impl TaskMeta<'_> {
pub(crate) fn new(id: crate::task::Id) -> Self {
Self {
id,
name: super::task::names::get(id),
_task: core::marker::PhantomData,
}
}
pub fn id(&self) -> crate::task::Id {
self.id
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
}
pub(crate) type TaskHook = Arc<dyn Fn(&TaskMeta<'_>) + Send + Sync>;
#[derive(Clone, Default)]
pub(crate) struct TaskHooks {
pub(crate) spawn: Option<TaskHook>,
pub(crate) before_poll: Option<TaskHook>,
pub(crate) after_poll: Option<TaskHook>,
pub(crate) terminate: Option<TaskHook>,
pub(crate) shutdown: Option<std::sync::Weak<super::runtime::Inner>>,
pub(crate) visiting: Option<Arc<super::sched::Visiting>>,
}
impl TaskHooks {
pub(crate) fn any(&self) -> bool {
self.spawn.is_some()
|| self.before_poll.is_some()
|| self.after_poll.is_some()
|| self.terminate.is_some()
|| self.shutdown.is_some()
}
}
pub(crate) static ANY_TASK_HOOKS: core::sync::atomic::AtomicBool =
core::sync::atomic::AtomicBool::new(false);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum UnhandledPanic {
#[default]
Ignore,
ShutdownRuntime,
}
pub(crate) fn call(hook: Option<&TaskHook>, id: crate::task::Id) {
if let Some(hook) = hook {
hook(&TaskMeta::new(id));
}
}
pub(crate) fn call_task_caught(
hook: Option<&TaskHook>,
id: crate::task::Id,
) -> Option<super::caught::Panic> {
let hook = hook?;
super::caught::catch(|| hook(&TaskMeta::new(id)))
}