mirror of
https://github.com/FreeOpcUa/opcua-asyncio
synced 2025-10-29 17:07:18 +08:00
Joey faulkner bugfix/server memory leak (#272)
* implement oroulet changes * closing_task must be a task not onl run once * do not catch CancelleError in closing_Task * make sure we use the correct loop in asyncio binary_server Co-authored-by: Joey Faulkner <joeymfaulkner@gmail.com>
This commit is contained in:
parent
97820f0a79
commit
f3de377a8f
|
|
@ -18,7 +18,7 @@ class OPCUAProtocol(asyncio.Protocol):
|
||||||
Instantiated for every connection.
|
Instantiated for every connection.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, iserver: InternalServer, policies, clients, protocol_tasks):
|
def __init__(self, iserver: InternalServer, policies, clients, closing_tasks):
|
||||||
self.peer_name = None
|
self.peer_name = None
|
||||||
self.transport = None
|
self.transport = None
|
||||||
self.processor = None
|
self.processor = None
|
||||||
|
|
@ -26,7 +26,7 @@ class OPCUAProtocol(asyncio.Protocol):
|
||||||
self.iserver: InternalServer = iserver
|
self.iserver: InternalServer = iserver
|
||||||
self.policies = policies
|
self.policies = policies
|
||||||
self.clients = clients
|
self.clients = clients
|
||||||
self.protocol_tasks = protocol_tasks
|
self.closing_tasks = closing_tasks
|
||||||
self.messages = asyncio.Queue()
|
self.messages = asyncio.Queue()
|
||||||
self._task = None
|
self._task = None
|
||||||
|
|
||||||
|
|
@ -50,12 +50,11 @@ class OPCUAProtocol(asyncio.Protocol):
|
||||||
self.transport.close()
|
self.transport.close()
|
||||||
self.iserver.asyncio_transports.remove(self.transport)
|
self.iserver.asyncio_transports.remove(self.transport)
|
||||||
closing_task = self.iserver.loop.create_task(self.processor.close())
|
closing_task = self.iserver.loop.create_task(self.processor.close())
|
||||||
self.protocol_tasks.append(closing_task)
|
self.closing_tasks.append(closing_task)
|
||||||
if self in self.clients:
|
if self in self.clients:
|
||||||
self.clients.remove(self)
|
self.clients.remove(self)
|
||||||
self.messages.put_nowait((None, None))
|
self.messages.put_nowait((None, None))
|
||||||
self._task.cancel()
|
self._task.cancel()
|
||||||
self.protocol_tasks.append(self._task)
|
|
||||||
|
|
||||||
def data_received(self, data):
|
def data_received(self, data):
|
||||||
self._buffer += data
|
self._buffer += data
|
||||||
|
|
@ -111,7 +110,8 @@ class BinaryServer:
|
||||||
self._server: Optional[asyncio.AbstractServer] = None
|
self._server: Optional[asyncio.AbstractServer] = None
|
||||||
self._policies = []
|
self._policies = []
|
||||||
self.clients = []
|
self.clients = []
|
||||||
self.protocol_tasks = []
|
self.closing_tasks = []
|
||||||
|
self.cleanup_task = None
|
||||||
|
|
||||||
def set_policies(self, policies):
|
def set_policies(self, policies):
|
||||||
self._policies = policies
|
self._policies = policies
|
||||||
|
|
@ -122,7 +122,7 @@ class BinaryServer:
|
||||||
iserver=self.iserver,
|
iserver=self.iserver,
|
||||||
policies=self._policies,
|
policies=self._policies,
|
||||||
clients=self.clients,
|
clients=self.clients,
|
||||||
protocol_tasks=self.protocol_tasks,
|
closing_tasks=self.closing_tasks,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
|
|
@ -136,19 +136,35 @@ class BinaryServer:
|
||||||
self.hostname = sockname[0]
|
self.hostname = sockname[0]
|
||||||
self.port = sockname[1]
|
self.port = sockname[1]
|
||||||
self.logger.info('Listening on %s:%s', self.hostname, self.port)
|
self.logger.info('Listening on %s:%s', self.hostname, self.port)
|
||||||
|
self.cleanup_task = self.iserver.loop.create_task(self._await_closing_tasks())
|
||||||
|
|
||||||
async def stop(self):
|
async def stop(self):
|
||||||
self.logger.info('Closing asyncio socket server')
|
self.logger.info('Closing asyncio socket server')
|
||||||
for transport in self.iserver.asyncio_transports:
|
for transport in self.iserver.asyncio_transports:
|
||||||
transport.close()
|
transport.close()
|
||||||
|
|
||||||
# Wait for all transport closing tasks to complete
|
# stop cleanup process and run it a last time
|
||||||
results = await asyncio.gather(*self.protocol_tasks, return_exceptions=True)
|
self.cleanup_task.cancel()
|
||||||
for result in results:
|
try:
|
||||||
if isinstance(result, Exception):
|
await self.cleanup_task
|
||||||
self.logger.error(f"An error ocurred while closing a transport: {result}")
|
except asyncio.CancelledError:
|
||||||
self.protocol_tasks = []
|
pass
|
||||||
|
await self._await_closing_tasks(recursive=False)
|
||||||
|
|
||||||
if self._server:
|
if self._server:
|
||||||
self.iserver.loop.call_soon(self._server.close)
|
self.iserver.loop.call_soon(self._server.close)
|
||||||
await self._server.wait_closed()
|
await self._server.wait_closed()
|
||||||
|
|
||||||
|
async def _await_closing_tasks(self, recursive=True):
|
||||||
|
while self.closing_tasks:
|
||||||
|
task = self.closing_tasks.pop()
|
||||||
|
try:
|
||||||
|
await task
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
# this means a stop request has been sent, it should not be catched
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Unexpected crash in BinaryServer._await_closing_tasks")
|
||||||
|
if recursive:
|
||||||
|
await asyncio.sleep(10)
|
||||||
|
await self._await_closing_tasks()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user