Skip to content

Commit

Permalink
Add a few changes suggested by thomcc for the locks.
Browse files Browse the repository at this point in the history
* Added needed compiler fences to `InitOnce::try_get`.
* Change the error in `RawMutex::raw_unlock` to better reflect the cause.
  • Loading branch information
Lymia committed Feb 18, 2021
1 parent e4e46a0 commit d6511a9
Showing 1 changed file with 27 additions and 18 deletions.
45 changes: 27 additions & 18 deletions src/sync/locks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl RawMutex {
fn raw_unlock(&self) {
compiler_fence(Ordering::Release);
if !self.0.replace(false) {
already_locked()
panic!("Internal error: Attempt to unlock a `RawMutex` which is not locked.")
}
}

Expand Down Expand Up @@ -158,28 +158,37 @@ impl<T> InitOnce<T> {
/// returns `Ok`. If it returns `Err`, it will be called again in the
/// future until an attempt at initialization succeeds.
///
/// Take care when sharing an `InitOnce` object between an IRQ and normal
/// code. If this function is called in an IRQ when it is already currently
/// being initialized by user code, this function will panic.
/// This function will disable interrupts if the contents are not already
/// initialized, which may cause audio skipping and similar issues. This
/// is generally not an issue as that will only happen once during the
/// lifetime of the program.
pub fn try_get<E>(&self, initializer: impl FnOnce() -> Result<T, E>) -> Result<&T, E> {
unsafe {
if self.state.read() != 2 {
// Locks the initializer
if self.state.replace(1) != 0 {
panic!("Attempt to initialize `InitOnce` that is already in initialization.");
}

// Initialize the actual value.
let init = match initializer() {
Ok(v) => v,
Err(e) => {
assert_eq!(self.state.replace(0), 1);
return Err(e);
// We can afford to disable interrupts here, because this code path
// should only be called once for each variable.
with_irqs_disabled(|| -> Result<(), E> {
// Locks the initializer
if self.state.replace(1) != 0 {
panic!("Attempt to initialize `InitOnce` that is already in initialization.");
}
};
ptr::write_volatile((*self.value.get()).as_mut_ptr(), init);
assert_eq!(self.state.replace(2), 1);

// Initialize the actual value.
let init = match initializer() {
Ok(v) => v,
Err(e) => {
assert_eq!(self.state.replace(0), 1);
return Err(e);
}
};
ptr::write_volatile((*self.value.get()).as_mut_ptr(), init);
compiler_fence(Ordering::Release);
assert_eq!(self.state.replace(2), 1);

Ok(())
});
}
compiler_fence(Ordering::Acquire);
Ok(&*(*self.value.get()).as_mut_ptr())
}
}
Expand Down

0 comments on commit d6511a9

Please sign in to comment.