Coverage Report

Created: 2025-12-14 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtorrent/src/session.cpp
Line
Count
Source
1
/*
2
3
Copyright (c) 2003, Magnus Jonsson
4
Copyright (c) 2003, 2006, 2008-2020, 2022, Arvid Norberg
5
Copyright (c) 2016, Alden Torres
6
Copyright (c) 2017, 2020, Steven Siloti
7
All rights reserved.
8
9
Redistribution and use in source and binary forms, with or without
10
modification, are permitted provided that the following conditions
11
are met:
12
13
    * Redistributions of source code must retain the above copyright
14
      notice, this list of conditions and the following disclaimer.
15
    * Redistributions in binary form must reproduce the above copyright
16
      notice, this list of conditions and the following disclaimer in
17
      the documentation and/or other materials provided with the distribution.
18
    * Neither the name of the author nor the names of its
19
      contributors may be used to endorse or promote products derived
20
      from this software without specific prior written permission.
21
22
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
POSSIBILITY OF SUCH DAMAGE.
33
34
*/
35
36
#include "libtorrent/config.hpp"
37
#include "libtorrent/session.hpp"
38
#include "libtorrent/extensions.hpp"
39
#include "libtorrent/aux_/session_impl.hpp"
40
#include "libtorrent/aux_/session_call.hpp"
41
#include "libtorrent/extensions.hpp" // for add_peer_flags_t
42
#include "libtorrent/disk_interface.hpp"
43
#include "libtorrent/mmap_disk_io.hpp"
44
#include "libtorrent/posix_disk_io.hpp"
45
#include "libtorrent/platform_util.hpp"
46
47
namespace libtorrent {
48
49
#ifndef TORRENT_DISABLE_EXTENSIONS
50
  // declared in extensions.hpp
51
  // remove this once C++17 is required
52
  constexpr feature_flags_t plugin::optimistic_unchoke_feature;
53
  constexpr feature_flags_t plugin::tick_feature;
54
  constexpr feature_flags_t plugin::dht_request_feature;
55
  constexpr feature_flags_t plugin::alert_feature;
56
  constexpr feature_flags_t plugin::unknown_torrent_feature;
57
#endif
58
59
namespace aux {
60
  constexpr torrent_list_index_t session_interface::torrent_state_updates;
61
  constexpr torrent_list_index_t session_interface::torrent_want_tick;
62
  constexpr torrent_list_index_t session_interface::torrent_want_peers_download;
63
  constexpr torrent_list_index_t session_interface::torrent_want_peers_finished;
64
  constexpr torrent_list_index_t session_interface::torrent_want_scrape;
65
  constexpr torrent_list_index_t session_interface::torrent_downloading_auto_managed;
66
  constexpr torrent_list_index_t session_interface::torrent_seeding_auto_managed;
67
  constexpr torrent_list_index_t session_interface::torrent_checking_auto_managed;
68
}
69
70
#ifndef TORRENT_DISABLE_EXTENSIONS
71
constexpr add_peer_flags_t torrent_plugin::first_time;
72
constexpr add_peer_flags_t torrent_plugin::filtered;
73
#endif
74
75
namespace {
76
77
#if defined TORRENT_ASIO_DEBUGGING
78
  void wait_for_asio_handlers()
79
  {
80
    int counter = 0;
81
    while (log_async())
82
    {
83
      std::this_thread::sleep_for(milliseconds(300));
84
      ++counter;
85
      std::printf("\x1b[2J\x1b[0;0H\x1b[33m==== Waiting to shut down: %d ==== \x1b[0m\n\n"
86
        , counter);
87
    }
88
    async_dec_threads();
89
90
    std::fprintf(stderr, "\n\nEXPECTS NO MORE ASYNC OPS\n\n\n");
91
  }
92
#endif
93
} // anonymous namespace
94
95
  settings_pack min_memory_usage()
96
0
  {
97
0
    settings_pack set;
98
0
#if TORRENT_ABI_VERSION == 1
99
    // receive data directly into disk buffers
100
    // this yields more system calls to read() and
101
    // kqueue(), but saves RAM.
102
0
    set.set_bool(settings_pack::contiguous_recv_buffer, false);
103
0
#endif
104
105
0
    set.set_int(settings_pack::max_peer_recv_buffer_size, 32 * 1024 + 200);
106
107
0
    set.set_int(settings_pack::disk_io_write_mode, settings_pack::disable_os_cache);
108
0
    set.set_int(settings_pack::disk_io_read_mode, settings_pack::disable_os_cache);
109
110
    // keep 2 blocks outstanding when hashing
111
0
    set.set_int(settings_pack::checking_mem_usage, 2);
112
113
    // don't use any extra threads to do SHA-1 hashing
114
0
    set.set_int(settings_pack::aio_threads, 1);
115
116
0
    set.set_int(settings_pack::alert_queue_size, 100);
117
118
0
    set.set_int(settings_pack::max_out_request_queue, 300);
119
0
    set.set_int(settings_pack::max_allowed_in_request_queue, 100);
120
121
    // setting this to a low limit, means more
122
    // peers are more likely to request from the
123
    // same piece. Which means fewer partial
124
    // pieces and fewer entries in the partial
125
    // piece list
126
0
    set.set_int(settings_pack::whole_pieces_threshold, 2);
127
0
    set.set_bool(settings_pack::use_parole_mode, false);
128
0
    set.set_bool(settings_pack::prioritize_partial_pieces, true);
129
130
    // connect to 5 peers per second
131
0
    set.set_int(settings_pack::connection_speed, 5);
132
133
    // only have 4 files open at a time
134
0
    set.set_int(settings_pack::file_pool_size, 4);
135
136
    // we want to keep the peer list as small as possible
137
0
    set.set_bool(settings_pack::allow_multiple_connections_per_ip, false);
138
0
    set.set_int(settings_pack::max_failcount, 2);
139
0
    set.set_int(settings_pack::inactivity_timeout, 120);
140
141
    // whenever a peer has downloaded one block, write
142
    // it to disk, and don't read anything from the
143
    // socket until the disk write is complete
144
0
    set.set_int(settings_pack::max_queued_disk_bytes, 1);
145
146
    // never keep more than one 16kB block in
147
    // the send buffer
148
0
    set.set_int(settings_pack::send_buffer_watermark, 9);
149
150
0
    set.set_bool(settings_pack::close_redundant_connections, true);
151
152
0
    set.set_int(settings_pack::max_peerlist_size, 500);
153
0
    set.set_int(settings_pack::max_paused_peerlist_size, 50);
154
155
    // udp trackers are cheaper to talk to
156
0
    set.set_bool(settings_pack::prefer_udp_trackers, true);
157
158
0
    set.set_int(settings_pack::max_rejects, 10);
159
160
0
    set.set_int(settings_pack::recv_socket_buffer_size, 16 * 1024);
161
0
    set.set_int(settings_pack::send_socket_buffer_size, 16 * 1024);
162
0
    return set;
163
0
  }
164
165
  settings_pack high_performance_seed()
166
0
  {
167
0
    settings_pack set;
168
    // don't throttle TCP, assume there is
169
    // plenty of bandwidth
170
0
    set.set_int(settings_pack::mixed_mode_algorithm, settings_pack::prefer_tcp);
171
172
0
    set.set_int(settings_pack::max_out_request_queue, 1500);
173
0
    set.set_int(settings_pack::max_allowed_in_request_queue, 2000);
174
175
0
    set.set_int(settings_pack::max_peer_recv_buffer_size, 5 * 1024 * 1024);
176
177
    // we will probably see a high rate of alerts, make it less
178
    // likely to loose alerts
179
0
    set.set_int(settings_pack::alert_queue_size, 10000);
180
181
    // allow 500 files open at a time
182
0
    set.set_int(settings_pack::file_pool_size, 500);
183
184
    // don't update access time for each read/write
185
0
    set.set_bool(settings_pack::no_atime_storage, true);
186
187
    // as a seed box, we must accept multiple peers behind
188
    // the same NAT
189
//    set.set_bool(settings_pack::allow_multiple_connections_per_ip, true);
190
191
    // connect to 50 peers per second
192
0
    set.set_int(settings_pack::connection_speed, 500);
193
194
    // allow 8000 peer connections
195
0
    set.set_int(settings_pack::connections_limit, 8000);
196
197
    // allow lots of peers to try to connect simultaneously
198
0
    set.set_int(settings_pack::listen_queue_size, 3000);
199
200
    // unchoke all peers
201
0
    set.set_int(settings_pack::unchoke_slots_limit, -1);
202
203
    // the max number of bytes pending write before we throttle
204
    // download rate
205
0
    set.set_int(settings_pack::max_queued_disk_bytes, 7 * 1024 * 1024);
206
207
    // prevent fast pieces to interfere with suggested pieces
208
    // since we unchoke everyone, we don't need fast pieces anyway
209
0
    set.set_int(settings_pack::allowed_fast_set_size, 0);
210
211
    // suggest pieces in the read cache for higher cache hit rate
212
0
    set.set_int(settings_pack::suggest_mode, settings_pack::suggest_read_cache);
213
214
0
    set.set_bool(settings_pack::close_redundant_connections, true);
215
216
0
    set.set_int(settings_pack::max_rejects, 10);
217
218
0
    set.set_int(settings_pack::send_not_sent_low_watermark, 524288);
219
220
    // don't let connections linger for too long
221
0
    set.set_int(settings_pack::request_timeout, 10);
222
0
    set.set_int(settings_pack::peer_timeout, 20);
223
0
    set.set_int(settings_pack::inactivity_timeout, 20);
224
225
0
    set.set_int(settings_pack::active_limit, 20000);
226
0
    set.set_int(settings_pack::active_tracker_limit, 2000);
227
0
    set.set_int(settings_pack::active_dht_limit, 600);
228
0
    set.set_int(settings_pack::active_seeds, 2000);
229
230
0
    set.set_int(settings_pack::choking_algorithm, settings_pack::fixed_slots_choker);
231
232
    // of 500 ms, and a send rate of 4 MB/s, the upper
233
    // limit should be 2 MB
234
0
    set.set_int(settings_pack::send_buffer_watermark, 3 * 1024 * 1024);
235
236
    // put 1.5 seconds worth of data in the send buffer
237
    // this gives the disk I/O more heads-up on disk
238
    // reads, and can maximize throughput
239
0
    set.set_int(settings_pack::send_buffer_watermark_factor, 150);
240
241
    // always stuff at least 1 MiB down each peer
242
    // pipe, to quickly ramp up send rates
243
0
    set.set_int(settings_pack::send_buffer_low_watermark, 1 * 1024 * 1024);
244
245
    // don't retry peers if they fail once. Let them
246
    // connect to us if they want to
247
0
    set.set_int(settings_pack::max_failcount, 1);
248
249
    // number of disk threads for low level file operations
250
0
    set.set_int(settings_pack::aio_threads, 8);
251
252
0
    set.set_int(settings_pack::checking_mem_usage, 2048);
253
254
0
    return set;
255
0
  }
256
257
  void session::start(session_flags_t const flags, session_params&& params, io_context* ios)
258
0
  {
259
0
    bool const internal_executor = ios == nullptr;
260
261
0
    if (internal_executor)
262
0
    {
263
      // the user did not provide an executor, we have to use our own
264
0
      m_io_service = std::make_shared<io_context>(1);
265
0
      ios = m_io_service.get();
266
0
    }
267
268
0
#if TORRENT_ABI_VERSION <= 2
269
0
#ifndef TORRENT_DISABLE_DHT
270
    // in case the session_params has its dht_settings in use, pick out the
271
    // non-default settings from there and move them into the main settings.
272
    // any conflicting options set in main settings take precedence
273
0
    {
274
0
    dht::dht_settings const def_sett{};
275
0
#define SET_BOOL(name) if (!params.settings.has_val(settings_pack::dht_ ## name) && \
276
0
  def_sett.name != params.dht_settings.name) \
277
0
    params.settings.set_bool(settings_pack::dht_ ## name, params.dht_settings.name)
278
0
#define SET_INT(name) if (!params.settings.has_val(settings_pack::dht_ ## name) && \
279
0
  def_sett.name != params.dht_settings.name) \
280
0
    params.settings.set_int(settings_pack::dht_ ## name, params.dht_settings.name)
281
282
0
    SET_INT(max_peers_reply);
283
0
    SET_INT(search_branching);
284
0
    SET_INT(max_fail_count);
285
0
    SET_INT(max_torrents);
286
0
    SET_INT(max_dht_items);
287
0
    SET_INT(max_peers);
288
0
    SET_INT(max_torrent_search_reply);
289
0
    SET_BOOL(restrict_routing_ips);
290
0
    SET_BOOL(restrict_search_ips);
291
0
    SET_BOOL(extended_routing_table);
292
0
    SET_BOOL(aggressive_lookups);
293
0
    SET_BOOL(privacy_lookups);
294
0
    SET_BOOL(enforce_node_id);
295
0
    SET_BOOL(ignore_dark_internet);
296
0
    SET_INT(block_timeout);
297
0
    SET_INT(block_ratelimit);
298
0
    SET_BOOL(read_only);
299
0
    SET_INT(item_lifetime);
300
0
    SET_INT(upload_rate_limit);
301
0
    SET_INT(sample_infohashes_interval);
302
0
    SET_INT(max_infohashes_sample_count);
303
0
#undef SET_BOOL
304
0
#undef SET_INT
305
0
    }
306
0
#endif
307
0
#endif
308
309
0
    m_impl = std::make_shared<aux::session_impl>(std::ref(*ios)
310
0
      , std::move(params.settings)
311
0
      , std::move(params.disk_io_constructor)
312
0
      , flags);
313
0
    *static_cast<session_handle*>(this) = session_handle(m_impl);
314
315
0
#ifndef TORRENT_DISABLE_EXTENSIONS
316
0
    for (auto& ext : params.extensions)
317
0
    {
318
0
      ext->load_state(params.ext_state);
319
0
      m_impl->add_ses_extension(std::move(ext));
320
0
    }
321
0
#endif
322
323
0
#ifndef TORRENT_DISABLE_DHT
324
0
    m_impl->set_dht_state(std::move(params.dht_state));
325
326
0
    TORRENT_ASSERT(params.dht_storage_constructor);
327
0
    m_impl->set_dht_storage(std::move(params.dht_storage_constructor));
328
0
#endif
329
330
0
    if (!params.ip_filter.empty())
331
0
    {
332
0
      std::shared_ptr<ip_filter> copy = std::make_shared<ip_filter>(std::move(params.ip_filter));
333
0
      m_impl->set_ip_filter(std::move(copy));
334
0
    }
335
336
0
    m_impl->start_session();
337
338
0
    if (internal_executor)
339
0
    {
340
      // start a thread for the message pump
341
0
      auto s = m_io_service;
342
0
      m_thread = std::make_shared<std::thread>([=]
343
0
      {
344
0
        set_thread_name("libtorrent-network-thread");
345
0
        s->run();
346
0
      });
347
0
    }
348
0
  }
349
350
#if TORRENT_ABI_VERSION <= 2
351
  void session::start(session_flags_t const flags, settings_pack&& sp, io_context* ios)
352
0
  {
353
0
    if (flags & add_default_plugins)
354
0
    {
355
0
      session_params sp_(std::move(sp));
356
0
      start(flags, std::move(sp_), ios);
357
0
    }
358
0
    else
359
0
    {
360
0
      session_params sp_(std::move(sp), {});
361
0
      start(flags, std::move(sp_), ios);
362
0
    }
363
0
  }
364
#endif
365
366
0
  session::session(session&&) = default;
367
368
  session::session(session_params const& params)
369
0
  {
370
0
    start({}, session_params(params), nullptr);
371
0
  }
372
373
  session::session(session_params&& params)
374
0
  {
375
0
    start({}, std::move(params), nullptr);
376
0
  }
377
378
  session::session(session_params const& params, session_flags_t const flags)
379
0
  {
380
0
    start(flags, session_params(params), nullptr);
381
0
  }
382
383
  session::session(session_params&& params, session_flags_t const flags)
384
0
  {
385
0
    start(flags, std::move(params), nullptr);
386
0
  }
387
388
  session::session()
389
0
  {
390
0
    session_params params;
391
0
    start({}, std::move(params), nullptr);
392
0
  }
393
394
  session::session(session_params&& params, io_context& ios)
395
0
  {
396
0
    start({}, std::move(params), &ios);
397
0
  }
398
399
  session::session(session_params const& params, io_context& ios)
400
0
  {
401
0
    start({}, session_params(params), &ios);
402
0
  }
403
404
  session::session(session_params&& params, io_context& ios, session_flags_t const flags)
405
0
  {
406
0
    start(flags, std::move(params), &ios);
407
0
  }
408
409
  session::session(session_params const& params, io_context& ios, session_flags_t const flags)
410
0
  {
411
0
    start(flags, session_params(params), &ios);
412
0
  }
413
414
415
#if TORRENT_ABI_VERSION <= 2
416
  session::session(settings_pack&& pack, session_flags_t const flags)
417
0
  {
418
0
    start(flags, std::move(pack), nullptr);
419
0
  }
420
421
  session::session(settings_pack const& pack, session_flags_t const flags)
422
0
  {
423
0
    start(flags, settings_pack(pack), nullptr);
424
0
  }
425
426
  session::session(settings_pack&& pack, io_context& ios, session_flags_t const flags)
427
0
  {
428
0
    start(flags, std::move(pack), &ios);
429
0
  }
430
431
  session::session(settings_pack const& pack, io_context& ios, session_flags_t const flags)
432
0
  {
433
0
    start(flags, settings_pack(pack), &ios);
434
0
  }
435
#endif
436
437
#if TORRENT_ABI_VERSION == 1
438
#include "libtorrent/aux_/disable_deprecation_warnings_push.hpp"
439
  session::session(fingerprint const& print, session_flags_t const flags
440
    , alert_category_t const alert_mask)
441
0
  {
442
0
    settings_pack pack;
443
0
    pack.set_int(settings_pack::alert_mask, int(alert_mask));
444
0
    pack.set_str(settings_pack::peer_fingerprint, print.to_string());
445
0
    if (!(flags & start_default_features))
446
0
    {
447
0
      pack.set_bool(settings_pack::enable_upnp, false);
448
0
      pack.set_bool(settings_pack::enable_natpmp, false);
449
0
      pack.set_bool(settings_pack::enable_lsd, false);
450
0
      pack.set_bool(settings_pack::enable_dht, false);
451
0
    }
452
453
0
    start(flags, std::move(pack), nullptr);
454
0
  }
455
456
  session::session(fingerprint const& print, std::pair<int, int> listen_port_range
457
    , char const* listen_interface, session_flags_t const flags
458
    , alert_category_t const alert_mask)
459
0
  {
460
0
    TORRENT_ASSERT(listen_port_range.first > 0);
461
0
    TORRENT_ASSERT(listen_port_range.first <= listen_port_range.second);
462
463
0
    settings_pack pack;
464
0
    pack.set_int(settings_pack::alert_mask, int(alert_mask));
465
0
    pack.set_int(settings_pack::max_retry_port_bind, listen_port_range.second - listen_port_range.first);
466
0
    pack.set_str(settings_pack::peer_fingerprint, print.to_string());
467
0
    char if_string[100];
468
469
0
    if (listen_interface == nullptr) listen_interface = "0.0.0.0";
470
0
    std::snprintf(if_string, sizeof(if_string), "%s:%d", listen_interface, listen_port_range.first);
471
0
    pack.set_str(settings_pack::listen_interfaces, if_string);
472
473
0
    if (!(flags & start_default_features))
474
0
    {
475
0
      pack.set_bool(settings_pack::enable_upnp, false);
476
0
      pack.set_bool(settings_pack::enable_natpmp, false);
477
0
      pack.set_bool(settings_pack::enable_lsd, false);
478
0
      pack.set_bool(settings_pack::enable_dht, false);
479
0
    }
480
0
    start(flags, std::move(pack), nullptr);
481
0
  }
482
#include "libtorrent/aux_/disable_warnings_pop.hpp"
483
#endif // TORRENT_ABI_VERSION
484
0
  session& session::operator=(session&&) & = default;
485
486
  session::~session()
487
0
  {
488
0
    if (!m_impl) return;
489
490
0
    aux::dump_call_profile();
491
492
    // capture the shared_ptr in the dispatched function
493
    // to keep the session_impl alive
494
0
    m_impl->call_abort();
495
496
0
    if (m_thread && m_thread.use_count() == 1)
497
0
    {
498
#if defined TORRENT_ASIO_DEBUGGING
499
      wait_for_asio_handlers();
500
#endif
501
0
      m_thread->join();
502
0
    }
503
0
  }
504
505
  session_proxy session::abort()
506
0
  {
507
    // stop calling the alert notify function now, to avoid it thinking the
508
    // session is still alive
509
0
    m_impl->alerts().set_notify_function({});
510
0
    return session_proxy(m_io_service, m_thread, m_impl);
511
0
  }
512
513
0
  session_proxy::session_proxy() = default;
514
  session_proxy::session_proxy(std::shared_ptr<io_context> ios
515
    , std::shared_ptr<std::thread> t
516
    , std::shared_ptr<aux::session_impl> impl)
517
0
    : m_io_service(std::move(ios))
518
0
    , m_thread(std::move(t))
519
0
    , m_impl(std::move(impl))
520
0
  {}
521
0
  session_proxy::session_proxy(session_proxy const&) = default;
522
0
  session_proxy& session_proxy::operator=(session_proxy const&) & = default;
523
0
  session_proxy::session_proxy(session_proxy&&) noexcept = default;
524
0
  session_proxy& session_proxy::operator=(session_proxy&&) & noexcept = default;
525
  session_proxy::~session_proxy()
526
0
  {
527
0
    if (m_thread && m_thread.use_count() == 1)
528
0
    {
529
#if defined TORRENT_ASIO_DEBUGGING
530
      wait_for_asio_handlers();
531
#endif
532
0
      m_thread->join();
533
0
    }
534
0
  }
535
536
  TORRENT_EXPORT std::unique_ptr<disk_interface> default_disk_io_constructor(
537
    io_context& ios, settings_interface const& sett, counters& cnt)
538
0
  {
539
0
#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE
540
    // TODO: In C++17. use if constexpr instead
541
0
#include "libtorrent/aux_/disable_deprecation_warnings_push.hpp"
542
0
    if (sizeof(void*) == 8)
543
0
      return mmap_disk_io_constructor(ios, sett, cnt);
544
0
    else
545
0
      return posix_disk_io_constructor(ios, sett, cnt);
546
0
#include "libtorrent/aux_/disable_warnings_pop.hpp"
547
#else
548
    return posix_disk_io_constructor(ios, sett, cnt);
549
#endif
550
0
  }
551
552
}