Skip to content

Commit

Permalink
glib_selector: Only ever iterate the mainloop once
Browse files Browse the repository at this point in the history
As it is right now, any python code that runs may modify the timeout
that would be passed into the select() call. As such, we can only run a
single mainloop iteration before the select() call needs to be
restarted.

Also, we cannot use g_source_set_ready_time, because GLib will always
do a full mainloop iteration afterwards to ensure that all sources had a
chance to dispatch.
This is easy to work around though as we can use the prepare callback to
pass the required timeout from python into the GLib main loop code.

Note that with this we end up iterating the main context but we never
actually run a GLib mainloop.

Fixes: jhenstridge#3
  • Loading branch information
Benjamin Berg committed Mar 17, 2021
1 parent 7d6db7f commit 1d46e54
Showing 1 changed file with 10 additions and 27 deletions.
37 changes: 10 additions & 27 deletions asyncio_glib/glib_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,17 @@
'GLibSelector',
)


# The override for GLib.MainLoop.run installs a signal wakeup fd,
# which interferes with asyncio signal handlers. Try to get the
# direct version.
try:
g_main_loop_run = super(GLib.MainLoop, GLib.MainLoop).run
except AttributeError:
g_main_loop_run = GLib.MainLoop.run


class _SelectorSource(GLib.Source):
"""A GLib source that gathers selector """

def __init__(self, main_loop):
def __init__(self, selector):
super().__init__()
self._fd_to_tag = {}
self._fd_to_events = {}
self._main_loop = main_loop
self._iteration_timeout = 0

def prepare(self):
return False, -1
return False, self._iteration_timeout

def check(self):
return False
Expand All @@ -41,7 +31,6 @@ def dispatch(self, callback, args):
if condition & GLib.IOCondition.OUT:
events |= selectors.EVENT_WRITE
self._fd_to_events[fd] = events
self._main_loop.quit()
return GLib.SOURCE_CONTINUE

def register(self, fd, events):
Expand Down Expand Up @@ -70,8 +59,7 @@ class GLibSelector(selectors._BaseSelectorImpl):
def __init__(self, context):
super().__init__()
self._context = context
self._main_loop = GLib.MainLoop.new(self._context, False)
self._source = _SelectorSource(self._main_loop)
self._source = _SelectorSource(self)
self._source.attach(self._context)

def close(self):
Expand All @@ -89,20 +77,15 @@ def unregister(self, fileobj):
return key

def select(self, timeout=None):
may_block = True
self._source.set_ready_time(-1)
# Calling .set_ready_time() always causes a mainloop iteration to finish.
if timeout is not None:
if timeout > 0:
self._source.set_ready_time(
GLib.get_monotonic_time() + int(timeout * 1000000))
else:
may_block = False
# Negative timeout implies an immediate dispatch
self._source._iteration_timeout = int(max(0, timeout) * 1000)
else:
self._source._iteration_timeout = -1

self._source.clear()
if may_block:
g_main_loop_run(self._main_loop)
else:
self._context.iteration(False)
self._context.iteration(True)

ready = []
for key in self.get_map().values():
Expand Down

0 comments on commit 1d46e54

Please sign in to comment.