reactor: Always create new greenlets from main process

If a greenlet is created from a greenlet then it seems the Python
interpreter will increment its call depth counter and that can
eventually lead to a RecursionError exception being raised.

To avoid this issue, switch back to the main run() instance if a new
greenlet needs to be created.

Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor
2026-03-18 21:44:30 -04:00
committed by KevinOConnor
parent 1370cf5c18
commit 51ceda9ede

View File

@@ -231,21 +231,22 @@ class SelectReactor:
return self._sys_pause(waketime)
if self._prevent_pause_count:
self.verify_can_pause()
# Determine if this greenlet is the main dispatch greenlet
g = greenlet.getcurrent()
if g is not self._g_dispatch:
# Switch to _check_timers (via g.timer.callback return)
# This greenlet has called pause() before and has a timer setup,
# so switch to _check_timers (via g.timer.callback return)
return self._g_dispatch.switch(waketime)
# Pausing the dispatch greenlet - prepare a new greenlet to do dispatch
if self._greenlets:
g_next = self._greenlets.pop()
else:
g_next = ReactorGreenlet(run=self._dispatch_loop)
self._all_greenlets.append(g_next)
g_next.parent = g.parent
# Pausing the dispatch greenlet - setup timer to resume this greenlet
g.timer = self.register_timer(g.switch, waketime)
self._next_timer = self.NOW
# Switch to _dispatch_loop (via _end_greenlet or direct)
eventtime = g_next.switch()
if self._greenlets:
# Switch to _end_greenlet to activate cached dispatch greenlet
g_next = self._greenlets.pop()
eventtime = g_next.switch()
else:
# No cached greenlets, switch to run() to create new dispatcher
eventtime = g.parent.switch()
# This greenlet activated from g.timer.callback (via _check_timers)
return eventtime
def _end_greenlet(self, g_old):
@@ -323,9 +324,10 @@ class SelectReactor:
self._setup_async_callbacks()
self._process = True
self._prevent_pause_count = 0
g_next = ReactorGreenlet(run=self._dispatch_loop)
self._all_greenlets.append(g_next)
g_next.switch()
while self._process:
g_next = ReactorGreenlet(run=self._dispatch_loop)
self._all_greenlets.append(g_next)
g_next.switch()
def end(self):
self._process = False
def finalize(self):