Make sure dependent components are stopped before their dependencies.
Make sure deferreds are not lost in new resume saving code.
This commit is contained in:
parent
ce99b5f688
commit
58cb9e1c22
|
@ -34,6 +34,7 @@
|
|||
#
|
||||
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from twisted.internet.defer import maybeDeferred, succeed, DeferredList, fail
|
||||
from twisted.internet.task import LoopingCall
|
||||
|
||||
|
@ -225,6 +226,8 @@ class ComponentRegistry(object):
|
|||
"""
|
||||
def __init__(self):
|
||||
self.components = {}
|
||||
# Stores all of the components that are dependent on a particular component
|
||||
self.dependents = defaultdict(list)
|
||||
|
||||
def register(self, obj):
|
||||
"""
|
||||
|
@ -243,6 +246,9 @@ class ComponentRegistry(object):
|
|||
"Component already registered with name %s" % name)
|
||||
|
||||
self.components[obj._component_name] = obj
|
||||
if obj._component_depend:
|
||||
for depend in obj._component_depend:
|
||||
self.dependents[depend].append(name)
|
||||
|
||||
def deregister(self, obj):
|
||||
"""
|
||||
|
@ -317,10 +323,22 @@ class ComponentRegistry(object):
|
|||
elif isinstance(names, str):
|
||||
names = [names]
|
||||
|
||||
def on_dependents_stopped(result, name):
|
||||
return self.components[name]._component_stop()
|
||||
|
||||
stopped_in_deferred = set()
|
||||
deferreds = []
|
||||
|
||||
for name in names:
|
||||
if name in stopped_in_deferred:
|
||||
continue
|
||||
if name in self.components:
|
||||
if name in self.dependents:
|
||||
# If other components depend on this component, stop them first
|
||||
d = self.stop(self.dependents[name]).addCallback(on_dependents_stopped, name)
|
||||
deferreds.append(d)
|
||||
stopped_in_deferred.update(self.dependents[name])
|
||||
else:
|
||||
deferreds.append(self.components[name]._component_stop())
|
||||
|
||||
return DeferredList(deferreds)
|
||||
|
@ -360,7 +378,7 @@ class ComponentRegistry(object):
|
|||
:param names: a list of Components to resume
|
||||
:type names: list
|
||||
|
||||
:returns: a Deferred object that will fire once all Components have been sucessfully resumed
|
||||
:returns: a Deferred object that will fire once all Components have been successfully resumed
|
||||
:rtype: twisted.internet.defer.Deferred
|
||||
|
||||
"""
|
||||
|
@ -384,16 +402,14 @@ class ComponentRegistry(object):
|
|||
be called when the program is exiting to ensure all Components have a
|
||||
chance to properly shutdown.
|
||||
|
||||
:returns: a Deferred object that will fire once all Components have been sucessfully resumed
|
||||
:returns: a Deferred object that will fire once all Components have been successfully shut down
|
||||
:rtype: twisted.internet.defer.Deferred
|
||||
|
||||
"""
|
||||
deferreds = []
|
||||
def on_stopped(result):
|
||||
return DeferredList(map(lambda c: c._component_shutdown(), self.components.values()))
|
||||
|
||||
for component in self.components.values():
|
||||
deferreds.append(component._component_shutdown())
|
||||
|
||||
return DeferredList(deferreds)
|
||||
return self.stop(self.components.keys()).addCallback(on_stopped)
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
|
|
|
@ -75,6 +75,7 @@ class AlertManager(component.Component):
|
|||
|
||||
def stop(self):
|
||||
for dc in self.delayed_calls:
|
||||
if dc.active():
|
||||
dc.cancel()
|
||||
self.delayed_calls = []
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ import shutil
|
|||
import operator
|
||||
import logging
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
from twisted.internet.task import LoopingCall
|
||||
from twisted.internet.defer import Deferred, DeferredList
|
||||
|
@ -134,7 +135,7 @@ class TorrentManager(component.Component):
|
|||
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "TorrentManager", interval=5,
|
||||
depend=["CorePluginManager"])
|
||||
depend=["CorePluginManager", "AlertManager"])
|
||||
log.debug("TorrentManager init..")
|
||||
# Set the libtorrent session
|
||||
self.session = component.get("Core").session
|
||||
|
@ -154,7 +155,7 @@ class TorrentManager(component.Component):
|
|||
|
||||
# This is a map of torrent_ids to Deferreds used to track needed resume data.
|
||||
# The Deferreds will be completed when resume data has been saved.
|
||||
self.waiting_on_resume_data = {}
|
||||
self.waiting_on_resume_data = defaultdict(list)
|
||||
|
||||
# Keeps track of resume data
|
||||
self.resume_data = {}
|
||||
|
@ -247,14 +248,7 @@ class TorrentManager(component.Component):
|
|||
# Stop the status cleanup LoopingCall here
|
||||
self.torrents[key].prev_status_cleanup_loop.stop()
|
||||
|
||||
# Save all resume data and wait until alerts have finished
|
||||
lc = LoopingCall(lambda: self.alerts.handle_alerts(True))
|
||||
|
||||
def on_save_resume_finished(result, lc):
|
||||
lc.stop()
|
||||
|
||||
self.save_resume_data(self.torrents.keys()).addBoth(on_save_resume_finished, lc)
|
||||
return lc.start(0.01)
|
||||
return self.save_resume_data(self.torrents.keys())
|
||||
|
||||
def update(self):
|
||||
for torrent_id, torrent in self.torrents.items():
|
||||
|
@ -768,24 +762,22 @@ class TorrentManager(component.Component):
|
|||
if torrent_ids is None:
|
||||
torrent_ids = (t[0] for t in self.torrents.iteritems() if t[1].handle.need_save_resume_data())
|
||||
|
||||
if self.waiting_on_resume_data:
|
||||
# If we are still waiting on resume data from last call, force write and clear the queue
|
||||
self.save_resume_data_file()
|
||||
self.waiting_on_resume_data = {}
|
||||
deferreds = []
|
||||
|
||||
def on_torrent_resume_save(result, torrent_id):
|
||||
self.waiting_on_resume_data.pop(torrent_id, None)
|
||||
|
||||
for torrent_id in torrent_ids:
|
||||
self.waiting_on_resume_data[torrent_id] = Deferred()
|
||||
self.waiting_on_resume_data[torrent_id].addBoth(on_torrent_resume_save, torrent_id)
|
||||
d = Deferred().addBoth(on_torrent_resume_save, torrent_id)
|
||||
self.waiting_on_resume_data[torrent_id].append(d)
|
||||
deferreds.append(d)
|
||||
self.torrents[torrent_id].save_resume_data()
|
||||
|
||||
def on_all_resume_data_finished(result):
|
||||
if result:
|
||||
self.save_resume_data_file()
|
||||
|
||||
return DeferredList(self.waiting_on_resume_data.values()).addBoth(on_all_resume_data_finished)
|
||||
return DeferredList(deferreds).addBoth(on_all_resume_data_finished)
|
||||
|
||||
def load_resume_data_file(self):
|
||||
resume_data = {}
|
||||
|
@ -1087,7 +1079,8 @@ class TorrentManager(component.Component):
|
|||
self.resume_data[torrent_id] = lt.bencode(alert.resume_data)
|
||||
|
||||
if torrent_id in self.waiting_on_resume_data:
|
||||
self.waiting_on_resume_data[torrent_id].callback(None)
|
||||
for d in self.waiting_on_resume_data[torrent_id]:
|
||||
d.callback(None)
|
||||
|
||||
def on_alert_save_resume_data_failed(self, alert):
|
||||
log.debug("on_alert_save_resume_data_failed: %s", alert.message())
|
||||
|
|
Loading…
Reference in New Issue