diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-07 03:55:27 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-07 03:55:27 +0100 |
| commit | bbfd674e08c6b1fa4081f9c678fd220bead749d7 (patch) | |
| tree | 41c279103efda2765217a26c8164817d3db96606 /src/trackerlogic.c | |
| parent | f18339a50eed09ed47b2b22212d2a50e543e9b09 (diff) | |
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
Diffstat (limited to 'src/trackerlogic.c')
| -rw-r--r-- | src/trackerlogic.c | 109 |
1 files changed, 55 insertions, 54 deletions
diff --git a/src/trackerlogic.c b/src/trackerlogic.c index 2313379..6b0659c 100644 --- a/src/trackerlogic.c +++ b/src/trackerlogic.c | |||
| @@ -42,6 +42,7 @@ void free_peerlist(ot_peerlist *peer_list) { | |||
| 42 | what a state dump records. */ | 42 | what a state dump records. */ |
| 43 | void add_torrent_from_saved_state(ot_hash const hash, ot_time age, size_t down_count) { | 43 | void add_torrent_from_saved_state(ot_hash const hash, ot_time age, size_t down_count) { |
| 44 | int exactmatch; | 44 | int exactmatch; |
| 45 | ot_time now = time_now_minutes(); | ||
| 45 | ot_torrent *torrent; | 46 | ot_torrent *torrent; |
| 46 | ot_vector *torrents_list; | 47 | ot_vector *torrents_list; |
| 47 | 48 | ||
| @@ -61,19 +62,12 @@ void add_torrent_from_saved_state(ot_hash const hash, ot_time age, size_t down_c | |||
| 61 | return; | 62 | return; |
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | /* Create a new torrent entry, then */ | 65 | /* A restored torrent starts with no peers, carrying only its base and its |
| 66 | download count until the first announce brings a family list to life */ | ||
| 65 | memset(torrent, 0, sizeof(ot_torrent)); | 67 | memset(torrent, 0, sizeof(ot_torrent)); |
| 66 | memcpy(torrent->hash, hash, sizeof(ot_hash)); | 68 | memcpy(torrent->hash, hash, sizeof(ot_hash)); |
| 67 | 69 | torrent->base = now > age ? (uint32_t)(now - age) : 0; | |
| 68 | if (!(torrent->peer_list6 = calloc(1, sizeof(ot_peerlist))) || !(torrent->peer_list4 = calloc(1, sizeof(ot_peerlist)))) { | 70 | torrent->down_count = (uint32_t)down_count; |
| 69 | vector_remove_torrent(torrents_list, torrent); | ||
| 70 | mutex_bucket_unlock_by_hash(hash, 0); | ||
| 71 | return; | ||
| 72 | } | ||
| 73 | |||
| 74 | torrent->peer_list6->base = time_now_minutes() - age; | ||
| 75 | torrent->peer_list4->base = time_now_minutes() - age; | ||
| 76 | torrent->peer_list6->down_count = down_count; | ||
| 77 | 71 | ||
| 78 | mutex_bucket_unlock_by_hash(hash, 1); | 72 | mutex_bucket_unlock_by_hash(hash, 1); |
| 79 | } | 73 | } |
| @@ -113,21 +107,23 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 113 | /* Create a new torrent entry, then */ | 107 | /* Create a new torrent entry, then */ |
| 114 | memset(torrent, 0, sizeof(ot_torrent)); | 108 | memset(torrent, 0, sizeof(ot_torrent)); |
| 115 | memcpy(torrent->hash, *ws->hash, sizeof(ot_hash)); | 109 | memcpy(torrent->hash, *ws->hash, sizeof(ot_hash)); |
| 116 | |||
| 117 | if (!(torrent->peer_list6 = calloc(1, sizeof(ot_peerlist))) || !(torrent->peer_list4 = calloc(1, sizeof(ot_peerlist)))) { | ||
| 118 | vector_remove_torrent(torrents_list, torrent); | ||
| 119 | mutex_bucket_unlock_by_hash(*ws->hash, 0); | ||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | |||
| 123 | delta_torrentcount = 1; | 110 | delta_torrentcount = 1; |
| 124 | } else | 111 | } else |
| 125 | clean_single_torrent(torrent); | 112 | clean_single_torrent(torrent); |
| 126 | 113 | ||
| 127 | torrent->peer_list6->base = time_now_minutes(); | 114 | torrent->base = time_now_minutes(); |
| 128 | torrent->peer_list4->base = time_now_minutes(); | ||
| 129 | 115 | ||
| 130 | peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; | 116 | /* The announcing peer's family list is created on first use */ |
| 117 | { | ||
| 118 | ot_peerlist **slot = peer_size == OT_PEER_SIZE6 ? &torrent->peer_list6 : &torrent->peer_list4; | ||
| 119 | if (!*slot && !(*slot = calloc(1, sizeof(ot_peerlist)))) { | ||
| 120 | if (delta_torrentcount) | ||
| 121 | vector_remove_torrent(torrents_list, torrent); | ||
| 122 | mutex_bucket_unlock_by_hash(*ws->hash, 0); | ||
| 123 | return 0; | ||
| 124 | } | ||
| 125 | peer_list = *slot; | ||
| 126 | } | ||
| 131 | 127 | ||
| 132 | /* Check for peer in torrent */ | 128 | /* Check for peer in torrent */ |
| 133 | peer_dest = vector_find_or_insert_peer(&(peer_list->peers), peer_src, peer_size, &exactmatch); | 129 | peer_dest = vector_find_or_insert_peer(&(peer_list->peers), peer_src, peer_size, &exactmatch); |
| @@ -155,7 +151,7 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 155 | 151 | ||
| 156 | peer_list->peer_count++; | 152 | peer_list->peer_count++; |
| 157 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) { | 153 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) { |
| 158 | peer_list->down_count++; | 154 | torrent->down_count++; |
| 159 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); | 155 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); |
| 160 | } | 156 | } |
| 161 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING) | 157 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING) |
| @@ -182,7 +178,7 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 182 | if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING)) | 178 | if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_SEEDING) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING)) |
| 183 | peer_list->seed_count++; | 179 | peer_list->seed_count++; |
| 184 | if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)) { | 180 | if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)) { |
| 185 | peer_list->down_count++; | 181 | torrent->down_count++; |
| 186 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); | 182 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); |
| 187 | } | 183 | } |
| 188 | if (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) | 184 | if (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) |
| @@ -286,11 +282,12 @@ static size_t return_peers_for_torrent_udp(struct ot_workstruct *ws, ot_torrent | |||
| 286 | char *r = reply; | 282 | char *r = reply; |
| 287 | size_t peer_size = peer_size_from_peer6(&ws->peer); | 283 | size_t peer_size = peer_size_from_peer6(&ws->peer); |
| 288 | ot_peerlist *peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; | 284 | ot_peerlist *peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; |
| 289 | size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count; | 285 | size_t list_count = peerlist_peer_count(peer_list); |
| 290 | size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count; | 286 | size_t peer_count = peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4); |
| 287 | size_t seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); | ||
| 291 | 288 | ||
| 292 | if (amount > peer_list->peer_count) | 289 | if (amount > list_count) |
| 293 | amount = peer_list->peer_count; | 290 | amount = list_count; |
| 294 | 291 | ||
| 295 | *(uint32_t *)(r + 0) = htonl(OT_CLIENT_REQUEST_INTERVAL_RANDOM); | 292 | *(uint32_t *)(r + 0) = htonl(OT_CLIENT_REQUEST_INTERVAL_RANDOM); |
| 296 | *(uint32_t *)(r + 4) = htonl(peer_count - seed_count); | 293 | *(uint32_t *)(r + 4) = htonl(peer_count - seed_count); |
| @@ -298,7 +295,7 @@ static size_t return_peers_for_torrent_udp(struct ot_workstruct *ws, ot_torrent | |||
| 298 | r += 12; | 295 | r += 12; |
| 299 | 296 | ||
| 300 | if (amount) { | 297 | if (amount) { |
| 301 | if (amount == peer_list->peer_count) | 298 | if (amount == list_count) |
| 302 | r += return_peers_all(peer_list, peer_size, r); | 299 | r += return_peers_all(peer_list, peer_size, r); |
| 303 | else | 300 | else |
| 304 | r += return_peers_selection(ws, peer_list, peer_size, amount, r); | 301 | r += return_peers_selection(ws, peer_list, peer_size, amount, r); |
| @@ -310,13 +307,15 @@ static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent | |||
| 310 | char *r = reply; | 307 | char *r = reply; |
| 311 | const char *r_end = ws->outbuf + G_OUTBUF_SIZE; | 308 | const char *r_end = ws->outbuf + G_OUTBUF_SIZE; |
| 312 | int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; | 309 | int erval = OT_CLIENT_REQUEST_INTERVAL_RANDOM; |
| 313 | size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count; | 310 | size_t count6 = peerlist_peer_count(torrent->peer_list6); |
| 314 | size_t down_count = torrent->peer_list6->down_count + torrent->peer_list4->down_count; | 311 | size_t count4 = peerlist_peer_count(torrent->peer_list4); |
| 315 | size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - seed_count; | 312 | size_t seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); |
| 313 | size_t down_count = torrent->down_count; | ||
| 314 | size_t peer_count = count6 + count4 - seed_count; | ||
| 316 | 315 | ||
| 317 | /* Simple case: amount of peers in both lists is less than requested, here we return all results */ | 316 | /* Simple case: amount of peers in both lists is less than requested, here we return all results */ |
| 318 | size_t amount_v4 = torrent->peer_list4->peer_count; | 317 | size_t amount_v4 = count4; |
| 319 | size_t amount_v6 = torrent->peer_list6->peer_count; | 318 | size_t amount_v6 = count6; |
| 320 | 319 | ||
| 321 | /* Complex case: both lists have more than enough entries and we need to split between v4 and v6 clients */ | 320 | /* Complex case: both lists have more than enough entries and we need to split between v4 and v6 clients */ |
| 322 | if (amount_v4 + amount_v6 > amount) { | 321 | if (amount_v4 + amount_v6 > amount) { |
| @@ -332,8 +331,8 @@ static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent | |||
| 332 | /* Fill the rest according to which family's pool provides more peers */ | 331 | /* Fill the rest according to which family's pool provides more peers */ |
| 333 | amount_left = amount - (amount_v4 + amount_v6); | 332 | amount_left = amount - (amount_v4 + amount_v6); |
| 334 | 333 | ||
| 335 | left_v4 = torrent->peer_list4->peer_count - amount_v4; | 334 | left_v4 = count4 - amount_v4; |
| 336 | left_v6 = torrent->peer_list6->peer_count - amount_v6; | 335 | left_v6 = count6 - amount_v6; |
| 337 | 336 | ||
| 338 | if (left_v4 + left_v6) { | 337 | if (left_v4 + left_v6) { |
| 339 | percent_v4 = (SCALE * left_v4) / (left_v4 + left_v6); | 338 | percent_v4 = (SCALE * left_v4) / (left_v4 + left_v6); |
| @@ -344,9 +343,9 @@ static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent | |||
| 344 | amount_v6 += (amount_left * percent_v6) / SCALE; | 343 | amount_v6 += (amount_left * percent_v6) / SCALE; |
| 345 | 344 | ||
| 346 | /* Integer division rounding can leave out a peer */ | 345 | /* Integer division rounding can leave out a peer */ |
| 347 | if (amount_v4 + amount_v6 < amount && amount_v6 < torrent->peer_list6->peer_count) | 346 | if (amount_v4 + amount_v6 < amount && amount_v6 < count6) |
| 348 | ++amount_v6; | 347 | ++amount_v6; |
| 349 | if (amount_v4 + amount_v6 < amount && amount_v4 < torrent->peer_list4->peer_count) | 348 | if (amount_v4 + amount_v6 < amount && amount_v4 < count4) |
| 350 | ++amount_v4; | 349 | ++amount_v4; |
| 351 | } | 350 | } |
| 352 | 351 | ||
| @@ -355,7 +354,7 @@ static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent | |||
| 355 | 354 | ||
| 356 | if (amount_v4) { | 355 | if (amount_v4) { |
| 357 | fmt_append(&r, r_end, PEERS_BENCODED4 "%zd:", OT_PEER_COMPARE_SIZE4 * amount_v4); | 356 | fmt_append(&r, r_end, PEERS_BENCODED4 "%zd:", OT_PEER_COMPARE_SIZE4 * amount_v4); |
| 358 | if (amount_v4 == torrent->peer_list4->peer_count) | 357 | if (amount_v4 == count4) |
| 359 | r += return_peers_all(torrent->peer_list4, OT_PEER_SIZE4, r); | 358 | r += return_peers_all(torrent->peer_list4, OT_PEER_SIZE4, r); |
| 360 | else | 359 | else |
| 361 | r += return_peers_selection(ws, torrent->peer_list4, OT_PEER_SIZE4, amount_v4, r); | 360 | r += return_peers_selection(ws, torrent->peer_list4, OT_PEER_SIZE4, amount_v4, r); |
| @@ -363,7 +362,7 @@ static size_t return_peers_for_torrent_tcp(struct ot_workstruct *ws, ot_torrent | |||
| 363 | 362 | ||
| 364 | if (amount_v6) { | 363 | if (amount_v6) { |
| 365 | fmt_append(&r, r_end, PEERS_BENCODED6 "%zd:", OT_PEER_COMPARE_SIZE6 * amount_v6); | 364 | fmt_append(&r, r_end, PEERS_BENCODED6 "%zd:", OT_PEER_COMPARE_SIZE6 * amount_v6); |
| 366 | if (amount_v6 == torrent->peer_list6->peer_count) | 365 | if (amount_v6 == count6) |
| 367 | r += return_peers_all(torrent->peer_list6, OT_PEER_SIZE6, r); | 366 | r += return_peers_all(torrent->peer_list6, OT_PEER_SIZE6, r); |
| 368 | else | 367 | else |
| 369 | r += return_peers_selection(ws, torrent->peer_list6, OT_PEER_SIZE6, amount_v6, r); | 368 | r += return_peers_selection(ws, torrent->peer_list6, OT_PEER_SIZE6, amount_v6, r); |
| @@ -402,9 +401,9 @@ size_t return_udp_scrape_for_torrent(ot_hash const hash, char *reply) { | |||
| 402 | memset(reply, 0, 12); | 401 | memset(reply, 0, 12); |
| 403 | delta_torrentcount = -1; | 402 | delta_torrentcount = -1; |
| 404 | } else { | 403 | } else { |
| 405 | r[0] = htonl(torrent->peer_list6->seed_count + torrent->peer_list4->seed_count); | 404 | r[0] = htonl(peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4)); |
| 406 | r[1] = htonl(torrent->peer_list6->down_count + torrent->peer_list4->down_count); | 405 | r[1] = htonl(torrent->down_count); |
| 407 | r[2] = htonl(torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count); | 406 | r[2] = htonl(peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4) - peerlist_seed_count(torrent->peer_list6) - peerlist_seed_count(torrent->peer_list4)); |
| 408 | } | 407 | } |
| 409 | } | 408 | } |
| 410 | mutex_bucket_unlock_by_hash(hash, delta_torrentcount); | 409 | mutex_bucket_unlock_by_hash(hash, delta_torrentcount); |
| @@ -431,9 +430,9 @@ void return_tcp_scrape_for_torrent(ot_hash const *hash_list, size_t amount, char | |||
| 431 | } else { | 430 | } else { |
| 432 | fmt_append(reply, reply_end, "20:"); | 431 | fmt_append(reply, reply_end, "20:"); |
| 433 | fmt_bytes(reply, reply_end, hash, sizeof(ot_hash)); | 432 | fmt_bytes(reply, reply_end, hash, sizeof(ot_hash)); |
| 434 | fmt_append(reply, reply_end, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", | 433 | size_t seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); |
| 435 | torrent->peer_list6->seed_count + torrent->peer_list4->seed_count, torrent->peer_list6->down_count + torrent->peer_list4->down_count, | 434 | size_t peer_count = peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4); |
| 436 | torrent->peer_list6->peer_count + torrent->peer_list4->peer_count - torrent->peer_list6->seed_count - torrent->peer_list4->seed_count); | 435 | fmt_append(reply, reply_end, "d8:completei%zde10:downloadedi%zde10:incompletei%zdee", seed_count, (size_t)torrent->down_count, peer_count - seed_count); |
| 437 | } | 436 | } |
| 438 | } | 437 | } |
| 439 | mutex_bucket_unlock_by_hash(*hash, delta_torrentcount); | 438 | mutex_bucket_unlock_by_hash(*hash, delta_torrentcount); |
| @@ -461,17 +460,19 @@ size_t remove_peer_from_torrent(PROTO_FLAG proto, struct ot_workstruct *ws) { | |||
| 461 | 460 | ||
| 462 | if (exactmatch) { | 461 | if (exactmatch) { |
| 463 | peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; | 462 | peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; |
| 464 | switch (vector_remove_peer(&peer_list->peers, peer_src, peer_size)) { | 463 | if (peer_list) { |
| 465 | case 2: | 464 | switch (vector_remove_peer(&peer_list->peers, peer_src, peer_size)) { |
| 466 | peer_list->seed_count--; /* Intentional fallthrough */ | 465 | case 2: |
| 467 | case 1: | 466 | peer_list->seed_count--; /* Intentional fallthrough */ |
| 468 | peer_list->peer_count--; /* Intentional fallthrough */ | 467 | case 1: |
| 469 | default: | 468 | peer_list->peer_count--; /* Intentional fallthrough */ |
| 470 | break; | 469 | default: |
| 470 | break; | ||
| 471 | } | ||
| 471 | } | 472 | } |
| 472 | 473 | ||
| 473 | peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count; | 474 | peer_count = peerlist_peer_count(torrent->peer_list6) + peerlist_peer_count(torrent->peer_list4); |
| 474 | seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count; | 475 | seed_count = peerlist_seed_count(torrent->peer_list6) + peerlist_seed_count(torrent->peer_list4); |
| 475 | } | 476 | } |
| 476 | 477 | ||
| 477 | if (proto == FLAG_TCP) { | 478 | if (proto == FLAG_TCP) { |
