statechart_instance.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import termcolor
  2. import functools
  3. from typing import List, Tuple, Iterable
  4. from sccd.util.debug import print_debug
  5. from sccd.util.bitmap import *
  6. from sccd.statechart.static.statechart import *
  7. from sccd.statechart.static import priority, concurrency
  8. from sccd.statechart.dynamic.builtin_scope import *
  9. from sccd.statechart.dynamic.round import *
  10. from sccd.statechart.dynamic.statechart_execution import *
  11. from sccd.statechart.dynamic.memory_snapshot import *
  12. # Interface for all instances and also the Object Manager
  13. class Instance(ABC):
  14. @abstractmethod
  15. def initialize(self):
  16. pass
  17. @abstractmethod
  18. def big_step(self, input_events: List[InternalEvent]):
  19. pass
  20. # Hardcoded limit on number of sub-rounds of combo and big step to detect never-ending superrounds.
  21. # TODO: make this a model parameter
  22. LIMIT = 100
  23. # An "instance" in the context of the SCCD runtime
  24. class StatechartInstance(Instance):
  25. def __init__(self, statechart: Statechart, object_manager, output_callback, schedule_callback, cancel_callback):
  26. # self.object_manager = object_manager
  27. self.output_callback = output_callback
  28. self.execution = StatechartExecution(self, statechart)
  29. semantics = statechart.semantics
  30. if semantics.has_multiple_variants():
  31. raise Exception("Cannot execute model with multiple semantics.")
  32. # Priority semantics
  33. with timer.Context("priority"):
  34. graph = priority.get_graph(statechart.tree, semantics)
  35. consistency = {
  36. Concurrency.SINGLE: concurrency.NoConcurrency(),
  37. Concurrency.MANY: concurrency.ArenaOrthogonal(),
  38. }[semantics.concurrency]
  39. priority_ordered_transitions = priority.generate_total_ordering(statechart.tree, graph, consistency)
  40. # strategy = CurrentConfigStrategy(priority_ordered_transitions)
  41. strategy = EnabledEventsStrategy(priority_ordered_transitions, statechart)
  42. # strategy = CurrentConfigAndEnabledEventsStrategy(priority_ordered_transitions, statechart)
  43. if semantics.concurrency == Concurrency.SINGLE:
  44. generator = CandidateGenerator(strategy)
  45. elif semantics.concurrency == Concurrency.MANY:
  46. generator = ConcurrentCandidateGenerator(strategy, synchronous=semantics.internal_event_lifeline == InternalEventLifeline.SAME)
  47. else:
  48. raise Exception("Unsupported option: %s" % semantics.concurrency)
  49. # Big step + combo step maximality semantics
  50. small_step = SmallStep(termcolor.colored("small", 'blue'), self.execution, generator)
  51. if semantics.big_step_maximality == Maximality.TAKE_ONE:
  52. self._big_step = combo_step = SuperRound(termcolor.colored("big_one", 'red'), subround=small_step, maximality=TakeOne()) # No combo steps
  53. elif semantics.big_step_maximality == Maximality.TAKE_MANY or semantics.big_step_maximality == Maximality.SYNTACTIC:
  54. # Always add a layer of 'fairness' above our small steps, so
  55. # orthogonal transitions take turns fairly.
  56. combo_one = SuperRound(termcolor.colored("combo_one", 'magenta'), subround=small_step, maximality=TakeOne())
  57. if semantics.combo_step_maximality == Maximality.TAKE_ONE:
  58. # Fairness round becomes our combo step round
  59. combo_step = combo_one
  60. elif semantics.combo_step_maximality == Maximality.TAKE_MANY:
  61. # Add even more layers, basically an onion at this point.
  62. combo_step = SuperRound(termcolor.colored("combo_many", 'cyan'), subround=combo_one, maximality=TakeMany(), limit=LIMIT)
  63. elif semantics.combo_step_maximality == Maximality.SYNTACTIC:
  64. combo_step = SuperRound(termcolor.colored("combo_syntactic", 'cyan'), subround=combo_one, maximality=Syntactic(), limit=LIMIT)
  65. else:
  66. raise Exception("Unsupported option: %s" % semantics.combo_step_maximality)
  67. if semantics.big_step_maximality == Maximality.TAKE_MANY:
  68. self._big_step = SuperRound(termcolor.colored("big_many", 'red'), subround=combo_step, maximality=TakeMany(), limit=LIMIT)
  69. elif semantics.big_step_maximality == Maximality.SYNTACTIC:
  70. self._big_step = SuperRound(termcolor.colored("big_syntactic", 'red'), subround=combo_step, maximality=Syntactic(), limit=LIMIT)
  71. else:
  72. raise Exception("Unsupported option: %s" % semantics.big_step_maximality)
  73. # Event lifeline semantics
  74. def whole(input):
  75. self._big_step.remainder_events.extend(input)
  76. def first_combo(input):
  77. combo_step.remainder_events.extend(input)
  78. def first_small(input):
  79. small_step.remainder_events.extend(input)
  80. self.set_input = {
  81. InputEventLifeline.WHOLE: whole,
  82. InputEventLifeline.FIRST_COMBO_STEP: first_combo,
  83. InputEventLifeline.FIRST_SMALL_STEP: first_small
  84. }[semantics.input_event_lifeline]
  85. self.self_list = [self]
  86. raise_internal = {
  87. InternalEventLifeline.QUEUE: lambda e: schedule_callback(0, e, self.self_list),
  88. InternalEventLifeline.NEXT_COMBO_STEP: combo_step.add_next_event,
  89. InternalEventLifeline.NEXT_SMALL_STEP: small_step.add_next_event,
  90. InternalEventLifeline.REMAINDER: self._big_step.add_remainder_event,
  91. InternalEventLifeline.SAME: small_step.add_remainder_event,
  92. }[semantics.internal_event_lifeline]
  93. # Memory protocol semantics
  94. memory = Memory()
  95. load_builtins(memory, self.execution)
  96. memory.push_frame(statechart.scope)
  97. instance_frame = memory.current_frame() # this is the stack frame that contains the statechart's "instance" variables
  98. def get_memory_protocol(protocol: MemoryProtocol) -> StatechartMemory:
  99. if protocol == MemoryProtocol.SMALL_STEP and semantics.concurrency == Concurrency.SINGLE:
  100. return StatechartMemory(delegate=memory) # no snapshots
  101. else:
  102. m = SnapshottingStatechartMemory(delegate=memory, frame=instance_frame)
  103. # refresh snapshot at end of big/combo/small step:
  104. if protocol == MemoryProtocol.BIG_STEP:
  105. self._big_step.when_done(m.flush_round)
  106. elif protocol == MemoryProtocol.COMBO_STEP:
  107. combo_step.when_done(m.flush_round)
  108. elif protocol == MemoryProtocol.SMALL_STEP:
  109. small_step.when_done(m.flush_round)
  110. return m
  111. if semantics.enabledness_memory_protocol == semantics.assignment_memory_protocol:
  112. # use the same memory object for RHS and GC, for performance
  113. gc_memory = rhs_memory = get_memory_protocol(semantics.assignment_memory_protocol)
  114. else:
  115. rhs_memory = get_memory_protocol(semantics.assignment_memory_protocol)
  116. gc_memory = get_memory_protocol(semantics.enabledness_memory_protocol)
  117. # Finally, wrap gc_memory in a layer of 'read-onlyness'
  118. gc_memory = ReadOnlyStatechartMemory(gc_memory, frame=instance_frame)
  119. print_debug("\nRound hierarchy: " + str(self._big_step) + '\n')
  120. self.execution.gc_memory = gc_memory
  121. self.execution.rhs_memory = rhs_memory
  122. self.execution.raise_internal = raise_internal
  123. self.execution.schedule_callback = schedule_callback
  124. self.execution.cancel_callback = cancel_callback
  125. self.execution.raise_output = output_callback
  126. # enter default states, generating a set of output events
  127. def initialize(self):
  128. self.execution.initialize()
  129. self.output_callback(OutputEvent(port="trace", name="big_step_completed", params=self.self_list))
  130. # perform a big step. generating a set of output events
  131. def big_step(self, input_events: List[InternalEvent]):
  132. with timer.Context("big step"):
  133. # print_debug('attempting big step, input_events='+str(input_events))
  134. self._big_step.reset()
  135. self.set_input(input_events)
  136. self._big_step.run_and_cycle_events()
  137. self.output_callback(OutputEvent(port="trace", name="big_step_completed", params=self.self_list))