1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/* eventwait.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! EventWait synchronisation primitive

// Imports ===================================================================
use avr_oxide::concurrency::util::ThreadSet;
use avr_oxide::concurrency::{scheduler,interrupt};
use avr_oxide::concurrency::scheduler::ThreadState;
use avr_oxide::thread;

// Declarations ==============================================================
/// A simple synchronisation primitive that allows one or more threads to
/// wait until an event is signalled by another the thread.
///
/// When the event occurs, the signalling thread may release one or all
/// of the waiting threads.
pub struct EventWait {
  blocked_threads: ThreadSet
}


// Code ======================================================================\
impl EventWait {
  /// Create a new EventWait instance, with no blocked threads.
  pub fn new() -> Self {
    EventWait {
      blocked_threads: ThreadSet::new()
    }
  }

  /// Block the calling thread waiting for an event to be signalled by another
  /// thread calling one of the [`release()`] methods.
  ///
  /// [`release()`]: EventWait::release
  pub fn wait(&mut self) {
    interrupt::isolated(|isotoken|{
      self.add_to_waitlist(isotoken);
    });
    thread::yield_now();
  }

  /// Add the calling thread to the wait list WITHOUT immediately blocking.
  /// Note that even though it will not immediately block, there is a
  /// possibility the caller will be pre-empted and blocked practically
  /// immediately anyway, so it is the caller's responsbility to ensure
  /// they keep interrupts disabled for as long as they need to do anything
  /// before they yield.
  pub fn add_to_waitlist(&mut self, isotoken: interrupt::token::Isolated) {
    self.blocked_threads.add_current_thread(isotoken);
    scheduler::set_current_thread_state(isotoken, ThreadState::BlockedOnEvent);
  }

  /// Release one waiting thread (if any)
  pub fn release_one(&mut self) {
    self.release(1)
  }

  /// Release all waiting threads (if any)
  pub fn release_all(&mut self) {
    self.release(avr_oxide::deviceconsts::oxide::MAX_THREADS)
  }

  /// Release up to a maximum of `count` waiting threads (if any)
  pub fn release(&mut self, mut count: usize) {
    interrupt::isolated(|isotoken|{
      self.blocked_threads.do_each_consuming(isotoken, |isotoken,thread_id|{
        if scheduler::try_set_thread_state(isotoken, thread_id, ThreadState::Schedulable).is_ok() {
          count -= 1;
        }

        count != 0
      });
    })
  }
}


// Tests =====================================================================