From 034291f78008de2930ca87d01971f388f0ed9c7e Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 17 Oct 2002 06:17:41 +0000 Subject: [PATCH] download throttle --- panda/src/downloader/httpChannel.I | 90 ++++++++++++++++++++++++++++ panda/src/downloader/httpChannel.cxx | 34 +++++++++++ panda/src/downloader/httpChannel.h | 15 +++++ 3 files changed, 139 insertions(+) diff --git a/panda/src/downloader/httpChannel.I b/panda/src/downloader/httpChannel.I index 62c648b0b6..435925621b 100644 --- a/panda/src/downloader/httpChannel.I +++ b/panda/src/downloader/httpChannel.I @@ -166,6 +166,96 @@ get_persistent_connection() const { return _persistent_connection; } +//////////////////////////////////////////////////////////////////// +// Function: HTTPChannel::set_download_throttle +// Access: Published +// Description: Specifies whether nonblocking downloads (via +// download_to_file() or download_to_ram()) will be +// limited so as not to use all available bandwidth. +// +// If this is true, when a download has been started on +// this channel it will be invoked no more frequently +// than get_max_updates_per_second(), and the total +// bandwidth used by the download will be no more than +// get_max_bytes_per_second(). If this is false, +// downloads will proceed as fast as the server can send +// the data. +// +// This only has effect on the nonblocking I/O methods +// like request_document(), etc. The blocking methods +// like get_document() always use as much CPU and +// bandwidth as they can get. +//////////////////////////////////////////////////////////////////// +INLINE void HTTPChannel:: +set_download_throttle(bool download_throttle) { + _download_throttle = download_throttle; +} + +//////////////////////////////////////////////////////////////////// +// Function: HTTPChannel::get_download_throttle +// Access: Published +// Description: Returns whether the nonblocking downloads will be +// bandwidth-limited. See set_download_throttle(). +//////////////////////////////////////////////////////////////////// +INLINE bool HTTPChannel:: +get_download_throttle() const { + return _download_throttle; +} + +//////////////////////////////////////////////////////////////////// +// Function: HTTPChannel::set_max_bytes_per_second +// Access: Published +// Description: When bandwidth throttling is in effect (see +// set_download_throttle()), this specifies the maximum +// number of bytes per second that may be consumed by +// this channel. +//////////////////////////////////////////////////////////////////// +INLINE void HTTPChannel:: +set_max_bytes_per_second(double max_bytes_per_second) { + _max_bytes_per_second = max_bytes_per_second; + _bytes_per_update = int(_max_bytes_per_second * _seconds_per_update); +} + +//////////////////////////////////////////////////////////////////// +// Function: HTTPChannel::get_max_bytes_per_second +// Access: Published +// Description: Returns the maximum number of bytes per second that +// may be consumed by this channel when +// get_download_throttle() is true. +//////////////////////////////////////////////////////////////////// +INLINE double HTTPChannel:: +get_max_bytes_per_second() const { + return _max_bytes_per_second; +} + +//////////////////////////////////////////////////////////////////// +// Function: HTTPChannel::set_max_updates_per_second +// Access: Published +// Description: When bandwidth throttling is in effect (see +// set_download_throttle()), this specifies the maximum +// number of times per second that run() will attempt to +// do any downloading at all. +//////////////////////////////////////////////////////////////////// +INLINE void HTTPChannel:: +set_max_updates_per_second(double max_updates_per_second) { + nassertv(max_updates_per_second != 0.0f); + _max_updates_per_second = max_updates_per_second; + _seconds_per_update = 1.0f / _max_updates_per_second; + _bytes_per_update = int(_max_bytes_per_second * _seconds_per_update); +} + +//////////////////////////////////////////////////////////////////// +// Function: HTTPChannel::get_max_updates_per_second +// Access: Published +// Description: Returns the maximum number of times per second that +// run() will do anything at all, when +// get_download_throttle() is true. +//////////////////////////////////////////////////////////////////// +INLINE double HTTPChannel:: +get_max_updates_per_second() const { + return _max_updates_per_second; +} + //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::get_file_size // Access: Published diff --git a/panda/src/downloader/httpChannel.cxx b/panda/src/downloader/httpChannel.cxx index b384c6c1ff..acbd738c7a 100644 --- a/panda/src/downloader/httpChannel.cxx +++ b/panda/src/downloader/httpChannel.cxx @@ -21,6 +21,8 @@ #include "bioStream.h" #include "chunkedStream.h" #include "identityStream.h" +#include "config_downloader.h" +#include "clockObject.h" #include "buffer.h" // for Ramfile #ifdef HAVE_SSL @@ -48,6 +50,11 @@ HTTPChannel(HTTPClient *client) : _client(client) { _persistent_connection = false; + _download_throttle = false; + _max_bytes_per_second = downloader_byte_rate; + _seconds_per_update = downloader_frequency; + _max_updates_per_second = 1.0f / _seconds_per_update; + _bytes_per_update = int(_max_bytes_per_second * _seconds_per_update); _nonblocking = false; _read_index = 0; _file_size = 0; @@ -63,6 +70,8 @@ HTTPChannel(HTTPClient *client) : _proxy_tunnel = false; _body_stream = NULL; _sbio = NULL; + _last_status_code = 0; + _last_run_time = 0.0f; _download_to_ramfile = NULL; } @@ -201,6 +210,15 @@ run() { } if (_started_download) { + if (_nonblocking && _download_throttle) { + double now = ClockObject::get_global_clock()->get_real_time(); + double elapsed = now - _last_run_time; + _last_run_time = now; + if (elapsed < _seconds_per_update) { + // Come back later. + return true; + } + } switch (_download_dest) { case DD_none: return false; // We're done. @@ -1102,9 +1120,17 @@ bool HTTPChannel:: run_download_to_file() { nassertr(_body_stream != (ISocketStream *)NULL, false); + bool do_throttle = _nonblocking && _download_throttle; + int count = 0; + int ch = _body_stream->get(); while (!_body_stream->eof() && !_body_stream->fail()) { _download_to_file.put(ch); + if (do_throttle && ++count > _bytes_per_update) { + // That's enough for now. + return true; + } + ch = _body_stream->get(); } @@ -1137,9 +1163,17 @@ run_download_to_ram() { nassertr(_body_stream != (ISocketStream *)NULL, false); nassertr(_download_to_ramfile != (Ramfile *)NULL, false); + bool do_throttle = _nonblocking && _download_throttle; + int count = 0; + int ch = _body_stream->get(); while (!_body_stream->eof() && !_body_stream->fail()) { _download_to_ramfile->_data += (char)ch; + if (do_throttle && ++count > _bytes_per_update) { + // That's enough for now. + return true; + } + ch = _body_stream->get(); } diff --git a/panda/src/downloader/httpChannel.h b/panda/src/downloader/httpChannel.h index 60a5741544..94e4e4463d 100644 --- a/panda/src/downloader/httpChannel.h +++ b/panda/src/downloader/httpChannel.h @@ -86,6 +86,15 @@ PUBLISHED: INLINE void set_persistent_connection(bool persistent_connection); INLINE bool get_persistent_connection() const; + INLINE void set_download_throttle(bool download_throttle); + INLINE bool get_download_throttle() const; + + INLINE void set_max_bytes_per_second(double max_bytes_per_second); + INLINE double get_max_bytes_per_second() const; + + INLINE void set_max_updates_per_second(double max_updates_per_second); + INLINE double get_max_updates_per_second() const; + INLINE size_t get_file_size() const; void write_headers(ostream &out) const; @@ -165,6 +174,11 @@ private: PT(BioPtr) _bio; PT(BioStreamPtr) _source; bool _persistent_connection; + bool _download_throttle; + double _max_bytes_per_second; + double _max_updates_per_second; + double _seconds_per_update; + int _bytes_per_update; bool _nonblocking; URLSpec _url; @@ -235,6 +249,7 @@ private: BIO *_sbio; pset _redirect_trail; int _last_status_code; + double _last_run_time; typedef pmap Tokens; typedef pmap AuthenticationSchemes;