use crate::bug_message::BUG_MESSAGE;
use crate::local_executor;
use crate::runtime::ExecutorSharedTaskList;
use crate::utils::{SpinLock, SpinLockGuard};
use crossbeam::utils::CachePadded;
use std::cell::UnsafeCell;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::{Acquire, Release};
use std::sync::Arc;
struct Inner {
current_version: CachePadded<AtomicUsize>,
processed_version: usize,
is_stopped: bool,
tasks_lists: Option<Vec<Arc<ExecutorSharedTaskList>>>,
}
pub(crate) struct SubscribedState {
inner: UnsafeCell<Inner>,
}
impl SubscribedState {
pub(crate) const fn new() -> Self {
Self {
inner: UnsafeCell::new(Inner {
current_version: CachePadded::new(AtomicUsize::new(1)),
processed_version: usize::MAX,
is_stopped: false,
tasks_lists: None,
}),
}
}
fn with_inner<R, F: FnOnce(&mut Inner) -> R>(&self, f: F) -> R {
unsafe { f(&mut *self.inner.get()) }
}
pub(crate) fn is_stopped(&self) -> bool {
self.with_inner(|inner| inner.is_stopped)
}
fn validate_tasks_lists(&self, executor_id: usize) {
self.with_inner(|inner| {
if inner.tasks_lists.is_none() {
return;
}
let tasks_lists = inner.tasks_lists.as_ref().unwrap();
let index = tasks_lists
.iter()
.position(|list| list.executor_id() == executor_id)
.unwrap();
let len = inner.tasks_lists.as_ref().unwrap().len() - 1;
if len == 0 {
inner.tasks_lists = Some(vec![]);
return;
}
let mut new_list = tasks_lists.clone();
new_list.remove(index);
new_list.rotate_left(index);
inner.tasks_lists = Some(new_list);
});
}
#[inline(always)]
pub(crate) fn check_version_and_update_if_needed(&self, executor_id: usize) {
self.with_inner(|inner| {
let current_version = inner.current_version.load(Acquire);
if inner.processed_version == current_version {
debug_assert_ne!(inner.processed_version, usize::MAX, "{BUG_MESSAGE}");
return;
}
inner.processed_version = current_version;
let shared_state = shared_state();
let found = shared_state
.states_of_alive_executors
.iter()
.any(|(alive_executor_id, _)| *alive_executor_id == executor_id);
if !found {
inner.is_stopped = true;
return;
}
if inner.tasks_lists.is_some() {
inner.tasks_lists = Some(shared_state.lists.clone());
self.validate_tasks_lists(executor_id);
}
});
}
pub(crate) unsafe fn with_tasks_lists<F>(&self, f: F)
where
F: FnOnce(&Vec<Arc<ExecutorSharedTaskList>>),
{
self.with_inner(|inner| {
f(unsafe { inner.tasks_lists.as_ref().unwrap_unchecked() });
});
}
}
unsafe impl Sync for SubscribedState {}
struct SharedState {
version: usize,
states_of_alive_executors: Vec<(usize, Arc<SubscribedState>)>,
lists: Vec<Arc<ExecutorSharedTaskList>>,
}
impl SharedState {
const fn new() -> Self {
Self {
version: 0,
states_of_alive_executors: Vec::new(),
lists: Vec::new(),
}
}
#[inline(always)]
pub(crate) fn register_local_executor(&mut self) {
self.version += 1;
let executor = local_executor();
if let Some(shared_task_list) = executor.shared_task_list() {
self.lists.push(shared_task_list.clone());
executor.subscribed_state().with_inner(|inner| {
inner.tasks_lists = Some(self.lists.clone());
});
let executor_id = executor.id();
executor
.subscribed_state()
.validate_tasks_lists(executor_id);
}
executor.subscribed_state().with_inner(|inner| {
inner.processed_version = self.version;
});
self.states_of_alive_executors
.push((executor.id(), executor.subscribed_state()));
executor.subscribed_state().with_inner(|inner| {
inner.is_stopped = false;
});
for (_, state) in &self.states_of_alive_executors {
state.with_inner(|inner| {
inner.current_version.store(self.version, Release);
});
}
}
#[inline(always)]
pub(crate) fn stop_executor(&mut self, id: usize) {
self.version += 1;
self.states_of_alive_executors
.retain(|(alive_executor_id, state)| {
state.with_inner(|inner| {
inner.current_version.store(self.version, Release);
});
*alive_executor_id != id
});
self.lists.retain(|list| list.executor_id() != id);
}
#[inline(always)]
pub(crate) fn stop_all_executors(&mut self) {
self.version += 1;
self.states_of_alive_executors.retain(|(_, state)| {
state.with_inner(|inner| {
inner.current_version.store(self.version, Release);
});
false
});
self.lists.clear();
}
}
unsafe impl Send for SharedState {}
static GLOBAL_STATE: SpinLock<SharedState> = SpinLock::new(SharedState::new());
fn shared_state() -> SpinLockGuard<'static, SharedState> {
GLOBAL_STATE.lock()
}
pub(crate) fn register_local_executor() {
shared_state().register_local_executor();
}
pub fn stop_executor(executor_id: usize) {
shared_state().stop_executor(executor_id);
}
pub fn stop_all_executors() {
shared_state().stop_all_executors();
}
#[cfg(test)]
mod tests {
use super::*;
use crate as orengine;
use crate::runtime::Config;
use crate::{local_executor, sleep, Executor};
use std::thread;
use std::time::Duration;
#[orengine::test::test_local]
fn test_stop_executor() {
thread::spawn(move || {
let ex = Executor::init_with_config(
Config::default()
.disable_work_sharing()
.disable_io_worker()
.disable_io_worker(),
);
ex.spawn_local(async {
println!("2");
stop_executor(local_executor().id());
});
println!("1");
ex.run();
println!("3");
});
sleep(Duration::from_millis(100)).await;
println!("4");
}
}