Skip to content

Commit 2caed7d

Browse files
committed
asyncio: Handle end of stream condition properly.
By removing any IO watches for associated file handle. The way it's implemented tries to preserve OS-like separation between event loop and tasks. So, stream to finish watching fd for IO also issues syscall, instead of calling methods on loop instance directly. Calling method on loop would be more efficient, but will require storing reference to loop in each stream. And those separation matters...
1 parent e6ed3ff commit 2caed7d

File tree

1 file changed

+18
-0
lines changed

1 file changed

+18
-0
lines changed

asyncio/asyncio.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
log = logging.getLogger("asyncio")
99

10+
IO_READ = 1
11+
IO_WRITE = 2
12+
13+
1014
def coroutine(f):
1115
return f
1216

@@ -78,6 +82,12 @@ def run_forever(self):
7882
elif isinstance(ret, IOWrite):
7983
self.add_writer(ret.obj.fileno(), lambda f: self.call_soon(cb, f), ret.obj)
8084
continue
85+
elif isinstance(ret, IODone):
86+
if ret.op == IO_READ:
87+
self.remove_reader(ret.obj.fileno())
88+
elif ret.op == IO_WRITE:
89+
self.remove_writer(ret.obj.fileno())
90+
continue
8191
except StopIteration as e:
8292
log.debug("Gen finished: %s", cb)
8393
continue
@@ -156,6 +166,12 @@ class IOWrite(SysCall):
156166
def __init__(self, obj):
157167
self.obj = obj
158168

169+
class IODone(SysCall):
170+
171+
def __init__(self, op, obj):
172+
self.op = op
173+
self.obj = obj
174+
159175

160176
def get_event_loop():
161177
return EpollEventLoop()
@@ -189,6 +205,8 @@ def readline(self):
189205
s = yield IORead(self.s)
190206
log.debug("StreamReader.readline(): after IORead: %s", s)
191207
res = self.s.readline()
208+
if not res:
209+
yield IODone(IO_READ, self.s)
192210
log.debug("StreamReader.readline(): res: %s", res)
193211
return res
194212

0 commit comments

Comments
 (0)