RP2 asyncio stream IO hangs: regression
Port, board and/or hardware
RP2040 or RP2350
MicroPython version
Fails on release builds >= 22.0
Bug seems RP2-specific: the script runs on STM32 (Pyboard 1.1) and ESP32-S3 with current firmware (1.27).
Reproduction
Either paste the script at the REPL or run from a file foo.py (mpremote mount . "exec import foo")
import asyncio # as asyncio
import time
import io
MP_STREAM_POLL_RD = const(1)
MP_STREAM_POLL = const(3)
MP_STREAM_ERROR = const(-1)
class MillisecTimer(io.IOBase):
def __init__(self):
self.end = 0
self.sreader = asyncio.StreamReader(self)
def __iter__(self):
await self.sreader.read(1)
def __call__(self, ms):
self.end = time.ticks_add(time.ticks_ms(), ms)
return self
def read(self, _):
return b"a"
def ioctl(self, req, arg):
ret = MP_STREAM_ERROR
#time.sleep_us(2_000)
if req == MP_STREAM_POLL: # hangs HERE
ret = 0
if arg & MP_STREAM_POLL_RD:
if time.ticks_diff(time.ticks_ms(), self.end) >= 0:
ret |= MP_STREAM_POLL_RD
return ret
async def timer_test(n):
timer = MillisecTimer()
for x in range(n):
await timer(100) # Pause 100ms
print(x)
asyncio.run(timer_test(20))
Expected behaviour
Expected to print integers from 0 to 19.
Observed behaviour
Hangs on the line commented with "hangs HERE".
Behaviour can be "fixed" by uncommenting the 2ms delay.
Additional Information
No, I've provided everything above.
Code of Conduct
Yes, I agree
If boot.py doesn't exit then rp2 port will never initialise USB (soft-bricked)
Port, board and/or hardware
Raspberry Pico
MicroPython version
MicroPython v1.23.0 on 2024-06-02; Raspberry Pi Pico with RP2040
Reproduction
I took my program code and stripped it down to a working repro. There are more lines that could be deleted but help with visualization. The content is saved to boot.py. There is no other file saved on the microcontroller.
With any version prior 1.23 this will yield a serial device in device manager, whenever the device is connected to the computer. With version 1.23 it will not and can only recovered by flashing a firmware version prior 1.23:
import _thread
import asyncio # type: ignore
from time import sleep
import gc
import machine
synchronousLoopAlive: bool = True
anyLoopAlive: bool = True
led = machine.Pin(25, machine.Pin.OUT)
async def yieldingBackgroundTaskOne():
while anyLoopAlive:
print("One")
led.value(not led.value())
await asyncio.sleep_ms(1000)
def synchronousTask():
while synchronousLoopAlive:
sleep(2)
if __name__ == "__main__":
try:
sleep(3)
loop = asyncio.get_event_loop()
gc.collect()
programThread = _thread.start_new_thread(synchronousTask, ())
print("program thread started")
taskOne = loop.create_task(yieldingBackgroundTaskOne())
print("task One started")
loop.run_forever()
except KeyboardInterrupt:
synchronousLoopAlive = False
anyLoopAlive = False
loop.stop()
print("INTERRUPTED - STOPPING EVERYTHING")
except Exception as e:
print(f"error occurred: {e}")
Expected behaviour
Expected a serial device in device manager as in all versions prior 1.23.
Observed behaviour
No serial device in device manager with micropython 1.23, but with any version prior 1.23.
Additional Information
No, I've provided everything above.
Code of Conduct
Yes, I agree