diff --git a/ChangeLog b/ChangeLog index 37c7f4f41..3332182ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * added new session functions to more efficiently query torrent status * added alerts for added and removed torrents * expanded plugin interface to support session wide states * made the metadata block requesting algorithm more robust against hash check failures diff --git a/docs/libtorrent_plugins.html b/docs/libtorrent_plugins.html index db2dc6b9e..4e45b5e5e 100644 --- a/docs/libtorrent_plugins.html +++ b/docs/libtorrent_plugins.html @@ -62,16 +62,17 @@
  • plugin interface
  • -
  • torrent_plugin

    libtorrent has a plugin interface for implementing extensions to the protocol. @@ -85,6 +86,8 @@ to fit a particular (closed) network.

  • add data and parse data from the extension handshake.
  • send extension messages and standard bittorrent messages.
  • override or block the handling of standard bittorrent messages.
  • +
  • save and restore state via the session state
  • +
  • see all alerts that are posted
  • a word of caution

    @@ -92,24 +95,30 @@ to fit a particular (closed) network.

    dead locks and race conditions. Since a plugin has access to internal structures it is also quite easy to sabotage libtorrent's operation.

    All the callbacks in this interface are called with the main libtorrent thread -mutex locked. And they are always called from the libtorrent main thread. In +mutex locked. And they are always called from the libtorrent network thread. In case portions of your plugin are called from other threads, typically the main thread, you cannot use any of the member functions on the internal structures in libtorrent, since those require the mutex to be locked. Futhermore, you would also need to have a mutex on your own shared data within the plugin, to make sure it is not accessed at the same time from the libtorrent thread (through a callback). See boost thread's mutex. If you need to send out a message from -another thread, use an internal queue, and do the actual sending in tick().

    +another thread, it is advised to use an internal queue, and do the actual +sending in tick().

    +

    Since the plugin interface gives you easy access to internal structures, it +is not supported as a stable API. Plugins should be considered spcific to a +specific version of libtorrent. Although, in practice the internals mostly +don't change that dramatically.

    plugin interface

    -

    The plugin interface consists of two base classes that the plugin may -implement. These are called torrent_plugin and peer_plugin. They are -both found in the <libtorrent/extensions.hpp> header.

    -

    These plugins are instantiated for each torrent and possibly each peer, +

    The plugin interface consists of three base classes that the plugin may +implement. These are called plugin, torrent_plugin and peer_plugin. +They are found in the <libtorrent/extensions.hpp> header.

    +

    These plugins are instantiated for each session, torrent and possibly each peer, respectively.

    -

    This is done by passing in a function or function object to +

    For plugins that only need per torrent state, it is enough to only implement +torrent_plugin and pass a constructor function or function object to session::add_extension() or torrent_handle::add_extension() (if the torrent has already been started and you want to hook in the extension at run-time).

    @@ -125,6 +134,25 @@ may or may not be 0. If it is a null pointer, the extension is simply ignored for this torrent. If it is a valid pointer (to a class inheriting torrent_plugin), it will be associated with this torrent and callbacks will be made on torrent events.

    +

    For more elaborate plugins which require session wide state, you would +implement plugin, construct an object (in a boost::shared_ptr) and pass +it in to session::add_extension().

    +
    +
    +

    plugin

    +
    +struct plugin
    +{
    +        virtual ~plugin();
    +        virtual boost::shared_ptr<torrent_plugin> new_torrent(torrent* t, void* user);
    +
    +        virtual void added(boost::weak_ptr<aux::session_impl> s);
    +        virtual void on_alert(alert const* a);
    +        virtual void on_tick();
    +        virtual void save_state(entry& ent) const;
    +        virtual void load_state(lazy_entry const& ent);
    +};
    +

    torrent_plugin

    diff --git a/docs/manual.html b/docs/manual.html index ffbf0aec4..05c5ed038 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -69,257 +69,261 @@
  • add_torrent()
  • remove_torrent()
  • find_torrent() get_torrents()
  • -
  • load_asnum_db() load_country_db() as_for_ip()
  • -
  • set_ip_filter()
  • -
  • get_ip_filter()
  • -
  • status()
  • -
  • get_cache_status()
  • -
  • get_cache_info()
  • -
  • is_listening() listen_port() listen_on()
  • -
  • set_alert_mask()
  • -
  • pop_alert() wait_for_alert() set_alert_queue_size_limit()
  • -
  • add_feed()
  • -
  • remove_feed()
  • -
  • get_feeds()
  • -
  • add_extension()
  • -
  • set_settings() set_pe_settings()
  • -
  • set_proxy() proxy()
  • -
  • set_i2p_proxy() i2p_proxy()
  • -
  • start_dht() stop_dht() set_dht_settings() dht_state() is_dht_running()
  • -
  • add_dht_node() add_dht_router()
  • -
  • start_lsd() stop_lsd()
  • -
  • start_upnp() stop_upnp()
  • -
  • start_natpmp() stop_natpmp()
  • +
  • get_torrent_status() refresh_torrent_status()
  • +
  • load_asnum_db() load_country_db() as_for_ip()
  • +
  • set_ip_filter()
  • +
  • get_ip_filter()
  • +
  • status()
  • +
  • get_cache_status()
  • +
  • get_cache_info()
  • +
  • is_listening() listen_port() listen_on()
  • +
  • set_alert_mask()
  • +
  • pop_alert() wait_for_alert()
  • +
  • add_feed()
  • +
  • remove_feed()
  • +
  • get_feeds()
  • +
  • add_extension()
  • +
  • set_settings() set_pe_settings()
  • +
  • set_proxy() proxy()
  • +
  • set_i2p_proxy() i2p_proxy()
  • +
  • start_dht() stop_dht() set_dht_settings() dht_state() is_dht_running()
  • +
  • add_dht_node() add_dht_router()
  • +
  • start_lsd() stop_lsd()
  • +
  • start_upnp() stop_upnp()
  • +
  • start_natpmp() stop_natpmp()
  • -
  • entry
  • @@ -466,7 +470,13 @@ class session: public boost::noncopyable void remove_torrent(torrent_handle const& h , int options = none); torrent_handle find_torrent(sha_hash const& ih); + std::vector<torrent_handle> get_torrents() const; + void get_torrent_status(std::vector<torrent_status>* ret + , boost::function<bool(torrent_status const&)> const& pred + , boost::uint32_t flags = 0) const; + void refresh_torrent_status(std::vector<torrent_status>* ret + , boost::uint32_t flags) const; void set_settings(session_settings const& settings); session_settings settings() const; @@ -838,6 +848,34 @@ In case the torrent cannot be found, an invalid torrent_handle is returned.

    get_torrents() returns a vector of torrent_handles to all the torrents currently in the session.

    +
    +

    get_torrent_status() refresh_torrent_status()

    +
    +
    +void get_torrent_status(std::vector<torrent_status>* ret
    +        , boost::function<bool(torrent_status const&)> const& pred
    +        , boost::uint32_t flags = 0) const;
    +void refresh_torrent_status(std::vector<torrent_status>* ret
    +        , boost::uint32_t flags = 0) const;
    +
    +
    +

    get_torrent_status returns a vector of the torrent_status for every +torrent which satisfies pred, which is a predicate function which determines +if a torrent should be included in the returned set or not. Returning true means +it should be included and false means excluded. The flags argument is the same +as to torrent_handle::status(). Since pred is guaranteed to be called for +every torrent, it may be used to count the number of torrents of different categories +as well.

    +

    refresh_torrent_status takes a vector of torrent_status structs (for instance +the same vector that was returned by get_torrent_status()) and refreshes the +status based on the handle member. It is possible to use this function by +first setting up a vector of default constructed torrent_status objects, only +initializing the handle member, in order to request the torrent status for +multiple torrents in a single call. This can save a significant amount of time +if you have a lot of torrents.

    +

    Any torrent_status object whose handle member is not referring to a +valid torrent are ignored.

    +

    load_asnum_db() load_country_db() as_for_ip()

    @@ -953,9 +991,18 @@ struct session_status int num_unchoked; int allowed_upload_slots; + int up_bandwidth_queue; + int down_bandwidth_queue; + + int up_bandwidth_bytes_queue; + int down_bandwidth_bytes_queue; + int optimistic_unchoke_counter; int unchoke_counter; + int disk_write_queue; + int disk_read_queue; + int dht_nodes; int dht_node_cache; int dht_torrents; @@ -997,10 +1044,17 @@ than the sum of all peers of all torrents because the incoming connections may n be assigned a torrent yet.

    num_unchoked is the current number of unchoked peers. allowed_upload_slots is the current allowed number of unchoked peers.

    +

    up_bandwidth_queue and down_bandwidth_queue are the number of peers that are +waiting for more bandwidth quota from the torrent rate limiter. +up_bandwidth_bytes_queue and down_bandwidth_bytes_queue count the number of +bytes the connections are waiting for to be able to send and receive.

    optimistic_unchoke_counter and unchoke_counter tells the number of seconds until the next optimistic unchoke change and the start of the next unchoke interval. These numbers may be reset prematurely if a peer that is unchoked disconnects or becomes notinterested.

    +

    disk_write_queue and disk_read_queue are the number of peers currently +waiting on a disk write or disk read to complete before it receives or sends +any more data on the socket. It'a a metric of how disk bound you are.

    dht_nodes, dht_node_cache and dht_torrents are only available when built with DHT support. They are all set to 0 if the DHT isn't running. When the DHT is running, dht_nodes is set to the number of nodes in the routing @@ -1167,13 +1221,12 @@ void set_alert_mask(int m); m is a bitmask where each bit represents a category of alerts.

    See alerts for mor information on the alert categories.

    -
    -

    pop_alert() wait_for_alert() set_alert_queue_size_limit()

    +
    +

    pop_alert() wait_for_alert()

     std::auto_ptr<alert> pop_alert();
     alert const* wait_for_alert(time_duration max_wait);
    -size_t set_alert_queue_size_limit(size_t queue_size_limit_);
     

    pop_alert() is used to ask the session if any errors or events has occurred. With @@ -1187,9 +1240,8 @@ same pointer until the alert is popped by calling < leaving any alert dispatching mechanism independent of this blocking call, the dispatcher can be called and it can pop the alert independently.

    In the python binding, wait_for_alert takes the number of milliseconds to wait as an integer.

    -

    set_alert_queue_size_limit() you can specify how many alerts can be awaiting for dispatching. -If this limit is reached, new incoming alerts can not be received until alerts are popped -by calling pop_alert. Default value is 1000.

    +

    To control the max number of alerts that's queued by the session, see +session_settings::alert_queue_size.

    save_resume_data_alert and save_resume_data_failed_alert are always posted, regardelss of the alert mask.

    @@ -3186,6 +3238,8 @@ struct torrent_status checking_resume_data }; + torrent_handle handle; + state_t state; bool paused; bool auto_managed; @@ -3284,6 +3338,7 @@ struct torrent_status bool need_save_resume; }; +

    handle is a handle to the torrent whose status the object represents.

    progress is a value in the range [0, 1], that represents the progress of the torrent's current task. It may be checking files or downloading.

    progress_ppm reflects the same value as progress, but instead in a range @@ -4246,6 +4301,10 @@ struct session_settings int torrent_connect_boost; bool seeding_outgoing_connections; + + bool no_connect_privileged_ports; + int alert_queue_size; + int max_metadata_size; };

    version is automatically set to the libtorrent version you're using @@ -4924,6 +4983,14 @@ may be set to false in very specific applications where the cost of making outgoing connections is high, and there are no or small benefits of doing so. For instance, if no nodes are behind a firewall or a NAT, seeds don't need to make outgoing connections.

    +

    if no_connect_privileged_ports is true (which is the default), libtorrent +will not connect to any peers on priviliged ports (<= 1023). This can mitigate +using bittorrent swarms for certain DDoS attacks.

    +

    alert_queue_size is the maximum number of alerts queued up internally. If +alerts are not popped, the queue will eventually fill up to this level. This +defaults to 1000.

    +

    max_metadata_size is the maximum allowed size (in bytes) to be received +by the metadata extension, i.e. magnet links. It defaults to 1 MiB.

    @@ -5011,6 +5078,7 @@ struct proxy_settings proxy_type type; bool proxy_hostnames; + bool proxy_peer_connections; }; @@ -5042,6 +5110,9 @@ user authorization. The username and password will be sent to the proxy.

    proxy_hostnames defaults to true. It means that hostnames should be attempted to be resolved through the proxy instead of using the local DNS service. This is only supported by SOCKS5 and HTTP.

    +

    proxy_peer_connections determines whether or not to excempt peer and +web seed connections from using the proxy. This defaults to true, i.e. peer +connections are proxied by default.

    ip_filter

    @@ -5630,6 +5701,7 @@ public: virtual std::string message() const = 0; virtual char const* what() const = 0; virtual int category() const = 0; + virtual bool discardable() const; virtual std::auto_ptr<alert> clone() const = 0; }; @@ -5658,6 +5730,9 @@ switch (a->type()) not include any information that might be bundled with the alert.

    category() returns a bitmask specifying which categories this alert belong to.

    clone() returns a pointer to a copy of the alert.

    +

    discardable() determines whether or not an alert is allowed to be discarded +when the alert queue is full. There are a few alerts which may not be discared, +since they would break the user contract, such as save_resume_data_alert.

    message() generate a string describing the alert and the information bundled with it. This is mainly intended for debug and development use. It is not suitable to use this for applications that may be localized. Instead, handle each alert @@ -5681,6 +5756,33 @@ struct tracker_alert: torrent_alert };

    The specific alerts are:

    +
    +

    torrent_added_alert

    +

    The torrent_added_alert is posted once every time a torrent is added. +It doesn't contain any members of its own, but inherits the torrent handle +from its base class. +It's posted when the status_notification bit is set in the alert mask.

    +
    +struct torrent_added_alert: torrent_alert
    +{
    +        // ...
    +};
    +
    +
    +
    +

    torrent_removed_alert

    +

    The torrent_removed_alert is posted whenever a torrent is removed. Since +the torrent handle in its baseclass will always be invalid (since the torrent +is already removed) it has the info hash as a member, to identify it. +It's posted when the status_notification bit is set in the alert mask.

    +
    +struct torrent_removed_alert: torrent_alert
    +{
    +        // ...
    +        sha1_hash info_hash;
    +};
    +
    +

    read_piece_alert

    This alert is posted when the asynchronous read operation initiated by @@ -5817,6 +5919,18 @@ struct file_error_alert: torrent_alert };

    +
    +

    torrent_error_alert

    +

    This is posted whenever a torrent is transitioned into the error state.

    +
    +struct torrent_error_alert: torrent_alert
    +{
    +        // ...
    +        error_code error;
    +};
    +
    +

    The error specifies which error the torrent encountered.

    +

    file_renamed_alert

    This is posted as a response to a torrent_handle::rename_file call, if the rename @@ -6060,7 +6174,7 @@ struct peer_disconnected_alert: peer_alert

    invalid_request_alert

    This is a debug alert that is generated by an incoming invalid piece request. -ìp is the address of the peer and the request is the actual incoming +Ïp is the address of the peer and the request is the actual incoming request from the peer.

     struct invalid_request_alert: peer_alert
    @@ -7495,13 +7609,13 @@ std::string error_code_to_string(boost::system::error_code const& ec)
             static const char const* swedish[] =
             {
                     "inget fel",
    -                "en fil i torrenten kolliderar med en fil från en annan torrent",
    +                "en fil i torrenten kolliderar med en fil frÂn en annan torrent",
                     "hash check misslyckades",
    -                "torrent filen är inte en dictionary",
    -                "'info'-nyckeln saknas eller är korrupt i torrentfilen",
    -                "'info'-fältet är inte en dictionary",
    -                "'piece length' fältet saknas eller är korrupt i torrentfilen",
    -                "torrentfilen saknar namnfältet",
    +                "torrent filen ‰r inte en dictionary",
    +                "'info'-nyckeln saknas eller ‰r korrupt i torrentfilen",
    +                "'info'-f‰ltet ‰r inte en dictionary",
    +                "'piece length' f‰ltet saknas eller ‰r korrupt i torrentfilen",
    +                "torrentfilen saknar namnf‰ltet",
                     "ogiltigt namn i torrentfilen (kan vara en attack)",
                     // ... more strings here
             };
    diff --git a/docs/manual.rst b/docs/manual.rst
    index c349a78cf..cd31d19c5 100644
    --- a/docs/manual.rst
    +++ b/docs/manual.rst
    @@ -158,7 +158,13 @@ The ``session`` class has the following synopsis::
     		void remove_torrent(torrent_handle const& h
     			, int options = none);
     		torrent_handle find_torrent(sha_hash const& ih);
    +
     		std::vector get_torrents() const;
    +		void get_torrent_status(std::vector* ret
    +			, boost::function const& pred
    +			, boost::uint32_t flags = 0) const;
    +		void refresh_torrent_status(std::vector* ret
    +			, boost::uint32_t flags) const;
     
     		void set_settings(session_settings const& settings);
     		session_settings settings() const;
    @@ -565,6 +571,36 @@ See ``torrent_handle::is_valid()`` to know if the torrent was found or not.
     ``get_torrents()`` returns a vector of torrent_handles to all the torrents
     currently in the session.
     
    +get_torrent_status() refresh_torrent_status()
    +---------------------------------------------
    +
    +	::
    +
    +		void get_torrent_status(std::vector* ret
    +			, boost::function const& pred
    +			, boost::uint32_t flags = 0) const;
    +		void refresh_torrent_status(std::vector* ret
    +			, boost::uint32_t flags = 0) const;
    +
    +``get_torrent_status`` returns a vector of the ``torrent_status`` for every
    +torrent which satisfies ``pred``, which is a predicate function which determines
    +if a torrent should be included in the returned set or not. Returning true means
    +it should be included and false means excluded. The ``flags`` argument is the same
    +as to ``torrent_handle::status()``. Since ``pred`` is guaranteed to be called for
    +every torrent, it may be used to count the number of torrents of different categories
    +as well.
    +
    +``refresh_torrent_status`` takes a vector of ``torrent_status`` structs (for instance
    +the same vector that was returned by ``get_torrent_status()``) and refreshes the
    +status based on the ``handle`` member. It is possible to use this function by
    +first setting up a vector of default constructed ``torrent_status`` objects, only
    +initializing the ``handle`` member, in order to request the torrent status for
    +multiple torrents in a single call. This can save a significant amount of time
    +if you have a lot of torrents.
    +
    +Any ``torrent_status`` object whose ``handle`` member is not referring to a
    +valid torrent are ignored.
    +
     load_asnum_db() load_country_db() as_for_ip()
     ---------------------------------------------
     
    @@ -3145,6 +3181,8 @@ It contains the following fields::
     			allocating,
     			checking_resume_data
     		};
    +
    +		torrent_handle handle;
     	
     		state_t state;
     		bool paused;
    @@ -3244,6 +3282,8 @@ It contains the following fields::
     		bool need_save_resume;
     	};
     
    +``handle`` is a handle to the torrent whose status the object represents.
    +
     ``progress`` is a value in the range [0, 1], that represents the progress of the
     torrent's current task. It may be checking files or downloading.
     
    diff --git a/docs/tuning.html b/docs/tuning.html
    index e33f88fb5..41389b91e 100644
    --- a/docs/tuning.html
    +++ b/docs/tuning.html
    @@ -73,17 +73,19 @@
     
  • high performance seeding
  • -
  • benchmarking
  • @@ -312,6 +314,21 @@ the same pieces, and on the other hand assume that they won't request the same p and drop them when the first peer requests it. To enable volatile read cache, set session_settings::volatile_read_cache to true.

    +
    +

    uTP-TCP mixed mode

    +

    libtorrent supports uTP, which has a delay based congestion controller. In order to +avoid having a single TCP bittorrent connection completely starve out any uTP connection, +there is a mixed mode algorithm. This attempts to detect congestion on the uTP peers and +throttle TCP to avoid it taking over all bandwidth. This balances the bandwidth resources +between the two protocols. When running on a network where the bandwidth is in such an +abundance that it's virtually infinite, this algorithm is no longer necessary, and might +even be harmful to throughput. It is adviced to experiment with the +session_setting::mixed_mode_algorithm, setting it to session_settings::prefer_tcp. +This setting entirely disables the balancing and unthrottles all connections. On a typical +home connection, this would mean that none of the benefits of uTP would be preserved +(the modem's send buffer would be full at all times) and uTP connections would for the most +part be squashed by the TCP traffic.

    +

    send buffer low watermark

    libtorrent uses a low watermark for send buffers to determine when a new piece should @@ -347,9 +364,37 @@ number via session::set_max_uploa and session_settings::active_seeds.

    +
    +

    scalability

    +

    In order to make more efficient use of the libtorrent interface when running a large +number of torrents simultaneously, one can use the session::get_torrent_status() call +together with session::refresh_torrent_status(). Keep in mind that every call into +libtorrent that return some value have to block your thread while posting a message to +the main network thread and then wait for a response (calls that don't return any data +will simply post the message and then immediately return). The time this takes might +become significant once you reach a few hundred torrents (depending on how many calls +you make to each torrent and how often). get_torrent_status lets you query the +status of all torrents in a single call. This will actually loop through all torrents +and run a provided predicate function to determine whether or not to include it in +the returned vector. If you have a lot of torrents, you might want to update the status +of only certain torrents. For instance, you might only be interested in torrents that +are being downloaded.

    +

    The intended use of these functions is to start off by calling get_torrent_status +to get a list of all torrents that match your criteria. Then call refresh_torrent_status +on that list. This will only refresh the status for the torrents in your list, and thus +ignore all other torrents you might be running. This may save a significant amount of +time, especially if the number of torrents you're interested in is small. In order to +keep your list of interested torrents up to date, you can either call get_torrent_status +from time to time, to include torrents you might have become interested in since the last +time. In order to stop refreshing a certain torrent, simply remove it from the list.

    +

    A more efficient way however, would be to subscribe to status alert notifications, and +update your list based on these alerts. There are alerts for when torrents are added, removed, +paused, resumed, completed etc. Doing this ensures that you only query status for the +minimal set of torrents you are actually interested in.

    +

    benchmarking

    -

    There are a bunch of built-in instrumentation of libtorrent that can be used to get an insight +

    There is a bunch of built-in instrumentation of libtorrent that can be used to get an insight into what it's doing and how well it performs. This instrumentation is enabled by defining preprocessor symbols when building.

    There are also a number of scripts that parses the log files and generates graphs (requires diff --git a/docs/tuning.rst b/docs/tuning.rst index 4936befae..35fb56dc9 100644 --- a/docs/tuning.rst +++ b/docs/tuning.rst @@ -343,10 +343,41 @@ torrent limits To seed thousands of torrents, you need to increase the ``session_settings::active_limit`` and ``session_settings::active_seeds``. +scalability +=========== + +In order to make more efficient use of the libtorrent interface when running a large +number of torrents simultaneously, one can use the ``session::get_torrent_status()`` call +together with ``session::refresh_torrent_status()``. Keep in mind that every call into +libtorrent that return some value have to block your thread while posting a message to +the main network thread and then wait for a response (calls that don't return any data +will simply post the message and then immediately return). The time this takes might +become significant once you reach a few hundred torrents (depending on how many calls +you make to each torrent and how often). ``get_torrent_status`` lets you query the +status of all torrents in a single call. This will actually loop through all torrents +and run a provided predicate function to determine whether or not to include it in +the returned vector. If you have a lot of torrents, you might want to update the status +of only certain torrents. For instance, you might only be interested in torrents that +are being downloaded. + +The intended use of these functions is to start off by calling ``get_torrent_status`` +to get a list of all torrents that match your criteria. Then call ``refresh_torrent_status`` +on that list. This will only refresh the status for the torrents in your list, and thus +ignore all other torrents you might be running. This may save a significant amount of +time, especially if the number of torrents you're interested in is small. In order to +keep your list of interested torrents up to date, you can either call ``get_torrent_status`` +from time to time, to include torrents you might have become interested in since the last +time. In order to stop refreshing a certain torrent, simply remove it from the list. + +A more efficient way however, would be to subscribe to status alert notifications, and +update your list based on these alerts. There are alerts for when torrents are added, removed, +paused, resumed, completed etc. Doing this ensures that you only query status for the +minimal set of torrents you are actually interested in. + benchmarking ============ -There are a bunch of built-in instrumentation of libtorrent that can be used to get an insight +There is a bunch of built-in instrumentation of libtorrent that can be used to get an insight into what it's doing and how well it performs. This instrumentation is enabled by defining preprocessor symbols when building. diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 284bfe04c..bfb84edde 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -185,11 +185,15 @@ bool print_fails = false; bool print_send_bufs = true; enum { - torrents_all = 0, - torrents_downloading = 1, - torrents_not_paused = 2, - torrents_seeding = 3, - torrents_paused = 4 + torrents_all, + torrents_downloading, + torrents_not_paused, + torrents_seeding, + torrents_queued, + torrents_stopped, + torrents_checking, + + torrents_max }; int torrent_filter = torrents_not_paused; @@ -201,13 +205,13 @@ struct torrent_entry libtorrent::torrent_status status; }; -typedef std::multimap handles_t; +// maps filenames to torrent_handles +typedef std::multimap handles_t; -bool show_torrent(torrent_entry const& te) +using libtorrent::torrent_status; + +bool show_torrent(libtorrent::torrent_status const& st, int torrent_filter) { - using libtorrent::torrent_status; - torrent_status const& st = te.status; - switch (torrent_filter) { case torrents_all: return true; @@ -220,11 +224,33 @@ bool show_torrent(torrent_entry const& te) return !st.paused && (st.state == torrent_status::seeding || st.state == torrent_status::finished); - case torrents_paused: return st.paused; + case torrents_queued: return st.paused && st.auto_managed; + case torrents_stopped: return st.paused && !st.auto_managed; + case torrents_checking: return st.state == torrent_status::checking_files + || st.state == torrent_status::queued_for_checking; } return true; } +bool yes(libtorrent::torrent_status const&) +{ return true; } + +bool compare_torrent(torrent_status const& lhs, torrent_status const& rhs) +{ + if (lhs.queue_position != -1 && rhs.queue_position != -1) + { + // both are downloading, sort by queue pos + return lhs.queue_position < rhs.queue_position; + } + else if (lhs.queue_position == -1 && rhs.queue_position == -1) + { + // both are seeding, sort by seed-rank + return lhs.seed_rank > rhs.seed_rank; + } + + return (lhs.queue_position == -1) < (rhs.queue_position == -1); +} + FILE* g_log_file = 0; int active_torrent = 0; @@ -627,8 +653,11 @@ void add_torrent(libtorrent::session& ses return; } - handles.insert(std::pair( - monitored_dir?std::string(torrent):std::string(), h)); + if (monitored_dir) + { + handles.insert(std::pair( + torrent, h)); + } h.set_max_connections(max_connections_per_torrent); h.set_max_uploads(-1); @@ -643,7 +672,7 @@ void add_torrent(libtorrent::session& ses void scan_dir(std::string const& dir_path , libtorrent::session& ses - , handles_t& handles + , handles_t& files , float preferred_ratio , int allocation_mode , std::string const& save_path @@ -660,8 +689,8 @@ void scan_dir(std::string const& dir_path std::string file = combine_path(dir_path, i.file()); if (extension(file) != ".torrent") continue; - handles_t::iterator k = handles.find(file); - if (k != handles.end()) + handles_t::iterator k = files.find(file); + if (k != files.end()) { valid.insert(file); continue; @@ -669,14 +698,14 @@ void scan_dir(std::string const& dir_path // the file has been added to the dir, start // downloading it. - add_torrent(ses, handles, file, preferred_ratio, allocation_mode + add_torrent(ses, files, file, preferred_ratio, allocation_mode , save_path, true, torrent_upload_limit, torrent_download_limit); valid.insert(file); } // remove the torrents that are no longer in the directory - for (handles_t::iterator i = handles.begin(); !handles.empty() && i != handles.end();) + for (handles_t::iterator i = files.begin(); !files.empty() && i != files.end();) { if (i->first.empty() || valid.find(i->first) != valid.end()) { @@ -684,10 +713,10 @@ void scan_dir(std::string const& dir_path continue; } - torrent_handle& h = i->second.handle; + torrent_handle& h = i->second; if (!h.is_valid()) { - handles.erase(i++); + files.erase(i++); continue; } @@ -697,17 +726,17 @@ void scan_dir(std::string const& dir_path // will save it to disk if (h.need_save_resume_data()) h.save_resume_data(); - handles.erase(i++); + files.erase(i++); } } -torrent_entry& get_active_torrent(handles_t const& handles) +torrent_status const& get_active_torrent(std::vector const& torrents) { - if (active_torrent >= handles.size() + if (active_torrent >= torrents.size() || active_torrent < 0) active_torrent = 0; - handles_t::const_iterator i = handles.begin(); + std::vector::const_iterator i = torrents.begin(); std::advance(i, active_torrent); - return const_cast(i->second); + return *i; } void print_alert(libtorrent::alert const* a, std::string& str) @@ -752,7 +781,7 @@ int save_file(std::string const& filename, std::vector& v) } void handle_alert(libtorrent::session& ses, libtorrent::alert* a - , handles_t const& handles) + , handles_t const& files) { using namespace libtorrent; @@ -775,16 +804,16 @@ void handle_alert(libtorrent::session& ses, libtorrent::alert* a std::vector out; bencode(std::back_inserter(out), *p->resume_data); save_file(combine_path(h.save_path(), ".resume/" + h.name() + ".resume"), out); - if (std::find_if(handles.begin(), handles.end() - , boost::bind(&torrent_entry::handle, boost::bind(&handles_t::value_type::second, _1)) == h) == handles.end()) + if (std::find_if(files.begin(), files.end() + , boost::bind(&handles_t::value_type::second, _1) == h) == files.end()) ses.remove_torrent(h); } } else if (save_resume_data_failed_alert* p = alert_cast(a)) { torrent_handle h = p->handle; - if (std::find_if(handles.begin(), handles.end() - , boost::bind(&torrent_entry::handle, boost::bind(&handles_t::value_type::second, _1)) == h) == handles.end()) + if (std::find_if(files.begin(), files.end() + , boost::bind(&handles_t::value_type::second, _1) == h) == files.end()) ses.remove_torrent(h); } } @@ -880,7 +909,9 @@ int main(int argc, char* argv[]) // it was added through the directory monitor. It is used to // be able to remove torrents that were added via the directory // monitor when they're not in the directory anymore. - handles_t handles; + std::vector handles; + handles_t files; + session ses(fingerprint("LT", LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0) , session::add_default_plugins , alert::all_categories @@ -1107,8 +1138,6 @@ int main(int argc, char* argv[]) continue; } - handles.insert(std::pair(std::string(), h)); - h.set_max_connections(max_connections_per_torrent); h.set_max_uploads(-1); h.set_ratio(preferred_ratio); @@ -1139,8 +1168,6 @@ int main(int argc, char* argv[]) continue; } - handles.insert(std::pair(std::string(), h)); - h.set_max_connections(max_connections_per_torrent); h.set_max_uploads(-1); h.set_ratio(preferred_ratio); @@ -1151,7 +1178,7 @@ int main(int argc, char* argv[]) } // if it's a torrent file, open it as usual - add_torrent(ses, handles, i->c_str(), preferred_ratio + add_torrent(ses, files, i->c_str(), preferred_ratio , allocation_mode, save_path, false , torrent_upload_limit, torrent_download_limit); } @@ -1162,6 +1189,13 @@ int main(int argc, char* argv[]) while (loop_limit > 1 || loop_limit == 0) { + + handles.clear(); + ses.get_torrent_status(&handles, boost::bind(&show_torrent, _1, torrent_filter)); + if (active_torrent >= handles.size()) active_torrent = handles.size() - 1; + + std::sort(handles.begin(), handles.end(), &compare_torrent); + if (loop_limit > 1) --loop_limit; char c = 0; while (sleep_and_input(&c, refresh_delay)) @@ -1183,30 +1217,38 @@ int main(int argc, char* argv[]) if (c == 68) { // arrow left - if (torrent_filter > 0) --torrent_filter; + if (torrent_filter > 0) + { + --torrent_filter; + handles.clear(); + ses.get_torrent_status(&handles, boost::bind(&show_torrent, _1, torrent_filter)); + if (active_torrent >= handles.size()) active_torrent = handles.size() - 1; + std::sort(handles.begin(), handles.end(), &compare_torrent); + } } else if (c == 67) { // arrow right - if (torrent_filter < torrents_paused) ++torrent_filter; + if (torrent_filter < torrents_max - 1) + { + ++torrent_filter; + handles.clear(); + ses.get_torrent_status(&handles, boost::bind(&show_torrent, _1, torrent_filter)); + if (active_torrent >= handles.size()) active_torrent = handles.size() - 1; + std::sort(handles.begin(), handles.end(), &compare_torrent); + } } else if (c == 65) { // arrow up - int prev = active_torrent; --active_torrent; - while (active_torrent > 0 && !show_torrent(get_active_torrent(handles))) - --active_torrent; - if (active_torrent < 0) active_torrent = prev; + if (active_torrent < 0) active_torrent = 0; } else if (c == 66) { // arrow down - int prev = active_torrent; ++active_torrent; - while (active_torrent < handles.size() && !show_torrent(get_active_torrent(handles))) - ++active_torrent; - if (active_torrent >= handles.size()) active_torrent = prev; + if (active_torrent >= handles.size()) active_torrent = handles.size() - 1; } } @@ -1221,11 +1263,12 @@ int main(int argc, char* argv[]) printf("saving peers for torrents\n"); std::vector peers; - for (handles_t::iterator i = handles.begin(); - i != handles.end(); ++i) + std::vector torrents = ses.get_torrents(); + for (std::vector::iterator i = torrents.begin(); + i != torrents.end(); ++i) { - i->second.handle.get_full_peer_list(peers); - FILE* f = fopen(("peers_" + i->second.handle.name()).c_str(), "w+"); + i->get_full_peer_list(peers); + FILE* f = fopen(("peers_" + i->name()).c_str(), "w+"); if (!f) break; for (std::vector::iterator k = peers.begin() , end(peers.end()); k != end; ++k) @@ -1255,59 +1298,59 @@ int main(int argc, char* argv[]) if (c == 's' && !handles.empty()) { - torrent_entry& te = get_active_torrent(handles); - te.handle.set_sequential_download(!te.status.sequential_download); + torrent_status const& ts = get_active_torrent(handles); + ts.handle.set_sequential_download(!ts.sequential_download); } if (c == 'R') { // save resume data for all torrents - for (handles_t::iterator i = handles.begin() + for (std::vector::iterator i = handles.begin() , end(handles.end()); i != end; ++i) { - if (i->second.status.need_save_resume) - i->second.handle.save_resume_data(); + if (i->need_save_resume) + i->handle.save_resume_data(); } } if (c == 'o' && !handles.empty()) { - torrent_entry& te = get_active_torrent(handles); - int num_pieces = te.handle.get_torrent_info().num_pieces(); + torrent_status const& ts = get_active_torrent(handles); + int num_pieces = ts.num_pieces; if (num_pieces > 300) num_pieces = 300; for (int i = 0; i < num_pieces; ++i) { - te.handle.set_piece_deadline(i, (i+5) * 1000, torrent_handle::alert_when_available); + ts.handle.set_piece_deadline(i, (i+5) * 1000, torrent_handle::alert_when_available); } } if (c == 'v' && !handles.empty()) { - torrent_entry& te = get_active_torrent(handles); - te.handle.scrape_tracker(); + torrent_status const& ts = get_active_torrent(handles); + ts.handle.scrape_tracker(); } if (c == 'p' && !handles.empty()) { - torrent_entry& te = get_active_torrent(handles); - if (!te.status.auto_managed && te.status.paused) + torrent_status const& ts = get_active_torrent(handles); + if (!ts.auto_managed && ts.paused) { - te.handle.auto_managed(true); + ts.handle.auto_managed(true); } else { - te.handle.auto_managed(false); - te.handle.pause(torrent_handle::graceful_pause); + ts.handle.auto_managed(false); + ts.handle.pause(torrent_handle::graceful_pause); } // the alert handler for save_resume_data_alert // will save it to disk - if (te.status.need_save_resume) te.handle.save_resume_data(); + if (ts.need_save_resume) ts.handle.save_resume_data(); } if (c == 'c' && !handles.empty()) { - torrent_entry& te = get_active_torrent(handles); - te.handle.clear_error(); + torrent_status const& ts = get_active_torrent(handles); + ts.handle.clear_error(); } // toggle displays @@ -1332,15 +1375,19 @@ int main(int argc, char* argv[]) if (c == 'q') break; int terminal_width = 80; + int terminal_height = 50; #ifndef _WIN32 { winsize size; ioctl(STDOUT_FILENO, TIOCGWINSZ, (char*)&size); terminal_width = size.ws_col; + terminal_height = size.ws_row; if (terminal_width < 64) terminal_width = 64; + if (terminal_height < 25) + terminal_height = 25; } #endif @@ -1353,7 +1400,7 @@ int main(int argc, char* argv[]) std::string event_string; ::print_alert(a.get(), event_string); - ::handle_alert(ses, a.get(), handles); + ::handle_alert(ses, a.get(), files); events.push_back(event_string); if (events.size() >= 20) events.pop_front(); @@ -1370,7 +1417,7 @@ int main(int argc, char* argv[]) "[1] toggle IP [2] toggle AS [3] toggle timers [4] toggle block progress " "[5] toggle peer rate [6] toggle failures [7] toggle send buffers [R] save resume data\n"; - char const* filter_names[] = { "all", "downloading", "non-paused", "seeding", "paused"}; + char const* filter_names[] = { "all", "downloading", "non-paused", "seeding", "queued", "stopped", "checking"}; for (int i = 0; i < sizeof(filter_names)/sizeof(filter_names[0]); ++i) { out += '['; @@ -1383,12 +1430,18 @@ int main(int argc, char* argv[]) char str[500]; int torrent_index = 0; - torrent_handle active_handle; - for (handles_t::iterator i = handles.begin(); + int lines_printed = 3; + for (std::vector::iterator i = handles.begin(); i != handles.end(); ++torrent_index) { - torrent_entry& te = i->second; - if (!te.handle.is_valid()) + if (lines_printed >= terminal_height - 15) + { + out += "...\n"; + break; + } + + torrent_status& s = *i; + if (!s.handle.is_valid()) { handles.erase(i++); continue; @@ -1398,12 +1451,6 @@ int main(int argc, char* argv[]) ++i; } - te.status = te.handle.status(); - torrent_status const& s = te.status; - - if (!show_torrent(te)) - continue; - #ifdef ANSI_TERMINAL_COLORS char const* term = "\x1b[0m"; #else @@ -1420,7 +1467,7 @@ int main(int argc, char* argv[]) out += " "; } - int queue_pos = te.status.queue_position; + int queue_pos = s.queue_position; if (queue_pos == -1) out += "- "; else { @@ -1431,7 +1478,7 @@ int main(int argc, char* argv[]) if (s.paused) out += esc("34"); else out += esc("37"); - std::string name = te.handle.name(); + std::string name = s.handle.name(); if (name.size() > 40) name.resize(40); snprintf(str, sizeof(str), "%-40s %s ", name.c_str(), term); out += str; @@ -1443,6 +1490,7 @@ int main(int argc, char* argv[]) out += s.error; out += esc("0"); out += "\n"; + ++lines_printed; continue; } @@ -1467,8 +1515,9 @@ int main(int argc, char* argv[]) , s.up_bandwidth_queue, s.down_bandwidth_queue , esc("32"), add_suffix(s.all_time_download).c_str(), term , esc("31"), add_suffix(s.all_time_upload).c_str(), term - , s.seed_rank, te.status.need_save_resume?'S':' ', esc("0")); + , s.seed_rank, s.need_save_resume?'S':' ', esc("0")); out += str; + ++lines_printed; if (torrent_index != active_torrent && s.state == torrent_status::seeding) continue; char const* progress_bar_color = "33"; // yellow @@ -1499,12 +1548,14 @@ int main(int argc, char* argv[]) , progress_bar(s.progress_ppm / 1000, terminal_width - 43 - 20, "35").c_str()); out += str; } + ++lines_printed; if (print_piece_bar && s.state != torrent_status::seeding) { out += " "; out += piece_bar(s.pieces, terminal_width - 7); out += "\n"; + ++lines_printed; } if (s.state != torrent_status::queued_for_checking && s.state != torrent_status::checking_files) @@ -1523,10 +1574,8 @@ int main(int argc, char* argv[]) , esc("37"), t.hours(), t.minutes(), t.seconds(), esc("0") , esc("36"), s.current_tracker.c_str(), esc("0")); out += str; + ++lines_printed; } - - if (torrent_index != active_torrent) continue; - active_handle = te.handle; } cache_status cs = ses.get_cache_status(); @@ -1624,10 +1673,12 @@ int main(int argc, char* argv[]) out += str; } - if (active_handle.is_valid()) + torrent_status const* st = 0; + if (!handles.empty()) st = &get_active_torrent(handles); + if (st && st->handle.is_valid()) { - torrent_handle h = active_handle; - torrent_status s = h.status(); + torrent_handle h = st->handle; + torrent_status const& s = *st; if ((print_downloads && s.state != torrent_status::seeding) || print_peers) @@ -1804,7 +1855,7 @@ int main(int argc, char* argv[]) if (!monitor_dir.empty() && next_dir_scan < time_now()) { - scan_dir(monitor_dir, ses, handles, preferred_ratio + scan_dir(monitor_dir, ses, files, preferred_ratio , allocation_mode, save_path, torrent_upload_limit , torrent_download_limit); next_dir_scan = time_now() + seconds(poll_interval); @@ -1819,17 +1870,18 @@ int main(int argc, char* argv[]) ses.pause(); printf("saving resume data\n"); - for (handles_t::iterator i = handles.begin(); - i != handles.end(); ++i) + std::vector temp; + ses.get_torrent_status(&temp, &yes, 0); + for (std::vector::iterator i = temp.begin(); + i != temp.end(); ++i) { - torrent_entry& te = i->second; - if (!te.handle.is_valid()) continue; - te.status = te.handle.status(); - if (te.status.paused) continue; - if (!te.status.has_metadata) continue; + torrent_status& st = *i; + if (!st.handle.is_valid()) continue; + if (st.paused) continue; + if (!st.has_metadata) continue; // save_resume_data will generate an alert when it's done - te.handle.save_resume_data(); + st.handle.save_resume_data(); ++num_resume_data; printf("\r%d ", num_resume_data); } diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 3f5dde957..c3197cb3a 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -274,7 +274,13 @@ namespace libtorrent void remove_torrent(torrent_handle const& h, int options); - std::vector get_torrents(); + void get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags) const; + void refresh_torrent_status(std::vector* ret + , boost::uint32_t flags) const; + + std::vector get_torrents() const; void queue_check_torrent(boost::shared_ptr const& t); void dequeue_check_torrent(boost::shared_ptr const& t); diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 752432c15..759cd9036 100644 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -176,6 +176,12 @@ namespace libtorrent void save_state(entry& e, boost::uint32_t flags = 0xffffffff) const; void load_state(lazy_entry const& e); + void get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags = 0) const; + void refresh_torrent_status(std::vector* ret + , boost::uint32_t flags = 0) const; + // returns a list of all torrents in this session std::vector get_torrents() const; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 269ef5eb1..9b06102a2 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -300,7 +300,7 @@ namespace libtorrent void set_piece_deadline(int piece, int t, int flags); void update_piece_priorities(); - torrent_status status(boost::uint32_t flags) const; + void status(torrent_status* st, boost::uint32_t flags); void file_progress(std::vector& fp, int flags = 0) const; diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 63fbc7af3..7bf483e30 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -69,6 +69,7 @@ namespace libtorrent struct torrent_plugin; struct peer_info; struct peer_list_entry; + struct torrent_status; #ifndef BOOST_NO_EXCEPTIONS // for compatibility with 0.14 @@ -77,273 +78,6 @@ namespace libtorrent void throw_invalid_handle(); #endif - struct TORRENT_EXPORT torrent_status - { - torrent_status() - : state(checking_resume_data) - , paused(false) - , auto_managed(false) - , sequential_download(false) - , is_seeding(false) - , is_finished(false) - , has_metadata(false) - , progress(0.f) - , progress_ppm(0) - , total_download(0) - , total_upload(0) - , total_payload_download(0) - , total_payload_upload(0) - , total_failed_bytes(0) - , total_redundant_bytes(0) - , download_rate(0) - , upload_rate(0) - , download_payload_rate(0) - , upload_payload_rate(0) - , num_seeds(0) - , num_peers(0) - , num_complete(-1) - , num_incomplete(-1) - , list_seeds(0) - , list_peers(0) - , num_pieces(0) - , total_done(0) - , total_wanted_done(0) - , total_wanted(0) - , distributed_copies(0.f) - , block_size(0) - , num_uploads(0) - , num_connections(0) - , uploads_limit(0) - , connections_limit(0) - , storage_mode(storage_mode_sparse) - , up_bandwidth_queue(0) - , down_bandwidth_queue(0) - , all_time_upload(0) - , all_time_download(0) - , active_time(0) - , finished_time(0) - , seeding_time(0) - , seed_rank(0) - , last_scrape(0) - , has_incoming(false) - , sparse_regions(0) - , seed_mode(false) - , upload_mode(false) - , share_mode(false) - , priority(0) - , added_time(0) - , completed_time(0) - , last_seen_complete(0) - , time_since_upload(0) - , time_since_download(0) - , queue_position(0) - {} - - enum state_t - { - queued_for_checking, - checking_files, - downloading_metadata, - downloading, - finished, - seeding, - allocating, - checking_resume_data - }; - - state_t state; - bool paused; - bool auto_managed; - bool sequential_download; - bool is_seeding; - bool is_finished; - bool has_metadata; - - float progress; - // progress parts per million (progress * 1000000) - // when disabling floating point operations, this is - // the only option to query progress - int progress_ppm; - std::string error; - - boost::posix_time::time_duration next_announce; - boost::posix_time::time_duration announce_interval; - - std::string current_tracker; - - // transferred this session! - // total, payload plus protocol - size_type total_download; - size_type total_upload; - - // payload only - size_type total_payload_download; - size_type total_payload_upload; - - // the amount of payload bytes that - // has failed their hash test - size_type total_failed_bytes; - - // the number of payload bytes that - // has been received redundantly. - size_type total_redundant_bytes; - - // current transfer rate - // payload plus protocol - int download_rate; - int upload_rate; - - // the rate of payload that is - // sent and received - int download_payload_rate; - int upload_payload_rate; - - // the number of peers this torrent is connected to - // that are seeding. - int num_seeds; - - // the number of peers this torrent - // is connected to (including seeds). - int num_peers; - - // if the tracker sends scrape info in its - // announce reply, these fields will be - // set to the total number of peers that - // have the whole file and the total number - // of peers that are still downloading - int num_complete; - int num_incomplete; - - // this is the number of seeds whose IP we know - // but are not necessarily connected to - int list_seeds; - - // this is the number of peers whose IP we know - // (including seeds), but are not necessarily - // connected to - int list_peers; - - // the number of peers in our peerlist that - // we potentially could connect to - int connect_candidates; - - bitfield pieces; - - // this is the number of pieces the client has - // downloaded. it is equal to: - // std::accumulate(pieces->begin(), pieces->end()); - int num_pieces; - - // the number of bytes of the file we have - // including pieces that may have been filtered - // after we downloaded them - size_type total_done; - - // the number of bytes we have of those that we - // want. i.e. not counting bytes from pieces that - // are filtered as not wanted. - size_type total_wanted_done; - - // the total number of bytes we want to download - // this may be smaller than the total torrent size - // in case any pieces are filtered as not wanted - size_type total_wanted; - - // the number of distributed copies of the file. - // note that one copy may be spread out among many peers. - // - // the integer part tells how many copies - // there are of the rarest piece(s) - // - // the fractional part tells the fraction of pieces that - // have more copies than the rarest piece(s). - - // the number of full distributed copies (i.e. the number - // of peers that have the rarest piece) - int distributed_full_copies; - - // the fraction of pieces that more peers has than the - // rarest pieces. This indicates how close the swarm is - // to have one more full distributed copy - int distributed_fraction; - - float distributed_copies; - - // the block size that is used in this torrent. i.e. - // the number of bytes each piece request asks for - // and each bit in the download queue bitfield represents - int block_size; - - int num_uploads; - int num_connections; - int uploads_limit; - int connections_limit; - - // true if the torrent is saved in compact mode - // false if it is saved in full allocation mode - storage_mode_t storage_mode; - - int up_bandwidth_queue; - int down_bandwidth_queue; - - // number of bytes downloaded since torrent was started - // saved and restored from resume data - size_type all_time_upload; - size_type all_time_download; - - // the number of seconds of being active - // and as being a seed, saved and restored - // from resume data - int active_time; - int finished_time; - int seeding_time; - - // higher value means more important to seed - int seed_rank; - - // number of seconds since last scrape, or -1 if - // there hasn't been a scrape - int last_scrape; - - // true if there are incoming connections to this - // torrent - bool has_incoming; - - // the number of "holes" in the torrent - int sparse_regions; - - // is true if this torrent is (still) in seed_mode - bool seed_mode; - - // this is set to true when the torrent is blocked - // from downloading, typically caused by a file - // write operation failing - bool upload_mode; - - // this is true if the torrent is in share-mode - bool share_mode; - - // the priority of this torrent - int priority; - - // the time this torrent was added and completed - time_t added_time; - time_t completed_time; - time_t last_seen_complete; - - // number of seconds since last upload or download activity - int time_since_upload; - int time_since_download; - - // the position in the download queue where this torrent is - // this is -1 for seeds and finished torrents - int queue_position; - - // true if this torrent has had changes since the last - // time resume data was saved - bool need_save_resume; - }; - struct TORRENT_EXPORT block_info { enum block_state_t @@ -677,6 +411,275 @@ namespace libtorrent }; + struct TORRENT_EXPORT torrent_status + { + torrent_status() + : state(checking_resume_data) + , paused(false) + , auto_managed(false) + , sequential_download(false) + , is_seeding(false) + , is_finished(false) + , has_metadata(false) + , progress(0.f) + , progress_ppm(0) + , total_download(0) + , total_upload(0) + , total_payload_download(0) + , total_payload_upload(0) + , total_failed_bytes(0) + , total_redundant_bytes(0) + , download_rate(0) + , upload_rate(0) + , download_payload_rate(0) + , upload_payload_rate(0) + , num_seeds(0) + , num_peers(0) + , num_complete(-1) + , num_incomplete(-1) + , list_seeds(0) + , list_peers(0) + , num_pieces(0) + , total_done(0) + , total_wanted_done(0) + , total_wanted(0) + , distributed_copies(0.f) + , block_size(0) + , num_uploads(0) + , num_connections(0) + , uploads_limit(0) + , connections_limit(0) + , storage_mode(storage_mode_sparse) + , up_bandwidth_queue(0) + , down_bandwidth_queue(0) + , all_time_upload(0) + , all_time_download(0) + , active_time(0) + , finished_time(0) + , seeding_time(0) + , seed_rank(0) + , last_scrape(0) + , has_incoming(false) + , sparse_regions(0) + , seed_mode(false) + , upload_mode(false) + , share_mode(false) + , priority(0) + , added_time(0) + , completed_time(0) + , last_seen_complete(0) + , time_since_upload(0) + , time_since_download(0) + , queue_position(0) + {} + + // handle to the torrent + torrent_handle handle; + + enum state_t + { + queued_for_checking, + checking_files, + downloading_metadata, + downloading, + finished, + seeding, + allocating, + checking_resume_data + }; + + state_t state; + bool paused; + bool auto_managed; + bool sequential_download; + bool is_seeding; + bool is_finished; + bool has_metadata; + + float progress; + // progress parts per million (progress * 1000000) + // when disabling floating point operations, this is + // the only option to query progress + int progress_ppm; + std::string error; + + boost::posix_time::time_duration next_announce; + boost::posix_time::time_duration announce_interval; + + std::string current_tracker; + + // transferred this session! + // total, payload plus protocol + size_type total_download; + size_type total_upload; + + // payload only + size_type total_payload_download; + size_type total_payload_upload; + + // the amount of payload bytes that + // has failed their hash test + size_type total_failed_bytes; + + // the number of payload bytes that + // has been received redundantly. + size_type total_redundant_bytes; + + // current transfer rate + // payload plus protocol + int download_rate; + int upload_rate; + + // the rate of payload that is + // sent and received + int download_payload_rate; + int upload_payload_rate; + + // the number of peers this torrent is connected to + // that are seeding. + int num_seeds; + + // the number of peers this torrent + // is connected to (including seeds). + int num_peers; + + // if the tracker sends scrape info in its + // announce reply, these fields will be + // set to the total number of peers that + // have the whole file and the total number + // of peers that are still downloading + int num_complete; + int num_incomplete; + + // this is the number of seeds whose IP we know + // but are not necessarily connected to + int list_seeds; + + // this is the number of peers whose IP we know + // (including seeds), but are not necessarily + // connected to + int list_peers; + + // the number of peers in our peerlist that + // we potentially could connect to + int connect_candidates; + + bitfield pieces; + + // this is the number of pieces the client has + // downloaded. it is equal to: + // std::accumulate(pieces->begin(), pieces->end()); + int num_pieces; + + // the number of bytes of the file we have + // including pieces that may have been filtered + // after we downloaded them + size_type total_done; + + // the number of bytes we have of those that we + // want. i.e. not counting bytes from pieces that + // are filtered as not wanted. + size_type total_wanted_done; + + // the total number of bytes we want to download + // this may be smaller than the total torrent size + // in case any pieces are filtered as not wanted + size_type total_wanted; + + // the number of distributed copies of the file. + // note that one copy may be spread out among many peers. + // + // the integer part tells how many copies + // there are of the rarest piece(s) + // + // the fractional part tells the fraction of pieces that + // have more copies than the rarest piece(s). + + // the number of full distributed copies (i.e. the number + // of peers that have the rarest piece) + int distributed_full_copies; + + // the fraction of pieces that more peers has than the + // rarest pieces. This indicates how close the swarm is + // to have one more full distributed copy + int distributed_fraction; + + float distributed_copies; + + // the block size that is used in this torrent. i.e. + // the number of bytes each piece request asks for + // and each bit in the download queue bitfield represents + int block_size; + + int num_uploads; + int num_connections; + int uploads_limit; + int connections_limit; + + // true if the torrent is saved in compact mode + // false if it is saved in full allocation mode + storage_mode_t storage_mode; + + int up_bandwidth_queue; + int down_bandwidth_queue; + + // number of bytes downloaded since torrent was started + // saved and restored from resume data + size_type all_time_upload; + size_type all_time_download; + + // the number of seconds of being active + // and as being a seed, saved and restored + // from resume data + int active_time; + int finished_time; + int seeding_time; + + // higher value means more important to seed + int seed_rank; + + // number of seconds since last scrape, or -1 if + // there hasn't been a scrape + int last_scrape; + + // true if there are incoming connections to this + // torrent + bool has_incoming; + + // the number of "holes" in the torrent + int sparse_regions; + + // is true if this torrent is (still) in seed_mode + bool seed_mode; + + // this is set to true when the torrent is blocked + // from downloading, typically caused by a file + // write operation failing + bool upload_mode; + + // this is true if the torrent is in share-mode + bool share_mode; + + // the priority of this torrent + int priority; + + // the time this torrent was added and completed + time_t added_time; + time_t completed_time; + time_t last_seen_complete; + + // number of seconds since last upload or download activity + int time_since_upload; + int time_since_download; + + // the position in the download queue where this torrent is + // this is -1 for seeds and finished torrents + int queue_position; + + // true if this torrent has had changes since the last + // time resume data was saved + bool need_save_resume; + }; } diff --git a/src/session.cpp b/src/session.cpp index 6820347ad..9af0160db 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -300,6 +300,12 @@ namespace libtorrent m_impl->m_io_service.post(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)))); \ do { m_impl->cond.wait(l); } while(!done) +#define TORRENT_SYNC_CALL3(x, a1, a2, a3) \ + bool done = false; \ + mutex::scoped_lock l(m_impl->mut); \ + m_impl->m_io_service.post(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3)))); \ + do { m_impl->cond.wait(l); } while(!done) + #define TORRENT_SYNC_CALL_RET(type, x) \ bool done = false; \ type r; \ @@ -509,6 +515,19 @@ namespace libtorrent TORRENT_ASYNC_CALL1(set_key, key); } + void session::get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags) const + { + TORRENT_SYNC_CALL3(get_torrent_status, ret, boost::ref(pred), flags); + } + + void session::refresh_torrent_status(std::vector* ret + , boost::uint32_t flags) const + { + TORRENT_SYNC_CALL2(refresh_torrent_status, ret, flags); + } + std::vector session::get_torrents() const { TORRENT_SYNC_CALL_RET(std::vector, get_torrents); diff --git a/src/session_impl.cpp b/src/session_impl.cpp index b0f4619cc..c1ed1ad36 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -3592,11 +3592,39 @@ namespace aux { } #endif - std::vector session_impl::get_torrents() + void session_impl::get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags) const + { + for (session_impl::torrent_map::const_iterator i + = m_torrents.begin(), end(m_torrents.end()); + i != end; ++i) + { + if (i->second->is_aborted()) continue; + torrent_status st; + i->second->status(&st, flags); + if (!pred(st)) continue; + ret->push_back(st); + } + } + + void session_impl::refresh_torrent_status(std::vector* ret + , boost::uint32_t flags) const + { + for (std::vector::iterator i + = ret->begin(), end(ret->end()); i != end; ++i) + { + boost::shared_ptr t = i->handle.m_torrent.lock(); + if (!t) continue; + t->status(&*i, flags); + } + } + + std::vector session_impl::get_torrents() const { std::vector ret; - for (session_impl::torrent_map::iterator i + for (session_impl::torrent_map::const_iterator i = m_torrents.begin(), end(m_torrents.end()); i != end; ++i) { diff --git a/src/torrent.cpp b/src/torrent.cpp index 663624324..cb21a194e 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -7139,96 +7139,96 @@ namespace libtorrent #endif } - torrent_status torrent::status(boost::uint32_t flags) const + void torrent::status(torrent_status* st, boost::uint32_t flags) { INVARIANT_CHECK; ptime now = time_now(); - torrent_status st; + st->handle = get_handle(); - st.has_incoming = m_has_incoming; - if (m_error) st.error = m_error.message() + ": " + m_error_file; - st.seed_mode = m_seed_mode; + st->has_incoming = m_has_incoming; + if (m_error) st->error = m_error.message() + ": " + m_error_file; + st->seed_mode = m_seed_mode; - st.added_time = m_added_time; - st.completed_time = m_completed_time; + st->added_time = m_added_time; + st->completed_time = m_completed_time; - st.last_scrape = m_last_scrape; - st.share_mode = m_share_mode; - st.upload_mode = m_upload_mode; - st.up_bandwidth_queue = 0; - st.down_bandwidth_queue = 0; - st.priority = m_priority; + st->last_scrape = m_last_scrape; + st->share_mode = m_share_mode; + st->upload_mode = m_upload_mode; + st->up_bandwidth_queue = 0; + st->down_bandwidth_queue = 0; + st->priority = m_priority; - st.num_peers = (int)std::count_if(m_connections.begin(), m_connections.end() + st->num_peers = (int)std::count_if(m_connections.begin(), m_connections.end() , !boost::bind(&peer_connection::is_connecting, _1)); - st.list_peers = m_policy.num_peers(); - st.list_seeds = m_policy.num_seeds(); - st.connect_candidates = m_policy.num_connect_candidates(); - st.seed_rank = seed_rank(settings()); + st->list_peers = m_policy.num_peers(); + st->list_seeds = m_policy.num_seeds(); + st->connect_candidates = m_policy.num_connect_candidates(); + st->seed_rank = seed_rank(settings()); - st.all_time_upload = m_total_uploaded; - st.all_time_download = m_total_downloaded; + st->all_time_upload = m_total_uploaded; + st->all_time_download = m_total_downloaded; // activity time - st.active_time = m_active_time; - st.active_time = m_active_time; - st.seeding_time = m_seeding_time; - st.time_since_upload = m_last_upload; - st.time_since_download = m_last_download; + st->active_time = m_active_time; + st->active_time = m_active_time; + st->seeding_time = m_seeding_time; + st->time_since_upload = m_last_upload; + st->time_since_download = m_last_download; - st.storage_mode = (storage_mode_t)m_storage_mode; + st->storage_mode = (storage_mode_t)m_storage_mode; - st.num_complete = (m_complete == 0xffffff) ? -1 : m_complete; - st.num_incomplete = (m_incomplete == 0xffffff) ? -1 : m_incomplete; - st.paused = is_torrent_paused(); - st.auto_managed = m_auto_managed; - st.sequential_download = m_sequential_download; - st.is_seeding = is_seed(); - st.is_finished = is_finished(); - st.has_metadata = valid_metadata(); - bytes_done(st, flags & torrent_handle::query_accurate_download_counters); - TORRENT_ASSERT(st.total_wanted_done >= 0); - TORRENT_ASSERT(st.total_done >= st.total_wanted_done); + st->num_complete = (m_complete == 0xffffff) ? -1 : m_complete; + st->num_incomplete = (m_incomplete == 0xffffff) ? -1 : m_incomplete; + st->paused = is_torrent_paused(); + st->auto_managed = m_auto_managed; + st->sequential_download = m_sequential_download; + st->is_seeding = is_seed(); + st->is_finished = is_finished(); + st->has_metadata = valid_metadata(); + bytes_done(*st, flags & torrent_handle::query_accurate_download_counters); + TORRENT_ASSERT(st->total_wanted_done >= 0); + TORRENT_ASSERT(st->total_done >= st->total_wanted_done); // payload transfer - st.total_payload_download = m_stat.total_payload_download(); - st.total_payload_upload = m_stat.total_payload_upload(); + st->total_payload_download = m_stat.total_payload_download(); + st->total_payload_upload = m_stat.total_payload_upload(); // total transfer - st.total_download = m_stat.total_payload_download() + st->total_download = m_stat.total_payload_download() + m_stat.total_protocol_download(); - st.total_upload = m_stat.total_payload_upload() + st->total_upload = m_stat.total_payload_upload() + m_stat.total_protocol_upload(); // failed bytes - st.total_failed_bytes = m_total_failed_bytes; - st.total_redundant_bytes = m_total_redundant_bytes; + st->total_failed_bytes = m_total_failed_bytes; + st->total_redundant_bytes = m_total_redundant_bytes; // transfer rate - st.download_rate = m_stat.download_rate(); - st.upload_rate = m_stat.upload_rate(); - st.download_payload_rate = m_stat.download_payload_rate(); - st.upload_payload_rate = m_stat.upload_payload_rate(); + st->download_rate = m_stat.download_rate(); + st->upload_rate = m_stat.upload_rate(); + st->download_payload_rate = m_stat.download_payload_rate(); + st->upload_payload_rate = m_stat.upload_payload_rate(); if (m_waiting_tracker && !is_paused()) - st.next_announce = boost::posix_time::seconds( + st->next_announce = boost::posix_time::seconds( total_seconds(next_announce() - now)); else - st.next_announce = boost::posix_time::seconds(0); + st->next_announce = boost::posix_time::seconds(0); - if (st.next_announce.is_negative()) - st.next_announce = boost::posix_time::seconds(0); + if (st->next_announce.is_negative()) + st->next_announce = boost::posix_time::seconds(0); - st.announce_interval = boost::posix_time::seconds(0); + st->announce_interval = boost::posix_time::seconds(0); - st.current_tracker.clear(); + st->current_tracker.clear(); if (m_last_working_tracker >= 0) { TORRENT_ASSERT(m_last_working_tracker < int(m_trackers.size())); - st.current_tracker = m_trackers[m_last_working_tracker].url; + st->current_tracker = m_trackers[m_last_working_tracker].url; } else { @@ -7236,82 +7236,82 @@ namespace libtorrent for (i = m_trackers.begin(); i != m_trackers.end(); ++i) { if (!i->updating) continue; - st.current_tracker = i->url; + st->current_tracker = i->url; break; } } - st.num_uploads = m_num_uploads; - st.uploads_limit = m_max_uploads; - st.num_connections = int(m_connections.size()); - st.connections_limit = m_max_connections; + st->num_uploads = m_num_uploads; + st->uploads_limit = m_max_uploads; + st->num_connections = int(m_connections.size()); + st->connections_limit = m_max_connections; // if we don't have any metadata, stop here - st.queue_position = queue_position(); - st.need_save_resume = need_save_resume_data(); + st->queue_position = queue_position(); + st->need_save_resume = need_save_resume_data(); - st.state = (torrent_status::state_t)m_state; + st->state = (torrent_status::state_t)m_state; if (!valid_metadata()) { - st.state = torrent_status::downloading_metadata; - st.progress_ppm = m_progress_ppm; + st->state = torrent_status::downloading_metadata; + st->progress_ppm = m_progress_ppm; #if !TORRENT_NO_FPU - st.progress = m_progress_ppm / 1000000.f; + st->progress = m_progress_ppm / 1000000.f; #endif - st.block_size = 0; - return st; + st->block_size = 0; + return; } - st.block_size = block_size(); + st->block_size = block_size(); if (m_state == torrent_status::checking_files) { - st.progress_ppm = m_progress_ppm; + st->progress_ppm = m_progress_ppm; #if !TORRENT_NO_FPU - st.progress = m_progress_ppm / 1000000.f; + st->progress = m_progress_ppm / 1000000.f; #endif } - else if (st.total_wanted == 0) + else if (st->total_wanted == 0) { - st.progress_ppm = 1000000; - st.progress = 1.f; + st->progress_ppm = 1000000; + st->progress = 1.f; } else { - st.progress_ppm = st.total_wanted_done * 1000000 - / st.total_wanted; + st->progress_ppm = st->total_wanted_done * 1000000 + / st->total_wanted; #if !TORRENT_NO_FPU - st.progress = st.progress_ppm / 1000000.f; + st->progress = st->progress_ppm / 1000000.f; #endif } if (has_picker()) { - st.sparse_regions = m_picker->sparse_regions(); + st->sparse_regions = m_picker->sparse_regions(); int num_pieces = m_picker->num_pieces(); - st.pieces.resize(num_pieces, false); + st->pieces.resize(num_pieces, false); for (int i = 0; i < num_pieces; ++i) - if (m_picker->have_piece(i)) st.pieces.set_bit(i); + if (m_picker->have_piece(i)) st->pieces.set_bit(i); } - st.num_pieces = num_have(); - st.num_seeds = num_seeds(); + st->num_pieces = num_have(); + st->num_seeds = num_seeds(); if ((flags & torrent_handle::query_distributed_copies) && m_picker.get()) { - boost::tie(st.distributed_full_copies, st.distributed_fraction) = + boost::tie(st->distributed_full_copies, st->distributed_fraction) = m_picker->distributed_copies(); #if TORRENT_NO_FPU - st.distributed_copies = -1.f; + st->distributed_copies = -1.f; #else - st.distributed_copies = st.distributed_full_copies - + float(st.distributed_fraction) / 1000; + st->distributed_copies = st->distributed_full_copies + + float(st->distributed_fraction) / 1000; #endif } else { - st.distributed_full_copies = -1; - st.distributed_fraction = -1; - st.distributed_copies = -1.f; + st->distributed_full_copies = -1; + st->distributed_fraction = -1; + st->distributed_copies = -1.f; } if (flags & torrent_handle::query_last_seen_complete) @@ -7322,13 +7322,12 @@ namespace libtorrent { last = (std::max)(last, (*i)->last_seen_complete()); } - st.last_seen_complete = last; + st->last_seen_complete = last; } else { - st.last_seen_complete = 0; + st->last_seen_complete = 0; } - return st; } void torrent::add_redundant_bytes(int b) diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index a4c81361c..c7d7e53a2 100644 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -469,8 +469,9 @@ namespace libtorrent torrent_status torrent_handle::status(boost::uint32_t flags) const { INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET1(torrent_status, torrent_status(), status, flags); - return r; + torrent_status st; + TORRENT_SYNC_CALL2(status, &st, flags); + return st; } void torrent_handle::set_sequential_download(bool sd) const