From ad541c67761b8aa6da1566802c347cac4018d0c2 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Mon, 2 Nov 2026 20:50:24 +0100 Subject: Move the build sources into src/ --- src/ot_clean.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/ot_clean.c (limited to 'src/ot_clean.c') diff --git a/src/ot_clean.c b/src/ot_clean.c new file mode 100644 index 0000000..f181e83 --- /dev/null +++ b/src/ot_clean.c @@ -0,0 +1,176 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. */ + +/* System */ +#include +#include +#include +#include + +/* Opentracker */ +#include "ot_accesslist.h" +#include "ot_clean.h" +#include "ot_mutex.h" +#include "ot_stats.h" +#include "ot_vector.h" +#include "trackerlogic.h" + +/* Returns amount of removed peers */ +static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t peer_size, time_t timedout, size_t *removed_seeders) { + ot_peer *last_peer = peers + peer_count * peer_size, *insert_point; + + /* Two scan modes: unless there is one peer removed, just increase ot_peertime */ + while (peers < last_peer) { + time_t timediff = timedout + OT_PEERTIME(peers, peer_size); + if (timediff >= OT_PEER_TIMEOUT) + break; + OT_PEERTIME(peers, peer_size) = timediff; + peers += peer_size; + } + + /* If we at least remove one peer, we have to copy */ + for (insert_point = peers; peers < last_peer; peers += peer_size) { + time_t timediff = timedout + OT_PEERTIME(peers, peer_size); + + if (timediff < OT_PEER_TIMEOUT) { + OT_PEERTIME(peers, peer_size) = timediff; + memcpy(insert_point, peers, peer_size); + insert_point += peer_size; + } else if (OT_PEERFLAG_D(peers, peer_size) & PEER_FLAG_SEEDING) + (*removed_seeders)++; + } + + 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; + time_t timedout = (time_t)(time_now_minutes() - peer_list->base); + 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 (OT_PEERLIST_HASBUCKETS(peer_list)) { + num_buckets = peer_vector->size; + peer_vector = (ot_vector *)peer_vector->data; + } + + while (num_buckets--) { + size_t removed_peers = clean_single_bucket(peer_vector->data, peer_vector->size, peer_size, timedout, &removed_seeders); + peer_list->peer_count -= removed_peers; + peer_vector->size -= removed_peers; + if (removed_peers) + vector_fixup_peers(peer_vector, peer_size); + + /* Skip to next bucket, a vector containing peers */ + ++peer_vector; + } + + 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 > 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); +} + +static pthread_mutex_t g_clean_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t g_clean_wakeup = PTHREAD_COND_INITIALIZER; + +static void clean_sleep(void) { + struct timespec deadline; + + clock_gettime(CLOCK_REALTIME, &deadline); + deadline.tv_sec += OT_CLEAN_SLEEP / 1000000; + deadline.tv_nsec += (OT_CLEAN_SLEEP % 1000000) * 1000; + if (deadline.tv_nsec >= 1000000000) { + ++deadline.tv_sec; + deadline.tv_nsec -= 1000000000; + } + + /* Both sides hold the mutex, so the flag cannot be cleared and signalled + in the window between testing it and waiting on it */ + pthread_mutex_lock(&g_clean_mutex); + while (g_opentracker_running) + if (pthread_cond_timedwait(&g_clean_wakeup, &g_clean_mutex, &deadline) == ETIMEDOUT) + break; + pthread_mutex_unlock(&g_clean_mutex); +} + +/* Clean up all peers in one bucket, remove timedout pools and torrents */ +static void clean_bucket(size_t bucket) { + ot_vector *torrents_list = mutex_bucket_lock((int)bucket); + size_t toffs; + int delta_torrentcount = 0; + + for (toffs = 0; toffs < torrents_list->size; ++toffs) { + ot_torrent *torrent = ((ot_torrent *)(torrents_list->data)) + toffs; + if (clean_single_torrent(torrent)) { + vector_remove_torrent(torrents_list, torrent); + --delta_torrentcount; + --toffs; + } + } + mutex_bucket_unlock((int)bucket, delta_torrentcount); +} + +static void *clean_worker(void *args) { + (void)args; + while (1) { + size_t bucket = OT_BUCKET_COUNT; + while (bucket) { + size_t todo = bucket < OT_CLEAN_BUCKETS_PER_WAKE ? bucket : OT_CLEAN_BUCKETS_PER_WAKE; + + /* One bucket is locked at a time, however many a wake gets through */ + while (todo--) + clean_bucket(--bucket); + + if (!g_opentracker_running) + return NULL; + clean_sleep(); + } + stats_cleanup(); +#ifdef WANT_ACCESSLIST + accesslist_cleanup(); +#endif + } + return NULL; +} + +static pthread_t thread_id; +void clean_init(void) { pthread_create(&thread_id, NULL, clean_worker, NULL); } + +void clean_deinit(void) { + pthread_mutex_lock(&g_clean_mutex); + pthread_cond_signal(&g_clean_wakeup); + pthread_mutex_unlock(&g_clean_mutex); + pthread_join(thread_id, NULL); +} -- cgit v1.3