#![deny(
unreachable_code,
improper_ctypes_definitions,
future_incompatible,
nonstandard_style,
rust_2018_idioms,
clippy::perf,
clippy::correctness,
clippy::suspicious,
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::arithmetic_side_effects,
clippy::missing_safety_doc,
clippy::same_item_push,
clippy::implicit_clone,
clippy::all,
clippy::pedantic,
missing_docs,
clippy::nursery,
clippy::single_call_fn
)]
#![warn(
dead_code,
warnings,
clippy::dbg_macro,
clippy::todo,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap,
clippy::unnecessary_safety_comment
)]
#![allow(
unsafe_code,
unused_unsafe,
private_interfaces,
clippy::restriction,
clippy::inline_always,
unused_doc_comments,
clippy::empty_line_after_doc_comments,
clippy::missing_const_for_thread_local
)]
#![crate_name = "dtact"]
extern crate alloc;
pub use crate::api::config::set_deflection_threshold;
pub use crate::api::fiber::spawn_with_stack;
pub use crate::api::fiber::yield_to as yield_to_sync;
#[cfg(feature = "hw-acceleration")]
pub use crate::api::hw::cldemote;
#[cfg(feature = "hw-acceleration")]
pub use crate::api::hw::uintr_signal as uintr;
pub use crate::api::spawn;
#[macro_export]
macro_rules! spawn {
($fut:expr) => {{
#[allow(unused_imports)]
use $crate::api::spawner_traits::FallbackSpawner as _;
$crate::api::spawner_traits::SpawnerTag.spawn($fut)
}};
}
pub use crate::api::yield_now;
#[doc(hidden)]
pub use crate::api::yield_to;
pub use crate::api::yield_to as yield_to_async;
pub use crate::c_ffi::dtact_await;
pub use crate::c_ffi::dtact_handle_t;
#[doc(hidden)]
pub use crate::future_bridge::wait;
pub use crate::future_bridge::wait as dtact_wait;
pub use dtact_macros::dtact_init;
pub use dtact_macros::export_async;
pub use dtact_macros::export_fiber;
pub use dtact_macros::task;
#[doc(hidden)]
pub mod api;
#[doc(hidden)]
pub mod c_ffi;
#[doc(hidden)]
pub mod common_types;
#[doc(hidden)]
pub mod context_switch;
#[doc(hidden)]
pub mod dta_scheduler;
#[doc(hidden)]
pub mod future_bridge;
#[doc(hidden)]
pub mod memory_management;
#[doc(hidden)]
pub mod utils;
pub use api::*;
#[doc(hidden)]
pub struct Runtime {
pub scheduler: dta_scheduler::DtaScheduler,
pub pool: memory_management::ContextPool,
pub started: crate::sync::atomic::AtomicBool,
pub shutdown: crate::sync::atomic::AtomicBool,
}
impl Runtime {
pub fn start(&'static self) {
if self
.started
.swap(true, crate::sync::atomic::Ordering::SeqCst)
{
return;
}
let workers_count = self.scheduler.workers.len();
for i in 0..workers_count {
let sched: &'static dta_scheduler::DtaScheduler = &self.scheduler;
let pool: &'static memory_management::ContextPool = &self.pool;
let shutdown: &'static crate::sync::atomic::AtomicBool = &self.shutdown;
let my_id = i;
std::thread::Builder::new()
.name(format!("dtact-worker-{my_id}"))
.spawn(move || {
crate::dta_scheduler::DtaScheduler::run_worker_static(
sched, my_id, pool, shutdown,
);
})
.expect("Failed to spawn Dtact worker thread");
}
}
}
#[doc(hidden)]
pub static GLOBAL_RUNTIME: std::sync::OnceLock<Runtime> = std::sync::OnceLock::new();
#[doc(hidden)]
pub static HEAP_ESCAPED_SPAWNS: core::sync::atomic::AtomicU64 =
core::sync::atomic::AtomicU64::new(0);
#[inline(always)]
pub(crate) fn wake_fiber(origin_core: usize, fiber_index: u32) {
let runtime = GLOBAL_RUNTIME
.get()
.expect("dtact::wake_fiber() invoked before Runtime Initialization");
let pool = &runtime.pool;
let ctx_ptr = pool.get_context_ptr(fiber_index);
let pinned = matches!(
unsafe { (*ctx_ptr).mode },
common_types::TopologyMode::Pinned
) || matches!(
unsafe { (*ctx_ptr).affinity },
crate::api::topology::Affinity::SameCore
);
let affinity = unsafe { (*ctx_ptr).affinity };
loop {
type EnqFn = fn(
&dta_scheduler::DtaScheduler,
usize,
u64,
u32,
crate::api::topology::Affinity,
) -> bool;
const ENQUEUE_FNS: [EnqFn; 2] = [enqueue_deflect_shim, enqueue_pinned_shim];
let success = ENQUEUE_FNS[usize::from(pinned)](
&runtime.scheduler,
origin_core,
u64::from(fiber_index),
fiber_index,
affinity,
);
if success {
return;
}
backpressure_yield();
}
}
#[inline(always)]
fn enqueue_pinned_shim(
sched: &dta_scheduler::DtaScheduler,
target: usize,
_flow: u64,
task: u32,
_affinity: crate::api::topology::Affinity,
) -> bool {
sched.enqueue_pinned(target, task)
}
#[inline(always)]
fn enqueue_deflect_shim(
sched: &dta_scheduler::DtaScheduler,
source: usize,
flow: u64,
task: u32,
affinity: crate::api::topology::Affinity,
) -> bool {
sched.enqueue_deflect(source, flow, task, affinity)
}
#[inline(always)]
pub(crate) fn awaken_fiber_by_index(target_worker: usize, fiber_index: u32) {
let runtime = GLOBAL_RUNTIME
.get()
.expect("dtact::awaken_fiber_by_index() invoked before Runtime Initialization");
let ctx_ptr = runtime.pool.get_context_ptr(fiber_index);
let prev = unsafe {
(*ctx_ptr).state.swap(
crate::memory_management::FiberStatus::Notified as u32,
core::sync::atomic::Ordering::AcqRel,
)
};
if prev == crate::memory_management::FiberStatus::Yielded as u32 {
wake_fiber(target_worker, fiber_index);
}
}
#[inline(always)]
pub(crate) fn wake_waiter_handle(packed: u64) {
let waiter = packed & !(1u64 << 63);
let fiber_index = (waiter & 0xFFFF_FFFF) as u32;
let target_worker = (waiter >> 32) as usize;
awaken_fiber_by_index(target_worker, fiber_index);
}
#[inline]
fn backpressure_yield() {
let ctx_ptr = crate::future_bridge::CURRENT_FIBER.with(std::cell::Cell::get);
if ctx_ptr.is_null() {
for _ in 0..32 {
core::hint::spin_loop();
}
std::thread::yield_now();
} else {
unsafe {
let ctx = &mut *ctx_ptr;
ctx.state.store(
crate::memory_management::FiberStatus::Notified as u32,
crate::sync::atomic::Ordering::Release,
);
(ctx.switch_fn)(&raw mut ctx.regs, &raw const ctx.executor_regs);
}
}
}
#[cfg(loom)]
#[allow(unused_imports)]
pub(crate) mod sync {
pub(crate) mod atomic {
pub(crate) use loom::sync::atomic::{
AtomicBool, AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize, Ordering,
};
}
pub(crate) use loom::sync::Arc;
pub(crate) use loom::thread;
}
#[cfg(not(loom))]
#[allow(unused_imports)]
pub(crate) mod sync {
pub mod atomic {
pub use core::sync::atomic::{
AtomicBool, AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize, Ordering,
};
}
pub use std::sync::Arc;
pub use std::thread;
}
#[allow(clippy::mixed_attributes_style)]
#[cfg_attr(miri, ignore)]
#[doc(hidden)]
mod readme {
#![doc = include_str!("../README.md")]
}