-
Notifications
You must be signed in to change notification settings - Fork 77
connection: Terminate background commands on close() #626
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
connection: Terminate background commands on close() #626
Conversation
devlib/connection.py
Outdated
| # accumulating terminated commands and therefore leaking associated | ||
| # resources if the user is not careful and does not use the context | ||
| # manager API. | ||
| for bg_cmd in conn._current_bg_cmds: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to spend some more time narrowing down the problem, however when I combine these changes with #624 on an AndroidTarget I'm run into the following error:
self.target.pull(self.target_output_path, host_output_file)
File ".../devlib/utils/asyn.py", line 209, in __call__
return self.blocking(*args, **kwargs)
File ".../devlib/utils/asyn.py", line 338, in blocking
return asyncio.run(wrapper())
File ".../lib/python3.8/site-packages/nest_asyncio.py", line 32, in run
return loop.run_until_complete(future)
File ".../lib/python3.8/site-packages/nest_asyncio.py", line 70, in run_until_complete
return f.result()
File "/usr/lib/python3.8/asyncio/futures.py", line 178, in result
raise self._exception
File "/usr/lib/python3.8/asyncio/tasks.py", line 280, in __step
result = coro.send(None)
File ".../devlib/utils/asyn.py", line 337, in wrapper
return await x
File ".../devlib/target.py", line 797, in pull
do_pull(sources, dest)
File ".../devlib/target.py", line 786, in do_pull
self.conn.pull(sources, dest, timeout=timeout)
File ".../devlib/utils/misc.py", line 941, in wrapper
return f_(*args, **kwargs)
File ".../devlib/utils/android.py", line 322, in pull
return self._push_pull('pull', sources, dest, timeout)
File ".../devlib/utils/misc.py", line 941, in wrapper
return f_(*args, **kwargs)
File ".../devlib/utils/android.py", line 337, in _push_pull
bg_cmd = adb_command_background(
File ".../devlib/utils/android.py", line 703, in adb_command_background
cmd = PopenBackgroundCommand(conn=conn, popen=popen)
File ".../devlib/connection.py", line 295, in __init__
super().__init__(conn=conn)
File ".../devlib/connection.py", line 132, in __init__
for bg_cmd in conn._current_bg_cmds:
Sending error-logged from <ErrorSignalHandler (DEBUG)>
RuntimeError(Set changed size during iteration)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, poll() will remove the command from the set if the command has completed, and that is the exact reason we are calling poll() here. I just need to "freeze" the set we are iterating over.
Ensure all background commands are terminated before we close the connection.
Add a constructor to BackgroundCommand so that the command knows the connection it's tied to.
Instead of loosely tracking the current BackgroundCommand for a connection in _current_bg_cmds WeakSet attribute, use a normal set and make the BackgroundCommand deregister itself upon termination. This allows canceling any outstanding BackgroundCommand when the connection is closed. Currently, destroying a BackgroundCommand will not cancel the command but devlib will simply loose track of it, and some threads will likely fail in the background if they try to use the now broken connection.
Make BackgroundCommand.__init__() poll all current BackgroundCommands on the associated connection so they deregister themselves if they are completed. This ensures that a BackgroundCommand-heavy application that also does not close them properly will not accumulate useless instances forever and leak associated resources like Popen objects.
ea39db0 to
ab26319
Compare
|
Updated PR with fix for |
Ensure all background commands are terminated before we close the connection.
This PR adds tracking of all outstanding BackgroundCommand inside
ConnectionBase._current_bg_cmds. This attribute is changed from WeakSet() to set(), and the BackgroundCommand themselves deregister themselves when they terminate. This allows properly canceling all outstanding_ bg command when the connection is closed.BackgroundCommand.init polls current bg cmds of its connection to reap any terminated commands, so that APIs that leak objects like the change introduced by this PR:
#624
or any other non-careful user does not result in endless leak of associated resources.