@@ -85,30 +85,76 @@ def wait_for_ms(aw, timeout):
85
85
return wait_for (aw , timeout , core .sleep_ms )
86
86
87
87
88
- async def gather (* aws , return_exceptions = False ):
89
- """Run all *aws* awaitables concurrently. Any *aws* that are not tasks
90
- are promoted to tasks.
88
+ class _Remove :
89
+ @staticmethod
90
+ def remove (t ):
91
+ pass
91
92
92
- Returns a list of return values of all *aws*
93
93
94
- This is a coroutine.
95
- """
94
+ async def gather (* aws , return_exceptions = False ):
95
+ if not aws :
96
+ return []
97
+
98
+ def done (t , er ):
99
+ # Sub-task "t" has finished, with exception "er".
100
+ nonlocal state
101
+ if gather_task .data is not _Remove :
102
+ # The main gather task has already been scheduled, so do nothing.
103
+ # This happens if another sub-task already raised an exception and
104
+ # woke the main gather task (via this done function), or if the main
105
+ # gather task was cancelled externally.
106
+ return
107
+ elif not return_exceptions and not isinstance (er , StopIteration ):
108
+ # A sub-task raised an exception, indicate that to the gather task.
109
+ state = er
110
+ else :
111
+ state -= 1
112
+ if state :
113
+ # Still some sub-tasks running.
114
+ return
115
+ # Gather waiting is done, schedule the main gather task.
116
+ core ._task_queue .push_head (gather_task )
96
117
97
118
ts = [core ._promote_to_task (aw ) for aw in aws ]
98
119
for i in range (len (ts )):
99
- try :
100
- # TODO handle cancel of gather itself
101
- # if ts[i].coro:
102
- # iter(ts[i]).waiting.push_head(cur_task)
103
- # try:
104
- # yield
105
- # except CancelledError as er:
106
- # # cancel all waiting tasks
107
- # raise er
108
- ts [i ] = await ts [i ]
109
- except (core .CancelledError , Exception ) as er :
110
- if return_exceptions :
111
- ts [i ] = er
112
- else :
113
- raise er
120
+ if ts [i ].state is not True :
121
+ # Task is not running, gather not currently supported for this case.
122
+ raise RuntimeError ("can't gather" )
123
+ # Register the callback to call when the task is done.
124
+ ts [i ].state = done
125
+
126
+ # Set the state for execution of the gather.
127
+ gather_task = core .cur_task
128
+ state = len (ts )
129
+ cancel_all = False
130
+
131
+ # Wait for the a sub-task to need attention.
132
+ gather_task .data = _Remove
133
+ try :
134
+ core ._never .state = False
135
+ await core ._never
136
+ except core .CancelledError as er :
137
+ cancel_all = True
138
+ state = er
139
+
140
+ # Clean up tasks.
141
+ for i in range (len (ts )):
142
+ if ts [i ].state is done :
143
+ # Sub-task is still running, deregister the callback and cancel if needed.
144
+ ts [i ].state = True
145
+ if cancel_all :
146
+ ts [i ].cancel ()
147
+ elif isinstance (ts [i ].data , StopIteration ):
148
+ # Sub-task ran to completion, get its return value.
149
+ ts [i ] = ts [i ].data .value
150
+ else :
151
+ # Sub-task had an exception with return_exceptions==True, so get its exception.
152
+ ts [i ] = ts [i ].data
153
+
154
+ # Either this gather was cancelled, or one of the sub-tasks raised an exception with
155
+ # return_exceptions==False, so reraise the exception here.
156
+ if state is not 0 :
157
+ raise state
158
+
159
+ # Return the list of return values of each sub-task.
114
160
return ts
0 commit comments