@@ -111,6 +111,48 @@ def test_socket_blocking_error(self):
111
111
self .loop .run_until_complete (
112
112
self .loop .sock_connect (sock , (b'' , 0 )))
113
113
114
+ def test_socket_handler_cleanup (self ):
115
+ # This tests recreates a rare condition where we have a socket
116
+ # with an attached reader. We then remove the reader, and close the
117
+ # socket. If the libuv Poll handler is still cached when we open
118
+ # a new TCP connection, it might so happen that the new TCP connection
119
+ # will receive a fileno that our previous socket was registered on.
120
+ # In this case, when the cached Poll handle is finally closed,
121
+ # we have a failed assertion in uv_poll_stop.
122
+ # See also https://github.com/MagicStack/uvloop/issues/34
123
+ # for details.
124
+
125
+ srv_sock = socket .socket ()
126
+ with srv_sock :
127
+ srv_sock .bind (('127.0.0.1' , 0 ))
128
+ srv_sock .listen (100 )
129
+
130
+ srv = self .loop .run_until_complete (
131
+ self .loop .create_server (
132
+ lambda : None , host = '127.0.0.1' , port = 0 ))
133
+ key_fileno = srv .sockets [0 ].fileno ()
134
+ srv .close ()
135
+ self .loop .run_until_complete (srv .wait_closed ())
136
+
137
+ # Schedule create_connection task's callbacks
138
+ tsk = self .loop .create_task (
139
+ self .loop .create_connection (
140
+ asyncio .Protocol , * srv_sock .getsockname ()))
141
+
142
+ sock = socket .socket ()
143
+ with sock :
144
+ # Add/remove readers
145
+ if sock .fileno () != key_fileno :
146
+ raise unittest .SkipTest ()
147
+ self .loop .add_reader (sock .fileno (), lambda : None )
148
+ self .loop .remove_reader (sock .fileno ())
149
+
150
+ tr , pr = self .loop .run_until_complete (
151
+ asyncio .wait_for (tsk , loop = self .loop , timeout = 0.1 ))
152
+ tr .close ()
153
+ # Let the transport close
154
+ self .loop .run_until_complete (asyncio .sleep (0 , loop = self .loop ))
155
+
114
156
115
157
class TestUVSockets (_TestSockets , tb .UVTestCase ):
116
158
pass
0 commit comments