asyncio.sleep() chokes on surprisingly small numbers #16201
-
Hello, I've encountered something that I think is either a bug, or a limitation that should be documented. I get these two inexplicable failures when calling asyncio.sleep() with large-ish values: import sys, asyncio
async def fail():
print('Starting')
#await asyncio.sleep(sys.maxsize) # OverflowError: overflow converting long int to machine word
#await asyncio.sleep(4194304) # 2^22 OverflowError: overflow converting long int to machine word
#await asyncio.sleep(2147484) # OverflowError: overflow converting long int to machine word
await asyncio.sleep(2147483) # OverflowError: ticks interval overflow
#await asyncio.sleep(2097152) # 2^21 OverflowError: ticks interval overflow
#await asyncio.sleep(1048576) # 2^20 OverflowError: ticks interval overflow
#await asyncio.sleep(536871) # OverflowError: ticks interval overflow
#await asyncio.sleep(536870) # ok
#await asyncio.sleep(524288) # 2^19 ok
print('Done')
asyncio.run( fail() ) It's unclear to me where these boundaries >536870 and >2147483 come from. Is this expected behaviour that I should work around, or is it a bug? I'm running MicroPython 1.23.0 on a Raspberry Pi Pico W. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 2 replies
-
The limits/boarders may depend on hardwares/architectures because the code you showed runs without error on unix port (x86-64 Linux). 536_870 / 3_600 / 24 = 6.21 ... [days] The followings are based on my guess, please consult the relevant parts of sorce code/hardware manuals you are using for details. A 32-bit signed int can express from -2_147_483_648 to 2_147_483_647. For the second one, 2_147_483 / 536_871 = 3.999998 ... seems to be related to division of 4, probably related to clock (I guess). Although the error showed might be not explanative enough for you, there is a certain error for the unacceptable number which seems to be sufficient to warn you at least that's prohibited. |
Beta Was this translation helpful? Give feedback.
-
A couple of general points. On most platforms MicroPython uses 30 bits for a short int, also for a float. It supports arbitrary precision integers, automatically adopting these if the value no longer fits 30 bits. Secondly the internal timing of In practice there are no practical consequences of the maximum time allowed by async def long_sleep(t): # Sleep with no bounds. Immediate return if t < 0.
while t > 0:
await asyncio.sleep(min(t, 1000))
t -= 1000 This library facilitates flexible handing of long periods and future eevents. |
Beta Was this translation helpful? Give feedback.
-
Thanks both, for the analysis and suggestions. I don't think I would have clocked that the thresholds were powers of two divided by powers of ten! My use case should never need a wait period of more than a day or two, so I can probably make do with just a line of code to cap the value rather than adding a library to support it. So it's solved for me - but I think it would be useful to have in the docs for asyncio that sleep (and presumably sleep_ms) have this limit, and some guidance as to what it might be. I spent maybe half an hour doing manual binary searches to figure out what the thresholds actually were, before posting here. Is there an official way to suggest this kind of thing? |
Beta Was this translation helpful? Give feedback.
-
Re ticks rollover. As per the docs reference the behaviour may be port dependent, but on a typical 32-bit port behaviour is as follows. Ticks values are stored in 30-bit small ints. Hence a ticks value is in the range >>> hex(ticks_add(0, 0x1fff_ffff+1))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: ticks interval overflow
>>> hex(ticks_add(0, 0x1fff_ffff))
'0x1fffffff' Rollover can be demonstrated by adding a value to the maximum ticks value: >>> ticks_add(0x3fff_ffff, 10)
9 |
Beta Was this translation helpful? Give feedback.
The limits/boarders may depend on hardwares/architectures because the code you showed runs without error on unix port (x86-64 Linux).
I am not sure if there is a real use case for such a large-ish asyncio.sleep() though.
536_870 / 3_600 / 24 = 6.21 ... [days]
The followings are based on my guess, please consult the relevant parts of sorce code/hardware manuals you are using for details.
A 32-bit signed int can express from -2_147_483_648 to 2_147_483_647.
Because of 1-ms precision, probably, dividing the limit by 1_000 gives 2_147_483, the first border that you mentioned.
For the second one,
2_147_483 / 536_871 = 3.999998 ...
2_147_483 / 536_870 = 4.000005 ...
seems to be related to divis…