Skip to content

Commit 3208bf3

Browse files
authored
feat(seatbelt): ResilienceContext is now ThreadAware (#290)
I found a need for it. Minimal changes so far, until more scenarios are uncovered. We can then start putting these on top of middleware services.
1 parent 1030bf4 commit 3208bf3

6 files changed

Lines changed: 50 additions & 12 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ layered = { path = "crates/layered", default-features = false, version = "0.3.0"
3636
ohno = { path = "crates/ohno", default-features = false, version = "0.3.1" }
3737
ohno_macros = { path = "crates/ohno_macros", default-features = false, version = "0.3.0" }
3838
recoverable = { path = "crates/recoverable", default-features = false, version = "0.1.0" }
39-
seatbelt = { path = "crates/seatbelt", default-features = false, version = "0.3.0" }
39+
seatbelt = { path = "crates/seatbelt", default-features = false, version = "0.3.1" }
4040
testing_aids = { path = "crates/testing_aids", default-features = false }
4141
thread_aware = { path = "crates/thread_aware", default-features = false, version = "0.6.2" }
4242
thread_aware_macros = { path = "crates/thread_aware_macros", default-features = false, version = "0.6.1" }

crates/seatbelt/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [0.3.1] - 2026-02-27
4+
5+
- ✨ Features
6+
7+
- ResilienceContext is now ThreadAware
8+
39
## [0.3.0] - 2026-02-17
410

511
- ✨ Features

crates/seatbelt/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[package]
55
name = "seatbelt"
66
description = "Resilience and recovery mechanisms for fallible operations."
7-
version = "0.3.0"
7+
version = "0.3.1"
88
readme = "README.md"
99
keywords = ["oxidizer", "resilience", "layered", "recovery", "retry"]
1010
categories = ["data-structures"]
@@ -27,6 +27,9 @@ allowed_external_types = [
2727
"tick::clock::Clock",
2828
"tower_layer::Layer",
2929
"tower_service::Service",
30+
"thread_aware::core::ThreadAware",
31+
"thread_aware::affinity::MemoryAffinity",
32+
"thread_aware::affinity::PinnedAffinity",
3033
]
3134

3235
[package.metadata.docs.rs]
@@ -46,6 +49,7 @@ fastrand = { workspace = true, optional = true }
4649
layered = { workspace = true }
4750
opentelemetry = { workspace = true, optional = true }
4851
recoverable = { workspace = true }
52+
thread_aware = { workspace = true }
4953
tick = { workspace = true }
5054
tower-service = { workspace = true, optional = true }
5155
tracing = { workspace = true, optional = true }

crates/seatbelt/README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,19 @@ This crate provides several optional features that can be enabled in your `Cargo
140140
This crate was developed as part of <a href="../..">The Oxidizer Project</a>. Browse this crate's <a href="https://github.com/microsoft/oxidizer/tree/main/crates/seatbelt">source code</a>.
141141
</sub>
142142

143-
[__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG4XTWg5D20iXG5-sZfM55RcnG_PRlQIWXOiaG6Oh8f0TxP2DYWSFgmdsYXllcmVkZTAuMy4wgmtyZWNvdmVyYWJsZWUwLjEuMIJoc2VhdGJlbHRlMC4zLjCCZHRpY2tlMC4yLjCCbXRvd2VyX3NlcnZpY2VlMC4zLjM
143+
[__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG4XTWg5D20iXG5-sZfM55RcnG_PRlQIWXOiaG6Oh8f0TxP2DYWSFgmdsYXllcmVkZTAuMy4wgmtyZWNvdmVyYWJsZWUwLjEuMIJoc2VhdGJlbHRlMC4zLjGCZHRpY2tlMC4yLjCCbXRvd2VyX3NlcnZpY2VlMC4zLjM
144144
[__link0]: https://crates.io/crates/layered/0.3.0
145145
[__link1]: https://docs.rs/layered/0.3.0/layered/?search=Stack
146-
[__link10]: https://docs.rs/seatbelt/0.3.0/seatbelt/breaker/index.html
147-
[__link11]: https://docs.rs/seatbelt/0.3.0/seatbelt/timeout/index.html
148-
[__link12]: https://docs.rs/seatbelt/0.3.0/seatbelt/retry/index.html
149-
[__link13]: https://docs.rs/seatbelt/0.3.0/seatbelt/breaker/index.html
146+
[__link10]: https://docs.rs/seatbelt/0.3.1/seatbelt/breaker/index.html
147+
[__link11]: https://docs.rs/seatbelt/0.3.1/seatbelt/timeout/index.html
148+
[__link12]: https://docs.rs/seatbelt/0.3.1/seatbelt/retry/index.html
149+
[__link13]: https://docs.rs/seatbelt/0.3.1/seatbelt/breaker/index.html
150150
[__link14]: https://docs.rs/tower_service/0.3.3/tower_service/?search=Service
151151
[__link2]: https://docs.rs/tick/0.2.0/tick/?search=Clock
152152
[__link3]: https://crates.io/crates/tick/0.2.0
153-
[__link4]: https://docs.rs/seatbelt/0.3.0/seatbelt/?search=ResilienceContext
154-
[__link5]: https://docs.rs/seatbelt/0.3.0/seatbelt/?search=ResilienceContext
153+
[__link4]: https://docs.rs/seatbelt/0.3.1/seatbelt/?search=ResilienceContext
154+
[__link5]: https://docs.rs/seatbelt/0.3.1/seatbelt/?search=ResilienceContext
155155
[__link6]: https://docs.rs/recoverable/0.1.0/recoverable/?search=RecoveryInfo
156156
[__link7]: https://docs.rs/recoverable/0.1.0/recoverable/?search=Recovery
157-
[__link8]: https://docs.rs/seatbelt/0.3.0/seatbelt/timeout/index.html
158-
[__link9]: https://docs.rs/seatbelt/0.3.0/seatbelt/retry/index.html
157+
[__link8]: https://docs.rs/seatbelt/0.3.1/seatbelt/timeout/index.html
158+
[__link9]: https://docs.rs/seatbelt/0.3.1/seatbelt/retry/index.html

crates/seatbelt/src/context.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
use std::borrow::Cow;
55

6+
use thread_aware::{
7+
ThreadAware,
8+
affinity::{MemoryAffinity, PinnedAffinity},
9+
};
610
use tick::Clock;
711

812
pub(crate) const DEFAULT_CONTEXT_NAME: &str = "default";
@@ -23,6 +27,15 @@ pub struct ResilienceContext<In, Out> {
2327
_out: std::marker::PhantomData<fn() -> Out>,
2428
}
2529

30+
impl<In, Out> ThreadAware for ResilienceContext<In, Out> {
31+
fn relocated(mut self, source: MemoryAffinity, destination: PinnedAffinity) -> Self {
32+
// Only clock is thread-aware for now. At some point, we also want
33+
// telemetry to be tread-aware too.
34+
self.clock = self.clock.relocated(source, destination);
35+
self
36+
}
37+
}
38+
2639
impl<In, Out> ResilienceContext<In, Out> {
2740
/// Create a context with a clock. Initializes with `name = "default"`.
2841
pub fn new(clock: impl AsRef<Clock>) -> Self {
@@ -113,8 +126,14 @@ impl<In, Out> Clone for ResilienceContext<In, Out> {
113126
#[cfg(test)]
114127
mod tests {
115128

129+
use std::fmt::Debug;
130+
131+
use thread_aware::affinity::pinned_affinities;
132+
116133
use super::*;
117134

135+
static_assertions::assert_impl_all!(ResilienceContext<(), ()>: Send, Sync, ThreadAware, Debug, Clone);
136+
118137
#[test]
119138
fn test_new_with_clock_sets_default_pipeline_name() {
120139
let clock = tick::Clock::new_frozen();
@@ -156,6 +175,14 @@ mod tests {
156175
assert!(dump.contains('3'));
157176
}
158177

178+
#[test]
179+
fn relocate_ok() {
180+
let ctx = ResilienceContext::<(), ()>::new(tick::Clock::new_frozen());
181+
let affinites = pinned_affinities(&[2]);
182+
183+
_ = ctx.relocated(affinites[0].into(), affinites[1]);
184+
}
185+
159186
#[cfg(not(miri))]
160187
fn test_meter_provider() -> (
161188
opentelemetry_sdk::metrics::SdkMeterProvider,

0 commit comments

Comments
 (0)