threads_platform.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. from sccd.realtime.time import *
  2. from sccd.controller.controller import *
  3. from sccd.util.duration import *
  4. import threading
  5. # Thread-safe real-time Controller execution in a thread
  6. class ThreadsPlatform:
  7. def __init__(self, controller: Controller):
  8. self.controller = controller
  9. self.timer = Timer(impl=DefaultTimeImplementation, unit=self.controller.cd.get_delta())
  10. self.lock = threading.Lock() # A queue would also work, but because of Python's GIL, a queue would not perform better.
  11. self.condition = threading.Condition()
  12. # keep simulation responsive even if computer cannot keep up
  13. self.purposefully_behind = 0
  14. def create_controller_thread(self) -> threading.Thread:
  15. def thread():
  16. # condition object expects fractions of seconds
  17. to_condition_wait_time = get_conversion_f_float(self.controller.cd.get_delta(), duration(1, Second))
  18. # simulation starts "now" (wall-clock time)
  19. self.timer.start()
  20. while True:
  21. with self.lock:
  22. self.controller.run_until(self.timer.now() + self.purposefully_behind) # may take a while
  23. next_wakeup = self.controller.next_wakeup()
  24. if next_wakeup is not None:
  25. sleep_duration = next_wakeup - self.timer.now()
  26. if sleep_duration < 0:
  27. self.purposefully_behind = sleep_duration
  28. sleep_duration = 0
  29. else:
  30. self.purposefully_behind = 0
  31. with self.condition:
  32. self.condition.wait(to_condition_wait_time(sleep_duration))
  33. else:
  34. with self.condition:
  35. self.condition.wait()
  36. return threading.Thread(target=thread)
  37. def now(self):
  38. return self.timer.now() + self.purposefully_behind
  39. # Add an input event with timestamp "now" (wall-clock time)
  40. # Safe to call this method from any thread.
  41. def add_input_now(self, port, event_name, params=[]):
  42. with self.lock:
  43. self.controller.add_input(timestamp=self.timer.now(), port=port, event_name=event_name, params=params)
  44. with self.condition:
  45. self.condition.notify() # wake up controller thread if sleeping