1 |
On 6/18/20 7:13 AM, Brian Dolbec wrote: |
2 |
> On Thu, 18 Jun 2020 00:35:48 -0700 |
3 |
> Zac Medico <zmedico@g.o> wrote: |
4 |
> |
5 |
>> In the coroutine finally clause, do not call remove_writer in cases |
6 |
>> where fd has been closed and then re-allocated to a concurrent |
7 |
>> coroutine as in bug 716636. |
8 |
>> |
9 |
>> Also, assume that the caller will put the file in non-blocking mode |
10 |
>> and close the file when done, so that this function is suitable for |
11 |
>> use within a loop. |
12 |
>> |
13 |
>> Bug: https://bugs.gentoo.org/728580 |
14 |
>> Signed-off-by: Zac Medico <zmedico@g.o> |
15 |
>> --- |
16 |
>> lib/portage/util/futures/_asyncio/process.py | 11 ++++- |
17 |
>> lib/portage/util/futures/_asyncio/streams.py | 50 |
18 |
>> ++++++++++---------- 2 files changed, 33 insertions(+), 28 |
19 |
>> deletions(-) |
20 |
>> |
21 |
>> diff --git a/lib/portage/util/futures/_asyncio/process.py |
22 |
>> b/lib/portage/util/futures/_asyncio/process.py index |
23 |
>> 020164c9b..2d3e9b0fd 100644 --- |
24 |
>> a/lib/portage/util/futures/_asyncio/process.py +++ |
25 |
>> b/lib/portage/util/futures/_asyncio/process.py @@ -1,9 +1,12 @@ |
26 |
>> -# Copyright 2018 Gentoo Foundation |
27 |
>> +# Copyright 2018-2020 Gentoo Authors |
28 |
>> # Distributed under the terms of the GNU General Public License v2 |
29 |
>> |
30 |
>> +import os |
31 |
>> + |
32 |
>> import portage |
33 |
>> portage.proxy.lazyimport.lazyimport(globals(), |
34 |
>> 'portage.util.futures:asyncio', |
35 |
>> + 'portage.util.futures.unix_events:_set_nonblocking', |
36 |
>> ) |
37 |
>> from portage.util.futures._asyncio.streams import _reader, _writer |
38 |
>> from portage.util.futures.compat_coroutine import coroutine, |
39 |
>> coroutine_return @@ -59,7 +62,11 @@ class _Process(object): |
40 |
>> if input is not None: |
41 |
>> if self._proc.stdin is None: |
42 |
>> raise TypeError('communicate: |
43 |
>> expected file or int, got {}'.format(type(self._proc.stdin))) |
44 |
>> - writer = |
45 |
>> asyncio.ensure_future(_writer(self._proc.stdin, input), |
46 |
>> loop=self._loop) |
47 |
>> + stdin = self._proc.stdin |
48 |
>> + stdin = os.fdopen(stdin, 'wb', 0) if |
49 |
>> isinstance(stdin, int) else stdin |
50 |
>> + _set_nonblocking(stdin.fileno()) |
51 |
>> + writer = |
52 |
>> asyncio.ensure_future(_writer(stdin, input, loop=self._loop), |
53 |
>> loop=self._loop) |
54 |
>> + writer.add_done_callback(lambda writer: |
55 |
>> stdin.close()) |
56 |
>> try: |
57 |
>> yield asyncio.wait(futures + [self.wait()], |
58 |
>> loop=self._loop) diff --git |
59 |
>> a/lib/portage/util/futures/_asyncio/streams.py |
60 |
>> b/lib/portage/util/futures/_asyncio/streams.py index |
61 |
>> 650a16491..870307e1e 100644 --- |
62 |
>> a/lib/portage/util/futures/_asyncio/streams.py +++ |
63 |
>> b/lib/portage/util/futures/_asyncio/streams.py @@ -1,4 +1,4 @@ -# |
64 |
>> Copyright 2018 Gentoo Foundation +# Copyright 2018-2020 Gentoo Authors |
65 |
>> # Distributed under the terms of the GNU General Public License v2 |
66 |
>> |
67 |
>> import errno |
68 |
>> @@ -8,7 +8,6 @@ import portage |
69 |
>> portage.proxy.lazyimport.lazyimport(globals(), |
70 |
>> '_emerge.PipeReader:PipeReader', |
71 |
>> 'portage.util.futures:asyncio', |
72 |
>> - 'portage.util.futures.unix_events:_set_nonblocking', |
73 |
>> ) |
74 |
>> from portage.util.futures.compat_coroutine import coroutine |
75 |
>> |
76 |
>> @@ -59,38 +58,37 @@ class _Reader(object): |
77 |
>> @coroutine |
78 |
>> def _writer(output_file, content, loop=None): |
79 |
>> """ |
80 |
>> - Asynchronously write bytes to output file, and close it when |
81 |
>> - done. If an EnvironmentError other than EAGAIN is |
82 |
>> encountered, |
83 |
>> - which typically indicates that the other end of the pipe has |
84 |
>> - close, the error is raised. This function is a coroutine. |
85 |
>> + Asynchronously write bytes to output file. The output file is |
86 |
>> + assumed to be in non-blocking mode. If an EnvironmentError |
87 |
>> + other than EAGAIN is encountered, which typically indicates |
88 |
>> that |
89 |
>> + the other end of the pipe has closed, the error is raised. |
90 |
>> + This function is a coroutine. |
91 |
>> |
92 |
>> - @param output_file: output file descriptor |
93 |
>> - @type output_file: file or int |
94 |
>> + @param output_file: output file |
95 |
>> + @type output_file: file object |
96 |
>> @param content: content to write |
97 |
>> @type content: bytes |
98 |
>> @param loop: asyncio.AbstractEventLoop (or compatible) |
99 |
>> @type loop: event loop |
100 |
>> """ |
101 |
>> - fd = output_file if isinstance(output_file, int) else |
102 |
>> output_file.fileno() |
103 |
>> - _set_nonblocking(fd) |
104 |
>> loop = asyncio._wrap_loop(loop) |
105 |
>> - try: |
106 |
>> - while content: |
107 |
>> + fd = output_file.fileno() |
108 |
>> + while content: |
109 |
>> + try: |
110 |
>> + content = content[os.write(fd, content):] |
111 |
>> + except EnvironmentError as e: |
112 |
>> + if e.errno != errno.EAGAIN: |
113 |
>> + raise |
114 |
>> waiter = loop.create_future() |
115 |
>> - loop.add_writer(fd, lambda: |
116 |
>> waiter.set_result(None)) |
117 |
>> + loop.add_writer(fd, lambda: waiter.done() or |
118 |
>> waiter.set_result(None)) try: |
119 |
>> yield waiter |
120 |
>> - while content: |
121 |
>> - try: |
122 |
>> - content = |
123 |
>> content[os.write(fd, content):] |
124 |
>> - except EnvironmentError as e: |
125 |
>> - if e.errno == |
126 |
>> errno.EAGAIN: |
127 |
>> - break |
128 |
>> - else: |
129 |
>> - raise |
130 |
>> finally: |
131 |
>> - loop.remove_writer(fd) |
132 |
>> - except GeneratorExit: |
133 |
>> - raise |
134 |
>> - finally: |
135 |
>> - os.close(output_file) if isinstance(output_file, |
136 |
>> int) else output_file.close() |
137 |
>> + # The loop and output file may have |
138 |
>> been closed. |
139 |
>> + if not loop.is_closed(): |
140 |
>> + waiter.done() or |
141 |
>> waiter.cancel() |
142 |
>> + # Do not call remove_writer |
143 |
>> in cases where fd has |
144 |
>> + # been closed and then |
145 |
>> re-allocated to a concurrent |
146 |
>> + # coroutine as in bug 716636. |
147 |
>> + if not output_file.closed: |
148 |
>> + |
149 |
>> loop.remove_writer(fd) |
150 |
> |
151 |
> |
152 |
> looks fine to me |
153 |
> |
154 |
|
155 |
Thanks, merged: |
156 |
|
157 |
https://gitweb.gentoo.org/proj/portage.git/commit/?id=92be5a02e452eb0810d2974bc7fa5ee2056ef8e7 |
158 |
-- |
159 |
Thanks, |
160 |
Zac |