v0.3.9-pre.002
This commit is contained in:
12
crates/ksp-worker-api/src/error.rs
Normal file
12
crates/ksp-worker-api/src/error.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
// file: crates/ksp-worker-api/src/error.rs
|
||||
// version: 1
|
||||
|
||||
/// Error code used when a Worker identifier violates its bounded safe-code contract.
|
||||
pub const ERROR_CODE_WORKER_ID_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("worker_api", "worker_id_invalid");
|
||||
/// Error code used when a Worker kind code violates its bounded safe-code contract.
|
||||
pub const ERROR_CODE_WORKER_KIND_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("worker_api", "worker_kind_invalid");
|
||||
/// Error code used when a Worker snapshot sequence cannot advance without wrapping.
|
||||
pub const ERROR_CODE_WORKER_SNAPSHOT_SEQUENCE_EXHAUSTED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("worker_api", "worker_snapshot_sequence_exhausted");
|
||||
/// Error code used when a requested Worker lifecycle transition is not allowed.
|
||||
pub const ERROR_CODE_WORKER_TRANSITION_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("worker_api", "worker_transition_invalid");
|
||||
76
crates/ksp-worker-api/src/identity.rs
Normal file
76
crates/ksp-worker-api/src/identity.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
// file: crates/ksp-worker-api/src/identity.rs
|
||||
// version: 1
|
||||
|
||||
/// Maximum UTF-8 byte length admitted for one Worker identifier.
|
||||
pub const MAX_WORKER_ID_BYTES: usize = 128;
|
||||
/// Maximum UTF-8 byte length admitted for one Worker kind code.
|
||||
pub const MAX_WORKER_KIND_CODE_BYTES: usize = 128;
|
||||
|
||||
/// Bounded caller-supplied identity of one logical Worker instance.
|
||||
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct WorkerId(std::string::String);
|
||||
|
||||
impl WorkerId {
|
||||
/// Creates one non-empty Worker identifier using the KSP safe-code alphabet.
|
||||
pub fn new(value: impl std::convert::Into<std::string::String>) -> crate::Result<Self> {
|
||||
let value = value.into();
|
||||
if !valid_worker_code(value.as_str(), crate::MAX_WORKER_ID_BYTES) {
|
||||
return std::result::Result::Err(identity_error(crate::ERROR_CODE_WORKER_ID_INVALID, "worker_id"));
|
||||
}
|
||||
return std::result::Result::Ok(Self(value));
|
||||
}
|
||||
|
||||
/// Returns the validated Worker identifier.
|
||||
#[must_use]
|
||||
pub fn as_str(&self) -> &str {
|
||||
return self.0.as_str();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WorkerId {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.write_str("WorkerId(..)");
|
||||
}
|
||||
}
|
||||
|
||||
/// Bounded stable code identifying one concrete family of Workers.
|
||||
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct WorkerKindCode(std::string::String);
|
||||
|
||||
impl WorkerKindCode {
|
||||
/// Creates one non-empty Worker kind using the KSP safe-code alphabet.
|
||||
pub fn new(value: impl std::convert::Into<std::string::String>) -> crate::Result<Self> {
|
||||
let value = value.into();
|
||||
if !valid_worker_code(value.as_str(), crate::MAX_WORKER_KIND_CODE_BYTES) {
|
||||
return std::result::Result::Err(identity_error(crate::ERROR_CODE_WORKER_KIND_INVALID, "worker_kind"));
|
||||
}
|
||||
return std::result::Result::Ok(Self(value));
|
||||
}
|
||||
|
||||
/// Returns the validated stable Worker kind code.
|
||||
#[must_use]
|
||||
pub fn as_str(&self) -> &str {
|
||||
return self.0.as_str();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WorkerKindCode {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.debug_tuple("WorkerKindCode").field(&self.0).finish();
|
||||
}
|
||||
}
|
||||
|
||||
fn identity_error(code: crate::ErrorCode, field: &'static str) -> crate::Error {
|
||||
return crate::Error::new(code, "invalid bounded Worker identity").with_context("field", field);
|
||||
}
|
||||
|
||||
fn valid_worker_code(value: &str, maximum_len: usize) -> bool {
|
||||
if value.is_empty() || value.len() > maximum_len {
|
||||
return false;
|
||||
}
|
||||
return value.bytes().all(|byte| return byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-' | b'.' | b':'));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/identity.rs"]
|
||||
mod tests;
|
||||
62
crates/ksp-worker-api/src/lib.rs
Normal file
62
crates/ksp-worker-api/src/lib.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
// file: crates/ksp-worker-api/src/lib.rs
|
||||
// version: 1
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
//! Passive runtime-neutral lifecycle contracts for continuous KSP Workers.
|
||||
//!
|
||||
//! This foundation owns bounded Worker identity, explicit continuous lifecycle
|
||||
//! transitions, cooperative stop intent and a fixed latest-value observation
|
||||
//! contract. Concrete runtimes, restart policy, Transport, Store, Config, Jobs
|
||||
//! and domain-specific Worker behavior remain outside this crate.
|
||||
|
||||
mod error;
|
||||
mod identity;
|
||||
mod lifecycle;
|
||||
mod snapshot;
|
||||
mod stop;
|
||||
|
||||
/// Error code used when a Worker identifier violates its bounded safe-code contract.
|
||||
pub use self::error::ERROR_CODE_WORKER_ID_INVALID;
|
||||
/// Error code used when a Worker kind code violates its bounded safe-code contract.
|
||||
pub use self::error::ERROR_CODE_WORKER_KIND_INVALID;
|
||||
/// Error code used when a Worker snapshot sequence cannot advance without wrapping.
|
||||
pub use self::error::ERROR_CODE_WORKER_SNAPSHOT_SEQUENCE_EXHAUSTED;
|
||||
/// Error code used when a requested Worker lifecycle transition is not allowed.
|
||||
pub use self::error::ERROR_CODE_WORKER_TRANSITION_INVALID;
|
||||
/// Maximum UTF-8 byte length admitted for one Worker identifier.
|
||||
pub use self::identity::MAX_WORKER_ID_BYTES;
|
||||
/// Maximum UTF-8 byte length admitted for one Worker kind code.
|
||||
pub use self::identity::MAX_WORKER_KIND_CODE_BYTES;
|
||||
/// Bounded caller-supplied identity of one logical Worker instance.
|
||||
pub use self::identity::WorkerId;
|
||||
/// Bounded stable code identifying one concrete family of Workers.
|
||||
pub use self::identity::WorkerKindCode;
|
||||
/// Minimal generic activity classification for a continuous Worker.
|
||||
pub use self::lifecycle::WorkerActivity;
|
||||
/// Operational health classification independent from Worker lifecycle phase.
|
||||
pub use self::lifecycle::WorkerHealth;
|
||||
/// Passive owner of one Worker identity and its validated lifecycle state.
|
||||
pub use self::lifecycle::WorkerLifecycle;
|
||||
/// Current lifecycle state of one continuous Worker.
|
||||
pub use self::lifecycle::WorkerState;
|
||||
/// Fixed common latest-value snapshot exposed by every Worker implementation.
|
||||
pub use self::snapshot::WorkerSnapshot;
|
||||
/// Runtime-neutral future returned while observing a latest-value Worker snapshot source.
|
||||
pub use self::snapshot::WorkerSnapshotFuture;
|
||||
/// Monotone sequence attached to one latest-value Worker snapshot stream.
|
||||
pub use self::snapshot::WorkerSnapshotSequence;
|
||||
/// Runtime-neutral read and change-wait contract for one latest-value Worker snapshot stream.
|
||||
pub use self::snapshot::WorkerSnapshotSource;
|
||||
/// Runtime-neutral cloneable token carrying cooperative Worker stop intent.
|
||||
pub use self::stop::WorkerStopToken;
|
||||
/// Common KSP error type used by Worker-facing contracts.
|
||||
pub use ksp_core_lib::Error;
|
||||
/// Stable structured code identifying a KSP error category and condition.
|
||||
pub use ksp_core_lib::ErrorCode;
|
||||
/// Structured contextual field attached to a KSP error.
|
||||
pub use ksp_core_lib::ErrorContext;
|
||||
/// Common KSP result alias using [`Error`].
|
||||
pub use ksp_core_lib::Result;
|
||||
199
crates/ksp-worker-api/src/lifecycle.rs
Normal file
199
crates/ksp-worker-api/src/lifecycle.rs
Normal file
@@ -0,0 +1,199 @@
|
||||
// file: crates/ksp-worker-api/src/lifecycle.rs
|
||||
// version: 1
|
||||
|
||||
/// Current lifecycle state of one continuous Worker.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum WorkerState {
|
||||
/// The Worker exists but has not started initialization.
|
||||
Created,
|
||||
/// The Worker is initializing resources before entering steady service.
|
||||
Starting,
|
||||
/// The Worker is actively providing its continuous service.
|
||||
Running,
|
||||
/// The Worker observed stop intent and is draining or releasing resources.
|
||||
Stopping,
|
||||
/// The Worker reached its normal terminal stopped state.
|
||||
Stopped,
|
||||
/// The Worker reached a terminal fault classified by one stable KSP error code.
|
||||
Faulted(crate::ErrorCode),
|
||||
}
|
||||
|
||||
impl WorkerState {
|
||||
/// Returns the stable safe lifecycle code without rendering Worker data.
|
||||
#[must_use]
|
||||
pub const fn code(&self) -> &'static str {
|
||||
return match self {
|
||||
Self::Created => "created",
|
||||
Self::Starting => "starting",
|
||||
Self::Running => "running",
|
||||
Self::Stopping => "stopping",
|
||||
Self::Stopped => "stopped",
|
||||
Self::Faulted(_) => "faulted",
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the terminal fault code when the state is [`Self::Faulted`].
|
||||
#[must_use]
|
||||
pub const fn fault_code(&self) -> std::option::Option<crate::ErrorCode> {
|
||||
return match self {
|
||||
Self::Faulted(code) => std::option::Option::Some(*code),
|
||||
_ => std::option::Option::None,
|
||||
};
|
||||
}
|
||||
|
||||
/// Reports whether no later lifecycle transition is permitted.
|
||||
#[must_use]
|
||||
pub const fn is_terminal(&self) -> bool {
|
||||
return matches!(self, Self::Stopped | Self::Faulted(_));
|
||||
}
|
||||
}
|
||||
|
||||
/// Operational health classification independent from Worker lifecycle phase.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum WorkerHealth {
|
||||
/// Health has not yet been established or cannot currently be classified.
|
||||
Unknown,
|
||||
/// The Worker is operating within its expected healthy envelope.
|
||||
Healthy,
|
||||
/// The Worker is operating with a known degradation while service remains available.
|
||||
Degraded,
|
||||
/// The Worker is currently unable to satisfy its expected service health contract.
|
||||
Unhealthy,
|
||||
}
|
||||
|
||||
impl WorkerHealth {
|
||||
/// Returns the stable safe code for this health classification.
|
||||
#[must_use]
|
||||
pub const fn code(&self) -> &'static str {
|
||||
return match self {
|
||||
Self::Unknown => "unknown",
|
||||
Self::Healthy => "healthy",
|
||||
Self::Degraded => "degraded",
|
||||
Self::Unhealthy => "unhealthy",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal generic activity classification for a continuous Worker.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum WorkerActivity {
|
||||
/// Activity has not yet been established or cannot currently be classified.
|
||||
Unknown,
|
||||
/// The Worker is alive but not currently processing concrete work.
|
||||
Idle,
|
||||
/// The Worker is currently processing concrete work.
|
||||
Active,
|
||||
}
|
||||
|
||||
impl WorkerActivity {
|
||||
/// Returns the stable safe code for this activity classification.
|
||||
#[must_use]
|
||||
pub const fn code(&self) -> &'static str {
|
||||
return match self {
|
||||
Self::Unknown => "unknown",
|
||||
Self::Idle => "idle",
|
||||
Self::Active => "active",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Passive owner of one Worker identity and its validated lifecycle state.
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub struct WorkerLifecycle {
|
||||
id: crate::WorkerId,
|
||||
kind: crate::WorkerKindCode,
|
||||
state: crate::WorkerState,
|
||||
}
|
||||
|
||||
impl WorkerLifecycle {
|
||||
/// Creates one lifecycle in [`WorkerState::Created`] state.
|
||||
#[must_use]
|
||||
pub const fn new(id: crate::WorkerId, kind: crate::WorkerKindCode) -> Self {
|
||||
return Self { id, kind, state: crate::WorkerState::Created };
|
||||
}
|
||||
|
||||
/// Returns the logical Worker identity.
|
||||
#[must_use]
|
||||
pub const fn id(&self) -> &crate::WorkerId {
|
||||
return &self.id;
|
||||
}
|
||||
|
||||
/// Returns the stable Worker family code.
|
||||
#[must_use]
|
||||
pub const fn kind(&self) -> &crate::WorkerKindCode {
|
||||
return &self.kind;
|
||||
}
|
||||
|
||||
/// Returns the current lifecycle state.
|
||||
#[must_use]
|
||||
pub const fn state(&self) -> crate::WorkerState {
|
||||
return self.state;
|
||||
}
|
||||
|
||||
/// Begins initialization of a newly created Worker.
|
||||
pub fn start(&mut self) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Starting);
|
||||
}
|
||||
|
||||
/// Records that initialization completed and steady service is running.
|
||||
pub fn mark_running(&mut self) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Running);
|
||||
}
|
||||
|
||||
/// Records that a starting or running Worker observed cooperative stop intent.
|
||||
pub fn mark_stopping(&mut self) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Stopping);
|
||||
}
|
||||
|
||||
/// Records normal terminal stop after draining, or before initialization began.
|
||||
pub fn mark_stopped(&mut self) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Stopped);
|
||||
}
|
||||
|
||||
/// Records a terminal fault using only a stable KSP error code.
|
||||
pub fn fault(&mut self, code: crate::ErrorCode) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Faulted(code));
|
||||
}
|
||||
|
||||
fn transition(&mut self, target: crate::WorkerState) -> crate::Result<()> {
|
||||
if !allowed_transition(self.state, target) {
|
||||
return std::result::Result::Err(transition_error(self.state, target));
|
||||
}
|
||||
self.state = target;
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WorkerLifecycle {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.debug_struct("WorkerLifecycle").field("id", &self.id).field("kind", &self.kind).field("state", &self.state).finish();
|
||||
}
|
||||
}
|
||||
|
||||
fn allowed_transition(source: crate::WorkerState, target: crate::WorkerState) -> bool {
|
||||
return matches!(
|
||||
(source, target),
|
||||
(crate::WorkerState::Created, crate::WorkerState::Starting)
|
||||
| (crate::WorkerState::Created, crate::WorkerState::Stopped)
|
||||
| (crate::WorkerState::Starting, crate::WorkerState::Running)
|
||||
| (crate::WorkerState::Starting, crate::WorkerState::Stopping)
|
||||
| (crate::WorkerState::Starting, crate::WorkerState::Faulted(_))
|
||||
| (crate::WorkerState::Running, crate::WorkerState::Stopping)
|
||||
| (crate::WorkerState::Running, crate::WorkerState::Faulted(_))
|
||||
| (crate::WorkerState::Stopping, crate::WorkerState::Stopped)
|
||||
| (crate::WorkerState::Stopping, crate::WorkerState::Faulted(_))
|
||||
);
|
||||
}
|
||||
|
||||
fn transition_error(source: crate::WorkerState, target: crate::WorkerState) -> crate::Error {
|
||||
return crate::Error::new(crate::ERROR_CODE_WORKER_TRANSITION_INVALID, "invalid Worker lifecycle transition")
|
||||
.with_context("source_state", source.code())
|
||||
.with_context("target_state", target.code());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/lifecycle.rs"]
|
||||
mod tests;
|
||||
138
crates/ksp-worker-api/src/snapshot.rs
Normal file
138
crates/ksp-worker-api/src/snapshot.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
// file: crates/ksp-worker-api/src/snapshot.rs
|
||||
// version: 1
|
||||
|
||||
/// Monotone sequence attached to one latest-value Worker snapshot stream.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct WorkerSnapshotSequence(u64);
|
||||
|
||||
impl WorkerSnapshotSequence {
|
||||
/// Creates the initial sequence position for one Worker snapshot stream.
|
||||
#[must_use]
|
||||
pub const fn initial() -> Self {
|
||||
return Self(0);
|
||||
}
|
||||
|
||||
/// Returns the opaque numeric position carried by this sequence.
|
||||
#[must_use]
|
||||
pub const fn value(&self) -> u64 {
|
||||
return self.0;
|
||||
}
|
||||
|
||||
/// Advances the sequence exactly once or reports exhaustion without wrapping.
|
||||
pub fn next(&self) -> crate::Result<Self> {
|
||||
let next = match self.0.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(
|
||||
crate::Error::new(crate::ERROR_CODE_WORKER_SNAPSHOT_SEQUENCE_EXHAUSTED, "Worker snapshot sequence exhausted")
|
||||
.with_context("sequence", self.0.to_string()),
|
||||
);
|
||||
},
|
||||
};
|
||||
return std::result::Result::Ok(Self(next));
|
||||
}
|
||||
|
||||
/// Reports whether this sequence is strictly newer than an observed sequence.
|
||||
#[must_use]
|
||||
pub const fn is_after(&self, observed: Self) -> bool {
|
||||
return self.0 > observed.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Fixed common latest-value snapshot exposed by every Worker implementation.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct WorkerSnapshot {
|
||||
id: crate::WorkerId,
|
||||
kind: crate::WorkerKindCode,
|
||||
sequence: crate::WorkerSnapshotSequence,
|
||||
state: crate::WorkerState,
|
||||
health: crate::WorkerHealth,
|
||||
activity: crate::WorkerActivity,
|
||||
}
|
||||
|
||||
impl WorkerSnapshot {
|
||||
/// Creates one immutable common Worker snapshot from already validated values.
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
id: crate::WorkerId,
|
||||
kind: crate::WorkerKindCode,
|
||||
sequence: crate::WorkerSnapshotSequence,
|
||||
state: crate::WorkerState,
|
||||
health: crate::WorkerHealth,
|
||||
activity: crate::WorkerActivity,
|
||||
) -> Self {
|
||||
return Self { id, kind, sequence, state, health, activity };
|
||||
}
|
||||
|
||||
/// Returns the logical Worker identity.
|
||||
#[must_use]
|
||||
pub const fn id(&self) -> &crate::WorkerId {
|
||||
return &self.id;
|
||||
}
|
||||
|
||||
/// Returns the stable Worker family code.
|
||||
#[must_use]
|
||||
pub const fn kind(&self) -> &crate::WorkerKindCode {
|
||||
return &self.kind;
|
||||
}
|
||||
|
||||
/// Returns the monotone sequence of this latest value.
|
||||
#[must_use]
|
||||
pub const fn sequence(&self) -> crate::WorkerSnapshotSequence {
|
||||
return self.sequence;
|
||||
}
|
||||
|
||||
/// Returns the lifecycle state represented by this snapshot.
|
||||
#[must_use]
|
||||
pub const fn state(&self) -> crate::WorkerState {
|
||||
return self.state;
|
||||
}
|
||||
|
||||
/// Returns the operational health represented by this snapshot.
|
||||
#[must_use]
|
||||
pub const fn health(&self) -> crate::WorkerHealth {
|
||||
return self.health;
|
||||
}
|
||||
|
||||
/// Returns the generic activity represented by this snapshot.
|
||||
#[must_use]
|
||||
pub const fn activity(&self) -> crate::WorkerActivity {
|
||||
return self.activity;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WorkerSnapshot {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("WorkerSnapshot")
|
||||
.field("id", &self.id)
|
||||
.field("kind", &self.kind)
|
||||
.field("sequence", &self.sequence)
|
||||
.field("state", &self.state)
|
||||
.field("health", &self.health)
|
||||
.field("activity", &self.activity)
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime-neutral future returned while observing a latest-value Worker snapshot source.
|
||||
pub type WorkerSnapshotFuture<'a> = std::pin::Pin<std::boxed::Box<dyn std::future::Future<Output = crate::WorkerSnapshot> + std::marker::Send + 'a>>;
|
||||
|
||||
/// Runtime-neutral read and change-wait contract for one latest-value Worker snapshot stream.
|
||||
pub trait WorkerSnapshotSource: std::marker::Send + std::marker::Sync {
|
||||
/// Returns the complete current common Worker snapshot without replaying prior updates.
|
||||
#[must_use]
|
||||
fn current(&self) -> crate::WorkerSnapshot;
|
||||
|
||||
/// Waits for a snapshot newer than `observed`, returning the complete current value after coalescing intermediate updates.
|
||||
fn wait_for_change(&self, observed: crate::WorkerSnapshotSequence) -> crate::WorkerSnapshotFuture<'_>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn exhausted_snapshot_sequence() -> crate::WorkerSnapshotSequence {
|
||||
return WorkerSnapshotSequence(u64::MAX);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/snapshot.rs"]
|
||||
mod tests;
|
||||
44
crates/ksp-worker-api/src/stop.rs
Normal file
44
crates/ksp-worker-api/src/stop.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
// file: crates/ksp-worker-api/src/stop.rs
|
||||
// version: 1
|
||||
|
||||
/// Runtime-neutral cloneable token carrying cooperative Worker stop intent.
|
||||
#[derive(Clone)]
|
||||
pub struct WorkerStopToken {
|
||||
requested: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||
}
|
||||
|
||||
impl WorkerStopToken {
|
||||
/// Creates a token with no stop request.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
return Self { requested: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)) };
|
||||
}
|
||||
|
||||
/// Requests stop and returns `true` only for the first request shared by all clones.
|
||||
#[must_use]
|
||||
pub fn request_stop(&self) -> bool {
|
||||
return !self.requested.swap(true, std::sync::atomic::Ordering::AcqRel);
|
||||
}
|
||||
|
||||
/// Reports whether stop has been requested through any clone.
|
||||
#[must_use]
|
||||
pub fn is_stop_requested(&self) -> bool {
|
||||
return self.requested.load(std::sync::atomic::Ordering::Acquire);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for WorkerStopToken {
|
||||
fn default() -> Self {
|
||||
return Self::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WorkerStopToken {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.debug_struct("WorkerStopToken").field("stop_requested", &self.is_stop_requested()).finish();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/stop.rs"]
|
||||
mod tests;
|
||||
Reference in New Issue
Block a user