Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): introduce status_kind in the Context #133

Merged
merged 7 commits into from
Jul 17, 2024
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased] - ReleaseDate
### Added
- core: `Context::status_kind` API, now actors can read `ActorStatusKind` from the context.
- core: `is_*` methods on `ActorStatusKind` for each variant.
nerodono marked this conversation as resolved.
Show resolved Hide resolved
- Specify MSRV as 1.76.
- logger: log truncation up to the `max_line_size` configuration parameter ([#128]).
- core: directly accept never returning functions in `ActorGroup::exec()` ([#127]).
Expand Down
42 changes: 41 additions & 1 deletion elfo-core/src/actor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::{fmt, mem, sync::Arc};
use std::{
fmt, mem,
sync::{atomic, Arc},
};

use futures_intrusive::sync::ManualResetEvent;
use metrics::{decrement_gauge, increment_counter, increment_gauge};
Expand All @@ -7,6 +10,7 @@ use serde::{Deserialize, Serialize};
use tracing::{error, info, warn};

use crate::{
atomic_status_kind::AtomicActorStatusKind,
envelope::Envelope,
errors::{SendError, TrySendError},
group::TerminationPolicy,
Expand Down Expand Up @@ -96,6 +100,7 @@ impl fmt::Display for ActorStatus {
/// A list specifying statuses of actors. It's used with the [`ActorStatus`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActorStatusKind {
Normal,
Initializing,
Expand All @@ -105,6 +110,32 @@ pub enum ActorStatusKind {
Failed,
}

impl ActorStatusKind {
pub const fn is_normal(&self) -> bool {
nerodono marked this conversation as resolved.
Show resolved Hide resolved
matches!(self, Self::Normal)
}

pub const fn is_initializing(&self) -> bool {
matches!(self, Self::Initializing)
}

pub const fn is_terminating(&self) -> bool {
matches!(self, Self::Terminating)
}

pub const fn is_terminated(&self) -> bool {
matches!(self, Self::Terminated)
}

pub const fn is_alarming(&self) -> bool {
matches!(self, Self::Alarming)
}

pub const fn is_failed(&self) -> bool {
matches!(self, Self::Failed)
}
}
nerodono marked this conversation as resolved.
Show resolved Hide resolved

impl ActorStatusKind {
fn as_str(&self) -> &'static str {
match self {
Expand Down Expand Up @@ -182,6 +213,7 @@ pub(crate) struct Actor {
termination_policy: TerminationPolicy,
mailbox: Mailbox,
request_table: RequestTable,
status_kind: AtomicActorStatusKind,
control: RwLock<Control>,
finished: ManualResetEvent, // TODO: remove in favor of `status_subscription`?
status_subscription: Arc<SubscriptionManager>,
Expand All @@ -206,6 +238,7 @@ impl Actor {
status_subscription: Arc<SubscriptionManager>,
) -> Self {
Actor {
status_kind: AtomicActorStatusKind::from(ActorStatusKind::Initializing),
meta,
termination_policy,
mailbox: Mailbox::new(mailbox_config),
Expand Down Expand Up @@ -306,8 +339,15 @@ impl Actor {
self.control.write().restart_policy = policy;
}

pub(crate) fn status_kind(&self) -> ActorStatusKind {
nerodono marked this conversation as resolved.
Show resolved Hide resolved
self.status_kind.load(atomic::Ordering::Acquire)
}

// Note that this method should be called inside a right scope.
pub(crate) fn set_status(&self, status: ActorStatus) {
self.status_kind
.store(status.kind(), atomic::Ordering::Release);

let mut control = self.control.write();
let prev_status = mem::replace(&mut control.status, status.clone());

Expand Down
29 changes: 29 additions & 0 deletions elfo-core/src/atomic_status_kind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::mem;
use std::sync::atomic::{self, AtomicU8};

nerodono marked this conversation as resolved.
Show resolved Hide resolved
use crate::ActorStatusKind;

#[derive(Debug)]
#[repr(transparent)]
pub(crate) struct AtomicActorStatusKind(AtomicU8);

impl From<ActorStatusKind> for AtomicActorStatusKind {
fn from(value: ActorStatusKind) -> Self {
Self(AtomicU8::new(value as _))
}
}

impl AtomicActorStatusKind {
pub(crate) fn store(&self, kind: ActorStatusKind, ordering: atomic::Ordering) {
self.0.store(kind as u8, ordering);
}

pub(crate) fn load(&self, ordering: atomic::Ordering) -> ActorStatusKind {
let result = self.0.load(ordering);

// SAFETY: `ActorStatusKind` has `#[repr(u8)]` annotation. The only
// place where value may be changed is `Self::store`, which consumes `ActorStatusKind`, thus,
// guarantees that possibly invalid value cannot be stored
unsafe { mem::transmute::<u8, ActorStatusKind>(result) }
}
}
25 changes: 25 additions & 0 deletions elfo-core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use crate::{
routers::Singleton,
scope,
source::{SourceHandle, Sources, UnattachedSource},
ActorStatusKind,
};

use self::stats::Stats;
Expand Down Expand Up @@ -105,6 +106,30 @@ impl<C, K> Context<C, K> {
ward!(self.actor.as_ref().and_then(|o| o.as_actor())).set_status(status);
}

/// Gets the actor's status kind.
nerodono marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Example
/// ```
/// # use elfo_core as elfo;
/// # fn exec(ctx: elfo::Context) {
/// // if actor is terminating.
/// assert!(ctx.status_kind().is_terminating());
/// // if actor is alarming.
/// assert!(ctx.status_kind().is_alarming());
/// // and so on...
/// # }
/// ```
/// # Panics
/// - Panics when called on pruned context.
nerodono marked this conversation as resolved.
Show resolved Hide resolved
pub fn status_kind(&self) -> ActorStatusKind {
self.actor
.as_ref()
.expect("called `status_kind()` on pruned context")
.as_actor()
.expect("invariant")
.status_kind()
}

/// Overrides the group's default mailbox capacity, which set in the config.
///
/// Note: after restart the actor will be created from scratch, so this
Expand Down
2 changes: 2 additions & 0 deletions elfo-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ mod supervisor;
mod telemetry;
mod thread;

nerodono marked this conversation as resolved.
Show resolved Hide resolved
mod atomic_status_kind;

#[doc(hidden)]
pub mod _priv {
pub mod node {
Expand Down