extmod/uasyncio/stream: Add a 'Stream.awrite' method.

The usual write+drain combo is suboptimal and causes excessive data
copying, esp. when you write a memory buffer.

Fixing this within the existing code is nontrivial, thus this commit
adds a new 'Stream.awrite' method that can (indeed should, if CPython
compatibility is not an issue) be used instead.
pull/10837/head
Matthias Urlichs 2023-02-24 11:47:40 +01:00
rodzic 2e4dda3c20
commit 95bd04df8e
3 zmienionych plików z 31 dodań i 16 usunięć

Wyświetl plik

@ -273,11 +273,27 @@ TCP stream connections
This is a coroutine.
.. method:: Stream.awrite(buf)
Write *buf* to the stream.
This method is slightly more efficient than the usual
`Stream.write`-plus-`Stream.drain` combination. It ignores the output
buffer.
This is a coroutine, and a MicroPython extension.
.. method:: Stream.write(buf)
Accumulated *buf* to the output buffer. The data is only flushed when
`Stream.drain` is called. It is recommended to call `Stream.drain` immediately
after calling this function.
Add *buf* to the output buffer. The data is only flushed when
`Stream.drain` is called. It is recommended to call `Stream.drain`
immediately after calling this function.
If the output buffer is empty when *write* is called, part or all of
*buf* might be written immediately instead of getting buffered.
If compatibility to CPython is not required, consider using `Stream.awrite`
instead.
.. method:: Stream.drain()

Wyświetl plik

@ -77,19 +77,22 @@ class Stream:
buf = buf[ret:]
self.out_buf += buf
# async
def drain(self):
if not self.out_buf:
# Drain must always yield, so a tight loop of write+drain can't block the scheduler.
return (yield from core.sleep_ms(0))
mv = memoryview(self.out_buf)
async def awrite(self, buf):
mv = memoryview(buf)
off = 0
while off < len(mv):
yield core._io_queue.queue_write(self.s)
ret = self.s.write(mv[off:])
if ret is not None:
off += ret
async def drain(self):
buf = self.out_buf
if not buf:
# Drain must always yield, so a tight loop of write+drain can't block the scheduler.
return await core.sleep_ms(0)
self.out_buf = b""
await self.awrite(buf)
# Stream can be used for both reading and writing to save code size

Wyświetl plik

@ -13,17 +13,13 @@ PORT = 8000
async def handle_connection(reader, writer):
writer.write(b"a")
await writer.drain()
await writer.awrite(b"a")
# Split the first 2 bytes up so the client must wait for the second one
await asyncio.sleep(0.1)
writer.write(b"b")
await writer.drain()
writer.write(b"c")
await writer.drain()
await writer.awrite(b"b")
await writer.awrite(b"c")
print("close")
writer.close()