From 6de20e818f7ed9b4d24458cb2ae7249cfe82b49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 7 May 2026 16:58:32 +0200 Subject: [PATCH 1/6] Fix new lints --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 3f062ae..b650bfd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -901,7 +901,7 @@ impl Interchange { } /// Claim one of the channels of the interchange. Returns None if called more than `N` times. - pub fn claim(&self) -> Option<(Requester, Responder)> { + pub fn claim(&'_ self) -> Option<(Requester<'_, Rq, Rp>, Responder<'_, Rq, Rp>)> { self.as_interchange_ref().claim() } From 3dd20626c26627f58dbbf25dc0359a00916b7a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 7 May 2026 16:59:16 +0200 Subject: [PATCH 2/6] Add callback mechanism --- src/lib.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b650bfd..69d4699 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,6 +171,9 @@ impl From for State { } } +/// Callback that can be called +pub type Callback = fn(); + // the repr(u8) is necessary so MaybeUninit::zeroized.assume_init() is valid and corresponds to // None #[repr(u8)] @@ -361,7 +364,10 @@ impl Channel { .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) .is_ok() { - Some(Requester { channel: self }) + Some(Requester { + channel: self, + callback: || {}, + }) } else { None } @@ -376,7 +382,10 @@ impl Channel { .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) .is_ok() { - Some(Responder { channel: self }) + Some(Responder { + channel: self, + callback: || {}, + }) } else { None } @@ -408,6 +417,7 @@ impl Default for Channel { /// the requester uses a `'static` lifetime parameter pub struct Requester<'i, Rq, Rp> { channel: &'i Channel, + callback: Callback, } impl Drop for Requester<'_, Rq, Rp> { @@ -419,6 +429,10 @@ impl Drop for Requester<'_, Rq, Rp> { } impl<'i, Rq, Rp> Requester<'i, Rq, Rp> { + pub fn callback_mut(&mut self) -> &mut Callback { + &mut self.callback + } + pub fn channel(&self) -> &'i Channel { self.channel } @@ -479,6 +493,7 @@ impl<'i, Rq, Rp> Requester<'i, Rq, Rp> { self.channel .state .store(State::Requested as u8, Ordering::Release); + (self.callback)(); Ok(()) } else { Err(Error) @@ -503,6 +518,7 @@ impl<'i, Rq, Rp> Requester<'i, Rq, Rp> { } if self.channel.transition(State::Requested, State::Idle) { + (self.callback)(); // we canceled before the responder was even aware of the request. return Ok(Some(unsafe { self.with_data_mut(|i| i.take_rq()) })); } @@ -611,6 +627,7 @@ where .channel .transition(State::BuildingRequest, State::Requested) { + (self.callback)(); Ok(()) } else { // logic error @@ -625,6 +642,7 @@ where /// the responder uses a `'static` lifetime parameter pub struct Responder<'i, Rq, Rp> { channel: &'i Channel, + callback: Callback, } impl Drop for Responder<'_, Rq, Rp> { @@ -636,6 +654,10 @@ impl Drop for Responder<'_, Rq, Rp> { } impl<'i, Rq, Rp> Responder<'i, Rq, Rp> { + pub fn callback_mut(&mut self) -> &mut Callback { + &mut self.callback + } + pub fn channel(&self) -> &'i Channel { self.channel } @@ -758,6 +780,7 @@ impl<'i, Rq, Rp> Responder<'i, Rq, Rp> { .channel .transition(State::BuildingResponse, State::Responded) { + (self.callback)(); Ok(()) } else { Err(Error) @@ -832,6 +855,7 @@ where .channel .transition(State::BuildingResponse, State::Responded) { + (self.callback)(); Ok(()) } else { // logic error From fab006afc0111c9da010abae67debb7a9f27291f Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 15 Sep 2026 16:11:56 +0200 Subject: [PATCH 3/6] Remove redundant reference in println! argument This fixes a new clippy lint. --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 69d4699..8331b25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,7 +75,7 @@ //! assert!(rq.request(request).is_ok()); //! let request = rp.take_request().unwrap(); //! -//! println!("responder could cancel: {:?}", &rq.cancel().unwrap().is_none()); +//! println!("responder could cancel: {:?}", rq.cancel().unwrap().is_none()); //! assert!(rp.is_canceled()); //! assert!(rp.respond(response).is_err()); //! assert!(rp.acknowledge_cancel().is_ok()); @@ -294,7 +294,7 @@ impl Message { /// assert!(rq.request(request).is_ok()); /// let request = rp.take_request().unwrap(); /// -/// println!("responder could cancel: {:?}", &rq.cancel().unwrap().is_none()); +/// println!("responder could cancel: {:?}", rq.cancel().unwrap().is_none()); /// assert!(rp.is_canceled()); /// assert!(rp.respond(response).is_err()); /// assert!(rp.acknowledge_cancel().is_ok()); @@ -1102,7 +1102,7 @@ mod tests { let request = rp.take_request().unwrap(); println!( "responder could cancel: {:?}", - &rq.cancel().unwrap().is_none() + rq.cancel().unwrap().is_none() ); assert_eq!(request, Request::This(1, 2)); assert!(rp.is_canceled()); @@ -1151,7 +1151,7 @@ mod tests { let request = rp.take_request().unwrap(); println!( "responder could cancel: {:?}", - &rq.cancel().unwrap().is_none() + rq.cancel().unwrap().is_none() ); assert_eq!(request, Request::This(1, 2)); assert!(rp.is_canceled()); From 41378fc25863ab376482cf5dc7c26736ddded217 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 15 Sep 2026 16:04:28 +0200 Subject: [PATCH 4/6] Add docs for callbacks --- CHANGELOG.md | 2 +- src/lib.rs | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1383148..6a62952 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- +- Add callbacks to `Requester` and `Responder` by adding a `callback_mut` function. ## [0.3.2][] - 2024-01-14 diff --git a/src/lib.rs b/src/lib.rs index 8331b25..5b34126 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -429,6 +429,11 @@ impl Drop for Requester<'_, Rq, Rp> { } impl<'i, Rq, Rp> Requester<'i, Rq, Rp> { + /// Allows to set a callback that is called when a request has been sent and when a request + /// has been cancelled before it was taken by the responder. + /// + /// The callback is called by [`Requester::request`][], [`Requester::send_request`][] and + /// [`Requester::cancel`][]. pub fn callback_mut(&mut self) -> &mut Callback { &mut self.callback } @@ -481,10 +486,13 @@ impl<'i, Rq, Rp> Requester<'i, Rq, Rp> { /// Send a request to the responder. /// /// If efficiency is a concern, or requests need multiple steps to - /// construct, use `request_mut` and `send_request. + /// construct, use `request_mut` and `send_request`. /// /// If the RPC state is `Idle`, this always succeeds, else calling /// is a logic error and the request is returned. + /// + /// If the request has been sent succesfully, this functions calls the callback set with + /// [`Requester::callback_mut`][]. pub fn request(&mut self, request: Rq) -> Result<(), Error> { if State::Idle == self.channel.state.load(Ordering::Acquire) { unsafe { @@ -508,6 +516,9 @@ impl<'i, Rq, Rp> Requester<'i, Rq, Rp> { /// If the responder has taken the request (is processing), we succeed and return None. /// /// In other cases (`Idle` or `Reponsed`) there is nothing to cancel and we fail. + /// + /// If the responder has not taken the request yet, this functions calls the callback set + /// with [`Requester::callback_mut`][]. pub fn cancel(&mut self) -> Result, Error> { if self .channel @@ -621,6 +632,9 @@ where /// Send a request that was already placed in the channel using `request_mut` or /// `with_request_mut`. + /// + /// If the request has been sent succesfully, this functions calls the callback set with + /// [`Requester::callback_mut`][]. pub fn send_request(&mut self) -> Result<(), Error> { if State::BuildingRequest == self.channel.state.load(Ordering::Acquire) && self @@ -654,6 +668,9 @@ impl Drop for Responder<'_, Rq, Rp> { } impl<'i, Rq, Rp> Responder<'i, Rq, Rp> { + /// Allows to set a callback that is called when a response has been sent. + /// + /// The callback is called by [`Responder::respond`][] and [`Responder::send_response`][]. pub fn callback_mut(&mut self) -> &mut Callback { &mut self.callback } @@ -771,6 +788,8 @@ impl<'i, Rq, Rp> Responder<'i, Rq, Rp> { /// If efficiency is a concern, or responses need multiple steps to /// construct, use `with_response_mut` or `response_mut` and `send_response`. /// + /// If the response has been sent succesfully, this functions calls the callback set with + /// [`Responder::callback_mut`][]. pub fn respond(&mut self, response: Rp) -> Result<(), Error> { if State::BuildingResponse == self.channel.state.load(Ordering::Acquire) { unsafe { @@ -849,6 +868,9 @@ where /// Send a response that was already placed in the channel using `response_mut` or /// `with_response_mut`. + /// + /// If the response has been sent succesfully, this functions calls the callback set with + /// [`Responder::callback_mut`][]. pub fn send_response(&mut self) -> Result<(), Error> { if State::BuildingResponse == self.channel.state.load(Ordering::Acquire) && self From 18aa249f2a6086b499d439f32653d406cca82190 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 15 Sep 2026 16:07:23 +0200 Subject: [PATCH 5/6] Add trusted publishing to crates.io --- .github/workflows/cd-test.yml | 18 ++++++++++++++++++ .github/workflows/cd.yml | 23 +++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 .github/workflows/cd-test.yml create mode 100644 .github/workflows/cd.yml diff --git a/.github/workflows/cd-test.yml b/.github/workflows/cd-test.yml new file mode 100644 index 0000000..0d2d1bc --- /dev/null +++ b/.github/workflows/cd-test.yml @@ -0,0 +1,18 @@ +# Copyright (C) 2023 Nitrokey GmbH +# SPDX-License-Identifier: CC0-1.0 + +name: Continuous delivery - test + +on: + pull_request: + # opened, reopenened, synchronize are the default types for pull_request + # labeled, unlabeled ensure this check is also run if a label is added or removed + types: [opened, reopened, synchronize, labeled, unlabeled] + +jobs: + test-publish: + runs-on: ubuntu-latest + if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-publish-check') }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - run: cargo publish --dry-run diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..e30f143 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,23 @@ +# Copyright (C) 2023 Nitrokey GmbH +# SPDX-License-Identifier: CC0-1.0 + +name: Continuous delivery - crates.io + +on: + release: + types: [published] + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + environment: crates.io + permissions: + id-token: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5 + id: auth + - run: cargo publish + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} From d94b32668e646851af2ec836a539b6139facccd9 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 15 Sep 2026 16:07:59 +0200 Subject: [PATCH 6/6] Release v0.3.3 --- CHANGELOG.md | 9 ++++++++- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a62952..fa0f88b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- + +## [0.3.3][] - 2026-09-15 + +### Added + - Add callbacks to `Requester` and `Responder` by adding a `callback_mut` function. ## [0.3.2][] - 2024-01-14 @@ -41,7 +47,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Changes API to use references instead of moves. This improves stack usage. -[Unreleased]: https://github.com/trussed-dev/interchange/compare/0.3.2...HEAD +[Unreleased]: https://github.com/trussed-dev/interchange/compare/0.3.3...HEAD +[0.3.2]: https://github.com/trussed-dev/interchange/compare/0.3.2...0.3.3 [0.3.2]: https://github.com/trussed-dev/interchange/compare/0.3.1...0.3.2 [0.3.1]: https://github.com/trussed-dev/interchange/compare/0.3.0...0.3.1 [0.3.0]: https://github.com/trussed-dev/interchange/compare/0.2.2...0.3.0 diff --git a/Cargo.toml b/Cargo.toml index 9ffb319..e139b06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "interchange" -version = "0.3.2" +version = "0.3.3" authors = ["The Trussed developers", "Nicolas Stalder "] edition = "2018" description = "Request/response mechanism for embedded development, using atomics"