Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libtorrent for openSUSE:Factory checked in at 2026-01-05 14:52:34 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libtorrent (Old) and /work/SRC/openSUSE:Factory/.libtorrent.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libtorrent" Mon Jan 5 14:52:34 2026 rev:27 rq:1325243 version:0.16.6 Changes: -------- --- /work/SRC/openSUSE:Factory/libtorrent/libtorrent.changes 2025-12-03 14:15:03.284964974 +0100 +++ /work/SRC/openSUSE:Factory/.libtorrent.new.1928/libtorrent.changes 2026-01-05 14:54:02.580485338 +0100 @@ -1,0 +2,11 @@ +Sat Jan 3 18:10:37 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 0.16.6 + * Session saves are now handled in a separate thread. + * Downloading of magnet links now save the metadata file to + session directory by default. + * New Timestamp Helper Commands: Added ``.or_zero`` and + ``.elapsed`` commands for ``d.timestamp.started`` and + ``d.timestamp.finished``. + +------------------------------------------------------------------- Old: ---- libtorrent-0.16.5.tar.gz New: ---- libtorrent-0.16.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libtorrent.spec ++++++ --- /var/tmp/diff_new_pack.N3GpKH/_old 2026-01-05 14:54:16.597068352 +0100 +++ /var/tmp/diff_new_pack.N3GpKH/_new 2026-01-05 14:54:16.597068352 +0100 @@ -16,9 +16,9 @@ # -%define lname libtorrent35 +%define lname libtorrent36 Name: libtorrent -Version: 0.16.5 +Version: 0.16.6 Release: 0 Summary: A BitTorrent library written in C++ License: SUSE-GPL-2.0+-with-openssl-exception ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.N3GpKH/_old 2026-01-05 14:54:16.641070181 +0100 +++ /var/tmp/diff_new_pack.N3GpKH/_new 2026-01-05 14:54:16.645070347 +0100 @@ -1,5 +1,5 @@ -mtime: 1764752201 -commit: 994a38c7ddd491b1ef7906eedf9419b7f1e842e940708f20791fa05da5a20a0d +mtime: 1767464045 +commit: 46e19fbca4be3b97cec57ec6bcd86a581e3fc10ab1c84f92f8dd753039de3178 url: https://src.opensuse.org/jengelh/libtorrent revision: master ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-01-03 19:14:19.000000000 +0100 @@ -0,0 +1 @@ +.osc ++++++ libtorrent-0.16.5.tar.gz -> libtorrent-0.16.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/configure new/libtorrent-0.16.6/configure --- old/libtorrent-0.16.5/configure 2025-12-02 18:15:57.000000000 +0100 +++ new/libtorrent-0.16.6/configure 2026-01-02 15:37:45.000000000 +0100 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.72 for libtorrent 0.16.5. +# Generated by GNU Autoconf 2.72 for libtorrent 0.16.6. # # Report bugs to <[email protected]>. # @@ -614,8 +614,8 @@ # Identity of this package. PACKAGE_NAME='libtorrent' PACKAGE_TARNAME='libtorrent' -PACKAGE_VERSION='0.16.5' -PACKAGE_STRING='libtorrent 0.16.5' +PACKAGE_VERSION='0.16.6' +PACKAGE_STRING='libtorrent 0.16.6' PACKAGE_BUGREPORT='[email protected]' PACKAGE_URL='' @@ -1416,7 +1416,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -'configure' configures libtorrent 0.16.5 to adapt to many kinds of systems. +'configure' configures libtorrent 0.16.6 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1487,7 +1487,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libtorrent 0.16.5:";; + short | recursive ) echo "Configuration of libtorrent 0.16.6:";; esac cat <<\_ACEOF @@ -1649,7 +1649,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libtorrent configure 0.16.5 +libtorrent configure 0.16.6 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. @@ -2367,7 +2367,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libtorrent $as_me 0.16.5, which was +It was created by libtorrent $as_me 0.16.6, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -4060,7 +4060,7 @@ # Define the identity of the package. PACKAGE='libtorrent' - VERSION='0.16.5' + VERSION='0.16.6' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -13703,14 +13703,16 @@ +# When releasing the first 1.x.y version, we need to start with 1.1.0 (or higher) as we've already +# used 0.16.x. -printf "%s\n" "#define PEER_NAME \"-lt1005-\"" >>confdefs.h +printf "%s\n" "#define PEER_NAME \"-lt1006-\"" >>confdefs.h -printf "%s\n" "#define PEER_VERSION \"lt\\x10\\x05\"" >>confdefs.h +printf "%s\n" "#define PEER_VERSION \"lt\\x10\\x06\"" >>confdefs.h -LIBTORRENT_CURRENT=35 +LIBTORRENT_CURRENT=36 LIBTORRENT_REVISION=0 LIBTORRENT_AGE=0 @@ -23877,7 +23879,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libtorrent $as_me 0.16.5, which was +This file was extended by libtorrent $as_me 0.16.6, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23945,7 +23947,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -libtorrent config.status 0.16.5 +libtorrent config.status 0.16.6 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/configure.ac new/libtorrent-0.16.6/configure.ac --- old/libtorrent-0.16.5/configure.ac 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/configure.ac 2026-01-02 15:37:32.000000000 +0100 @@ -1,4 +1,4 @@ -AC_INIT([[libtorrent]],[[0.16.5]],[[[email protected]]]) +AC_INIT([[libtorrent]],[[0.16.6]],[[[email protected]]]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIRS([scripts]) @@ -6,10 +6,12 @@ LT_INIT([[disable-static]]) -AC_DEFINE([[PEER_NAME]], [["-lt1005-"]], [[Identifier that is part of the default peer id.]]) -AC_DEFINE([[PEER_VERSION]], [["lt\x10\x05"]], [[4 byte client and version identifier for DHT.]]) +# When releasing the first 1.x.y version, we need to start with 1.1.0 (or higher) as we've already +# used 0.16.x. +AC_DEFINE([[PEER_NAME]], [["-lt1006-"]], [[Identifier that is part of the default peer id.]]) +AC_DEFINE([[PEER_VERSION]], [["lt\x10\x06"]], [[4 byte client and version identifier for DHT.]]) -LIBTORRENT_CURRENT=35 +LIBTORRENT_CURRENT=36 LIBTORRENT_REVISION=0 LIBTORRENT_AGE=0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/data/hash_torrent.cc new/libtorrent-0.16.6/src/data/hash_torrent.cc --- old/libtorrent-0.16.5/src/data/hash_torrent.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/data/hash_torrent.cc 2026-01-02 15:37:32.000000000 +0100 @@ -140,7 +140,7 @@ } if (handle.error_number().is_valid() && handle.error_number().value() != rak::error_number::e_noent) { - LT_LOG_THIS(DEBUG, "Return on handle errno == E_NOENT: position:%u.", m_position); + LT_LOG_THIS(DEBUG, "Return on handle errno != E_NOENT: position:%u.", m_position); return; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/net/curl_socket.cc new/libtorrent-0.16.6/src/net/curl_socket.cc --- old/libtorrent-0.16.5/src/net/curl_socket.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/net/curl_socket.cc 2026-01-02 15:37:32.000000000 +0100 @@ -3,6 +3,7 @@ #include "curl_socket.h" #include <cassert> +#include <unistd.h> #include <curl/multi.h> #include "net/curl_stack.h" @@ -11,20 +12,33 @@ namespace torrent::net { +CurlSocket::CurlSocket(int fd, CurlStack* stack, CURL* easy_handle) + : m_stack(stack), + m_easy_handle(easy_handle) { + m_fileDesc = fd; +} + int -CurlSocket::receive_socket([[maybe_unused]] void* easy_handle, curl_socket_t fd, int what, CurlStack* stack, CurlSocket* socket) { +CurlSocket::receive_socket(CURL* easy_handle, curl_socket_t fd, int what, CurlStack* stack, CurlSocket* socket) { // We always return 0, even when stack is not running, as we depend on these calls to delete // sockets. if (what == CURL_POLL_REMOVE) { if (socket == nullptr) + throw internal_error("CurlSocket::receive_socket() called with CURL_POLL_REMOVE and null socket."); + + // LibCurl already called close_socket(). + if (socket->m_fileDesc == -1) { + delete socket; return 0; + } - // We also probably need the special code here as we're not - // guaranteed that the fd will be closed, afaik. - socket->close(); + if (socket->m_fileDesc != fd) + throw internal_error("CurlSocket::receive_socket() CURL_POLL_REMOVE fd mismatch."); + socket->close(); delete socket; + return 0; } @@ -32,11 +46,11 @@ if (!stack->is_running()) return 0; - // TODO: Assign nullptr here.... - // TODO: Might not be getting removed ? verify fd, etc. + socket = new CurlSocket(fd, stack, easy_handle); - socket = new CurlSocket(fd, stack); curl_multi_assign(stack->handle(), fd, socket); + curl_easy_setopt(easy_handle, CURLOPT_CLOSESOCKETDATA, socket); + curl_easy_setopt(easy_handle, CURLOPT_CLOSESOCKETFUNCTION, &CurlSocket::close_socket); this_thread::poll()->open(socket); this_thread::poll()->insert_error(socket); @@ -62,6 +76,29 @@ return 0; } +// When receive_socket() is called with CURL_POLL_REMOVE, we call CurlSocket::close() which +// deregisters this callback. +int +CurlSocket::close_socket(CurlSocket* socket, curl_socket_t fd) { + if (socket == nullptr) + throw internal_error("CurlSocket::close_socket() called with null socket."); + + if (fd != socket->m_fileDesc) + throw internal_error("CurlSocket::close_socket() fd mismatch."); + + socket->close(); + + if (::close(fd) != 0) + throw internal_error("CurlSocket::close_socket() error closing fd."); + + // We assume the socket is deleted in receive_socket() after CURL_POLL_REMOVE. + // + // TODO: We need to verify that libcurl calls CURL_POLL_REMOVE after this. + + // delete socket; + return 0; +} + CurlSocket::~CurlSocket() { assert(m_fileDesc == -1 && "CurlSocket::~CurlSocket() m_fileDesc != -1."); } @@ -69,10 +106,14 @@ void CurlSocket::close() { if (m_fileDesc == -1) - throw internal_error("CurlSocket::close() m_fileDesc == -1."); + return; this_thread::poll()->remove_and_close(this); + // Deregister close callback for when receive_socket() is called with CURL_POLL_REMOVE, as this + // CurlSocket is deleted. + curl_easy_setopt(m_easy_handle, CURLOPT_CLOSESOCKETFUNCTION, nullptr); + m_stack = nullptr; m_fileDesc = -1; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/net/curl_socket.h new/libtorrent-0.16.6/src/net/curl_socket.h --- old/libtorrent-0.16.5/src/net/curl_socket.h 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/net/curl_socket.h 2026-01-02 15:37:32.000000000 +0100 @@ -11,26 +11,28 @@ class CurlSocket : public torrent::Event { public: - CurlSocket(int fd, CurlStack* stack) : m_stack(stack) { m_fileDesc = fd; } + CurlSocket(int fd, CurlStack* stack, CURL* easy_handle); ~CurlSocket() override; - const char* type_name() const override { return "curl_socket"; } + const char* type_name() const override { return "curl_socket"; } - void close(); + void close(); - void event_read() override; - void event_write() override; - void event_error() override; + void event_read() override; + void event_write() override; + void event_error() override; - static int receive_socket(void* easy_handle, curl_socket_t fd, int what, CurlStack* userp, CurlSocket* socketp); + static int receive_socket(CURL* easy_handle, curl_socket_t fd, int what, CurlStack* userp, CurlSocket* socketp); + static int close_socket(CurlSocket* socket, curl_socket_t item); private: CurlSocket(const CurlSocket&) = delete; CurlSocket& operator=(const CurlSocket&) = delete; - void handle_action(int ev_bitmask); + void handle_action(int ev_bitmask); - CurlStack* m_stack{}; + CurlStack* m_stack{}; + CURL* m_easy_handle{}; }; } // namespace torrent::net diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/net/listen.h new/libtorrent-0.16.6/src/net/listen.h --- old/libtorrent-0.16.5/src/net/listen.h 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/net/listen.h 2026-01-02 15:37:32.000000000 +0100 @@ -13,6 +13,8 @@ public: ~Listen() override { close(); } + const char* type_name() const override { return "listen"; } + static bool open_single(Listen* listen, const sockaddr* bind_address, uint16_t first, uint16_t last, int backlog, bool block_ipv4in6); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/protocol/handshake_manager.cc new/libtorrent-0.16.6/src/protocol/handshake_manager.cc --- old/libtorrent-0.16.5/src/protocol/handshake_manager.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/protocol/handshake_manager.cc 2026-01-02 15:37:32.000000000 +0100 @@ -110,7 +110,11 @@ manager->connection_manager()->inc_socket_count(); auto handshake = std::make_unique<Handshake>(fd, this, config::network_config()->encryption_options()); - handshake->initialize_incoming(sa); + + if (sa_is_v4mapped(sa)) + handshake->initialize_incoming(sa_from_v4mapped(sa).get()); + else + handshake->initialize_incoming(sa); base_type::push_back(std::move(handshake)); } @@ -130,18 +134,14 @@ encryption_options &= ~net::NetworkConfig::encryption_enable_retry; } - create_outgoing(sa, download, encryption_options); + if (sa_is_v4mapped(sa)) + create_outgoing(sa_from_v4mapped(sa).get(), download, encryption_options); + else + create_outgoing(sa, download, encryption_options); } void HandshakeManager::create_outgoing(const sockaddr* sa, DownloadMain* download, int encryption_options) { - auto connect_address = [sa]() { - if (sa_is_v4mapped(sa)) - return sa_from_v4mapped(sa); - else - return sa_copy(sa); - }(); - int connection_options = PeerList::connect_keep_handshakes; if (!(encryption_options & net::NetworkConfig::encryption_retrying)) @@ -150,10 +150,11 @@ PeerInfo* peer_info = download->peer_list()->connected(sa, connection_options); if (peer_info == NULL || peer_info->failed_counter() > max_failed) { - LT_LOG_SAP(connect_address, "rejected outgoing connection: no peer info or too many failures", 0); + LT_LOG_SA(sa, "rejected outgoing connection: no peer info or too many failures", 0); return; } + auto connect_address = sa_copy(sa); auto proxy_address = config::network_config()->proxy_address(); if (proxy_address->sa_family != AF_UNSPEC) { @@ -177,8 +178,12 @@ else message = ConnectionManager::handshake_outgoing; - LT_LOG_SAP(connect_address, "created outgoing connection: fd:%i address:%s encryption:%x message:%x", - fd, sa_pretty_str(sa).c_str(), encryption_options, message); + if (proxy_address->sa_family != AF_UNSPEC) { + LT_LOG_SA(sa, "created outgoing connection via proxy: fd:%i proxy:%s encryption:%x message:%x", + fd, sa_addr_str(proxy_address.get()).c_str(), encryption_options, message); + } else { + LT_LOG_SA(sa, "created outgoing connection: fd:%i encryption:%x message:%x", fd, encryption_options, message); + } manager->connection_manager()->inc_socket_count(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/common.h new/libtorrent-0.16.6/src/torrent/common.h --- old/libtorrent-0.16.5/src/torrent/common.h 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/common.h 2026-01-02 15:37:32.000000000 +0100 @@ -134,9 +134,9 @@ std::chrono::microseconds cached_time() LIBTORRENT_EXPORT; std::chrono::seconds cached_seconds() LIBTORRENT_EXPORT; -void callback(void* target, std::function<void ()>&& fn); -void cancel_callback(void* target); -void cancel_callback_and_wait(void* target); +void callback(void* target, std::function<void ()>&& fn) LIBTORRENT_EXPORT; +void cancel_callback(void* target) LIBTORRENT_EXPORT; +void cancel_callback_and_wait(void* target) LIBTORRENT_EXPORT; net::Poll* poll() LIBTORRENT_EXPORT; net::Resolver* resolver() LIBTORRENT_EXPORT; @@ -161,9 +161,9 @@ torrent::utils::Thread* thread() LIBTORRENT_EXPORT; std::thread::id thread_id() LIBTORRENT_EXPORT; -void callback(void* target, std::function<void ()>&& fn); -void cancel_callback(void* target); -void cancel_callback_and_wait(void* target); +void callback(void* target, std::function<void ()>&& fn) LIBTORRENT_EXPORT; +void cancel_callback(void* target) LIBTORRENT_EXPORT; +void cancel_callback_and_wait(void* target) LIBTORRENT_EXPORT; uint32_t hash_queue_size() LIBTORRENT_EXPORT; @@ -174,9 +174,9 @@ torrent::utils::Thread* thread() LIBTORRENT_EXPORT; std::thread::id thread_id() LIBTORRENT_EXPORT; -void callback(void* target, std::function<void ()>&& fn); -void cancel_callback(void* target); -void cancel_callback_and_wait(void* target); +void callback(void* target, std::function<void ()>&& fn) LIBTORRENT_EXPORT; +void cancel_callback(void* target) LIBTORRENT_EXPORT; +void cancel_callback_and_wait(void* target) LIBTORRENT_EXPORT; torrent::net::HttpStack* http_stack() LIBTORRENT_EXPORT; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/connection_manager.cc new/libtorrent-0.16.6/src/torrent/connection_manager.cc --- old/libtorrent-0.16.5/src/torrent/connection_manager.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/connection_manager.cc 2026-01-02 15:37:32.000000000 +0100 @@ -25,11 +25,17 @@ if (config::network_config()->is_block_ipv6() && sa_is_inet6(sa)) return 0; - if (config::network_config()->is_block_ipv4in6() && sa_is_v4mapped(sa)) - return 0; + if (sa_is_v4mapped(sa)) { + if (config::network_config()->is_block_ipv4in6()) + return 0; + + if (m_slot_filter) + return m_slot_filter(sa_from_v4mapped(sa).get()); - if (m_slot_filter) - return m_slot_filter(sa); + } else { + if (m_slot_filter) + return m_slot_filter(sa); + } return 1; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/download.cc new/libtorrent-0.16.6/src/torrent/download.cc --- old/libtorrent-0.16.5/src/torrent/download.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/download.cc 2026-01-02 15:37:32.000000000 +0100 @@ -129,8 +129,16 @@ m_ptr->main()->tracker_controller().disable(); } +// When session data is loaded, it includes a bitfield and list of files+mtime. +// +// As the download is opened it checks the mtimes of the files, if the file is missing or has wrong mtime then +// the file's range is added to hashing_ranges() and the bitfield range is cleared. +// +// Hash check with try_quick never does any actual hashing, it only checks if the underlying file +// exists and if it does it fails the whole try_quick hash check. + bool -Download::hash_check(bool tryQuick) { +Download::hash_check(bool try_quick) { if (m_ptr->hash_checker()->is_checking()) throw internal_error("Download::hash_check(...) called but the hash is already being checked."); @@ -142,7 +150,7 @@ Bitfield* bitfield = m_ptr->data()->mutable_completed_bitfield(); - LT_LOG_THIS(INFO, "Checking hash: allocated:%i try_quick:%i.", !bitfield->empty(), (int)tryQuick); + LT_LOG_THIS(INFO, "Checking hash: allocated:%i try_quick:%i.", !bitfield->empty(), (int)try_quick); if (bitfield->empty()) { // The bitfield still hasn't been allocated, so no resume data was @@ -151,11 +159,19 @@ bitfield->unset_all(); m_ptr->hash_checker()->hashing_ranges().insert(0, m_ptr->main()->file_list()->size_chunks()); + + } else if (!try_quick) { + // Clear ranges we're about to recheck so mark_completed can re-set them. + for (auto range : m_ptr->hash_checker()->hashing_ranges()) + bitfield->unset_range(range.first, range.second); + + // TODO: Consider adding a sanity check above instead, and print out the files (size+range) of + // the invalid marked bit. } m_ptr->main()->file_list()->update_completed(); - return m_ptr->hash_checker()->start(tryQuick); + return m_ptr->hash_checker()->start(try_quick); } // Propably not correct, need to clear content, etc. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/event.cc new/libtorrent-0.16.6/src/torrent/event.cc --- old/libtorrent-0.16.5/src/torrent/event.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/event.cc 2026-01-02 15:37:32.000000000 +0100 @@ -2,14 +2,25 @@ #include "event.h" +#include <cassert> + #include "torrent/exceptions.h" #include "torrent/net/fd.h" namespace torrent { +Event::~Event() { + assert(m_poll_event == nullptr && "Event::~Event() called with m_poll_event != nullptr."); +} + const char* Event::type_name() const { - return "default"; + throw internal_error("Event::type_name() must be overridden in derived class."); +} + +std::string +Event::print_name_fd_str() const { + return "name:" + std::string(type_name()) + " fd:" + std::to_string(file_descriptor()); } void diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/event.h new/libtorrent-0.16.6/src/torrent/event.h --- old/libtorrent-0.16.5/src/torrent/event.h 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/event.h 2026-01-02 15:37:32.000000000 +0100 @@ -1,19 +1,26 @@ #ifndef LIBTORRENT_TORRENT_EVENT_H #define LIBTORRENT_TORRENT_EVENT_H +#include <memory> #include <torrent/common.h> namespace torrent { +namespace net { +class PollEvent; +class PollInternal; +} + class LIBTORRENT_EXPORT Event { public: - virtual ~Event() = default; + virtual ~Event(); bool is_open() const; int file_descriptor() const; - // TODO: Require all to define their own typename. + std::string print_name_fd_str() const; + virtual const char* type_name() const; // TODO: Make these protected. @@ -21,12 +28,16 @@ virtual void event_write() = 0; virtual void event_error() = 0; - protected: + friend class net::Poll; + friend class net::PollInternal; + void close_file_descriptor(); void set_file_descriptor(int fd); - // TODO: Rename to m_fd. + std::shared_ptr<net::PollEvent> m_poll_event; + + // TODO: replace by m_poll_event->fd() int m_fileDesc{-1}; // TODO: Deprecate. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/hash_string.cc new/libtorrent-0.16.6/src/torrent/hash_string.cc --- old/libtorrent-0.16.5/src/torrent/hash_string.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/hash_string.cc 2026-01-02 15:37:32.000000000 +0100 @@ -1,39 +1,3 @@ -// libTorrent - BitTorrent library -// Copyright (C) 2005-2011, Jari Sundell -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations -// including the two. -// -// You must obey the GNU General Public License in all respects for -// all of the code used other than OpenSSL. If you modify file(s) -// with this exception, you may extend this exception to your version -// of the file(s), but you are not obligated to do so. If you do not -// wish to do so, delete this exception statement from your version. -// If you delete this exception statement from all source files in the -// program, then also delete it here. -// -// Contact: Jari Sundell <[email protected]> -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - #include "config.h" #include <rak/string_manip.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/net/poll_epoll.cc new/libtorrent-0.16.6/src/torrent/net/poll_epoll.cc --- old/libtorrent-0.16.5/src/torrent/net/poll_epoll.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/net/poll_epoll.cc 2026-01-02 15:37:32.000000000 +0100 @@ -14,6 +14,9 @@ #include "torrent/utils/log.h" #include "torrent/utils/thread.h" +#define LT_LOG(log_fmt, ...) \ + lt_log_print(LOG_CONNECTION_FD, "epoll: " log_fmt, __VA_ARGS__); + #define LT_LOG_EVENT(log_fmt, ...) \ lt_log_print(LOG_CONNECTION_FD, "epoll->%i : %s : " log_fmt, event->file_descriptor(), event->type_name(), __VA_ARGS__); @@ -23,62 +26,57 @@ namespace torrent::net { +class PollEvent { +public: + PollEvent(Event* event) : event(event) {} + ~PollEvent() = default; + + uint32_t mask{}; + Event* event{}; +}; + class PollInternal { public: - using Table = std::vector<std::pair<uint32_t, Event*>>; + using Table = std::map<unsigned int, std::shared_ptr<PollEvent>>; - inline uint32_t event_mask(Event* e); - inline uint32_t event_mask_any(int fd); - inline void set_event_mask(Event* e, uint32_t m); + uint32_t event_mask(Event* event); + void set_event_mask(Event* event, uint32_t mask); + void flush(); void modify(torrent::Event* event, unsigned short op, uint32_t mask); int m_fd; - unsigned int m_max_events; + unsigned int m_max_sockets{}; + unsigned int m_max_events{}; unsigned int m_waiting_events{}; Table m_table; std::unique_ptr<struct epoll_event[]> m_events; }; -inline uint32_t -PollInternal::event_mask(Event* e) { - if (e->file_descriptor() == -1) - throw internal_error("PollInternal::event_mask() invalid file descriptor for event: name:" + std::string(e->type_name())); - - if (static_cast<unsigned int>(e->file_descriptor()) >= m_table.size()) - throw internal_error("PollInternal::event_mask() file descriptor out of range: name:" + std::string(e->type_name()) + " fd:" + std::to_string(e->file_descriptor())); - - if (m_table[e->file_descriptor()].second != e) - throw internal_error("PollInternal::event_mask() event mismatch: name:" + std::string(e->type_name()) + " fd:" + std::to_string(e->file_descriptor())); +uint32_t +PollInternal::event_mask(Event* event) { + if (event->file_descriptor() == -1) + throw internal_error("PollInternal::event_mask() invalid file descriptor for event: " + event->print_name_fd_str()); - return m_table[e->file_descriptor()].first; -} + auto itr = m_table.find(event->file_descriptor()); -inline uint32_t -PollInternal::event_mask_any(int fd) { - if (fd == -1) - throw internal_error("PollInternal::event_mask_any() invalid file descriptor for event"); + if (itr == m_table.end()) + throw internal_error("PollInternal::event_mask() event not found: " + event->print_name_fd_str()); - if (static_cast<unsigned int>(fd) >= m_table.size()) - throw internal_error("PollInternal::event_mask_any() file descriptor out of range: fd:" + std::to_string(fd)); + if (event != itr->second->event) + throw internal_error("PollInternal::event_mask() event mismatch: " + event->print_name_fd_str()); - return m_table[fd].first; + return itr->second->mask; } -inline void -PollInternal::set_event_mask(Event* e, uint32_t m) { - if (e->file_descriptor() == -1) - throw internal_error("PollInternal::set_event_mask() invalid file descriptor for event: name:" + std::string(e->type_name())); - - if (static_cast<unsigned int>(e->file_descriptor()) >= m_table.size()) - throw internal_error("PollInternal::set_event_mask() file descriptor out of range: name:" + std::string(e->type_name()) + " fd:" + std::to_string(e->file_descriptor())); - - if (m_table[e->file_descriptor()].second != e) - throw internal_error("PollInternal::set_event_mask() event mismatch: name:" + std::string(e->type_name()) + " fd:" + std::to_string(e->file_descriptor())); +void +PollInternal::set_event_mask(Event* event, uint32_t mask) { + if (event->file_descriptor() == -1) + throw internal_error("PollInternal::set_event_mask() invalid file descriptor for event: " + event->print_name_fd_str()); - m_table[e->file_descriptor()] = Table::value_type(m, e); + event->m_poll_event->mask = mask; } void @@ -88,9 +86,11 @@ LT_LOG_EVENT("modify event : op:%hx mask:%hx", op, mask); - epoll_event e; - e.data.u64 = 0; - e.data.fd = event->file_descriptor(); + if (event->m_poll_event == nullptr) + throw internal_error("PollInternal::modify() event has no PollEvent associated: " + event->print_name_fd_str()); + + epoll_event e{}; + e.data.ptr = event->m_poll_event.get(); e.events = mask; set_event_mask(event, mask); @@ -114,7 +114,7 @@ if (errno || epoll_ctl(m_fd, retry, event->file_descriptor(), &e)) { char errmsg[1024]; snprintf(errmsg, sizeof(errmsg), - "Poll::modify(...) epoll_ctl(%d, %d -> %d, %d, [%p:%x]) = %d: %s", + "PollInternal::modify() epoll_ctl(%d, %d -> %d, %d, [%p:%x]) = %d: %s", m_fd, op, retry, event->file_descriptor(), event, mask, errno, strerror(errno)); throw internal_error(errmsg); @@ -127,28 +127,29 @@ auto socket_open_max = sysconf(_SC_OPEN_MAX); if (socket_open_max == -1) - throw internal_error("Poll::create() : sysconf(_SC_OPEN_MAX) failed: " + std::string(std::strerror(errno))); + throw internal_error("Poll::create() : sysconf(_SC_OPEN_MAX) failed : " + std::string(std::strerror(errno))); int fd = epoll_create(socket_open_max); if (fd == -1) - return nullptr; + throw internal_error("Poll::create() : kqueue() failed : " + std::string(std::strerror(errno))); auto poll = new Poll(); - poll->m_internal = std::make_unique<PollInternal>(); - poll->m_internal->m_table.resize(socket_open_max); - poll->m_internal->m_fd = fd; - poll->m_internal->m_max_events = 1024; - poll->m_internal->m_events = std::make_unique<struct epoll_event[]>(poll->m_internal->m_max_events); + poll->m_internal = std::make_unique<PollInternal>(); + poll->m_internal->m_fd = fd; + poll->m_internal->m_max_sockets = static_cast<unsigned int>(socket_open_max); + poll->m_internal->m_max_events = 1024; + poll->m_internal->m_events = std::make_unique<struct epoll_event[]>(poll->m_internal->m_max_events); return std::unique_ptr<Poll>(poll); } Poll::~Poll() { - m_internal->m_table.clear(); + assert(m_internal->m_table.empty() && "Poll::~Poll() called with non-empty event table."); ::close(m_internal->m_fd); + m_internal->m_fd = -1; } unsigned int @@ -189,18 +190,14 @@ unsigned int count = 0; for (epoll_event *itr = m_internal->m_events.get(), *last = m_internal->m_events.get() + m_internal->m_waiting_events; itr != last; ++itr) { - if (itr->data.fd < 0) - throw internal_error("Poll::process() received negative file descriptor: " + std::to_string(itr->data.fd)); - - if (static_cast<size_t>(itr->data.fd) >= m_internal->m_table.size()) - throw internal_error("Poll::process() received invalid file descriptor: " + std::to_string(itr->data.fd)); - if (utils::Thread::self()->callbacks_should_interrupt_polling()) utils::Thread::self()->process_callbacks(true); - auto evItr = m_internal->m_table.begin() + itr->data.fd; + auto* poll_event = static_cast<PollEvent*>(itr->data.ptr); + auto* event = poll_event->event; - if (evItr->second == nullptr) { + if (event == nullptr) { + // TODO: This should fail. LT_LOG_DEBUG_DATA_FD("event is null, skipping : events:%hx", itr->events); continue; } @@ -211,22 +208,30 @@ // TODO: Make it so that it checks that read/write is wanted, that // it wasn't removed from one of them but not closed. - if (itr->events & EPOLLERR && evItr->first & EPOLLERR) { - count++; - evItr->second->event_error(); + if (itr->events & EPOLLERR && poll_event->mask & EPOLLERR) { + if (!(poll_event->mask & EPOLLERR)) + throw internal_error("Poll::process() received error event for event not in error: " + poll_event->event->print_name_fd_str()); + + auto event_info = event->print_name_fd_str(); + + event->event_error(); + + if (poll_event->mask != 0) + throw internal_error("Poll::process() event_error called but event mask not cleared: " + event_info); // We assume that the event gets closed if we get an error. + count++; continue; } - if (itr->events & EPOLLIN && evItr->first & EPOLLIN) { + if (itr->events & EPOLLIN && poll_event->mask & EPOLLIN) { + event->event_read(); count++; - evItr->second->event_read(); } - if (itr->events & EPOLLOUT && evItr->first & EPOLLOUT) { + if (itr->events & EPOLLOUT && poll_event->mask & EPOLLOUT) { + event->event_write(); count++; - evItr->second->event_write(); } } @@ -236,35 +241,47 @@ uint32_t Poll::open_max() const { - return m_internal->m_table.size(); + return m_internal->m_max_sockets; } void Poll::open(Event* event) { LT_LOG_EVENT("open event", 0); - if (m_internal->event_mask_any(event->file_descriptor()) != 0) - throw internal_error("Poll::open() called but the file descriptor is active"); + if (event->file_descriptor() == -1) + throw internal_error("Poll::open() invalid file descriptor for event: " + event->print_name_fd_str()); - m_internal->m_table[event->file_descriptor()] = PollInternal::Table::value_type(0, event); + if (event->m_poll_event != nullptr) + throw internal_error("Poll::open() called but the event is already associated with a poll: " + event->print_name_fd_str()); + + if (m_internal->m_table.find(event->file_descriptor()) != m_internal->m_table.end()) + throw internal_error("Poll::open() event already exists: " + event->print_name_fd_str()); + + event->m_poll_event = std::make_shared<PollEvent>(event); + + m_internal->m_table[event->file_descriptor()] = event->m_poll_event; } void Poll::close(Event* event) { LT_LOG_EVENT("close event", 0); + auto* poll_event = event->m_poll_event.get(); + + if (poll_event == nullptr) + return; + + if (poll_event->event != event) + throw internal_error("Poll::close() event mismatch: " + event->print_name_fd_str()); + if (m_internal->event_mask(event) != 0) - throw internal_error("Poll::close() called but the file descriptor is active"); + throw internal_error("Poll::close() called but the file descriptor is active: " + event->print_name_fd_str()); - m_internal->m_table[event->file_descriptor()] = PollInternal::Table::value_type(); + if (m_internal->m_table.erase(event->file_descriptor()) == 0) + throw internal_error("Poll::close() event not found: " + event->print_name_fd_str()); - // Clear the event list just in case we open a new socket with the - // same fd while in the middle of calling Poll::perform. - // - // Removed. - // - // Shouldn't be needed as we unset the read/write/error flags in m_internal->m_events using - // remove_read/write/error. + poll_event->event = nullptr; + event->m_poll_event.reset(); } bool diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/net/poll_kqueue.cc new/libtorrent-0.16.6/src/torrent/net/poll_kqueue.cc --- old/libtorrent-0.16.5/src/torrent/net/poll_kqueue.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/net/poll_kqueue.cc 2026-01-02 15:37:32.000000000 +0100 @@ -7,8 +7,9 @@ #include <algorithm> #include <cassert> #include <cerrno> -#include <sys/event.h> +#include <map> #include <unistd.h> +#include <sys/event.h> #include "utils/log.h" #include "utils/thread.h" @@ -31,24 +32,33 @@ namespace torrent::net { +class PollEvent { +public: + PollEvent(Event* e) : event(e) {} + ~PollEvent() = default; + + uint32_t mask{}; + Event* event{}; +}; + class PollInternal { public: - using Table = std::vector<std::pair<uint32_t, Event*>>; + using Table = std::map<unsigned int, std::shared_ptr<PollEvent>>; + + static constexpr uint32_t flag_read = 0x1; + static constexpr uint32_t flag_write = 0x2; + static constexpr uint32_t flag_error = 0x4; - static constexpr uint32_t flag_read = (1 << 0); - static constexpr uint32_t flag_write = (1 << 1); - static constexpr uint32_t flag_error = (1 << 2); - - inline uint32_t event_mask(Event* e); - inline uint32_t event_mask_any(int fd); - inline void set_event_mask(Event* e, uint32_t m); + uint32_t event_mask(Event* event); + void set_event_mask(Event* event, uint32_t mask); void flush(); void modify(torrent::Event* event, unsigned short op, short mask); int m_fd; - unsigned int m_max_events; + unsigned int m_max_sockets{}; + unsigned int m_max_events{}; unsigned int m_waiting_events{}; unsigned int m_changed_events{}; @@ -57,43 +67,30 @@ std::unique_ptr<struct kevent[]> m_changes; }; -inline uint32_t -PollInternal::event_mask(Event* e) { - if (e->file_descriptor() == -1) - throw internal_error("PollInternal::event_mask() invalid file descriptor for event: name:" + std::string(e->type_name())); - - if (static_cast<unsigned int>(e->file_descriptor()) >= m_table.size()) - throw internal_error("PollInternal::event_mask() file descriptor out of range: name:" + std::string(e->type_name()) + " fd:" + std::to_string(e->file_descriptor())); +uint32_t +PollInternal::event_mask(Event* event) { + // TODO: Replace `file_descriptor()` with m_event_poll. - if (e != m_table[e->file_descriptor()].second) - throw internal_error("PollInternal::event_mask() event mismatch: name:" + std::string(e->type_name()) + " fd:" + std::to_string(e->file_descriptor())); + if (event->file_descriptor() == -1) + throw internal_error("PollInternal::event_mask() invalid file descriptor for event: " + event->print_name_fd_str()); - return m_table[e->file_descriptor()].first; -} + auto itr = m_table.find(event->file_descriptor()); -inline uint32_t -PollInternal::event_mask_any(int fd) { - if (fd == -1) - throw internal_error("PollInternal::event_mask_any() invalid file descriptor for event"); + if (itr == m_table.end()) + throw internal_error("PollInternal::event_mask() event not found: " + event->print_name_fd_str()); - if (static_cast<unsigned int>(fd) >= m_table.size()) - throw internal_error("PollInternal::event_mask_any() file descriptor out of range: fd:" + std::to_string(fd)); + if (event != itr->second->event) + throw internal_error("PollInternal::event_mask() event mismatch: " + event->print_name_fd_str()); - return m_table[fd].first; + return itr->second->mask; } -inline void -PollInternal::set_event_mask(Event* e, uint32_t m) { - if (e->file_descriptor() == -1) - throw internal_error("PollInternal::set_event_mask() invalid file descriptor for event: name:" + std::string(e->type_name())); - - if (static_cast<unsigned int>(e->file_descriptor()) >= m_table.size()) - throw internal_error("PollInternal::set_event_mask() file descriptor out of range: name:" + std::string(e->type_name()) + " fd:" + std::to_string(e->file_descriptor())); - - if (e != m_table[e->file_descriptor()].second) - throw internal_error("PollInternal::set_event_mask() event mismatch: name:" + std::string(e->type_name()) + " fd:" + std::to_string(e->file_descriptor())); +void +PollInternal::set_event_mask(Event* event, uint32_t mask) { + if (event->file_descriptor() == -1) + throw internal_error("PollInternal::set_event_mask() invalid file descriptor for event: " + event->print_name_fd_str()); - m_table[e->file_descriptor()] = Table::value_type(m, e); + event->m_poll_event->mask = mask; } void @@ -119,8 +116,7 @@ struct kevent* itr = m_changes.get() + (m_changed_events++); - assert(event == m_table[event->file_descriptor()].second); - EV_SET(itr, event->file_descriptor(), mask, op, 0, 0, event); + EV_SET(itr, event->file_descriptor(), mask, op, 0, 0, event->m_poll_event.get()); } std::unique_ptr<Poll> @@ -128,32 +124,30 @@ auto socket_open_max = sysconf(_SC_OPEN_MAX); if (socket_open_max == -1) - throw internal_error("Poll::create() : sysconf(_SC_OPEN_MAX) failed: " + std::string(std::strerror(errno))); + throw internal_error("Poll::create() : sysconf(_SC_OPEN_MAX) failed : " + std::string(std::strerror(errno))); - int fd = kqueue(); + int fd = ::kqueue(); if (fd == -1) - return nullptr; + throw internal_error("Poll::create() : kqueue() failed : " + std::string(std::strerror(errno))); auto poll = new Poll(); - poll->m_internal = std::make_unique<PollInternal>(); - poll->m_internal->m_table.resize(socket_open_max); - poll->m_internal->m_fd = fd; - poll->m_internal->m_max_events = 1024; - poll->m_internal->m_events = std::make_unique<struct kevent[]>(poll->m_internal->m_max_events); - - // TODO: Dynamically resize. - // !!!!! check if correct size - poll->m_internal->m_changes = std::make_unique<struct kevent[]>(socket_open_max); + poll->m_internal = std::make_unique<PollInternal>(); + poll->m_internal->m_fd = fd; + poll->m_internal->m_max_sockets = static_cast<unsigned int>(socket_open_max); + poll->m_internal->m_max_events = 1024; + poll->m_internal->m_events = std::make_unique<struct kevent[]>(poll->m_internal->m_max_events); + poll->m_internal->m_changes = std::make_unique<struct kevent[]>(poll->m_internal->m_max_events); return std::unique_ptr<Poll>(poll); } Poll::~Poll() { - m_internal->m_table.clear(); + assert(m_internal->m_table.empty() && "Poll::~Poll() called with non-empty event table."); ::close(m_internal->m_fd); + m_internal->m_fd = -1; } unsigned int @@ -162,7 +156,7 @@ if (status == -1) { if (errno != EINTR) - throw internal_error("Poll::work() " + std::string(std::strerror(errno))); + throw internal_error("Poll::do_poll() error: " + std::string(std::strerror(errno))); return 0; } @@ -201,42 +195,45 @@ unsigned int count = 0; for (struct kevent *itr = m_internal->m_events.get(), *last = m_internal->m_events.get() + m_internal->m_waiting_events; itr != last; ++itr) { - if (itr->ident >= m_internal->m_table.size()) - throw internal_error("Poll::process() received ident out of range: " + std::to_string(itr->ident)); - if (utils::Thread::self()->callbacks_should_interrupt_polling()) utils::Thread::self()->process_callbacks(true); - auto ev_itr = m_internal->m_table.begin() + itr->ident; + auto* poll_event = static_cast<PollEvent*>(itr->udata); + auto* event = poll_event->event; - if (ev_itr->second == nullptr) { - LT_LOG_DEBUG_IDENT("event is null, skipping : flags:%hx fflag:%hx filter:%hx", itr->flags, itr->fflags, itr->filter); + if (event == nullptr) { + // TODO: This should fail. + LT_LOG_DEBUG_IDENT("event is null, skipping : udata:%p", itr->udata); continue; } if ((itr->flags & EV_ERROR)) { - if (ev_itr->first & PollInternal::flag_error) - ev_itr->second->event_error(); + if (!(poll_event->mask & PollInternal::flag_error)) + throw internal_error("Poll::process() received error event for event not in error: " + poll_event->event->print_name_fd_str()); - count++; + auto event_info = event->print_name_fd_str(); + + event->event_error(); + + if (poll_event->mask != 0) + throw internal_error("Poll::process() event_error called but event mask not cleared: " + event_info); // We assume that the event gets closed if we get an error. + count++; continue; } - // Also check current mask. - - if (itr->filter == EVFILT_READ && ev_itr->first & PollInternal::flag_read) { + if (itr->filter == EVFILT_READ && (poll_event->mask & PollInternal::flag_read)) { count++; - ev_itr->second->event_read(); + event->event_read(); } else if (itr->filter == EVFILT_READ) { LT_LOG_DEBUG_IDENT("spurious read event, skipping", 0); } - if (itr->filter == EVFILT_WRITE && ev_itr->first & PollInternal::flag_write) { + if (itr->filter == EVFILT_WRITE && (poll_event->mask & PollInternal::flag_write)) { count++; - ev_itr->second->event_write(); + event->event_write(); } else if (itr->filter == EVFILT_WRITE) { LT_LOG_DEBUG_IDENT("spurious write event, skipping", 0); @@ -250,29 +247,49 @@ uint32_t Poll::open_max() const { - return m_internal->m_table.size(); + return m_internal->m_max_sockets; } void Poll::open(Event* event) { LT_LOG_EVENT("open event", 0); - if (m_internal->event_mask_any(event->file_descriptor()) != 0) - throw internal_error("Poll::open() called but the file descriptor is active"); + if (event->file_descriptor() == -1) + throw internal_error("Poll::open() invalid file descriptor for event: " + event->print_name_fd_str()); + + if (event->m_poll_event != nullptr) + throw internal_error("Poll::open() called but the event is already associated with a poll: " + event->print_name_fd_str()); - m_internal->m_table[event->file_descriptor()] = PollInternal::Table::value_type(0, event); + if (m_internal->m_table.find(event->file_descriptor()) != m_internal->m_table.end()) + throw internal_error("Poll::open() event already exists: " + event->print_name_fd_str()); + + event->m_poll_event = std::make_shared<PollEvent>(event); + + m_internal->m_table[event->file_descriptor()] = event->m_poll_event; } void Poll::close(Event* event) { LT_LOG_EVENT("close event", 0); + auto* poll_event = event->m_poll_event.get(); + + if (poll_event == nullptr) + return; + + if (poll_event->event != event) + throw internal_error("Poll::close() event mismatch: " + event->print_name_fd_str()); + if (m_internal->event_mask(event) != 0) - throw internal_error("Poll::close() called but the file descriptor is active"); + throw internal_error("Poll::close() called but the file descriptor is active: " + event->print_name_fd_str()); - m_internal->m_table[event->file_descriptor()] = PollInternal::Table::value_type(); + if (m_internal->m_table.erase(event->file_descriptor()) == 0) + throw internal_error("Poll::close() event not found: " + event->print_name_fd_str()); m_internal->flush(); + + poll_event->event = nullptr; + event->m_poll_event.reset(); } bool @@ -366,7 +383,7 @@ remove_read(event); remove_write(event); - // remove_error(event); + remove_error(event); close(event); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/utils/log.h new/libtorrent-0.16.6/src/torrent/utils/log.h --- old/libtorrent-0.16.5/src/torrent/utils/log.h 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/utils/log.h 2026-01-02 15:37:32.000000000 +0100 @@ -20,13 +20,6 @@ LOG_INFO, LOG_DEBUG, - // LOG_DHT_CRITICAL, - // LOG_DHT_ERROR, - // LOG_DHT_WARN, - // LOG_DHT_NOTICE, - // LOG_DHT_INFO, - // LOG_DHT_DEBUG, - LOG_PEER_CRITICAL, LOG_PEER_ERROR, LOG_PEER_WARN, @@ -102,6 +95,7 @@ LOG_RPC_EVENTS, LOG_RPC_DUMP, + LOG_SESSION_EVENTS, LOG_STORAGE, LOG_SYSTEM, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/utils/option_strings.cc new/libtorrent-0.16.6/src/torrent/utils/option_strings.cc --- old/libtorrent-0.16.5/src/torrent/utils/option_strings.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/utils/option_strings.cc 2026-01-02 15:37:32.000000000 +0100 @@ -183,6 +183,7 @@ "rpc_events", "rpc_dump", + "session_events", "storage", "system", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/utils/thread.cc new/libtorrent-0.16.6/src/torrent/utils/thread.cc --- old/libtorrent-0.16.5/src/torrent/utils/thread.cc 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/utils/thread.cc 2026-01-02 15:37:32.000000000 +0100 @@ -21,6 +21,15 @@ thread_local Thread* Thread::m_self{nullptr}; +Thread::~Thread() = default; + +Thread* Thread::self() { return m_self; } + +void Thread::init_thread() {} +void Thread::init_thread_pre_start() {} +void Thread::init_thread_post_local() {} +void Thread::cleanup_thread() {} + Thread::Thread() : m_instrumentation_index(INSTRUMENTATION_POLLING_DO_POLL_OTHERS - INSTRUMENTATION_POLLING_DO_POLL), m_poll(net::Poll::create()), @@ -32,13 +41,6 @@ m_scheduler->set_cached_time(m_cached_time); } -Thread::~Thread() = default; - -Thread* -Thread::self() { - return m_self; -} - void Thread::start_thread() { if (m_poll == nullptr) @@ -47,6 +49,8 @@ if (!is_initialized()) throw internal_error("Called Thread::start_thread on an uninitialized object."); + init_thread_pre_start(); + if (pthread_create(&m_thread, nullptr, &Thread::enter_event_loop, this)) throw internal_error("Failed to create thread."); @@ -141,6 +145,7 @@ m_poll->open(m_interrupt_receiver.get()); m_poll->insert_read(m_interrupt_receiver.get()); + m_poll->insert_error(m_interrupt_receiver.get()); while (true) { process_events(); @@ -171,10 +176,9 @@ lt_log_print(LOG_THREAD_NOTICE, "%s: Shutting down thread.", name()); } - // Some test, and perhaps other code, segfaults on this. - // TODO: Test - //m_poll->remove_read(m_interrupt_receiver.get()); - //m_poll->close(m_interrupt_receiver.get()); + m_poll->remove_read(m_interrupt_receiver.get()); + m_poll->remove_error(m_interrupt_receiver.get()); + m_poll->close(m_interrupt_receiver.get()); auto previous_state = STATE_ACTIVE; @@ -210,10 +214,6 @@ } void -Thread::init_thread_post_local() { -} - -void Thread::cleanup_thread_local() { lt_log_print(LOG_THREAD_NOTICE, "%s : cleaning up thread local data", name()); @@ -224,12 +224,6 @@ } void -Thread::set_cached_time(std::chrono::microseconds t) { - m_cached_time = t; - m_scheduler->set_cached_time(t); -} - -void Thread::process_events() { // TODO: We should call process_callbacks() here before and after call_events, however due to the // many different cached times in the code, we need to let each thread manage this themselves. @@ -283,6 +277,12 @@ } } +void +Thread::set_cached_time(std::chrono::microseconds t) { + m_cached_time = t; + m_scheduler->set_cached_time(t); +} + } // namespace torrent::utils namespace torrent::this_thread { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/src/torrent/utils/thread.h new/libtorrent-0.16.6/src/torrent/utils/thread.h --- old/libtorrent-0.16.5/src/torrent/utils/thread.h 2025-12-02 18:15:46.000000000 +0100 +++ new/libtorrent-0.16.6/src/torrent/utils/thread.h 2026-01-02 15:37:32.000000000 +0100 @@ -64,16 +64,7 @@ // auto signal_bitfield() { return &m_signal_bitfield; } - virtual void init_thread() = 0; - void init_thread_local(); - virtual void init_thread_post_local(); - - // It is assumed that any thread-specific resources no longer are accessed at the time - // cleanup_thread is called, or that those resources remain safe to call. - // - // This mean that e.g. tracker::Manager never gets called once thread_tracker is stopped. - virtual void cleanup_thread() = 0; - void cleanup_thread_local(); + virtual void init_thread(); void start_thread(); void stop_thread_wait(); @@ -97,8 +88,6 @@ net::Resolver* resolver() { return m_resolver.get(); } Scheduler* scheduler() { return m_scheduler.get(); } - void set_cached_time(std::chrono::microseconds t); - bool callbacks_should_interrupt_polling() const { return m_callbacks_should_interrupt_polling.load(); } static void* enter_event_loop(void* thread); @@ -106,10 +95,23 @@ virtual void call_events() = 0; virtual std::chrono::microseconds next_timeout() = 0; + virtual void init_thread_pre_start(); + void init_thread_local(); + virtual void init_thread_post_local(); + + // It is assumed that any thread-specific resources no longer are accessed at the time + // cleanup_thread is called, or that those resources remain safe to call. + // + // This mean that e.g. tracker::Manager never gets called once thread_tracker is stopped. + virtual void cleanup_thread(); + void cleanup_thread_local(); + void process_events(); void process_events_without_cached_time(); void process_callbacks(bool only_interrupt = false); + void set_cached_time(std::chrono::microseconds t); + static thread_local Thread* m_self; // TODO: Remove m_thread. @@ -135,6 +137,7 @@ std::multimap<const void*, std::function<void ()>> m_callbacks; std::multimap<const void*, std::function<void ()>> m_interrupt_callbacks; std::atomic<bool> m_callbacks_should_interrupt_polling{false}; + std::mutex m_callbacks_processing_lock; std::atomic<bool> m_callbacks_processing{false}; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.5/test/torrent/test_tracker_controller.cc new/libtorrent-0.16.6/test/torrent/test_tracker_controller.cc --- old/libtorrent-0.16.5/test/torrent/test_tracker_controller.cc 2025-12-02 18:15:47.000000000 +0100 +++ new/libtorrent-0.16.6/test/torrent/test_tracker_controller.cc 2026-01-02 15:37:32.000000000 +0100 @@ -80,7 +80,7 @@ void TestTrackerController::test_basic() { - torrent::TrackerController tracker_controller(NULL); + torrent::TrackerController tracker_controller(nullptr); CPPUNIT_ASSERT(tracker_controller.flags() == 0); CPPUNIT_ASSERT(!tracker_controller.is_active());
