controller.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // Implementation of "Controller", a primitive for simulation of statecharts.
  2. use std::collections::BinaryHeap;
  3. use std::cmp::Ordering;
  4. use std::cmp::Reverse;
  5. use crate::statechart::Timestamp;
  6. use crate::statechart::Scheduler;
  7. use crate::statechart::SC;
  8. pub type TimerIndex = u16;
  9. #[derive(Default, Copy, Clone, Ord, PartialOrd, PartialEq, Eq)]
  10. pub struct TimerId {
  11. timestamp: Timestamp,
  12. // This field maintains FIFO order for equally timestamped entries.
  13. n: TimerIndex,
  14. }
  15. pub struct QueueEntry<InEvent> {
  16. id: TimerId,
  17. event: InEvent,
  18. }
  19. impl<InEvent> Ord for QueueEntry<InEvent> {
  20. fn cmp(&self, other: &Self) -> Ordering {
  21. self.id.cmp(&other.id)
  22. }
  23. }
  24. impl<InEvent> PartialOrd for QueueEntry<InEvent> {
  25. fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
  26. Some(self.cmp(other))
  27. }
  28. }
  29. impl<InEvent> PartialEq for QueueEntry<InEvent> {
  30. fn eq(&self, other: &Self) -> bool {
  31. self.id == other.id
  32. }
  33. }
  34. impl<InEvent> Eq for QueueEntry<InEvent> {}
  35. pub struct Controller<InEvent> {
  36. simtime: Timestamp,
  37. next_id: TimerIndex,
  38. queue: BinaryHeap<Reverse<QueueEntry<InEvent>>>,
  39. // Right now, removed queue entries are put into a second BinaryHeap.
  40. // This is not very efficient. Queue entries should be allocated on the heap,
  41. // (preferrably using a pool allocator), and directly marked as "removed".
  42. // Optionally, if the number of removed items exceeds a threshold, a sweep could remove "removed" entries.
  43. removed: BinaryHeap<Reverse<TimerId>>,
  44. }
  45. impl<InEvent> Scheduler<InEvent, TimerId> for Controller<InEvent> {
  46. fn set_timeout(&mut self, delay: Timestamp, event: InEvent) -> TimerId {
  47. let id = TimerId{ timestamp: self.simtime + delay, n: self.next_id };
  48. let entry = QueueEntry::<InEvent>{ id, event };
  49. self.queue.push(Reverse(entry));
  50. self.next_id += 1; // TODO: will overflow eventually :(
  51. return id
  52. }
  53. fn unset_timeout(&mut self, id: TimerId) {
  54. self.removed.push(Reverse(id));
  55. }
  56. }
  57. pub enum Until {
  58. Timestamp(Timestamp),
  59. Eternity,
  60. }
  61. impl<InEvent: Copy> Controller<InEvent> {
  62. pub fn new() -> Self {
  63. Self {
  64. simtime: 0,
  65. next_id: 0,
  66. queue: BinaryHeap::with_capacity(8),
  67. removed: BinaryHeap::with_capacity(4),
  68. }
  69. }
  70. pub fn run_until<StatechartType: SC<InEvent, TimerId, Controller<InEvent>, OutputCallback>, OutEvent, OutputCallback: FnMut(OutEvent)>(&mut self, sc: &mut StatechartType, until: Until, output: &mut OutputCallback) {
  71. 'running: loop {
  72. if let Some(Reverse(entry)) = self.queue.peek() {
  73. // Check if event was removed
  74. if let Some(Reverse(removed)) = self.removed.peek() {
  75. if entry.id == *removed {
  76. self.queue.pop();
  77. self.removed.pop();
  78. continue;
  79. }
  80. }
  81. // Check if event too far in the future
  82. if let Until::Timestamp(t) = until {
  83. if entry.id.timestamp > t {
  84. println!("break, timestamp {}, t {}", entry.id.timestamp, t);
  85. break 'running;
  86. }
  87. }
  88. // OK, handle event
  89. self.simtime = entry.id.timestamp;
  90. // eprintln!("time is now {}", self.simtime);
  91. sc.big_step(Some(entry.event), self, output);
  92. self.queue.pop();
  93. }
  94. else {
  95. break 'running;
  96. }
  97. }
  98. }
  99. }