memory_snapshot.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. from sccd.action_lang.dynamic.memory import *
  2. from sccd.util import timer
  3. class StatechartMemoryInterface(MemoryInterface):
  4. # Notify the memory implementation that a transition was completed.
  5. @abstractmethod
  6. def flush_transition(self):
  7. pass
  8. # Notify the memory implementation that its 'round' was completed and therefore the snapshot should be refreshed.
  9. @abstractmethod
  10. def flush_round(self):
  11. pass
  12. # Non-snapshotting memory. Basically delegates all operations.
  13. class StatechartMemory(StatechartMemoryInterface):
  14. __slots__ = ["delegate"]
  15. def __init__(self, delegate: MemoryInterface):
  16. self.delegate = delegate
  17. def current_frame(self) -> StackFrame:
  18. return self.delegate.current_frame()
  19. def push_frame(self, scope: Scope):
  20. return self.delegate.push_frame(scope)
  21. def push_frame_w_context(self, scope: Scope, context: StackFrame):
  22. return self.delegate.push_frame_w_context(scope, context)
  23. def pop_frame(self):
  24. return self.delegate.pop_frame()
  25. def load(self, offset: int) -> Any:
  26. return self.delegate.load(offset)
  27. def store(self, offset: int, value: Any):
  28. self.delegate.store(offset, value)
  29. def _get_frame(self, offset):
  30. return self.delegate._get_frame(offset)
  31. # No shapshots are maintained, so the following 2 methods don't have to do anything:
  32. def flush_transition(self):
  33. pass
  34. def flush_round(self):
  35. pass
  36. # Snapshotting memory. Takes snapshots of a single frame in memory.
  37. class SnapshottingStatechartMemory(StatechartMemory):
  38. __slots__ = ["frame", "actual", "snapshot", "trans_dirty", "round_dirty"]
  39. def __init__(self, delegate: MemoryInterface, frame: StackFrame):
  40. super().__init__(delegate)
  41. self.frame = frame # frame to be snapshotted
  42. self.actual: List[Any] = self.frame.storage
  43. self.snapshot: List[Any] = list(self.actual)
  44. # Positions in stack frame written to by current transition.
  45. self.trans_dirty = Bitmap()
  46. # Positions in stack frame written to during current big, combo or small step (depending on semantic option chosen)
  47. self.round_dirty = Bitmap()
  48. # override
  49. def load(self, offset: int) -> Any:
  50. frame, offset = self.delegate._get_frame(offset)
  51. # Sometimes read from snapshot
  52. if frame is self.frame:
  53. # "our" frame! :)
  54. if bm_has(self.trans_dirty, offset):
  55. return self.actual[offset]
  56. else:
  57. return self.snapshot[offset]
  58. else:
  59. return frame.storage[offset]
  60. # override
  61. def store(self, offset: int, value: Any):
  62. frame, offset = self.delegate._get_frame(offset)
  63. if frame is self.frame:
  64. # Remember that we wrote, such that next read during same transition will be the value we wrote.
  65. self.trans_dirty |= bit(offset)
  66. # Always write to 'actual' storage
  67. frame.storage[offset] = value
  68. # override
  69. def flush_transition(self):
  70. race_conditions = self.trans_dirty & self.round_dirty
  71. if race_conditions:
  72. variables = self.frame.scope.variables
  73. # some variable written to twice before refresh
  74. raise ModelRuntimeError("Race condition: More than one transition assigned a new value to variables: %s" % (", ".join(variables[offset].name for offset in bm_items(race_conditions))))
  75. self.round_dirty |= self.trans_dirty
  76. self.trans_dirty = Bitmap() # reset
  77. # override
  78. def flush_round(self):
  79. assert not self.trans_dirty # only allowed to be called right after flush_temp
  80. # Probably quickest to just copy the entire list in Python
  81. self.snapshot = list(self.actual) # refresh
  82. self.round_dirty = Bitmap() # reset
  83. # Treats a single frame in memory as read-only.
  84. class ReadOnlyStatechartMemory(StatechartMemory):
  85. __slots__ = ["frame"]
  86. def __init__(self, delegate: StatechartMemory, frame: StackFrame):
  87. super().__init__(delegate)
  88. self.frame = frame
  89. # override
  90. def store(self, offset: int, value: Any):
  91. frame, offset = self.delegate._get_frame(offset)
  92. if frame is self.frame:
  93. raise ModelRuntimeError("Attempt to write to read-only memory.")
  94. self.delegate.store(offset, value)
  95. # override
  96. def flush_transition(self):
  97. self.delegate.flush_transition()
  98. # override
  99. def flush_round(self):
  100. self.delegate.flush_round()