diff --git a/ChangeLog b/ChangeLog index 7d62ab013..1ad47fe8d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ 1.1.0 release + * support filtering which parts of session state are loaded by load_state() * deprecate support for adding torrents by HTTP URL * allow specifying which tracker to scrape in scrape_tracker * tracker response alerts from user initiated announces/scrapes are now @@ -91,6 +92,8 @@ * almost completely changed the storage interface (for custom storage) * added support for hashing pieces in multiple threads + * fix division by zero in super-seeding logic + 1.0.9 release * fix issue in checking outgoing interfaces (when that option is enabled) diff --git a/bindings/python/src/session.cpp b/bindings/python/src/session.cpp index 799154ab4..2d370e99c 100644 --- a/bindings/python/src/session.cpp +++ b/bindings/python/src/session.cpp @@ -510,18 +510,18 @@ namespace } #endif - void load_state(lt::session& ses, entry const& st) - { - allow_threading_guard guard; + void load_state(lt::session& ses, entry const& st, boost::uint32_t flags) + { + allow_threading_guard guard; - std::vector buf; - bencode(std::back_inserter(buf), st); - bdecode_node e; - error_code ec; - bdecode(&buf[0], &buf[0] + buf.size(), e, ec); - TORRENT_ASSERT(!ec); - ses.load_state(e); - } + std::vector buf; + bencode(std::back_inserter(buf), st); + bdecode_node e; + error_code ec; + bdecode(&buf[0], &buf[0] + buf.size(), e, ec); + TORRENT_ASSERT(!ec); + ses.load_state(e, flags); + } #ifndef TORRENT_DISABLE_DHT void dht_get_mutable_item(lt::session& ses, std::string key, std::string salt) @@ -796,7 +796,7 @@ void bind_session() .def("get_pe_settings", allow_threads(<::session::get_pe_settings)) #endif #endif - .def("load_state", &load_state) + .def("load_state", &load_state, (arg("entry"), arg("flags") = 0xffffffff)) .def("save_state", &save_state, (arg("entry"), arg("flags") = 0xffffffff)) .def("pop_alerts", &pop_alerts) .def("wait_for_alert", &wait_for_alert @@ -885,8 +885,8 @@ void bind_session() .value("save_dht_settings", lt::session::save_dht_settings) .value("save_dht_state", lt::session::save_dht_state) .value("save_encryption_settings", lt::session:: save_encryption_settings) - .value("save_as_map", lt::session::save_as_map) #ifndef TORRENT_NO_DEPRECATE + .value("save_as_map", lt::session::save_as_map) .value("save_i2p_proxy", lt::session::save_i2p_proxy) .value("save_proxy", lt::session::save_proxy) .value("save_dht_proxy", lt::session::save_dht_proxy) diff --git a/bindings/python/test.py b/bindings/python/test.py index 20988b9b4..324b8cb46 100644 --- a/bindings/python/test.py +++ b/bindings/python/test.py @@ -31,7 +31,8 @@ class test_alerts(unittest.TestCase): ses = lt.session({'alert_mask': lt.alert.category_t.all_categories}) shutil.copy(os.path.join('..', '..', 'test', 'test_torrents', 'base.torrent'), '.') ti = lt.torrent_info('base.torrent'); - h = ses.add_torrent({'ti': ti, 'save_path': '.'}) + h = ses.add_torrent({'ti': ti, 'save_path': os.getcwd()}) + st = h.status() time.sleep(1) ses.remove_torrent(h) ses.wait_for_alert(1000) # milliseconds @@ -39,9 +40,8 @@ class test_alerts(unittest.TestCase): for a in alerts: print(a.message()) - st = h.status() print(st.next_announce) - print(st.name) + self.assertEqual(st.name, 'temp') print(st.errc.message()) print(st.pieces) print(st.last_seen_complete) @@ -51,6 +51,7 @@ class test_alerts(unittest.TestCase): print(st.distributed_copies) print(st.paused) print(st.info_hash) + self.assertEqual(st.save_path, os.getcwd()) class test_bencoder(unittest.TestCase): diff --git a/docs/index.rst b/docs/index.rst index 3543475f4..cb816b59d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,7 +10,7 @@ * tutorial_ * examples_ * overview_ -* `reference documentation`_ +* documentation_ * contributing_ * building_ * troubleshooting_ @@ -41,8 +41,10 @@ Extensions Bindings -* `ruby bindings`_ -* `python bindings`_ +* python_ +* java_ +* go_ +* node_ -------- @@ -64,7 +66,7 @@ libtorrent .. _building: building.html .. _examples: examples.html .. _overview: manual-ref.html -.. _`reference documentation`: reference.html +.. _documentation: reference.html .. _troubleshooting: troubleshooting.html .. _`tuning`: tuning.html .. _screenshot: client_test.png @@ -85,8 +87,10 @@ libtorrent .. _`github page`: https://github.com/arvidn/libtorrent .. _blog: http://blog.libtorrent.org -.. _`ruby bindings`: http://libtorrent-ruby.rubyforge.org/ -.. _`python bindings`: python_binding.html +.. _java: https://github.com/frostwire/frostwire-jlibtorrent/ +.. _python: python_binding.html +.. _go: https://github.com/steeve/libtorrent-go +.. _node: https://github.com/fanatid/node-libtorrent .. _`Introduction, slides`: bittorrent.pdf @@ -104,22 +108,46 @@ The main goals of libtorrent are: * to be memory efficient * to be very easy to use -Donate -====== +Getting started +=============== -Support the development of libtorrent +The tutorial_ is an introduction to using libtorrent (C++). Also see the +`reference documentation`_. + +.. _`reference documentation`: reference.html + +Contribute +========== + +If your organization use libtorrent, please consider supporting its development. +See the contribute_ page for other ways to help out. .. raw:: html - + + + + + +
+ + + + + + + + +
+
-Feedback -======== +Support +======= -There's a `mailing list`__, general libtorrent discussion. +Please direct questions to the `mailing list`__, general libtorrent discussion. __ http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss diff --git a/docs/rst.css b/docs/rst.css index 6f523d942..c2d7875d6 100644 --- a/docs/rst.css +++ b/docs/rst.css @@ -223,13 +223,13 @@ div.contents { #librarySidebar { float: left; - width: 150px; + width: 170px; } #libraryBody { border-left: solid 1px #eee; padding-left: 10px; - margin-left: 158px; + margin-left: 178px; margin-right: 10px; } diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 511e52281..eae609ac7 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -52,10 +52,10 @@ First, let's extend the example to print out messages from the bittorrent engine about progress and events happening under the hood. libtorrent has a mechanism referred to as *alerts* to communicate back information to the client application. -Clients can poll libtorrents for new alerts via the `pop_alerts()`_ call on the -session object. This call fills in a vector of alert pointers with all new -alerts since the last call to this function. The pointers are owned by the -session object at will become invalidated by the next call to `pop_alerts()`_. +Clients can poll a session for new alerts via the `pop_alerts()`_ call. This +function fills in a vector of alert pointers with all new alerts since the last +call to this function. The pointers are owned by the session object at will +become invalidated by the next call to `pop_alerts()`_. The alerts form a class hierarchy with alert_ as the root class. Each specific kind of alert may include additional state, specific to the kind of message. All @@ -183,10 +183,10 @@ resuming torrents Since bittorrent downloads pieces of files in random order, it's not trivial to resume a partial download. When resuming a download, the bittorrent engine must -restore the state of the downloading torrent, specifically, which parts of the +restore the state of the downloading torrent, specifically which parts of the file(s) are downloaded. There are two approaches to doing this: -1. read every piece of the downloaded files from disk and compare it against the +1. read every piece of the downloaded files from disk and compare it against its expected hash. 2. save to disk the state of which pieces (and partial pieces) are downloaded, and load it back in again when resuming. @@ -197,8 +197,14 @@ employ (1) by default. To save resume data, call `save_resume_data()`_ on the torrent_handle_ object. This will ask libtorrent to generate the resume data and post it back in a save_resume_data_alert_. If generating the resume data fails for any reason, -a save_resume_data_failed_alert_ is posted instead. Exactly one of those alerts -will be posted for every call to `save_resume_data()`_. +a save_resume_data_failed_alert_ is posted instead. + +Exactly one of those alerts will be posted for every call to +`save_resume_data()`_. This is an important property when shutting down a +session with multiple torrents, every resume alert must be handled before +resuming with shut down. Any torrent may fail to save resume data, so the client +would need to keep a count of the outstanding resume files, decremented on +either save_resume_data_alert_ or save_resume_data_failed_alert_. The save_resume_data_alert_ looks something like this: @@ -212,10 +218,13 @@ The save_resume_data_alert_ looks something like this: boost::shared_ptr resume_data; }; -``resume_data`` points to an entry_ object. This represents a node or a tree -of nodes in a bencoded_ structure, which is the native encoding scheme in +``resume_data`` points to an entry_ object. This represents a node or a tree of +nodes in a bencoded_ structure, which is the native encoding scheme in bittorrent. It can be encoded into a byte buffer or file using `bencode()`_. +When adding a torrent with resume data, set the `add_torrent_params::resume_data`_ +to contain the bencoded buffer of the resume data. + example ------- @@ -230,6 +239,41 @@ Here's an updated version of the above example with the following updates: :tab-width: 2 :start-after: */ +torrent files +------------- + +To add torrent files to a session (as opposed to a magnet link), it must first +be loaded into a torrent_info_ object. + +The torrent_info_ object can be created either by filename a buffer or a +bencoded structure. When adding by filename, there's a sanity check limit on the +size of the file, for adding arbitrarily large torrents, load the file outside +of the constructor. + +The torrent_info_ object provides an opportunity to query information about the +.torrent file as well as mutating it before adding it to the session. + +bencoding +--------- + +bencoded_ structures is the default data storage format used by bittorrent, such +as .torrent files, tracker announce and scrape responses and some wire protocol +extensions. libtorrent provides an efficient framework for decoding bencoded +data through `bdecode()`_ function. + +There are two separate mechanisms for *encoding* and *decoding*. When decoding, +use the `bdecode()`_ function that returns a bdecode_node_. When encoding, use +`bencode()`_ taking an entry_ object. + +The key property of `bdecode()`_ is that it does not copy any data out of the +buffer that was parsed. It builds the tree structures of references pointing +into the buffer. The buffer must stay alive and valid for as long as the +bdecode_node_ is in use. + +For performance details on `bdecode()`_, see the `blog post about it`__. + +__ http://blog.libtorrent.org/2015/03/bdecode-parsers/ + .. _session: reference-Core.html#session .. _session_handle: reference-Core.html#session_handle .. _add_torrent_params: reference-Core.html#add_torrent_params @@ -258,4 +302,9 @@ Here's an updated version of the above example with the following updates: .. _bencoded: https://en.wikipedia.org/wiki/Bencode .. _entry: reference-Bencoding.html#entry .. _`bencode()`: reference-Bencoding.html#bencode() +.. _torrent_info: reference-Core.html#torrent_info +.. _`add_torrent_params::resume_data`: reference-Core.html#resume_data +.. _`bdecode()`: reference-Bdecoding.html#bdecode() +.. _bdecode_node: reference-Bdecoding.html#bdecode-node + diff --git a/examples/bt-get2.cpp b/examples/bt-get2.cpp index ab0873747..f8db4db04 100644 --- a/examples/bt-get2.cpp +++ b/examples/bt-get2.cpp @@ -43,12 +43,12 @@ POSSIBILITY OF SUCH DAMAGE. #include namespace lt = libtorrent; +using clock_t = std::chrono::monotonic_clock; // return the name of a torrent status enum char const* state(lt::torrent_status::state_t s) { - switch(s) - { + switch(s) { case lt::torrent_status::checking_files: return "checking"; case lt::torrent_status::downloading_metadata: return "dl metadata"; case lt::torrent_status::downloading: return "downloading"; @@ -74,8 +74,8 @@ int main(int argc, char const* argv[]) | lt::alert::status_notification); lt::session ses(pack); - lt::add_torrent_params atp; + std::chrono::time_point last_save_resume = clock_t::now(); // load resume data from disk and pass it in as we add the magnet link std::ifstream ifs(".resume_file", std::ios_base::binary); @@ -134,9 +134,15 @@ int main(int argc, char const* argv[]) // state output for the torrent ses.post_torrent_updates(); - // TODO: 3 call save_resume_data() once every 30 seconds or so + // save resume data once every 30 seconds + if (clock_t::now() - last_save_resume > seconds(30)) { + h.save_resume_data(); + } } - done: + + // TODO: ideally we should save resume data here + +done: std::cout << "\ndone, shutting down" << std::endl; } diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 0fdbb61a0..1413d3318 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -377,9 +377,13 @@ FILE* g_log_file = 0; std::string const& piece_bar(libtorrent::bitfield const& p, int width) { - const int table_size = 18; +#ifdef _WIN32 + int const table_size = 2; +#else + int const table_size = 18; +#endif - double piece_per_char = p.size() / double(width); + double const piece_per_char = p.size() / double(width); static std::string bar; bar.clear(); bar.reserve(width * 6); @@ -401,9 +405,13 @@ std::string const& piece_bar(libtorrent::bitfield const& p, int width) for (int k = int(piece); k < end; ++k, ++num_pieces) if (p[k]) ++num_have; int color = int(std::ceil(num_have / float((std::max)(num_pieces, 1)) * (table_size - 1))); - char buf[10]; - snprintf(buf, 10, "48;5;%d", 232 + color); - bar += esc(buf); + char buf[40]; +#ifdef _WIN32 + snprintf(buf, sizeof(buf), "\x1b[4%dm", color ? 7 : 0); +#else + snprintf(buf, sizeof(buf), "\x1b[48;5;%dm", 232 + color); +#endif + bar += buf; bar += " "; } bar += esc("0"); @@ -597,6 +605,7 @@ std::string monitor_dir; int poll_interval = 5; int max_connections_per_torrent = 50; bool seed_mode = false; +int cache_size = 1024; bool share_mode = false; bool disable_storage = false; @@ -1295,6 +1304,7 @@ int main(int argc, char* argv[]) namespace lt = libtorrent; settings_pack settings; + settings.set_int(settings_pack::cache_size, cache_size); settings.set_int(settings_pack::active_loaded_limit, 20); settings.set_int(settings_pack::choking_algorithm, settings_pack::rate_based_choker); @@ -1446,9 +1456,9 @@ int main(int argc, char* argv[]) } #endif // TORRENT_USE_I2P case 'C': - settings.set_int(settings_pack::cache_size, atoi(arg)); - settings.set_bool(settings_pack::use_read_cache, atoi(arg) > 0); - settings.set_int(settings_pack::cache_buffer_chunk_size, atoi(arg) / 100); + cache_size = atoi(arg); + settings.set_int(settings_pack::cache_size, cache_size); + settings.set_int(settings_pack::cache_buffer_chunk_size, 0); break; case 'A': settings.set_int(settings_pack::allowed_fast_set_size, atoi(arg)); break; case 'R': settings.set_int(settings_pack::read_cache_line_size, atoi(arg)); break; @@ -1564,6 +1574,9 @@ int main(int argc, char* argv[]) } ses.set_ip_filter(loaded_ip_filter); + ses.set_load_function(&load_torrent); + + error_code ec; #ifndef TORRENT_DISABLE_DHT dht_settings dht; @@ -1581,18 +1594,15 @@ int main(int argc, char* argv[]) ses.add_dht_router(std::make_pair( std::string("router.bitcomet.com"), 6881)); } -#endif - - ses.set_load_function(&load_torrent); std::vector in; - error_code ec; if (load_file(".ses_state", in, ec) == 0) { bdecode_node e; if (bdecode(&in[0], &in[0] + in.size(), e, ec) == 0) - ses.load_state(e); + ses.load_state(e, session::save_dht_state); } +#endif for (std::vector::iterator i = magnet_links.begin() , end(magnet_links.end()); i != end; ++i) @@ -1926,6 +1936,13 @@ int main(int argc, char* argv[]) if (c == '5') print_peer_rate = !print_peer_rate; if (c == '6') print_fails = !print_fails; if (c == '7') print_send_bufs = !print_send_bufs; + if (c == 'C') + { + cache_size = (cache_size == 0) ? -1 : 0; + settings_pack p; + p.set_int(settings_pack::cache_size, cache_size); + ses.apply_settings(p); + } if (c == 'h') { clear_screen(); @@ -1936,7 +1953,7 @@ int main(int argc, char* argv[]) "[q] quit client [m] add magnet link\n" "\n" "TORRENT ACTIONS\n" - "[p] pause/unpause selected torrent\n" + "[p] pause/unpause selected torrent [C] toggle disk cache\n" "[s] toggle sequential download [j] force recheck\n" "[space] toggle session pause [c] clear error\n" "[v] scrape [D] delete torrent and data\n" @@ -2290,11 +2307,6 @@ int main(int argc, char* argv[]) } } - // keep track of the number of resume data - // alerts to wait for - int num_paused = 0; - int num_failed = 0; - ses.pause(); printf("saving resume data\n"); std::vector temp; @@ -2337,51 +2349,29 @@ int main(int argc, char* argv[]) for (std::vector::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { - torrent_paused_alert* tp = alert_cast(*i); - if (tp) + if (!::handle_alert(ses, *i, files, non_files)) { - ++num_paused; - printf("\rleft: %d failed: %d pause: %d " - , num_outstanding_resume_data, num_failed, num_paused); - continue; + // if we didn't handle the alert, print it to the log + std::string event_string; + print_alert(*i, event_string); } - - if (alert_cast(*i)) - { - ++num_failed; - --num_outstanding_resume_data; - printf("\rleft: %d failed: %d pause: %d " - , num_outstanding_resume_data, num_failed, num_paused); - continue; - } - - save_resume_data_alert* rd = alert_cast(*i); - if (!rd) continue; - --num_outstanding_resume_data; - printf("\rleft: %d failed: %d pause: %d " - , num_outstanding_resume_data, num_failed, num_paused); - - if (!rd->resume_data) continue; - - torrent_handle h = rd->handle; - torrent_status st = h.status(torrent_handle::query_save_path); - std::vector out; - bencode(std::back_inserter(out), *rd->resume_data); - save_file(path_append(st.save_path, path_append(".resume" - , leaf_path(hash_to_filename[st.info_hash]) + ".resume")), out); } } if (g_log_file) fclose(g_log_file); + + // we're just saving the DHT state +#ifndef TORRENT_DISABLE_DHT printf("\nsaving session state\n"); { entry session_state; - ses.save_state(session_state); + ses.save_state(session_state, session::save_dht_state); std::vector out; bencode(std::back_inserter(out), session_state); save_file(".ses_state", out); } +#endif printf("closing session"); diff --git a/examples/print.cpp b/examples/print.cpp index ce94d6697..83f888c77 100644 --- a/examples/print.cpp +++ b/examples/print.cpp @@ -88,7 +88,7 @@ std::string const& progress_bar(int progress, int width, color_code c bar.clear(); bar.reserve(size_t(width + 10)); - int progress_chars = (progress * width + 500) / 1000; + int const progress_chars = (progress * width + 500) / 1000; if (caption.empty()) { @@ -108,14 +108,21 @@ std::string const& progress_bar(int progress, int width, color_code c caption.resize(size_t(width), ' '); +#ifdef _WIN32 + char const* background = "40"; +#else + char const* background = "48;5;238"; +#endif + char str[256]; if (flags & progress_invert) - snprintf(str, sizeof(str), "\x1b[48;5;238m\x1b[37m%s\x1b[4%d;3%dm%s\x1b[49;39m" - , caption.substr(0, progress_chars).c_str(), c, tc + snprintf(str, sizeof(str), "\x1b[%sm\x1b[37m%s\x1b[4%d;3%dm%s\x1b[49;39m" + , background, caption.substr(0, progress_chars).c_str(), c, tc , caption.substr(progress_chars).c_str()); else - snprintf(str, sizeof(str), "\x1b[4%d;3%dm%s\x1b[48;5;238m\x1b[37m%s\x1b[49;39m" - , c, tc, caption.substr(0, progress_chars).c_str(), caption.substr(progress_chars).c_str()); + snprintf(str, sizeof(str), "\x1b[4%d;3%dm%s\x1b[%sm\x1b[37m%s\x1b[49;39m" + , c, tc, caption.substr(0, progress_chars).c_str(), background + , caption.substr(progress_chars).c_str()); bar = str; } return bar; @@ -208,7 +215,7 @@ void terminal_size(int* terminal_width, int* terminal_height) } #ifdef _WIN32 -void apply_ansi_code(int* attributes, bool* reverse, int code) +void apply_ansi_code(int* attributes, bool* reverse, bool* support_chaining, int code) { static const int color_table[8] = { @@ -224,8 +231,8 @@ void apply_ansi_code(int* attributes, bool* reverse, int code) enum { - foreground_mask = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, - background_mask = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE + foreground_mask = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, + background_mask = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY }; static const int fg_mask[2] = {foreground_mask, background_mask}; @@ -233,14 +240,29 @@ void apply_ansi_code(int* attributes, bool* reverse, int code) static const int fg_shift[2] = { 0, 4}; static const int bg_shift[2] = { 4, 0}; + // default foreground + if (code == 39) code = 37; + + // default background + if (code == 49) code = 40; + if (code == 0) { // reset *attributes = color_table[7]; *reverse = false; + *support_chaining = true; + } + else if (code == 1) + { + // intensity + *attributes |= *reverse ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY; + *support_chaining = true; } else if (code == 7) { + // reverse video + *support_chaining = true; if (*reverse) return; *reverse = true; int fg_col = *attributes & foreground_mask; @@ -254,12 +276,14 @@ void apply_ansi_code(int* attributes, bool* reverse, int code) // foreground color *attributes &= ~fg_mask[*reverse]; *attributes |= color_table[code - 30] << fg_shift[*reverse]; + *support_chaining = true; } else if (code >= 40 && code <= 47) { - // foreground color + // background color *attributes &= ~bg_mask[*reverse]; *attributes |= color_table[code - 40] << bg_shift[*reverse]; + *support_chaining = true; } } #endif @@ -288,14 +312,15 @@ void print(char const* buf) CONSOLE_SCREEN_BUFFER_INFO sbi; if (GetConsoleScreenBufferInfo(out, &sbi)) { - COORD pos = sbi.dwCursorPosition; - int width = sbi.dwSize.X; - int run = width - pos.X; + COORD const pos = sbi.dwCursorPosition; + int const width = sbi.dwSize.X; + int const run = width - pos.X; DWORD n; FillConsoleOutputAttribute(out, 0x7, run, pos, &n); FillConsoleOutputCharacter(out, ' ', run, pos, &n); } ++buf; + start = buf; continue; } else if (*start == 'J') @@ -312,18 +337,30 @@ void print(char const* buf) FillConsoleOutputCharacter(out, ' ', run, pos, &n); } ++buf; + start = buf; continue; } - one_more: +one_more: while (*buf != 'm' && *buf != ';' && *buf != 0) ++buf; + + // this is where we handle reset, color and reverse codes if (*buf == 0) break; int code = atoi(start); - apply_ansi_code(¤t_attributes, &reverse, code); - if (*buf == ';') + bool support_chaining = false; + apply_ansi_code(¤t_attributes, &reverse, &support_chaining, code); + if (support_chaining) { - ++buf; - start = buf; - goto one_more; + if (*buf == ';') + { + ++buf; + start = buf; + goto one_more; + } + } + else + { + // ignore codes with multiple fields for now + while (*buf != 'm' && *buf != 0) ++buf; } SetConsoleTextAttribute(out, current_attributes); ++buf; // skip 'm' diff --git a/examples/session_view.cpp b/examples/session_view.cpp index 39e32d476..a2011d2b4 100644 --- a/examples/session_view.cpp +++ b/examples/session_view.cpp @@ -96,7 +96,11 @@ void session_view::render() snprintf(str, sizeof(str), "%s%swaste: %s up: %s (%s) " "disk queue: %s | %s cache w: %3d%% r: %3d%% " "size: w: %s r: %s total: %s %s\x1b[K" +#ifdef _WIN32 + , esc("40") +#else , esc("48;5;238") +#endif , esc("1") , add_suffix(m_cnt[0][m_wasted_bytes_idx]).c_str() , color(add_suffix(upload_rate, "/s"), col_red).c_str() diff --git a/include/libtorrent/alert.hpp b/include/libtorrent/alert.hpp index 08751de51..2bcb3661b 100644 --- a/include/libtorrent/alert.hpp +++ b/include/libtorrent/alert.hpp @@ -77,25 +77,12 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/time.hpp" #include "libtorrent/config.hpp" -#ifndef TORRENT_NO_DEPRECATE -#ifndef BOOST_NO_TYPEID -#include -#endif -#endif // TORRENT_NO_DEPRECATE - -#ifndef TORRENT_MAX_ALERT_TYPES -#define TORRENT_MAX_ALERT_TYPES 15 -#endif - -#ifndef TORRENT_NO_DEPRECATE -#ifndef BOOST_NO_TYPEID -#include -#endif -#endif - namespace libtorrent { // The ``alert`` class is the base class that specific messages are derived from. + // alert types are not copyable, and cannot be constructed by the client. The + // pointers returned by libtorrent are short lived (the details are described + // under session_handle::pop_alerts()) class TORRENT_EXPORT alert { public: @@ -216,9 +203,6 @@ namespace libtorrent { alert(); // hidden virtual ~alert(); -#if __cplusplus >= 201103L - alert(alert const&) = default; -#endif // a timestamp is automatically created in the constructor time_point timestamp() const; @@ -301,8 +285,15 @@ namespace libtorrent { #endif // TORRENT_NO_DEPRECATE + protected: + // the alert is not copyable (but for backwards compatibility reasons it + // retains the ability to clone itself, for now). +#if __cplusplus >= 201103L + alert(alert const& rhs) = default; +#endif + private: - // explicitly disallow assignment, to silence msvc warning + // explicitly disallow assignment and copyconstruction alert& operator=(alert const&); time_point m_timestamp; diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 5aadacda3..c9ba53140 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -163,7 +163,25 @@ namespace libtorrent #define TORRENT_CLONE(name) #endif + // we can only use = default in C++11 + // the purpose of this is just to make all alert types non-copyable from user + // code. The heterogeneous queue does not yet have an emplace_back(), so it + // still needs to copy alerts, but the important part is that it's not + // copyable for clients. + // TODO: Once the backwards compatibility of clone() is removed, and once + // C++11 is required, this can be simplified to just say = delete +#if __cplusplus >= 201103L + #define TORRENT_PROTECTED_CCTOR(name) \ + protected: \ + template friend struct heterogeneous_queue; \ + name(name const&) = default; \ + public: +#else + #define TORRENT_PROTECTED_CCTOR(name) +#endif + #define TORRENT_DEFINE_ALERT_IMPL(name, seq, prio) \ + TORRENT_PROTECTED_CCTOR(name) \ static const int priority = prio; \ static const int alert_type = seq; \ virtual int type() const TORRENT_OVERRIDE { return alert_type; } \ diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index a789ead95..e6c428570 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -345,6 +345,7 @@ namespace libtorrent #ifndef TORRENT_NO_DEPRECATE entry dht_state() const; + void start_dht_deprecated(entry const& startup_state); #endif void on_dht_announce(error_code const& e); void on_dht_name_lookup(error_code const& e @@ -512,7 +513,7 @@ namespace libtorrent void announce_lsd(sha1_hash const& ih, int port, bool broadcast = false) TORRENT_OVERRIDE; void save_state(entry* e, boost::uint32_t flags) const; - void load_state(bdecode_node const* e); + void load_state(bdecode_node const* e, boost::uint32_t flags); bool has_connection(peer_connection* p) const TORRENT_OVERRIDE; void insert_peer(boost::shared_ptr const& c) TORRENT_OVERRIDE; diff --git a/include/libtorrent/disk_io_job.hpp b/include/libtorrent/disk_io_job.hpp index b6c3d8065..003624466 100644 --- a/include/libtorrent/disk_io_job.hpp +++ b/include/libtorrent/disk_io_job.hpp @@ -136,7 +136,11 @@ namespace libtorrent in_progress = 0x20, // turns into file::coalesce_buffers in the file operation - coalesce_buffers = 0x40 + coalesce_buffers = 0x40, + + // the disk cache was enabled when this job was issued, it should use + // the disk cache once it's handled by a disk thread + use_disk_cache = 0x80 }; // for write jobs, returns true if its block diff --git a/include/libtorrent/entry.hpp b/include/libtorrent/entry.hpp index 49aaacfa1..627d4c67c 100644 --- a/include/libtorrent/entry.hpp +++ b/include/libtorrent/entry.hpp @@ -229,7 +229,7 @@ namespace libtorrent // The const version of ``operator[]`` will only return a reference to an // existing element at the given key. If the key is not found, it will // throw ``libtorrent::type_error``. - entry& operator[](char const* key); + entry& operator[](char const* key); entry& operator[](std::string const& key); #ifndef BOOST_NO_EXCEPTIONS const entry& operator[](char const* key) const; @@ -290,7 +290,7 @@ namespace libtorrent public: // in debug mode this is set to false by bdecode to indicate that the - // program has not yet queried the type of this entry, and sould not + // program has not yet queried the type of this entry, and should not // assume that it has a certain type. This is asserted in the accessor // functions. This does not apply if exceptions are used. mutable boost::uint8_t m_type_queried:1; diff --git a/include/libtorrent/kademlia/msg.hpp b/include/libtorrent/kademlia/msg.hpp index 607eab4f8..d82b91a47 100644 --- a/include/libtorrent/kademlia/msg.hpp +++ b/include/libtorrent/kademlia/msg.hpp @@ -90,7 +90,7 @@ struct key_desc_t // generate an error response message void TORRENT_EXPORT incoming_error(entry& e, char const* msg, int error_code = 203); -// given a redundent name to avoid clashing with libtorrent::detail +// given a redundant name to avoid clashing with libtorrent::detail namespace dht_detail { bool TORRENT_EXPORT verify_message(bdecode_node const& msg, key_desc_t const desc[] diff --git a/include/libtorrent/operations.hpp b/include/libtorrent/operations.hpp index 9cb0bad45..524b6648e 100644 --- a/include/libtorrent/operations.hpp +++ b/include/libtorrent/operations.hpp @@ -89,7 +89,7 @@ namespace libtorrent // a call related to bittorrent protocol encryption failed op_encryption, - // an attempt to connect a socket failed + // an attempt to connect a socket failed op_connect, // establishing an SSL connection failed diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 1bdfaed0c..05db4ecf2 100644 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -589,7 +589,7 @@ namespace libtorrent bool can_disconnect(error_code const& ec) const; void incoming_dht_port(int listen_port); - + void incoming_reject_request(peer_request const& r); void incoming_have_all(); void incoming_have_none(); diff --git a/include/libtorrent/receive_buffer.hpp b/include/libtorrent/receive_buffer.hpp index b35a6cdd0..db377ae9d 100644 --- a/include/libtorrent/receive_buffer.hpp +++ b/include/libtorrent/receive_buffer.hpp @@ -40,7 +40,7 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { -struct receive_buffer +struct TORRENT_EXTRA_EXPORT receive_buffer { friend struct crypto_receive_buffer; @@ -58,6 +58,7 @@ struct receive_buffer int packet_bytes_remaining() const { TORRENT_ASSERT(m_recv_start == 0); + TORRENT_ASSERT(m_packet_size > 0); return m_packet_size - m_recv_pos; } @@ -67,13 +68,19 @@ struct receive_buffer int pos() const { return m_recv_pos; } int capacity() const { return m_recv_buffer.capacity() + m_disk_recv_buffer_size; } - int regular_buffer_size() const { return m_packet_size - m_disk_recv_buffer_size; } + int regular_buffer_size() const + { + TORRENT_ASSERT(m_packet_size > 0); + return m_packet_size - m_disk_recv_buffer_size; + } // regular buffer only boost::asio::mutable_buffer reserve(int size); // with possible disk buffer usage int reserve(boost::array& vec, int size); + // tell the buffer we just receved more bytes at the end of it. This will + // advance the end cursor void received(int bytes_transferred) { TORRENT_ASSERT(m_packet_size > 0); @@ -82,9 +89,14 @@ struct receive_buffer + m_disk_recv_buffer_size)); } + // tell the buffer we consumed some bytes of it. This will advance the read + // cursor int advance_pos(int bytes); + + // has the read cursor reached the end cursor? bool pos_at_end() { return m_recv_pos == m_recv_end; } + // make the buffer size dividible by 8 bytes (RC4 block size) void clamp_size(); void set_soft_packet_size(int size) { m_soft_packet_size = size; } @@ -94,6 +106,8 @@ struct receive_buffer // offset = the offset into the receive buffer where to remove `size` bytes void cut(int size, int packet_size, int offset = 0); + // return the interval between the start of the buffer to the read cursor. + // This is the "current" packet. buffer::const_interval get() const; #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) diff --git a/include/libtorrent/session_handle.hpp b/include/libtorrent/session_handle.hpp index bd5f816a7..d387d4c9f 100644 --- a/include/libtorrent/session_handle.hpp +++ b/include/libtorrent/session_handle.hpp @@ -92,13 +92,11 @@ namespace libtorrent save_dht_state = 0x004, // save pe_settings - save_encryption_settings = 0x020, - - // internal - save_as_map = 0x040 + save_encryption_settings = 0x020 #ifndef TORRENT_NO_DEPRECATE , + save_as_map = 0x040, // saves RSS feeds save_feeds = 0x080, save_proxy = 0x008, @@ -118,11 +116,11 @@ namespace libtorrent // ``load_state`` expects a bdecode_node which can be built from a bencoded // buffer with bdecode(). // - // The ``flags`` arguments passed in to ``save_state`` can be used to - // filter which parts of the session state to save. By default, all state - // is saved (except for the individual torrents). see save_state_flags_t + // The ``flags`` argument is used to filter which parts of the session + // state to save or load. By default, all state is saved/restored (except + // for the individual torrents). see save_state_flags_t void save_state(entry& e, boost::uint32_t flags = 0xffffffff) const; - void load_state(bdecode_node const& e); + void load_state(bdecode_node const& e, boost::uint32_t flags = 0xffffffff); // .. note:: // these calls are potentially expensive and won't scale well with @@ -547,12 +545,14 @@ namespace libtorrent // deprecated in 0.15 // use load_state and save_state instead TORRENT_DEPRECATED - void load_state(entry const& ses_state); + void load_state(entry const& ses_state + , boost::uint32_t flags = 0xffffffff); TORRENT_DEPRECATED entry state() const; // deprecated in 1.1 TORRENT_DEPRECATED - void load_state(lazy_entry const& ses_state); + void load_state(lazy_entry const& ses_state + , boost::uint32_t flags = 0xffffffff); #endif // TORRENT_NO_DEPRECATE // Sets a filter that will be used to reject and accept incoming as well diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 2f110db8e..149d7a4a7 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -453,7 +453,13 @@ namespace libtorrent void flush_cache(); void pause(bool graceful = false); void resume(); - void set_allow_peers(bool b, bool graceful_pause = false); + + enum pause_flags_t + { + flag_graceful_pause = 1, + flag_clear_disk_cache = 2 + }; + void set_allow_peers(bool b, int flags = flag_clear_disk_cache); void set_announce_to_dht(bool b) { m_announce_to_dht = b; } void set_announce_to_trackers(bool b) { m_announce_to_trackers = b; } void set_announce_to_lsd(bool b) { m_announce_to_lsd = b; } @@ -462,7 +468,7 @@ namespace libtorrent int started() const { return m_started; } void step_session_time(int seconds); - void do_pause(); + void do_pause(bool clear_disk_cache = true); void do_resume(); int finished_time() const; diff --git a/include/libtorrent/upnp.hpp b/include/libtorrent/upnp.hpp index 06e17077c..720e72205 100644 --- a/include/libtorrent/upnp.hpp +++ b/include/libtorrent/upnp.hpp @@ -98,6 +98,7 @@ namespace libtorrent // int: port-mapping index // address: external address as queried from router // int: external port +// int: protocol (UDP, TCP) // std::string: error message // an empty string as error means success typedef boost::function portmap_callback_t; diff --git a/simulation/Jamfile b/simulation/Jamfile index 23e69b057..e222b1a13 100644 --- a/simulation/Jamfile +++ b/simulation/Jamfile @@ -15,6 +15,7 @@ project setup_swarm.cpp setup_dht.cpp create_torrent.cpp + utils.cpp : default-build multi full @@ -23,6 +24,7 @@ project ; alias libtorrent-sims : + [ run test_checking.cpp ] [ run test_optimistic_unchoke.cpp ] [ run test_transfer.cpp ] [ run test_http_connection.cpp ] diff --git a/simulation/test_checking.cpp b/simulation/test_checking.cpp new file mode 100644 index 000000000..87d27a3ca --- /dev/null +++ b/simulation/test_checking.cpp @@ -0,0 +1,115 @@ +/* + +Copyright (c) 2016, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/session.hpp" +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/torrent_status.hpp" +#include "simulator/simulator.hpp" + +#include "test.hpp" +#include "settings.hpp" +#include "create_torrent.hpp" +#include "utils.hpp" + +template +void run_test(Setup const& setup, Test const& test) +{ + // this is a seeding torrent + lt::add_torrent_params atp = create_torrent(0, true); + + sim::default_config network_cfg; + sim::simulation sim{network_cfg}; + auto ios = std::unique_ptr(new sim::asio::io_service( + sim, lt::address_v4::from_string("50.0.0.1"))); + lt::session_proxy zombie; + + // setup settings pack to use for the session (customization point) + lt::settings_pack pack = settings(); + setup(atp, pack); + + auto ses = std::make_shared(pack, *ios); + + ses->async_add_torrent(atp); + + print_alerts(*ses); + + lt::deadline_timer timer(*ios); + timer.expires_from_now(lt::seconds(6)); + timer.async_wait([&](lt::error_code const& ec) + { + test(*ses); + + // shut down + ses->set_alert_notify([]{}); + zombie = ses->abort(); + ses.reset(); + }); + + sim.run(); +} + +TORRENT_TEST(cache_after_checking) +{ + run_test( + [](lt::add_torrent_params& atp, lt::settings_pack& p) { + atp.flags |= lt::add_torrent_params::flag_auto_managed; + p.set_int(lt::settings_pack::cache_size, 100); + }, + [](lt::session& ses) { + int cache = get_cache_size(ses); + TEST_CHECK(cache > 0); + + std::vector tor = ses.get_torrents(); + TEST_EQUAL(tor.size(), 1); + + TEST_EQUAL(tor[0].status().is_seeding, true); + }); +} + +TORRENT_TEST(checking_no_cache) +{ + run_test( + [](lt::add_torrent_params& atp, lt::settings_pack& p) { + atp.flags |= lt::add_torrent_params::flag_auto_managed; + p.set_int(lt::settings_pack::cache_size, 0); + }, + [](lt::session& ses) { + int cache = get_cache_size(ses); + TEST_EQUAL(cache, 0); + + std::vector tor = ses.get_torrents(); + TEST_EQUAL(tor.size(), 1); + + TEST_EQUAL(tor[0].status().is_seeding, true); + }); +} diff --git a/simulation/test_swarm.cpp b/simulation/test_swarm.cpp index d8ca4fb1e..e8d2d9bab 100644 --- a/simulation/test_swarm.cpp +++ b/simulation/test_swarm.cpp @@ -97,7 +97,7 @@ TORRENT_TEST(session_stats) // on alert , [=](lt::alert const* a, lt::session& ses) { - lt::session_stats_alert const* ss = lt::alert_cast(a); + auto const* ss = lt::alert_cast(a); if (!ss) return; // there's one downloading torrent diff --git a/simulation/test_transfer.cpp b/simulation/test_transfer.cpp index e3d519aee..07f948dcb 100644 --- a/simulation/test_transfer.cpp +++ b/simulation/test_transfer.cpp @@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "create_torrent.hpp" #include "settings.hpp" #include "libtorrent/session.hpp" +#include "libtorrent/session_stats.hpp" #include "libtorrent/deadline_timer.hpp" #include "libtorrent/settings_pack.hpp" #include "libtorrent/ip_filter.hpp" @@ -44,16 +45,12 @@ POSSIBILITY OF SUCH DAMAGE. #include "simulator/simulator.hpp" #include "simulator/socks_server.hpp" #include "setup_swarm.hpp" +#include "utils.hpp" using namespace sim; namespace lt = libtorrent; -enum flags_t -{ - ipv6 = 1, -}; - template void run_test( Setup const& setup @@ -63,8 +60,6 @@ void run_test( { using namespace libtorrent; - lt::time_point start_time = lt::clock_type::now(); - const bool use_ipv6 = flags & ipv6; char const* peer0_ip[2] = { "50.0.0.1", "feed:face:baad:f00d::1" }; @@ -112,32 +107,15 @@ void run_test( setup(*ses[0], *ses[1]); // only monitor alerts for session 0 (the downloader) - ses[0]->set_alert_notify([&] { ios0.post([&] { - std::vector alerts; - ses[0]->pop_alerts(&alerts); - - for (lt::alert const* a : alerts) + print_alerts(*ses[0], [=](lt::session& ses, lt::alert const* a) { + if (auto ta = alert_cast(a)) { - printf("%-3d [0] %s\n", int(lt::duration_cast(a->timestamp() - - start_time).count()), a->message().c_str()); - if (auto ta = alert_cast(a)) - { - ta->handle.connect_peer(lt::tcp::endpoint(peer1, 6881)); - } - // call the user handler - on_alert(*ses[0], a); + ta->handle.connect_peer(lt::tcp::endpoint(peer1, 6881)); } - } ); } ); + on_alert(ses, a); + }); - ses[1]->set_alert_notify([&] { ios0.post([&] { - std::vector alerts; - ses[1]->pop_alerts(&alerts); - for (lt::alert const* a : alerts) - { - printf("%-3d [1] %s\n", int(lt::duration_cast(a->timestamp() - - start_time).count()), a->message().c_str()); - } - } ); } ); + print_alerts(*ses[1]); // the first peer is a downloader, the second peer is a seed lt::add_torrent_params params = create_torrent(1); @@ -169,52 +147,6 @@ void run_test( sim.run(); } -void utp_only(lt::session& ses) -{ - using namespace libtorrent; - settings_pack p; - utp_only(p); - ses.apply_settings(p); -} - -void enable_enc(lt::session& ses) -{ - using namespace libtorrent; - settings_pack p; - enable_enc(p); - ses.apply_settings(p); -} - -void filter_ips(lt::session& ses) -{ - using namespace libtorrent; - ip_filter filter; - filter.add_rule(asio::ip::address_v4::from_string("50.0.0.1") - , asio::ip::address_v4::from_string("50.0.0.2"), ip_filter::blocked); - ses.set_ip_filter(filter); -} - -void set_proxy(lt::session& ses, int proxy_type, int flags = 0, bool proxy_peer_connections = true) -{ - // apply the proxy settings to session 0 - using namespace libtorrent; - settings_pack p; - p.set_int(settings_pack::proxy_type, proxy_type); - if (proxy_type == settings_pack::socks4) - p.set_int(settings_pack::proxy_port, 4444); - else - p.set_int(settings_pack::proxy_port, 5555); - if (flags & ipv6) - p.set_str(settings_pack::proxy_hostname, "2001::2"); - else - p.set_str(settings_pack::proxy_hostname, "50.50.50.50"); - p.set_bool(settings_pack::proxy_hostnames, true); - p.set_bool(settings_pack::proxy_peer_connections, proxy_peer_connections); - p.set_bool(settings_pack::proxy_tracker_connections, true); - - ses.apply_settings(p); -} - TORRENT_TEST(socks4_tcp) { using namespace libtorrent; @@ -391,3 +323,36 @@ TORRENT_TEST(no_proxy_utp_banned) ); } +TORRENT_TEST(auto_disk_cache_size) +{ + using namespace libtorrent; + run_test( + [](lt::session& ses0, lt::session& ses1) { set_cache_size(ses0, -1); }, + [](lt::session& ses, lt::alert const* alert) {}, + [](std::shared_ptr ses[2]) { + TEST_EQUAL(is_seed(*ses[0]), true); + + int const cache_size = get_cache_size(*ses[0]); + printf("cache size: %d\n", cache_size); + // this assumes the test torrent is at least 4 blocks + TEST_CHECK(cache_size > 4); + } + ); +} + +TORRENT_TEST(disable_disk_cache) +{ + using namespace libtorrent; + run_test( + [](lt::session& ses0, lt::session& ses1) { set_cache_size(ses0, 0); }, + [](lt::session& ses, lt::alert const* alert) {}, + [](std::shared_ptr ses[2]) { + TEST_EQUAL(is_seed(*ses[0]), true); + + int const cache_size = get_cache_size(*ses[0]); + printf("cache size: %d\n", cache_size); + TEST_EQUAL(cache_size, 0); + } + ); +} + diff --git a/simulation/utils.cpp b/simulation/utils.cpp new file mode 100644 index 000000000..e8d007205 --- /dev/null +++ b/simulation/utils.cpp @@ -0,0 +1,139 @@ +/* + +Copyright (c) 2016, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "utils.hpp" +#include "test.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/settings_pack.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/session_stats.hpp" +#include "libtorrent/alert.hpp" +#include "setup_swarm.hpp" + +using namespace libtorrent; + +void utp_only(lt::session& ses) +{ + settings_pack p; + utp_only(p); + ses.apply_settings(p); +} + +void enable_enc(lt::session& ses) +{ + settings_pack p; + enable_enc(p); + ses.apply_settings(p); +} + +void filter_ips(lt::session& ses) +{ + ip_filter filter; + filter.add_rule(address_v4::from_string("50.0.0.1") + , address_v4::from_string("50.0.0.2"), ip_filter::blocked); + ses.set_ip_filter(filter); +} + +void set_cache_size(lt::session& ses, int val) +{ + settings_pack pack; + pack.set_int(settings_pack::cache_size, val); + ses.apply_settings(pack); +} + +int get_cache_size(lt::session& ses) +{ + std::vector stats = session_stats_metrics(); + int const read_cache_idx = find_metric_idx("disk.read_cache_blocks"); + int const write_cache_idx = find_metric_idx("disk.write_cache_blocks"); + TEST_CHECK(read_cache_idx >= 0); + TEST_CHECK(write_cache_idx >= 0); + ses.set_alert_notify([](){}); + ses.post_session_stats(); + std::vector alerts; + ses.pop_alerts(&alerts); + int cache_size = -1; + for (auto const a : alerts) + { + if (auto const* st = alert_cast(a)) + { + cache_size = st->values[read_cache_idx]; + cache_size += st->values[write_cache_idx]; + break; + } + } + return cache_size; +} + +void set_proxy(lt::session& ses, int proxy_type, int flags, bool proxy_peer_connections) +{ + // apply the proxy settings to session 0 + settings_pack p; + p.set_int(settings_pack::proxy_type, proxy_type); + if (proxy_type == settings_pack::socks4) + p.set_int(settings_pack::proxy_port, 4444); + else + p.set_int(settings_pack::proxy_port, 5555); + if (flags & ipv6) + p.set_str(settings_pack::proxy_hostname, "2001::2"); + else + p.set_str(settings_pack::proxy_hostname, "50.50.50.50"); + p.set_bool(settings_pack::proxy_hostnames, true); + p.set_bool(settings_pack::proxy_peer_connections, proxy_peer_connections); + p.set_bool(settings_pack::proxy_tracker_connections, true); + + ses.apply_settings(p); +} + +void print_alerts(lt::session& ses + , std::function on_alert) +{ + lt::time_point start_time = lt::clock_type::now(); + + ses.set_alert_notify([&ses,start_time,on_alert] { + ses.get_io_service().post([&ses,start_time,on_alert] { + + std::vector alerts; + ses.pop_alerts(&alerts); + + for (lt::alert const* a : alerts) + { + printf("%-3d [0] %s\n", int(lt::duration_cast(a->timestamp() + - start_time).count()), a->message().c_str()); + // call the user handler + on_alert(ses, a); + } + } ); } ); +} + diff --git a/simulation/utils.hpp b/simulation/utils.hpp new file mode 100644 index 000000000..26cfbf96e --- /dev/null +++ b/simulation/utils.hpp @@ -0,0 +1,60 @@ +/* + +Copyright (c) 2016, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +namespace libtorrent +{ + class session; + class alert; +} + +namespace lt = libtorrent; + +void utp_only(lt::session& ses); +void enable_enc(lt::session& ses); +void filter_ips(lt::session& ses); +void set_cache_size(lt::session& ses, int val); +int get_cache_size(lt::session& ses); + +enum flags_t +{ + ipv6 = 1, +}; + +void set_proxy(lt::session& ses, int proxy_type, int flags = 0 + , bool proxy_peer_connections = true); + +void print_alerts(lt::session& ses + , std::function on_alert + = [](lt::session& ses, lt::alert const* a) {}); + diff --git a/src/alert.cpp b/src/alert.cpp index e8707bc4f..9d61e4da0 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -87,7 +87,11 @@ namespace libtorrent { char const* torrent_alert::torrent_name() const { +#ifndef TORRENT_NO_DEPRECATE + return name.c_str(); +#else return m_alloc.ptr(m_name_idx); +#endif } std::string torrent_alert::message() const @@ -124,7 +128,11 @@ namespace libtorrent { char const* tracker_alert::tracker_url() const { +#ifndef TORRENT_NO_DEPRECATE + return url.c_str(); +#else return m_alloc.ptr(m_url_idx); +#endif } std::string tracker_alert::message() const @@ -195,7 +203,11 @@ namespace libtorrent { char const* file_renamed_alert::new_name() const { +#ifndef TORRENT_NO_DEPRECATE + return name.c_str(); +#else return m_alloc.ptr(m_name_idx); +#endif } std::string file_renamed_alert::message() const @@ -292,7 +304,11 @@ namespace libtorrent { char const* tracker_error_alert::error_message() const { +#ifndef TORRENT_NO_DEPRECATE + return msg.c_str(); +#else return m_alloc.ptr(m_msg_idx); +#endif } std::string tracker_error_alert::message() const @@ -320,7 +336,11 @@ namespace libtorrent { char const* tracker_warning_alert::warning_message() const { +#ifndef TORRENT_NO_DEPRECATE + return msg.c_str(); +#else return m_alloc.ptr(m_msg_idx); +#endif } std::string tracker_warning_alert::message() const @@ -378,8 +398,12 @@ namespace libtorrent { char const* scrape_failed_alert::error_message() const { +#ifndef TORRENT_NO_DEPRECATE + return msg.c_str(); +#else if (m_msg_idx == -1) return ""; else return m_alloc.ptr(m_msg_idx); +#endif } std::string scrape_failed_alert::message() const @@ -649,7 +673,11 @@ namespace libtorrent { char const* storage_moved_alert::storage_path() const { +#ifndef TORRENT_NO_DEPRECATE + return path.c_str(); +#else return m_alloc.ptr(m_path_idx); +#endif } storage_moved_failed_alert::storage_moved_failed_alert( @@ -669,7 +697,11 @@ namespace libtorrent { char const* storage_moved_failed_alert::file_path() const { +#ifndef TORRENT_NO_DEPRECATE + return file.c_str(); +#else return m_alloc.ptr(m_file_idx); +#endif } std::string storage_moved_failed_alert::message() const @@ -935,7 +967,11 @@ namespace libtorrent { char const* portmap_log_alert::log_message() const { +#ifndef TORRENT_NO_DEPRECATE + return msg.c_str(); +#else return m_alloc.ptr(m_log_idx); +#endif } std::string portmap_log_alert::message() const @@ -976,7 +1012,11 @@ namespace libtorrent { char const* fastresume_rejected_alert::file_path() const { +#ifndef TORRENT_NO_DEPRECATE + return file.c_str(); +#else return m_alloc.ptr(m_path_idx); +#endif } peer_blocked_alert::peer_blocked_alert(aux::stack_allocator& alloc @@ -1141,7 +1181,11 @@ namespace libtorrent { char const* trackerid_alert::tracker_id() const { +#ifndef TORRENT_NO_DEPRECATE + return trackerid.c_str(); +#else return m_alloc.ptr(m_tracker_idx); +#endif } std::string trackerid_alert::message() const @@ -1706,8 +1750,12 @@ namespace libtorrent { char const* url_seed_alert::error_message() const { +#ifndef TORRENT_NO_DEPRECATE + return msg.c_str(); +#else if (m_msg_idx == -1) return ""; return m_alloc.ptr(m_msg_idx); +#endif } file_error_alert::file_error_alert(aux::stack_allocator& alloc @@ -1730,7 +1778,11 @@ namespace libtorrent { char const* file_error_alert::filename() const { +#ifndef TORRENT_NO_DEPRECATE + return file.c_str(); +#else return m_alloc.ptr(m_file_idx); +#endif } std::string file_error_alert::message() const diff --git a/src/block_cache.cpp b/src/block_cache.cpp index 1cc7b49dd..81d6fa0ba 100644 --- a/src/block_cache.cpp +++ b/src/block_cache.cpp @@ -1082,7 +1082,7 @@ int block_cache::try_evict_blocks(int num, cached_piece_entry* ignore) // the first pass, only evict blocks that have been // hashed if (pass == 0 && pe->hash) - end = pe->hash->offset / block_size(); + end = pe->hash->offset / block_size(); // go through the blocks and evict the ones // that are not dirty and not referenced diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index a42631c50..113a85d6b 100644 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -808,7 +808,7 @@ namespace libtorrent // we support FAST extension *(ptr + 7) |= 0x04; -#ifndef TORRENT_DISABLE_LOGGING +#ifndef TORRENT_DISABLE_LOGGING std::string bitmask; for (int k = 0; k < 8; ++k) { @@ -3284,8 +3284,7 @@ namespace libtorrent if (!m_recv_buffer.packet_finished()) return; recv_buffer = m_recv_buffer.get(); - -#ifndef TORRENT_DISABLE_LOGGING +#ifndef TORRENT_DISABLE_LOGGING std::string extensions; extensions.resize(8 * 8); for (int i=0; i < 8; ++i) @@ -3461,7 +3460,7 @@ namespace libtorrent #endif #ifndef TORRENT_DISABLE_LOGGING - peer_log(peer_log_alert::incoming_message, "HANDSHAKE"); + peer_log(peer_log_alert::incoming_message, "HANDSHAKE", "connection ready"); #endif // consider this a successful connection, reset the failcount if (peer_info_struct()) diff --git a/src/choker.cpp b/src/choker.cpp index d3cf88071..103bd9802 100644 --- a/src/choker.cpp +++ b/src/choker.cpp @@ -133,14 +133,14 @@ namespace libtorrent // when seeding, prefer the peer we're uploading the fastest to c1 = lhs->uploaded_in_last_round(); c2 = rhs->uploaded_in_last_round(); - + // take torrent priority into account c1 *= prio1; c2 *= prio2; if (c1 > c2) return true; if (c2 > c1) return false; - + // prioritize the one that has waited the longest to be unchoked // the round-robin unchoker relies on this logic. Don't change it // without moving this into that unchoker logic @@ -200,7 +200,7 @@ namespace libtorrent ? t2_total - rhs->num_have_pieces() : rhs->num_have_pieces()) * 1000 / t2_total; if (score1 > score2) return true; if (score2 > score1) return false; - + // prioritize the one that has waited the longest to be unchoked // the round-robin unchoker relies on this logic. Don't change it // without moving this into that unchoker logic @@ -215,7 +215,7 @@ namespace libtorrent c1 = lhs->uploaded_in_last_round(); c2 = rhs->uploaded_in_last_round(); - + // take torrent priority into account c1 *= lhs->get_priority(peer_connection::upload_channel); c2 *= rhs->get_priority(peer_connection::upload_channel); @@ -256,7 +256,18 @@ namespace libtorrent , time_duration unchoke_interval , aux::session_settings const& sett) { +#if TORRENT_USE_ASSERTS + for (std::vector::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + TORRENT_ASSERT((*i)->self()); + TORRENT_ASSERT((*i)->associated_torrent().lock()); + } +#endif + int upload_slots = sett.get_int(settings_pack::unchoke_slots_limit); + if (upload_slots < 0) + upload_slots = (std::numeric_limits::max)(); // ==== BitTyrant ==== // diff --git a/src/disk_buffer_pool.cpp b/src/disk_buffer_pool.cpp index 769783ebc..79486d75e 100644 --- a/src/disk_buffer_pool.cpp +++ b/src/disk_buffer_pool.cpp @@ -371,11 +371,14 @@ namespace libtorrent #else if (m_using_pool_allocator) { - ret = static_cast(m_pool.malloc()); - int effective_block_size = m_cache_buffer_chunk_size + int const effective_block_size + = m_in_use >= m_max_use + ? 20 // use small increments once we've exceeded the cache size + : m_cache_buffer_chunk_size ? m_cache_buffer_chunk_size : (std::max)(m_max_use / 10, 1); m_pool.set_next_size(effective_block_size); + ret = static_cast(m_pool.malloc()); } else { @@ -453,7 +456,9 @@ namespace libtorrent m_cache_buffer_chunk_size = sett.get_int(settings_pack::cache_buffer_chunk_size); m_lock_disk_cache = sett.get_bool(settings_pack::lock_disk_cache); #ifndef TORRENT_DISABLE_POOL_ALLOCATOR - m_want_pool_allocator = sett.get_bool(settings_pack::use_disk_cache_pool); + // if the chunk size is set to 1, there's no point in creating a pool + m_want_pool_allocator = sett.get_bool(settings_pack::use_disk_cache_pool) + && (m_cache_buffer_chunk_size != 1); // if there are no allocated blocks, it's OK to switch allocator if (m_in_use == 0) m_using_pool_allocator = m_want_pool_allocator; @@ -473,12 +478,38 @@ namespace libtorrent #endif sett.get_str(settings_pack::mmap_cache).empty()) { - int cache_size = sett.get_int(settings_pack::cache_size); + int const cache_size = sett.get_int(settings_pack::cache_size); if (cache_size < 0) { boost::uint64_t phys_ram = total_physical_ram(); if (phys_ram == 0) m_max_use = 1024; - else m_max_use = phys_ram / 8 / m_block_size; + else + { + // this is the logic to calculate the automatic disk cache size + // based on the amount of physical RAM. + // The more physical RAM, the smaller portion of it is allocated + // for the cache. + + // we take a 30th of everything exceeding 4 GiB + // a 20th of everything exceeding 1 GiB + // and a 10th of everything below a GiB + + boost::int64_t const gb = 1024 * 1024 * 1024; + + boost::int64_t result = 0; + if (phys_ram > 4 * gb) + { + result += (phys_ram - 4 * gb) / 30; + phys_ram = 4 * gb; + } + if (phys_ram > 1 * gb) + { + result += (phys_ram - 1 * gb) / 20; + phys_ram = 1 * gb; + } + result += phys_ram / 10; + m_max_use = result / m_block_size; + } if (sizeof(void*) == 4) { @@ -500,6 +531,8 @@ namespace libtorrent m_exceeded_max_size = true; m_trigger_cache_trim(); } + if (m_cache_buffer_chunk_size > m_max_use) + m_cache_buffer_chunk_size = m_max_use; } #if TORRENT_USE_ASSERTS diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index deb03c549..aa185c666 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -573,7 +573,7 @@ namespace libtorrent int piece_size = pe->storage->files()->piece_size(pe->piece); TORRENT_PIECE_ASSERT(piece_size > 0, pe); - + int iov_len = 0; // the blocks we're flushing int num_flushing = 0; @@ -1142,21 +1142,6 @@ namespace libtorrent return; } -#if TORRENT_USE_ASSERTS - // TODO: it should clear the hash state even when there's an error, right? - if (j->action == disk_io_job::hash && !j->error.ec) - { - // a hash job should never return without clearing pe->hash - l.lock(); - cached_piece_entry* pe = m_disk_cache.find_piece(j); - if (pe != NULL) - { - TORRENT_PIECE_ASSERT(pe->hash == NULL, pe); - } - l.unlock(); - } -#endif - if (ret == defer_handler) return; j->ret = ret; @@ -1202,16 +1187,11 @@ namespace libtorrent int disk_io_thread::do_read(disk_io_job* j, jobqueue_t& completed_jobs) { - if (!m_settings.get_bool(settings_pack::use_read_cache) - || m_settings.get_int(settings_pack::cache_size) == 0) + if ((j->flags & disk_io_job::use_disk_cache) == 0) { // we're not using a cache. This is the simple path // just read straight from the file int ret = do_uncached_read(j); - - mutex::scoped_lock l(m_cache_mutex); - cached_piece_entry* pe = m_disk_cache.find_piece(j); - if (pe) maybe_issue_queued_read_jobs(pe, completed_jobs); return ret; } @@ -1475,8 +1455,7 @@ namespace libtorrent // should we put this write job in the cache? // if we don't use the cache we shouldn't. - if (m_settings.get_bool(settings_pack::use_write_cache) - && m_settings.get_int(settings_pack::cache_size) > 0) + if (j->flags & disk_io_job::use_disk_cache) { mutex::scoped_lock l(m_cache_mutex); @@ -1592,7 +1571,7 @@ namespace libtorrent TORRENT_ASSERT(j->action == disk_io_job::read); if (m_settings.get_bool(settings_pack::use_read_cache) - && m_settings.get_int(settings_pack::cache_size) > 0) + && m_settings.get_int(settings_pack::cache_size) != 0) { int ret = m_disk_cache.try_read(j); if (ret >= 0) @@ -1629,7 +1608,7 @@ namespace libtorrent j->error.operation = storage_error::read; return 0; } - + j->flags |= disk_io_job::use_disk_cache; if (pe->outstanding_read) { TORRENT_PIECE_ASSERT(j->piece == pe->piece, pe); @@ -1705,10 +1684,11 @@ namespace libtorrent TORRENT_ASSERT(m_disk_cache.is_disk_buffer(j->buffer.disk_block)); l_.unlock(); #endif - if (m_settings.get_int(settings_pack::cache_size) > 0 + if (m_settings.get_int(settings_pack::cache_size) != 0 && m_settings.get_bool(settings_pack::use_write_cache)) { TORRENT_ASSERT((r.start % m_disk_cache.block_size()) == 0); + j->flags |= disk_io_job::use_disk_cache; if (storage->is_blocked(j)) { @@ -1798,6 +1778,12 @@ namespace libtorrent } l.unlock(); + if (m_settings.get_bool(settings_pack::use_read_cache) + && m_settings.get_int(settings_pack::cache_size) != 0) + { + j->flags |= disk_io_job::use_disk_cache; + } + add_job(j); } @@ -2233,10 +2219,10 @@ namespace libtorrent // just read straight from the file TORRENT_ASSERT(m_magic == 0x1337); - int piece_size = j->storage->files()->piece_size(j->piece); - int block_size = m_disk_cache.block_size(); - int blocks_in_piece = (piece_size + block_size - 1) / block_size; - int file_flags = file_flags_for_job(j); + int const piece_size = j->storage->files()->piece_size(j->piece); + int const block_size = m_disk_cache.block_size(); + int const blocks_in_piece = (piece_size + block_size - 1) / block_size; + int const file_flags = file_flags_for_job(j); file::iovec_t iov; iov.iov_base = m_disk_cache.allocate_buffer("hashing"); @@ -2257,7 +2243,7 @@ namespace libtorrent if (!j->error.ec) { - boost::uint32_t read_time = total_microseconds(clock_type::now() - start_time); + boost::uint32_t const read_time = total_microseconds(clock_type::now() - start_time); m_read_time.add_sample(read_time); m_stats_counters.inc_stats_counter(counters::num_blocks_read); @@ -2281,11 +2267,11 @@ namespace libtorrent { INVARIANT_CHECK; - if (m_settings.get_int(settings_pack::cache_size) == 0) + if ((j->flags & disk_io_job::use_disk_cache) == 0) return do_uncached_hash(j); - int piece_size = j->storage->files()->piece_size(j->piece); - int file_flags = file_flags_for_job(j); + int const piece_size = j->storage->files()->piece_size(j->piece); + int const file_flags = file_flags_for_job(j); mutex::scoped_lock l(m_cache_mutex); @@ -2324,7 +2310,7 @@ namespace libtorrent } } - if (pe == NULL && !m_settings.get_bool(settings_pack::use_read_cache)) + if (pe == NULL && m_settings.get_bool(settings_pack::use_read_cache) == false) { l.unlock(); // if there's no piece in the cache, and the read cache is disabled @@ -2373,7 +2359,7 @@ namespace libtorrent int block_size = m_disk_cache.block_size(); int blocks_in_piece = (piece_size + block_size - 1) / block_size; - + // keep track of which blocks we have locked by incrementing // their refcounts. This is used to decrement only these blocks // later. @@ -2616,7 +2602,7 @@ namespace libtorrent { INVARIANT_CHECK; TORRENT_ASSERT(j->buffer.disk_block == 0); - + if (m_settings.get_int(settings_pack::cache_size) == 0 || m_settings.get_bool(settings_pack::use_read_cache) == false) return 0; @@ -2819,17 +2805,17 @@ namespace libtorrent if (no_pieces == false) { int block_size = m_disk_cache.block_size(); - + if (storage) { ret->pieces.reserve(storage->num_pieces()); - + for (boost::unordered_set::iterator i = storage->cached_pieces().begin(), end(storage->cached_pieces().end()); i != end; ++i) { TORRENT_ASSERT((*i)->storage.get() == storage); - + if ((*i)->cache_state == cached_piece_entry::read_lru2_ghost || (*i)->cache_state == cached_piece_entry::read_lru1_ghost) continue; @@ -2840,10 +2826,10 @@ namespace libtorrent else { ret->pieces.reserve(m_disk_cache.num_pieces()); - + std::pair range = m_disk_cache.all_pieces(); - + for (block_cache::iterator i = range.first; i != range.second; ++i) { if (i->cache_state == cached_piece_entry::read_lru2_ghost @@ -3444,7 +3430,7 @@ namespace libtorrent if (j->action == disk_io_job::read && m_settings.get_bool(settings_pack::use_read_cache) - && m_settings.get_int(settings_pack::cache_size) > 0) + && m_settings.get_int(settings_pack::cache_size) != 0) { int state = prep_read_job_impl(j, false); switch (state) diff --git a/src/enum_net.cpp b/src/enum_net.cpp index 01bf12341..9b0abab46 100644 --- a/src/enum_net.cpp +++ b/src/enum_net.cpp @@ -599,6 +599,7 @@ namespace libtorrent if (r == ERROR_BUFFER_OVERFLOW) { buffer.resize(buf_size); + adapter_addresses = reinterpret_cast(&buffer[0]); r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &buf_size); } diff --git a/src/kademlia/dht_storage.cpp b/src/kademlia/dht_storage.cpp index ddf8e26fd..57d2fd13c 100644 --- a/src/kademlia/dht_storage.cpp +++ b/src/kademlia/dht_storage.cpp @@ -152,7 +152,7 @@ namespace } } - // return true of the first argument is a better canidate for removal, i.e. + // return true of the first argument is a better candidate for removal, i.e. // less important to keep struct immutable_item_comparator { diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index ac991159c..db8f14d79 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -803,7 +803,7 @@ namespace libtorrent m_connected = false; if (!m_download_queue.empty()) m_counters.inc_stats_counter(counters::num_peers_down_requests, -1); - + // defensive boost::shared_ptr t = m_torrent.lock(); // if t is NULL, we better not be connecting, since @@ -990,13 +990,13 @@ namespace libtorrent TORRENT_ASSERT(is_single_thread()); return m_request_queue; } - + std::vector const& peer_connection::download_queue() const { TORRENT_ASSERT(is_single_thread()); return m_download_queue; } - + std::vector const& peer_connection::upload_queue() const { TORRENT_ASSERT(is_single_thread()); @@ -1878,7 +1878,7 @@ namespace libtorrent m_have_piece.set_bit(index); ++m_num_pieces; - // if the peer is downloading stuff, it must have metadata + // if the peer is downloading stuff, it must have metadata m_has_metadata = true; // only update the piece_picker if @@ -2576,9 +2576,9 @@ namespace libtorrent { check_postcondition(boost::shared_ptr const& t_ , bool init_check = true): t(t_) { if (init_check) check(); } - + ~check_postcondition() { check(); } - + void check() { if (!t->is_seed()) @@ -2596,7 +2596,7 @@ namespace libtorrent } } } - + shared_ptr t; }; #endif @@ -3229,7 +3229,7 @@ namespace libtorrent disconnect_if_redundant(); } - + // ----------------------------- // --------- HAVE NONE --------- // ----------------------------- @@ -3495,8 +3495,8 @@ namespace libtorrent if (t->alerts().should_post()) { - t->alerts().emplace_alert(t->get_handle(), - remote(), pid(), block.block_index, block.piece_index); + t->alerts().emplace_alert(t->get_handle() + , remote(), pid(), block.block_index, block.piece_index); } pending_block pb(block); @@ -3643,6 +3643,8 @@ namespace libtorrent TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; + TORRENT_ASSERT(!is_connecting()); + if (m_choked) { TORRENT_ASSERT(m_peer_info == NULL @@ -4082,6 +4084,15 @@ namespace libtorrent , op, ec.message().c_str()); break; } + + if (ec == error_code(boost::asio::error::eof + , boost::asio::error::get_misc_category()) + && !in_handshake() + && !is_connecting() + && clock_type::now() - connected_time() < seconds(15)) + { + peer_log(peer_log_alert::info, "SHORT_LIVED_DISCONNECT", ""); + } #endif if ((m_channel_state[upload_channel] & peer_info::bw_network) == 0) @@ -4955,8 +4966,8 @@ namespace libtorrent boost::int64_t piece_size = t->torrent_file().piece_length(); if (m_remote_dl_rate > 0) - m_remote_dl_rate = int((m_remote_dl_rate * 2 / 3) + - ((boost::int64_t(m_remote_pieces_dled) * piece_size / 3) / 60)); + m_remote_dl_rate = int((m_remote_dl_rate * 2 / 3) + + ((boost::int64_t(m_remote_pieces_dled) * piece_size / 3) / 60)); else m_remote_dl_rate = int(boost::int64_t(m_remote_pieces_dled) * piece_size / 60); @@ -5355,7 +5366,7 @@ namespace libtorrent disconnect(j->error.ec, op_file_read); return; } - + if (j->ret != r.length) { // handle_disk_error may disconnect us @@ -5715,7 +5726,7 @@ namespace libtorrent { return; } - + if (!can_read()) { #ifndef TORRENT_DISABLE_LOGGING @@ -5980,8 +5991,11 @@ namespace libtorrent if (error) { + TORRENT_ASSERT_VAL(error.value() != 0, error.value()); #ifndef TORRENT_DISABLE_LOGGING - peer_log(peer_log_alert::info, "ERROR", "in peer_connection::on_receive_data_nb error: %s" + peer_log(peer_log_alert::info, "ERROR" + , "in peer_connection::on_receive_data_nb error: (%s:%d) %s" + , error.category().name(), error.value() , error.message().c_str()); #endif on_receive(error, bytes_transferred); @@ -6106,8 +6120,10 @@ namespace libtorrent { TORRENT_ASSERT(is_single_thread()); #ifndef TORRENT_DISABLE_LOGGING - peer_log(peer_log_alert::incoming, "ON_RECEIVE_DATA", "bytes: %d error: %s" - , int(bytes_transferred), error.message().c_str()); + peer_log(peer_log_alert::incoming, "ON_RECEIVE_DATA" + , "bytes: %d error: (%s:%d) %s" + , int(bytes_transferred), error.category().name(), error.value() + , error.message().c_str()); #endif // submit all disk jobs later @@ -6836,7 +6852,7 @@ namespace libtorrent time_duration d; d = aux::time_now() - m_last_sent; if (total_seconds(d) < timeout() / 2) return; - + if (m_connecting) return; if (in_handshake()) return; diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index 4cfd34796..02f4d101f 100644 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -2823,8 +2823,6 @@ get_out: if (num_blocks <= 0) return 0; } - TORRENT_ASSERT(num_blocks >= 0 || prefer_contiguous_blocks > 0); - if (num_blocks <= 0) return 0; if (options & on_parole) return num_blocks; diff --git a/src/receive_buffer.cpp b/src/receive_buffer.cpp index e37aa051c..d5e86a67d 100644 --- a/src/receive_buffer.cpp +++ b/src/receive_buffer.cpp @@ -54,8 +54,12 @@ boost::asio::mutable_buffer receive_buffer::reserve(int size) { TORRENT_ASSERT(size > 0); TORRENT_ASSERT(!m_disk_recv_buffer); - m_recv_buffer.resize(m_recv_pos + size); - return boost::asio::buffer(&m_recv_buffer[m_recv_pos], size); + // this is unintuitive, but we used to use m_recv_pos in this function when + // we should have used m_recv_end. perhaps they always happen to be equal + TORRENT_ASSERT(m_recv_pos == m_recv_end); + + m_recv_buffer.resize(m_recv_end + size); + return boost::asio::buffer(&m_recv_buffer[0] + m_recv_end, size); } int receive_buffer::reserve(boost::array& vec, int size) @@ -64,41 +68,48 @@ int receive_buffer::reserve(boost::array& vec, i TORRENT_ASSERT(m_recv_pos >= 0); TORRENT_ASSERT(m_packet_size > 0); + // normalize() must be called before receiving more data + TORRENT_ASSERT(m_recv_start == 0); + + // this is unintuitive, but we used to use m_recv_pos in this function when + // we should have used m_recv_end. perhaps they always happen to be equal + TORRENT_ASSERT(m_recv_pos == m_recv_end); + int num_bufs; - int regular_buf_size = regular_buffer_size(); + int const regular_buf_size = regular_buffer_size(); if (int(m_recv_buffer.size()) < regular_buf_size) m_recv_buffer.resize(round_up8(regular_buf_size)); - if (!m_disk_recv_buffer || regular_buf_size >= m_recv_pos + size) + if (!m_disk_recv_buffer || regular_buf_size >= m_recv_end + size) { // only receive into regular buffer - TORRENT_ASSERT(m_recv_pos + size <= int(m_recv_buffer.size())); - vec[0] = boost::asio::buffer(&m_recv_buffer[m_recv_pos], size); + TORRENT_ASSERT(m_recv_end + size <= int(m_recv_buffer.size())); + vec[0] = boost::asio::buffer(&m_recv_buffer[0] + m_recv_end, size); TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) > 0); num_bufs = 1; } - else if (m_recv_pos >= regular_buf_size) + else if (m_recv_end >= regular_buf_size) { // only receive into disk buffer - TORRENT_ASSERT(m_recv_pos - regular_buf_size >= 0); - TORRENT_ASSERT(m_recv_pos - regular_buf_size + size <= m_disk_recv_buffer_size); - vec[0] = boost::asio::buffer(m_disk_recv_buffer.get() + m_recv_pos - regular_buf_size, size); + TORRENT_ASSERT(m_recv_end - regular_buf_size >= 0); + TORRENT_ASSERT(m_recv_end - regular_buf_size + size <= m_disk_recv_buffer_size); + vec[0] = boost::asio::buffer(m_disk_recv_buffer.get() + m_recv_end - regular_buf_size, size); TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) > 0); num_bufs = 1; } else { // receive into both regular and disk buffer - TORRENT_ASSERT(size + m_recv_pos > regular_buf_size); - TORRENT_ASSERT(m_recv_pos < regular_buf_size); + TORRENT_ASSERT(size + m_recv_end > regular_buf_size); + TORRENT_ASSERT(m_recv_end < regular_buf_size); TORRENT_ASSERT(size - regular_buf_size - + m_recv_pos <= m_disk_recv_buffer_size); + + m_recv_end <= m_disk_recv_buffer_size); - vec[0] = boost::asio::buffer(&m_recv_buffer[m_recv_pos] - , regular_buf_size - m_recv_pos); + vec[0] = boost::asio::buffer(&m_recv_buffer[0] + m_recv_end + , regular_buf_size - m_recv_end); vec[1] = boost::asio::buffer(m_disk_recv_buffer.get() - , size - regular_buf_size + m_recv_pos); + , size - regular_buf_size + m_recv_end); TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) + boost::asio::buffer_size(vec[1])> 0); num_bufs = 2; @@ -109,9 +120,9 @@ int receive_buffer::reserve(boost::array& vec, i int receive_buffer::advance_pos(int bytes) { - int packet_size = m_soft_packet_size ? m_soft_packet_size : m_packet_size; - int limit = packet_size > m_recv_pos ? packet_size - m_recv_pos : packet_size; - int sub_transferred = (std::min)(bytes, limit); + int const packet_size = m_soft_packet_size ? m_soft_packet_size : m_packet_size; + int const limit = packet_size > m_recv_pos ? packet_size - m_recv_pos : packet_size; + int const sub_transferred = (std::min)(bytes, limit); m_recv_pos += sub_transferred; if (m_recv_pos >= m_soft_packet_size) m_soft_packet_size = 0; return sub_transferred; @@ -174,7 +185,8 @@ buffer::const_interval receive_buffer::get() const TORRENT_ASSERT(m_recv_pos == 0); return buffer::interval(0,0); } - int rcv_pos = (std::min)(m_recv_pos, int(m_recv_buffer.size())); + + int rcv_pos = (std::min)(m_recv_pos, int(m_recv_buffer.size()) - m_recv_start); return buffer::const_interval(&m_recv_buffer[0] + m_recv_start , &m_recv_buffer[0] + m_recv_start + rcv_pos); } @@ -194,34 +206,48 @@ buffer::interval receive_buffer::mutable_buffer() , &m_recv_buffer[0] + m_recv_start + rcv_pos); } -void receive_buffer::mutable_buffers(std::vector& vec, int bytes) +// TODO: 2 should this take a boost::array<..., 2> instead? it could return the +// number of buffers added, just like reserve. +void receive_buffer::mutable_buffers(std::vector& vec, int const bytes) { - using namespace boost; + namespace asio = boost::asio; + + // bytes is the number of bytes we just received, and m_recv_pos has + // already been adjusted for these bytes. The receive pos immediately + // before we received these bytes was (m_recv_pos - bytes) + + int const last_recv_pos = m_recv_pos - bytes; TORRENT_ASSERT(bytes <= m_recv_pos); - int regular_buf_size = regular_buffer_size(); + // the number of bytes in the current packet that are being received into a + // regular receive buffer (as opposed to a disk cache buffer) + int const regular_buf_size = regular_buffer_size(); + TORRENT_ASSERT(regular_buf_size >= 0); if (!m_disk_recv_buffer || regular_buf_size >= m_recv_pos) { + // we just received into a regular disk buffer vec.push_back(asio::mutable_buffer(&m_recv_buffer[0] + m_recv_start - + m_recv_pos - bytes, bytes)); + + last_recv_pos, bytes)); } - else if (m_recv_pos - bytes >= regular_buf_size) + else if (last_recv_pos >= regular_buf_size) { - vec.push_back(asio::mutable_buffer(m_disk_recv_buffer.get() + m_recv_pos - - regular_buf_size - bytes, bytes)); + // we only received into a disk buffer + vec.push_back(asio::mutable_buffer(m_disk_recv_buffer.get() + + last_recv_pos - regular_buf_size, bytes)); } else { - TORRENT_ASSERT(m_recv_pos - bytes < regular_buf_size); + // we received into a regular and a disk buffer + TORRENT_ASSERT(last_recv_pos < regular_buf_size); TORRENT_ASSERT(m_recv_pos > regular_buf_size); - vec.push_back(asio::mutable_buffer(&m_recv_buffer[0] + m_recv_start + m_recv_pos - bytes - , regular_buf_size - (m_recv_start + m_recv_pos - bytes))); + vec.push_back(asio::mutable_buffer(&m_recv_buffer[0] + m_recv_start + last_recv_pos + , regular_buf_size - last_recv_pos)); vec.push_back(asio::mutable_buffer(m_disk_recv_buffer.get() , m_recv_pos - regular_buf_size)); } -#if defined TORRENT_DEBUG || defined TORRENT_RELEASE_ASSERTS +#if TORRENT_USE_ASSERTS int vec_bytes = 0; for (std::vector::iterator i = vec.begin(); i != vec.end(); ++i) diff --git a/src/session_handle.cpp b/src/session_handle.cpp index 2cd0e06a6..498c63222 100644 --- a/src/session_handle.cpp +++ b/src/session_handle.cpp @@ -106,11 +106,12 @@ namespace libtorrent TORRENT_SYNC_CALL2(save_state, &e, flags); } - void session_handle::load_state(bdecode_node const& e) + void session_handle::load_state(bdecode_node const& e + , boost::uint32_t const flags) { // this needs to be synchronized since the lifespan // of e is tied to the caller - TORRENT_SYNC_CALL1(load_state, &e); + TORRENT_SYNC_CALL2(load_state, &e, flags); } void session_handle::get_torrent_status(std::vector* ret @@ -607,7 +608,7 @@ namespace libtorrent void session_handle::start_dht(entry const& startup_state) { #ifndef TORRENT_DISABLE_DHT - TORRENT_ASYNC_CALL1(start_dht, startup_state); + TORRENT_ASYNC_CALL1(start_dht_deprecated, startup_state); #else TORRENT_UNUSED(startup_state); #endif @@ -644,7 +645,8 @@ namespace libtorrent void session_handle::load_country_db(wchar_t const*) {} #endif // TORRENT_USE_WSTRING - void session_handle::load_state(entry const& ses_state) + void session_handle::load_state(entry const& ses_state + , boost::uint32_t const flags) { if (ses_state.type() == entry::undefined_t) return; std::vector buf; @@ -660,7 +662,7 @@ namespace libtorrent #ifndef BOOST_NO_EXCEPTIONS if (ret != 0) throw libtorrent_exception(ec); #endif - TORRENT_SYNC_CALL1(load_state, &e); + TORRENT_SYNC_CALL2(load_state, &e, flags); } entry session_handle::state() const @@ -670,7 +672,8 @@ namespace libtorrent return ret; } - void session_handle::load_state(lazy_entry const& ses_state) + void session_handle::load_state(lazy_entry const& ses_state + , boost::uint32_t const flags) { if (ses_state.type() == lazy_entry::none_t) return; std::pair buf = ses_state.data_section(); @@ -685,7 +688,7 @@ namespace libtorrent #ifndef BOOST_NO_EXCEPTIONS if (ret != 0) throw libtorrent_exception(ec); #endif - TORRENT_SYNC_CALL1(load_state, &e); + TORRENT_SYNC_CALL2(load_state, &e, flags); } #endif // TORRENT_NO_DEPRECATE diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 6bd3b2b57..9dd264305 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -640,12 +640,16 @@ namespace aux { void session_impl::save_state(entry* eptr, boost::uint32_t flags) const { TORRENT_ASSERT(is_single_thread()); - TORRENT_UNUSED(flags); // potentially unused entry& e = *eptr; + // make it a dict + e.dict(); - entry::dictionary_type& sett = e["settings"].dict(); - save_settings_to_dict(m_settings, sett); + if (flags & session::save_settings) + { + entry::dictionary_type& sett = e["settings"].dict(); + save_settings_to_dict(m_settings, sett); + } #ifndef TORRENT_DISABLE_DHT if (flags & session::save_dht_settings) @@ -707,7 +711,8 @@ namespace aux { return proxy_settings(m_settings); } - void session_impl::load_state(bdecode_node const* e) + void session_impl::load_state(bdecode_node const* e + , boost::uint32_t const flags = 0xffffffff) { TORRENT_ASSERT(is_single_thread()); @@ -717,77 +722,86 @@ namespace aux { #ifndef TORRENT_DISABLE_DHT bool need_update_dht = false; // load from the old settings names - settings = e->dict_find_dict("dht"); - if (settings) + if (flags & session::save_dht_settings) { - bdecode_node val; - val = settings.dict_find_int("max_peers_reply"); - if (val) m_dht_settings.max_peers_reply = val.int_value(); - val = settings.dict_find_int("search_branching"); - if (val) m_dht_settings.search_branching = val.int_value(); - val = settings.dict_find_int("max_fail_count"); - if (val) m_dht_settings.max_fail_count = val.int_value(); - val = settings.dict_find_int("max_torrents"); - if (val) m_dht_settings.max_torrents = val.int_value(); - val = settings.dict_find_int("max_dht_items"); - if (val) m_dht_settings.max_dht_items = val.int_value(); - val = settings.dict_find_int("max_peers"); - if (val) m_dht_settings.max_peers = val.int_value(); - val = settings.dict_find_int("max_torrent_search_reply"); - if (val) m_dht_settings.max_torrent_search_reply = val.int_value(); - val = settings.dict_find_int("restrict_routing_ips"); - if (val) m_dht_settings.restrict_routing_ips = val.int_value(); - val = settings.dict_find_int("restrict_search_ips"); - if (val) m_dht_settings.restrict_search_ips = val.int_value(); - val = settings.dict_find_int("extended_routing_table"); - if (val) m_dht_settings.extended_routing_table = val.int_value(); - val = settings.dict_find_int("aggressive_lookups"); - if (val) m_dht_settings.aggressive_lookups = val.int_value(); - val = settings.dict_find_int("privacy_lookups"); - if (val) m_dht_settings.privacy_lookups = val.int_value(); - val = settings.dict_find_int("enforce_node_id"); - if (val) m_dht_settings.enforce_node_id = val.int_value(); - val = settings.dict_find_int("ignore_dark_internet"); - if (val) m_dht_settings.ignore_dark_internet = val.int_value(); - val = settings.dict_find_int("block_timeout"); - if (val) m_dht_settings.block_timeout = val.int_value(); - val = settings.dict_find_int("block_ratelimit"); - if (val) m_dht_settings.block_ratelimit = val.int_value(); - val = settings.dict_find_int("read_only"); - if (val) m_dht_settings.read_only = val.int_value(); - val = settings.dict_find_int("item_lifetime"); - if (val) m_dht_settings.item_lifetime = val.int_value(); + settings = e->dict_find_dict("dht"); + if (settings) + { + bdecode_node val; + val = settings.dict_find_int("max_peers_reply"); + if (val) m_dht_settings.max_peers_reply = val.int_value(); + val = settings.dict_find_int("search_branching"); + if (val) m_dht_settings.search_branching = val.int_value(); + val = settings.dict_find_int("max_fail_count"); + if (val) m_dht_settings.max_fail_count = val.int_value(); + val = settings.dict_find_int("max_torrents"); + if (val) m_dht_settings.max_torrents = val.int_value(); + val = settings.dict_find_int("max_dht_items"); + if (val) m_dht_settings.max_dht_items = val.int_value(); + val = settings.dict_find_int("max_peers"); + if (val) m_dht_settings.max_peers = val.int_value(); + val = settings.dict_find_int("max_torrent_search_reply"); + if (val) m_dht_settings.max_torrent_search_reply = val.int_value(); + val = settings.dict_find_int("restrict_routing_ips"); + if (val) m_dht_settings.restrict_routing_ips = val.int_value(); + val = settings.dict_find_int("restrict_search_ips"); + if (val) m_dht_settings.restrict_search_ips = val.int_value(); + val = settings.dict_find_int("extended_routing_table"); + if (val) m_dht_settings.extended_routing_table = val.int_value(); + val = settings.dict_find_int("aggressive_lookups"); + if (val) m_dht_settings.aggressive_lookups = val.int_value(); + val = settings.dict_find_int("privacy_lookups"); + if (val) m_dht_settings.privacy_lookups = val.int_value(); + val = settings.dict_find_int("enforce_node_id"); + if (val) m_dht_settings.enforce_node_id = val.int_value(); + val = settings.dict_find_int("ignore_dark_internet"); + if (val) m_dht_settings.ignore_dark_internet = val.int_value(); + val = settings.dict_find_int("block_timeout"); + if (val) m_dht_settings.block_timeout = val.int_value(); + val = settings.dict_find_int("block_ratelimit"); + if (val) m_dht_settings.block_ratelimit = val.int_value(); + val = settings.dict_find_int("read_only"); + if (val) m_dht_settings.read_only = val.int_value(); + val = settings.dict_find_int("item_lifetime"); + if (val) m_dht_settings.item_lifetime = val.int_value(); + } } - settings = e->dict_find_dict("dht state"); - if (settings) + if (flags & session::save_dht_state) { - m_dht_state = settings; - need_update_dht = true; + settings = e->dict_find_dict("dht state"); + if (settings) + { + m_dht_state = settings; + need_update_dht = true; + } } #endif #ifndef TORRENT_NO_DEPRECATE bool need_update_proxy = false; - settings = e->dict_find_dict("proxy"); - if (settings) + if (flags & session::save_proxy) { - bdecode_node val; - val = settings.dict_find_int("port"); - if (val) m_settings.set_int(settings_pack::proxy_port, val.int_value()); - val = settings.dict_find_int("type"); - if (val) m_settings.set_int(settings_pack::proxy_type, val.int_value()); - val = settings.dict_find_int("proxy_hostnames"); - if (val) m_settings.set_bool(settings_pack::proxy_hostnames, val.int_value()); - val = settings.dict_find_int("proxy_peer_connections"); - if (val) m_settings.set_bool(settings_pack::proxy_peer_connections, val.int_value()); - val = settings.dict_find_string("hostname"); - if (val) m_settings.set_str(settings_pack::proxy_hostname, val.string_value()); - val = settings.dict_find_string("password"); - if (val) m_settings.set_str(settings_pack::proxy_password, val.string_value()); - val = settings.dict_find_string("username"); - if (val) m_settings.set_str(settings_pack::proxy_username, val.string_value()); - need_update_proxy = true; + settings = e->dict_find_dict("proxy"); + if (settings) + { + bdecode_node val; + val = settings.dict_find_int("port"); + if (val) m_settings.set_int(settings_pack::proxy_port, val.int_value()); + val = settings.dict_find_int("type"); + if (val) m_settings.set_int(settings_pack::proxy_type, val.int_value()); + val = settings.dict_find_int("proxy_hostnames"); + if (val) m_settings.set_bool(settings_pack::proxy_hostnames, val.int_value()); + val = settings.dict_find_int("proxy_peer_connections"); + if (val) m_settings.set_bool(settings_pack::proxy_peer_connections, val.int_value()); + val = settings.dict_find_string("hostname"); + if (val) m_settings.set_str(settings_pack::proxy_hostname, val.string_value()); + val = settings.dict_find_string("password"); + if (val) m_settings.set_str(settings_pack::proxy_password, val.string_value()); + val = settings.dict_find_string("username"); + if (val) m_settings.set_str(settings_pack::proxy_username, val.string_value()); + need_update_proxy = true; + } } settings = e->dict_find_dict("encryption"); @@ -804,39 +818,49 @@ namespace aux { if (val) m_settings.set_int(settings_pack::allowed_enc_level, val.int_value()); } - settings = e->dict_find_list("feeds"); - if (settings) + if (flags & session::save_feeds) { - m_feeds.reserve(settings.list_size()); - for (int i = 0; i < settings.list_size(); ++i) + settings = e->dict_find_list("feeds"); + if (settings) { - if (settings.list_at(i).type() != bdecode_node::dict_t) continue; - boost::shared_ptr f(new_feed(*this, feed_settings())); - f->load_state(settings.list_at(i)); - f->update_feed(); - m_feeds.push_back(f); + m_feeds.reserve(settings.list_size()); + for (int i = 0; i < settings.list_size(); ++i) + { + if (settings.list_at(i).type() != bdecode_node::dict_t) continue; + boost::shared_ptr f(new_feed(*this, feed_settings())); + f->load_state(settings.list_at(i)); + f->update_feed(); + m_feeds.push_back(f); + } + update_rss_feeds(); } - update_rss_feeds(); } #endif - settings = e->dict_find_dict("settings"); - if (settings) - { - // apply_settings_pack will update dht and proxy - boost::shared_ptr pack = load_pack_from_dict(settings); - apply_settings_pack(pack); - } - else + if (flags & session::save_settings) { + settings = e->dict_find_dict("settings"); + if (settings) + { + // apply_settings_pack will update dht and proxy + boost::shared_ptr pack = load_pack_from_dict(settings); + apply_settings_pack(pack); #ifndef TORRENT_DISABLE_DHT - if (need_update_dht) update_dht(); + need_update_dht = false; #endif #ifndef TORRENT_NO_DEPRECATE - if (need_update_proxy) update_proxy(); + need_update_proxy = false; #endif + } } +#ifndef TORRENT_DISABLE_DHT + if (need_update_dht) update_dht(); +#endif +#ifndef TORRENT_NO_DEPRECATE + if (need_update_proxy) update_proxy(); +#endif + #ifndef TORRENT_DISABLE_EXTENSIONS for (ses_extension_list_t::iterator i = m_ses_extensions.begin() , end(m_ses_extensions.end()); i != end; ++i) @@ -3663,7 +3687,8 @@ namespace aux { t->log_to_all_peers("auto manager pausing torrent"); #endif // use graceful pause for auto-managed torrents - t->set_allow_peers(false, true); + t->set_allow_peers(false, torrent::flag_graceful_pause + | torrent::flag_clear_disk_cache); t->set_announce_to_dht(false); t->set_announce_to_trackers(false); t->set_announce_to_lsd(false); @@ -4141,6 +4166,7 @@ namespace aux { int const allowed_upload_slots = unchoke_sort(peers, max_upload_rate , unchoke_interval, m_settings); + m_stats_counters.set_value(counters::num_unchoke_slots , allowed_upload_slots); @@ -5594,6 +5620,12 @@ namespace aux { if (!m_dht) return entry(); return m_dht->state(); } + + void session_impl::start_dht_deprecated(entry const& startup_state) + { + m_settings.set_bool(settings_pack::enable_dht, true); + start_dht(startup_state); + } #endif void session_impl::add_dht_node_name(std::pair const& node) @@ -6024,9 +6056,7 @@ namespace aux { void session_impl::update_unchoke_limit() { - int unchoke_limit = m_settings.get_int(settings_pack::unchoke_slots_limit); - - int allowed_upload_slots = unchoke_limit; + int allowed_upload_slots = m_settings.get_int(settings_pack::unchoke_slots_limit); if (allowed_upload_slots < 0) allowed_upload_slots = (std::numeric_limits::max)(); diff --git a/src/session_stats.cpp b/src/session_stats.cpp index d35219d43..0ac6e7e2b 100644 --- a/src/session_stats.cpp +++ b/src/session_stats.cpp @@ -355,10 +355,10 @@ namespace libtorrent // the total number of blocks run through SHA-1 hashing METRIC(disk, num_blocks_hashed) - + // the number of blocks read from the disk cache METRIC(disk, num_blocks_cache_hits) - + // the number of disk I/O operation for reads and writes. One disk // operation may transfer more then one block. METRIC(disk, num_write_ops) diff --git a/src/settings_pack.cpp b/src/settings_pack.cpp index ad4a8b566..d37e86871 100644 --- a/src/settings_pack.cpp +++ b/src/settings_pack.cpp @@ -204,7 +204,7 @@ namespace libtorrent SET(support_merkle_torrents, true, 0), SET(report_redundant_bytes, true, 0), SET_NOPREV(listen_system_port_fallback, true, 0), - SET(use_disk_cache_pool, false, 0), + SET(use_disk_cache_pool, true, 0), SET_NOPREV(announce_crypto_support, true, 0), SET_NOPREV(enable_upnp, true, &session_impl::update_upnp), SET_NOPREV(enable_natpmp, true, &session_impl::update_natpmp), @@ -237,7 +237,7 @@ namespace libtorrent SET(max_failcount, 3, &session_impl::update_max_failcount), SET(min_reconnect_time, 60, 0), SET(peer_connect_timeout, 15, 0), - SET(connection_speed, 6, &session_impl::update_connection_speed), + SET(connection_speed, 10, &session_impl::update_connection_speed), SET(inactivity_timeout, 600, 0), SET(unchoke_interval, 15, 0), SET(optimistic_unchoke_interval, 30, 0), diff --git a/src/torrent.cpp b/src/torrent.cpp index fc0313be9..c51265b8f 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1173,6 +1173,8 @@ namespace libtorrent TORRENT_UNUSED(j); TORRENT_UNUSED(b); + if (m_abort) return; + update_gauge(); // some peers that previously was no longer interesting may // now have become interesting, since we lack this one piece now. @@ -2201,6 +2203,8 @@ namespace libtorrent return; } + if (m_abort) return; + state_updated(); if (m_add_torrent_params) @@ -2447,6 +2451,8 @@ namespace libtorrent dec_refcount("force_recheck"); state_updated(); + if (m_abort) return; + if (j->ret == piece_manager::fatal_disk_error) { handle_disk_error(j); @@ -2476,7 +2482,10 @@ namespace libtorrent int num_outstanding = settings().get_int(settings_pack::checking_mem_usage) * block_size() / m_torrent_file->piece_length(); - if (num_outstanding <= 0) num_outstanding = 1; + // if we only keep a single read operation in-flight at a time, we suffer + // significant performance degradation. Always keep at least two jobs + // outstanding + if (num_outstanding < 2) num_outstanding = 2; // we might already have some outstanding jobs, if we were paused and // resumed quickly, before the outstanding jobs completed @@ -2527,6 +2536,8 @@ namespace libtorrent dec_refcount("start_checking"); + if (m_abort) return; + if (j->ret == piece_manager::disk_check_aborted) { m_checking_piece = 0; @@ -2668,7 +2679,9 @@ namespace libtorrent { // if we're auto managed. assume we need to be paused until the auto // managed logic runs again (which is triggered further down) - pause(); + // setting flags to 0 prevents the disk cache from being evicted as a + // result of this + set_allow_peers(false, 0); } // we're done checking! (this should cause a call to trigger_auto_manage) @@ -2844,6 +2857,7 @@ namespace libtorrent , int(peers.size())); #endif + if (m_abort) return; if (peers.empty()) return; if (m_ses.alerts().should_post()) @@ -3554,7 +3568,7 @@ namespace libtorrent if (ec) debug_log("i2p_resolve error: %s", ec.message().c_str()); #endif - if (ec || m_ses.is_aborted()) return; + if (ec || m_abort || m_ses.is_aborted()) return; need_peer_list(); torrent_state st = get_peer_list_state(); @@ -3580,7 +3594,7 @@ namespace libtorrent debug_log("peer name lookup error: %s", e.message().c_str()); #endif - if (e || host_list.empty() || m_ses.is_aborted()) return; + if (e || m_abort || host_list.empty() || m_ses.is_aborted()) return; // TODO: add one peer per IP the hostname resolves to tcp::endpoint host(host_list.front(), port); @@ -3926,6 +3940,8 @@ namespace libtorrent dec_refcount("verify_piece"); + if (m_abort) return; + int ret = j->ret; if (settings().get_bool(settings_pack::disable_hash_checks)) { @@ -4788,6 +4804,9 @@ namespace libtorrent // seeded by any peer TORRENT_ASSERT(m_super_seeding); + if (!need_loaded()) + return -1; + // do a linear search from the first piece int min_availability = 9999; std::vector avail_vec; @@ -4820,6 +4839,7 @@ namespace libtorrent avail_vec.push_back(i); } + if (avail_vec.empty()) return -1; return avail_vec[random() % avail_vec.size()]; } @@ -9150,10 +9170,11 @@ namespace libtorrent set_need_save_resume(); } - set_allow_peers(false, graceful); + int const flags = graceful ? flag_graceful_pause : 0; + set_allow_peers(false, flags | flag_clear_disk_cache); } - void torrent::do_pause() + void torrent::do_pause(bool const clear_disk_cache) { TORRENT_ASSERT(is_single_thread()); if (!is_paused()) return; @@ -9217,7 +9238,7 @@ namespace libtorrent { // this will make the storage close all // files and flush all cached data - if (m_storage.get()) + if (m_storage.get() && clear_disk_cache) { TORRENT_ASSERT(m_storage); m_ses.disk_thread().async_stop_torrent(m_storage.get() @@ -9317,7 +9338,7 @@ namespace libtorrent set_need_save_resume(); } - void torrent::set_allow_peers(bool b, bool graceful) + void torrent::set_allow_peers(bool b, int flags) { TORRENT_ASSERT(is_single_thread()); @@ -9327,7 +9348,7 @@ namespace libtorrent // if there are no peers, we must not enter graceful pause mode, and post // the torrent_paused_alert immediately instead. if (m_connections.empty()) - graceful = false; + flags &= ~flag_graceful_pause; if (m_allow_peers == b) { @@ -9336,9 +9357,9 @@ namespace libtorrent // paused mode, we need to actually pause the torrent properly if (m_allow_peers == false && m_graceful_pause_mode == true - && graceful == false) + && (flags & flag_graceful_pause) == 0) { - m_graceful_pause_mode = graceful; + m_graceful_pause_mode = false; update_gauge(); do_pause(); } @@ -9347,7 +9368,7 @@ namespace libtorrent m_allow_peers = b; if (!m_ses.is_paused()) - m_graceful_pause_mode = graceful; + m_graceful_pause_mode = (flags & flag_graceful_pause) ? true : false; if (!b) { @@ -9364,7 +9385,7 @@ namespace libtorrent if (!b) { - do_pause(); + do_pause(flags & flag_clear_disk_cache); } else { @@ -10016,6 +10037,7 @@ namespace libtorrent update_want_peers(); } + // TODO: 2 this should probably be removed void torrent::refresh_explicit_cache(int cache_size) { TORRENT_ASSERT(is_single_thread()); diff --git a/src/utp_stream.cpp b/src/utp_stream.cpp index f1311df1e..318bd8972 100644 --- a/src/utp_stream.cpp +++ b/src/utp_stream.cpp @@ -923,8 +923,11 @@ int utp_stream::read_buffer_size() const void utp_stream::on_close_reason(void* self, boost::uint16_t close_reason) { utp_stream* s = static_cast(self); - TORRENT_ASSERT(s->m_impl); - s->m_incoming_close_reason = close_reason; + + // it's possible the socket has been unlinked already, in which case m_impl + // will be NULL + if (s->m_impl) + s->m_incoming_close_reason = close_reason; } void utp_stream::on_read(void* self, size_t bytes_transferred diff --git a/test/Jamfile b/test/Jamfile index 4957244d2..2254503a6 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -154,6 +154,7 @@ test-suite libtorrent : test_linked_list.cpp test_file_progress.cpp ] + [ run test_receive_buffer.cpp ] [ run test_alert_manager.cpp ] [ run test_direct_dht.cpp ] [ run test_magnet.cpp ] @@ -194,8 +195,8 @@ test-suite libtorrent : [ run test_priority.cpp ] # turn these tests into simulations -# [ run test_upnp.cpp ] -# [ run test_lsd.cpp ] + [ run test_upnp.cpp ] + [ run test_lsd.cpp ] ; # these are the tests run on appveyor, while the flapping ones are being diff --git a/test/Makefile.am b/test/Makefile.am index e99ba9339..42b722636 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -18,6 +18,7 @@ test_programs = \ test_pe_crypto \ test_pex \ test_read_piece \ + test_receive_buffer \ test_resume \ test_read_resume \ test_ssl \ diff --git a/test/test_checking.cpp b/test/test_checking.cpp index d19386a9d..f78f3b04b 100644 --- a/test/test_checking.cpp +++ b/test/test_checking.cpp @@ -94,7 +94,7 @@ void test_checking(int flags = read_only_files) create_directory(combine_path("tmp1_checking", "test_torrent_dir"), ec); if (ec) fprintf(stderr, "ERROR: creating directory test_torrent_dir: (%d) %s\n" , ec.value(), ec.message().c_str()); - + file_storage fs; std::srand(10); int piece_size = 0x4000; diff --git a/test/test_receive_buffer.cpp b/test/test_receive_buffer.cpp new file mode 100644 index 000000000..7059864a0 --- /dev/null +++ b/test/test_receive_buffer.cpp @@ -0,0 +1,255 @@ +/* + +Copyright (c) 2016, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "test.hpp" +#include "libtorrent/receive_buffer.hpp" + +using namespace libtorrent; + +struct allocator : buffer_allocator_interface +{ + void free_disk_buffer(char*) {} + char* allocate_disk_buffer(char const*) { TORRENT_ASSERT(false); return NULL; } + char* allocate_disk_buffer(bool& + , boost::shared_ptr + , char const*) { TORRENT_ASSERT(false); return NULL; } + char* async_allocate_disk_buffer(char const* + , boost::function const&) { TORRENT_ASSERT(false); return NULL; } + void reclaim_block(block_cache_reference ref) {} +}; + +TORRENT_TEST(recv_buffer_init) +{ + allocator a; + receive_buffer b(a); + + b.cut(0, 10); + + TEST_EQUAL(b.packet_size(), 10); + TEST_EQUAL(b.packet_bytes_remaining(), 10); + TEST_EQUAL(b.packet_finished(), false); + TEST_EQUAL(b.pos(), 0); + TEST_EQUAL(b.capacity(), 0); +} + +TORRENT_TEST(recv_buffer_pos_at_end_false) +{ + allocator a; + receive_buffer b(a); + + b.cut(0, 1000); + // allocate some space to receive into + boost::array vec; + int num_bufs = b.reserve(vec, 1000); + + // since we don't have a disk buffer, there should only be a single + // range/buffer + TEST_EQUAL(num_bufs, 1); + + b.received(1000); + b.advance_pos(999); + + TEST_EQUAL(b.pos_at_end(), false); +} + +TORRENT_TEST(recv_buffer_pos_at_end_true) +{ + allocator a; + receive_buffer b(a); + b.cut(0, 1000); + b.reserve(1000); + boost::array vec; + int num_bufs = b.reserve(vec, 1000); + TEST_EQUAL(num_bufs, 1); + b.received(1000); + b.advance_pos(1000); + TEST_EQUAL(b.pos_at_end(), true); +} + +TORRENT_TEST(recv_buffer_packet_finished) +{ + allocator a; + receive_buffer b(a); + // packet_size = 10 + b.cut(0, 10); + b.reserve(1000); + boost::array vec; + int num_bufs = b.reserve(vec, 1000); + TEST_EQUAL(num_bufs, 1); + b.received(1000); + + for (int i = 0; i < 10; ++i) + { + TEST_EQUAL(b.packet_finished(), false); + b.advance_pos(1); + } + TEST_EQUAL(b.packet_finished(), true); +} + +TORRENT_TEST(recv_buffer_disk_buffer) +{ + char disk_buffer; // fake disk buffer pointer + + allocator a; + receive_buffer b(a); + b.reserve(1000); + b.cut(0, 1000); // packet size = 1000 + boost::array vec; + b.assign_disk_buffer(&disk_buffer, 137); + int num_bufs = b.reserve(vec, 1000); + TEST_EQUAL(num_bufs, 2); + + // regular buffer disk buffer + // -----------------====== + // + // |----------------------| 1000 + // |-----| 137 + // |----------------| 863 + + TEST_EQUAL(boost::asio::buffer_size(vec[0]), 863); + TEST_EQUAL(boost::asio::buffer_size(vec[1]), 137); +} + +#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) + +TORRENT_TEST(recv_buffer_mutable_buffers_regular_and_disk) +{ + char disk_buffer; // fake disk buffer pointer + + allocator a; + receive_buffer b(a); + b.reserve(1100); + b.cut(0, 100); // packet size = 100 + b.received(1100); + int packet_transferred = b.advance_pos(1100); + // this is just the first packet + TEST_EQUAL(packet_transferred, 100); + // the next packet is 1000, and we're done with the first 100 bytes now + b.cut(100, 1000); // packet size = 1000 + // and it has a disk buffer + b.assign_disk_buffer(&disk_buffer, 137); + std::vector vec; + packet_transferred = b.advance_pos(999); + TEST_EQUAL(packet_transferred, 999); + b.mutable_buffers(vec, 999); + TEST_EQUAL(vec.size(), 2); + + // previous packet + // | + // v regular buffer disk buffer + // - - - -----------------====== + // ^ + // | + // m_recv_start + + // |----------------------| 1000 packet size + // |-----| 137 disk buffer + // |----------------| 863 regular buffer + + TEST_EQUAL(boost::asio::buffer_size(vec[0]), 863); + TEST_EQUAL(boost::asio::buffer_size(vec[1]), 137 - 1); + TEST_EQUAL(boost::asio::buffer_size(vec[0]) + + boost::asio::buffer_size(vec[1]), 999); +} + +TORRENT_TEST(recv_buffer_mutable_buffers_regular_only) +{ + allocator a; + receive_buffer b(a); + b.reserve(1100); + b.cut(0, 100); // packet size = 100 + b.received(1100); + int packet_transferred = b.advance_pos(1100); + // this is just the first packet + TEST_EQUAL(packet_transferred, 100); + // the next packet is 1000, and we're done with the first 100 bytes now + b.cut(100, 1000); // packet size = 1000 + std::vector vec; + packet_transferred = b.advance_pos(999); + TEST_EQUAL(packet_transferred, 999); + b.mutable_buffers(vec, 999); + TEST_EQUAL(vec.size(), 1); + + // previous packet + // | + // v regular buffer + // - - - ----------------------- + // ^ + // | + // m_recv_start + + // |----------------------| 1000 packet size + // |---------------------| 999 regular buffer + + TEST_EQUAL(boost::asio::buffer_size(vec[0]), 999); +} + +TORRENT_TEST(recv_buffer_mutable_buffers_disk) +{ + char disk_buffer; // fake disk buffer pointer + + allocator a; + receive_buffer b(a); + b.reserve(1100); + b.cut(0, 100); // packet size = 100 + b.received(1100); + int packet_transferred = b.advance_pos(1100); + // this is just the first packet + TEST_EQUAL(packet_transferred, 100); + // the next packet is 1000, and we're done with the first 100 bytes now + b.cut(100, 1000); // packet size = 1000 + // and it has a disk buffer + b.assign_disk_buffer(&disk_buffer, 1000); + std::vector vec; + packet_transferred = b.advance_pos(999); + TEST_EQUAL(packet_transferred, 999); + b.mutable_buffers(vec, 999); + TEST_EQUAL(vec.size(), 1); + + // previous packet + // | + // v disk buffer + // - - - ======================= + // ^ + // | + // m_recv_start + + // |----------------------| 1000 packet size + // |----------------------| 999 disk buffer + + TEST_EQUAL(boost::asio::buffer_size(vec[0]), 999); + TEST_EQUAL(boost::asio::buffer_cast(vec[0]), &disk_buffer); +} + +#endif + diff --git a/test/test_recheck.cpp b/test/test_recheck.cpp index d35293d1b..4b290e7ee 100644 --- a/test/test_recheck.cpp +++ b/test/test_recheck.cpp @@ -55,12 +55,20 @@ const int mask = alert::all_categories & ~(alert::performance_warning | alert::s void wait_for_complete(lt::session& ses, torrent_handle h) { - for (int i = 0; i < 70; ++i) + int last_progress = 0; + clock_type::time_point last_change = clock_type::now(); + for (int i = 0; i < 400; ++i) { print_alerts(ses, "ses1"); torrent_status st = h.status(); fprintf(stderr, "%f %%\n", st.progress_ppm / 10000.f); if (st.progress_ppm == 1000000) return; + if (st.progress_ppm != last_progress) + { + last_progress = st.progress_ppm; + last_change = clock_type::now(); + } + if (clock_type::now() - last_change > seconds(10)) break; test_sleep(500); } TEST_ERROR("torrent did not finish"); @@ -107,3 +115,4 @@ TORRENT_TEST(recheck) TEST_CHECK(st1.progress_ppm <= 1000000); wait_for_complete(ses1, tor1); } + diff --git a/test/test_session.cpp b/test/test_session.cpp index 7b30365bc..2c6e4d745 100644 --- a/test/test_session.cpp +++ b/test/test_session.cpp @@ -38,6 +38,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert_types.hpp" #include "libtorrent/session_stats.hpp" #include "libtorrent/performance_counters.hpp" +#include "libtorrent/bdecode.hpp" +#include "libtorrent/bencode.hpp" using namespace libtorrent; namespace lt = libtorrent; @@ -105,4 +107,107 @@ TORRENT_TEST(session_stats) TEST_EQUAL(stats[i].value_index, i); } } +#if __cplusplus >= 201103L + +template +void test_save_restore(Set setup, Save s, Default d, Load l) +{ + entry st; + { + settings_pack p; + setup(p); + lt::session ses(p); + s(ses, st); + } + + { + settings_pack p; + d(p); + lt::session ses(p); + // the loading function takes a bdecode_node, so we have to transform the + // entry + printf("%s\n", st.to_string().c_str()); + std::vector buf; + bencode(std::back_inserter(buf), st); + bdecode_node state; + error_code ec; + int ret = bdecode(buf.data(), buf.data() + buf.size() + , state, ec, nullptr, 100, 1000); + TEST_EQUAL(ret, 0); + if (ec) + { + printf("bdecode: %s\n", ec.message().c_str()); + printf("%s\n", std::string(buf.data(), buf.size()).c_str()); + } + TEST_CHECK(!ec); + l(ses, state); + } +} + +TORRENT_TEST(save_restore_state) +{ + test_save_restore( + [](settings_pack& p) { + // set the cache size + p.set_int(settings_pack::cache_size, 1337); + }, + [](lt::session& ses, entry& st) { + ses.save_state(st); + }, + [](settings_pack& p) { + p.set_int(settings_pack::cache_size, 90); + }, + [](lt::session& ses, bdecode_node& st) { + ses.load_state(st); + // make sure we loaded the cache size correctly + settings_pack sett = ses.get_settings(); + TEST_EQUAL(sett.get_int(settings_pack::cache_size), 1337); + }); +} + +TORRENT_TEST(save_restore_state_save_filter) +{ + test_save_restore( + [](settings_pack& p) { + // set the cache size + p.set_int(settings_pack::cache_size, 1337); + }, + [](lt::session& ses, entry& st) { + // save everything _but_ the settings + ses.save_state(st, ~session::save_settings); + }, + [](settings_pack& p) { + p.set_int(settings_pack::cache_size, 90); + }, + [](lt::session& ses, bdecode_node& st) { + ses.load_state(st); + // make sure whatever we loaded did not include the cache size + settings_pack sett = ses.get_settings(); + TEST_EQUAL(sett.get_int(settings_pack::cache_size), 90); + }); +} + +TORRENT_TEST(save_restore_state_load_filter) +{ + test_save_restore( + [](settings_pack& p) { + // set the cache size + p.set_int(settings_pack::cache_size, 1337); + }, + [](lt::session& ses, entry& st) { + // save everything + ses.save_state(st); + }, + [](settings_pack& p) { + p.set_int(settings_pack::cache_size, 90); + }, + [](lt::session& ses, bdecode_node& st) { + // load everything _but_ the settings + ses.load_state(st, ~session::save_settings); + settings_pack sett = ses.get_settings(); + TEST_EQUAL(sett.get_int(settings_pack::cache_size), 90); + }); +} + +#endif diff --git a/test/test_upnp.cpp b/test/test_upnp.cpp index 6d0cb5f66..5c56eee01 100644 --- a/test/test_upnp.cpp +++ b/test/test_upnp.cpp @@ -119,12 +119,12 @@ struct callback_info std::list callbacks; -void callback(int mapping, address const& ip, int port, error_code const& err) +void callback(int mapping, address const& ip, int port, int protocol, error_code const& err) { callback_info info = {mapping, port, err}; callbacks.push_back(info); std::cerr << "mapping: " << mapping << ", port: " << port << ", IP: " << ip - << ", error: \"" << err.message() << "\"\n"; + << ", proto: " << protocol << ", error: \"" << err.message() << "\"\n"; } void run_upnp_test(char const* root_filename, char const* router_model, char const* control_name, int igd_version) @@ -160,7 +160,6 @@ void run_upnp_test(char const* root_filename, char const* router_model, char con std::string user_agent = "test agent"; boost::shared_ptr upnp_handler = boost::make_shared(boost::ref(ios) - , address_v4::from_string("127.0.0.1") , user_agent, &callback, &log_callback, false); upnp_handler->start(); upnp_handler->discover_device();