except Exception as e:
print(f"Task {self.coro_id} step exception")
if not isinstance(e, SessionException):
self.session.on_task_exception()
self.task_closed = True
self.on_coro_stop(self)
+ self.on_coro_stop = None
future = None
if isinstance(coro_yield, WebIOFuture):
if coro_yield.coro:
future = asyncio.run_coroutine_threadsafe(coro_yield.coro, asyncio.get_event_loop())
elif coro_yield is not None:
future = coro_yield
if not self.session.closed() and hasattr(future, 'add_done_callback'):
future.add_done_callback(self._wakeup)
self.pending_futures[id(future)] = future
+ elif self.session.closed():
+ self.session = None
清理 unclosed_sessions
修改 notify_connection_lost
def notify_connection_lost(self):
_state.active_connections.pop(self.session_id, None)
if not self.reconnectable:
# when the connection lost is caused by `on_session_close()`, it's OK to close the session here though.
# because the `session.close()` is reentrant
self.session.close(nonblock=True)
+ _state.unclosed_sessions.pop(self.session_id, None)
else:
if self.session_id in _state.unclosed_sessions:
_state.detached_sessions[self.session_id] = time.time()
logger.debug("WebSocket closed")
BUG描述
使用 fastapi (ws adapter),协程会话时:(问题与fastapi无关)
pywebio.session.coroutinebased.Task
在运行时断开浏览器连接,没有清理Task.pending_futures
,内部的 future 引用了Task._wakeup
可能导致了 Task 不会被 gc 清理。pywebio.platform.adaptor.ws.WebSocketHandler.notify_connection_lost()
,其中没有处理_state.unclosed_sessions
,包含当前的 CoroutineBasedSession 对象。由于默认的reconnect_timeout
为0,session_clean_task()
也不会启动,最后导致该对象一直存活,不会被 gc 清理。notify_connection_lost()
的调用中,有调用Session.close()
->Session._cleanup()
,但是没有清理由WebSocketHandler 传入的_on_task_command
和_on_session_close
,导致 WebSocketHandler 无法被 gc 清理。复现
首先我在
CoroutineBasedSession
,WebSocketHandler
,Task
对象中的__init__
和__del__
Task.close
, 和它的末尾Task.step
中except Exception as e:
后添加了 print 提示函数调用。这部分代码省略。
浏览器访问,并关闭网页,五秒内和五秒后关闭效果是一样的。
如何修复
Task.step
的 try: ... except: 的最后添加这是从
Task.close
里复制过来的,直接将pending_futures.clear()了也可以。我不清楚 pending_future 的作用,也许前面的except StopIteration as e:
后也需要添加?在 debug 的过程中,发现 Session 和 Task 有互相引用,将 Task 内对 Session 的引用删除可以让 Session 自动清理,但是 Task 仍然因为上述原因保留着。
notify_connection_lost
_on_task_command
和_on_session_close
修改_cleanup
修改后 Task 和 Session 都能自动清理了。
原因
我的代码里使用
weakref.WeakValueDictionary
弱引用了一个对象,这个对象被我放进 session.local 里了。导致退出时字典值仍然有引用而不会自动清理。环境信息