summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--opentracker.c3
-rw-r--r--ot_fullscrape.c25
-rw-r--r--ot_fullscrape.h1
-rw-r--r--ot_http.c3
-rw-r--r--ot_mutex.c221
-rw-r--r--ot_mutex.h57
-rw-r--r--ot_stats.c9
-rw-r--r--ot_sync.c7
-rw-r--r--ot_workqueue.c243
-rw-r--r--ot_workqueue.h78
-rw-r--r--tests/runner.c2
-rw-r--r--tests/t_workqueue.c (renamed from tests/t_mutex.c)50
-rw-r--r--tests/test.h2
-rw-r--r--trackerlogic.c3
15 files changed, 382 insertions, 330 deletions
diff --git a/Makefile b/Makefile
index 8f4dcf6..61eb872 100644
--- a/Makefile
+++ b/Makefile
@@ -69,8 +69,8 @@ CFLAGS_libowfat=-I$(LIBOWFAT_HEADERS)
69LDFLAGS_libowfat=-L$(LIBOWFAT_LIBRARY) -lowfat 69LDFLAGS_libowfat=-L$(LIBOWFAT_LIBRARY) -lowfat
70 70
71BINARY =opentracker 71BINARY =opentracker
72HEADERS=trackerlogic.h ot_urlencoded.h ot_batch.h ot_buf.h ot_event.h ot_fdstate.h ot_fmt.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.h ot_tcp.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h 72HEADERS=trackerlogic.h ot_urlencoded.h ot_batch.h ot_buf.h ot_event.h ot_fdstate.h ot_fmt.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.h ot_tcp.h ot_workqueue.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h
73SOURCES=opentracker.c trackerlogic.c ot_urlencoded.c ot_batch.c ot_buf.c ot_event.c ot_fdstate.c ot_fmt.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.c ot_tcp.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c 73SOURCES=opentracker.c trackerlogic.c ot_urlencoded.c ot_batch.c ot_buf.c ot_event.c ot_fdstate.c ot_fmt.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.c ot_tcp.c ot_workqueue.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c
74SOURCES_proxy=proxy.c ot_batch.c ot_fdstate.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c 74SOURCES_proxy=proxy.c ot_batch.c ot_fdstate.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c
75 75
76all: $(BINARY) $(BINARY).debug 76all: $(BINARY) $(BINARY).debug
@@ -96,8 +96,8 @@ proxy.debug: $(SOURCES_proxy) $(HEADERS)
96# has to. tests/stubs.c supplies the few symbols those modules reference but 96# has to. tests/stubs.c supplies the few symbols those modules reference but
97# the tests never reach. 97# the tests never reach.
98TESTBIN=tests/runner 98TESTBIN=tests/runner
99TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_mutex.c tests/t_batch.c tests/t_fdstate.c tests/t_event.c tests/t_fmt.c 99TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_workqueue.c tests/t_batch.c tests/t_fdstate.c tests/t_event.c tests/t_fmt.c
100TEST_UNITS=ot_urlencoded.c ot_fmt.c ot_event.c ot_batch.c ot_fdstate.c ot_buf.c ot_ip.c ot_socket.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c ot_mutex.c 100TEST_UNITS=ot_urlencoded.c ot_fmt.c ot_event.c ot_batch.c ot_fdstate.c ot_buf.c ot_ip.c ot_socket.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c ot_mutex.c ot_workqueue.c
101TEST_HEADERS=tests/test.h $(HEADERS) 101TEST_HEADERS=tests/test.h $(HEADERS)
102# Tests reach code that sits behind feature flags, so the units under test are 102# Tests reach code that sits behind feature flags, so the units under test are
103# built with those features enabled. A test binary is not a production binary. 103# built with those features enabled. A test binary is not a production binary.
diff --git a/opentracker.c b/opentracker.c
index 6e073e9..2f2746d 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -29,6 +29,7 @@
29#include "ot_ip.h" 29#include "ot_ip.h"
30#include "ot_livesync.h" 30#include "ot_livesync.h"
31#include "ot_mutex.h" 31#include "ot_mutex.h"
32#include "ot_workqueue.h"
32#include "ot_random.h" 33#include "ot_random.h"
33#include "ot_scan.h" 34#include "ot_scan.h"
34#include "ot_event.h" 35#include "ot_event.h"
@@ -148,7 +149,7 @@ static void *server_mainloop(void *args) {
148 } 149 }
149 } 150 }
150 151
151 while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) 152 while ((sock = workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1)
152 http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); 153 http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial);
153 154
154 if (time_now_seconds() > next_timeout_check) { 155 if (time_now_seconds() > next_timeout_check) {
diff --git a/ot_fullscrape.c b/ot_fullscrape.c
index a86d6d7..ae29308 100644
--- a/ot_fullscrape.c
+++ b/ot_fullscrape.c
@@ -23,6 +23,7 @@
23#include "ot_fullscrape.h" 23#include "ot_fullscrape.h"
24#include "ot_iovec.h" 24#include "ot_iovec.h"
25#include "ot_mutex.h" 25#include "ot_mutex.h"
26#include "ot_workqueue.h"
26#include "ot_urlencoded.h" 27#include "ot_urlencoded.h"
27#include "trackerlogic.h" 28#include "trackerlogic.h"
28 29
@@ -54,7 +55,7 @@ static void *fullscrape_worker(void *args) {
54 55
55 while (g_opentracker_running) { 56 while (g_opentracker_running) {
56 ot_tasktype tasktype = TASK_FULLSCRAPE; 57 ot_tasktype tasktype = TASK_FULLSCRAPE;
57 ot_taskid taskid = mutex_workqueue_poptask(&tasktype); 58 ot_taskid taskid = workqueue_poptask(&tasktype);
58 if (!taskid) 59 if (!taskid)
59 break; 60 break;
60#ifdef WANT_COMPRESSION_ZSTD 61#ifdef WANT_COMPRESSION_ZSTD
@@ -68,7 +69,7 @@ static void *fullscrape_worker(void *args) {
68 else 69 else
69#endif 70#endif
70 fullscrape_make(taskid, tasktype); 71 fullscrape_make(taskid, tasktype);
71 mutex_workqueue_pushchunked(taskid, NULL); 72 workqueue_pushchunked(taskid, NULL);
72 } 73 }
73 return NULL; 74 return NULL;
74} 75}
@@ -79,12 +80,12 @@ void fullscrape_init(void) {
79} 80}
80 81
81void fullscrape_deinit(void) { 82void fullscrape_deinit(void) {
82 mutex_workqueue_wakeall(); 83 workqueue_wakeall();
83 pthread_join( thread_id, NULL ); 84 pthread_join( thread_id, NULL );
84} 85}
85 86
86void fullscrape_deliver( int64_t sock, ot_tasktype tasktype ) { 87void fullscrape_deliver( int64_t sock, ot_tasktype tasktype ) {
87 mutex_workqueue_pushtask( sock, TASK_FULLSCRAPE | tasktype ); 88 workqueue_pushtask( sock, TASK_FULLSCRAPE | tasktype );
88} 89}
89 90
90static void fullscrape_write_one( ot_tasktype mode, char **cursor, ot_torrent *torrent, ot_hash *hash ) { 91static void fullscrape_write_one( ot_tasktype mode, char **cursor, ot_torrent *torrent, ot_hash *hash ) {
@@ -170,7 +171,7 @@ static void fullscrape_make(int taskid, ot_tasktype mode) {
170 if (r > re) { 171 if (r > re) {
171 iovector.iov_len = r - (char *)iovector.iov_base; 172 iovector.iov_len = r - (char *)iovector.iov_base;
172 173
173 if (mutex_workqueue_pushchunked(taskid, &iovector)) { 174 if (workqueue_pushchunked(taskid, &iovector)) {
174 free(iovector.iov_base); 175 free(iovector.iov_base);
175 mutex_bucket_unlock(bucket, 0); 176 mutex_bucket_unlock(bucket, 0);
176 return; 177 return;
@@ -200,7 +201,7 @@ static void fullscrape_make(int taskid, ot_tasktype mode) {
200 201
201 /* Send rest of data */ 202 /* Send rest of data */
202 iovector.iov_len = r - (char *)iovector.iov_base; 203 iovector.iov_len = r - (char *)iovector.iov_base;
203 if (mutex_workqueue_pushchunked(taskid, &iovector)) 204 if (workqueue_pushchunked(taskid, &iovector))
204 free(iovector.iov_base); 205 free(iovector.iov_base);
205} 206}
206 207
@@ -251,7 +252,7 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
251 while (!strm.avail_out) { 252 while (!strm.avail_out) {
252 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base; 253 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base;
253 254
254 if (mutex_workqueue_pushchunked(taskid, &iovector)) { 255 if (workqueue_pushchunked(taskid, &iovector)) {
255 free(iovector.iov_base); 256 free(iovector.iov_base);
256 mutex_bucket_unlock(bucket, 0); 257 mutex_bucket_unlock(bucket, 0);
257 return; 258 return;
@@ -291,7 +292,7 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
291 fprintf(stderr, "deflate() failed while in fullscrape_make()'s endgame.\n"); 292 fprintf(stderr, "deflate() failed while in fullscrape_make()'s endgame.\n");
292 293
293 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base; 294 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base;
294 if (mutex_workqueue_pushchunked(taskid, &iovector)) { 295 if (workqueue_pushchunked(taskid, &iovector)) {
295 free(iovector.iov_base); 296 free(iovector.iov_base);
296 deflateEnd(&strm); 297 deflateEnd(&strm);
297 return; 298 return;
@@ -314,7 +315,7 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) {
314 315
315 /* Only pass the new buffer if there actually was some data left in the buffer */ 316 /* Only pass the new buffer if there actually was some data left in the buffer */
316 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base; 317 iovector.iov_len = (char *)strm.next_out - (char *)iovector.iov_base;
317 if (!iovector.iov_len || mutex_workqueue_pushchunked(taskid, &iovector)) 318 if (!iovector.iov_len || workqueue_pushchunked(taskid, &iovector))
318 free(iovector.iov_base); 319 free(iovector.iov_base);
319 } 320 }
320 321
@@ -380,7 +381,7 @@ static void fullscrape_make_zstd(int taskid, ot_tasktype mode) {
380 while (outbuf.pos + OT_SCRAPE_MAXENTRYLEN > outbuf.size) { 381 while (outbuf.pos + OT_SCRAPE_MAXENTRYLEN > outbuf.size) {
381 iovector.iov_len = outbuf.size; 382 iovector.iov_len = outbuf.size;
382 383
383 if (mutex_workqueue_pushchunked(taskid, &iovector)) { 384 if (workqueue_pushchunked(taskid, &iovector)) {
384 free(iovector.iov_base); 385 free(iovector.iov_base);
385 ZSTD_freeCCtx(zstream); 386 ZSTD_freeCCtx(zstream);
386 return mutex_bucket_unlock(bucket, 0); 387 return mutex_bucket_unlock(bucket, 0);
@@ -418,7 +419,7 @@ static void fullscrape_make_zstd(int taskid, ot_tasktype mode) {
418 more_bytes = ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_end); 419 more_bytes = ZSTD_compressStream2(zstream, &outbuf, &inbuf, ZSTD_e_end);
419 420
420 iovector.iov_len = outbuf.pos; 421 iovector.iov_len = outbuf.pos;
421 if (mutex_workqueue_pushchunked(taskid, &iovector)) { 422 if (workqueue_pushchunked(taskid, &iovector)) {
422 free(iovector.iov_base); 423 free(iovector.iov_base);
423 ZSTD_freeCCtx(zstream); 424 ZSTD_freeCCtx(zstream);
424 return; 425 return;
@@ -443,7 +444,7 @@ static void fullscrape_make_zstd(int taskid, ot_tasktype mode) {
443 444
444 /* Only pass the new buffer if there actually was some data left in the buffer */ 445 /* Only pass the new buffer if there actually was some data left in the buffer */
445 iovector.iov_len = outbuf.pos; 446 iovector.iov_len = outbuf.pos;
446 if (!iovector.iov_len || mutex_workqueue_pushchunked(taskid, &iovector)) 447 if (!iovector.iov_len || workqueue_pushchunked(taskid, &iovector))
447 free(iovector.iov_base); 448 free(iovector.iov_base);
448 } 449 }
449 450
diff --git a/ot_fullscrape.h b/ot_fullscrape.h
index 0e7690c..b759a27 100644
--- a/ot_fullscrape.h
+++ b/ot_fullscrape.h
@@ -9,6 +9,7 @@
9#ifdef WANT_FULLSCRAPE 9#ifdef WANT_FULLSCRAPE
10 10
11#include "ot_mutex.h" 11#include "ot_mutex.h"
12#include "ot_workqueue.h"
12 13
13void fullscrape_init(void); 14void fullscrape_init(void);
14void fullscrape_deinit(void); 15void fullscrape_deinit(void);
diff --git a/ot_http.c b/ot_http.c
index 602ff94..b0e1de3 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -22,6 +22,7 @@
22#include "ot_iovec.h" 22#include "ot_iovec.h"
23#include "ot_ip.h" 23#include "ot_ip.h"
24#include "ot_mutex.h" 24#include "ot_mutex.h"
25#include "ot_workqueue.h"
25#include "ot_scan.h" 26#include "ot_scan.h"
26#include "ot_event.h" 27#include "ot_event.h"
27#include "ot_fdstate.h" 28#include "ot_fdstate.h"
@@ -77,7 +78,7 @@ void http_close(const int64_t sock) {
77 free(cookie->batch); 78 free(cookie->batch);
78 buf_reset(&cookie->request); 79 buf_reset(&cookie->request);
79 if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER)) 80 if (cookie->flag & (STRUCT_HTTP_FLAG_WAITINGFORTASK | STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER))
80 mutex_workqueue_canceltask(sock); 81 workqueue_canceltask(sock);
81 if (!(cookie->flag & STRUCT_HTTP_FLAG_ONSTACK)) 82 if (!(cookie->flag & STRUCT_HTTP_FLAG_ONSTACK))
82 free(cookie); 83 free(cookie);
83 } 84 }
diff --git a/ot_mutex.c b/ot_mutex.c
index 5392264..d856d5f 100644
--- a/ot_mutex.c
+++ b/ot_mutex.c
@@ -31,7 +31,6 @@ static pthread_mutex_t bucket_mutex[OT_BUCKET_COUNT];
31static _Atomic size_t g_torrent_count; 31static _Atomic size_t g_torrent_count;
32 32
33/* Self pipe from opentracker.c */ 33/* Self pipe from opentracker.c */
34extern int g_self_pipe[2];
35 34
36ot_vector *mutex_bucket_lock(int bucket) { 35ot_vector *mutex_bucket_lock(int bucket) {
37 pthread_mutex_lock(bucket_mutex + bucket); 36 pthread_mutex_lock(bucket_mutex + bucket);
@@ -60,237 +59,17 @@ void mutex_bucket_unlock_by_hash(ot_hash const hash, int delta_torrentcount) {
60 59
61size_t mutex_get_torrent_count(void) { return atomic_load_explicit(&g_torrent_count, memory_order_relaxed); } 60size_t mutex_get_torrent_count(void) { return atomic_load_explicit(&g_torrent_count, memory_order_relaxed); }
62 61
63/* TaskQueue Magic */
64
65struct ot_task {
66 ot_taskid taskid;
67 ot_tasktype tasktype;
68 int64_t sock;
69 size_t iovec_entries;
70 struct iovec *iovec;
71 struct ot_task *next;
72};
73
74static ot_taskid next_free_taskid = 1;
75static struct ot_task *tasklist;
76static pthread_mutex_t tasklist_mutex;
77static pthread_cond_t tasklist_being_filled;
78
79int mutex_workqueue_pushtask(int64_t sock, ot_tasktype tasktype) {
80 struct ot_task **tmptask, *task;
81
82 task = malloc(sizeof(struct ot_task));
83 if (!task)
84 return -1;
85
86 task->taskid = 0;
87 task->tasktype = tasktype;
88 task->sock = sock;
89 task->iovec_entries = 0;
90 task->iovec = NULL;
91 task->next = 0;
92
93 /* Want exclusive access to tasklist */
94 pthread_mutex_lock(&tasklist_mutex);
95
96 /* Skip to end of list */
97 tmptask = &tasklist;
98 while (*tmptask)
99 tmptask = &(*tmptask)->next;
100 *tmptask = task;
101
102 /* Inform waiting workers and release lock */
103 pthread_cond_broadcast(&tasklist_being_filled);
104 pthread_mutex_unlock(&tasklist_mutex);
105 return 0;
106}
107
108void mutex_workqueue_canceltask(int64_t sock) {
109 struct ot_task **task;
110
111 /* Want exclusive access to tasklist */
112 pthread_mutex_lock(&tasklist_mutex);
113
114 for (task = &tasklist; *task; task = &((*task)->next))
115 if ((*task)->sock == sock) {
116 struct iovec *iovec = (*task)->iovec;
117 struct ot_task *ptask = *task;
118 size_t i;
119
120 /* Free task's iovec, the buffers it points at and the vector itself */
121 for (i = 0; i < (*task)->iovec_entries; ++i)
122 free(iovec[i].iov_base);
123 free(iovec);
124
125 *task = (*task)->next;
126 free(ptask);
127 break;
128 }
129
130 /* Release lock */
131 pthread_mutex_unlock(&tasklist_mutex);
132}
133
134ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype) {
135 struct ot_task *task;
136 ot_taskid taskid = 0;
137
138 /* Want exclusive access to tasklist */
139 pthread_mutex_lock(&tasklist_mutex);
140
141 while (!taskid) {
142 /* Skip to the first unassigned task this worker wants to do */
143 for (task = tasklist; task; task = task->next)
144 if (!task->taskid && (TASK_CLASS_MASK & task->tasktype) == *tasktype) {
145 /* If we found an outstanding task, assign a taskid to it
146 and leave the loop */
147 task->taskid = taskid = ++next_free_taskid;
148 *tasktype = task->tasktype;
149 break;
150 }
151
152 /* Wait until the next task is being fed, unless the tracker is on its
153 way out, in which case the worker is meant to leave rather than wait */
154 if (!taskid) {
155 if (!g_opentracker_running)
156 break;
157 pthread_cond_wait(&tasklist_being_filled, &tasklist_mutex);
158 }
159 }
160
161 /* Release lock */
162 pthread_mutex_unlock(&tasklist_mutex);
163
164 return taskid;
165}
166
167void mutex_workqueue_pushsuccess(ot_taskid taskid) {
168 struct ot_task **task;
169
170 /* Want exclusive access to tasklist */
171 pthread_mutex_lock(&tasklist_mutex);
172
173 for (task = &tasklist; *task; task = &((*task)->next))
174 if ((*task)->taskid == taskid) {
175 struct ot_task *ptask = *task;
176 *task = (*task)->next;
177 free(ptask);
178 break;
179 }
180
181 /* Release lock */
182 pthread_mutex_unlock(&tasklist_mutex);
183}
184
185int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovec) {
186 struct ot_task *task;
187 const char byte = 'o';
188
189 /* Want exclusive access to tasklist */
190 pthread_mutex_lock(&tasklist_mutex);
191
192 for (task = tasklist; task; task = task->next)
193 if (task->taskid == taskid) {
194 task->iovec_entries = iovec_entries;
195 task->iovec = iovec;
196 task->tasktype = TASK_DONE;
197 break;
198 }
199
200 /* Release lock */
201 pthread_mutex_unlock(&tasklist_mutex);
202
203 /* A full pipe has already done this nudge's job, so a failure is fine */
204 (void)!write(g_self_pipe[1], &byte, 1);
205
206 /* Indicate whether the worker has to throw away results */
207 return task ? 0 : -1;
208}
209
210int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec) {
211 struct ot_task *task;
212 const char byte = 'o';
213
214 /* Want exclusive access to tasklist */
215 pthread_mutex_lock(&tasklist_mutex);
216
217 for (task = tasklist; task; task = task->next)
218 if (task->taskid == taskid) {
219 if (iovec) {
220 if (iovec_append(&task->iovec_entries, &task->iovec, iovec))
221 task->tasktype = TASK_DONE_PARTIAL;
222 else
223 task = NULL;
224 } else
225 task->tasktype = TASK_DONE;
226 break;
227 }
228
229 /* Release lock */
230 pthread_mutex_unlock(&tasklist_mutex);
231
232 /* A full pipe has already done this nudge's job, so a failure is fine */
233 (void)!write(g_self_pipe[1], &byte, 1);
234
235 /* Indicate whether the worker has to throw away results */
236 return task ? 0 : -1;
237}
238
239int64_t mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovec, int *is_partial) {
240 struct ot_task **task;
241 int64_t sock = -1;
242
243 *is_partial = 0;
244
245 /* Want exclusive access to tasklist */
246 pthread_mutex_lock(&tasklist_mutex);
247
248 for (task = &tasklist; *task; task = &((*task)->next))
249 if (((*task)->tasktype & TASK_CLASS_MASK) == TASK_DONE) {
250 struct ot_task *ptask = *task;
251 *iovec_entries = ptask->iovec_entries;
252 *iovec = ptask->iovec;
253 sock = ptask->sock;
254
255 if ((*task)->tasktype == TASK_DONE) {
256 *task = ptask->next;
257 free(ptask);
258 } else {
259 ptask->iovec_entries = 0;
260 ptask->iovec = NULL;
261 *is_partial = 1;
262 /* Prevent task from showing up immediately again unless new data was added */
263 (*task)->tasktype = TASK_FULLSCRAPE;
264 }
265 break;
266 }
267
268 /* Release lock */
269 pthread_mutex_unlock(&tasklist_mutex);
270 return sock;
271}
272
273void mutex_init(void) { 62void mutex_init(void) {
274 size_t i; 63 size_t i;
275 pthread_mutex_init(&tasklist_mutex, NULL);
276 pthread_cond_init(&tasklist_being_filled, NULL);
277 for (i = 0; i < OT_BUCKET_COUNT; ++i) 64 for (i = 0; i < OT_BUCKET_COUNT; ++i)
278 pthread_mutex_init(bucket_mutex + i, NULL); 65 pthread_mutex_init(bucket_mutex + i, NULL);
279 memset(all_torrents, 0, sizeof(all_torrents)); 66 memset(all_torrents, 0, sizeof(all_torrents));
280} 67}
281 68
282/* Let every worker parked on the queue see that the tracker is stopping */ 69/* Let every worker parked on the queue see that the tracker is stopping */
283void mutex_workqueue_wakeall(void) {
284 pthread_mutex_lock(&tasklist_mutex);
285 pthread_cond_broadcast(&tasklist_being_filled);
286 pthread_mutex_unlock(&tasklist_mutex);
287}
288
289void mutex_deinit(void) { 70void mutex_deinit(void) {
290 size_t i; 71 size_t i;
291 for (i = 0; i < OT_BUCKET_COUNT; ++i) 72 for (i = 0; i < OT_BUCKET_COUNT; ++i)
292 pthread_mutex_destroy(bucket_mutex + i); 73 pthread_mutex_destroy(bucket_mutex + i);
293 pthread_mutex_destroy(&tasklist_mutex);
294 pthread_cond_destroy(&tasklist_being_filled);
295 memset(all_torrents, 0, sizeof(all_torrents)); 74 memset(all_torrents, 0, sizeof(all_torrents));
296} 75}
diff --git a/ot_mutex.h b/ot_mutex.h
index c048fdb..c1218d7 100644
--- a/ot_mutex.h
+++ b/ot_mutex.h
@@ -10,7 +10,6 @@
10#include "trackerlogic.h" 10#include "trackerlogic.h"
11 11
12void mutex_init(void); 12void mutex_init(void);
13void mutex_workqueue_wakeall(void);
14void mutex_deinit(void); 13void mutex_deinit(void);
15 14
16ot_vector *mutex_bucket_lock(int bucket); 15ot_vector *mutex_bucket_lock(int bucket);
@@ -21,60 +20,4 @@ void mutex_bucket_unlock_by_hash(ot_hash const hash, int delta_torrentcoun
21 20
22size_t mutex_get_torrent_count(void); 21size_t mutex_get_torrent_count(void);
23 22
24typedef enum {
25 TASK_STATS_CONNS = 0x0001,
26 TASK_STATS_TCP = 0x0002,
27 TASK_STATS_UDP = 0x0003,
28 TASK_STATS_SCRAPE = 0x0004,
29 TASK_STATS_FULLSCRAPE = 0x0005,
30 TASK_STATS_TPB = 0x0006,
31 TASK_STATS_HTTPERRORS = 0x0007,
32 TASK_STATS_VERSION = 0x0008,
33 TASK_STATS_RENEW = 0x000a,
34 TASK_STATS_SYNCS = 0x000b,
35 TASK_STATS_COMPLETED = 0x000c,
36 TASK_STATS_NUMWANTS = 0x000d,
37
38 TASK_STATS = 0x0100, /* Mask */
39 TASK_STATS_TORRENTS = 0x0101,
40 TASK_STATS_PEERS = 0x0102,
41 TASK_STATS_SLASH24S = 0x0103,
42 TASK_STATS_TOP10 = 0x0104,
43 TASK_STATS_TOP100 = 0x0105,
44 TASK_STATS_EVERYTHING = 0x0106,
45 TASK_STATS_FULLLOG = 0x0107,
46 TASK_STATS_WOODPECKERS = 0x0108,
47
48 TASK_FULLSCRAPE = 0x0200, /* Default mode */
49 TASK_FULLSCRAPE_TPB_BINARY = 0x0201,
50 TASK_FULLSCRAPE_TPB_ASCII = 0x0202,
51 TASK_FULLSCRAPE_TPB_ASCII_PLUS = 0x0203,
52 TASK_FULLSCRAPE_TPB_URLENCODED = 0x0204,
53 TASK_FULLSCRAPE_TRACKERSTATE = 0x0205,
54
55 TASK_DMEM = 0x0300,
56
57 TASK_DONE = 0x0f00,
58 TASK_DONE_PARTIAL = 0x0f01,
59
60 TASK_FLAG_GZIP = 0x1000,
61 TASK_FLAG_BZIP2 = 0x2000,
62 TASK_FLAG_ZSTD = 0x4000,
63 TASK_FLAG_CHUNKED = 0x8000,
64
65 TASK_TASK_MASK = 0x0fff,
66 TASK_CLASS_MASK = 0x0f00,
67 TASK_FLAGS_MASK = 0xf000
68} ot_tasktype;
69
70typedef unsigned long ot_taskid;
71
72int mutex_workqueue_pushtask(int64_t sock, ot_tasktype tasktype);
73void mutex_workqueue_canceltask(int64_t sock);
74void mutex_workqueue_pushsuccess(ot_taskid taskid);
75ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype);
76int mutex_workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovector);
77int mutex_workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec);
78int64_t mutex_workqueue_popresult(size_t *iovec_entries, struct iovec **iovector, int *is_partial);
79
80#endif 23#endif
diff --git a/ot_stats.c b/ot_stats.c
index 5f97a2f..d38355a 100644
--- a/ot_stats.c
+++ b/ot_stats.c
@@ -23,6 +23,7 @@
23#include "ot_iovec.h" 23#include "ot_iovec.h"
24#include "ot_ip.h" 24#include "ot_ip.h"
25#include "ot_mutex.h" 25#include "ot_mutex.h"
26#include "ot_workqueue.h"
26#include "ot_fmt.h" 27#include "ot_fmt.h"
27#include "ot_stats.h" 28#include "ot_stats.h"
28#include "trackerlogic.h" 29#include "trackerlogic.h"
@@ -803,17 +804,17 @@ static void *stats_worker(void *args) {
803 804
804 while (g_opentracker_running) { 805 while (g_opentracker_running) {
805 ot_tasktype tasktype = TASK_STATS; 806 ot_tasktype tasktype = TASK_STATS;
806 ot_taskid taskid = mutex_workqueue_poptask(&tasktype); 807 ot_taskid taskid = workqueue_poptask(&tasktype);
807 if (!taskid) 808 if (!taskid)
808 break; 809 break;
809 stats_make(&iovec_entries, &iovector, tasktype); 810 stats_make(&iovec_entries, &iovector, tasktype);
810 if (mutex_workqueue_pushresult(taskid, iovec_entries, iovector)) 811 if (workqueue_pushresult(taskid, iovec_entries, iovector))
811 iovec_free(&iovec_entries, &iovector); 812 iovec_free(&iovec_entries, &iovector);
812 } 813 }
813 return NULL; 814 return NULL;
814} 815}
815 816
816void stats_deliver(int64_t sock, int tasktype) { mutex_workqueue_pushtask(sock, tasktype); } 817void stats_deliver(int64_t sock, int tasktype) { workqueue_pushtask(sock, tasktype); }
817 818
818static pthread_t thread_id; 819static pthread_t thread_id;
819void stats_init(void) { 820void stats_init(void) {
@@ -822,6 +823,6 @@ void stats_init(void) {
822} 823}
823 824
824void stats_deinit(void) { 825void stats_deinit(void) {
825 mutex_workqueue_wakeall(); 826 workqueue_wakeall();
826 pthread_join(thread_id, NULL); 827 pthread_join(thread_id, NULL);
827} 828}
diff --git a/ot_sync.c b/ot_sync.c
index 3668aa1..c19cf36 100644
--- a/ot_sync.c
+++ b/ot_sync.c
@@ -14,6 +14,7 @@
14#include "ot_fmt.h" 14#include "ot_fmt.h"
15#include "ot_iovec.h" 15#include "ot_iovec.h"
16#include "ot_mutex.h" 16#include "ot_mutex.h"
17#include "ot_workqueue.h"
17#include "ot_scan.h" 18#include "ot_scan.h"
18#include "ot_stats.h" 19#include "ot_stats.h"
19#include "ot_sync.h" 20#include "ot_sync.h"
@@ -139,10 +140,10 @@ static void *sync_worker(void *args) {
139 140
140 while (1) { 141 while (1) {
141 ot_tasktype tasktype = TASK_SYNC_OUT; 142 ot_tasktype tasktype = TASK_SYNC_OUT;
142 ot_taskid taskid = mutex_workqueue_poptask(&tasktype); 143 ot_taskid taskid = workqueue_poptask(&tasktype);
143 sync_make(&iovec_entries, &iovector); 144 sync_make(&iovec_entries, &iovector);
144 stats_issue_event(ws, EVENT_SYNC_OUT, FLAG_TCP, iovec_length(&iovec_entries, &iovector)); 145 stats_issue_event(ws, EVENT_SYNC_OUT, FLAG_TCP, iovec_length(&iovec_entries, &iovector));
145 if (mutex_workqueue_pushresult(taskid, iovec_entries, iovector)) 146 if (workqueue_pushresult(taskid, iovec_entries, iovector))
146 iovec_free(&iovec_entries, &iovector); 147 iovec_free(&iovec_entries, &iovector);
147 } 148 }
148 return NULL; 149 return NULL;
@@ -158,7 +159,7 @@ void sync_deinit( ) {
158} 159}
159 160
160void sync_deliver( int64_t socket ) { 161void sync_deliver( int64_t socket ) {
161 mutex_workqueue_pushtask( socket, TASK_SYNC_OUT ); 162 workqueue_pushtask( socket, TASK_SYNC_OUT );
162} 163}
163 164
164#endif 165#endif
diff --git a/ot_workqueue.c b/ot_workqueue.c
new file mode 100644
index 0000000..aac366c
--- /dev/null
+++ b/ot_workqueue.c
@@ -0,0 +1,243 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <pthread.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/uio.h>
11#include <unistd.h>
12
13/* Opentracker */
14#include "ot_iovec.h"
15#include "ot_workqueue.h"
16#include "trackerlogic.h"
17
18/* Pushing a result wakes the thread that will send it */
19extern int g_self_pipe[2];
20
21struct ot_task {
22 ot_taskid taskid;
23 ot_tasktype tasktype;
24 int64_t sock;
25 size_t iovec_entries;
26 struct iovec *iovec;
27 struct ot_task *next;
28};
29
30static ot_taskid next_free_taskid = 1;
31static struct ot_task *tasklist;
32static pthread_mutex_t tasklist_mutex;
33static pthread_cond_t tasklist_being_filled;
34
35int workqueue_pushtask(int64_t sock, ot_tasktype tasktype) {
36 struct ot_task **tmptask, *task;
37
38 task = malloc(sizeof(struct ot_task));
39 if (!task)
40 return -1;
41
42 task->taskid = 0;
43 task->tasktype = tasktype;
44 task->sock = sock;
45 task->iovec_entries = 0;
46 task->iovec = NULL;
47 task->next = 0;
48
49 /* Want exclusive access to tasklist */
50 pthread_mutex_lock(&tasklist_mutex);
51
52 /* Skip to end of list */
53 tmptask = &tasklist;
54 while (*tmptask)
55 tmptask = &(*tmptask)->next;
56 *tmptask = task;
57
58 /* Inform waiting workers and release lock */
59 pthread_cond_broadcast(&tasklist_being_filled);
60 pthread_mutex_unlock(&tasklist_mutex);
61 return 0;
62}
63
64void workqueue_canceltask(int64_t sock) {
65 struct ot_task **task;
66
67 /* Want exclusive access to tasklist */
68 pthread_mutex_lock(&tasklist_mutex);
69
70 for (task = &tasklist; *task; task = &((*task)->next))
71 if ((*task)->sock == sock) {
72 struct iovec *iovec = (*task)->iovec;
73 struct ot_task *ptask = *task;
74 size_t i;
75
76 /* Free task's iovec, the buffers it points at and the vector itself */
77 for (i = 0; i < (*task)->iovec_entries; ++i)
78 free(iovec[i].iov_base);
79 free(iovec);
80
81 *task = (*task)->next;
82 free(ptask);
83 break;
84 }
85
86 /* Release lock */
87 pthread_mutex_unlock(&tasklist_mutex);
88}
89
90ot_taskid workqueue_poptask(ot_tasktype *tasktype) {
91 struct ot_task *task;
92 ot_taskid taskid = 0;
93
94 /* Want exclusive access to tasklist */
95 pthread_mutex_lock(&tasklist_mutex);
96
97 while (!taskid) {
98 /* Skip to the first unassigned task this worker wants to do */
99 for (task = tasklist; task; task = task->next)
100 if (!task->taskid && (TASK_CLASS_MASK & task->tasktype) == *tasktype) {
101 /* If we found an outstanding task, assign a taskid to it
102 and leave the loop */
103 task->taskid = taskid = ++next_free_taskid;
104 *tasktype = task->tasktype;
105 break;
106 }
107
108 /* Wait until the next task is being fed, unless the tracker is on its
109 way out, in which case the worker is meant to leave rather than wait */
110 if (!taskid) {
111 if (!g_opentracker_running)
112 break;
113 pthread_cond_wait(&tasklist_being_filled, &tasklist_mutex);
114 }
115 }
116
117 /* Release lock */
118 pthread_mutex_unlock(&tasklist_mutex);
119
120 return taskid;
121}
122
123void workqueue_pushsuccess(ot_taskid taskid) {
124 struct ot_task **task;
125
126 /* Want exclusive access to tasklist */
127 pthread_mutex_lock(&tasklist_mutex);
128
129 for (task = &tasklist; *task; task = &((*task)->next))
130 if ((*task)->taskid == taskid) {
131 struct ot_task *ptask = *task;
132 *task = (*task)->next;
133 free(ptask);
134 break;
135 }
136
137 /* Release lock */
138 pthread_mutex_unlock(&tasklist_mutex);
139}
140
141int workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovec) {
142 struct ot_task *task;
143 const char byte = 'o';
144
145 /* Want exclusive access to tasklist */
146 pthread_mutex_lock(&tasklist_mutex);
147
148 for (task = tasklist; task; task = task->next)
149 if (task->taskid == taskid) {
150 task->iovec_entries = iovec_entries;
151 task->iovec = iovec;
152 task->tasktype = TASK_DONE;
153 break;
154 }
155
156 /* Release lock */
157 pthread_mutex_unlock(&tasklist_mutex);
158
159 /* A full pipe has already done this nudge's job, so a failure is fine */
160 (void)!write(g_self_pipe[1], &byte, 1);
161
162 /* Indicate whether the worker has to throw away results */
163 return task ? 0 : -1;
164}
165
166int workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec) {
167 struct ot_task *task;
168 const char byte = 'o';
169
170 /* Want exclusive access to tasklist */
171 pthread_mutex_lock(&tasklist_mutex);
172
173 for (task = tasklist; task; task = task->next)
174 if (task->taskid == taskid) {
175 if (iovec) {
176 if (iovec_append(&task->iovec_entries, &task->iovec, iovec))
177 task->tasktype = TASK_DONE_PARTIAL;
178 else
179 task = NULL;
180 } else
181 task->tasktype = TASK_DONE;
182 break;
183 }
184
185 /* Release lock */
186 pthread_mutex_unlock(&tasklist_mutex);
187
188 /* A full pipe has already done this nudge's job, so a failure is fine */
189 (void)!write(g_self_pipe[1], &byte, 1);
190
191 /* Indicate whether the worker has to throw away results */
192 return task ? 0 : -1;
193}
194
195int64_t workqueue_popresult(size_t *iovec_entries, struct iovec **iovec, int *is_partial) {
196 struct ot_task **task;
197 int64_t sock = -1;
198
199 *is_partial = 0;
200
201 /* Want exclusive access to tasklist */
202 pthread_mutex_lock(&tasklist_mutex);
203
204 for (task = &tasklist; *task; task = &((*task)->next))
205 if (((*task)->tasktype & TASK_CLASS_MASK) == TASK_DONE) {
206 struct ot_task *ptask = *task;
207 *iovec_entries = ptask->iovec_entries;
208 *iovec = ptask->iovec;
209 sock = ptask->sock;
210
211 if ((*task)->tasktype == TASK_DONE) {
212 *task = ptask->next;
213 free(ptask);
214 } else {
215 ptask->iovec_entries = 0;
216 ptask->iovec = NULL;
217 *is_partial = 1;
218 /* Prevent task from showing up immediately again unless new data was added */
219 (*task)->tasktype = TASK_FULLSCRAPE;
220 }
221 break;
222 }
223
224 /* Release lock */
225 pthread_mutex_unlock(&tasklist_mutex);
226 return sock;
227}
228
229void workqueue_wakeall(void) {
230 pthread_mutex_lock(&tasklist_mutex);
231 pthread_cond_broadcast(&tasklist_being_filled);
232 pthread_mutex_unlock(&tasklist_mutex);
233}
234
235void workqueue_init(void) {
236 pthread_mutex_init(&tasklist_mutex, NULL);
237 pthread_cond_init(&tasklist_being_filled, NULL);
238}
239
240void workqueue_deinit(void) {
241 pthread_mutex_destroy(&tasklist_mutex);
242 pthread_cond_destroy(&tasklist_being_filled);
243}
diff --git a/ot_workqueue.h b/ot_workqueue.h
new file mode 100644
index 0000000..f99a7b5
--- /dev/null
+++ b/ot_workqueue.h
@@ -0,0 +1,78 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6#ifndef OT_WORKQUEUE_H__
7#define OT_WORKQUEUE_H__
8
9#include <stdint.h>
10#include <sys/uio.h>
11
12#include "trackerlogic.h"
13
14/* Work the tracker hands to a thread of its own: a full scrape or an
15 expensive stats page, which take too long to build while a client waits.
16 The task goes in here, the reply comes back out. */
17
18typedef enum {
19 TASK_STATS_CONNS = 0x0001,
20 TASK_STATS_TCP = 0x0002,
21 TASK_STATS_UDP = 0x0003,
22 TASK_STATS_SCRAPE = 0x0004,
23 TASK_STATS_FULLSCRAPE = 0x0005,
24 TASK_STATS_TPB = 0x0006,
25 TASK_STATS_HTTPERRORS = 0x0007,
26 TASK_STATS_VERSION = 0x0008,
27 TASK_STATS_RENEW = 0x000a,
28 TASK_STATS_SYNCS = 0x000b,
29 TASK_STATS_COMPLETED = 0x000c,
30 TASK_STATS_NUMWANTS = 0x000d,
31
32 TASK_STATS = 0x0100, /* Mask */
33 TASK_STATS_TORRENTS = 0x0101,
34 TASK_STATS_PEERS = 0x0102,
35 TASK_STATS_SLASH24S = 0x0103,
36 TASK_STATS_TOP10 = 0x0104,
37 TASK_STATS_TOP100 = 0x0105,
38 TASK_STATS_EVERYTHING = 0x0106,
39 TASK_STATS_FULLLOG = 0x0107,
40 TASK_STATS_WOODPECKERS = 0x0108,
41
42 TASK_FULLSCRAPE = 0x0200, /* Default mode */
43 TASK_FULLSCRAPE_TPB_BINARY = 0x0201,
44 TASK_FULLSCRAPE_TPB_ASCII = 0x0202,
45 TASK_FULLSCRAPE_TPB_ASCII_PLUS = 0x0203,
46 TASK_FULLSCRAPE_TPB_URLENCODED = 0x0204,
47 TASK_FULLSCRAPE_TRACKERSTATE = 0x0205,
48
49 TASK_DMEM = 0x0300,
50
51 TASK_DONE = 0x0f00,
52 TASK_DONE_PARTIAL = 0x0f01,
53
54 TASK_FLAG_GZIP = 0x1000,
55 TASK_FLAG_BZIP2 = 0x2000,
56 TASK_FLAG_ZSTD = 0x4000,
57 TASK_FLAG_CHUNKED = 0x8000,
58
59 TASK_TASK_MASK = 0x0fff,
60 TASK_CLASS_MASK = 0x0f00,
61 TASK_FLAGS_MASK = 0xf000
62} ot_tasktype;
63
64typedef unsigned long ot_taskid;
65
66int workqueue_pushtask(int64_t sock, ot_tasktype tasktype);
67void workqueue_canceltask(int64_t sock);
68void workqueue_pushsuccess(ot_taskid taskid);
69ot_taskid workqueue_poptask(ot_tasktype *tasktype);
70int workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovector);
71int workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec);
72int64_t workqueue_popresult(size_t *iovec_entries, struct iovec **iovector, int *is_partial);
73
74void workqueue_init(void);
75void workqueue_deinit(void);
76void workqueue_wakeall(void);
77
78#endif
diff --git a/tests/runner.c b/tests/runner.c
index 71a5e2a..38bfb86 100644
--- a/tests/runner.c
+++ b/tests/runner.c
@@ -27,7 +27,7 @@ static const struct module modules[] = {{"ot_urlencoded", test_urlencoded},
27 {"ot_buf", test_buf}, 27 {"ot_buf", test_buf},
28 {"ot_ip", test_ip}, 28 {"ot_ip", test_ip},
29 {"ot_socket", test_socket}, 29 {"ot_socket", test_socket},
30 {"ot_mutex", test_mutex}, 30 {"ot_workqueue", test_workqueue},
31 {"ot_batch", test_batch}, 31 {"ot_batch", test_batch},
32 {"ot_fdstate", test_fdstate}, 32 {"ot_fdstate", test_fdstate},
33 {"ot_event", test_event}, 33 {"ot_event", test_event},
diff --git a/tests/t_mutex.c b/tests/t_workqueue.c
index 3307d70..13e0b2e 100644
--- a/tests/t_mutex.c
+++ b/tests/t_workqueue.c
@@ -8,7 +8,7 @@
8#include <string.h> 8#include <string.h>
9 9
10/* Opentracker */ 10/* Opentracker */
11#include "ot_mutex.h" 11#include "ot_workqueue.h"
12#include "test.h" 12#include "test.h"
13 13
14/* A worker asks for a class of task and blocks until one turns up, so a task 14/* A worker asks for a class of task and blocks until one turns up, so a task
@@ -20,25 +20,25 @@ static void case_task_goes_to_its_own_class(void) {
20 ot_taskid taskid; 20 ot_taskid taskid;
21 21
22 test_case("a worker is handed the task of the class it asked for"); 22 test_case("a worker is handed the task of the class it asked for");
23 CHECK_INT(mutex_workqueue_pushtask(41, TASK_FULLSCRAPE_TPB_ASCII), 0); 23 CHECK_INT(workqueue_pushtask(41, TASK_FULLSCRAPE_TPB_ASCII), 0);
24 asked = TASK_FULLSCRAPE; 24 asked = TASK_FULLSCRAPE;
25 taskid = mutex_workqueue_poptask(&asked); 25 taskid = workqueue_poptask(&asked);
26 CHECK(taskid != 0); 26 CHECK(taskid != 0);
27 27
28 test_case("and the full task type, not just its class"); 28 test_case("and the full task type, not just its class");
29 CHECK_INT(asked, TASK_FULLSCRAPE_TPB_ASCII); 29 CHECK_INT(asked, TASK_FULLSCRAPE_TPB_ASCII);
30 mutex_workqueue_canceltask(41); 30 workqueue_canceltask(41);
31} 31}
32 32
33static void case_flags_survive_the_queue(void) { 33static void case_flags_survive_the_queue(void) {
34 ot_tasktype asked; 34 ot_tasktype asked;
35 35
36 test_case("flags outside the class mask are carried through"); 36 test_case("flags outside the class mask are carried through");
37 CHECK_INT(mutex_workqueue_pushtask(42, TASK_FULLSCRAPE_TPB_URLENCODED | TASK_FLAG_GZIP), 0); 37 CHECK_INT(workqueue_pushtask(42, TASK_FULLSCRAPE_TPB_URLENCODED | TASK_FLAG_GZIP), 0);
38 asked = TASK_FULLSCRAPE; 38 asked = TASK_FULLSCRAPE;
39 CHECK(mutex_workqueue_poptask(&asked) != 0); 39 CHECK(workqueue_poptask(&asked) != 0);
40 CHECK_INT(asked, TASK_FULLSCRAPE_TPB_URLENCODED | TASK_FLAG_GZIP); 40 CHECK_INT(asked, TASK_FULLSCRAPE_TPB_URLENCODED | TASK_FLAG_GZIP);
41 mutex_workqueue_canceltask(42); 41 workqueue_canceltask(42);
42} 42}
43 43
44static void case_classes_do_not_cross(void) { 44static void case_classes_do_not_cross(void) {
@@ -46,22 +46,22 @@ static void case_classes_do_not_cross(void) {
46 ot_taskid taskid; 46 ot_taskid taskid;
47 47
48 test_case("a stats worker passes over a fullscrape task"); 48 test_case("a stats worker passes over a fullscrape task");
49 CHECK_INT(mutex_workqueue_pushtask(43, TASK_FULLSCRAPE_TPB_BINARY), 0); 49 CHECK_INT(workqueue_pushtask(43, TASK_FULLSCRAPE_TPB_BINARY), 0);
50 CHECK_INT(mutex_workqueue_pushtask(44, TASK_STATS_PEERS), 0); 50 CHECK_INT(workqueue_pushtask(44, TASK_STATS_PEERS), 0);
51 51
52 asked = TASK_STATS; 52 asked = TASK_STATS;
53 taskid = mutex_workqueue_poptask(&asked); 53 taskid = workqueue_poptask(&asked);
54 CHECK(taskid != 0); 54 CHECK(taskid != 0);
55 CHECK_INT(asked, TASK_STATS_PEERS); 55 CHECK_INT(asked, TASK_STATS_PEERS);
56 56
57 test_case("and leaves it for the one that wants it"); 57 test_case("and leaves it for the one that wants it");
58 asked = TASK_FULLSCRAPE; 58 asked = TASK_FULLSCRAPE;
59 taskid = mutex_workqueue_poptask(&asked); 59 taskid = workqueue_poptask(&asked);
60 CHECK(taskid != 0); 60 CHECK(taskid != 0);
61 CHECK_INT(asked, TASK_FULLSCRAPE_TPB_BINARY); 61 CHECK_INT(asked, TASK_FULLSCRAPE_TPB_BINARY);
62 62
63 mutex_workqueue_canceltask(43); 63 workqueue_canceltask(43);
64 mutex_workqueue_canceltask(44); 64 workqueue_canceltask(44);
65} 65}
66 66
67static void case_every_task_gets_its_own_id(void) { 67static void case_every_task_gets_its_own_id(void) {
@@ -69,36 +69,36 @@ static void case_every_task_gets_its_own_id(void) {
69 ot_taskid first, second; 69 ot_taskid first, second;
70 70
71 test_case("two queued tasks are handed out under different ids"); 71 test_case("two queued tasks are handed out under different ids");
72 CHECK_INT(mutex_workqueue_pushtask(45, TASK_FULLSCRAPE), 0); 72 CHECK_INT(workqueue_pushtask(45, TASK_FULLSCRAPE), 0);
73 CHECK_INT(mutex_workqueue_pushtask(46, TASK_FULLSCRAPE), 0); 73 CHECK_INT(workqueue_pushtask(46, TASK_FULLSCRAPE), 0);
74 74
75 asked = TASK_FULLSCRAPE; 75 asked = TASK_FULLSCRAPE;
76 first = mutex_workqueue_poptask(&asked); 76 first = workqueue_poptask(&asked);
77 asked = TASK_FULLSCRAPE; 77 asked = TASK_FULLSCRAPE;
78 second = mutex_workqueue_poptask(&asked); 78 second = workqueue_poptask(&asked);
79 CHECK(first != 0); 79 CHECK(first != 0);
80 CHECK(second != 0); 80 CHECK(second != 0);
81 CHECK(first != second); 81 CHECK(first != second);
82 82
83 mutex_workqueue_canceltask(45); 83 workqueue_canceltask(45);
84 mutex_workqueue_canceltask(46); 84 workqueue_canceltask(46);
85} 85}
86 86
87static void case_cancelled_task_is_not_handed_out(void) { 87static void case_cancelled_task_is_not_handed_out(void) {
88 ot_tasktype asked; 88 ot_tasktype asked;
89 89
90 test_case("a cancelled task is gone before any worker sees it"); 90 test_case("a cancelled task is gone before any worker sees it");
91 CHECK_INT(mutex_workqueue_pushtask(47, TASK_FULLSCRAPE_TPB_ASCII_PLUS), 0); 91 CHECK_INT(workqueue_pushtask(47, TASK_FULLSCRAPE_TPB_ASCII_PLUS), 0);
92 CHECK_INT(mutex_workqueue_pushtask(48, TASK_FULLSCRAPE), 0); 92 CHECK_INT(workqueue_pushtask(48, TASK_FULLSCRAPE), 0);
93 mutex_workqueue_canceltask(47); 93 workqueue_canceltask(47);
94 94
95 asked = TASK_FULLSCRAPE; 95 asked = TASK_FULLSCRAPE;
96 CHECK(mutex_workqueue_poptask(&asked) != 0); 96 CHECK(workqueue_poptask(&asked) != 0);
97 CHECK_INT(asked, TASK_FULLSCRAPE); 97 CHECK_INT(asked, TASK_FULLSCRAPE);
98 mutex_workqueue_canceltask(48); 98 workqueue_canceltask(48);
99} 99}
100 100
101void test_mutex(void) { 101void test_workqueue(void) {
102 case_task_goes_to_its_own_class(); 102 case_task_goes_to_its_own_class();
103 case_flags_survive_the_queue(); 103 case_flags_survive_the_queue();
104 case_classes_do_not_cross(); 104 case_classes_do_not_cross();
diff --git a/tests/test.h b/tests/test.h
index 0eddc82..d58742c 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -86,7 +86,7 @@ void test_scan(void);
86void test_buf(void); 86void test_buf(void);
87void test_ip(void); 87void test_ip(void);
88void test_socket(void); 88void test_socket(void);
89void test_mutex(void); 89void test_workqueue(void);
90void test_batch(void); 90void test_batch(void);
91void test_fdstate(void); 91void test_fdstate(void);
92void test_event(void); 92void test_event(void);
diff --git a/trackerlogic.c b/trackerlogic.c
index fc0fa56..b0b7f4e 100644
--- a/trackerlogic.c
+++ b/trackerlogic.c
@@ -20,6 +20,7 @@
20#include "ot_ip.h" 20#include "ot_ip.h"
21#include "ot_livesync.h" 21#include "ot_livesync.h"
22#include "ot_mutex.h" 22#include "ot_mutex.h"
23#include "ot_workqueue.h"
23#include "ot_random.h" 24#include "ot_random.h"
24#include "ot_stats.h" 25#include "ot_stats.h"
25#include "ot_udp.h" 26#include "ot_udp.h"
@@ -560,6 +561,7 @@ void trackerlogic_init(void) {
560 561
561 /* Initialise background worker threads */ 562 /* Initialise background worker threads */
562 mutex_init(); 563 mutex_init();
564 workqueue_init();
563 clean_init(); 565 clean_init();
564 fullscrape_init(); 566 fullscrape_init();
565 accesslist_init(); 567 accesslist_init();
@@ -623,5 +625,6 @@ void trackerlogic_deinit(void) {
623 } 625 }
624 626
625 /* Release mutexes */ 627 /* Release mutexes */
628 workqueue_deinit();
626 mutex_deinit(); 629 mutex_deinit();
627} 630}