@@ -96,6 +96,7 @@ def __init__(
96
96
) -> None :
97
97
self ._params = params
98
98
self .on_stop : Optional [Callable [[], Coroutine [Any , Any , None ]]] = on_stop
99
+ self ._on_stop_task : Optional [asyncio .Task [None ]] = None
99
100
self ._socket : Optional [socket .socket ] = None
100
101
self ._frame_helper : Optional [APIFrameHelper ] = None
101
102
self ._api_version : Optional [APIVersion ] = None
@@ -117,6 +118,7 @@ def __init__(
117
118
self ._ping_stop_event = asyncio .Event ()
118
119
119
120
self ._connect_task : Optional [asyncio .Task [None ]] = None
121
+ self ._keep_alive_task : Optional [asyncio .Task [None ]] = None
120
122
self ._fatal_exception : Optional [Exception ] = None
121
123
self ._expected_disconnect = False
122
124
@@ -142,6 +144,10 @@ def _cleanup(self) -> None:
142
144
self ._connect_task .cancel ()
143
145
self ._connect_task = None
144
146
147
+ if self ._keep_alive_task is not None :
148
+ self ._keep_alive_task .cancel ()
149
+ self ._keep_alive_task = None
150
+
145
151
if self ._frame_helper is not None :
146
152
self ._frame_helper .close ()
147
153
self ._frame_helper = None
@@ -151,8 +157,19 @@ def _cleanup(self) -> None:
151
157
self ._socket = None
152
158
153
159
if self .on_stop and self ._connect_complete :
160
+
161
+ def _remove_on_stop_task (_fut : asyncio .Future [None ]) -> None :
162
+ """Remove the stop task.
163
+
164
+ We need to do this because the asyncio does not hold
165
+ a strong reference to the task, so it can be garbage
166
+ collected unexpectedly.
167
+ """
168
+ self ._on_stop_task = None
169
+
154
170
# Ensure on_stop is called only once
155
- asyncio .create_task (self .on_stop ())
171
+ self ._on_stop_task = asyncio .create_task (self .on_stop ())
172
+ self ._on_stop_task .add_done_callback (_remove_on_stop_task )
156
173
self .on_stop = None
157
174
158
175
# Note: we don't explicitly cancel the ping/read task here
@@ -318,7 +335,7 @@ async def _keep_alive_loop() -> None:
318
335
self ._report_fatal_error (err )
319
336
return
320
337
321
- asyncio .create_task (_keep_alive_loop ())
338
+ self . _keep_alive_task = asyncio .create_task (_keep_alive_loop ())
322
339
323
340
async def connect (self , * , login : bool ) -> None :
324
341
if self ._connection_state != ConnectionState .INITIALIZED :
0 commit comments