avr-oxide 0.4.0

An extremely simple Rusty operating system for AVR microcontrollers
/* staticwrap.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Turn locals that we will never drop into static references.


// Imports ===================================================================
use core::cell::{Ref, RefCell, RefMut};
use core::ops::{Deref, DerefMut};
use avr_oxide::alloc::boxed::Box;

// Declarations ==============================================================

/// In an embedded environment, we often need static references to things,
/// for example because we are passing them on to an interrupt service
/// routine.
///
/// We also often need to do some non-const setup on those things, something
/// Rust makes quite ugly (mutable statics are unsafe and unpleasant to work
/// with.)
///
/// Fortunately, we know that if our `main` routine never returns (i.e. it
/// is declared `-> !`) we will never drop any locals there, and can thus
/// safely get static references to them right?  Well, right in that *we*
/// know that - but for 'reasons' (`panic_unwind`, I'm glaring at you),
/// the Rust compiler doesn't know that.
///
/// Thus, `StaticWrap<T>`.  This will wrap a variable that *you* know
/// will never be dropped, and then give you access to static references
/// via the [`StaticWrap::static_ref()`] and [`StaticWrap::static_ref_mut()`]
/// methods.
///
/// The contract you promise to obey is that you will indeed never drop
/// them of course.  This is enforced at runtime - sropping a `StaticWrap`
/// is a *fatal runtime error*.
///
/// The normal borrowing rules - i.e. a maximum of one mutable borrow,
/// and no mutable borrows if there are immutable borrows - for a variable
/// placed inside a StaticWrap, are enforced at runtime.
pub struct StaticWrap<T: 'static> {
  wrapped: *mut RefCell<T>
}

pub struct StaticRef<'sr, T: ?Sized + 'sr> {
  r: Ref<'sr,T>
}

pub struct StaticRefMut<'sr, T: ?Sized + 'sr>  {
  rm: RefMut<'sr,T>
}

pub trait AsStaticRef<T>
where
  T: ?Sized
{
  unsafe fn as_static_ref(&self) -> &'static T;
}

pub trait AsStaticRefMut<T> : AsStaticRef<T>
  where
    T: ?Sized
{
  unsafe fn as_static_ref_mut(&mut self) -> &'static mut T;
}


// Code ======================================================================
impl<'sr,T: ?Sized + 'sr> AsStaticRef<T> for StaticRef<'sr, T> {
  unsafe fn as_static_ref(&self) -> &'static T {
    self.static_ref()
  }
}

impl<'sr,T: ?Sized + 'sr> AsStaticRef<T> for StaticRefMut<'sr, T> {
  unsafe fn as_static_ref(&self) -> &'static T {
    self.static_ref()
  }
}

impl<'sr,T: ?Sized + 'sr> AsStaticRefMut<T> for StaticRefMut<'sr, T> {
  unsafe fn as_static_ref_mut(&mut self) -> &'static mut T {
    self.static_ref_mut()
  }
}

impl<T: ?Sized> AsStaticRef<T> for &'static T {
  unsafe fn as_static_ref(&self) -> &'static T {
    self
  }
}




impl<T> StaticWrap<T> {
  /// Create a new StaticWrap around the given variable.  You may then
  /// access static references using
  /// [`StaticWrap::static_ref()`] and [`StaticWrap::static_ref_mut()`].
  ///
  /// # Warning
  /// You promise this instance *will never be dropped*.  If it is, a fatal
  /// runtime error will result.
  pub fn new(wrapping: T) -> Self {
    StaticWrap {
      wrapped: Box::leak(Box::new(RefCell::new(wrapping)))
    }
  }

  /// Obtain a static reference to the contained instance
  pub fn borrow(&self) -> StaticRef<'static,T> {
    unsafe {
      StaticRef {
        r: match (&*self.wrapped).try_borrow() {
          Ok(b) => b,
          #[cfg(not(feature="panic_unwind"))]
          Err(_e) => {
            avr_oxide::oserror::halt(avr_oxide::oserror::OsError::StaticBorrow);
          }
          #[cfg(feature="panic_unwind")]
          Err(e) => {
            panic!(e);
          }
        }
      }
    }
  }

  /// Obtain a mutable static reference to the contained instance.
  ///
  /// # SAFETY
  /// We don't (or rather, the borrow checker can't) enforce the limitation
  /// that a mutual reference should be exclusive.  Be careful what you wish
  /// for.
  pub fn borrow_mut(&mut self) -> StaticRefMut<'static, T> {
    unsafe {
      StaticRefMut {
        rm: match (&mut *self.wrapped).try_borrow_mut() {
          Ok(b) => b,
          #[cfg(not(feature="panic_unwind"))]
          Err(_e) => {
            avr_oxide::oserror::halt(avr_oxide::oserror::OsError::StaticBorrow);
          }
          #[cfg(feature="panic_unwind")]
          Err(e) => {
            panic!(e);
          }
        }
      }
    }
  }
}

impl<'sr,T: ?Sized + 'sr> Deref for StaticRef<'sr, T> {
  type Target = T;

  fn deref(&self) -> &Self::Target {
    &*self.r
  }
}

impl<'sr,T: ?Sized + 'sr> Deref for StaticRefMut<'sr, T> {
  type Target = T;

  fn deref(&self) -> &Self::Target {
    &*self.rm
  }
}

impl<'sr,T: ?Sized + 'sr> DerefMut for StaticRefMut<'sr, T> {
  fn deref_mut(&mut self) -> &mut Self::Target {
    &mut *self.rm
  }
}

impl<'sr,T: ?Sized + 'sr> StaticRef<'sr, T> {
  /// Return a reference to the contained object with a `'static` lifetime.
  ///
  /// # Safety
  /// The returned reference is guaranteed to be valid for the `'static`
  /// lifetime.  However, this remains unsafe because it allows the caller
  /// to evade the normal borrow checker rules about not holding more than
  /// one mutable reference to the contents of the type.
  pub unsafe fn static_ref(&self) -> &'static T {
    core::mem::transmute(self.deref())
  }

  pub fn clone(orig: &Self) -> Self {
    StaticRef {
      r: Ref::clone(&orig.r)
    }
  }
}

impl<'sr,T: ?Sized + 'sr> StaticRefMut<'sr, T> {
  /// Return a reference to the contained object with a `'static` lifetime.
  ///
  /// # Safety
  /// The returned reference is guaranteed to be valid for the `'static`
  /// lifetime.  However, this remains unsafe because it allows the caller
  /// to evade the normal borrow checker rules about not holding more than
  /// one mutable reference to the contents of the type.
  pub unsafe fn static_ref(&self) -> &'static T {
    core::mem::transmute(self.deref())
  }

  /// Return a mutable reference to the contained object with a `'static` lifetime.
  ///
  /// # Safety
  /// The returned reference is guaranteed to be valid for the `'static`
  /// lifetime.  However, this remains unsafe because it allows the caller
  /// to evade the normal borrow checker rules about not holding more than
  /// one mutable reference to the contents of the type.
  pub unsafe fn static_ref_mut(&mut self) -> &'static mut T {
    core::mem::transmute(self.deref_mut())
  }
}

#[cfg(feature="runtime_checks")]
impl<T> Drop for StaticWrap<T> {
  fn drop(&mut self) {
    #[cfg(not(test))]
    avr_oxide::oserror::halt(avr_oxide::oserror::OsError::StaticDropped);
    #[cfg(test)]
    println!("((StaticWrap dropped - only allowed in testing))");
  }
}

unsafe impl<'sr,T: ?Sized + Send + 'sr> Send for StaticRef<'sr,T> {}
unsafe impl<'sr,T: ?Sized + Sync + 'sr> Sync for StaticRef<'sr,T> {}

unsafe impl<'sr,T: ?Sized + Send + 'sr> Send for StaticRefMut<'sr,T> {}
unsafe impl<'sr,T: ?Sized + Sync + 'sr> Sync for StaticRefMut<'sr,T> {}


// Tests =====================================================================
#[cfg(test)]
mod tests {
  use avr_oxide::StaticWrap;

  #[test]
  fn test_multiple_borrows() {
    let thing = StaticWrap::new(0x123);

    let borrow_one = thing.borrow();
    let borrow_two = thing.borrow();
  }

  #[test]
  #[should_panic]
  fn test_no_mut_with_immut() {
    let mut thing = StaticWrap::new(0x123);

    let borrow_one = thing.borrow();
    let borrow_two = thing.borrow_mut();
  }
}