statechart_instance.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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. priority_ordered_transitions = priority.priority_and_concurrency(statechart)
  35. # strategy = CurrentConfigStrategy(priority_ordered_transitions)
  36. strategy = EnabledEventsStrategy(priority_ordered_transitions, statechart)
  37. # strategy = CurrentConfigAndEnabledEventsStrategy(priority_ordered_transitions, statechart)
  38. if semantics.concurrency == Concurrency.SINGLE:
  39. generator = CandidateGenerator(strategy)
  40. elif semantics.concurrency == Concurrency.MANY:
  41. generator = ConcurrentCandidateGenerator(strategy, synchronous=semantics.internal_event_lifeline == InternalEventLifeline.SAME)
  42. else:
  43. raise Exception("Unsupported option: %s" % semantics.concurrency)
  44. # Big step + combo step maximality semantics
  45. small_step = SmallStep(termcolor.colored("small", 'blue'), self.execution, generator)
  46. if semantics.big_step_maximality == Maximality.TAKE_ONE:
  47. # If Big-Step Maximality is Take One, we disable combo steps.
  48. # This is not entirely according to the BSML spec, but in 99% of cases, this is what you'd want.
  49. # If we did not do this, we would have to allow the user to explicitly disable combo-steps instead.
  50. self._big_step = combo_step = SuperRound(termcolor.colored("big_one", 'red'), subround=small_step, maximality=TakeOne()) # No combo steps
  51. elif semantics.big_step_maximality == Maximality.TAKE_MANY or semantics.big_step_maximality == Maximality.SYNTACTIC:
  52. # Always add a layer of 'fairness' above our small steps, so
  53. # orthogonal transitions take turns fairly.
  54. combo_one = SuperRound(termcolor.colored("combo_one", 'magenta'), subround=small_step, maximality=TakeOne())
  55. if semantics.combo_step_maximality == Maximality.TAKE_ONE:
  56. # Fairness round becomes our combo step round
  57. combo_step = combo_one
  58. elif semantics.combo_step_maximality == Maximality.TAKE_MANY:
  59. # Add even more layers, basically an onion at this point.
  60. combo_step = SuperRound(termcolor.colored("combo_many", 'cyan'), subround=combo_one, maximality=TakeMany(), limit=LIMIT)
  61. elif semantics.combo_step_maximality == Maximality.SYNTACTIC:
  62. combo_step = SuperRound(termcolor.colored("combo_syntactic", 'cyan'), subround=combo_one, maximality=Syntactic(), limit=LIMIT)
  63. else:
  64. raise Exception("Unsupported option: %s" % semantics.combo_step_maximality)
  65. if semantics.big_step_maximality == Maximality.TAKE_MANY:
  66. self._big_step = SuperRound(termcolor.colored("big_many", 'red'), subround=combo_step, maximality=TakeMany(), limit=LIMIT)
  67. elif semantics.big_step_maximality == Maximality.SYNTACTIC:
  68. self._big_step = SuperRound(termcolor.colored("big_syntactic", 'red'), subround=combo_step, maximality=Syntactic(), limit=LIMIT)
  69. else:
  70. raise Exception("Unsupported option: %s" % semantics.big_step_maximality)
  71. # Event lifeline semantics
  72. def whole(input):
  73. self._big_step.remainder_events.extend(input)
  74. def first_combo(input):
  75. combo_step.remainder_events.extend(input)
  76. def first_small(input):
  77. small_step.remainder_events.extend(input)
  78. self.set_input = {
  79. InputEventLifeline.WHOLE: whole,
  80. InputEventLifeline.FIRST_COMBO_STEP: first_combo,
  81. InputEventLifeline.FIRST_SMALL_STEP: first_small
  82. }[semantics.input_event_lifeline]
  83. self.self_list = [self]
  84. raise_internal = {
  85. InternalEventLifeline.QUEUE: lambda e: schedule_callback(0, e, self.self_list),
  86. InternalEventLifeline.NEXT_COMBO_STEP: combo_step.add_next_event,
  87. InternalEventLifeline.NEXT_SMALL_STEP: small_step.add_next_event,
  88. InternalEventLifeline.REMAINDER: self._big_step.add_remainder_event,
  89. InternalEventLifeline.SAME: small_step.add_remainder_event,
  90. }[semantics.internal_event_lifeline]
  91. # Memory protocol semantics
  92. memory = Memory()
  93. load_builtins(memory, self.execution)
  94. memory.push_frame(statechart.scope)
  95. instance_frame = memory.current_frame() # this is the stack frame that contains the statechart's "instance" variables
  96. def get_memory_protocol(protocol: MemoryProtocol) -> StatechartMemory:
  97. if protocol == MemoryProtocol.SMALL_STEP and semantics.concurrency == Concurrency.SINGLE:
  98. return StatechartMemory(delegate=memory) # no snapshots
  99. else:
  100. m = SnapshottingStatechartMemory(delegate=memory, frame=instance_frame)
  101. # refresh snapshot at end of big/combo/small step:
  102. if protocol == MemoryProtocol.BIG_STEP:
  103. self._big_step.when_done(m.flush_round)
  104. elif protocol == MemoryProtocol.COMBO_STEP:
  105. combo_step.when_done(m.flush_round)
  106. elif protocol == MemoryProtocol.SMALL_STEP:
  107. small_step.when_done(m.flush_round)
  108. return m
  109. if semantics.enabledness_memory_protocol == semantics.assignment_memory_protocol:
  110. # use the same memory object for RHS and GC, for performance
  111. gc_memory = rhs_memory = get_memory_protocol(semantics.assignment_memory_protocol)
  112. else:
  113. rhs_memory = get_memory_protocol(semantics.assignment_memory_protocol)
  114. gc_memory = get_memory_protocol(semantics.enabledness_memory_protocol)
  115. # Finally, wrap gc_memory in a layer of 'read-onlyness'
  116. gc_memory = ReadOnlyStatechartMemory(gc_memory, frame=instance_frame)
  117. print_debug("\nRound hierarchy: " + str(self._big_step) + '\n')
  118. self.execution.gc_memory = gc_memory
  119. self.execution.rhs_memory = rhs_memory
  120. self.execution.raise_internal = raise_internal
  121. self.execution.schedule_callback = schedule_callback
  122. self.execution.cancel_callback = cancel_callback
  123. self.execution.raise_output = output_callback
  124. # enter default states, generating a set of output events
  125. def initialize(self):
  126. self.execution.initialize()
  127. self.output_callback(OutputEvent(port="trace", name="big_step_completed", params=self.self_list))
  128. # perform a big step. generating a set of output events
  129. def big_step(self, input_events: List[InternalEvent]):
  130. with timer.Context("big step"):
  131. # print_debug('attempting big step, input_events='+str(input_events))
  132. self._big_step.reset()
  133. self.set_input(input_events)
  134. self._big_step.run_and_cycle_events()
  135. self.output_callback(OutputEvent(port="trace", name="big_step_completed", params=self.self_list))