From bbfd674e08c6b1fa4081f9c678fd220bead749d7 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Sat, 7 Nov 2026 03:55:27 +0100 Subject: Start to opentracker's memory requirements * peer lists for v4 and v6 are now lazily allocated and, more importantly, will be freed once there are no more peers * The downloaded count is now per torrent and not per peer list anymore, moving in what was alignment loss, anyway * The (now) per torrent downloaded and the per peer list peer and seed count shrunk from size_t to uint32_t * A helper guards accesses to the peer and seed count to prevent null pointer dereferences * The base time also shrank from time_t to uint32_t, minutes since the epoch currently fits in 25 bits --- src/ot_clean.c | 77 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 29 deletions(-) (limited to 'src/ot_clean.c') diff --git a/src/ot_clean.c b/src/ot_clean.c index eaf069a..77cb4ba 100644 --- a/src/ot_clean.c +++ b/src/ot_clean.c @@ -49,26 +49,17 @@ static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t pee return (peers - insert_point) / peer_size; } -int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) { - ot_vector *peer_vector = &peer_list->peers; - ot_time timedout = time_since_minutes(peer_list->base); - size_t num_buckets = 1, removed_seeders = 0; +/* Age one family's peers by timedout minutes. Frees the list and clears the + slot once its last peer is gone, so an absent family costs nothing. */ +static void clean_peer_list_family(ot_peerlist **slot, size_t peer_size, ot_time timedout) { + ot_peerlist *peer_list = *slot; + ot_vector *peer_vector; + size_t num_buckets = 1, removed_seeders = 0; - /* No need to clean empty torrent */ - if (!timedout) - return 0; - - /* Torrent has idled out */ - if (timedout > OT_TORRENT_TIMEOUT) - return 1; - - /* Nothing to be cleaned here? Test if torrent is worth keeping */ - if (timedout > OT_PEER_TIMEOUT) { - if (!peer_list->peer_count) - return peer_list->down_count ? 0 : 1; - timedout = OT_PEER_TIMEOUT; - } + if (!peer_list) + return; + peer_vector = &peer_list->peers; if (OT_PEERLIST_HASBUCKETS(peer_list)) { num_buckets = peer_vector->size; peer_vector = (ot_vector *)peer_vector->data; @@ -87,25 +78,53 @@ int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) { peer_list->seed_count -= removed_seeders; - /* See if we need to convert a torrent from simple vector to bucket list */ + if (!peer_list->peer_count) { + free_peerlist(peer_list); + *slot = NULL; + return; + } + + /* See if we need to convert the list from simple vector to bucket list */ if ((peer_list->peer_count > OT_PEER_BUCKET_MINCOUNT) || OT_PEERLIST_HASBUCKETS(peer_list)) vector_redistribute_buckets(peer_list, peer_size); - - if (peer_list->peer_count) - peer_list->base = time_now_minutes(); - else { - /* When we got here, the last time that torrent - has been touched is OT_PEER_TIMEOUT Minutes before */ - peer_list->base = time_now_minutes() - OT_PEER_TIMEOUT; - } - return 0; } /* Clean a single torrent return 1 if torrent timed out */ int clean_single_torrent(ot_torrent *torrent) { - return clean_single_peer_list(torrent->peer_list6, OT_PEER_SIZE6) * clean_single_peer_list(torrent->peer_list4, OT_PEER_SIZE4); + ot_time timedout = time_since_minutes(torrent->base); + + /* Touched since the last sweep: nothing to do */ + if (!timedout) + return 0; + + /* Torrent has idled out */ + if (timedout > OT_TORRENT_TIMEOUT) + return 1; + + /* Already empty and nothing arrived before the peer timeout: keep it only + while a down count is worth remembering */ + if (timedout > OT_PEER_TIMEOUT && !torrent->peer_list6 && !torrent->peer_list4) + return torrent->down_count ? 0 : 1; + + /* Peers past the peer timeout are gone regardless, which caps how far the + lists are aged in this pass */ + if (timedout > OT_PEER_TIMEOUT) + timedout = OT_PEER_TIMEOUT; + + clean_peer_list_family(&torrent->peer_list6, OT_PEER_SIZE6, timedout); + clean_peer_list_family(&torrent->peer_list4, OT_PEER_SIZE4, timedout); + + if (torrent->peer_list6 || torrent->peer_list4) + torrent->base = time_now_minutes(); /* still carrying peers */ + else { + /* Fell empty: hold base a peer timeout back so a later idle sweep makes + the keep-or-drop decision above */ + ot_time now = time_now_minutes(); + torrent->base = now > OT_PEER_TIMEOUT ? (uint32_t)(now - OT_PEER_TIMEOUT) : 0; + } + return 0; } /* Written to once, by clean_deinit, and never read: the byte stays in and -- cgit v1.3