summaryrefslogtreecommitdiff
path: root/src/ot_clean.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ot_clean.c')
-rw-r--r--src/ot_clean.c176
1 files changed, 176 insertions, 0 deletions
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 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <errno.h>
6#include <pthread.h>
7#include <string.h>
8#include <unistd.h>
9
10/* Opentracker */
11#include "ot_accesslist.h"
12#include "ot_clean.h"
13#include "ot_mutex.h"
14#include "ot_stats.h"
15#include "ot_vector.h"
16#include "trackerlogic.h"
17
18/* Returns amount of removed peers */
19static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t peer_size, time_t timedout, size_t *removed_seeders) {
20 ot_peer *last_peer = peers + peer_count * peer_size, *insert_point;
21
22 /* Two scan modes: unless there is one peer removed, just increase ot_peertime */
23 while (peers < last_peer) {
24 time_t timediff = timedout + OT_PEERTIME(peers, peer_size);
25 if (timediff >= OT_PEER_TIMEOUT)
26 break;
27 OT_PEERTIME(peers, peer_size) = timediff;
28 peers += peer_size;
29 }
30
31 /* If we at least remove one peer, we have to copy */
32 for (insert_point = peers; peers < last_peer; peers += peer_size) {
33 time_t timediff = timedout + OT_PEERTIME(peers, peer_size);
34
35 if (timediff < OT_PEER_TIMEOUT) {
36 OT_PEERTIME(peers, peer_size) = timediff;
37 memcpy(insert_point, peers, peer_size);
38 insert_point += peer_size;
39 } else if (OT_PEERFLAG_D(peers, peer_size) & PEER_FLAG_SEEDING)
40 (*removed_seeders)++;
41 }
42
43 return (peers - insert_point) / peer_size;
44}
45
46int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) {
47 ot_vector *peer_vector = &peer_list->peers;
48 time_t timedout = (time_t)(time_now_minutes() - peer_list->base);
49 size_t num_buckets = 1, removed_seeders = 0;
50
51 /* No need to clean empty torrent */
52 if (!timedout)
53 return 0;
54
55 /* Torrent has idled out */
56 if (timedout > OT_TORRENT_TIMEOUT)
57 return 1;
58
59 /* Nothing to be cleaned here? Test if torrent is worth keeping */
60 if (timedout > OT_PEER_TIMEOUT) {
61 if (!peer_list->peer_count)
62 return peer_list->down_count ? 0 : 1;
63 timedout = OT_PEER_TIMEOUT;
64 }
65
66 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
67 num_buckets = peer_vector->size;
68 peer_vector = (ot_vector *)peer_vector->data;
69 }
70
71 while (num_buckets--) {
72 size_t removed_peers = clean_single_bucket(peer_vector->data, peer_vector->size, peer_size, timedout, &removed_seeders);
73 peer_list->peer_count -= removed_peers;
74 peer_vector->size -= removed_peers;
75 if (removed_peers)
76 vector_fixup_peers(peer_vector, peer_size);
77
78 /* Skip to next bucket, a vector containing peers */
79 ++peer_vector;
80 }
81
82 peer_list->seed_count -= removed_seeders;
83
84 /* See if we need to convert a torrent from simple vector to bucket list */
85 if ((peer_list->peer_count > OT_PEER_BUCKET_MINCOUNT) || OT_PEERLIST_HASBUCKETS(peer_list))
86 vector_redistribute_buckets(peer_list, peer_size);
87
88 if (peer_list->peer_count)
89 peer_list->base = time_now_minutes();
90 else {
91 /* When we got here, the last time that torrent
92 has been touched is OT_PEER_TIMEOUT Minutes before */
93 peer_list->base = time_now_minutes() - OT_PEER_TIMEOUT;
94 }
95 return 0;
96}
97
98/* Clean a single torrent
99 return 1 if torrent timed out
100*/
101int clean_single_torrent(ot_torrent *torrent) {
102 return clean_single_peer_list(torrent->peer_list6, OT_PEER_SIZE6) * clean_single_peer_list(torrent->peer_list4, OT_PEER_SIZE4);
103}
104
105static pthread_mutex_t g_clean_mutex = PTHREAD_MUTEX_INITIALIZER;
106static pthread_cond_t g_clean_wakeup = PTHREAD_COND_INITIALIZER;
107
108static void clean_sleep(void) {
109 struct timespec deadline;
110
111 clock_gettime(CLOCK_REALTIME, &deadline);
112 deadline.tv_sec += OT_CLEAN_SLEEP / 1000000;
113 deadline.tv_nsec += (OT_CLEAN_SLEEP % 1000000) * 1000;
114 if (deadline.tv_nsec >= 1000000000) {
115 ++deadline.tv_sec;
116 deadline.tv_nsec -= 1000000000;
117 }
118
119 /* Both sides hold the mutex, so the flag cannot be cleared and signalled
120 in the window between testing it and waiting on it */
121 pthread_mutex_lock(&g_clean_mutex);
122 while (g_opentracker_running)
123 if (pthread_cond_timedwait(&g_clean_wakeup, &g_clean_mutex, &deadline) == ETIMEDOUT)
124 break;
125 pthread_mutex_unlock(&g_clean_mutex);
126}
127
128/* Clean up all peers in one bucket, remove timedout pools and torrents */
129static void clean_bucket(size_t bucket) {
130 ot_vector *torrents_list = mutex_bucket_lock((int)bucket);
131 size_t toffs;
132 int delta_torrentcount = 0;
133
134 for (toffs = 0; toffs < torrents_list->size; ++toffs) {
135 ot_torrent *torrent = ((ot_torrent *)(torrents_list->data)) + toffs;
136 if (clean_single_torrent(torrent)) {
137 vector_remove_torrent(torrents_list, torrent);
138 --delta_torrentcount;
139 --toffs;
140 }
141 }
142 mutex_bucket_unlock((int)bucket, delta_torrentcount);
143}
144
145static void *clean_worker(void *args) {
146 (void)args;
147 while (1) {
148 size_t bucket = OT_BUCKET_COUNT;
149 while (bucket) {
150 size_t todo = bucket < OT_CLEAN_BUCKETS_PER_WAKE ? bucket : OT_CLEAN_BUCKETS_PER_WAKE;
151
152 /* One bucket is locked at a time, however many a wake gets through */
153 while (todo--)
154 clean_bucket(--bucket);
155
156 if (!g_opentracker_running)
157 return NULL;
158 clean_sleep();
159 }
160 stats_cleanup();
161#ifdef WANT_ACCESSLIST
162 accesslist_cleanup();
163#endif
164 }
165 return NULL;
166}
167
168static pthread_t thread_id;
169void clean_init(void) { pthread_create(&thread_id, NULL, clean_worker, NULL); }
170
171void clean_deinit(void) {
172 pthread_mutex_lock(&g_clean_mutex);
173 pthread_cond_signal(&g_clean_wakeup);
174 pthread_mutex_unlock(&g_clean_mutex);
175 pthread_join(thread_id, NULL);
176}