Selaa lähdekoodia

Progress with SCCD Rust library

Joeri Exelmans 4 vuotta sitten
vanhempi
commit
da171175c5

+ 45 - 42
src/rust/action_lang/action_lang.rs

@@ -1,47 +1,50 @@
-mod sccd {
-  // Helpers used by from-action-lang-generated Rust code
-  mod action_lang {
-    // This macro lets a struct "inherit" the data members of another struct
-    // The inherited struct is added as a struct member and the Deref and DerefMut
-    // traits are implemented to return a reference to the base struct
-    #[macro_export]
-    macro_rules! inherit_struct {
-      ($name: ident ($base: ty) { $($element: ident: $ty: ty),* $(,)? } ) => {
-        #[derive(Copy, Clone)]
-        struct $name {
-          _base: $base,
-          $($element: $ty),*
-        }
-        impl Deref for $name {
-          type Target = $base;
-          fn deref(&self) -> &$base {
-            &self._base
-          }
-        }
-        impl DerefMut for $name {
-          fn deref_mut(&mut self) -> &mut $base {
-            &mut self._base
-          }
-        }
-      }
-    }
+// Helpers used by from-action-lang-generated Rust code
 
-    // "Base struct" for all scopes
-    #[derive(Copy, Clone)]
-    pub struct Empty{}
+#[allow(unused_imports)]
+use std::ops::Deref;
 
-    // A closure object is a pair of a functions first argument and that function.
-    // The call may be part of an larger expression, and therefore we cannot just write 'let' statements to assign the pair's elements to identifiers which we need for the call.
-    // This macro does exactly that, in an anonymous Rust closure, which is immediately called.
-    #[macro_export]
-    macro_rules! call_closure {
-      ($closure: expr, $($param: expr),*  $(,)?) => {
-      (||{
-        let scope = &mut $closure.0;
-        let function = &mut $closure.1;
-        return function($($param),* scope);
-      })()
-      };
+#[allow(unused_imports)]
+use std::ops::DerefMut;
+
+// This macro lets a struct "inherit" the data members of another struct
+// The inherited struct is added as a struct member and the Deref and DerefMut
+// traits are implemented to return a reference to the base struct
+#[macro_export]
+macro_rules! inherit_struct {
+  ($name: ident ($base: ty) { $($element: ident: $ty: ty),* $(,)? } ) => {
+    #[derive(Copy, Clone)]
+    struct $name {
+      _base: $base,
+      $($element: $ty),*
+    }
+    impl Deref for $name {
+      type Target = $base;
+      fn deref(&self) -> &$base {
+        &self._base
+      }
+    }
+    impl DerefMut for $name {
+      fn deref_mut(&mut self) -> &mut $base {
+        &mut self._base
+      }
     }
   }
 }
+
+// "Base struct" for all scopes
+#[derive(Copy, Clone)]
+pub struct Empty{}
+
+// A closure object is a pair of a functions first argument and that function.
+// The call may be part of an larger expression, and therefore we cannot just write 'let' statements to assign the pair's elements to identifiers which we need for the call.
+// This macro does exactly that, in an anonymous Rust closure, which is immediately called.
+#[macro_export]
+macro_rules! call_closure {
+  ($closure: expr, $($param: expr),*  $(,)?) => {
+  (||{
+    let scope = &mut $closure.0;
+    let function = &mut $closure.1;
+    return function($($param),* scope);
+  })()
+  };
+}

+ 1 - 0
src/rust/controller/Cargo.toml

@@ -5,6 +5,7 @@ authors = ["Joeri Exelmans <joeri.exelmans@gmail.com>"]
 edition = "2018"
 
 [dependencies]
+statechart = { path = "../statechart" }
 
 [lib]
 name = "controller"

+ 92 - 94
src/rust/controller/controller.rs

@@ -1,112 +1,110 @@
-mod sccd {
-  // Implementation of "Controller", a primitive for simulation of statecharts.
-  mod controller {
-    use std::collections::BinaryHeap;
-    use std::cmp::Ordering;
-    use std::cmp::Reverse;
-    use std::ops::Deref;
-    use std::ops::DerefMut;
+// Implementation of "Controller", a primitive for simulation of statecharts.
 
-    use sccd::statechart::Timestamp;
+use std::collections::BinaryHeap;
+use std::cmp::Ordering;
+use std::cmp::Reverse;
 
-    pub type TimerId = u16;
+use statechart::Timestamp;
+use statechart::Scheduler;
+use statechart::SC;
+use statechart::OutEvent;
 
-    #[derive(Default, Copy, Clone, Ord, PartialOrd, PartialEq, Eq)]
-    pub struct EntryId {
-      timestamp: Timestamp,
+pub type TimerId = u16;
 
-      // This field maintains FIFO order for equally timestamped entries.
-      n: TimerId,
-    }
+#[derive(Default, Copy, Clone, Ord, PartialOrd, PartialEq, Eq)]
+pub struct EntryId {
+  timestamp: Timestamp,
 
-    pub struct QueueEntry<InEvent> {
-      id: EntryId,
-      event: InEvent,
-    }
-    impl<InEvent> Ord for QueueEntry<InEvent> {
-      fn cmp(&self, other: &Self) -> Ordering {
-        self.id.cmp(&other.id)
-      }
-    }
-    impl<InEvent> PartialOrd for QueueEntry<InEvent> {
-      fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-      }
-    }
-    impl<InEvent> PartialEq for QueueEntry<InEvent> {
-      fn eq(&self, other: &Self) -> bool {
-        self.id == other.id
-      }
-    }
-    impl<InEvent> Eq for QueueEntry<InEvent> {}
+  // This field maintains FIFO order for equally timestamped entries.
+  n: TimerId,
+}
 
-    // Right now, "removed" queue entries are put into a second BinaryHeap.
-    // This is not very efficient. Queue entries should be allocated on the heap,
-    // (preferrably using a pool allocator), and directly marked as "removed".
-    // Optionally, if the number of removed items exceeds a threshold, a sweep could remove "removed" entries.
+pub struct QueueEntry<InEvent> {
+  id: EntryId,
+  event: InEvent,
+}
+impl<InEvent> Ord for QueueEntry<InEvent> {
+  fn cmp(&self, other: &Self) -> Ordering {
+    self.id.cmp(&other.id)
+  }
+}
+impl<InEvent> PartialOrd for QueueEntry<InEvent> {
+  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+    Some(self.cmp(other))
+  }
+}
+impl<InEvent> PartialEq for QueueEntry<InEvent> {
+  fn eq(&self, other: &Self) -> bool {
+    self.id == other.id
+  }
+}
+impl<InEvent> Eq for QueueEntry<InEvent> {}
 
-    pub struct Controller<InEvent> {
-      simtime: Timestamp,
-      next_id: TimerId,
-      queue: BinaryHeap<Reverse<QueueEntry<InEvent>>>,
-      removed: BinaryHeap<Reverse<EntryId>>,
-    }
+// Right now, "removed" queue entries are put into a second BinaryHeap.
+// This is not very efficient. Queue entries should be allocated on the heap,
+// (preferrably using a pool allocator), and directly marked as "removed".
+// Optionally, if the number of removed items exceeds a threshold, a sweep could remove "removed" entries.
 
-    impl<InEvent> Scheduler<InEvent, EntryId> for Controller<InEvent> {
-      fn set_timeout(&mut self, delay: Timestamp, event: InEvent) -> EntryId {
-        let id = EntryId{ timestamp: self.simtime + delay, n: self.next_id };
-        let entry = QueueEntry::<InEvent>{ id, event };
-        self.queue.push(Reverse(entry));
-        self.next_id += 1; // TODO: will overflow eventually :(
-        return id
-      }
-      fn unset_timeout(&mut self, id: EntryId) {
-        self.removed.push(Reverse(id));
-      }
-    }
+pub struct Controller<InEvent> {
+  simtime: Timestamp,
+  next_id: TimerId,
+  queue: BinaryHeap<Reverse<QueueEntry<InEvent>>>,
+  removed: BinaryHeap<Reverse<EntryId>>,
+}
 
-    pub enum Until {
-      Timestamp(Timestamp),
-      Eternity,
-    }
+impl<InEvent> Scheduler<InEvent, EntryId> for Controller<InEvent> {
+  fn set_timeout(&mut self, delay: Timestamp, event: InEvent) -> EntryId {
+    let id = EntryId{ timestamp: self.simtime + delay, n: self.next_id };
+    let entry = QueueEntry::<InEvent>{ id, event };
+    self.queue.push(Reverse(entry));
+    self.next_id += 1; // TODO: will overflow eventually :(
+    return id
+  }
+  fn unset_timeout(&mut self, id: EntryId) {
+    self.removed.push(Reverse(id));
+  }
+}
 
-    impl<InEvent: Copy> Controller<InEvent> {
-      fn new() -> Self {
-        Self {
-          simtime: 0,
-          next_id: 0,
-          queue: BinaryHeap::with_capacity(8),
-          removed: BinaryHeap::with_capacity(4),
-        }
-      }
-      fn run_until<StatechartType: SC<InEvent, Controller<InEvent>, OutputCallback>, OutputCallback: FnMut(OutEvent)>(&mut self, sc: &mut StatechartType, until: Until, output: &mut OutputCallback) {
-        'running: loop {
-          if let Some(Reverse(entry)) = self.queue.peek() {
-            // Check if event was removed
-            if let Some(Reverse(removed)) = self.removed.peek() {
-              if entry.id == *removed {
-                self.queue.pop();
-                self.removed.pop();
-                continue;
-              }
-            }
-            // Check if event too far in the future
-            if let Until::Timestamp(t) = until {
-              if entry.id.timestamp > t {
-                println!("break, timestamp {}, t {}", entry.id.timestamp, t);
-                break 'running;
-              }
-            }
-            // OK, handle event
-            self.simtime = entry.id.timestamp;
-            // eprintln!("time is now {}", self.simtime);
-            sc.big_step(Some(entry.event), self, output);
+pub enum Until {
+  Timestamp(Timestamp),
+  Eternity,
+}
+
+impl<InEvent: Copy> Controller<InEvent> {
+  pub fn new() -> Self {
+    Self {
+      simtime: 0,
+      next_id: 0,
+      queue: BinaryHeap::with_capacity(8),
+      removed: BinaryHeap::with_capacity(4),
+    }
+  }
+  pub fn run_until<StatechartType: SC<InEvent, EntryId, Controller<InEvent>, OutputCallback>, OutputCallback: FnMut(OutEvent)>(&mut self, sc: &mut StatechartType, until: Until, output: &mut OutputCallback) {
+    'running: loop {
+      if let Some(Reverse(entry)) = self.queue.peek() {
+        // Check if event was removed
+        if let Some(Reverse(removed)) = self.removed.peek() {
+          if entry.id == *removed {
             self.queue.pop();
+            self.removed.pop();
+            continue;
           }
-          else {
+        }
+        // Check if event too far in the future
+        if let Until::Timestamp(t) = until {
+          if entry.id.timestamp > t {
+            println!("break, timestamp {}, t {}", entry.id.timestamp, t);
             break 'running;
           }
         }
+        // OK, handle event
+        self.simtime = entry.id.timestamp;
+        // eprintln!("time is now {}", self.simtime);
+        sc.big_step(Some(entry.event), self, output);
+        self.queue.pop();
+      }
+      else {
+        break 'running;
       }
     }
   }

+ 82 - 80
src/rust/statechart/statechart.rs

@@ -1,93 +1,95 @@
-mod sccd {
-  // Helpers and traits implemented by, or used by from-statechart-generated Rust code.
-  mod statechart {
+// Helpers and traits implemented by, or used by from-statechart-generated Rust code.
 
-    pub trait EventLifeline<T> {
-      fn get(&self) -> &T;
-      fn get_mut(&mut self) -> &mut T;
-      fn cycle(&mut self);
-    }
+pub trait EventLifeline<T> {
+  fn get(&self) -> &T;
+  fn get_mut(&mut self) -> &mut T;
+  fn cycle(&mut self);
+}
 
-    #[derive(Default)]
-    pub struct SameRoundLifeline<T> {
-      current: T,
-    }
+#[derive(Default)]
+pub struct SameRoundLifeline<T> {
+  current: T,
+}
 
-    impl<T: Default> EventLifeline<T> for SameRoundLifeline<T> {
-      fn get(&self) -> &T {
-        &self.current
-      }
-      fn get_mut(&mut self) -> &mut T {
-        &mut self.current
-      }
-      fn cycle(&mut self) {
-        // Reset
-        self.current = Default::default()
-      }
-    }
+impl<T: Default> EventLifeline<T> for SameRoundLifeline<T> {
+  fn get(&self) -> &T {
+    &self.current
+  }
+  fn get_mut(&mut self) -> &mut T {
+    &mut self.current
+  }
+  fn cycle(&mut self) {
+    // Reset
+    self.current = Default::default()
+  }
+}
 
-    enum Which {
-      One,
-      Two,
-    }
-    impl Default for Which {
-      fn default() -> Self {
-        Self::One
-      }
-    }
+enum Which {
+  One,
+  Two,
+}
+impl Default for Which {
+  fn default() -> Self {
+    Self::One
+  }
+}
 
-    #[derive(Default)]
-    pub struct NextRoundLifeline<T> {
-      one: T,
-      two: T,
+#[derive(Default)]
+pub struct NextRoundLifeline<T> {
+  one: T,
+  two: T,
 
-      current: Which,
-    }
+  current: Which,
+}
 
-    impl<T: Default> EventLifeline<T> for NextRoundLifeline<T> {
-      fn get(&self) -> &T {
-        match self.current {
-          Which::One => &self.one,
-          Which::Two => &self.two,
-        }
-      }
-      fn get_mut(&mut self) -> &mut T {
-        match self.current {
-          Which::One => &mut self.one,
-          Which::Two => &mut self.two,
-        }
-      }
-      fn cycle(&mut self) {
-        match self.current {
-          Which::One => {
-            self.one = Default::default();
-            self.current = Which::Two;
-          },
-          Which::Two => {
-            self.two = Default::default();
-            self.current = Which::One;
-          },
-        };
-      }
+impl<T: Default> EventLifeline<T> for NextRoundLifeline<T> {
+  fn get(&self) -> &T {
+    match self.current {
+      Which::One => &self.one,
+      Which::Two => &self.two,
+    }
+  }
+  fn get_mut(&mut self) -> &mut T {
+    match self.current {
+      Which::One => &mut self.one,
+      Which::Two => &mut self.two,
     }
+  }
+  fn cycle(&mut self) {
+    match self.current {
+      Which::One => {
+        self.one = Default::default();
+        self.current = Which::Two;
+      },
+      Which::Two => {
+        self.two = Default::default();
+        self.current = Which::One;
+      },
+    };
+  }
+}
 
-    pub type Timestamp = u32;
+// For simplicity, we assume timestamps are always 32-bit unsigned integers.
+// This type is large enough for practical purposes, and has better performance than 64-bit unsigned integers, even on amd64 architectures.
+pub type Timestamp = u32;
 
-    pub trait Scheduler<InEvent, EntryId> {
-      fn set_timeout(&mut self, delay: Timestamp, event: InEvent) -> EntryId;
-      fn unset_timeout(&mut self, id: EntryId);
-    }
+// This trait defines the scheduler-operations a statechart may call during a step.
+// By defining them in a trait, the generated statechart code can be independent of the
+// scheduler (event queue) implementation.
+pub trait Scheduler<InEvent, EntryId> {
+  fn set_timeout(&mut self, delay: Timestamp, event: InEvent) -> EntryId;
+  fn unset_timeout(&mut self, id: EntryId);
+}
 
-    pub trait SC<InEvent, EntryId, Sched: Scheduler<InEvent, EntryId>, OutputCallback> {
-      fn init(&mut self, sched: &mut Sched, output: &mut OutputCallback);
-      fn big_step(&mut self, event: Option<InEvent>, sched: &mut Sched, output: &mut OutputCallback);
-    }
+// Generated statechart types will implement this trait.
+pub trait SC<InEvent, EntryId, Sched: Scheduler<InEvent, EntryId>, OutputCallback> {
+  fn init(&mut self, sched: &mut Sched, output: &mut OutputCallback);
+  fn big_step(&mut self, event: Option<InEvent>, sched: &mut Sched, output: &mut OutputCallback);
+}
 
-    // TODO: Does not belong in "common", this should become a statechart-specific enum-type.
-    #[derive(Debug, Eq, PartialEq)]
-    pub struct OutEvent {
-      port: &'static str,
-      event: &'static str,
-    }
-  }
+// TODO: Does not belong in "common", this should become a statechart-specific enum-type.
+#[derive(Debug, Eq, PartialEq)]
+pub struct OutEvent {
+  port: &'static str,
+  event: &'static str,
 }

+ 2 - 2
src/sccd/statechart/codegen/rust.py

@@ -77,7 +77,7 @@ class StatechartRustGenerator(ActionLangRustGenerator):
         self.w.writeln("(output)(OutEvent{port:\"%s\", event:\"%s\"});" % (a.outport, a.name))
 
     def visit_RaiseInternalEvent(self, a):
-        self.w.writeln("internal.raise().%s = Some(%s{});" % (ident_event_field(a.name), (ident_event_type(a.name))))
+        self.w.writeln("internal.get_mut().%s = Some(%s{});" % (ident_event_field(a.name), (ident_event_type(a.name))))
 
     def visit_Code(self, a):
             a.block.accept(self)
@@ -462,7 +462,7 @@ class StatechartRustGenerator(ActionLangRustGenerator):
                             if bit(e.id) & input_events:
                                 condition.append("let Some(InEvent::%s) = &input" % ident_event_type(e.name))
                             elif bit(e.id) & internal_events:
-                                condition.append("let Some(%s) = &internal.current().%s" % (ident_event_type(e.name), ident_event_field(e.name)))
+                                condition.append("let Some(%s) = &internal.get().%s" % (ident_event_type(e.name), ident_event_field(e.name)))
                             else:
                                 # Bug in SCCD :(
                                 raise Exception("Illegal event ID")