Avoid recursing in the scheduler
By doing this, we don't have to worry about hitting the recursion limit.
It is still possible to recurse through `NullTrigger`, but I will attempt to handle that in a later patch.
@@ -317,17 +317,19 @@ class Scheduler(object):
self.schedule(test)
self.advance()
- def react(self, trigger, depth=0):
+ def react(self, trigger):
"""React called when a trigger fires.
We find any coroutines that are waiting on the particular trigger and
schedule them.
"""
- if _profiling and not depth:
+ if _profiling:
ctx = profiling_context()
else:
ctx = nullcontext()
+ called_by_simulator = isinstance(trigger, GPITrigger)
+
with ctx:
# When a trigger fires it is unprimed internally
if _debug:
@@ -359,6 +361,8 @@ class Scheduler(object):
self._readwrite.unprime()
+ # this should be the only trigger, probably?
+ assert not self._pending_triggers
return
# Similarly if we've scheduled our next_timestep on way to readwrite
@@ -372,8 +376,26 @@ class Scheduler(object):
"Priming ReadWrite trigger so we can playback writes")
self._readwrite.prime(self.react)
+ # this should be the only trigger, probably?
+ assert not self._pending_triggers
return
+ # work through triggers one by one
+ is_first = True
+ self._pending_triggers.append(trigger)
+ while self._pending_triggers:
+ trigger = self._pending_triggers.pop(0)
+
+ if not is_first and isinstance(trigger, GPITrigger):
+ self.log.warning(
+ "A GPI trigger occurred after entering react - this "
+ "should not happen."
+ )
+ assert False
+
+ # this only exists to enable the warning above
+ is_first = False
+
if trigger not in self._trigger2coros:
# GPI triggers should only be ever pending if there is an
@@ -393,7 +415,7 @@ class Scheduler(object):
"No coroutines waiting on trigger that fired: %s" %
str(trigger))
- return
+ continue
# Scheduled coroutines may append to our waiting list so the first
# thing to do is pop all entries waiting on this trigger.
@@ -432,7 +454,6 @@ class Scheduler(object):
if _debug:
self.log.debug("Scheduled coroutine %s" % (coro.__name__))
- if not depth:
# Schedule may have queued up some events so we'll burn through those
while self._pending_events:
if _debug:
@@ -440,14 +461,8 @@ class Scheduler(object):
(str(self._pending_events[0])))
self._pending_events.pop(0).set()
- while self._pending_triggers:
- if _debug:
- self.log.debug("Scheduling pending trigger %s" %
- (str(self._pending_triggers[0])))
- self.react(self._pending_triggers.pop(0), depth=depth + 1)
-
# We only advance for GPI triggers
- if not depth and isinstance(trigger, GPITrigger):
+ if called_by_simulator:
self.advance()
if _debug:
