diff --git a/deluge/config.py b/deluge/config.py index 11d54ca48..b0041fb60 100644 --- a/deluge/config.py +++ b/deluge/config.py @@ -100,7 +100,7 @@ class Config: if filedump == self.config: # The config has not changed so lets just return return - except IOError: + except (EOFError, IOError): log.warning("IOError: Unable to open file: '%s'", filename) try: diff --git a/deluge/core/alertmanager.py b/deluge/core/alertmanager.py index 2f171f759..d0d403151 100644 --- a/deluge/core/alertmanager.py +++ b/deluge/core/alertmanager.py @@ -78,7 +78,7 @@ class AlertManager(component.Component): # Handler is in this alert type list value.remove(handler) - def handle_alerts(self): + def handle_alerts(self, wait=False): """Pops all libtorrent alerts in the session queue and handles them appropriately.""" alert = self.session.pop_alert() @@ -91,7 +91,10 @@ class AlertManager(component.Component): # Call any handlers for this alert type if alert_type in self.handlers.keys(): for handler in self.handlers[alert_type]: - gobject.idle_add(handler, alert) + if not wait: + gobject.idle_add(handler, alert) + else: + handler(alert) alert = self.session.pop_alert() diff --git a/deluge/core/core.py b/deluge/core/core.py index cfad4e562..eeb732abd 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -60,7 +60,7 @@ DEFAULT_PREFS = { "config_location": deluge.configmanager.get_config_dir(), "daemon_port": 58846, "allow_remote": False, - "compact_allocation": True, + "compact_allocation": False, "download_location": deluge.common.get_default_download_dir(), "listen_ports": [6881, 6891], "torrentfiles_location": os.path.join(deluge.configmanager.get_config_dir(), "torrentfiles"), @@ -68,11 +68,11 @@ DEFAULT_PREFS = { "state_location": os.path.join(deluge.configmanager.get_config_dir(), "state"), "prioritize_first_last_pieces": False, "random_port": True, - "dht": False, - "upnp": False, - "natpmp": False, - "utpex": False, - "lsd": False, + "dht": True, + "upnp": True, + "natpmp": True, + "utpex": True, + "lsd": True, "enc_in_policy": 1, "enc_out_policy": 1, "enc_level": 2, @@ -89,13 +89,16 @@ DEFAULT_PREFS = { "autoadd_location": "", "autoadd_enable": False, "add_paused": False, - "max_active_seeding": -1, - "max_active_downloading": -1, + "max_active_seeding": 5, + "max_active_downloading": 8, "queue_new_to_top": False, - "queue_finished_to_bottom": False, "stop_seed_at_ratio": False, "remove_seed_at_ratio": False, - "stop_seed_ratio": 1.00 + "stop_seed_ratio": 2.00, + "share_ratio_limit": 2.00, + "seed_time_ratio_limit": 7.00, + "seed_time_limit": 180, + "auto_managed": True } class Core( @@ -234,6 +237,16 @@ class Core( self._on_set_max_download_speed) self.config.register_set_function("max_upload_slots_global", self._on_set_max_upload_slots_global) + self.config.register_set_function("share_ratio_limit", + self._on_set_share_ratio_limit) + self.config.register_set_function("seed_time_ratio_limit", + self._on_set_seed_time_ratio_limit) + self.config.register_set_function("seed_time_limit", + self._on_set_seed_time_limit) + self.config.register_set_function("max_active_downloading", + self._on_set_max_active_downloading) + self.config.register_set_function("max_active_seeding", + self._on_set_max_active_seeding) self.config.register_change_callback(self._on_config_value_change) # Start the AlertManager @@ -550,7 +563,7 @@ class Core( for torrent_id in torrent_ids: try: # If the queue method returns True, then we should emit a signal - if self.torrents.queue.top(torrent_id): + if self.torrents.queue_top(torrent_id): self._torrent_queue_changed() except KeyError: log.warning("torrent_id: %s does not exist in the queue", torrent_id) @@ -562,7 +575,7 @@ class Core( for torrent_id in torrent_ids: try: # If the queue method returns True, then we should emit a signal - if self.torrents.queue.up(torrent_id): + if self.torrents.queue_up(torrent_id): self._torrent_queue_changed() except KeyError: log.warning("torrent_id: %s does not exist in the queue", torrent_id) @@ -574,7 +587,7 @@ class Core( for torrent_id in torrent_ids: try: # If the queue method returns True, then we should emit a signal - if self.torrents.queue.down(torrent_id): + if self.torrents.queue_down(torrent_id): self._torrent_queue_changed() except KeyError: log.warning("torrent_id: %s does not exist in the queue", torrent_id) @@ -584,7 +597,7 @@ class Core( for torrent_id in torrent_ids: try: # If the queue method returns True, then we should emit a signal - if self.torrents.queue.bottom(torrent_id): + if self.torrents.queue_bottom(torrent_id): self._torrent_queue_changed() except KeyError: log.warning("torrent_id: %s does not exist in the queue", torrent_id) @@ -755,3 +768,32 @@ class Core( def _on_set_max_upload_slots_global(self, key, value): log.debug("max_upload_slots_global set to %s..", value) self.session.set_max_uploads(value) + + def _on_set_share_ratio_limit(self, key, value): + log.debug("%s set to %s..", key, value) + self.settings.share_ratio_limit = value + self.session.set_settings(self.settings) + + def _on_set_seed_time_ratio_limit(self, key, value): + log.debug("%s set to %s..", key, value) + self.settings.seed_time_ratio_limit = value + self.session.set_settings(self.settings) + + def _on_set_seed_time_limit(self, key, value): + log.debug("%s set to %s..", key, value) + # This value is stored in minutes in deluge, but libtorrent wants seconds + self.settings.seed_time_limit = int(value * 60) + self.session.set_settings(self.settings) + + def _on_set_max_active_downloading(self, key, value): + log.debug("%s set to %s..", key, value) + log.debug("active_downloads: %s", self.settings.active_downloads) + self.settings.active_downloads = value + self.session.set_settings(self.settings) + + def _on_set_max_active_seeding(self, key, value): + log.debug("%s set to %s..", key, value) + log.debug("active_seeds: %s", self.settings.active_seeds) + self.settings.active_seeds = value + self.session.set_settings(self.settings) + diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 856422cff..f579d1ce7 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -51,9 +51,7 @@ class Torrent: log.debug("Creating torrent object %s", str(handle.info_hash())) # Get the core config self.config = ConfigManager("core.conf") - - # Get a reference to the TorrentQueue - self.torrentqueue = component.get("TorrentQueue") + self.signals = component.get("SignalManager") # Set the libtorrent handle @@ -73,6 +71,9 @@ class Torrent: # Default total_uploaded to 0, this may be changed by the state self.total_uploaded = 0 + # Set default auto_managed value + self.set_auto_managed(options["auto_managed"]) + # Load values from state if we have it if state is not None: # This is for saving the total uploaded between sessions @@ -107,15 +108,12 @@ class Torrent: self.statusmsg = "OK" # The torrents state - self.state = "" + #self.state = "" + self.update_state() # The tracker status self.tracker_status = "" - # This variable is to prevent a state change to 'Paused' when it should - # be 'Queued' - self.next_pause_is_queued = False - log.debug("Torrent object created.") def set_tracker_status(self, status): @@ -144,6 +142,11 @@ class Torrent: def set_save_path(self, save_path): self.save_path = save_path + def set_auto_managed(self, auto_managed): + self.auto_managed = auto_managed + if not self.handle.is_paused(): + self.handle.auto_managed(auto_managed) + def set_file_priorities(self, file_priorities): log.debug("setting %s's file priorities: %s", self.torrent_id, file_priorities) if 0 in self.file_priorities: @@ -182,7 +185,7 @@ class Torrent: # Force a reannounce if there is at least 1 tracker self.force_reannounce() - def set_state_based_on_ltstate(self): + def update_state(self): """Updates the state based on what libtorrent's state for the torrent is""" # Set the initial state based on the lt state LTSTATE = deluge.common.LT_TORRENT_STATE @@ -199,35 +202,26 @@ class Torrent: self.state = "Seeding" elif ltstate == LTSTATE["Allocating"]: self.state = "Allocating" + + if self.handle.is_paused() and len(self.handle.status().error) > 0: + # This is an error'd torrent + self.state = "Error" + self.set_status_message(self.handle.status().error) + self.handle.auto_managed(False) + elif self.handle.is_paused() and self.handle.is_auto_managed(): + self.state = "Queued" + elif self.handle.is_paused() and not self.handle.is_auto_managed(): + self.state = "Paused" def set_state(self, state): """Accepts state strings, ie, "Paused", "Seeding", etc.""" - if state not in TORRENT_STATE: log.debug("Trying to set an invalid state %s", state) return - if state != self.state: - if state == "Queued" and not self.handle.is_paused(): - #component.get("TorrentManager").append_not_state_paused(self.torrent_id) - self.next_pause_is_queued = True - self.handle.pause() - if state == "Error" and not self.handle.is_paused(): - self.next_pause_is_queued = True - - if state == "Paused": - if self.next_pause_is_queued: - self.state = "Queued" - self.next_pause_is_queued = False - else: - self.state = "Paused" + self.state = state + return - log.debug("Setting %s's state to %s", self.torrent_id, state) - self.state = state - - # Update the torrentqueue on any state changes - self.torrentqueue.update_queue() - def set_status_message(self, message): self.statusmsg = message @@ -288,11 +282,6 @@ class Torrent: 'offset': file.offset }) return ret - - def get_queue_position(self): - # We augment the queue position + 1 so that the user sees a 1 indexed - # list. - return self.torrentqueue[self.torrent_id] + 1 def get_peers(self): """Returns a list of peers and various information about them""" @@ -326,7 +315,11 @@ class Torrent: }) return ret - + + def get_queue_position(self): + """Returns the torrents queue position""" + return self.handle.queue_position() + def get_status(self, keys): """Returns the status of the torrent based on the keys provided""" # Create the full dictionary @@ -371,7 +364,11 @@ class Torrent: "max_download_speed": self.max_download_speed, "prioritize_first_last": self.prioritize_first_last, "message": self.statusmsg, - "hash": self.torrent_id + "hash": self.torrent_id, + "active_time": self.status.active_time, + "seeding_time": self.status.seeding_time, + "seed_rank": self.status.seed_rank, + "is_auto_managed": self.auto_managed } fns = { @@ -384,9 +381,9 @@ class Torrent: "eta": self.get_eta, "ratio": self.get_ratio, "file_progress": self.handle.file_progress, - "queue": self.get_queue_position, + "queue": self.handle.queue_position, "is_seed": self.handle.is_seed, - "peers": self.get_peers + "peers": self.get_peers, } self.status = None @@ -419,10 +416,9 @@ class Torrent: def pause(self): """Pause this torrent""" - if self.state == "Queued": - self.set_state("Paused") - return True - + # Turn off auto-management so the torrent will not be unpaused by lt queueing + self.handle.auto_managed(False) + try: self.handle.pause() except Exception, e: @@ -443,55 +439,19 @@ class Torrent: if self.get_ratio() >= self.config["stop_seed_ratio"]: self.signals.emit("torrent_resume_at_stop_ratio") return - - # If the torrent is a seed and there are already the max number of seeds - # active, then just change it to a Queued state. - if self.torrentqueue.get_num_seeding() >= self.config["max_active_seeding"]: - self.set_state("Queued") - - # Update the queuing order if necessary - self.torrentqueue.update_order() + return True - else: - if self.torrentqueue.get_num_downloading() >= self.config["max_active_downloading"]: - self.set_state("Queued") - - # Update the queuing order if necessary - self.torrentqueue.update_order() - return True - + + if self.auto_managed: + # This torrent is to be auto-managed by lt queueing + self.handle.auto_managed(True) + try: self.handle.resume() except: pass - - if self.handle.is_finished(): - self.set_state("Seeding") - else: - # Only delete the .fastresume file if we're still downloading stuff - self.delete_fastresume() - self.set_state("Downloading") return True - - elif self.state == "Queued": - if self.handle.is_finished(): - if self.torrentqueue.get_num_seeding() < self.config["max_active_seeding"] or\ - self.config["max_active_seeding"] == -1: - self.handle.resume() - self.state = "Seeding" - self.torrentqueue.update_order() - else: - return False - else: - if self.torrentqueue.get_num_downloading() < self.config["max_active_downloading"] or\ - self.config["max_active_downloading"] == -1: - self.handle.resume() - self.state = "Downloading" - self.torrentqueue.update_order() - else: - return False - def move_storage(self, dest): """Move a torrent's storage location""" @@ -566,4 +526,3 @@ class Torrent: log.debug("Unable to force recheck: %s", e) return False return True - diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index 070ffdccf..86dcaa003 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -43,7 +43,6 @@ import deluge.libtorrent as lt import deluge.common import deluge.component as component -from deluge.core.torrentqueue import TorrentQueue from deluge.configmanager import ConfigManager from deluge.core.torrent import Torrent @@ -55,7 +54,7 @@ class TorrentState: total_uploaded, trackers, compact, - state, + paused, save_path, max_connections, max_upload_slots, @@ -63,7 +62,8 @@ class TorrentState: max_download_speed, prioritize_first_last, file_priorities, - queue + queue, + auto_managed ): self.torrent_id = torrent_id self.total_uploaded = total_uploaded @@ -72,7 +72,7 @@ class TorrentState: # Options self.compact = compact - self.state = state + self.paused = paused self.save_path = save_path self.max_connections = max_connections self.max_upload_slots = max_upload_slots @@ -80,6 +80,7 @@ class TorrentState: self.max_download_speed = max_download_speed self.prioritize_first_last = prioritize_first_last self.file_priorities = file_priorities + self.auto_managed = auto_managed class TorrentManagerState: def __init__(self): @@ -91,7 +92,7 @@ class TorrentManager(component.Component): session for use on restart.""" def __init__(self, session, alerts): - component.Component.__init__(self, "TorrentManager", depend=["PluginManager"]) + component.Component.__init__(self, "TorrentManager", interval=5000, depend=["PluginManager"]) log.debug("TorrentManager init..") # Set the libtorrent session self.session = session @@ -99,8 +100,6 @@ class TorrentManager(component.Component): self.alerts = alerts # Get the core config self.config = ConfigManager("core.conf") - # Create the TorrentQueue object - self.queue = TorrentQueue() # Create the torrents dict { torrent_id: Torrent } self.torrents = {} @@ -131,7 +130,8 @@ class TorrentManager(component.Component): self.on_alert_tracker_warning) self.alerts.register_handler("storage_moved_alert", self.on_alert_storage_moved) - self.alerts.register_handler("file_error_alert", self.on_alert_file_error) + self.alerts.register_handler("torrent_resumed_alert", + self.on_alert_torrent_resumed) def start(self): # Get the pluginmanager reference @@ -149,14 +149,17 @@ class TorrentManager(component.Component): # Save state on shutdown self.save_state() for key in self.torrents.keys(): - if not self.torrents[key].handle.is_paused() and \ - not self.torrents[key].handle.is_finished(): - if self.torrents[key].compact: - try: - self.torrents[key].pause() - except: - log.warning("Unable to pause torrent %s", key) - self.torrents[key].write_fastresume() + self.torrents[key].handle.pause() + # Wait for all alerts + self.alerts.handle_alerts(True) + + def update(self): + if self.config["stop_seed_at_ratio"]: + for torrent in self.torrents: + if torrent.get_ratio() >= self.config["stop_seed_ratio"]: + torrent.pause() + if self.config["remove_seed_at_ratio"]: + self.remove(torrent.torrent_id) def __getitem__(self, torrent_id): """Return the Torrent with torrent_id""" @@ -223,10 +226,16 @@ class TorrentManager(component.Component): options["file_priorities"] = state.file_priorities options["compact_allocation"] = state.compact options["download_location"] = state.save_path + options["auto_managed"] = state.auto_managed + options["add_paused"] = state.paused add_torrent_params["ti"] =\ self.get_torrent_info_from_file( os.path.join(self.config["state_location"], state.torrent_id + ".torrent")) + if not add_torrent_params["ti"]: + log.error("Unable to add torrent!") + return + add_torrent_params["resume_data"] = self.get_resume_data_from_file(state.torrent_id) else: # We have a torrent_info object so we're not loading from state. @@ -240,6 +249,7 @@ class TorrentManager(component.Component): "prioritize_first_last_pieces", "download_location", "add_paused", + "auto_managed" ] if options == None: @@ -292,28 +302,15 @@ class TorrentManager(component.Component): torrent = Torrent(handle, options, state) # Add the torrent object to the dictionary self.torrents[torrent.torrent_id] = torrent + if self.config["queue_new_to_top"]: + handle.queue_position_top() + component.resume("AlertManager") - - # Add the torrent to the queue - if state is not None: - self.queue.insert(state.queue, torrent.torrent_id) - else: - if self.config["queue_new_to_top"]: - self.queue.insert(0, torrent.torrent_id) - else: - self.queue.append(torrent.torrent_id) - log.debug("state: %s", state) - # Resume the torrent if needed - if state == "Paused" or state == "Error": - torrent.state = "Paused" - elif state == None and not options["add_paused"]: - torrent.handle.resume() - # We set the state based on libtorrent's state - torrent.set_state_based_on_ltstate() - elif state == None and options["add_paused"]: - torrent.set_state = "Paused" + if not options["add_paused"]: + handle.resume() + handle.auto_managed(options["auto_managed"]) if save_state: # Save the session state @@ -342,7 +339,7 @@ class TorrentManager(component.Component): return filedump - def remove(self, torrent_id, remove_torrent, remove_data): + def remove(self, torrent_id, remove_torrent=False, remove_data=False): """Remove a torrent from the manager""" try: # Remove from libtorrent session @@ -372,9 +369,6 @@ class TorrentManager(component.Component): # Remove the .torrent file in the state self.torrents[torrent_id].delete_torrentfile() - # Remove the torrent from the queue - self.queue.remove(torrent_id) - # Remove the torrent from deluge's session try: del self.torrents[torrent_id] @@ -418,60 +412,44 @@ class TorrentManager(component.Component): os.path.join(self.config["state_location"], "torrents.state"), "rb") state = cPickle.load(state_file) state_file.close() - except IOError: + except (EOFError, IOError): log.warning("Unable to load state file.") - - # Try to add the torrents in the state to the session - resume_torrents = [] - # First lets clear the queue and make it the correct length.. This will - # help with inserting values at the right position. - self.queue.set_size(len(state.torrents)) - # Reorder the state.torrents list to add torrents with .fastresume files - # first. - fr_first = [] + # Reorder the state.torrents list to add torrents in the correct queue + # order. + ordered_state = [] for torrent_state in state.torrents: - if os.path.exists(os.path.join( - self.config["state_location"], - torrent_state.torrent_id, ".fastresume")): - fr_first.insert(0, torrent_state) - else: - fr_first.append(torrent_state) + for t in ordered_state: + if torrent_state.queue < t.queue: + ordered_state.insert(0, torrent_state) + break + ordered_state.append(torrent_state) - for torrent_state in fr_first: + for torrent_state in ordered_state: try: - # We need to resume all non-add_paused torrents after plugin hook - if torrent_state.state not in ["Paused", "Queued", "Error"]: - resume_torrents.append(torrent_state.torrent_id) - - self.add( - state=torrent_state, - save_state=False) - + self.add(state=torrent_state, save_state=False) except AttributeError, e: log.error("Torrent state file is either corrupt or incompatible!") - add_paused = {} break # Run the post_session_load plugin hooks self.plugins.run_post_session_load() - - # Resume any torrents that need to be resumed - for torrent_id in resume_torrents: - self.torrents[torrent_id].handle.resume() - self.torrents[torrent_id].set_state_based_on_ltstate() def save_state(self): """Save the state of the TorrentManager to the torrents.state file""" state = TorrentManagerState() # Create the state for each Torrent and append to the list for torrent in self.torrents.values(): + paused = False + if torrent.state == "Paused": + paused = True + torrent_state = TorrentState( torrent.torrent_id, torrent.get_status(["total_uploaded"])["total_uploaded"], torrent.trackers, torrent.compact, - torrent.state, + paused, torrent.save_path, torrent.max_connections, torrent.max_upload_slots, @@ -479,7 +457,8 @@ class TorrentManager(component.Component): torrent.max_download_speed, torrent.prioritize_first_last, torrent.file_priorities, - torrent.get_status(["queue"])["queue"] - 1 # We subtract 1 due to augmentation + torrent.get_queue_position(), + torrent.auto_managed ) state.torrents.append(torrent_state) @@ -496,7 +475,39 @@ class TorrentManager(component.Component): # We return True so that the timer thread will continue return True - + + def queue_top(self, torrent_id): + """Queue torrent to top""" + if self.torrents[torrent_id].get_queue_position() == 0: + return False + + self.torrents[torrent_id].handle.queue_position_top() + return True + + def queue_up(self, torrent_id): + """Queue torrent up one position""" + if self.torrents[torrent_id].get_queue_position() == 0: + return False + + self.torrents[torrent_id].handle.queue_position_up() + return True + + def queue_down(self, torrent_id): + """Queue torrent down one position""" + if self.torrents[torrent_id].get_queue_position() == (len(self.torrents) - 1): + return False + + self.torrents[torrent_id].handle.queue_position_down() + return True + + def queue_bottom(self, torrent_id): + """Queue torrent to bottom""" + if self.torrents[torrent_id].get_queue_position() == (len(self.torrents) - 1): + return False + + self.torrents[torrent_id].handle.queue_position_bottom() + return True + def on_set_max_connections_per_torrent(self, key, value): """Sets the per-torrent connection limit""" log.debug("max_connections_per_torrent set to %s..", value) @@ -508,7 +519,7 @@ class TorrentManager(component.Component): log.debug("max_upload_slots_per_torrent set to %s..", value) for key in self.torrents.keys(): self.torrents[key].set_max_upload_slots(value) - + def on_set_max_upload_speed_per_torrent(self, key, value): log.debug("max_upload_speed_per_torrent set to %s..", value) for key in self.torrents.keys(): @@ -525,20 +536,7 @@ class TorrentManager(component.Component): # Get the torrent_id torrent_id = str(alert.handle.info_hash()) log.debug("%s is finished..", torrent_id) - - # Queue to bottom if enabled - if alert.msg() == "torrent has finished downloading": - if self.config["queue_finished_to_bottom"]: - self.queue.bottom(torrent_id) - - # Set the torrent state if not paused - if not self.torrents[torrent_id].handle.is_paused(): - if self.queue.get_num_seeding() < self.config["max_active_seeding"] or\ - self.config["max_active_seeding"] == -1: - self.torrents[torrent_id].set_state("Seeding") - else: - self.torrents[torrent_id].set_state("Queued") - + self.torrents[torrent_id].update_state() # Write the fastresume file self.torrents[torrent_id].write_fastresume() @@ -547,8 +545,7 @@ class TorrentManager(component.Component): # Get the torrent_id torrent_id = str(alert.handle.info_hash()) # Set the torrent state - log.debug("Setting state 'Paused'..") - self.torrents[torrent_id].set_state("Paused") + self.torrents[torrent_id].update_state() component.get("SignalManager").emit("torrent_paused", torrent_id) # Write the fastresume file @@ -559,12 +556,8 @@ class TorrentManager(component.Component): # Get the torrent_id torrent_id = str(alert.handle.info_hash()) # Set the torrent state - if not self.torrents[torrent_id].handle.is_paused(): - if self.torrents[torrent_id].handle.is_finished(): - self.torrents[torrent_id].set_state("Seeding") - else: - self.torrents[torrent_id].set_state("Downloading") - + self.torrents[torrent_id].update_state() + def on_alert_tracker_reply(self, alert): log.debug("on_alert_tracker_reply") # Get the torrent_id @@ -622,16 +615,16 @@ class TorrentManager(component.Component): def on_alert_storage_moved(self, alert): log.debug("on_alert_storage_moved") + log.debug("save_path: %s", alert.handle.save_path()) # Get the torrent_id torrent_id = str(alert.handle.info_hash()) try: + log.debug("save_path2: %s", self.torrents[torrent_id].handle.save_path()) self.torrents[torrent_id].set_save_path(alert.handle.save_path()) except KeyError: log.debug("torrent_id doesn't exist.") - def on_alert_file_error(self, alert): - log.debug("on_alert_file_error") + def on_alert_torrent_resumed(self, alert): + log.debug("on_alert_torrent_resumed") torrent_id = str(alert.handle.info_hash()) - self.torrents[torrent_id].set_state("Error") - self.torrents[torrent_id].set_status_message(str(alert.msg())) - + self.torrents[torrent_id].update_state() diff --git a/deluge/core/torrentqueue.py b/deluge/core/torrentqueue.py deleted file mode 100644 index 03cfc021a..000000000 --- a/deluge/core/torrentqueue.py +++ /dev/null @@ -1,331 +0,0 @@ -# -# torrentqueue.py -# -# Copyright (C) 2007, 2008 Andrew Resch ('andar') -# -# Deluge is free software. -# -# You may redistribute it and/or modify it under the terms of the -# GNU General Public License, as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) -# any later version. -# -# deluge is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with deluge. If not, write to: -# The Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor -# Boston, MA 02110-1301, USA. -# -# In addition, as a special exception, the copyright holders give -# permission to link the code of portions of this program with the OpenSSL -# library. -# You must obey the GNU General Public License in all respects for all of -# the code used other than OpenSSL. If you modify file(s) with this -# exception, you may extend this exception to your version of the file(s), -# but you are not obligated to do so. If you do not wish to do so, delete -# this exception statement from your version. If you delete this exception -# statement from all source files in the program, then also delete it here. - -import deluge.component as component -import deluge.common -from deluge.configmanager import ConfigManager -from deluge.log import LOG as log - -class TorrentQueue(component.Component): - def __init__(self): - component.Component.__init__(self, "TorrentQueue", depend=["TorrentManager"]) - # This is a list of torrent_ids in the queueing order - self.queue = [] - - # These lists keep track of the torrent states - self.seeding = [] - self.queued_seeding = [] - self.downloading = [] - self.queued_downloading = [] - - self.torrents = component.get("TorrentManager") - self.config = ConfigManager("core.conf") - - # Register config set functions - self.config.register_set_function("max_active_seeding", - self._on_set_max_active_seeding, False) - self.config.register_set_function("max_active_downloading", - self._on_set_max_active_downloading, False) - - def update(self): - # If we're not checking share ratios, just return - if not self.config["stop_seed_at_ratio"]: - return - - stop_ratio = self.config["stop_seed_ratio"] - - for torrent_id in self.torrents.get_torrent_list(): - if self.torrents[torrent_id].handle.is_finished(): - if self.torrents[torrent_id].get_ratio() >= stop_ratio: - # This torrent is at or exceeding the stop ratio so we need to - # pause or remove it from the session. - if self.config["remove_seed_at_ratio"]: - self.torrents.remove(torrent_id, False, False) - else: - self.torrents[torrent_id].pause() - - def update_queue(self): - # Updates the queueing order and max active states - # This only gets called when necessary - self.update_state_lists() - self.update_order() - self.update_max_active() - - def update_state_lists(self): - # Get ordered lists of torrents - self.seeding = [] - self.queued_seeding = [] - self.downloading = [] - self.queued_downloading = [] - - for torrent_id in self.torrents.get_torrent_list(): - if self.torrents[torrent_id].state == "Seeding": - self.seeding.append((self.queue.index(torrent_id), torrent_id)) - elif self.torrents[torrent_id].state == "Downloading": - self.downloading.append((self.queue.index(torrent_id), torrent_id)) - elif self.torrents[torrent_id].state == "Queued": - if self.torrents[torrent_id].handle.is_finished(): - self.queued_seeding.append((self.queue.index(torrent_id), torrent_id)) - else: - self.queued_downloading.append((self.queue.index(torrent_id), torrent_id)) - - # We need to sort these lists by queue position - self.seeding.sort() - self.downloading.sort() - self.queued_downloading.sort() - self.queued_seeding.sort() - - #log.debug("total seeding: %s", len(self.seeding)) - #log.debug("total downloading: %s", len(self.downloading)) - #log.debug("queued seeding: %s", len(self.queued_seeding)) - #log.debug("queued downloading: %s", len(self.queued_downloading)) - - def update_order(self): - # This will queue/resume torrents if the queueing order changes - - #try: - # log.debug("max(seeding): %s", max(self.seeding)[0]) - # log.debug("min(queued_seeding): %s", min(self.queued_seeding)[0]) - #except: - # pass - - #log.debug("queued seeding: %s", self.queued_seeding) - #log.debug("queued downloading: %s", self.queued_downloading) - - if self.seeding != [] and self.queued_seeding != []: - if min(self.queued_seeding)[0] < max(self.seeding)[0]: - num_to_queue = max(self.seeding)[0] - min(self.queued_seeding)[0] - log.debug("queueing: %s", self.seeding[-num_to_queue:]) - - for (pos, torrent_id) in self.seeding[-num_to_queue:]: - self.torrents[torrent_id].set_state("Queued") - - if self.downloading != [] and self.queued_downloading != []: - if min(self.queued_downloading)[0] < max(self.downloading)[0]: - num_to_queue = max(self.downloading)[0] - min(self.queued_downloading)[0] - log.debug("queueing: %s", self.downloading[-num_to_queue:]) - - for (pos, torrent_id) in self.downloading[-num_to_queue:]: - self.torrents[torrent_id].set_state("Queued") - - def update_max_active(self): - if self.config["max_active_seeding"] > -1: - log.debug("max_active_seeding: %s", self.config["max_active_seeding"]) - if len(self.seeding) > self.config["max_active_seeding"]: - # We need to queue some more torrents because we're over the active limit - num_to_queue = len(self.seeding) - self.config["max_active_seeding"] - for (pos, torrent_id) in self.seeding[-num_to_queue:]: - self.torrents[torrent_id].set_state("Queued") - else: - # We need to unqueue more torrents if possible - num_to_unqueue = self.config["max_active_seeding"] - len(self.seeding) - to_unqueue = [] - if num_to_unqueue <= len(self.queued_seeding): - to_unqueue = self.queued_seeding[:num_to_unqueue] - else: - to_unqueue = self.queued_seeding - for (pos, torrent_id) in to_unqueue: - self.torrents[torrent_id].resume() - else: - # The max_active_seeding is set to unlimited, so lets make sure - # all queued seeds are activated. - for (pos, torrent_id) in self.queued_seeding: - self.torrents[torrent_id].resume() - - if self.config["max_active_downloading"] > -1: - if len(self.downloading) > self.config["max_active_downloading"]: - num_to_queue = len(self.downloading) - self.config["max_active_downloading"] - for (pos, torrent_id) in self.downloading[-num_to_queue:]: - self.torrents[torrent_id].set_state("Queued") - else: - # We need to unqueue more torrents if possible - num_to_unqueue = self.config["max_active_downloading"] - len(self.downloading) - to_unqueue = [] - if num_to_unqueue <= len(self.queued_downloading): - to_unqueue = self.queued_downloading[:num_to_unqueue] - else: - to_unqueue = self.queued_downloading - for (pos, torrent_id) in to_unqueue: - self.torrents[torrent_id].resume() - else: - # Unlimited downloading torrents set - for (pos, torrent_id) in self.queued_downloading: - self.torrents[torrent_id].resume() - - def set_size(self, size): - """Clear and set the self.queue list to the length of size""" - log.debug("Setting queue size to %s..", size) - self.queue = [None] * size - - def get_num_seeding(self): - self.update_state_lists() - return len(self.seeding) - - def get_num_downloading(self): - self.update_state_lists() - return len(self.downloading) - - def __getitem__(self, torrent_id): - """Return the queue position of the torrent_id""" - try: - return self.queue.index(torrent_id) - except ValueError: - return -1 - - def append(self, torrent_id): - """Append torrent_id to the bottom of the queue""" - log.debug("Append torrent %s to queue..", torrent_id) - self.queue.append(torrent_id) - return self.queue.index(torrent_id) - - def prepend(self, torrent_id): - """Prepend torrent_id to the top of the queue""" - log.debug("Prepend torrent %s to queue..", torrent_id) - self.queue.insert(0, torrent_id) - return self.queue.index(torrent_id) - - def insert(self, position, torrent_id): - """Inserts torrent_id at position in queue.""" - log.debug("Inserting torrent %s at position %s..", torrent_id, position) - - if position < 0: - for q in self.queue: - if q == None: - self.queue[self.queue.index(q)] = torrent_id - return self.queue.index(q) - - self.queue.append(torrent_id) - return self.queue.index(torrent_id) - - else: - if position > (len(self.queue) - 1): - self.queue.insert(position, torrent_id) - - try: - value = self.queue[position] - except KeyError, IndexError: - self.queue.insert(position, torrent_id) - return position - - if value == None: - self.queue[position] = torrent_id - else: - self.queue.insert(position, torrent_id) - - return position - - def remove(self, torrent_id): - """Removes torrent_id from the list""" - log.debug("Remove torrent %s from queue..", torrent_id) - self.queue.remove(torrent_id) - - def up(self, torrent_id): - """Move torrent_id up one in the queue""" - if torrent_id not in self.queue: - # Raise KeyError if the torrent_id is not in the queue - raise KeyError - - log.debug("Move torrent %s up..", torrent_id) - # Get the index of the torrent_id - index = self.queue.index(torrent_id) - - # Can't queue up if torrent is already at top - if index is 0: - return False - - # Pop and insert the torrent_id at index - 1 - self.queue.insert(index - 1, self.queue.pop(index)) - self.update_queue() - return True - - def top(self, torrent_id): - """Move torrent_id to top of the queue""" - if torrent_id not in self.queue: - # Raise KeyError if the torrent_id is not in the queue - raise KeyError - - log.debug("Move torrent %s to top..", torrent_id) - # Get the index of the torrent_id - index = self.queue.index(torrent_id) - - # Can't queue up if torrent is already at top - if index is 0: - return False - - self.queue.insert(0, self.queue.pop(index)) - self.update_queue() - return True - - def down(self, torrent_id): - """Move torrent_id down one in the queue""" - if torrent_id not in self.queue: - # Raise KeyError if torrent_id is not in the queue - raise KeyError - - log.debug("Move torrent %s down..", torrent_id) - # Get the index of the torrent_id - index = self.queue.index(torrent_id) - - # Can't queue down of torrent_id is at bottom - if index is len(self.queue) - 1: - return False - - # Pop and insert the torrent_id at index + 1 - self.queue.insert(index + 1, self.queue.pop(index)) - self.update_queue() - return True - - def bottom(self, torrent_id): - """Move torrent_id to bottom of the queue""" - if torrent_id not in self.queue: - # Raise KeyError if torrent_id is not in the queue - raise KeyError - - log.debug("Move torrent %s to bottom..", torrent_id) - # Get the index of the torrent_id - index = self.queue.index(torrent_id) - - # Can't queue down of torrent_id is at bottom - if index is len(self.queue) - 1: - return False - - # Pop and append the torrent_id - self.append(self.queue.pop(index)) - self.update_queue() - return True - - def _on_set_max_active_seeding(self, key, value): - self.update_queue() - - def _on_set_max_active_downloading(self, key, value): - self.update_queue() diff --git a/deluge/ui/gtkui/files_tab.py b/deluge/ui/gtkui/files_tab.py index 00ca88cbf..ff199c6ce 100644 --- a/deluge/ui/gtkui/files_tab.py +++ b/deluge/ui/gtkui/files_tab.py @@ -194,7 +194,7 @@ class FilesTab(Tab): state_file = open(os.path.join(config_location, filename), "rb") state = cPickle.load(state_file) state_file.close() - except IOError, e: + except (EOFError, IOError), e: log.warning("Unable to load state file: %s", e) if state == None: diff --git a/deluge/ui/gtkui/glade/main_window.glade b/deluge/ui/gtkui/glade/main_window.glade index 238a4db83..10cff89c1 100644 --- a/deluge/ui/gtkui/glade/main_window.glade +++ b/deluge/ui/gtkui/glade/main_window.glade @@ -630,201 +630,292 @@ True 5 - 6 + 8 15 5 - - - - - - - + True - 0 - - - 1 - 2 - GTK_FILL - - - - - True - 0 - - - 3 - 4 - GTK_FILL - - - - - True - 0 - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - True - 0 - - - 3 - 4 - 1 - 2 - GTK_FILL - - - - - True - 5 - - - True - 0 - <b>Downloaded:</b> - True - - - - - GTK_FILL - - - - - True - 5 - - - True - 0 - <b>Uploaded:</b> - True - - - - - 1 - 2 - GTK_FILL - - - - - True - 5 - - - True - 0 - <b>Share Ratio:</b> - True - - - - - 2 - 3 - GTK_FILL - - - - - True - 5 - - - True - 0 - <b>Next Announce:</b> - True - - + 5 + 6 3 4 GTK_FILL - + + + + + + + + + + + + + True - 15 - 5 - - - True - 0 - <b>Speed:</b> - True - - + 0 + <b>Auto Managed:</b> + True - 2 - 3 + 4 + 5 + 3 + 4 GTK_FILL - + True - 15 - 5 - - - True - 0 - <b>Speed:</b> - True - - - 2 - 3 + 7 + 8 + 2 + 3 + GTK_FILL + + + + + True + + + 7 + 8 1 2 GTK_FILL - + True - 15 - 5 - - - True - 0 - <b>ETA:</b> - True - - + 0 + <b>Seed Rank:</b> + True - 2 - 3 + 6 + 7 2 3 GTK_FILL + + + True + 0 + <b>Seeding Time:</b> + True + + + 6 + 7 + 1 + 2 + GTK_FILL + + + + + True + + + 7 + 8 + GTK_FILL + + + + + True + 0 + <b>Active Time:</b> + True + + + 6 + 7 + GTK_FILL + + + + + True + 0 + True + True + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + True + 0 + + + 3 + 4 + 3 + 4 + GTK_FILL + + + + + True + 0 + PANGO_WRAP_CHAR + True + + + 1 + 6 + 4 + 5 + GTK_FILL + + + + + True + 0 + <b>Tracker Status:</b> + True + + + 4 + 5 + GTK_FILL + + + + + + True + 0 + True + PANGO_WRAP_WORD_CHAR + + + 5 + 6 + 2 + 3 + GTK_FILL + + + + + + True + 0 + 1 + <b>Availability:</b> + True + + + 4 + 5 + 2 + 3 + GTK_FILL + + + + + True + 0 + + + 3 + 4 + 2 + 3 + GTK_FILL + + + + + True + 0 + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + True + 0 + + + 5 + 6 + 1 + 2 + GTK_FILL + + + + + True + 0 + <b>Peers:</b> + True + + + 4 + 5 + 1 + 2 + GTK_FILL + + + + + True + 0 + + + 5 + 6 + GTK_FILL + + + + + True + 0 + <b>Seeders:</b> + True + + + 4 + 5 + GTK_FILL + + True @@ -848,169 +939,188 @@ - + True - 0 - <b>Seeders:</b> - True + 15 + 5 + + + True + 0 + <b>ETA:</b> + True + + - 4 - 5 + 2 + 3 + 2 + 3 GTK_FILL - + True - 0 + 15 + 5 + + + True + 0 + <b>Speed:</b> + True + + - 5 - 6 - GTK_FILL - - - - - True - 0 - <b>Peers:</b> - True - - - 4 - 5 + 2 + 3 1 2 GTK_FILL - + True - 0 + 15 + 5 + + + True + 0 + <b>Speed:</b> + True + + + + + 2 + 3 + GTK_FILL + + + + + True + 5 + + + True + 0 + <b>Next Announce:</b> + True + + + + + 3 + 4 + GTK_FILL + + + + + True + 5 + + + True + 0 + <b>Share Ratio:</b> + True + + + + + 2 + 3 + GTK_FILL + + + + + True + 5 + + + True + 0 + <b>Uploaded:</b> + True + + - 5 - 6 1 2 GTK_FILL - + True - 0 + 5 + + + True + 0 + <b>Downloaded:</b> + True + + - 1 - 2 - 2 - 3 GTK_FILL - + True 0 3 4 - 2 - 3 + 1 + 2 GTK_FILL - + True 0 - 1 - <b>Availability:</b> - True - - - 4 - 5 - 2 - 3 - GTK_FILL - - - - - True - 0 - True - PANGO_WRAP_WORD_CHAR - - - 5 - 6 - 2 - 3 - GTK_FILL - - - - - - True - 0 - <b>Tracker Status:</b> - True - - - 4 - 5 - GTK_FILL - - - - - - True - 0 - PANGO_WRAP_CHAR - True 1 - 6 - 4 - 5 + 2 + 1 + 2 GTK_FILL - + True 0 3 4 - 3 - 4 GTK_FILL - + True 0 - True - True 1 2 - 3 - 4 GTK_FILL @@ -1097,7 +1207,7 @@ - + True 0 True @@ -1105,42 +1215,28 @@ 1 2 - 3 - 4 + 4 + 5 - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - - - 1 - 4 - 5 - 6 - - - - - + True 0 1 - <b>Status:</b> + <b># of files:</b> True - 5 - 6 + 4 + 5 GTK_FILL - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 @@ -1151,65 +1247,52 @@ 1 4 - 2 - 3 + 1 + 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - <b>Path:</b> - True - - + 0 + <b>Hash:</b> + True - 2 - 3 + 1 + 2 GTK_FILL - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - 0 - 0 - 1 - <b>Name:</b> - True - - - - - GTK_FILL - - - - - + True 0 - True - PANGO_WRAP_CHAR True 1 4 + 6 + 7 + + + + + + True + 0 + 1 + <b>Tracker:</b> + True + + + 6 + 7 + GTK_FILL @@ -1236,51 +1319,64 @@ - - True - 0 - 1 - <b>Tracker:</b> - True - - - 6 - 7 - GTK_FILL - - - - - + True 0 + True + PANGO_WRAP_CHAR True 1 4 - 6 - 7 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - <b>Hash:</b> - True + 5 + + + True + 0 + 0 + 1 + <b>Name:</b> + True + + - 1 - 2 GTK_FILL - + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + <b>Path:</b> + True + + + + + 2 + 3 + GTK_FILL + + + + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 @@ -1291,28 +1387,42 @@ 1 4 - 1 - 2 + 2 + 3 - + True 0 1 - <b># of files:</b> + <b>Status:</b> True - 4 - 5 + 5 + 6 GTK_FILL - + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + 1 + 4 + 5 + 6 + + + + + True 0 True @@ -1320,8 +1430,8 @@ 1 2 - 4 - 5 + 3 + 4 @@ -1504,7 +1614,7 @@ - + True True 6 @@ -1514,100 +1624,8 @@ 1 2 - 3 - 4 - - - - - - - True - 0 - Max Upload Slots: - - - 3 - 4 - GTK_FILL - - - - - - True - KiB/s - - - 2 - 3 - 1 - 2 - - - - - - - True - KiB/s - - - 2 - 3 - - - - - - - True - 0 - Max Download Speed: - - - GTK_FILL - - - - - - True - 0 - Max Upload Speed: - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - Max Connections: - - 2 3 - GTK_FILL - - - - - - True - True - 6 - 1 - -1 -1 999999 1 10 10 - 1 - - - 1 - 2 @@ -1631,7 +1649,99 @@ - + + True + True + 6 + 1 + -1 -1 999999 1 10 10 + 1 + + + 1 + 2 + + + + + + + True + 0 + Max Connections: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + Max Upload Speed: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Max Download Speed: + + + GTK_FILL + + + + + + True + KiB/s + + + 2 + 3 + + + + + + + True + KiB/s + + + 2 + 3 + 1 + 2 + + + + + + + True + 0 + Max Upload Slots: + + + 3 + 4 + GTK_FILL + + + + + True True 6 @@ -1641,8 +1751,8 @@ 1 2 - 2 - 3 + 3 + 4 diff --git a/deluge/ui/gtkui/glade/preferences_dialog.glade b/deluge/ui/gtkui/glade/preferences_dialog.glade index 509a696c5..9c1b98b3e 100644 --- a/deluge/ui/gtkui/glade/preferences_dialog.glade +++ b/deluge/ui/gtkui/glade/preferences_dialog.glade @@ -1,6 +1,6 @@ - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -2153,40 +2153,6 @@ Disabled 2 2 10 - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 1 - -1 -1 9999 1 10 10 - True - True - - - 1 - 2 - 1 - 2 - - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 1 - -1 -1 9999 1 10 10 - True - True - - - 1 - 2 - - - True @@ -2195,11 +2161,25 @@ Disabled Total active downloading: - 1 - 2 GTK_FILL + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 1 + 0 -1 9999 1 10 10 + True + True + + + 1 + 2 + + + True @@ -2208,9 +2188,29 @@ Disabled Total active seeding: + 1 + 2 GTK_FILL + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 1 + 0 -1 9999 1 10 10 + True + True + + + 1 + 2 + 1 + 2 + + + @@ -2251,13 +2251,93 @@ Disabled GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 - + True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Queue newly finished torrents to bottom - 0 - True + 3 + 2 + 10 + + + True + 0 + Share Ratio Limit: + + + GTK_FILL + + + + + True + 0 + Seed Time Ratio: + + + 1 + 2 + GTK_FILL + + + + + True + 0 + Seed Time (m): + + + 2 + 3 + GTK_FILL + + + + + True + True + 6 + 1 + 1.5 -1 100 0.10000000000000001 10 10 + 2 + + + 1 + 2 + + + + + + True + True + 6 + 1 + 6 -1 100 0.10000000000000001 10 10 + 2 + + + 1 + 2 + 1 + 2 + + + + + + True + True + 6 + 1 + 6 -1 100 1 10 10 + + + 1 + 2 + 2 + 3 + + + @@ -2286,7 +2366,7 @@ Disabled True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 - 1 0.5 100 0.10000000000000001 1 1 + 2 0.5 100 0.10000000000000001 1 1 2 True diff --git a/deluge/ui/gtkui/listview.py b/deluge/ui/gtkui/listview.py index c36063630..708853df0 100644 --- a/deluge/ui/gtkui/listview.py +++ b/deluge/ui/gtkui/listview.py @@ -189,7 +189,7 @@ class ListView: state_file = open(os.path.join(config_location, filename), "rb") state = cPickle.load(state_file) state_file.close() - except IOError, e: + except (EOFError, IOError), e: log.warning("Unable to load state file: %s", e) # Keep the state in self.state so we can access it as we add new columns diff --git a/deluge/ui/gtkui/mainwindow.py b/deluge/ui/gtkui/mainwindow.py index 1836a1b56..491642e7e 100644 --- a/deluge/ui/gtkui/mainwindow.py +++ b/deluge/ui/gtkui/mainwindow.py @@ -83,8 +83,6 @@ class MainWindow(component.Component): except: pass - # Load the state prior to showing - self.load_window_state() self.window.show() def hide(self): diff --git a/deluge/ui/gtkui/peers_tab.py b/deluge/ui/gtkui/peers_tab.py index 1b9f33556..5555e39ce 100644 --- a/deluge/ui/gtkui/peers_tab.py +++ b/deluge/ui/gtkui/peers_tab.py @@ -176,7 +176,7 @@ class PeersTab(Tab): state_file = open(os.path.join(config_location, filename), "rb") state = cPickle.load(state_file) state_file.close() - except IOError, e: + except (EOFError, IOError), e: log.warning("Unable to load state file: %s", e) if state == None: diff --git a/deluge/ui/gtkui/preferences.py b/deluge/ui/gtkui/preferences.py index accf79935..2196ebb82 100644 --- a/deluge/ui/gtkui/preferences.py +++ b/deluge/ui/gtkui/preferences.py @@ -238,7 +238,10 @@ class Preferences(component.Component): "spin_seeding": ("value", self.core_config["max_active_seeding"]), "spin_downloading": ("value", self.core_config["max_active_downloading"]), "chk_queue_new_top": ("active", self.core_config["queue_new_to_top"]), - "chk_finished_bottom": ("active", self.core_config["queue_finished_to_bottom"]), + "spin_share_ratio_limit": ("value", self.core_config["share_ratio_limit"]), + "spin_seed_time_ratio_limit": \ + ("value", self.core_config["seed_time_ratio_limit"]), + "spin_seed_time_limit": ("value", self.core_config["seed_time_limit"]), "chk_seed_ratio": ("active", self.core_config["stop_seed_at_ratio"]), "spin_share_ratio": ("value", self.core_config["stop_seed_ratio"]), "chk_remove_ratio": ("active", self.core_config["remove_seed_at_ratio"]) @@ -324,10 +327,12 @@ class Preferences(component.Component): "spin_seeding", "spin_downloading", "chk_queue_new_top", - "chk_finished_bottom", "chk_seed_ratio", "spin_share_ratio", - "chk_remove_ratio" + "chk_remove_ratio", + "spin_share_ratio_limit", + "spin_seed_time_ratio_limit", + "spin_seed_time_limit" ] # We don't appear to be connected to a daemon for key in core_widget_list: @@ -500,14 +505,18 @@ class Preferences(component.Component): self.glade.get_widget("spin_seeding").get_value_as_int() new_core_config["max_active_downloading"] = \ self.glade.get_widget("spin_downloading").get_value_as_int() - new_core_config["queue_finished_to_bottom"] = \ - self.glade.get_widget("chk_finished_bottom").get_active() new_core_config["stop_seed_at_ratio"] = \ self.glade.get_widget("chk_seed_ratio").get_active() new_core_config["remove_seed_at_ratio"] = \ self.glade.get_widget("chk_remove_ratio").get_active() new_core_config["stop_seed_ratio"] = \ self.glade.get_widget("spin_share_ratio").get_value() + new_core_config["share_ratio_limit"] = \ + self.glade.get_widget("spin_share_ratio_limit").get_value() + new_core_config["seed_time_ratio_limit"] = \ + self.glade.get_widget("spin_seed_time_ratio_limit").get_value() + new_core_config["seed_time_limit"] = \ + self.glade.get_widget("spin_seed_time_limit").get_value() # GtkUI for key in new_gtkui_config.keys(): diff --git a/deluge/ui/gtkui/statistics_tab.py b/deluge/ui/gtkui/statistics_tab.py index b0e9ad5e4..43f20dc47 100644 --- a/deluge/ui/gtkui/statistics_tab.py +++ b/deluge/ui/gtkui/statistics_tab.py @@ -37,6 +37,7 @@ from deluge.ui.client import aclient as client import deluge.component as component import deluge.common from deluge.ui.gtkui.torrentdetails import Tab +from deluge.log import LOG as log def fpeer_sized(first, second): return "%s (%s)" % (deluge.common.fsize(first), deluge.common.fsize(second)) @@ -79,6 +80,10 @@ class StatisticsTab(Tab): (glade.get_widget("summary_share_ratio"), fratio, ("ratio",)), (glade.get_widget("summary_tracker_status"), None, ("tracker_status",)), (glade.get_widget("summary_next_announce"), deluge.common.ftime, ("next_announce",)), + (glade.get_widget("summary_active_time"), deluge.common.ftime, ("active_time",)), + (glade.get_widget("summary_seed_time"), deluge.common.ftime, ("seeding_time",)), + (glade.get_widget("summary_seed_rank"), str, ("seed_rank",)), + (glade.get_widget("summary_auto_managed"), str, ("is_auto_managed",)), (glade.get_widget("progressbar"), fpcnt, ("progress",)) ] @@ -100,7 +105,8 @@ class StatisticsTab(Tab): "upload_payload_rate", "num_peers", "num_seeds", "total_peers", "total_seeds", "eta", "ratio", "next_announce", "tracker_status", "max_connections", "max_upload_slots", - "max_upload_speed", "max_download_speed"] + "max_upload_speed", "max_download_speed", "active_time", + "seeding_time", "seed_rank", "is_auto_managed"] client.get_torrent_status( self._on_get_torrent_status, selected, status_keys) diff --git a/deluge/ui/gtkui/torrentdetails.py b/deluge/ui/gtkui/torrentdetails.py index f7b0f34de..f5f1902f8 100644 --- a/deluge/ui/gtkui/torrentdetails.py +++ b/deluge/ui/gtkui/torrentdetails.py @@ -299,7 +299,7 @@ class TorrentDetails(component.Component): state_file = open(os.path.join(config_location, filename), "rb") state = cPickle.load(state_file) state_file.close() - except IOError, e: + except (EOFError, IOError), e: log.warning("Unable to load state file: %s", e) return state diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index 8473286e2..62ffcc667 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -92,7 +92,14 @@ def cell_data_progress(column, cell, model, row, data): textstr = textstr + " %.2f%%" % value if cell.get_property("text") != textstr: cell.set_property("text", textstr) - + +def cell_data_queue(column, cell, model, row, data): + value = model.get_value(row, data) + if value < 0: + cell.set_property("text", "") + else: + cell.set_property("text", value + 1) + class TorrentView(listview.ListView, component.Component): """TorrentView handles the listing of torrents.""" def __init__(self): @@ -116,7 +123,7 @@ class TorrentView(listview.ListView, component.Component): # Add the columns to the listview self.add_text_column("torrent_id", hidden=True) self.add_bool_column("filter", hidden=True) - self.add_text_column("#", col_type=int, status_field=["queue"]) + self.add_func_column("#", cell_data_queue, [int], status_field=["queue"]) self.add_texticon_column(_("Name"), status_field=["state", "name"], function=cell_data_statusicon) self.add_func_column(_("Size"), diff --git a/libtorrent/bindings/python/src/torrent_status.cpp b/libtorrent/bindings/python/src/torrent_status.cpp index 091dbf7d4..186d0f2f5 100755 --- a/libtorrent/bindings/python/src/torrent_status.cpp +++ b/libtorrent/bindings/python/src/torrent_status.cpp @@ -74,6 +74,7 @@ void bind_torrent_status() .def_readonly("seeding_time", &torrent_status::seeding_time) .def_readonly("seed_rank", &torrent_status::seed_rank) .def_readonly("last_scrape", &torrent_status::last_scrape) + .def_readonly("error", &torrent_status::error) ; enum_("states") diff --git a/libtorrent/include/libtorrent/disk_io_thread.hpp b/libtorrent/include/libtorrent/disk_io_thread.hpp index 68bb8a61a..3556c3cf6 100644 --- a/libtorrent/include/libtorrent/disk_io_thread.hpp +++ b/libtorrent/include/libtorrent/disk_io_thread.hpp @@ -207,20 +207,11 @@ namespace libtorrent int num_blocks; // the pointers to the block data boost::shared_array blocks; -#ifndef NDEBUG - ~cached_piece_entry() - { - TORRENT_ASSERT(storage == 0); - } -#endif }; typedef boost::recursive_mutex mutex_t; typedef std::list cache_t; - char* allocate_buffer(mutex_t::scoped_lock& l); - void free_buffer(char* buf, mutex_t::scoped_lock& l); - // cache operations cache_t::iterator find_cached_piece( cache_t& cache, disk_io_job const& j @@ -228,7 +219,7 @@ namespace libtorrent // write cache operations void flush_oldest_piece(mutex_t::scoped_lock& l); - void flush_expired_pieces(mutex_t::scoped_lock& l); + void flush_expired_pieces(); void flush_and_remove(cache_t::iterator i, mutex_t::scoped_lock& l); void flush(cache_t::iterator i, mutex_t::scoped_lock& l); void cache_block(disk_io_job& j, mutex_t::scoped_lock& l); @@ -242,14 +233,18 @@ namespace libtorrent bool make_room(int num_blocks , cache_t::iterator ignore , mutex_t::scoped_lock& l); - int try_read_from_cache(disk_io_job const& j, mutex_t::scoped_lock& l); + int try_read_from_cache(disk_io_job const& j); - mutable mutex_t m_mutex; + // this mutex only protects m_jobs, m_queue_buffer_size + // and m_abort + mutable mutex_t m_queue_mutex; boost::condition m_signal; bool m_abort; std::list m_jobs; size_type m_queue_buffer_size; + // this protects the piece cache and related members + mutable mutex_t m_piece_mutex; // write cache cache_t m_pieces; @@ -280,6 +275,8 @@ namespace libtorrent bool m_coalesce_reads; bool m_use_read_cache; + // this only protects the pool allocator + mutable mutex_t m_pool_mutex; #ifndef TORRENT_DISABLE_POOL_ALLOCATOR // memory pool for read and write operations // and disk cache diff --git a/libtorrent/include/libtorrent/session.hpp b/libtorrent/include/libtorrent/session.hpp index ebd6b7ea5..fcae0be32 100755 --- a/libtorrent/include/libtorrent/session.hpp +++ b/libtorrent/include/libtorrent/session.hpp @@ -129,7 +129,7 @@ namespace libtorrent , resume_data(0) , storage_mode(storage_mode_sparse) , paused(true) - , auto_managed(false) + , auto_managed(true) , duplicate_is_error(false) , storage(sc) , userdata(0) diff --git a/libtorrent/src/disk_io_thread.cpp b/libtorrent/src/disk_io_thread.cpp index bd0cd49fb..2845acee7 100644 --- a/libtorrent/src/disk_io_thread.cpp +++ b/libtorrent/src/disk_io_thread.cpp @@ -78,7 +78,7 @@ namespace libtorrent void disk_io_thread::join() { - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_queue_mutex); disk_io_job j; j.action = disk_io_job::abort_thread; m_jobs.insert(m_jobs.begin(), j); @@ -90,7 +90,7 @@ namespace libtorrent void disk_io_thread::get_cache_info(sha1_hash const& ih, std::vector& ret) const { - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_piece_mutex); ret.clear(); ret.reserve(m_pieces.size()); for (cache_t::const_iterator i = m_pieces.begin() @@ -111,20 +111,20 @@ namespace libtorrent cache_status disk_io_thread::status() const { - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_piece_mutex); return m_cache_stats; } void disk_io_thread::set_cache_size(int s) { - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_piece_mutex); TORRENT_ASSERT(s >= 0); m_cache_size = s; } void disk_io_thread::set_cache_expiry(int ex) { - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_piece_mutex); TORRENT_ASSERT(ex > 0); m_cache_expiry = ex; } @@ -132,7 +132,7 @@ namespace libtorrent // aborts read operations void disk_io_thread::stop(boost::intrusive_ptr s) { - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_queue_mutex); // read jobs are aborted, write and move jobs are syncronized for (std::list::iterator i = m_jobs.begin(); i != m_jobs.end();) @@ -200,10 +200,12 @@ namespace libtorrent return cache.end(); } - void disk_io_thread::flush_expired_pieces(mutex_t::scoped_lock& l) + void disk_io_thread::flush_expired_pieces() { ptime now = time_now(); + mutex_t::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; for (;;) { @@ -226,15 +228,12 @@ namespace libtorrent for (int i = 0; i < blocks_in_piece; ++i) { if (p.blocks[i] == 0) continue; - free_buffer(p.blocks[i], l); + free_buffer(p.blocks[i]); p.blocks[i] = 0; --p.num_blocks; --m_cache_stats.cache_size; --m_cache_stats.read_cache_size; } - l.unlock(); - p.storage = 0; - l.lock(); } bool disk_io_thread::clear_oldest_read_piece( @@ -330,7 +329,7 @@ namespace libtorrent offset += m_block_size; buffer_size += block_size; } - free_buffer(p.blocks[i], l); + free_buffer(p.blocks[i]); p.blocks[i] = 0; TORRENT_ASSERT(p.num_blocks > 0); --p.num_blocks; @@ -343,9 +342,6 @@ namespace libtorrent for (int i = 0; i < blocks_in_piece; ++i) TORRENT_ASSERT(p.blocks[i] == 0); #endif - l.unlock(); - p.storage = 0; - l.lock(); } void disk_io_thread::cache_block(disk_io_job& j, mutex_t::scoped_lock& l) @@ -368,9 +364,6 @@ namespace libtorrent p.blocks[block] = j.buffer; ++m_cache_stats.cache_size; m_pieces.push_back(p); -#ifndef NDEBUG - p.storage = 0; -#endif } // fills a piece with data from disk, returns the total number of bytes @@ -388,7 +381,7 @@ namespace libtorrent // stop allocating and don't read more than // what we've allocated now if (p.blocks[i]) break; - p.blocks[i] = allocate_buffer(l); + p.blocks[i] = allocate_buffer(); // the allocation failed, break if (p.blocks[i] == 0) break; @@ -484,9 +477,6 @@ namespace libtorrent else m_read_pieces.push_back(p); -#ifndef NDEBUG - p.storage = 0; -#endif return ret; } @@ -509,7 +499,7 @@ namespace libtorrent if (p.blocks[k]) { #ifndef TORRENT_DISABLE_POOL_ALLOCATOR - TORRENT_ASSERT(m_pool.is_from(p.blocks[k])); + TORRENT_ASSERT(is_disk_buffer(p.blocks[k])); #endif ++blocks; } @@ -533,7 +523,7 @@ namespace libtorrent if (p.blocks[k]) { #ifndef TORRENT_DISABLE_POOL_ALLOCATOR - TORRENT_ASSERT(m_pool.is_from(p.blocks[k])); + TORRENT_ASSERT(is_disk_buffer(p.blocks[k])); #endif ++blocks; } @@ -551,10 +541,11 @@ namespace libtorrent } #endif - int disk_io_thread::try_read_from_cache(disk_io_job const& j, mutex_t::scoped_lock& l) + int disk_io_thread::try_read_from_cache(disk_io_job const& j) { TORRENT_ASSERT(j.buffer); + mutex_t::scoped_lock l(m_piece_mutex); if (!m_use_read_cache) return -2; cache_t::iterator p @@ -624,7 +615,7 @@ namespace libtorrent TORRENT_ASSERT(!j.callback); TORRENT_ASSERT(j.storage); TORRENT_ASSERT(j.buffer_size <= m_block_size); - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_queue_mutex); #ifndef NDEBUG if (j.action == disk_io_job::write) { @@ -696,7 +687,7 @@ namespace libtorrent #ifdef TORRENT_DISABLE_POOL_ALLOCATOR return true; #else - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_pool_mutex); return m_pool.is_from(buffer); #endif } @@ -704,18 +695,7 @@ namespace libtorrent char* disk_io_thread::allocate_buffer() { - mutex_t::scoped_lock l(m_mutex); - return allocate_buffer(l); - } - - void disk_io_thread::free_buffer(char* buf) - { - mutex_t::scoped_lock l(m_mutex); - free_buffer(buf, l); - } - - char* disk_io_thread::allocate_buffer(mutex_t::scoped_lock& l) - { + mutex_t::scoped_lock l(m_pool_mutex); #ifdef TORRENT_STATS ++m_allocations; #endif @@ -726,8 +706,9 @@ namespace libtorrent #endif } - void disk_io_thread::free_buffer(char* buf, mutex_t::scoped_lock& l) + void disk_io_thread::free_buffer(char* buf) { + mutex_t::scoped_lock l(m_pool_mutex); #ifdef TORRENT_STATS --m_allocations; #endif @@ -745,12 +726,15 @@ namespace libtorrent #ifdef TORRENT_DISK_STATS m_log << log_time() << " idle" << std::endl; #endif - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock jl(m_queue_mutex); while (m_jobs.empty() && !m_abort) - m_signal.wait(l); + m_signal.wait(jl); if (m_abort && m_jobs.empty()) { + jl.unlock(); + + mutex_t::scoped_lock l(m_piece_mutex); // flush all disk caches for (cache_t::iterator i = m_pieces.begin() , end(m_pieces.end()); i != end; ++i) @@ -758,10 +742,8 @@ namespace libtorrent for (cache_t::iterator i = m_read_pieces.begin() , end(m_read_pieces.end()); i != end; ++i) free_piece(*i, l); - l.unlock(); m_pieces.clear(); m_read_pieces.clear(); - l.lock(); return; } @@ -777,40 +759,14 @@ namespace libtorrent disk_io_job j = m_jobs.front(); m_jobs.pop_front(); - - if (j.action == disk_io_job::abort_thread) - { - m_abort = true; - - for (std::list::iterator i = m_jobs.begin(); - i != m_jobs.end();) - { - if (i->action == disk_io_job::read) - { - if (i->callback) m_ios.post(bind(i->callback, -1, *i)); - m_jobs.erase(i++); - continue; - } - if (i->action == disk_io_job::check_files) - { - if (i->callback) m_ios.post(bind(i->callback - , piece_manager::disk_check_aborted, *i)); - m_jobs.erase(i++); - continue; - } - ++i; - } - continue; - } - m_queue_buffer_size -= j.buffer_size; + jl.unlock(); - flush_expired_pieces(l); - l.unlock(); + flush_expired_pieces(); int ret = 0; - TORRENT_ASSERT(j.storage); + TORRENT_ASSERT(j.storage || j.action == disk_io_job::abort_thread); #ifdef TORRENT_DISK_STATS ptime start = time_now(); #endif @@ -820,6 +776,31 @@ namespace libtorrent switch (j.action) { + case disk_io_job::abort_thread: + { + mutex_t::scoped_lock jl(m_queue_mutex); + m_abort = true; + + for (std::list::iterator i = m_jobs.begin(); + i != m_jobs.end();) + { + if (i->action == disk_io_job::read) + { + if (i->callback) m_ios.post(bind(i->callback, -1, *i)); + m_jobs.erase(i++); + continue; + } + if (i->action == disk_io_job::check_files) + { + if (i->callback) m_ios.post(bind(i->callback + , piece_manager::disk_check_aborted, *i)); + m_jobs.erase(i++); + continue; + } + ++i; + } + break; + } case disk_io_job::read: { std::string const& error_string = j.storage->error(); @@ -837,7 +818,6 @@ namespace libtorrent #ifdef TORRENT_DISK_STATS m_log << log_time() << " read " << j.buffer_size << std::endl; #endif - mutex_t::scoped_lock l(m_mutex); INVARIANT_CHECK; TORRENT_ASSERT(j.buffer == 0); j.buffer = allocate_buffer(); @@ -850,7 +830,7 @@ namespace libtorrent } disk_buffer_holder read_holder(*this, j.buffer); - ret = try_read_from_cache(j, l); + ret = try_read_from_cache(j); // -2 means there's no space in the read cache // or that the read cache is disabled @@ -864,7 +844,6 @@ namespace libtorrent } else if (ret == -2) { - l.unlock(); ret = j.storage->read_impl(j.buffer, j.piece, j.offset , j.buffer_size); if (ret < 0) @@ -874,7 +853,6 @@ namespace libtorrent j.storage->clear_error(); break; } - l.lock(); ++m_cache_stats.blocks_read; } read_holder.release(); @@ -897,7 +875,7 @@ namespace libtorrent #ifdef TORRENT_DISK_STATS m_log << log_time() << " write " << j.buffer_size << std::endl; #endif - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_piece_mutex); INVARIANT_CHECK; cache_t::iterator p = find_cached_piece(m_pieces, j, l); @@ -934,8 +912,7 @@ namespace libtorrent #ifdef TORRENT_DISK_STATS m_log << log_time() << " hash" << std::endl; #endif - mutex_t::scoped_lock l(m_mutex); - + mutex_t::scoped_lock l(m_piece_mutex); INVARIANT_CHECK; cache_t::iterator i @@ -993,8 +970,8 @@ namespace libtorrent m_log << log_time() << " release" << std::endl; #endif TORRENT_ASSERT(j.buffer == 0); - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_piece_mutex); INVARIANT_CHECK; for (cache_t::iterator i = m_pieces.begin(); i != m_pieces.end();) @@ -1009,10 +986,13 @@ namespace libtorrent ++i; } } -#ifndef TORRENT_DISABLE_POOL_ALLOCATOR - m_pool.release_memory(); -#endif l.unlock(); +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + { + mutex_t::scoped_lock l(m_pool_mutex); + m_pool.release_memory(); + } +#endif ret = j.storage->release_files_impl(); if (ret != 0) { @@ -1028,8 +1008,8 @@ namespace libtorrent m_log << log_time() << " delete" << std::endl; #endif TORRENT_ASSERT(j.buffer == 0); - mutex_t::scoped_lock l(m_mutex); + mutex_t::scoped_lock l(m_piece_mutex); INVARIANT_CHECK; cache_t::iterator i = std::remove_if( @@ -1042,15 +1022,18 @@ namespace libtorrent for (int j = 0; j < blocks_in_piece; ++j) { if (k->blocks[j] == 0) continue; - free_buffer(k->blocks[j], l); + free_buffer(k->blocks[j]); k->blocks[j] = 0; } } m_pieces.erase(i, m_pieces.end()); -#ifndef TORRENT_DISABLE_POOL_ALLOCATOR - m_pool.release_memory(); -#endif l.unlock(); +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + { + mutex_t::scoped_lock l(m_pool_mutex); + m_pool.release_memory(); + } +#endif ret = j.storage->delete_files_impl(); if (ret != 0) { @@ -1094,9 +1077,7 @@ namespace libtorrent // if the check is not done, add it at the end of the job queue if (ret == piece_manager::need_full_check) { - mutex_t::scoped_lock l(m_mutex); - m_jobs.push_back(j); - m_jobs.back().callback.swap(handler); + add_job(j, handler); continue; } break; diff --git a/libtorrent/src/piece_picker.cpp b/libtorrent/src/piece_picker.cpp index f0b7fb937..7de18a326 100755 --- a/libtorrent/src/piece_picker.cpp +++ b/libtorrent/src/piece_picker.cpp @@ -37,9 +37,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include -// non-standard header, is_sorted() -//#include - #include "libtorrent/piece_picker.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/bitfield.hpp" @@ -255,6 +252,7 @@ namespace libtorrent TORRENT_ASSERT(m_num_have >= 0); TORRENT_ASSERT(m_num_have_filtered >= 0); TORRENT_ASSERT(m_num_filtered >= 0); + TORRENT_ASSERT(m_seeds >= 0); if (!m_downloads.empty()) { @@ -446,6 +444,7 @@ namespace libtorrent float piece_picker::distributed_copies() const { + TORRENT_ASSERT(m_seeds >= 0); const float num_pieces = static_cast(m_piece_map.size()); int min_availability = piece_pos::max_peer_count; @@ -768,8 +767,12 @@ namespace libtorrent int new_priority = p.priority(this); if (new_priority == prev_priority) return; - if (m_sequential_download >= 0) return; if (m_dirty) return; + if (m_sequential_download >= 0) + { + m_dirty = true; + return; + } if (prev_priority == -1) { add(index); @@ -784,7 +787,6 @@ namespace libtorrent { TORRENT_PIECE_PICKER_INVARIANT_CHECK; ++m_seeds; - if (m_sequential_download >= 0) return; if (m_seeds == 1) { // when m_seeds is increased from 0 to 1 @@ -798,12 +800,6 @@ namespace libtorrent { TORRENT_PIECE_PICKER_INVARIANT_CHECK; - if (m_sequential_download >= 0) - { - --m_seeds; - return; - } - if (m_seeds > 0) { --m_seeds; @@ -816,6 +812,7 @@ namespace libtorrent } return; } + TORRENT_ASSERT(m_seeds == 0); for (std::vector::iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end; ++i) @@ -835,6 +832,7 @@ namespace libtorrent if (m_sequential_download >= 0) { ++p.peer_count; + m_dirty = true; return; } @@ -856,7 +854,9 @@ namespace libtorrent piece_pos& p = m_piece_map[index]; if (m_sequential_download >= 0) { + TORRENT_ASSERT(p.peer_count > 0); --p.peer_count; + m_dirty = true; return; } int prev_priority = p.priority(this); @@ -1756,6 +1756,7 @@ namespace libtorrent void piece_picker::get_availability(std::vector& avail) const { + TORRENT_ASSERT(m_seeds >= 0); TORRENT_PIECE_PICKER_INVARIANT_CHECK; avail.resize(m_piece_map.size()); diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index 446693e6e..6b812cfbe 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -524,6 +524,12 @@ namespace aux { m_disk_thread.set_cache_size(s.cache_size); if (m_settings.cache_expiry != s.cache_expiry) m_disk_thread.set_cache_size(s.cache_expiry); + // if queuing settings were changed, recalculate + // queued torrents sooner + if ((m_settings.active_downloads != s.active_downloads + || m_settings.active_seeds != s.active_seeds) + && m_auto_manage_time_scaler > 2) + m_auto_manage_time_scaler = 2; m_settings = s; if (m_settings.connection_speed <= 0) m_settings.connection_speed = 200; @@ -1324,7 +1330,6 @@ namespace aux { if (t->is_finished()) { --num_seeds; - --num_downloaders; } else { @@ -1368,9 +1373,8 @@ namespace aux { , end(seeds.end()); i != end; ++i) { torrent* t = *i; - if (num_downloaders > 0 && num_seeds > 0) + if (num_seeds > 0) { - --num_downloaders; --num_seeds; if (t->is_paused()) t->resume(); } diff --git a/libtorrent/src/storage.cpp b/libtorrent/src/storage.cpp index 3a8090144..327d9e20a 100755 --- a/libtorrent/src/storage.cpp +++ b/libtorrent/src/storage.cpp @@ -255,7 +255,7 @@ namespace libtorrent template void recursive_copy(Path const& old_path, Path const& new_path, std::string& error) { - using boost::filesystem::directory_iterator; + using boost::filesystem::basic_directory_iterator; #ifndef BOOST_NO_EXCEPTIONS try { #endif @@ -263,7 +263,7 @@ namespace libtorrent if (is_directory(old_path)) { create_directory(new_path); - for (directory_iterator i(old_path), end; i != end; ++i) + for (basic_directory_iterator i(old_path), end; i != end; ++i) { recursive_copy(i->path(), new_path / i->leaf(), error); if (!error.empty()) return; @@ -281,13 +281,13 @@ namespace libtorrent template void recursive_remove(Path const& old_path) { - using boost::filesystem::directory_iterator; + using boost::filesystem::basic_directory_iterator; #ifndef BOOST_NO_EXCEPTIONS try { #endif if (is_directory(old_path)) { - for (directory_iterator i(old_path), end; i != end; ++i) + for (basic_directory_iterator i(old_path), end; i != end; ++i) recursive_remove(i->path()); remove(old_path); }