1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! One process per role, bound to one run of its owner.
//!
//! An advisory lock keeps a helper process unique — until the process that
//! spawned it goes away. The orphan keeps the lock, so nothing can start the
//! helper the new run needs, and it does not look broken: it looks exactly
//! like a healthy tenant.
//!
//! This crate pairs the lock with an identity — which run a tenant serves — so
//! an onlooker can recognize an obsolete tenant ([`verdict`]) and the tenant
//! can recognize that its own run is over ([`Allegiance`]).
//!
//! Roles come in three kinds: peers negotiate with each other, subordinates
//! serve one run of an owner, and user-owned ones are never evicted at all.
//! This crate implements the subordinate kind.
//!
//! # Two contracts
//!
//! 1. **[`Compat`] must be readable across incompatible versions.** A helper
//! decides whether to stay by reading its owner's identity, so that read
//! cannot depend on the two agreeing: give `Compat` a frozen channel — a
//! protocol method whose position and encoding never change — and read
//! [`Run`] only once the fingerprints match.
//! 2. **The claim record must be tolerant.** [`Record::parse`] ignores unknown
//! keys, so a record from a newer build still reads, and an unreadable one
//! degrades to [`Occupancy::HeldAnonymously`] rather than to an error.
//!
//! # Features
//!
//! The core is dependency-free and decides only: it never spawns, signals,
//! exits, or resolves a path. Each feature adds one piece of glue.
//!
//! | Feature | Adds | Dependency |
//! |---|---|---|
//! | `serde` | Serialization for the identity types, for carrying [`Identity`] on an application's own wire | `serde` |
//! | `sysinfo` | `Tenant::look_up`, so live process facts need not be supplied by hand | `sysinfo` |
//! | `eviction` | `eviction::evict`, which verifies a pid before signalling it and waits for the role to be released | `sysinfo` |
//! | `supervision` | `supervision::Supervisor`, a probe/spawn/wait/back-off loop over the verdict | none |
//!
//! Whole-process-tree containment and async supervision stay out of scope;
//! [`processkit`](https://docs.rs/processkit) does those well.
//!
//! # Examples
//!
//! The helper takes the role, publishes who it is, and re-checks its owner on
//! every handshake:
//!
//! ```no_run
//! use succession::{Allegiance, ClaimError, Compat, Identity, Record, Role, Run, Standing, Tenant};
//!
//! # fn owner_identity() -> Identity { Identity::mine(Compat::from_raw(18)) }
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! const PROTOCOL: Compat = Compat::from_raw(18);
//! let spawned_by = Run::from_raw(std::env::var("MY_APP_RUN")?.parse()?);
//!
//! let role = Role::new("/run/my-app", "overlay");
//! let tenancy = match role.claim() {
//! Ok(tenancy) => tenancy,
//! // Someone is already the overlay. Our supervisor will deal with it.
//! Err(ClaimError::Occupied) => return Ok(()),
//! Err(error) => return Err(error.into()),
//! };
//! let _ = tenancy.publish(&Record::new(
//! Identity::new(spawned_by, PROTOCOL),
//! Tenant::current(),
//! ));
//!
//! let allegiance = Allegiance::to(PROTOCOL, spawned_by);
//! if let Standing::Superseded(because) = allegiance.observe(owner_identity()) {
//! eprintln!("stepping aside: {because}");
//! return Ok(()); // dropping `tenancy` frees the role for the replacement
//! }
//! # Ok(())
//! # }
//! ```
//!
//! Its owner's supervisor asks whether the seat is free before filling it:
//!
//! ```no_run
//! use std::time::{Duration, Instant};
//! use succession::{Role, Run, Verdict, verdict};
//!
//! # fn spawn_helper() {}
//! # fn ask_to_leave(_: u32) {}
//! # fn main() -> std::io::Result<()> {
//! let role = Role::new("/run/my-app", "overlay");
//! let waiting_since = Instant::now();
//!
//! match verdict(&role.occupancy()?, Run::mint(), waiting_since.elapsed(), Duration::from_secs(15))
//! {
//! Verdict::Start => spawn_helper(),
//! Verdict::Wait => std::thread::sleep(Duration::from_millis(500)),
//! // Verify the pid is still that process before signalling it.
//! Verdict::Evict(record) => ask_to_leave(record.tenant.pid),
//! Verdict::EvictAnonymous => { /* fall back on what you know out of band */ }
//! }
//! # Ok(())
//! # }
//! ```
/// Compile-tests the README's examples, so the front page cannot drift away
/// from the API it advertises.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;