@@ -92,7 +92,7 @@ def __next__(self):
9292 raise self .exc
9393
9494
95- # Pause task execution for the given time (integer in milliseconds, uPy extension)
95+ # Pause task execution for the given time (integer in milliseconds, MicroPython extension)
9696# Use a SingletonGenerator to do it without allocating on the heap
9797def sleep_ms (t , sgen = SingletonGenerator ()):
9898 # CIRCUITPY-CHANGE: doc
@@ -267,9 +267,16 @@ def run_until_complete(main_task=None):
267267 # A task waiting on _task_queue; "ph_key" is time to schedule task at
268268 dt = max (0 , ticks_diff (t .ph_key , ticks ()))
269269 elif not _io_queue .map :
270- # No tasks can be woken so finished running
270+ # No tasks can be woken
271271 cur_task = None
272- return
272+ if not main_task or not main_task .state :
273+ # no main_task, or main_task is done so finished running
274+ return
275+ # At this point, there is theoretically nothing that could wake the
276+ # scheduler, but it is not allowed to exit either. We keep the code
277+ # running so that a hypothetical debugger (or other such meta-process)
278+ # can get a view of what is happening and possibly abort.
279+ dt = 3
273280 # print('(poll {})'.format(dt), len(_io_queue.map))
274281 _io_queue .wait_io_event (dt )
275282
@@ -291,31 +298,33 @@ def run_until_complete(main_task=None):
291298 except excs_all as er :
292299 # Check the task is not on any event queue
293300 assert t .data is None
294- # This task is done, check if it's the main task and then loop should stop
295- if t is main_task :
301+ # If it's the main task, it is considered as awaited by the caller
302+ awaited = t is main_task
303+ if awaited :
296304 cur_task = None
297- if isinstance (er , StopIteration ):
298- return er .value
299- raise er
305+ if not isinstance (er , StopIteration ):
306+ t .state = False
307+ raise er
308+ if t .state is None :
309+ t .state = False
300310 if t .state :
301311 # Task was running but is now finished.
302- waiting = False
303312 if t .state is True :
304313 # "None" indicates that the task is complete and not await'ed on (yet).
305- t .state = None
314+ t .state = False if awaited else None
306315 elif callable (t .state ):
307316 # The task has a callback registered to be called on completion.
308317 t .state (t , er )
309318 t .state = False
310- waiting = True
319+ awaited = True
311320 else :
312321 # Schedule any other tasks waiting on the completion of this task.
313322 while t .state .peek ():
314323 _task_queue .push (t .state .pop ())
315- waiting = True
324+ awaited = True
316325 # "False" indicates that the task is complete and has been await'ed on.
317326 t .state = False
318- if not waiting and not isinstance (er , excs_stop ):
327+ if not awaited and not isinstance (er , excs_stop ):
319328 # An exception ended this detached task, so queue it for later
320329 # execution to handle the uncaught exception if no other task retrieves
321330 # the exception in the meantime (this is handled by Task.throw).
@@ -333,6 +342,9 @@ def run_until_complete(main_task=None):
333342 _exc_context ["exception" ] = exc
334343 _exc_context ["future" ] = t
335344 Loop .call_exception_handler (_exc_context )
345+ # If it's the main task then the loop should stop
346+ if t is main_task :
347+ return er .value
336348
337349
338350# Create a new task from a coroutine and run it until it finishes
0 commit comments