Skip to content

Commit 9ab73ae

Browse files
committed
fix a bug on windows
1 parent 59401a4 commit 9ab73ae

6 files changed

Lines changed: 58 additions & 5 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rsloop"
3-
version = "0.1.25"
3+
version = "0.1.26"
44
edition = "2024"
55
description = "An event loop for asyncio written in Rust"
66
license = "Apache-2.0"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "maturin"
44

55
[project]
66
name = "rsloop"
7-
version = "0.1.25"
7+
version = "0.1.26"
88
description = "An event loop for asyncio written in Rust"
99
readme = "README.md"
1010
license = { file = "LICENSE" }

src/stream_transport.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1607,7 +1607,17 @@ impl StreamTransportCore {
16071607
};
16081608
let mut writer = writer.lock().expect("poisoned direct tasked writer");
16091609
match writer.deref_mut() {
1610-
TaskedDirectWriter::Tcp(stream) => stream.write(data),
1610+
TaskedDirectWriter::Tcp(stream) => {
1611+
#[cfg(windows)]
1612+
// A paused reader does not consume Winsock's asynchronous reset
1613+
// notification. Surface it before another small write can appear
1614+
// to succeed from the local send buffer.
1615+
if let Some(err) = stream.take_error()? {
1616+
return Err(err);
1617+
}
1618+
1619+
stream.write(data)
1620+
}
16111621
#[cfg(unix)]
16121622
TaskedDirectWriter::Unix(stream) => stream.write(data),
16131623
}

tests/test_compat.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import signal
99
import socket
10+
import struct
1011
import sys
1112
import tempfile
1213
import time
@@ -222,6 +223,48 @@ def connection_lost(self, exc):
222223
self.assertEqual(received, b"response-before-eof")
223224
self.assertEqual(events, ["data", "eof", "lost"])
224225

226+
@unittest.skipUnless(sys.platform == "win32", "requires Winsock")
227+
def test_write_reports_reset_while_reading_is_paused(self) -> None:
228+
async def main() -> BaseException | None:
229+
loop = asyncio.get_running_loop()
230+
connection_lost = loop.create_future()
231+
232+
class ClientProtocol(asyncio.Protocol):
233+
def connection_made(self, transport):
234+
transport.set_write_buffer_limits(0)
235+
transport.pause_reading()
236+
237+
def connection_lost(self, exc):
238+
if not connection_lost.done():
239+
connection_lost.set_result(exc)
240+
241+
server_sock = socket.create_server(("127.0.0.1", 0))
242+
try:
243+
transport, _ = await loop.create_connection(
244+
ClientProtocol, *server_sock.getsockname()[:2]
245+
)
246+
peer_sock, _ = server_sock.accept()
247+
peer_sock.setsockopt(
248+
socket.SOL_SOCKET,
249+
socket.SO_LINGER,
250+
struct.pack("ii", 1, 0),
251+
)
252+
peer_sock.close()
253+
try:
254+
for _ in range(1000):
255+
await asyncio.sleep(0)
256+
transport.write(b"foo")
257+
if connection_lost.done():
258+
break
259+
260+
return await asyncio.wait_for(connection_lost, 3.0)
261+
finally:
262+
transport.close()
263+
finally:
264+
server_sock.close()
265+
266+
self.assertIsNotNone(rsloop.run(main()))
267+
225268
def test_readexactly_larger_than_flow_control_window(self) -> None:
226269
# Regression test: a readexactly() waiting for more than 2 * limit
227270
# bytes (default limit 64 KiB) used to deadlock because the reader

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)