Removed LoopingCall from torrent.py

Having a LoopingCall for each torrent is expencive with a lot of torrents.
The LoopingCall in torrent.py has been moved to torrentmanager.py
which runs through all the torrents and calls cleanup_prev_status.
This commit is contained in:
bendikro 2013-10-03 21:06:17 +02:00 committed by Calum Lind
commit feaeee0379
3 changed files with 25 additions and 30 deletions

View file

@ -124,8 +124,6 @@ class Torrent(object):
# We use this to return dicts that only contain changes from the previous # We use this to return dicts that only contain changes from the previous
# {session_id: status_dict, ...} # {session_id: status_dict, ...}
self.prev_status = {} self.prev_status = {}
self.prev_status_cleanup_loop = LoopingCall(self._cleanup_prev_status)
self.prev_status_cleanup_loop.start(10)
# Set the libtorrent handle # Set the libtorrent handle
self.handle = handle self.handle = handle
@ -1017,7 +1015,7 @@ class Torrent(object):
except OSError as (errno, strerror): except OSError as (errno, strerror):
log.debug("Cannot Remove Folder: %s (ErrNo %s)", strerror, errno) log.debug("Cannot Remove Folder: %s (ErrNo %s)", strerror, errno)
def _cleanup_prev_status(self): def cleanup_prev_status(self):
""" """
This method gets called to check the validity of the keys in the prev_status This method gets called to check the validity of the keys in the prev_status
dict. If the key is no longer valid, the dict will be deleted. dict. If the key is no longer valid, the dict will be deleted.

View file

@ -199,6 +199,7 @@ class TorrentManager(component.Component):
self.save_state_timer = LoopingCall(self.save_state) self.save_state_timer = LoopingCall(self.save_state)
self.save_resume_data_timer = LoopingCall(self.save_resume_data) self.save_resume_data_timer = LoopingCall(self.save_resume_data)
self.save_all_resume_data_timer = LoopingCall(self.save_resume_data, self.torrents.keys()) self.save_all_resume_data_timer = LoopingCall(self.save_resume_data, self.torrents.keys())
self.prev_status_cleanup_loop = LoopingCall(self.cleanup_torrents_prev_status)
def start(self): def start(self):
# Check for old temp file to verify safe shutdown # Check for old temp file to verify safe shutdown
@ -237,6 +238,7 @@ class TorrentManager(component.Component):
self.save_resume_data_timer.start(190, False) self.save_resume_data_timer.start(190, False)
# Force update for all resume data a bit less frequently # Force update for all resume data a bit less frequently
self.save_all_resume_data_timer.start(900, False) self.save_all_resume_data_timer.start(900, False)
self.prev_status_cleanup_loop.start(10)
def stop(self): def stop(self):
# Stop timers # Stop timers
@ -249,13 +251,13 @@ class TorrentManager(component.Component):
if self.save_all_resume_data_timer.running: if self.save_all_resume_data_timer.running:
self.save_all_resume_data_timer.stop() self.save_all_resume_data_timer.stop()
if self.prev_status_cleanup_loop.running:
self.prev_status_cleanup_loop.stop()
# Save state on shutdown # Save state on shutdown
self.save_state() self.save_state()
self.session.pause() self.session.pause()
for key in self.torrents:
# Stop the status cleanup LoopingCall here
self.torrents[key].prev_status_cleanup_loop.stop()
def remove_temp_file(result): def remove_temp_file(result):
"""Remove the temp_file to signify successfully saved state""" """Remove the temp_file to signify successfully saved state"""
@ -530,8 +532,7 @@ class TorrentManager(component.Component):
return filedump return filedump
def remove(self, torrent_id, remove_data=False): def remove(self, torrent_id, remove_data=False):
""" """Remove torrent from the session
Remove a torrent from the session.
:param torrent_id: the torrent to remove :param torrent_id: the torrent to remove
:type torrent_id: string :type torrent_id: string
@ -574,9 +575,6 @@ class TorrentManager(component.Component):
except OSError as ex: except OSError as ex:
log.warning("Unable to remove copy torrent file: %s", ex) log.warning("Unable to remove copy torrent file: %s", ex)
# Stop the looping call
self.torrents[torrent_id].prev_status_cleanup_loop.stop()
# Remove from set if it wasn't finished # Remove from set if it wasn't finished
if not self.torrents[torrent_id].is_finished: if not self.torrents[torrent_id].is_finished:
try: try:
@ -726,12 +724,12 @@ class TorrentManager(component.Component):
return True return True
def save_resume_data(self, torrent_ids=None): def save_resume_data(self, torrent_ids=None):
""" """Saves resume data for list of torrent_ids or for all torrents
Saves resume data for list of torrent_ids or for all torrents
needing resume data updated if torrent_ids is None needing resume data updated if torrent_ids is None
:returns: A Deferred whose callback will be invoked when save is complete :returns: A Deferred whose callback will be invoked when save is complete
:rtype: twisted.internet.defer.Deferred :rtype: twisted.internet.defer.Deferred
""" """
if torrent_ids is None: if torrent_ids is None:
torrent_ids = (t[0] for t in self.torrents.iteritems() if t[1].handle.need_save_resume_data()) torrent_ids = (t[0] for t in self.torrents.iteritems() if t[1].handle.need_save_resume_data())
@ -758,9 +756,11 @@ class TorrentManager(component.Component):
return DeferredList(deferreds).addBoth(on_all_resume_data_finished) return DeferredList(deferreds).addBoth(on_all_resume_data_finished)
def load_resume_data_file(self): def load_resume_data_file(self):
"""Loads the resume data from file for all the torrents """Load the resume data from file for all torrents
:returns: resume_data :returns: resume_data
:rtype: dict :rtype: dict
""" """
filename = "torrents.fastresume" filename = "torrents.fastresume"
filepath = os.path.join(self.state_dir, filename) filepath = os.path.join(self.state_dir, filename)
@ -786,9 +786,7 @@ class TorrentManager(component.Component):
return resume_data return resume_data
def save_resume_data_file(self): def save_resume_data_file(self):
""" """Saves the resume data file with the contents of self.resume_data"""
Saves the resume data file with the contents of self.resume_data.
"""
filename = "torrents.fastresume" filename = "torrents.fastresume"
filepath = os.path.join(self.state_dir, filename) filepath = os.path.join(self.state_dir, filename)
filepath_bak = filepath + ".bak" filepath_bak = filepath + ".bak"
@ -850,6 +848,11 @@ class TorrentManager(component.Component):
self.torrents[torrent_id].handle.queue_position_bottom() self.torrents[torrent_id].handle.queue_position_bottom()
return True return True
def cleanup_torrents_prev_status(self):
"""Run cleanup_prev_status for each registered torrent"""
for torrent in self.torrents.iteritems():
torrent[1].cleanup_prev_status()
def on_set_max_connections_per_torrent(self, key, value): def on_set_max_connections_per_torrent(self, key, value):
"""Sets the per-torrent connection limit""" """Sets the per-torrent connection limit"""
log.debug("max_connections_per_torrent set to %s...", value) log.debug("max_connections_per_torrent set to %s...", value)
@ -863,11 +866,13 @@ class TorrentManager(component.Component):
self.torrents[key].set_max_upload_slots(value) self.torrents[key].set_max_upload_slots(value)
def on_set_max_upload_speed_per_torrent(self, key, value): def on_set_max_upload_speed_per_torrent(self, key, value):
"""Sets the per-torrent upload speed limit"""
log.debug("max_upload_speed_per_torrent set to %s...", value) log.debug("max_upload_speed_per_torrent set to %s...", value)
for key in self.torrents.keys(): for key in self.torrents.keys():
self.torrents[key].set_max_upload_speed(value) self.torrents[key].set_max_upload_speed(value)
def on_set_max_download_speed_per_torrent(self, key, value): def on_set_max_download_speed_per_torrent(self, key, value):
"""Sets the per-torrent download speed limit"""
log.debug("max_download_speed_per_torrent set to %s...", value) log.debug("max_download_speed_per_torrent set to %s...", value)
for key in self.torrents.keys(): for key in self.torrents.keys():
self.torrents[key].set_max_download_speed(value) self.torrents[key].set_max_download_speed(value)
@ -1155,9 +1160,7 @@ class TorrentManager(component.Component):
self.handle_torrents_status_callback(self.torrents_status_requests.pop()) self.handle_torrents_status_callback(self.torrents_status_requests.pop())
def separate_keys(self, keys, torrent_ids): def separate_keys(self, keys, torrent_ids):
"""Separates the input keys into keys for the Torrent class """Separates the input keys into torrent class keys and plugins keys"""
and keys for plugins.
"""
if self.torrents: if self.torrents:
for torrent_id in torrent_ids: for torrent_id in torrent_ids:
if torrent_id in self.torrents: if torrent_id in self.torrents:
@ -1168,9 +1171,7 @@ class TorrentManager(component.Component):
return [], [] return [], []
def handle_torrents_status_callback(self, status_request): def handle_torrents_status_callback(self, status_request):
""" """Build the status dictionary with torrent values"""
Builds the status dictionary with the values from the Torrent.
"""
d, torrent_ids, keys, diff = status_request d, torrent_ids, keys, diff = status_request
status_dict = {}.fromkeys(torrent_ids) status_dict = {}.fromkeys(torrent_ids)
torrent_keys, plugin_keys = self.separate_keys(keys, torrent_ids) torrent_keys, plugin_keys = self.separate_keys(keys, torrent_ids)
@ -1187,10 +1188,9 @@ class TorrentManager(component.Component):
d.callback((status_dict, plugin_keys)) d.callback((status_dict, plugin_keys))
def torrents_status_update(self, torrent_ids, keys, diff=False): def torrents_status_update(self, torrent_ids, keys, diff=False):
""" """Returns status dict for the supplied torrent_ids async
returns status dict for the supplied torrent_ids async
If the torrent states were updated recently (less than 1.5 seconds ago, If torrent states updated recently post_torrent_updates is not called, cached state is used.
post_torrent_updates is not called. Instead the cached state is used.
:param torrent_ids: the torrent IDs to get the status on :param torrent_ids: the torrent IDs to get the status on
:type torrent_ids: list of str :type torrent_ids: list of str

View file

@ -51,9 +51,6 @@ class TorrentTestCase(unittest.TestCase):
return component.start() return component.start()
def tearDown(self): def tearDown(self):
if self.torrent:
self.torrent.prev_status_cleanup_loop.stop()
deluge.core.torrent.component = self.original_component deluge.core.torrent.component = self.original_component
def on_shutdown(result): def on_shutdown(result):