void load_struct_from_settings(aux::session_settings const& current, session_settings& ret) { for (int i = 0; i < settings_pack::num_string_settings; ++i) { if (str_settings[i].offset == 0) continue; std::string& val = *(std::string*)(((char*)&ret) + str_settings[i].offset); val = current.get_str(settings_pack::string_type_base + i); } for (int i = 0; i < settings_pack::num_int_settings; ++i) { if (int_settings[i].offset == 0) continue; int& val = *(int*)(((char*)&ret) + int_settings[i].offset); val = current.get_int(settings_pack::int_type_base + i); } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { if (bool_settings[i].offset == 0) continue; bool& val = *(bool*)(((char*)&ret) + bool_settings[i].offset); val = current.get_bool(settings_pack::bool_type_base + i); } // special case for deprecated float values ret.share_ratio_limit = float(current.get_int(settings_pack::share_ratio_limit)) / 100.f; ret.seed_time_ratio_limit = float(current.get_int(settings_pack::seed_time_ratio_limit)) / 100.f; ret.peer_turnover = float(current.get_int(settings_pack::peer_turnover)) / 100.f; ret.peer_turnover_cutoff = float(current.get_int(settings_pack::peer_turnover_cutoff)) / 100.f; }
boost::shared_ptr<settings_pack> load_pack_from_struct( aux::session_settings const& current, session_settings const& s) { boost::shared_ptr<settings_pack> p = boost::make_shared<settings_pack>(); for (int i = 0; i < settings_pack::num_string_settings; ++i) { if (str_settings[i].offset == 0) continue; std::string& val = *(std::string*)(((char*)&s) + str_settings[i].offset); int setting_name = settings_pack::string_type_base + i; if (val == current.get_str(setting_name)) continue; p->set_str(setting_name, val); } for (int i = 0; i < settings_pack::num_int_settings; ++i) { if (int_settings[i].offset == 0) continue; int& val = *(int*)(((char*)&s) + int_settings[i].offset); int setting_name = settings_pack::int_type_base + i; if (val == current.get_int(setting_name)) continue; p->set_int(setting_name, val); } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { if (bool_settings[i].offset == 0) continue; bool& val = *(bool*)(((char*)&s) + bool_settings[i].offset); int setting_name = settings_pack::bool_type_base + i; if (val == current.get_bool(setting_name)) continue; p->set_bool(setting_name, val); } // special case for deprecated float values int val = current.get_int(settings_pack::share_ratio_limit); if (fabs(s.share_ratio_limit - float(val) / 100.f) > 0.001f) p->set_int(settings_pack::share_ratio_limit, s.share_ratio_limit * 100); val = current.get_int(settings_pack::seed_time_ratio_limit); if (fabs(s.seed_time_ratio_limit - float(val) / 100.f) > 0.001f) p->set_int(settings_pack::seed_time_ratio_limit, s.seed_time_ratio_limit * 100); val = current.get_int(settings_pack::peer_turnover); if (fabs(s.peer_turnover - float(val) / 100.f) > 0.001) p->set_int(settings_pack::peer_turnover, s.peer_turnover * 100); val = current.get_int(settings_pack::peer_turnover_cutoff); if (fabs(s.peer_turnover_cutoff - float(val) / 100.f) > 0.001) p->set_int(settings_pack::peer_turnover_cutoff, s.peer_turnover_cutoff * 100); return p; }
void web_connection_base::add_headers(std::string& request , aux::session_settings const& sett, bool using_proxy) const { request += "Host: "; request += m_host; if (m_first_request || m_settings.get_bool(settings_pack::always_send_user_agent)) { request += "\r\nUser-Agent: "; request += m_settings.get_str(settings_pack::user_agent); } if (!m_external_auth.empty()) { request += "\r\nAuthorization: "; request += m_external_auth; } else if (!m_basic_auth.empty()) { request += "\r\nAuthorization: Basic "; request += m_basic_auth; } if (sett.get_int(settings_pack::proxy_type) == settings_pack::http_pw) { request += "\r\nProxy-Authorization: Basic "; request += base64encode(sett.get_str(settings_pack::proxy_username) + ":" + sett.get_str(settings_pack::proxy_password)); } for (web_seed_entry::headers_t::const_iterator it = m_extra_headers.begin(); it != m_extra_headers.end(); ++it) { request += "\r\n"; request += it->first; request += ": "; request += it->second; } if (using_proxy) { request += "\r\nProxy-Connection: keep-alive"; } if (m_first_request || using_proxy) { request += "\r\nConnection: keep-alive"; } }
void initialize_default_settings(aux::session_settings& s) { for (int i = 0; i < settings_pack::num_string_settings; ++i) { if (str_settings[i].default_value == nullptr) continue; s.set_str(settings_pack::string_type_base + i, str_settings[i].default_value); TORRENT_ASSERT(s.get_str(settings_pack::string_type_base + i) == str_settings[i].default_value); } for (int i = 0; i < settings_pack::num_int_settings; ++i) { s.set_int(settings_pack::int_type_base + i, int_settings[i].default_value); TORRENT_ASSERT(s.get_int(settings_pack::int_type_base + i) == int_settings[i].default_value); } for (int i = 0; i < settings_pack::num_bool_settings; ++i) { s.set_bool(settings_pack::bool_type_base + i, bool_settings[i].default_value); TORRENT_ASSERT(s.get_bool(settings_pack::bool_type_base + i) == bool_settings[i].default_value); } }
void disk_buffer_pool::set_settings(aux::session_settings const& sett) { std::unique_lock<std::mutex> l(m_pool_mutex); int const cache_size = sett.get_int(settings_pack::cache_size); if (cache_size < 0) { std::int64_t phys_ram = total_physical_ram(); if (phys_ram == 0) m_max_use = 1024; 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 40th of everything exceeding 4 GiB // a 30th of everything exceeding 1 GiB // and a 10th of everything below a GiB constexpr std::int64_t gb = 1024 * 1024 * 1024; std::int64_t result = 0; if (phys_ram > 4 * gb) { result += (phys_ram - 4 * gb) / 40; phys_ram = 4 * gb; } if (phys_ram > 1 * gb) { result += (phys_ram - 1 * gb) / 30; phys_ram = 1 * gb; } result += phys_ram / 20; m_max_use = int(result / default_block_size); } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4127 ) /* warning C4127: conditional expression is constant */ #endif // _MSC_VER if (sizeof(void*) == 4) #ifdef _MSC_VER #pragma warning(pop) #endif // _MSC_VER { // 32 bit builds should capped below 2 GB of memory, even // when more actual ram is available, because we're still // constrained by the 32 bit virtual address space. m_max_use = std::min(2 * 1024 * 1024 * 3 / 4 * 1024 / default_block_size, m_max_use); } } else { m_max_use = cache_size; } m_low_watermark = m_max_use - std::max(16, sett.get_int(settings_pack::max_queued_disk_bytes) / 0x4000); if (m_low_watermark < 0) m_low_watermark = 0; if (m_in_use >= m_max_use && !m_exceeded_max_size) { m_exceeded_max_size = true; m_trigger_cache_trim(); } #if TORRENT_USE_ASSERTS m_settings_set = true; #endif }
int unchoke_sort(std::vector<peer_connection*>& peers , int max_upload_rate , time_duration unchoke_interval , aux::session_settings const& sett) { int upload_slots = sett.get_int(settings_pack::unchoke_slots_limit); // ==== BitTyrant ==== // // if we're using the bittyrant unchoker, go through all peers that // we have unchoked already, and adjust our estimated reciprocation // rate. If the peer has reciprocated, lower the estimate, if it hasn't, // increase the estimate (this attempts to optimize "ROI" of upload // capacity, by sending just enough to be reciprocated). // For more information, see: http://bittyrant.cs.washington.edu/ if (sett.get_int(settings_pack::choking_algorithm) == settings_pack::bittyrant_choker) { for (std::vector<peer_connection*>::const_iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection* p = *i; if (p->is_choked() || !p->is_interesting()) continue; if (!p->has_peer_choked()) { // we're unchoked, we may want to lower our estimated // reciprocation rate p->decrease_est_reciprocation_rate(); } else { // we've unchoked this peer, and it hasn't reciprocated // we may want to increase our estimated reciprocation rate p->increase_est_reciprocation_rate(); } } // if we're using the bittyrant choker, sort peers by their return // on investment. i.e. download rate / upload rate std::sort(peers.begin(), peers.end() , boost::bind(&bittyrant_unchoke_compare, _1, _2)); int upload_capacity_left = max_upload_rate; // now, figure out how many peers should be unchoked. We deduct the // estimated reciprocation rate from our upload_capacity estimate // until there none left upload_slots = 0; for (std::vector<peer_connection*>::iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection* p = *i; TORRENT_ASSERT(p); if (p->est_reciprocation_rate() > upload_capacity_left) break; ++upload_slots; upload_capacity_left -= p->est_reciprocation_rate(); } return upload_slots; } // ==== rate-based ==== // // The rate based unchoker looks at our upload rate to peers, and find // a balance between number of upload slots and the rate we achieve. The // intention is to not spread upload bandwidth too thin, but also to not // unchoke few enough peers to not be able to saturate the up-link. // this is done by traversing the peers sorted by our upload rate to // them in decreasing rates. For each peer we increase our threshold // by 1 kB/s. The first peer we get to to whom we upload slower than // the threshold, we stop and that's the number of unchoke slots we have. if (sett.get_int(settings_pack::choking_algorithm) == settings_pack::rate_based_choker) { // first reset the number of unchoke slots, because we'll calculate // it purely based on the current state of our peers. upload_slots = 0; // TODO: optimize this using partial_sort or something. We don't need // to sort the entire list // TODO: make the comparison function a free function and move it // into this cpp file std::sort(peers.begin(), peers.end() , boost::bind(&upload_rate_compare, _1, _2)); // TODO: make configurable int rate_threshold = 1024; for (std::vector<peer_connection*>::const_iterator i = peers.begin() , end(peers.end()); i != end; ++i) { peer_connection const& p = **i; int rate = int(p.uploaded_in_last_round() * 1000 / total_milliseconds(unchoke_interval)); if (rate < rate_threshold) break; ++upload_slots; // TODO: make configurable rate_threshold += 1024; } ++upload_slots; } // sorts the peers that are eligible for unchoke by download rate and // secondary by total upload. The reason for this is, if all torrents are // being seeded, the download rate will be 0, and the peers we have sent // the least to should be unchoked // we use partial sort here, because we only care about the top // upload_slots peers. if (sett.get_int(settings_pack::seed_choking_algorithm) == settings_pack::round_robin) { int pieces = sett.get_int(settings_pack::seeding_piece_quota); std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_rr, _1, _2, pieces)); } else if (sett.get_int(settings_pack::seed_choking_algorithm) == settings_pack::fastest_upload) { std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_fastest_upload, _1, _2)); } else if (sett.get_int(settings_pack::seed_choking_algorithm) == settings_pack::anti_leech) { std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_anti_leech, _1, _2)); } else { TORRENT_ASSERT(false && "unknown seed choking algorithm"); int pieces = sett.get_int(settings_pack::seeding_piece_quota); std::partial_sort(peers.begin(), peers.begin() + (std::min)(upload_slots, int(peers.size())), peers.end() , boost::bind(&unchoke_compare_rr, _1, _2, pieces)); } return upload_slots; }
void apply_pack(settings_pack const* pack, aux::session_settings& sett , aux::session_impl* ses) { typedef void (aux::session_impl::*fun_t)(); std::vector<fun_t> callbacks; for (auto const& p : pack->m_strings) { // disregard setting indices that are not string types if ((p.first & settings_pack::type_mask) != settings_pack::string_type_base) continue; // ignore settings that are out of bounds int const index = p.first & settings_pack::index_mask; TORRENT_ASSERT_PRECOND(index >= 0 && index < settings_pack::num_string_settings); if (index < 0 || index >= settings_pack::num_string_settings) continue; // if the value did not change, don't call the update callback if (sett.get_str(p.first) == p.second) continue; sett.set_str(p.first, p.second); str_setting_entry_t const& sa = str_settings[index]; if (sa.fun && ses && std::find(callbacks.begin(), callbacks.end(), sa.fun) == callbacks.end()) callbacks.push_back(sa.fun); } for (auto const& p : pack->m_ints) { // disregard setting indices that are not int types if ((p.first & settings_pack::type_mask) != settings_pack::int_type_base) continue; // ignore settings that are out of bounds int const index = p.first & settings_pack::index_mask; TORRENT_ASSERT_PRECOND(index >= 0 && index < settings_pack::num_int_settings); if (index < 0 || index >= settings_pack::num_int_settings) continue; // if the value did not change, don't call the update callback if (sett.get_int(p.first) == p.second) continue; sett.set_int(p.first, p.second); int_setting_entry_t const& sa = int_settings[index]; if (sa.fun && ses && std::find(callbacks.begin(), callbacks.end(), sa.fun) == callbacks.end()) callbacks.push_back(sa.fun); } for (auto const& p : pack->m_bools) { // disregard setting indices that are not bool types if ((p.first & settings_pack::type_mask) != settings_pack::bool_type_base) continue; // ignore settings that are out of bounds int const index = p.first & settings_pack::index_mask; TORRENT_ASSERT_PRECOND(index >= 0 && index < settings_pack::num_bool_settings); if (index < 0 || index >= settings_pack::num_bool_settings) continue; // if the value did not change, don't call the update callback if (sett.get_bool(p.first) == p.second) continue; sett.set_bool(p.first, p.second); bool_setting_entry_t const& sa = bool_settings[index]; if (sa.fun && ses && std::find(callbacks.begin(), callbacks.end(), sa.fun) == callbacks.end()) callbacks.push_back(sa.fun); } // call the callbacks once all the settings have been applied, and // only once per callback for (auto const& f : callbacks) { (ses->*f)(); } }
void disk_buffer_pool::set_settings(aux::session_settings const& sett) { mutex::scoped_lock l(m_pool_mutex); // 0 cache_buffer_chunk_size means 'automatic' (i.e. // proportional to the total disk cache size) 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 there are no allocated blocks, it's OK to switch allocator if (m_in_use == 0) m_using_pool_allocator = m_want_pool_allocator; #endif #if TORRENT_HAVE_MMAP // if we've already allocated an mmap, we can't change // anything unless there are no allocations in use if (m_cache_pool && m_in_use > 0) return; #endif // only allow changing size if we're not using mmapped // cache, or if we're just about to turn it off if ( #if TORRENT_HAVE_MMAP m_cache_pool == 0 || #endif sett.get_str(settings_pack::mmap_cache).empty()) { int cache_size = sett.get_int(settings_pack::cache_size); if (cache_size < 0) { boost::uint64_t phys_ram = physical_ram(); if (phys_ram == 0) m_max_use = 1024; else m_max_use = phys_ram / 8 / m_block_size; } else { m_max_use = cache_size; } m_low_watermark = m_max_use - (std::max)(16, sett.get_int(settings_pack::max_queued_disk_bytes) / 0x4000); if (m_low_watermark < 0) m_low_watermark = 0; if (m_in_use >= m_max_use && !m_exceeded_max_size) { m_exceeded_max_size = true; m_trigger_cache_trim(); } } #if TORRENT_USE_ASSERTS m_settings_set = true; #endif #if TORRENT_HAVE_MMAP // #error support resizing the map if (m_cache_pool && sett.get_str(settings_pack::mmap_cache).empty()) { TORRENT_ASSERT(m_in_use == 0); munmap(m_cache_pool, boost::uint64_t(m_max_use) * 0x4000); m_cache_pool = 0; // attempt to make MacOS not flush this to disk, making close() // block for a long time ftruncate(m_cache_fd, 0); close(m_cache_fd); m_cache_fd = -1; std::vector<int>().swap(m_free_list); } else if (m_cache_pool == 0 && !sett.get_str(settings_pack::mmap_cache).empty()) { // O_TRUNC here is because we don't actually care about what's // in the file now, there's no need to ever read that into RAM #ifndef O_EXLOCK #define O_EXLOCK 0 #endif m_cache_fd = open(sett.get_str(settings_pack::mmap_cache).c_str(), O_RDWR | O_CREAT | O_EXLOCK | O_TRUNC, 0700); if (m_cache_fd < 0) { if (m_post_alert) { error_code ec(errno, boost::system::generic_category()); m_ios.post(boost::bind(alert_callback, m_post_alert, new mmap_cache_alert(ec))); } } else { #ifndef MAP_NOCACHE #define MAP_NOCACHE 0 #endif ftruncate(m_cache_fd, boost::uint64_t(m_max_use) * 0x4000); m_cache_pool = (char*)mmap(0, boost::uint64_t(m_max_use) * 0x4000, PROT_READ | PROT_WRITE , MAP_SHARED | MAP_NOCACHE, m_cache_fd, 0); if (intptr_t(m_cache_pool) == -1) { if (m_post_alert) { error_code ec(errno, boost::system::generic_category()); m_ios.post(boost::bind(alert_callback, m_post_alert, new mmap_cache_alert(ec))); } m_cache_pool = 0; // attempt to make MacOS not flush this to disk, making close() // block for a long time ftruncate(m_cache_fd, 0); close(m_cache_fd); m_cache_fd = -1; } else { TORRENT_ASSERT((size_t(m_cache_pool) & 0xfff) == 0); m_free_list.reserve(m_max_use); for (int i = 0; i < m_max_use; ++i) m_free_list.push_back(i); } } } #endif }