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

Allow calling interrupt::free with zero-arity closures. (0.7.x) #436

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Changed
- `interrupt::free` can be called with closures of type `FnOnce() -> T`.

### Deprecated
- the `ptr()` function on all peripherals register blocks in favor of
the associated constant `PTR` (#386).
- Calling `interrupt::free` with closures of type `FnOnce(&CriticalSection) -> T` is deprecated. Users should switch to using `critical_section::with`.

## [v0.7.4] - 2021-12-31

Expand Down
8 changes: 4 additions & 4 deletions cortex-m-semihosting/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::hio::{self, HostStream};
static mut HSTDOUT: Option<HostStream> = None;

pub fn hstdout_str(s: &str) {
let _result = interrupt::free(|_| unsafe {
let _result = interrupt::free(|| unsafe {
if HSTDOUT.is_none() {
HSTDOUT = Some(hio::hstdout()?);
}
Expand All @@ -19,7 +19,7 @@ pub fn hstdout_str(s: &str) {
}

pub fn hstdout_fmt(args: fmt::Arguments) {
let _result = interrupt::free(|_| unsafe {
let _result = interrupt::free(|| unsafe {
if HSTDOUT.is_none() {
HSTDOUT = Some(hio::hstdout()?);
}
Expand All @@ -31,7 +31,7 @@ pub fn hstdout_fmt(args: fmt::Arguments) {
static mut HSTDERR: Option<HostStream> = None;

pub fn hstderr_str(s: &str) {
let _result = interrupt::free(|_| unsafe {
let _result = interrupt::free(|| unsafe {
if HSTDERR.is_none() {
HSTDERR = Some(hio::hstderr()?);
}
Expand All @@ -41,7 +41,7 @@ pub fn hstderr_str(s: &str) {
}

pub fn hstderr_fmt(args: fmt::Arguments) {
let _result = interrupt::free(|_| unsafe {
let _result = interrupt::free(|| unsafe {
if HSTDERR.is_none() {
HSTDERR = Some(hio::hstderr()?);
}
Expand Down
41 changes: 38 additions & 3 deletions src/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,55 @@ pub unsafe fn enable() {
call_asm!(__cpsie());
}

/// Hacky compatibility layer to allow calling `interrupt::free` using
/// closures with arity 0 as well as 1. This trait is not considered
/// part of the public API.
///
/// The generic `Args` type is not actually used, see:
/// https://geo-ant.github.io/blog/2021/rust-traits-and-variadic-functions/
///
/// TODO: Remove before releasing 0.8.
#[doc(hidden)]
pub trait InterruptFreeFn<Args, R> {
/// Call the closure.
unsafe fn call(self) -> R;
}

impl<F, R> InterruptFreeFn<(), R> for F
where
F: FnOnce() -> R,
{
#[inline]
unsafe fn call(self) -> R {
self()
}
}

impl<'cs, F, R> InterruptFreeFn<&'cs CriticalSection, R> for F
where
F: FnOnce(&'cs CriticalSection) -> R,
{
#[inline]
unsafe fn call(self) -> R {
let cs: &'cs CriticalSection = core::mem::transmute(&CriticalSection::new());
self(cs)
}
}

/// Execute closure `f` in an interrupt-free context.
///
/// This as also known as a "critical section".
#[inline]
pub fn free<F, R>(f: F) -> R
pub fn free<Args, F, R>(f: F) -> R
where
F: FnOnce(&CriticalSection) -> R,
F: InterruptFreeFn<Args, R>,
{
let primask = crate::register::primask::read();

// disable interrupts
disable();

let r = f(unsafe { &CriticalSection::new() });
let r = unsafe { f.call() };

// If the interrupts were active before our `disable` call, then re-enable
// them. Otherwise, keep them disabled
Expand Down
3 changes: 0 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,6 @@
// Don't warn about feature(asm) being stable on Rust >= 1.59.0
#![allow(stable_features)]

extern crate bare_metal;
extern crate volatile_register;

#[macro_use]
mod call_asm;
#[macro_use]
Expand Down
2 changes: 1 addition & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ macro_rules! iprintln {
#[macro_export]
macro_rules! singleton {
(: $ty:ty = $expr:expr) => {
$crate::interrupt::free(|_| {
$crate::interrupt::free(|| {
static mut VAR: Option<$ty> = None;

#[allow(unsafe_code)]
Expand Down
2 changes: 1 addition & 1 deletion src/peripheral/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ impl Peripherals {
/// Returns all the core peripherals *once*
#[inline]
pub fn take() -> Option<Self> {
interrupt::free(|_| {
interrupt::free(|| {
if unsafe { TAKEN } {
None
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/peripheral/sau.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl SAU {
/// This function is executed under a critical section to prevent having inconsistent results.
#[inline]
pub fn set_region(&mut self, region_number: u8, region: SauRegion) -> Result<(), SauError> {
interrupt::free(|_| {
interrupt::free(|| {
let base_address = region.base_address;
let limit_address = region.limit_address;
let attribute = region.attribute;
Expand Down Expand Up @@ -215,7 +215,7 @@ impl SAU {
/// This function is executed under a critical section to prevent having inconsistent results.
#[inline]
pub fn get_region(&mut self, region_number: u8) -> Result<SauRegion, SauError> {
interrupt::free(|_| {
interrupt::free(|| {
if region_number >= self.region_numbers() {
Err(SauError::RegionNumberTooBig)
} else {
Expand Down