Skip to content

Commit 76c86d0

Browse files
author
Benjamin Berg
committed
glib_selector: Only ever iterate the mainloop once
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: #3
1 parent 7d6db7f commit 76c86d0

File tree

1 file changed

+15
-27
lines changed

1 file changed

+15
-27
lines changed

asyncio_glib/glib_selector.py

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,17 @@
77
'GLibSelector',
88
)
99

10-
11-
# The override for GLib.MainLoop.run installs a signal wakeup fd,
12-
# which interferes with asyncio signal handlers. Try to get the
13-
# direct version.
14-
try:
15-
g_main_loop_run = super(GLib.MainLoop, GLib.MainLoop).run
16-
except AttributeError:
17-
g_main_loop_run = GLib.MainLoop.run
18-
19-
2010
class _SelectorSource(GLib.Source):
2111
"""A GLib source that gathers selector """
2212

23-
def __init__(self, main_loop):
13+
def __init__(self, selector):
2414
super().__init__()
2515
self._fd_to_tag = {}
2616
self._fd_to_events = {}
27-
self._main_loop = main_loop
17+
self._selector = selector
2818

2919
def prepare(self):
30-
return False, -1
20+
return False, self._selector._get_timeout_ms()
3121

3222
def check(self):
3323
return False
@@ -41,7 +31,6 @@ def dispatch(self, callback, args):
4131
if condition & GLib.IOCondition.OUT:
4232
events |= selectors.EVENT_WRITE
4333
self._fd_to_events[fd] = events
44-
self._main_loop.quit()
4534
return GLib.SOURCE_CONTINUE
4635

4736
def register(self, fd, events):
@@ -70,9 +59,9 @@ class GLibSelector(selectors._BaseSelectorImpl):
7059
def __init__(self, context):
7160
super().__init__()
7261
self._context = context
73-
self._main_loop = GLib.MainLoop.new(self._context, False)
74-
self._source = _SelectorSource(self._main_loop)
62+
self._source = _SelectorSource(self)
7563
self._source.attach(self._context)
64+
self._timeout = -1
7665

7766
def close(self):
7867
self._source.destroy()
@@ -88,21 +77,20 @@ def unregister(self, fileobj):
8877
self._source.unregister(key.fd)
8978
return key
9079

80+
def _get_timeout_ms(self):
81+
"""Return the timeout for the current select/iteration"""
82+
return self._timeout
83+
9184
def select(self, timeout=None):
92-
may_block = True
93-
self._source.set_ready_time(-1)
85+
# Calling .set_ready_time() always causes a mainloop iteration to finish.
9486
if timeout is not None:
95-
if timeout > 0:
96-
self._source.set_ready_time(
97-
GLib.get_monotonic_time() + int(timeout * 1000000))
98-
else:
99-
may_block = False
87+
# Negative timeout implies an immediate dispatch
88+
self._timeout = int(max(0, timeout) * 1000)
89+
else:
90+
self._timeout = -1
10091

10192
self._source.clear()
102-
if may_block:
103-
g_main_loop_run(self._main_loop)
104-
else:
105-
self._context.iteration(False)
93+
self._context.iteration(True)
10694

10795
ready = []
10896
for key in self.get_map().values():

0 commit comments

Comments
 (0)