avr-oxide 0.3.1

An extremely simple Rusty operating system for AVR microcontrollers
/* util.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Internal utility types for the AVRoxide concurrency implementation.

// Imports ===================================================================
use avr_oxide::util::datatypes::{VolatileBitField, BitIndex, BitFieldAccess};
use avr_oxide::concurrency::interrupt;
use avr_oxide::concurrency::scheduler;
use avr_oxide::deviceconsts::oxide;

// Declarations ==============================================================
/**
 * A thread ID.
 */
pub type ThreadId = usize;

#[repr(C)]
pub struct ThreadSet {
  threadids: VolatileBitField
}

// Code =====================================================================
const THREADSET_SIZE: usize = oxide::MAX_THREADS;

impl ThreadSet {
  pub fn new() -> Self {
    ThreadSet {
      threadids: VolatileBitField::all_clr()
    }
  }

  pub fn add_thread(&mut self, isotoken: interrupt::token::Isolated, id: ThreadId){
    self.threadids.set_isolated(isotoken, BitIndex::bit(id));
  }
  pub fn add_current_thread(&mut self, isotoken: interrupt::token::Isolated){
    self.add_thread(isotoken, scheduler::current_thread_id(isotoken));
  }
  pub fn remove_thread(&mut self, isotoken: interrupt::token::Isolated, id: ThreadId){
    self.threadids.clr_isolated(isotoken, BitIndex::bit(id));
  }
  pub fn remove_current_thread(&mut self, isotoken: interrupt::token::Isolated){
    self.remove_thread(isotoken, scheduler::current_thread_id(isotoken));
  }
  pub fn contains_thread(&self, _isotoken: interrupt::token::Isolated, id: ThreadId) -> bool {
    self.threadids.is_set(BitIndex::bit(id))
  }
  pub fn contains_current_thread(&self, isotoken: interrupt::token::Isolated) -> bool {
    self.contains_thread(isotoken, scheduler::current_thread_id(isotoken))
  }
  pub fn remove_all(&mut self) {
    self.threadids.clr_all()
  }

  /// Execute the given closure for each thread in this threadset.
  ///
  /// The closure should return a boolean indicating if it should continue
  /// iterating through the threads.
  pub fn do_each<F>(&self, isotoken: interrupt::token::Isolated, mut f: F)
  where
    F: FnMut(interrupt::token::Isolated, ThreadId) -> bool
  {
    for id in 0..THREADSET_SIZE {
      if self.contains_thread(isotoken, id) {
        let cont = (f)(isotoken, id);

        if !cont {
          break;
        }
      }
    }
  }

  /// Execute the given closure for each thread in this threadset.
  /// The thread is removed from the threadset following execution of the
  /// closure.
  ///
  /// The closure should return a boolean indicating if it should continue
  /// iterating through the threads.
  pub fn do_each_consuming<F>(&mut self, isotoken: interrupt::token::Isolated, mut f: F)
  where
    F: FnMut(interrupt::token::Isolated, ThreadId) -> bool
  {
    for id in 0..THREADSET_SIZE {
      if self.contains_thread(isotoken, id) {
        let cont = (f)(isotoken, id);

        self.remove_thread(isotoken, id);

        if !cont {
          break;
        }
      }
    }
  }
}



// Tests =====================================================================
#[cfg(test)]
mod tests {
  use std::fmt::{Debug, Formatter};
  use avr_oxide::concurrency::util::{ThreadId, ThreadSet};
  use avr_oxide::deviceconsts::oxide::MAX_THREADS;

  #[test]
  fn test_threadset() {
    let mut threadset = ThreadSet::new();

    assert!(MAX_THREADS >= 4);

    avr_oxide::concurrency::interrupt::isolated(|isotoken|{
      threadset.add_thread(isotoken, 1u8.into());
      threadset.add_thread(isotoken, 3u8.into());

      assert!(!threadset.contains_thread(isotoken,0u8.into()));
      assert!(threadset.contains_thread(isotoken, 1u8.into()));
      assert!(!threadset.contains_thread(isotoken, 2u8.into()));
      assert!(threadset.contains_thread(isotoken, 3u8.into()));
    })
  }

  #[test]
  fn test_threadset_doeach() {
    let mut threadset = ThreadSet::new();

    assert!(MAX_THREADS >= 4);

    avr_oxide::concurrency::interrupt::isolated(|isotoken|{
      threadset.add_thread(isotoken, 1u8.into());
      threadset.add_thread(isotoken, 3u8.into());

      println!("Testing threadset do_each:");
      let mut total = 0usize;
      threadset.do_each(isotoken, |isotoken,thread_id|{
        total += thread_id;

        println!("Iterating - thread id {:?}", thread_id);

        assert_ne!(thread_id, 0u8.into());
        assert_ne!(thread_id, 2u8.into());
        true
      });
      assert_eq!(total, 4);
      assert!(threadset.contains_thread(isotoken,1u8.into()));
      assert!(threadset.contains_thread(isotoken, 3u8.into()));

      // Now test the consuming version
      println!("Testing threadset do_each_consuming:");
      total = 0;
      threadset.do_each_consuming(isotoken, |isotoken,thread_id|{
        total += thread_id;

        println!("Iterating - thread id {:?}", thread_id);

        assert_ne!(thread_id, 0u8.into());
        assert_ne!(thread_id, 2u8.into());
        true
      });
      assert_eq!(total, 4);
      assert!(!threadset.contains_thread(isotoken,0u8.into()));
      assert!(!threadset.contains_thread(isotoken,1u8.into()));
      assert!(!threadset.contains_thread(isotoken,2u8.into()));
      assert!(!threadset.contains_thread(isotoken, 3u8.into()));
    })
  }
}