use crate::observer::{Observer, UserEvent};
use crate::task_id::TaskId;
use iceoryx2::port::notifier::Notifier as IxNotifier;
use iceoryx2::prelude::ipc;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Clone)]
pub struct Stoppable {
flag: Arc<AtomicBool>,
waker: Option<Arc<IxNotifier<ipc::Service>>>,
}
#[allow(unsafe_code, clippy::non_send_fields_in_send_ty)]
unsafe impl Send for Stoppable {}
impl Default for Stoppable {
fn default() -> Self {
Self {
flag: Arc::new(AtomicBool::new(false)),
waker: None,
}
}
}
impl Stoppable {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[doc(hidden)]
pub(crate) fn with_waker(waker: Arc<IxNotifier<ipc::Service>>) -> Self {
Self {
flag: Arc::new(AtomicBool::new(false)),
waker: Some(waker),
}
}
#[track_caller]
pub fn stop(&self) {
self.flag.store(true, Ordering::Release);
if let Some(w) = &self.waker {
let _ = w.notify();
}
}
#[must_use]
pub fn is_stopped(&self) -> bool {
self.flag.load(Ordering::Acquire)
}
}
impl core::fmt::Debug for Stoppable {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Stoppable")
.field("flag", &self.is_stopped())
.field("waker", &self.waker.is_some())
.finish()
}
}
#[non_exhaustive]
pub struct Context<'a> {
task_id: &'a TaskId,
stop: &'a Stoppable,
observer: &'a dyn Observer,
}
impl<'a> Context<'a> {
#[doc(hidden)]
pub fn new(task_id: &'a TaskId, stop: &'a Stoppable, observer: &'a dyn Observer) -> Self {
Self {
task_id,
stop,
observer,
}
}
pub const fn task_id(&self) -> &TaskId {
self.task_id
}
pub fn stop_executor(&self) {
self.stop.stop();
}
pub fn stoppable(&self) -> Stoppable {
self.stop.clone()
}
pub fn send_event(&self, ev: UserEvent) {
self.observer.on_send_event(self.task_id.clone(), ev);
}
}
#[cfg(test)]
pub struct ContextHarness {
task_id: TaskId,
stop: Stoppable,
}
#[cfg(test)]
impl ContextHarness {
pub(crate) fn new(id: impl Into<TaskId>) -> Self {
Self {
task_id: id.into(),
stop: Stoppable::new(),
}
}
pub(crate) fn context(&self) -> Context<'_> {
static NOOP: crate::observer::NoopObserver = crate::observer::NoopObserver;
Context::new(&self.task_id, &self.stop, &NOOP)
}
}