diff options
| -rw-r--r-- | opentracker.c | 73 | ||||
| -rw-r--r-- | ot_accesslist.c | 12 | ||||
| -rw-r--r-- | ot_clean.c | 39 | ||||
| -rw-r--r-- | ot_event.c | 18 | ||||
| -rw-r--r-- | ot_fullscrape.c | 5 | ||||
| -rw-r--r-- | ot_http.c | 16 | ||||
| -rw-r--r-- | ot_livesync.c | 12 | ||||
| -rw-r--r-- | ot_mutex.c | 25 | ||||
| -rw-r--r-- | ot_mutex.h | 1 | ||||
| -rw-r--r-- | ot_stats.c | 227 | ||||
| -rw-r--r-- | ot_stats.h | 17 | ||||
| -rw-r--r-- | ot_sync.c | 2 | ||||
| -rw-r--r-- | ot_udp.c | 23 | ||||
| -rw-r--r-- | ot_vector.c | 3 | ||||
| -rw-r--r-- | proxy.c | 3 | ||||
| -rw-r--r-- | tests/integration/harness.py | 6 | ||||
| -rw-r--r-- | tests/integration/test_concurrency.py | 209 | ||||
| -rw-r--r-- | tests/stubs.c | 3 | ||||
| -rw-r--r-- | trackerlogic.c | 25 | ||||
| -rw-r--r-- | trackerlogic.h | 75 |
20 files changed, 602 insertions, 192 deletions
diff --git a/opentracker.c b/opentracker.c index 11da486..a546fc5 100644 --- a/opentracker.c +++ b/opentracker.c | |||
| @@ -39,20 +39,22 @@ | |||
| 39 | #include "trackerlogic.h" | 39 | #include "trackerlogic.h" |
| 40 | 40 | ||
| 41 | /* Globals */ | 41 | /* Globals */ |
| 42 | time_t g_now_seconds; | ||
| 43 | uint32_t g_tracker_id; | 42 | uint32_t g_tracker_id; |
| 44 | volatile int g_opentracker_running = 1; | 43 | _Atomic int g_opentracker_running = 1; |
| 45 | int g_self_pipe[2]; | 44 | int g_self_pipe[2]; |
| 46 | 45 | ||
| 47 | |||
| 48 | static void panic(const char *routine) __attribute__((noreturn)); | 46 | static void panic(const char *routine) __attribute__((noreturn)); |
| 49 | static void panic(const char *routine) { | 47 | static void panic(const char *routine) { |
| 50 | fprintf(stderr, "%s: %s\n", routine, strerror(errno)); | 48 | fprintf(stderr, "%s: %s\n", routine, strerror(errno)); |
| 51 | exit(111); | 49 | exit(111); |
| 52 | } | 50 | } |
| 53 | 51 | ||
| 52 | /* Say we are stopping, wake the loop, and let main do the work on its own stack. */ | ||
| 54 | static void signal_handler(int s) { | 53 | static void signal_handler(int s) { |
| 55 | if (s == SIGINT) { | 54 | if (s == SIGINT) { |
| 55 | const char byte = 0; | ||
| 56 | int saved = errno; | ||
| 57 | |||
| 56 | /* Any new interrupt signal quits the application */ | 58 | /* Any new interrupt signal quits the application */ |
| 57 | signal(SIGINT, SIG_DFL); | 59 | signal(SIGINT, SIG_DFL); |
| 58 | 60 | ||
| @@ -60,13 +62,8 @@ static void signal_handler(int s) { | |||
| 60 | but cancel their operations and return */ | 62 | but cancel their operations and return */ |
| 61 | g_opentracker_running = 0; | 63 | g_opentracker_running = 0; |
| 62 | 64 | ||
| 63 | trackerlogic_deinit(); | 65 | (void)!write(g_self_pipe[1], &byte, 1); |
| 64 | 66 | errno = saved; | |
| 65 | #ifdef WANT_SYSLOGS | ||
| 66 | closelog(); | ||
| 67 | #endif | ||
| 68 | |||
| 69 | exit(0); | ||
| 70 | } | 67 | } |
| 71 | } | 68 | } |
| 72 | 69 | ||
| @@ -87,7 +84,7 @@ static void install_signal_handlers(void) { | |||
| 87 | 84 | ||
| 88 | sa.sa_handler = signal_handler; | 85 | sa.sa_handler = signal_handler; |
| 89 | sigemptyset(&sa.sa_mask); | 86 | sigemptyset(&sa.sa_mask); |
| 90 | sa.sa_flags = SA_RESTART; | 87 | sa.sa_flags = 0; |
| 91 | if ((sigaction(SIGINT, &sa, NULL) == -1) || (sigaction(SIGALRM, &sa, NULL) == -1)) | 88 | if ((sigaction(SIGINT, &sa, NULL) == -1) || (sigaction(SIGALRM, &sa, NULL) == -1)) |
| 92 | panic("install_signal_handlers"); | 89 | panic("install_signal_handlers"); |
| 93 | 90 | ||
| @@ -203,12 +200,12 @@ static void handle_write(const int64_t sock) { | |||
| 203 | waiting to write and go back to waiting for the next request */ | 200 | waiting to write and go back to waiting for the next request */ |
| 204 | http_interest_drop(sock, cookie, EVENT_OUT); | 201 | http_interest_drop(sock, cookie, EVENT_OUT); |
| 205 | http_interest_add(sock, cookie, EVENT_IN); | 202 | http_interest_add(sock, cookie, EVENT_IN); |
| 206 | fdstate_set_expiry(sock, g_now_seconds + OT_CLIENT_TIMEOUT); | 203 | fdstate_set_expiry(sock, time_future_seconds(OT_CLIENT_TIMEOUT)); |
| 207 | } else | 204 | } else |
| 208 | handle_dead(sock); | 205 | handle_dead(sock); |
| 209 | } | 206 | } |
| 210 | 207 | ||
| 211 | static void handle_accept(const int64_t serversocket) { | 208 | static void handle_accept(const int64_t serversocket, struct ot_workstruct *ws) { |
| 212 | struct http_data *cookie; | 209 | struct http_data *cookie; |
| 213 | int64_t sock; | 210 | int64_t sock; |
| 214 | ot_ip6 ip; | 211 | ot_ip6 ip; |
| @@ -218,22 +215,21 @@ static void handle_accept(const int64_t serversocket) { | |||
| 218 | 215 | ||
| 219 | socket_set_blocking((int)sock, 0); | 216 | socket_set_blocking((int)sock, 0); |
| 220 | 217 | ||
| 221 | if (!(cookie = (struct http_data *)malloc(sizeof(struct http_data)))) { | 218 | if (!(cookie = (struct http_data *)calloc(1, sizeof(struct http_data)))) { |
| 222 | close((int)sock); | 219 | close((int)sock); |
| 223 | continue; | 220 | continue; |
| 224 | } | 221 | } |
| 225 | memset(cookie, 0, sizeof(struct http_data)); | ||
| 226 | memcpy(cookie->ip, ip, sizeof(ot_ip6)); | 222 | memcpy(cookie->ip, ip, sizeof(ot_ip6)); |
| 227 | cookie->interest = EVENT_IN; | 223 | cookie->interest = EVENT_IN; |
| 228 | 224 | ||
| 229 | if (event_add((int)sock, EVENT_IN, fdstate_open(sock, cookie, g_now_seconds + OT_CLIENT_TIMEOUT)) == -1) { | 225 | if (event_add((int)sock, EVENT_IN, fdstate_open(sock, cookie, time_future_seconds(OT_CLIENT_TIMEOUT))) == -1) { |
| 230 | fdstate_close(sock); | 226 | fdstate_close(sock); |
| 231 | free(cookie); | 227 | free(cookie); |
| 232 | close((int)sock); | 228 | close((int)sock); |
| 233 | continue; | 229 | continue; |
| 234 | } | 230 | } |
| 235 | 231 | ||
| 236 | stats_issue_event(EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip); | 232 | stats_issue_event(ws, EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip); |
| 237 | } | 233 | } |
| 238 | } | 234 | } |
| 239 | 235 | ||
| @@ -242,7 +238,7 @@ static void handle_accept(const int64_t serversocket) { | |||
| 242 | 238 | ||
| 243 | static void *server_mainloop(void *args) { | 239 | static void *server_mainloop(void *args) { |
| 244 | struct ot_workstruct ws; | 240 | struct ot_workstruct ws; |
| 245 | time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; | 241 | time_t next_timeout_check = time_future_seconds(OT_CLIENT_TIMEOUT_CHECKINTERVAL); |
| 246 | struct iovec *iovector; | 242 | struct iovec *iovector; |
| 247 | size_t iovec_entries; | 243 | size_t iovec_entries; |
| 248 | int is_partial; | 244 | int is_partial; |
| @@ -250,6 +246,8 @@ static void *server_mainloop(void *args) { | |||
| 250 | (void)args; | 246 | (void)args; |
| 251 | 247 | ||
| 252 | /* Initialize our "thread local storage" */ | 248 | /* Initialize our "thread local storage" */ |
| 249 | memset(&ws, 0, sizeof(ws)); | ||
| 250 | stats_counters_register(&ws); | ||
| 253 | ws.inbuf = malloc(G_INBUF_SIZE); | 251 | ws.inbuf = malloc(G_INBUF_SIZE); |
| 254 | ws.outbuf = malloc(G_OUTBUF_SIZE); | 252 | ws.outbuf = malloc(G_OUTBUF_SIZE); |
| 255 | #ifdef _DEBUG_HTTPERROR | 253 | #ifdef _DEBUG_HTTPERROR |
| @@ -261,7 +259,7 @@ static void *server_mainloop(void *args) { | |||
| 261 | 259 | ||
| 262 | random_bytes(ws.rand48_state, sizeof(ws.rand48_state)); | 260 | random_bytes(ws.rand48_state, sizeof(ws.rand48_state)); |
| 263 | 261 | ||
| 264 | for (;;) { | 262 | while (g_opentracker_running) { |
| 265 | ot_event ready[OT_EVENT_BATCH]; | 263 | ot_event ready[OT_EVENT_BATCH]; |
| 266 | int64_t sock; | 264 | int64_t sock; |
| 267 | int count, i; | 265 | int count, i; |
| @@ -280,7 +278,7 @@ static void *server_mainloop(void *args) { | |||
| 280 | cookie = fdstate_cookie((int)sock); | 278 | cookie = fdstate_cookie((int)sock); |
| 281 | 279 | ||
| 282 | if ((intptr_t)cookie == FLAG_TCP) | 280 | if ((intptr_t)cookie == FLAG_TCP) |
| 283 | handle_accept(sock); | 281 | handle_accept(sock, &ws); |
| 284 | else if ((intptr_t)cookie == FLAG_UDP) | 282 | else if ((intptr_t)cookie == FLAG_UDP) |
| 285 | handle_udp6(sock, &ws); | 283 | handle_udp6(sock, &ws); |
| 286 | else if ((intptr_t)cookie == FLAG_SELFPIPE) | 284 | else if ((intptr_t)cookie == FLAG_SELFPIPE) |
| @@ -298,16 +296,26 @@ static void *server_mainloop(void *args) { | |||
| 298 | while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) | 296 | while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) |
| 299 | http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); | 297 | http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); |
| 300 | 298 | ||
| 301 | if (g_now_seconds > next_timeout_check) { | 299 | if (time_now_seconds() > next_timeout_check) { |
| 302 | int cursor = 0; | 300 | int cursor = 0; |
| 303 | 301 | ||
| 304 | while ((sock = fdstate_next_expired(g_now_seconds, &cursor)) != -1) | 302 | while ((sock = fdstate_next_expired(time_now_seconds(), &cursor)) != -1) |
| 305 | handle_dead(sock); | 303 | handle_dead(sock); |
| 306 | next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL; | 304 | next_timeout_check = time_future_seconds(OT_CLIENT_TIMEOUT_CHECKINTERVAL); |
| 307 | } | 305 | } |
| 308 | 306 | ||
| 309 | livesync_ticker(); | 307 | livesync_ticker(); |
| 310 | } | 308 | } |
| 309 | |||
| 310 | /* This work struct is about to go out of scope, so hand its tally over | ||
| 311 | before anything can read it there */ | ||
| 312 | stats_counters_retire(&ws); | ||
| 313 | |||
| 314 | free(ws.inbuf); | ||
| 315 | free(ws.outbuf); | ||
| 316 | #ifdef _DEBUG_HTTPERROR | ||
| 317 | free(ws.debugbuf); | ||
| 318 | #endif | ||
| 311 | return 0; | 319 | return 0; |
| 312 | } | 320 | } |
| 313 | 321 | ||
| @@ -461,17 +469,7 @@ int drop_privileges(const char *const serveruser, const char *const serverdir) { | |||
| 461 | } | 469 | } |
| 462 | 470 | ||
| 463 | /* Maintain our copy of the clock. time() on BSDs is very expensive. */ | 471 | /* Maintain our copy of the clock. time() on BSDs is very expensive. */ |
| 464 | static void *time_caching_worker(void *args) { | ||
| 465 | (void)args; | ||
| 466 | while (1) { | ||
| 467 | g_now_seconds = time(NULL); | ||
| 468 | sleep(5); | ||
| 469 | } | ||
| 470 | return NULL; | ||
| 471 | } | ||
| 472 | |||
| 473 | int main(int argc, char **argv) { | 472 | int main(int argc, char **argv) { |
| 474 | pthread_t thread_id; /* time cacher */ | ||
| 475 | 473 | ||
| 476 | random_init(); | 474 | random_init(); |
| 477 | if (fdstate_init() == -1) | 475 | if (fdstate_init() == -1) |
| @@ -492,8 +490,6 @@ int main(int argc, char **argv) { | |||
| 492 | if (drop_privileges(g_serveruser ? g_serveruser : "nobody", g_serverdir) == -1) | 490 | if (drop_privileges(g_serveruser ? g_serveruser : "nobody", g_serverdir) == -1) |
| 493 | panic("drop_privileges failed, exiting. Last error"); | 491 | panic("drop_privileges failed, exiting. Last error"); |
| 494 | 492 | ||
| 495 | g_now_seconds = time(NULL); | ||
| 496 | pthread_create(&thread_id, NULL, time_caching_worker, NULL); | ||
| 497 | 493 | ||
| 498 | /* Create our self pipe which allows us to interrupt mainloops | 494 | /* Create our self pipe which allows us to interrupt mainloops |
| 499 | wait in case some data is available to send out */ | 495 | wait in case some data is available to send out */ |
| @@ -524,6 +520,13 @@ int main(int argc, char **argv) { | |||
| 524 | 520 | ||
| 525 | server_mainloop(0); | 521 | server_mainloop(0); |
| 526 | 522 | ||
| 523 | /* The loop has left, so no thread is serving any more and the teardown | ||
| 524 | can run where a debugger and the sanitizers can follow it */ | ||
| 525 | trackerlogic_deinit(); | ||
| 526 | #ifdef WANT_SYSLOGS | ||
| 527 | closelog(); | ||
| 528 | #endif | ||
| 529 | |||
| 527 | event_deinit(); | 530 | event_deinit(); |
| 528 | fdstate_deinit(); | 531 | fdstate_deinit(); |
| 529 | return 0; | 532 | return 0; |
diff --git a/ot_accesslist.c b/ot_accesslist.c index 4bf1701..e8e0476 100644 --- a/ot_accesslist.c +++ b/ot_accesslist.c | |||
| @@ -93,7 +93,7 @@ static ot_accesslist *accesslist_make(ot_accesslist *next, size_t size) { | |||
| 93 | This will never delete head, because that might still be in use. */ | 93 | This will never delete head, because that might still be in use. */ |
| 94 | static void accesslist_clean(ot_accesslist *accesslist) { | 94 | static void accesslist_clean(ot_accesslist *accesslist) { |
| 95 | while (accesslist && accesslist->next) { | 95 | while (accesslist && accesslist->next) { |
| 96 | if (accesslist->next->base + 5 < g_now_minutes) | 96 | if (accesslist->next->base + 5 < time_now_minutes()) |
| 97 | accesslist->next = accesslist_free(accesslist->next); | 97 | accesslist->next = accesslist_free(accesslist->next); |
| 98 | accesslist = accesslist->next; | 98 | accesslist = accesslist->next; |
| 99 | } | 99 | } |
| @@ -197,7 +197,7 @@ static void accesslist_readfile(void) { | |||
| 197 | 197 | ||
| 198 | /* Mark old accesslist as "needs to live another five minutes" */ | 198 | /* Mark old accesslist as "needs to live another five minutes" */ |
| 199 | if (g_accesslist) | 199 | if (g_accesslist) |
| 200 | g_accesslist->base = g_now_minutes; | 200 | g_accesslist->base = time_now_minutes(); |
| 201 | accesslist_new->next = g_accesslist; | 201 | accesslist_new->next = g_accesslist; |
| 202 | g_accesslist = accesslist_new; /* Only now set a new list */ | 202 | g_accesslist = accesslist_new; /* Only now set a new list */ |
| 203 | 203 | ||
| @@ -205,11 +205,11 @@ static void accesslist_readfile(void) { | |||
| 205 | /* If we have dynamic accesslists, reloading a new one will always void the add/delete lists. | 205 | /* If we have dynamic accesslists, reloading a new one will always void the add/delete lists. |
| 206 | Insert empty ones at the list head */ | 206 | Insert empty ones at the list head */ |
| 207 | if (g_accesslist_add && (accesslist_new = accesslist_make(g_accesslist_add, 0)) != NULL) { | 207 | if (g_accesslist_add && (accesslist_new = accesslist_make(g_accesslist_add, 0)) != NULL) { |
| 208 | g_accesslist_add->base = g_now_minutes; | 208 | g_accesslist_add->base = time_now_minutes(); |
| 209 | g_accesslist_add = accesslist_new; | 209 | g_accesslist_add = accesslist_new; |
| 210 | } | 210 | } |
| 211 | if (g_accesslist_delete && (accesslist_new = accesslist_make(g_accesslist_delete, 0)) != NULL) { | 211 | if (g_accesslist_delete && (accesslist_new = accesslist_make(g_accesslist_delete, 0)) != NULL) { |
| 212 | g_accesslist_delete->base = g_now_minutes; | 212 | g_accesslist_delete->base = time_now_minutes(); |
| 213 | g_accesslist_delete = accesslist_new; | 213 | g_accesslist_delete = accesslist_new; |
| 214 | } | 214 | } |
| 215 | #endif | 215 | #endif |
| @@ -326,7 +326,7 @@ static void *accesslist_adddel_worker(const char *fifoname, ot_accesslist *_ | |||
| 326 | if (accesslist_new) { | 326 | if (accesslist_new) { |
| 327 | memcpy(accesslist_new->list, (*removing_from)->list, sizeof(ot_hash) * off); | 327 | memcpy(accesslist_new->list, (*removing_from)->list, sizeof(ot_hash) * off); |
| 328 | memcpy(accesslist_new->list + off, (*removing_from)->list + off + 1, sizeof(ot_hash) * ((*removing_from)->size - off - 1)); | 328 | memcpy(accesslist_new->list + off, (*removing_from)->list + off + 1, sizeof(ot_hash) * ((*removing_from)->size - off - 1)); |
| 329 | (*removing_from)->base = g_now_minutes; | 329 | (*removing_from)->base = time_now_minutes(); |
| 330 | *removing_from = accesslist_new; | 330 | *removing_from = accesslist_new; |
| 331 | } | 331 | } |
| 332 | } | 332 | } |
| @@ -351,7 +351,7 @@ static void *accesslist_adddel_worker(const char *fifoname, ot_accesslist *_ | |||
| 351 | memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off); | 351 | memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off); |
| 352 | memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash)); | 352 | memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash)); |
| 353 | memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, sizeof(ot_hash) * ((*adding_to)->size - off)); | 353 | memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, sizeof(ot_hash) * ((*adding_to)->size - off)); |
| 354 | (*adding_to)->base = g_now_minutes; | 354 | (*adding_to)->base = time_now_minutes(); |
| 355 | *adding_to = accesslist_new; | 355 | *adding_to = accesslist_new; |
| 356 | } | 356 | } |
| 357 | } | 357 | } |
| @@ -4,6 +4,7 @@ | |||
| 4 | $id$ */ | 4 | $id$ */ |
| 5 | 5 | ||
| 6 | /* System */ | 6 | /* System */ |
| 7 | #include <errno.h> | ||
| 7 | #include <pthread.h> | 8 | #include <pthread.h> |
| 8 | #include <string.h> | 9 | #include <string.h> |
| 9 | #include <unistd.h> | 10 | #include <unistd.h> |
| @@ -46,7 +47,7 @@ static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t pee | |||
| 46 | 47 | ||
| 47 | int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) { | 48 | int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) { |
| 48 | ot_vector *peer_vector = &peer_list->peers; | 49 | ot_vector *peer_vector = &peer_list->peers; |
| 49 | time_t timedout = (time_t)(g_now_minutes - peer_list->base); | 50 | time_t timedout = (time_t)(time_now_minutes() - peer_list->base); |
| 50 | size_t num_buckets = 1, removed_seeders = 0; | 51 | size_t num_buckets = 1, removed_seeders = 0; |
| 51 | 52 | ||
| 52 | /* No need to clean empty torrent */ | 53 | /* No need to clean empty torrent */ |
| @@ -87,11 +88,11 @@ int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) { | |||
| 87 | vector_redistribute_buckets(peer_list, peer_size); | 88 | vector_redistribute_buckets(peer_list, peer_size); |
| 88 | 89 | ||
| 89 | if (peer_list->peer_count) | 90 | if (peer_list->peer_count) |
| 90 | peer_list->base = g_now_minutes; | 91 | peer_list->base = time_now_minutes(); |
| 91 | else { | 92 | else { |
| 92 | /* When we got here, the last time that torrent | 93 | /* When we got here, the last time that torrent |
| 93 | has been touched is OT_PEER_TIMEOUT Minutes before */ | 94 | has been touched is OT_PEER_TIMEOUT Minutes before */ |
| 94 | peer_list->base = g_now_minutes - OT_PEER_TIMEOUT; | 95 | peer_list->base = time_now_minutes() - OT_PEER_TIMEOUT; |
| 95 | } | 96 | } |
| 96 | return 0; | 97 | return 0; |
| 97 | } | 98 | } |
| @@ -103,6 +104,29 @@ int clean_single_torrent(ot_torrent *torrent) { | |||
| 103 | return clean_single_peer_list(torrent->peer_list6, OT_PEER_SIZE6) * clean_single_peer_list(torrent->peer_list4, OT_PEER_SIZE4); | 104 | return clean_single_peer_list(torrent->peer_list6, OT_PEER_SIZE6) * clean_single_peer_list(torrent->peer_list4, OT_PEER_SIZE4); |
| 104 | } | 105 | } |
| 105 | 106 | ||
| 107 | static pthread_mutex_t g_clean_mutex = PTHREAD_MUTEX_INITIALIZER; | ||
| 108 | static pthread_cond_t g_clean_wakeup = PTHREAD_COND_INITIALIZER; | ||
| 109 | |||
| 110 | static void clean_sleep(void) { | ||
| 111 | struct timespec deadline; | ||
| 112 | |||
| 113 | clock_gettime(CLOCK_REALTIME, &deadline); | ||
| 114 | deadline.tv_sec += OT_CLEAN_SLEEP / 1000000; | ||
| 115 | deadline.tv_nsec += (OT_CLEAN_SLEEP % 1000000) * 1000; | ||
| 116 | if (deadline.tv_nsec >= 1000000000) { | ||
| 117 | ++deadline.tv_sec; | ||
| 118 | deadline.tv_nsec -= 1000000000; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Both sides hold the mutex, so the flag cannot be cleared and signalled | ||
| 122 | in the window between testing it and waiting on it */ | ||
| 123 | pthread_mutex_lock(&g_clean_mutex); | ||
| 124 | while (g_opentracker_running) | ||
| 125 | if (pthread_cond_timedwait(&g_clean_wakeup, &g_clean_mutex, &deadline) == ETIMEDOUT) | ||
| 126 | break; | ||
| 127 | pthread_mutex_unlock(&g_clean_mutex); | ||
| 128 | } | ||
| 129 | |||
| 106 | /* Clean up all peers in current bucket, remove timedout pools and | 130 | /* Clean up all peers in current bucket, remove timedout pools and |
| 107 | torrents */ | 131 | torrents */ |
| 108 | static void *clean_worker(void *args) { | 132 | static void *clean_worker(void *args) { |
| @@ -125,7 +149,7 @@ static void *clean_worker(void *args) { | |||
| 125 | mutex_bucket_unlock(bucket, delta_torrentcount); | 149 | mutex_bucket_unlock(bucket, delta_torrentcount); |
| 126 | if (!g_opentracker_running) | 150 | if (!g_opentracker_running) |
| 127 | return NULL; | 151 | return NULL; |
| 128 | usleep(OT_CLEAN_SLEEP); | 152 | clean_sleep(); |
| 129 | } | 153 | } |
| 130 | stats_cleanup(); | 154 | stats_cleanup(); |
| 131 | #ifdef WANT_ACCESSLIST | 155 | #ifdef WANT_ACCESSLIST |
| @@ -138,4 +162,9 @@ static void *clean_worker(void *args) { | |||
| 138 | static pthread_t thread_id; | 162 | static pthread_t thread_id; |
| 139 | void clean_init(void) { pthread_create(&thread_id, NULL, clean_worker, NULL); } | 163 | void clean_init(void) { pthread_create(&thread_id, NULL, clean_worker, NULL); } |
| 140 | 164 | ||
| 141 | void clean_deinit(void) { pthread_cancel(thread_id); } | 165 | void clean_deinit(void) { |
| 166 | pthread_mutex_lock(&g_clean_mutex); | ||
| 167 | pthread_cond_signal(&g_clean_wakeup); | ||
| 168 | pthread_mutex_unlock(&g_clean_mutex); | ||
| 169 | pthread_join(thread_id, NULL); | ||
| 170 | } | ||
| @@ -83,8 +83,10 @@ int event_wait(ot_event *out, int max, int timeout_ms) { | |||
| 83 | g_ready_space = max; | 83 | g_ready_space = max; |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | while ((count = epoll_wait(g_epoll, g_ready, max, timeout_ms)) == -1 && errno == EINTR) | 86 | /* An interrupted wait is reported as no events rather than retried here, |
| 87 | ; | 87 | so that the caller gets to look at whatever the signal was about */ |
| 88 | if ((count = epoll_wait(g_epoll, g_ready, max, timeout_ms)) == -1) | ||
| 89 | return errno == EINTR ? 0 : -1; | ||
| 88 | for (i = 0; i < count; ++i) { | 90 | for (i = 0; i < count; ++i) { |
| 89 | uint32_t events = g_ready[i].events; | 91 | uint32_t events = g_ready[i].events; |
| 90 | 92 | ||
| @@ -189,8 +191,10 @@ int event_wait(ot_event *out, int max, int timeout_ms) { | |||
| 189 | deadline = &timeout; | 191 | deadline = &timeout; |
| 190 | } | 192 | } |
| 191 | 193 | ||
| 192 | while ((count = kevent(g_kqueue, NULL, 0, g_ready, max, deadline)) == -1 && errno == EINTR) | 194 | /* An interrupted wait is reported as no events rather than retried here, |
| 193 | ; | 195 | so that the caller gets to look at whatever the signal was about */ |
| 196 | if ((count = kevent(g_kqueue, NULL, 0, g_ready, max, deadline)) == -1) | ||
| 197 | return errno == EINTR ? 0 : -1; | ||
| 194 | for (i = 0; i < count; ++i) { | 198 | for (i = 0; i < count; ++i) { |
| 195 | uint64_t udata = (uint64_t)(uintptr_t)g_ready[i].udata; | 199 | uint64_t udata = (uint64_t)(uintptr_t)g_ready[i].udata; |
| 196 | int eof = (g_ready[i].flags & (EV_EOF | EV_ERROR)) != 0; | 200 | int eof = (g_ready[i].flags & (EV_EOF | EV_ERROR)) != 0; |
| @@ -310,8 +314,10 @@ int event_del(int fd) { | |||
| 310 | int event_wait(ot_event *out, int max, int timeout_ms) { | 314 | int event_wait(ot_event *out, int max, int timeout_ms) { |
| 311 | int ready, found = 0, i; | 315 | int ready, found = 0, i; |
| 312 | 316 | ||
| 313 | while ((ready = poll(g_pfds, (nfds_t)g_count, timeout_ms)) == -1 && errno == EINTR) | 317 | /* An interrupted wait is reported as no events rather than retried here, |
| 314 | ; | 318 | so that the caller gets to look at whatever the signal was about */ |
| 319 | if ((ready = poll(g_pfds, (nfds_t)g_count, timeout_ms)) == -1) | ||
| 320 | return errno == EINTR ? 0 : -1; | ||
| 315 | if (ready <= 0) | 321 | if (ready <= 0) |
| 316 | return ready; | 322 | return ready; |
| 317 | 323 | ||
diff --git a/ot_fullscrape.c b/ot_fullscrape.c index 8518bd2..a86d6d7 100644 --- a/ot_fullscrape.c +++ b/ot_fullscrape.c | |||
| @@ -55,6 +55,8 @@ static void *fullscrape_worker(void *args) { | |||
| 55 | while (g_opentracker_running) { | 55 | while (g_opentracker_running) { |
| 56 | ot_tasktype tasktype = TASK_FULLSCRAPE; | 56 | ot_tasktype tasktype = TASK_FULLSCRAPE; |
| 57 | ot_taskid taskid = mutex_workqueue_poptask(&tasktype); | 57 | ot_taskid taskid = mutex_workqueue_poptask(&tasktype); |
| 58 | if (!taskid) | ||
| 59 | break; | ||
| 58 | #ifdef WANT_COMPRESSION_ZSTD | 60 | #ifdef WANT_COMPRESSION_ZSTD |
| 59 | if (tasktype & TASK_FLAG_ZSTD) | 61 | if (tasktype & TASK_FLAG_ZSTD) |
| 60 | fullscrape_make_zstd(taskid, tasktype); | 62 | fullscrape_make_zstd(taskid, tasktype); |
| @@ -77,7 +79,8 @@ void fullscrape_init(void) { | |||
| 77 | } | 79 | } |
| 78 | 80 | ||
| 79 | void fullscrape_deinit(void) { | 81 | void fullscrape_deinit(void) { |
| 80 | pthread_cancel( thread_id ); | 82 | mutex_workqueue_wakeall(); |
| 83 | pthread_join( thread_id, NULL ); | ||
| 81 | } | 84 | } |
| 82 | 85 | ||
| 83 | void fullscrape_deliver( int64_t sock, ot_tasktype tasktype ) { | 86 | void fullscrape_deliver( int64_t sock, ot_tasktype tasktype ) { |
| @@ -143,7 +143,7 @@ ssize_t http_issue_error(const int64_t sock, struct ot_workstruct *ws, int code) | |||
| 143 | #ifdef _DEBUG_HTTPERROR | 143 | #ifdef _DEBUG_HTTPERROR |
| 144 | fprintf(stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf); | 144 | fprintf(stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf); |
| 145 | #endif | 145 | #endif |
| 146 | stats_issue_event(EVENT_FAILED, FLAG_TCP, code); | 146 | stats_issue_event(ws, EVENT_FAILED, FLAG_TCP, code); |
| 147 | http_senddata(sock, ws); | 147 | http_senddata(sock, ws); |
| 148 | return ws->reply_size = -2; | 148 | return ws->reply_size = -2; |
| 149 | } | 149 | } |
| @@ -222,7 +222,7 @@ ssize_t http_sendiovecdata(const int64_t sock, struct ot_workstruct *ws, size_t | |||
| 222 | cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; | 222 | cookie->flag &= ~STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER; |
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | fdstate_set_expiry(sock, g_now_seconds + OT_CLIENT_TIMEOUT_SEND); | 225 | fdstate_set_expiry(sock, time_future_seconds(OT_CLIENT_TIMEOUT_SEND)); |
| 226 | http_interest_add(sock, cookie, EVENT_OUT); | 226 | http_interest_add(sock, cookie, EVENT_OUT); |
| 227 | return 0; | 227 | return 0; |
| 228 | } | 228 | } |
| @@ -359,7 +359,7 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct * | |||
| 359 | ot_scrape_log this_peer, *new_peer; | 359 | ot_scrape_log this_peer, *new_peer; |
| 360 | int exactmatch; | 360 | int exactmatch; |
| 361 | memcpy(this_peer.ip, cookie->ip, sizeof(ot_ip6)); | 361 | memcpy(this_peer.ip, cookie->ip, sizeof(ot_ip6)); |
| 362 | this_peer.last_fullscrape = g_now_seconds; | 362 | this_peer.last_fullscrape = time_now_seconds(); |
| 363 | pthread_mutex_lock(&g_modest_fullscrape_mutex); | 363 | pthread_mutex_lock(&g_modest_fullscrape_mutex); |
| 364 | new_peer = vector_find_or_insert(&g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch); | 364 | new_peer = vector_find_or_insert(&g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch); |
| 365 | if (!new_peer) { | 365 | if (!new_peer) { |
| @@ -402,7 +402,7 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct * | |||
| 402 | #endif | 402 | #endif |
| 403 | #endif | 403 | #endif |
| 404 | 404 | ||
| 405 | stats_issue_event(EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip); | 405 | stats_issue_event(ws, EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip); |
| 406 | 406 | ||
| 407 | #ifdef _DEBUG_HTTPERROR | 407 | #ifdef _DEBUG_HTTPERROR |
| 408 | fprintf(stderr, "%s", ws->debugbuf); | 408 | fprintf(stderr, "%s", ws->debugbuf); |
| @@ -474,7 +474,7 @@ static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, | |||
| 474 | /* Enough for http header + whole scrape string */ | 474 | /* Enough for http header + whole scrape string */ |
| 475 | return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, &reply, ws->outbuf + G_OUTBUF_SIZE); | 475 | return_tcp_scrape_for_torrent((const ot_hash *)multiscrape_buf, numwant, &reply, ws->outbuf + G_OUTBUF_SIZE); |
| 476 | ws->reply_size = reply - ws->reply; | 476 | ws->reply_size = reply - ws->reply; |
| 477 | stats_issue_event(EVENT_SCRAPE, FLAG_TCP, ws->reply_size); | 477 | stats_issue_event(ws, EVENT_SCRAPE, FLAG_TCP, ws->reply_size); |
| 478 | return ws->reply_size; | 478 | return ws->reply_size; |
| 479 | } | 479 | } |
| 480 | 480 | ||
| @@ -682,7 +682,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 682 | #endif | 682 | #endif |
| 683 | 683 | ||
| 684 | /* XXX DEBUG | 684 | /* XXX DEBUG |
| 685 | stats_issue_event( EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ws->reply ); | 685 | stats_issue_event(ws, EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ws->reply ); |
| 686 | */ | 686 | */ |
| 687 | 687 | ||
| 688 | /* Scanned whole query string */ | 688 | /* Scanned whole query string */ |
| @@ -694,7 +694,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct | |||
| 694 | else | 694 | else |
| 695 | ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_TCP, ws, numwant); | 695 | ws->reply_size = add_peer_to_torrent_and_return_peers(FLAG_TCP, ws, numwant); |
| 696 | 696 | ||
| 697 | stats_issue_event(EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); | 697 | stats_issue_event(ws, EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); |
| 698 | return ws->reply_size; | 698 | return ws->reply_size; |
| 699 | } | 699 | } |
| 700 | 700 | ||
| @@ -710,7 +710,7 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) { | |||
| 710 | log->size = ws->request_size; | 710 | log->size = ws->request_size; |
| 711 | log->data = malloc(ws->request_size); | 711 | log->data = malloc(ws->request_size); |
| 712 | log->next = 0; | 712 | log->next = 0; |
| 713 | log->time = g_now_seconds; | 713 | log->time = time_now_seconds(); |
| 714 | memcpy(log->ip, cookie->ip, sizeof(ot_ip6)); | 714 | memcpy(log->ip, cookie->ip, sizeof(ot_ip6)); |
| 715 | if (log->data) { | 715 | if (log->data) { |
| 716 | memcpy(log->data, ws->request, ws->request_size); | 716 | memcpy(log->data, ws->request, ws->request_size); |
diff --git a/ot_livesync.c b/ot_livesync.c index 5a63f3c..45271bc 100644 --- a/ot_livesync.c +++ b/ot_livesync.c | |||
| @@ -70,8 +70,8 @@ void livesync_init(void) { | |||
| 70 | g_v6_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t); | 70 | g_v6_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t); |
| 71 | g_v4_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t); | 71 | g_v4_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t); |
| 72 | 72 | ||
| 73 | g_v6_buf.next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY; | 73 | g_v6_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); |
| 74 | g_v4_buf.next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY; | 74 | g_v4_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); |
| 75 | 75 | ||
| 76 | pthread_create(&thread_id, NULL, livesync_worker, NULL); | 76 | pthread_create(&thread_id, NULL, livesync_worker, NULL); |
| 77 | } | 77 | } |
| @@ -106,7 +106,7 @@ static void livesync_issue_peersync(sync_buffer *buf) { | |||
| 106 | 106 | ||
| 107 | memcpy(mycopy, buf->data, fill); | 107 | memcpy(mycopy, buf->data, fill); |
| 108 | buf->fill = sizeof(g_tracker_id) + sizeof(uint32_t); | 108 | buf->fill = sizeof(g_tracker_id) + sizeof(uint32_t); |
| 109 | buf->next_packet_time = g_now_seconds + LIVESYNC_MAXDELAY; | 109 | buf->next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); |
| 110 | 110 | ||
| 111 | /* From now this thread has a local copy of the buffer and | 111 | /* From now this thread has a local copy of the buffer and |
| 112 | has modified the protected element */ | 112 | has modified the protected element */ |
| @@ -143,7 +143,7 @@ static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) | |||
| 143 | off += sizeof(ot_hash) + peer_size; | 143 | off += sizeof(ot_hash) + peer_size; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | stats_issue_event(EVENT_SYNC, 0, (ws->request_size - sizeof(g_tracker_id) - sizeof(uint32_t)) / ((ssize_t)sizeof(ot_hash) + peer_size)); | 146 | stats_issue_event(ws, EVENT_SYNC, 0, (ws->request_size - sizeof(g_tracker_id) - sizeof(uint32_t)) / ((ssize_t)sizeof(ot_hash) + peer_size)); |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | /* Tickle the live sync module from time to time, so no events get | 149 | /* Tickle the live sync module from time to time, so no events get |
| @@ -152,13 +152,13 @@ static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) | |||
| 152 | void livesync_ticker(void) { | 152 | void livesync_ticker(void) { |
| 153 | /* livesync_issue_peersync sets g_next_packet_time */ | 153 | /* livesync_issue_peersync sets g_next_packet_time */ |
| 154 | pthread_mutex_lock(&g_outbuf_mutex); | 154 | pthread_mutex_lock(&g_outbuf_mutex); |
| 155 | if (g_now_seconds > g_v6_buf.next_packet_time && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) | 155 | if (time_now_seconds() > g_v6_buf.next_packet_time && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) |
| 156 | livesync_issue_peersync(&g_v6_buf); | 156 | livesync_issue_peersync(&g_v6_buf); |
| 157 | else | 157 | else |
| 158 | pthread_mutex_unlock(&g_outbuf_mutex); | 158 | pthread_mutex_unlock(&g_outbuf_mutex); |
| 159 | 159 | ||
| 160 | pthread_mutex_lock(&g_outbuf_mutex); | 160 | pthread_mutex_lock(&g_outbuf_mutex); |
| 161 | if (g_now_seconds > g_v4_buf.next_packet_time && g_v4_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) | 161 | if (time_now_seconds() > g_v4_buf.next_packet_time && g_v4_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) |
| 162 | livesync_issue_peersync(&g_v4_buf); | 162 | livesync_issue_peersync(&g_v4_buf); |
| 163 | else | 163 | else |
| 164 | pthread_mutex_unlock(&g_outbuf_mutex); | 164 | pthread_mutex_unlock(&g_outbuf_mutex); |
| @@ -6,6 +6,7 @@ | |||
| 6 | /* System */ | 6 | /* System */ |
| 7 | #include <arpa/inet.h> | 7 | #include <arpa/inet.h> |
| 8 | #include <pthread.h> | 8 | #include <pthread.h> |
| 9 | #include <stdatomic.h> | ||
| 9 | #include <stdio.h> | 10 | #include <stdio.h> |
| 10 | #include <stdlib.h> | 11 | #include <stdlib.h> |
| 11 | #include <unistd.h> | 12 | #include <unistd.h> |
| @@ -25,7 +26,9 @@ | |||
| 25 | /* Our global all torrents list */ | 26 | /* Our global all torrents list */ |
| 26 | static ot_vector all_torrents[OT_BUCKET_COUNT]; | 27 | static ot_vector all_torrents[OT_BUCKET_COUNT]; |
| 27 | static pthread_mutex_t bucket_mutex[OT_BUCKET_COUNT]; | 28 | static pthread_mutex_t bucket_mutex[OT_BUCKET_COUNT]; |
| 28 | static size_t g_torrent_count; | 29 | /* Every bucket's unlock adjusts this, and a bucket lock only keeps other |
| 30 | threads out of that one bucket, so the counter needs its own protection */ | ||
| 31 | static _Atomic size_t g_torrent_count; | ||
| 29 | 32 | ||
| 30 | /* Self pipe from opentracker.c */ | 33 | /* Self pipe from opentracker.c */ |
| 31 | extern int g_self_pipe[2]; | 34 | extern int g_self_pipe[2]; |
| @@ -47,14 +50,15 @@ ot_vector *mutex_bucket_lock_by_hash(ot_hash const hash) { return mutex_bucket_l | |||
| 47 | 50 | ||
| 48 | void mutex_bucket_unlock(int bucket, int delta_torrentcount) { | 51 | void mutex_bucket_unlock(int bucket, int delta_torrentcount) { |
| 49 | pthread_mutex_unlock(bucket_mutex + bucket); | 52 | pthread_mutex_unlock(bucket_mutex + bucket); |
| 50 | g_torrent_count += delta_torrentcount; | 53 | if (delta_torrentcount) |
| 54 | atomic_fetch_add_explicit(&g_torrent_count, (size_t)delta_torrentcount, memory_order_relaxed); | ||
| 51 | } | 55 | } |
| 52 | 56 | ||
| 53 | void mutex_bucket_unlock_by_hash(ot_hash const hash, int delta_torrentcount) { | 57 | void mutex_bucket_unlock_by_hash(ot_hash const hash, int delta_torrentcount) { |
| 54 | mutex_bucket_unlock(bucket_from_hash(hash), delta_torrentcount); | 58 | mutex_bucket_unlock(bucket_from_hash(hash), delta_torrentcount); |
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | size_t mutex_get_torrent_count(void) { return g_torrent_count; } | 61 | size_t mutex_get_torrent_count(void) { return atomic_load_explicit(&g_torrent_count, memory_order_relaxed); } |
| 58 | 62 | ||
| 59 | /* TaskQueue Magic */ | 63 | /* TaskQueue Magic */ |
| 60 | 64 | ||
| @@ -145,9 +149,13 @@ ot_taskid mutex_workqueue_poptask(ot_tasktype *tasktype) { | |||
| 145 | break; | 149 | break; |
| 146 | } | 150 | } |
| 147 | 151 | ||
| 148 | /* Wait until the next task is being fed */ | 152 | /* Wait until the next task is being fed, unless the tracker is on its |
| 149 | if (!taskid) | 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; | ||
| 150 | pthread_cond_wait(&tasklist_being_filled, &tasklist_mutex); | 157 | pthread_cond_wait(&tasklist_being_filled, &tasklist_mutex); |
| 158 | } | ||
| 151 | } | 159 | } |
| 152 | 160 | ||
| 153 | /* Release lock */ | 161 | /* Release lock */ |
| @@ -271,6 +279,13 @@ void mutex_init(void) { | |||
| 271 | memset(all_torrents, 0, sizeof(all_torrents)); | 279 | memset(all_torrents, 0, sizeof(all_torrents)); |
| 272 | } | 280 | } |
| 273 | 281 | ||
| 282 | /* Let every worker parked on the queue see that the tracker is stopping */ | ||
| 283 | void 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 | |||
| 274 | void mutex_deinit(void) { | 289 | void mutex_deinit(void) { |
| 275 | size_t i; | 290 | size_t i; |
| 276 | for (i = 0; i < OT_BUCKET_COUNT; ++i) | 291 | for (i = 0; i < OT_BUCKET_COUNT; ++i) |
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "trackerlogic.h" | 10 | #include "trackerlogic.h" |
| 11 | 11 | ||
| 12 | void mutex_init(void); | 12 | void mutex_init(void); |
| 13 | void mutex_workqueue_wakeall(void); | ||
| 13 | void mutex_deinit(void); | 14 | void mutex_deinit(void); |
| 14 | 15 | ||
| 15 | ot_vector *mutex_bucket_lock(int bucket); | 16 | ot_vector *mutex_bucket_lock(int bucket); |
| @@ -6,6 +6,7 @@ | |||
| 6 | /* System */ | 6 | /* System */ |
| 7 | #include <inttypes.h> | 7 | #include <inttypes.h> |
| 8 | #include <pthread.h> | 8 | #include <pthread.h> |
| 9 | #include <stdatomic.h> | ||
| 9 | #include <stdio.h> | 10 | #include <stdio.h> |
| 10 | #include <stdlib.h> | 11 | #include <stdlib.h> |
| 11 | #include <string.h> | 12 | #include <string.h> |
| @@ -36,27 +37,58 @@ | |||
| 36 | static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode); | 37 | static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode); |
| 37 | #define OT_STATS_TMPSIZE 8192 | 38 | #define OT_STATS_TMPSIZE 8192 |
| 38 | 39 | ||
| 39 | /* Clumsy counters... to be rethought */ | 40 | /* Each serving thread keeps its own statistics tally. The list is linked |
| 40 | static unsigned long long ot_overall_tcp_connections; | 41 | through intrusive next pointers within the workstruct. |
| 41 | static unsigned long long ot_overall_udp_connections; | 42 | |
| 42 | static unsigned long long ot_overall_tcp_successfulannounces; | 43 | Registration happens once per thread and the adding up once per statistics |
| 43 | static unsigned long long ot_overall_udp_successfulannounces; | 44 | request, so this lock is never on anybody's hot path. */ |
| 44 | static unsigned long long ot_overall_tcp_successfulscrapes; | 45 | static struct ot_workstruct *g_counter_contexts; |
| 45 | static unsigned long long ot_overall_udp_successfulscrapes; | 46 | static ot_counter g_retired[OT_COUNTER_COUNT]; |
| 46 | static unsigned long long ot_overall_udp_connectionidmissmatches; | 47 | static pthread_mutex_t g_counter_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 47 | static unsigned long long ot_overall_tcp_connects; | 48 | |
| 48 | static unsigned long long ot_overall_udp_connects; | 49 | void stats_counters_register(struct ot_workstruct *ws) { |
| 49 | static unsigned long long ot_overall_completed; | 50 | pthread_mutex_lock(&g_counter_mutex); |
| 50 | static unsigned long long ot_full_scrape_count; | 51 | ws->counters_next = g_counter_contexts; |
| 51 | static unsigned long long ot_full_scrape_request_count; | 52 | g_counter_contexts = ws; |
| 52 | static unsigned long long ot_full_scrape_size; | 53 | pthread_mutex_unlock(&g_counter_mutex); |
| 53 | static unsigned long long ot_failed_request_counts[CODE_HTTPERROR_COUNT]; | 54 | } |
| 55 | |||
| 56 | void stats_counters_retire(struct ot_workstruct *ws) { | ||
| 57 | struct ot_workstruct **link; | ||
| 58 | size_t i; | ||
| 59 | |||
| 60 | pthread_mutex_lock(&g_counter_mutex); | ||
| 61 | for (link = &g_counter_contexts; *link; link = &(*link)->counters_next) | ||
| 62 | if (*link == ws) { | ||
| 63 | *link = ws->counters_next; | ||
| 64 | break; | ||
| 65 | } | ||
| 66 | |||
| 67 | /* When retiring a worker thread, collect the counter tally in the global */ | ||
| 68 | for (i = 0; i < OT_COUNTER_COUNT; ++i) | ||
| 69 | atomic_fetch_add_explicit(g_retired + i, atomic_load_explicit(ws->counters + i, memory_order_relaxed), memory_order_relaxed); | ||
| 70 | pthread_mutex_unlock(&g_counter_mutex); | ||
| 71 | } | ||
| 72 | |||
| 73 | /* What every thread has counted so far, added together */ | ||
| 74 | static void counters_total(unsigned long long *total) { | ||
| 75 | struct ot_workstruct *ws; | ||
| 76 | size_t i; | ||
| 77 | |||
| 78 | pthread_mutex_lock(&g_counter_mutex); | ||
| 79 | for (i = 0; i < OT_COUNTER_COUNT; ++i) | ||
| 80 | total[i] = atomic_load_explicit(g_retired + i, memory_order_relaxed); | ||
| 81 | for (ws = g_counter_contexts; ws; ws = ws->counters_next) | ||
| 82 | for (i = 0; i < OT_COUNTER_COUNT; ++i) | ||
| 83 | total[i] += atomic_load_explicit(ws->counters + i, memory_order_relaxed); | ||
| 84 | pthread_mutex_unlock(&g_counter_mutex); | ||
| 85 | } | ||
| 86 | |||
| 87 | #define COUNT(index, by) atomic_fetch_add_explicit(ws->counters + (index), (unsigned long long)(by), memory_order_relaxed) | ||
| 88 | |||
| 54 | static const char *ot_failed_request_names[] = { | 89 | static const char *ot_failed_request_names[] = { |
| 55 | "302 Redirect", "400 Parse Error", "400 Invalid Parameter", "400 Invalid Parameter (compact=0)", "400 Not Modest", | 90 | "302 Redirect", "400 Parse Error", "400 Invalid Parameter", "400 Invalid Parameter (compact=0)", "400 Not Modest", |
| 56 | "402 Payment Required", "403 Access Denied", "404 Not found", "500 Internal Server Error"}; | 91 | "402 Payment Required", "403 Access Denied", "404 Not found", "500 Internal Server Error"}; |
| 57 | static unsigned long long ot_renewed[OT_PEER_TIMEOUT]; | ||
| 58 | static unsigned long long ot_overall_sync_count; | ||
| 59 | static unsigned long long ot_overall_stall_count; | ||
| 60 | 92 | ||
| 61 | static time_t ot_start_time; | 93 | static time_t ot_start_time; |
| 62 | 94 | ||
| @@ -347,39 +379,61 @@ static unsigned long events_per_time(unsigned long long events, time_t t) { retu | |||
| 347 | 379 | ||
| 348 | static void stats_connections_mrtg(char **reply, const char *reply_end) { | 380 | static void stats_connections_mrtg(char **reply, const char *reply_end) { |
| 349 | ot_time t = time(NULL) - ot_start_time; | 381 | ot_time t = time(NULL) - ot_start_time; |
| 382 | unsigned long long total[OT_COUNTER_COUNT]; | ||
| 383 | |||
| 384 | counters_total(total); | ||
| 385 | |||
| 350 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", | 386 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", |
| 351 | ot_overall_tcp_connections + ot_overall_udp_connections, | 387 | total[OT_COUNTER_TCP_CONNECTIONS] + total[OT_COUNTER_UDP_CONNECTIONS], |
| 352 | ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, (int)t, (int)(t / 3600), | 388 | total[OT_COUNTER_TCP_ANNOUNCES] + total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], (int)t, (int)(t / 3600), |
| 353 | events_per_time(ot_overall_tcp_connections + ot_overall_udp_connections, t), | 389 | events_per_time(total[OT_COUNTER_TCP_CONNECTIONS] + total[OT_COUNTER_UDP_CONNECTIONS], t), |
| 354 | events_per_time(ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, t)); | 390 | events_per_time(total[OT_COUNTER_TCP_ANNOUNCES] + total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], t)); |
| 355 | } | 391 | } |
| 356 | 392 | ||
| 357 | static void stats_udpconnections_mrtg(char **reply, const char *reply_end) { | 393 | static void stats_udpconnections_mrtg(char **reply, const char *reply_end) { |
| 358 | ot_time t = time(NULL) - ot_start_time; | 394 | ot_time t = time(NULL) - ot_start_time; |
| 359 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats, %lu conns/s :: %lu success/s.", ot_overall_udp_connections, | 395 | unsigned long long total[OT_COUNTER_COUNT]; |
| 360 | ot_overall_udp_successfulannounces + ot_overall_udp_connects, (int)t, (int)(t / 3600), events_per_time(ot_overall_udp_connections, t), | 396 | |
| 361 | events_per_time(ot_overall_udp_successfulannounces + ot_overall_udp_connects, t)); | 397 | counters_total(total); |
| 398 | |||
| 399 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats, %lu conns/s :: %lu success/s.", total[OT_COUNTER_UDP_CONNECTIONS], | ||
| 400 | total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], (int)t, (int)(t / 3600), | ||
| 401 | events_per_time(total[OT_COUNTER_UDP_CONNECTIONS], t), | ||
| 402 | events_per_time(total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], t)); | ||
| 362 | } | 403 | } |
| 363 | 404 | ||
| 364 | static void stats_tcpconnections_mrtg(char **reply, const char *reply_end) { | 405 | static void stats_tcpconnections_mrtg(char **reply, const char *reply_end) { |
| 365 | time_t t = time(NULL) - ot_start_time; | 406 | time_t t = time(NULL) - ot_start_time; |
| 366 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats, %lu conns/s :: %lu success/s.", ot_overall_tcp_connections, | 407 | unsigned long long total[OT_COUNTER_COUNT]; |
| 367 | ot_overall_tcp_successfulannounces, (int)t, (int)(t / 3600), events_per_time(ot_overall_tcp_connections, t), | 408 | |
| 368 | events_per_time(ot_overall_tcp_successfulannounces, t)); | 409 | counters_total(total); |
| 410 | |||
| 411 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats, %lu conns/s :: %lu success/s.", total[OT_COUNTER_TCP_CONNECTIONS], | ||
| 412 | total[OT_COUNTER_TCP_ANNOUNCES], (int)t, (int)(t / 3600), events_per_time(total[OT_COUNTER_TCP_CONNECTIONS], t), | ||
| 413 | events_per_time(total[OT_COUNTER_TCP_ANNOUNCES], t)); | ||
| 369 | } | 414 | } |
| 370 | 415 | ||
| 371 | static void stats_scrape_mrtg(char **reply, const char *reply_end) { | 416 | static void stats_scrape_mrtg(char **reply, const char *reply_end) { |
| 372 | time_t t = time(NULL) - ot_start_time; | 417 | time_t t = time(NULL) - ot_start_time; |
| 418 | unsigned long long total[OT_COUNTER_COUNT]; | ||
| 419 | |||
| 420 | counters_total(total); | ||
| 421 | |||
| 373 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker scrape stats, %lu scrape/s (tcp and udp)", | 422 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker scrape stats, %lu scrape/s (tcp and udp)", |
| 374 | ot_overall_tcp_successfulscrapes, ot_overall_udp_successfulscrapes, (int)t, (int)(t / 3600), | 423 | total[OT_COUNTER_TCP_SCRAPES], total[OT_COUNTER_UDP_SCRAPES], (int)t, (int)(t / 3600), |
| 375 | events_per_time((ot_overall_tcp_successfulscrapes + ot_overall_udp_successfulscrapes), t)); | 424 | events_per_time((total[OT_COUNTER_TCP_SCRAPES] + total[OT_COUNTER_UDP_SCRAPES]), t)); |
| 376 | } | 425 | } |
| 377 | 426 | ||
| 378 | static void stats_fullscrapes_mrtg(char **reply, const char *reply_end) { | 427 | static void stats_fullscrapes_mrtg(char **reply, const char *reply_end) { |
| 379 | ot_time t = time(NULL) - ot_start_time; | 428 | ot_time t = time(NULL) - ot_start_time; |
| 429 | unsigned long long total[OT_COUNTER_COUNT]; | ||
| 430 | |||
| 431 | counters_total(total); | ||
| 432 | |||
| 380 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats, %lu conns/s :: %lu bytes/s.", | 433 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats, %lu conns/s :: %lu bytes/s.", |
| 381 | ot_full_scrape_count * 1000, ot_full_scrape_size, (int)t, (int)(t / 3600), events_per_time(ot_full_scrape_count, t), | 434 | total[OT_COUNTER_FULLSCRAPES] * 1000, total[OT_COUNTER_FULLSCRAPE_BYTES], (int)t, (int)(t / 3600), |
| 382 | events_per_time(ot_full_scrape_size, t)); | 435 | events_per_time(total[OT_COUNTER_FULLSCRAPES], t), |
| 436 | events_per_time(total[OT_COUNTER_FULLSCRAPE_BYTES], t)); | ||
| 383 | } | 437 | } |
| 384 | 438 | ||
| 385 | static void stats_peers_mrtg(char **reply, const char *reply_end) { | 439 | static void stats_peers_mrtg(char **reply, const char *reply_end) { |
| @@ -398,30 +452,46 @@ static void stats_torrents_mrtg(char **reply, const char *reply_end) { | |||
| 398 | } | 452 | } |
| 399 | 453 | ||
| 400 | static void stats_httperrors_txt(char **reply, const char *reply_end) { | 454 | static void stats_httperrors_txt(char **reply, const char *reply_end) { |
| 455 | unsigned long long total[OT_COUNTER_COUNT]; | ||
| 456 | |||
| 457 | counters_total(total); | ||
| 458 | |||
| 401 | fmt_append(reply, reply_end, "302 RED %llu\n400 ... %llu\n400 PAR %llu\n400 COM %llu\n403 IP %llu\n404 INV %llu\n500 SRV %llu\n", | 459 | fmt_append(reply, reply_end, "302 RED %llu\n400 ... %llu\n400 PAR %llu\n400 COM %llu\n403 IP %llu\n404 INV %llu\n500 SRV %llu\n", |
| 402 | ot_failed_request_counts[0], ot_failed_request_counts[1], ot_failed_request_counts[2], ot_failed_request_counts[3], | 460 | total[OT_COUNTER_FAILED + 0], total[OT_COUNTER_FAILED + 1], total[OT_COUNTER_FAILED + 2], total[OT_COUNTER_FAILED + 3], |
| 403 | ot_failed_request_counts[4], ot_failed_request_counts[5], ot_failed_request_counts[6]); | 461 | total[OT_COUNTER_FAILED + 4], total[OT_COUNTER_FAILED + 5], total[OT_COUNTER_FAILED + 6]); |
| 404 | } | 462 | } |
| 405 | 463 | ||
| 406 | static void stats_return_renew_bucket(char **reply, const char *reply_end) { | 464 | static void stats_return_renew_bucket(char **reply, const char *reply_end) { |
| 465 | unsigned long long total[OT_COUNTER_COUNT]; | ||
| 466 | |||
| 467 | counters_total(total); | ||
| 468 | |||
| 407 | int i; | 469 | int i; |
| 408 | 470 | ||
| 409 | for (i = 0; i < OT_PEER_TIMEOUT; ++i) | 471 | for (i = 0; i < OT_PEER_TIMEOUT; ++i) |
| 410 | fmt_append(reply, reply_end, "%02i %llu\n", i, ot_renewed[i]); | 472 | fmt_append(reply, reply_end, "%02i %llu\n", i, total[OT_COUNTER_RENEWED + i]); |
| 411 | } | 473 | } |
| 412 | 474 | ||
| 413 | static void stats_return_sync_mrtg(char **reply, const char *reply_end) { | 475 | static void stats_return_sync_mrtg(char **reply, const char *reply_end) { |
| 414 | ot_time t = time(NULL) - ot_start_time; | 476 | ot_time t = time(NULL) - ot_start_time; |
| 415 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", ot_overall_sync_count, 0LL, | 477 | unsigned long long total[OT_COUNTER_COUNT]; |
| 416 | (int)t, (int)(t / 3600), events_per_time(ot_overall_tcp_connections + ot_overall_udp_connections, t), | 478 | |
| 417 | events_per_time(ot_overall_tcp_successfulannounces + ot_overall_udp_successfulannounces + ot_overall_udp_connects, t)); | 479 | counters_total(total); |
| 480 | |||
| 481 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", total[OT_COUNTER_SYNCS], 0LL, | ||
| 482 | (int)t, (int)(t / 3600), events_per_time(total[OT_COUNTER_TCP_CONNECTIONS] + total[OT_COUNTER_UDP_CONNECTIONS], t), | ||
| 483 | events_per_time(total[OT_COUNTER_TCP_ANNOUNCES] + total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], t)); | ||
| 418 | } | 484 | } |
| 419 | 485 | ||
| 420 | static void stats_return_completed_mrtg(char **reply, const char *reply_end) { | 486 | static void stats_return_completed_mrtg(char **reply, const char *reply_end) { |
| 421 | ot_time t = time(NULL) - ot_start_time; | 487 | ot_time t = time(NULL) - ot_start_time; |
| 488 | unsigned long long total[OT_COUNTER_COUNT]; | ||
| 489 | |||
| 490 | counters_total(total); | ||
| 491 | |||
| 422 | 492 | ||
| 423 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker, %lu completed/h.", ot_overall_completed, 0LL, (int)t, (int)(t / 3600), | 493 | fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker, %lu completed/h.", total[OT_COUNTER_COMPLETED], 0LL, (int)t, (int)(t / 3600), |
| 424 | events_per_time(ot_overall_completed, t / 3600)); | 494 | events_per_time(total[OT_COUNTER_COMPLETED], t / 3600)); |
| 425 | } | 495 | } |
| 426 | 496 | ||
| 427 | #ifdef WANT_LOG_NUMWANT | 497 | #ifdef WANT_LOG_NUMWANT |
| @@ -464,6 +534,10 @@ static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector, | |||
| 464 | #endif | 534 | #endif |
| 465 | 535 | ||
| 466 | static void stats_return_everything(char **reply, const char *reply_end) { | 536 | static void stats_return_everything(char **reply, const char *reply_end) { |
| 537 | unsigned long long total[OT_COUNTER_COUNT]; | ||
| 538 | |||
| 539 | counters_total(total); | ||
| 540 | |||
| 467 | torrent_stats stats = {0, 0, 0}; | 541 | torrent_stats stats = {0, 0, 0}; |
| 468 | int i; | 542 | int i; |
| 469 | 543 | ||
| @@ -482,25 +556,25 @@ static void stats_return_everything(char **reply, const char *reply_end) { | |||
| 482 | fmt_append(reply, reply_end, " </torrents>\n"); | 556 | fmt_append(reply, reply_end, " </torrents>\n"); |
| 483 | fmt_append(reply, reply_end, " <peers>\n <count>%llu</count>\n </peers>\n", stats.peer_count); | 557 | fmt_append(reply, reply_end, " <peers>\n <count>%llu</count>\n </peers>\n", stats.peer_count); |
| 484 | fmt_append(reply, reply_end, " <seeds>\n <count>%llu</count>\n </seeds>\n", stats.seed_count); | 558 | fmt_append(reply, reply_end, " <seeds>\n <count>%llu</count>\n </seeds>\n", stats.seed_count); |
| 485 | fmt_append(reply, reply_end, " <completed>\n <count>%llu</count>\n </completed>\n", ot_overall_completed); | 559 | fmt_append(reply, reply_end, " <completed>\n <count>%llu</count>\n </completed>\n", total[OT_COUNTER_COMPLETED]); |
| 486 | fmt_append(reply, reply_end, " <connections>\n"); | 560 | fmt_append(reply, reply_end, " <connections>\n"); |
| 487 | fmt_append(reply, reply_end, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n", | 561 | fmt_append(reply, reply_end, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n", |
| 488 | ot_overall_tcp_connections, ot_overall_tcp_successfulannounces, ot_overall_tcp_successfulscrapes); | 562 | total[OT_COUNTER_TCP_CONNECTIONS], total[OT_COUNTER_TCP_ANNOUNCES], total[OT_COUNTER_TCP_SCRAPES]); |
| 489 | fmt_append(reply, reply_end, " <udp>\n <overall>%llu</overall>\n <connect>%llu</connect>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n <missmatch>%llu</missmatch>\n </udp>\n", | 563 | fmt_append(reply, reply_end, " <udp>\n <overall>%llu</overall>\n <connect>%llu</connect>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n <missmatch>%llu</missmatch>\n </udp>\n", |
| 490 | ot_overall_udp_connections, ot_overall_udp_connects, ot_overall_udp_successfulannounces, ot_overall_udp_successfulscrapes, | 564 | total[OT_COUNTER_UDP_CONNECTIONS], total[OT_COUNTER_UDP_CONNECTS], total[OT_COUNTER_UDP_ANNOUNCES], total[OT_COUNTER_UDP_SCRAPES], |
| 491 | ot_overall_udp_connectionidmissmatches); | 565 | total[OT_COUNTER_UDP_CONNID_MISSMATCH]); |
| 492 | fmt_append(reply, reply_end, " <livesync>\n <count>%llu</count>\n </livesync>\n", ot_overall_sync_count); | 566 | fmt_append(reply, reply_end, " <livesync>\n <count>%llu</count>\n </livesync>\n", total[OT_COUNTER_SYNCS]); |
| 493 | fmt_append(reply, reply_end, " </connections>\n"); | 567 | fmt_append(reply, reply_end, " </connections>\n"); |
| 494 | fmt_append(reply, reply_end, " <debug>\n"); | 568 | fmt_append(reply, reply_end, " <debug>\n"); |
| 495 | fmt_append(reply, reply_end, " <renew>\n"); | 569 | fmt_append(reply, reply_end, " <renew>\n"); |
| 496 | for (i = 0; i < OT_PEER_TIMEOUT; ++i) | 570 | for (i = 0; i < OT_PEER_TIMEOUT; ++i) |
| 497 | fmt_append(reply, reply_end, " <count interval=\"%02i\">%llu</count>\n", i, ot_renewed[i]); | 571 | fmt_append(reply, reply_end, " <count interval=\"%02i\">%llu</count>\n", i, total[OT_COUNTER_RENEWED + i]); |
| 498 | fmt_append(reply, reply_end, " </renew>\n"); | 572 | fmt_append(reply, reply_end, " </renew>\n"); |
| 499 | fmt_append(reply, reply_end, " <http_error>\n"); | 573 | fmt_append(reply, reply_end, " <http_error>\n"); |
| 500 | for (i = 0; i < CODE_HTTPERROR_COUNT; ++i) | 574 | for (i = 0; i < CODE_HTTPERROR_COUNT; ++i) |
| 501 | fmt_append(reply, reply_end, " <count code=\"%s\">%llu</count>\n", ot_failed_request_names[i], ot_failed_request_counts[i]); | 575 | fmt_append(reply, reply_end, " <count code=\"%s\">%llu</count>\n", ot_failed_request_names[i], total[OT_COUNTER_FAILED + i]); |
| 502 | fmt_append(reply, reply_end, " </http_error>\n"); | 576 | fmt_append(reply, reply_end, " </http_error>\n"); |
| 503 | fmt_append(reply, reply_end, " <mutex_stall>\n <count>%llu</count>\n </mutex_stall>\n", ot_overall_stall_count); | 577 | fmt_append(reply, reply_end, " <mutex_stall>\n <count>%llu</count>\n </mutex_stall>\n", total[OT_COUNTER_STALLS]); |
| 504 | fmt_append(reply, reply_end, " </debug>\n"); | 578 | fmt_append(reply, reply_end, " </debug>\n"); |
| 505 | fmt_append(reply, reply_end, "</stats>"); | 579 | fmt_append(reply, reply_end, "</stats>"); |
| 506 | } | 580 | } |
| @@ -608,33 +682,35 @@ static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_taskty | |||
| 608 | iovec_trim_last(iovec_entries, iovector, r); | 682 | iovec_trim_last(iovec_entries, iovector, r); |
| 609 | } | 683 | } |
| 610 | 684 | ||
| 611 | void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) { | 685 | void stats_issue_event(struct ot_workstruct *ws, ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) { |
| 686 | if (!ws) | ||
| 687 | return; | ||
| 688 | |||
| 612 | switch (event) { | 689 | switch (event) { |
| 613 | case EVENT_ACCEPT: | 690 | case EVENT_ACCEPT: |
| 614 | if (proto == FLAG_TCP) | 691 | if (proto == FLAG_TCP) |
| 615 | ot_overall_tcp_connections++; | 692 | COUNT(OT_COUNTER_TCP_CONNECTIONS, 1); |
| 616 | else | 693 | else |
| 617 | ot_overall_udp_connections++; | 694 | COUNT(OT_COUNTER_UDP_CONNECTIONS, 1); |
| 618 | #ifdef WANT_LOG_NETWORKS | 695 | #ifdef WANT_LOG_NETWORKS |
| 619 | stat_increase_network_count(&stats_network_counters_root, 0, event_data); | 696 | stat_increase_network_count(&stats_network_counters_root, 0, event_data); |
| 620 | #endif | 697 | #endif |
| 621 | break; | 698 | break; |
| 622 | case EVENT_ANNOUNCE: | 699 | case EVENT_ANNOUNCE: |
| 623 | if (proto == FLAG_TCP) | 700 | if (proto == FLAG_TCP) |
| 624 | ot_overall_tcp_successfulannounces++; | 701 | COUNT(OT_COUNTER_TCP_ANNOUNCES, 1); |
| 625 | else | 702 | else |
| 626 | ot_overall_udp_successfulannounces++; | 703 | COUNT(OT_COUNTER_UDP_ANNOUNCES, 1); |
| 627 | break; | 704 | break; |
| 628 | case EVENT_CONNECT: | 705 | case EVENT_CONNECT: |
| 629 | if (proto == FLAG_TCP) | 706 | if (proto == FLAG_TCP) |
| 630 | ot_overall_tcp_connects++; | 707 | COUNT(OT_COUNTER_TCP_CONNECTS, 1); |
| 631 | else | 708 | else |
| 632 | ot_overall_udp_connects++; | 709 | COUNT(OT_COUNTER_UDP_CONNECTS, 1); |
| 633 | break; | 710 | break; |
| 634 | case EVENT_COMPLETED: | 711 | case EVENT_COMPLETED: |
| 635 | #ifdef WANT_SYSLOGS | 712 | #ifdef WANT_SYSLOGS |
| 636 | if (event_data) { | 713 | { |
| 637 | struct ot_workstruct *ws = (struct ot_workstruct *)event_data; | ||
| 638 | char timestring[64]; | 714 | char timestring[64]; |
| 639 | char hash_hex[42], peerid_hex[42], ip_readable[64]; | 715 | char hash_hex[42], peerid_hex[42], ip_readable[64]; |
| 640 | struct tm time_now; | 716 | struct tm time_now; |
| @@ -655,47 +731,47 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_ | |||
| 655 | syslog(LOG_INFO, "time=%s event=completed info_hash=%s peer_id=%s ip=%s", timestring, hash_hex, peerid_hex, ip_readable); | 731 | syslog(LOG_INFO, "time=%s event=completed info_hash=%s peer_id=%s ip=%s", timestring, hash_hex, peerid_hex, ip_readable); |
| 656 | } | 732 | } |
| 657 | #endif | 733 | #endif |
| 658 | ot_overall_completed++; | 734 | COUNT(OT_COUNTER_COMPLETED, 1); |
| 659 | break; | 735 | break; |
| 660 | case EVENT_SCRAPE: | 736 | case EVENT_SCRAPE: |
| 661 | if (proto == FLAG_TCP) | 737 | if (proto == FLAG_TCP) |
| 662 | ot_overall_tcp_successfulscrapes++; | 738 | COUNT(OT_COUNTER_TCP_SCRAPES, 1); |
| 663 | else | 739 | else |
| 664 | ot_overall_udp_successfulscrapes++; | 740 | COUNT(OT_COUNTER_UDP_SCRAPES, 1); |
| 665 | break; | 741 | break; |
| 666 | case EVENT_FULLSCRAPE: | 742 | case EVENT_FULLSCRAPE: |
| 667 | ot_full_scrape_count++; | 743 | COUNT(OT_COUNTER_FULLSCRAPES, 1); |
| 668 | ot_full_scrape_size += event_data; | 744 | COUNT(OT_COUNTER_FULLSCRAPE_BYTES, event_data); |
| 669 | break; | 745 | break; |
| 670 | case EVENT_FULLSCRAPE_REQUEST: { | 746 | case EVENT_FULLSCRAPE_REQUEST: { |
| 671 | ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ | 747 | ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ |
| 672 | char _debug[512]; | 748 | char _debug[512]; |
| 673 | int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60); | 749 | int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(time_now_seconds() - ot_start_time) / 60); |
| 674 | off += ip_format(_debug + off, *ip); | 750 | off += ip_format(_debug + off, *ip); |
| 675 | off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); | 751 | off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); |
| 676 | (void)write(2, _debug, off); | 752 | (void)write(2, _debug, off); |
| 677 | ot_full_scrape_request_count++; | 753 | COUNT(OT_COUNTER_FULLSCRAPE_REQUESTS, 1); |
| 678 | } break; | 754 | } break; |
| 679 | case EVENT_FULLSCRAPE_REQUEST_GZIP: { | 755 | case EVENT_FULLSCRAPE_REQUEST_GZIP: { |
| 680 | ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ | 756 | ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ |
| 681 | char _debug[512]; | 757 | char _debug[512]; |
| 682 | int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time) / 60); | 758 | int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(time_now_seconds() - ot_start_time) / 60); |
| 683 | off += ip_format(_debug + off, *ip); | 759 | off += ip_format(_debug + off, *ip); |
| 684 | off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); | 760 | off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); |
| 685 | (void)write(2, _debug, off); | 761 | (void)write(2, _debug, off); |
| 686 | ot_full_scrape_request_count++; | 762 | COUNT(OT_COUNTER_FULLSCRAPE_REQUESTS, 1); |
| 687 | } break; | 763 | } break; |
| 688 | case EVENT_FAILED: | 764 | case EVENT_FAILED: |
| 689 | ot_failed_request_counts[event_data]++; | 765 | COUNT(OT_COUNTER_FAILED + event_data, 1); |
| 690 | break; | 766 | break; |
| 691 | case EVENT_RENEW: | 767 | case EVENT_RENEW: |
| 692 | ot_renewed[event_data]++; | 768 | COUNT(OT_COUNTER_RENEWED + event_data, 1); |
| 693 | break; | 769 | break; |
| 694 | case EVENT_SYNC: | 770 | case EVENT_SYNC: |
| 695 | ot_overall_sync_count += event_data; | 771 | COUNT(OT_COUNTER_SYNCS, event_data); |
| 696 | break; | 772 | break; |
| 697 | case EVENT_BUCKET_LOCKED: | 773 | case EVENT_BUCKET_LOCKED: |
| 698 | ot_overall_stall_count++; | 774 | COUNT(OT_COUNTER_STALLS, 1); |
| 699 | break; | 775 | break; |
| 700 | #ifdef WANT_SPOT_WOODPECKER | 776 | #ifdef WANT_SPOT_WOODPECKER |
| 701 | case EVENT_WOODPECKER: | 777 | case EVENT_WOODPECKER: |
| @@ -705,7 +781,7 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_ | |||
| 705 | break; | 781 | break; |
| 706 | #endif | 782 | #endif |
| 707 | case EVENT_CONNID_MISSMATCH: | 783 | case EVENT_CONNID_MISSMATCH: |
| 708 | ++ot_overall_udp_connectionidmissmatches; | 784 | COUNT(OT_COUNTER_UDP_CONNID_MISSMATCH, 1); |
| 709 | default: | 785 | default: |
| 710 | break; | 786 | break; |
| 711 | } | 787 | } |
| @@ -725,9 +801,11 @@ static void *stats_worker(void *args) { | |||
| 725 | 801 | ||
| 726 | (void)args; | 802 | (void)args; |
| 727 | 803 | ||
| 728 | while (1) { | 804 | while (g_opentracker_running) { |
| 729 | ot_tasktype tasktype = TASK_STATS; | 805 | ot_tasktype tasktype = TASK_STATS; |
| 730 | ot_taskid taskid = mutex_workqueue_poptask(&tasktype); | 806 | ot_taskid taskid = mutex_workqueue_poptask(&tasktype); |
| 807 | if (!taskid) | ||
| 808 | break; | ||
| 731 | stats_make(&iovec_entries, &iovector, tasktype); | 809 | stats_make(&iovec_entries, &iovector, tasktype); |
| 732 | if (mutex_workqueue_pushresult(taskid, iovec_entries, iovector)) | 810 | if (mutex_workqueue_pushresult(taskid, iovec_entries, iovector)) |
| 733 | iovec_free(&iovec_entries, &iovector); | 811 | iovec_free(&iovec_entries, &iovector); |
| @@ -739,10 +817,11 @@ void stats_deliver(int64_t sock, int tasktype) { mutex_workqueue_pushtask(sock, | |||
| 739 | 817 | ||
| 740 | static pthread_t thread_id; | 818 | static pthread_t thread_id; |
| 741 | void stats_init(void) { | 819 | void stats_init(void) { |
| 742 | ot_start_time = g_now_seconds; | 820 | ot_start_time = time_now_seconds(); |
| 743 | pthread_create(&thread_id, NULL, stats_worker, NULL); | 821 | pthread_create(&thread_id, NULL, stats_worker, NULL); |
| 744 | } | 822 | } |
| 745 | 823 | ||
| 746 | void stats_deinit(void) { | 824 | void stats_deinit(void) { |
| 747 | pthread_cancel(thread_id); | 825 | mutex_workqueue_wakeall(); |
| 826 | pthread_join(thread_id, NULL); | ||
| 748 | } | 827 | } |
| @@ -27,21 +27,10 @@ typedef enum { | |||
| 27 | EVENT_CONNID_MISSMATCH | 27 | EVENT_CONNID_MISSMATCH |
| 28 | } ot_status_event; | 28 | } ot_status_event; |
| 29 | 29 | ||
| 30 | enum { | 30 | void stats_counters_register(struct ot_workstruct *ws); |
| 31 | CODE_HTTPERROR_302, | 31 | void stats_counters_retire(struct ot_workstruct *ws); |
| 32 | CODE_HTTPERROR_400, | ||
| 33 | CODE_HTTPERROR_400_PARAM, | ||
| 34 | CODE_HTTPERROR_400_COMPACT, | ||
| 35 | CODE_HTTPERROR_402_NOTMODEST, | ||
| 36 | CODE_HTTPERROR_402_PAYMENT_REQUIRED, | ||
| 37 | CODE_HTTPERROR_403_IP, | ||
| 38 | CODE_HTTPERROR_404, | ||
| 39 | CODE_HTTPERROR_500, | ||
| 40 | 32 | ||
| 41 | CODE_HTTPERROR_COUNT | 33 | void stats_issue_event(struct ot_workstruct *ws, ot_status_event event, PROTO_FLAG proto, uintptr_t event_data); |
| 42 | }; | ||
| 43 | |||
| 44 | void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data); | ||
| 45 | void stats_deliver(int64_t sock, int tasktype); | 34 | void stats_deliver(int64_t sock, int tasktype); |
| 46 | void stats_cleanup(void); | 35 | void stats_cleanup(void); |
| 47 | void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int format); | 36 | void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int format); |
| @@ -141,7 +141,7 @@ static void *sync_worker(void *args) { | |||
| 141 | ot_tasktype tasktype = TASK_SYNC_OUT; | 141 | ot_tasktype tasktype = TASK_SYNC_OUT; |
| 142 | ot_taskid taskid = mutex_workqueue_poptask(&tasktype); | 142 | ot_taskid taskid = mutex_workqueue_poptask(&tasktype); |
| 143 | sync_make(&iovec_entries, &iovector); | 143 | sync_make(&iovec_entries, &iovector); |
| 144 | stats_issue_event(EVENT_SYNC_OUT, FLAG_TCP, iovec_length(&iovec_entries, &iovector)); | 144 | stats_issue_event(ws, EVENT_SYNC_OUT, FLAG_TCP, iovec_length(&iovec_entries, &iovector)); |
| 145 | if (mutex_workqueue_pushresult(taskid, iovec_entries, iovector)) | 145 | if (mutex_workqueue_pushresult(taskid, iovec_entries, iovector)) |
| 146 | iovec_free(&iovec_entries, &iovector); | 146 | iovec_free(&iovec_entries, &iovector); |
| 147 | } | 147 | } |
| @@ -30,7 +30,7 @@ static void udp_generate_rijndael_round_key(void) { | |||
| 30 | rijndaelKeySetupEnc128(g_rijndael_round_key, (uint8_t *)key); | 30 | rijndaelKeySetupEnc128(g_rijndael_round_key, (uint8_t *)key); |
| 31 | 31 | ||
| 32 | g_key_of_the_hour[0] = random_uint32(); | 32 | g_key_of_the_hour[0] = random_uint32(); |
| 33 | g_hour_of_the_key = g_now_minutes; | 33 | g_hour_of_the_key = time_now_minutes(); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | /* Generate current and previous connection id for ip */ | 36 | /* Generate current and previous connection id for ip */ |
| @@ -39,14 +39,14 @@ static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int | |||
| 39 | int i; | 39 | int i; |
| 40 | uint32_t current_key_of_the_hour; | 40 | uint32_t current_key_of_the_hour; |
| 41 | 41 | ||
| 42 | if (g_now_minutes - g_hour_of_the_key >= 60) { | 42 | if (time_now_minutes() - g_hour_of_the_key >= 60) { |
| 43 | uint32_t old_key_of_the_hour = g_key_of_the_hour[0]; | 43 | uint32_t old_key_of_the_hour = g_key_of_the_hour[0]; |
| 44 | uint32_t new_key_of_the_hour = random_uint32(); | 44 | uint32_t new_key_of_the_hour = random_uint32(); |
| 45 | /* If in the meantime another thread has performed | 45 | /* If in the meantime another thread has performed |
| 46 | key rotation, do not overwrite their results */ | 46 | key rotation, do not overwrite their results */ |
| 47 | if (g_now_minutes - g_hour_of_the_key >= 60) { | 47 | if (time_now_minutes() - g_hour_of_the_key >= 60) { |
| 48 | /* Upgrade en bloc */ | 48 | /* Upgrade en bloc */ |
| 49 | g_hour_of_the_key = g_now_minutes; | 49 | g_hour_of_the_key = time_now_minutes(); |
| 50 | g_key_of_the_hour[0] = new_key_of_the_hour; | 50 | g_key_of_the_hour[0] = new_key_of_the_hour; |
| 51 | g_key_of_the_hour[1] = old_key_of_the_hour; | 51 | g_key_of_the_hour[1] = old_key_of_the_hour; |
| 52 | } | 52 | } |
| @@ -77,8 +77,8 @@ int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) { | |||
| 77 | return 0; | 77 | return 0; |
| 78 | byte_count = (size_t)result; | 78 | byte_count = (size_t)result; |
| 79 | 79 | ||
| 80 | stats_issue_event(EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip); | 80 | stats_issue_event(ws, EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip); |
| 81 | stats_issue_event(EVENT_READ, FLAG_UDP, byte_count); | 81 | stats_issue_event(ws, EVENT_READ, FLAG_UDP, byte_count); |
| 82 | 82 | ||
| 83 | /* Minimum udp tracker packet size, also catches error */ | 83 | /* Minimum udp tracker packet size, also catches error */ |
| 84 | if (byte_count < 16) | 84 | if (byte_count < 16) |
| @@ -111,7 +111,7 @@ int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) { | |||
| 111 | outpacket[1] = inpacket[3]; | 111 | outpacket[1] = inpacket[3]; |
| 112 | memcpy(&outpacket[2], &err_idmiss[0], s); | 112 | memcpy(&outpacket[2], &err_idmiss[0], s); |
| 113 | socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport); | 113 | socket_send(serversocket, ws->outbuf, 8 + s, remoteip, remoteport); |
| 114 | stats_issue_event(EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s); | 114 | stats_issue_event(ws, EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s); |
| 115 | return 1; | 115 | return 1; |
| 116 | } | 116 | } |
| 117 | } | 117 | } |
| @@ -128,7 +128,7 @@ int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) { | |||
| 128 | outpacket[3] = connid[1]; | 128 | outpacket[3] = connid[1]; |
| 129 | 129 | ||
| 130 | socket_send(serversocket, ws->outbuf, 16, remoteip, remoteport); | 130 | socket_send(serversocket, ws->outbuf, 16, remoteip, remoteport); |
| 131 | stats_issue_event(EVENT_CONNECT, FLAG_UDP, 16); | 131 | stats_issue_event(ws, EVENT_CONNECT, FLAG_UDP, 16); |
| 132 | break; | 132 | break; |
| 133 | case 1: /* This is an announce action */ | 133 | case 1: /* This is an announce action */ |
| 134 | /* Minimum udp announce packet size */ | 134 | /* Minimum udp announce packet size */ |
| @@ -188,7 +188,7 @@ int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) { | |||
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | socket_send(serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport); | 190 | socket_send(serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport); |
| 191 | stats_issue_event(EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size); | 191 | stats_issue_event(ws, EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size); |
| 192 | break; | 192 | break; |
| 193 | 193 | ||
| 194 | case 2: /* This is a scrape action */ | 194 | case 2: /* This is a scrape action */ |
| @@ -199,7 +199,7 @@ int handle_udp6(int64_t serversocket, struct ot_workstruct *ws) { | |||
| 199 | return_udp_scrape_for_torrent(*(ot_hash *)(((char *)inpacket) + 16 + 20 * scrape_count), ((char *)outpacket) + 8 + 12 * scrape_count); | 199 | return_udp_scrape_for_torrent(*(ot_hash *)(((char *)inpacket) + 16 + 20 * scrape_count), ((char *)outpacket) + 8 + 12 * scrape_count); |
| 200 | 200 | ||
| 201 | socket_send(serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport); | 201 | socket_send(serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport); |
| 202 | stats_issue_event(EVENT_SCRAPE, FLAG_UDP, scrape_count); | 202 | stats_issue_event(ws, EVENT_SCRAPE, FLAG_UDP, scrape_count); |
| 203 | break; | 203 | break; |
| 204 | } | 204 | } |
| 205 | return 1; | 205 | return 1; |
| @@ -210,6 +210,7 @@ static void *udp_worker(void *args) { | |||
| 210 | struct ot_workstruct ws; | 210 | struct ot_workstruct ws; |
| 211 | memset(&ws, 0, sizeof(ws)); | 211 | memset(&ws, 0, sizeof(ws)); |
| 212 | 212 | ||
| 213 | stats_counters_register(&ws); | ||
| 213 | ws.inbuf = malloc(G_INBUF_SIZE); | 214 | ws.inbuf = malloc(G_INBUF_SIZE); |
| 214 | ws.outbuf = malloc(G_OUTBUF_SIZE); | 215 | ws.outbuf = malloc(G_OUTBUF_SIZE); |
| 215 | #ifdef _DEBUG_HTTPERROR | 216 | #ifdef _DEBUG_HTTPERROR |
| @@ -219,6 +220,8 @@ static void *udp_worker(void *args) { | |||
| 219 | while (g_opentracker_running) | 220 | while (g_opentracker_running) |
| 220 | handle_udp6(sock, &ws); | 221 | handle_udp6(sock, &ws); |
| 221 | 222 | ||
| 223 | stats_counters_retire(&ws); | ||
| 224 | |||
| 222 | free(ws.inbuf); | 225 | free(ws.inbuf); |
| 223 | free(ws.outbuf); | 226 | free(ws.outbuf); |
| 224 | #ifdef _DEBUG_HTTPERROR | 227 | #ifdef _DEBUG_HTTPERROR |
diff --git a/ot_vector.c b/ot_vector.c index a56d55b..8310634 100644 --- a/ot_vector.c +++ b/ot_vector.c | |||
| @@ -208,10 +208,9 @@ void vector_redistribute_buckets(ot_peerlist *peer_list, size_t peer_size) { | |||
| 208 | return; | 208 | return; |
| 209 | 209 | ||
| 210 | /* Assume near perfect distribution */ | 210 | /* Assume near perfect distribution */ |
| 211 | bucket_list_new = malloc(num_buckets_new * sizeof(ot_vector)); | 211 | bucket_list_new = calloc(1, num_buckets_new * sizeof(ot_vector)); |
| 212 | if (!bucket_list_new) | 212 | if (!bucket_list_new) |
| 213 | return; | 213 | return; |
| 214 | memset(bucket_list_new, 0, num_buckets_new * sizeof(ot_vector)); | ||
| 215 | 214 | ||
| 216 | bucket_size_new = OT_VECTOR_MIN_MEMBERS; | 215 | bucket_size_new = OT_VECTOR_MIN_MEMBERS; |
| 217 | while (bucket_size_new * num_buckets_new < peer_list->peer_count) | 216 | while (bucket_size_new * num_buckets_new < peer_list->peer_count) |
| @@ -78,7 +78,8 @@ void exerr(const char *message) { | |||
| 78 | exit(111); | 78 | exit(111); |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) { | 81 | void stats_issue_event(struct ot_workstruct *ws, ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) { |
| 82 | (void)ws; | ||
| 82 | (void)event; | 83 | (void)event; |
| 83 | (void)proto; | 84 | (void)proto; |
| 84 | (void)event_data; | 85 | (void)event_data; |
diff --git a/tests/integration/harness.py b/tests/integration/harness.py index 03a0561..122c36d 100644 --- a/tests/integration/harness.py +++ b/tests/integration/harness.py | |||
| @@ -121,6 +121,7 @@ class Tracker: | |||
| 121 | all: LeakSanitizer reports from an exit handler, so a killed tracker | 121 | all: LeakSanitizer reports from an exit handler, so a killed tracker |
| 122 | reports nothing and the suite passes over leaks it never looked for. | 122 | reports nothing and the suite passes over leaks it never looked for. |
| 123 | """ | 123 | """ |
| 124 | refused_to_exit = False | ||
| 124 | if self.process is not None and self.process.poll() is None: | 125 | if self.process is not None and self.process.poll() is None: |
| 125 | self.process.send_signal(signal.SIGINT) | 126 | self.process.send_signal(signal.SIGINT) |
| 126 | try: | 127 | try: |
| @@ -128,11 +129,16 @@ class Tracker: | |||
| 128 | except subprocess.TimeoutExpired: | 129 | except subprocess.TimeoutExpired: |
| 129 | self.process.kill() | 130 | self.process.kill() |
| 130 | self.process.wait() | 131 | self.process.wait() |
| 132 | refused_to_exit = True | ||
| 131 | complaints = [ | 133 | complaints = [ |
| 132 | line | 134 | line |
| 133 | for line in self.output().splitlines() | 135 | for line in self.output().splitlines() |
| 134 | if "Sanitizer" in line or "runtime error" in line or line.startswith("ERROR") | 136 | if "Sanitizer" in line or "runtime error" in line or line.startswith("ERROR") |
| 135 | ] | 137 | ] |
| 138 | if refused_to_exit: | ||
| 139 | # Killing it throws away whatever the sanitizer had to say on the | ||
| 140 | # way out, so a test that ends this way has checked nothing. | ||
| 141 | complaints.append("the tracker did not exit within 5 s of SIGINT and had to be killed") | ||
| 136 | self._cleanup() | 142 | self._cleanup() |
| 137 | return complaints | 143 | return complaints |
| 138 | 144 | ||
diff --git a/tests/integration/test_concurrency.py b/tests/integration/test_concurrency.py new file mode 100644 index 0000000..1374852 --- /dev/null +++ b/tests/integration/test_concurrency.py | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | """Several clients at once. | ||
| 2 | |||
| 3 | The rest of the suite talks to the tracker one request at a time, which | ||
| 4 | leaves the worker threads and the bucket locks almost untouched. These reach | ||
| 5 | them. | ||
| 6 | |||
| 7 | What they assert is weak on purpose. A lost update needs two threads inside | ||
| 8 | the same few instructions, which is rare enough that these tests pass | ||
| 9 | against code that has the race: measured, eight runs in a row. Their worth | ||
| 10 | is that they make the racing code run at all, so that "make check-tsan" | ||
| 11 | has something to report. Reaching the code and judging it are two jobs, and | ||
| 12 | these only do the first. | ||
| 13 | """ | ||
| 14 | |||
| 15 | import struct | ||
| 16 | import threading | ||
| 17 | import unittest | ||
| 18 | |||
| 19 | from harness import TrackerTestCase, bdecode | ||
| 20 | |||
| 21 | THREADS = 8 | ||
| 22 | PER_THREAD = 40 | ||
| 23 | |||
| 24 | |||
| 25 | def run_threads(target, count=THREADS): | ||
| 26 | errors = [] | ||
| 27 | |||
| 28 | def guarded(index): | ||
| 29 | try: | ||
| 30 | target(index) | ||
| 31 | except Exception as exception: # noqa: BLE001 - reported, not swallowed | ||
| 32 | errors.append("thread %d: %r" % (index, exception)) | ||
| 33 | |||
| 34 | threads = [threading.Thread(target=guarded, args=(i,)) for i in range(count)] | ||
| 35 | for thread in threads: | ||
| 36 | thread.start() | ||
| 37 | for thread in threads: | ||
| 38 | thread.join() | ||
| 39 | return errors | ||
| 40 | |||
| 41 | |||
| 42 | class ConcurrentAnnounce(TrackerTestCase): | ||
| 43 | def test_every_peer_of_one_swarm_arrives(self): | ||
| 44 | """One torrent, one bucket, every thread contending for its lock.""" | ||
| 45 | info_hash = self.hash_for("concurrent-one") | ||
| 46 | |||
| 47 | def announce(index): | ||
| 48 | for i in range(PER_THREAD): | ||
| 49 | self.tracker.announce(info_hash, port=10000 + index * PER_THREAD + i, left=0) | ||
| 50 | |||
| 51 | self.assertEqual(run_threads(announce), []) | ||
| 52 | answer = self.tracker.scrape(info_hash) | ||
| 53 | self.assertEqual(answer[b"files"][info_hash][b"complete"], THREADS * PER_THREAD) | ||
| 54 | |||
| 55 | def test_inserts_spread_over_buckets(self): | ||
| 56 | """Every thread inserts into a different bucket, so the bucket locks | ||
| 57 | serialise nothing between them, and the counter they all adjust is | ||
| 58 | the one global the locks do not cover. Under check-tsan this is what | ||
| 59 | names mutex_bucket_unlock if that counter stops being atomic.""" | ||
| 60 | |||
| 61 | def announce(index): | ||
| 62 | for i in range(PER_THREAD): | ||
| 63 | self.tracker.announce(struct.pack(">HH", index, i) + b"\0" * 16, port=11000 + i, left=0) | ||
| 64 | |||
| 65 | self.assertEqual(run_threads(announce), []) | ||
| 66 | self.assertEqual(self.tracker.torrent_count(), THREADS * PER_THREAD) | ||
| 67 | |||
| 68 | def test_a_swarm_that_fills_and_empties_at_once(self): | ||
| 69 | """Inserts and removals racing in the same buckets.""" | ||
| 70 | info_hash = self.hash_for("concurrent-churn") | ||
| 71 | for port in range(12000, 12000 + THREADS * PER_THREAD): | ||
| 72 | self.tracker.announce(info_hash, port=port, left=0) | ||
| 73 | |||
| 74 | def churn(index): | ||
| 75 | base = 12000 + index * PER_THREAD | ||
| 76 | for i in range(PER_THREAD): | ||
| 77 | self.tracker.announce(info_hash, port=base + i, left=0, event="stopped") | ||
| 78 | self.tracker.announce(info_hash, port=base + i, left=100) | ||
| 79 | |||
| 80 | self.assertEqual(run_threads(churn), []) | ||
| 81 | answer = self.tracker.scrape(info_hash) | ||
| 82 | self.assertEqual(answer[b"files"][info_hash][b"incomplete"], THREADS * PER_THREAD) | ||
| 83 | self.assertEqual(answer[b"files"][info_hash][b"complete"], 0) | ||
| 84 | |||
| 85 | |||
| 86 | class ConcurrentMixedBearers(TrackerTestCase): | ||
| 87 | def test_tcp_and_udp_reach_the_same_swarm(self): | ||
| 88 | info_hash = self.hash_for("concurrent-mixed") | ||
| 89 | |||
| 90 | def over_tcp(index): | ||
| 91 | for i in range(PER_THREAD): | ||
| 92 | self.tracker.announce(info_hash, port=13000 + index * PER_THREAD + i, left=0) | ||
| 93 | |||
| 94 | def over_udp(index): | ||
| 95 | sock, connection_id = self.tracker.udp_connect() | ||
| 96 | try: | ||
| 97 | for i in range(PER_THREAD): | ||
| 98 | self.tracker.udp_announce( | ||
| 99 | sock, connection_id, info_hash, left=0, port=14000 + index * PER_THREAD + i | ||
| 100 | ) | ||
| 101 | finally: | ||
| 102 | sock.close() | ||
| 103 | |||
| 104 | errors = [] | ||
| 105 | threads = [threading.Thread(target=lambda i=i: over_tcp(i)) for i in range(THREADS // 2)] | ||
| 106 | threads += [threading.Thread(target=lambda i=i: over_udp(i)) for i in range(THREADS // 2)] | ||
| 107 | for thread in threads: | ||
| 108 | thread.start() | ||
| 109 | for thread in threads: | ||
| 110 | thread.join() | ||
| 111 | |||
| 112 | self.assertEqual(errors, []) | ||
| 113 | answer = self.tracker.scrape(info_hash) | ||
| 114 | self.assertEqual(answer[b"files"][info_hash][b"complete"], THREADS * PER_THREAD) | ||
| 115 | |||
| 116 | |||
| 117 | class ConcurrentWorkerTasks(TrackerTestCase): | ||
| 118 | def test_full_scrapes_while_the_swarm_moves(self): | ||
| 119 | """The full scrape runs on a worker thread, walking the same buckets | ||
| 120 | the announces are changing.""" | ||
| 121 | for i in range(200): | ||
| 122 | self.tracker.announce(struct.pack(">I", i) + b"\0" * 16, port=15000 + i, left=0) | ||
| 123 | |||
| 124 | def announce(index): | ||
| 125 | for i in range(PER_THREAD): | ||
| 126 | self.tracker.announce(struct.pack(">I", i) + b"\0" * 16, port=16000 + index * PER_THREAD + i, left=0) | ||
| 127 | |||
| 128 | def scrape(index): | ||
| 129 | for _ in range(6): | ||
| 130 | answer = bdecode(self.tracker.get("/scrape")) | ||
| 131 | for counts in answer[b"files"].values(): | ||
| 132 | if counts[b"complete"] < 0 or counts[b"incomplete"] < 0: | ||
| 133 | raise AssertionError("negative count in a full scrape: %r" % counts) | ||
| 134 | |||
| 135 | threads = [threading.Thread(target=announce, args=(i,)) for i in range(THREADS // 2)] | ||
| 136 | threads += [threading.Thread(target=scrape, args=(i,)) for i in range(2)] | ||
| 137 | for thread in threads: | ||
| 138 | thread.start() | ||
| 139 | for thread in threads: | ||
| 140 | thread.join() | ||
| 141 | |||
| 142 | self.assertEqual(self.tracker.torrent_count(), 200) | ||
| 143 | |||
| 144 | def test_stats_while_the_swarm_moves(self): | ||
| 145 | def announce(index): | ||
| 146 | for i in range(PER_THREAD): | ||
| 147 | self.tracker.announce(struct.pack(">HH", index, i) + b"\0" * 16, port=17000 + i, left=i % 2) | ||
| 148 | |||
| 149 | def poll(index): | ||
| 150 | for _ in range(10): | ||
| 151 | for mode in ("everything", "top100", "peer", "torr", "s24s"): | ||
| 152 | self.tracker.stats(mode) | ||
| 153 | |||
| 154 | threads = [threading.Thread(target=announce, args=(i,)) for i in range(THREADS // 2)] | ||
| 155 | threads += [threading.Thread(target=poll, args=(i,)) for i in range(2)] | ||
| 156 | for thread in threads: | ||
| 157 | thread.start() | ||
| 158 | for thread in threads: | ||
| 159 | thread.join() | ||
| 160 | |||
| 161 | self.assertEqual(self.tracker.torrent_count(), (THREADS // 2) * PER_THREAD) | ||
| 162 | |||
| 163 | |||
| 164 | class CountersAcrossContexts(TrackerTestCase): | ||
| 165 | """With udp workers configured, every one of them counts into a block of | ||
| 166 | its own, and the figure reported is those blocks added up. A registry | ||
| 167 | that lost a block, or a worker that wrote into somebody else's, would | ||
| 168 | show here and nowhere else in the suite: without this directive udp is | ||
| 169 | served by the main loop, which is a single context.""" | ||
| 170 | |||
| 171 | config = "listen.udp.workers 4\n" | ||
| 172 | |||
| 173 | def test_the_udp_count_is_the_sum_of_every_worker(self): | ||
| 174 | before = int(self.tracker.stats("udp4").splitlines()[0]) | ||
| 175 | |||
| 176 | packets = 0 | ||
| 177 | for round_ in range(6): | ||
| 178 | sock, connection_id = self.tracker.udp_connect() # one packet | ||
| 179 | packets += 1 | ||
| 180 | try: | ||
| 181 | for i in range(10): | ||
| 182 | self.tracker.udp_announce(sock, connection_id, self.hash_for("ctx%d" % i), left=0) | ||
| 183 | packets += 1 | ||
| 184 | finally: | ||
| 185 | sock.close() | ||
| 186 | |||
| 187 | after = int(self.tracker.stats("udp4").splitlines()[0]) | ||
| 188 | self.assertEqual(after - before, packets, "every packet counted exactly once") | ||
| 189 | |||
| 190 | def test_announces_from_several_workers_reach_one_swarm(self): | ||
| 191 | info_hash = self.hash_for("ctx-swarm") | ||
| 192 | |||
| 193 | def announce(index): | ||
| 194 | sock, connection_id = self.tracker.udp_connect() | ||
| 195 | try: | ||
| 196 | for i in range(PER_THREAD): | ||
| 197 | self.tracker.udp_announce( | ||
| 198 | sock, connection_id, info_hash, left=0, port=18000 + index * PER_THREAD + i | ||
| 199 | ) | ||
| 200 | finally: | ||
| 201 | sock.close() | ||
| 202 | |||
| 203 | self.assertEqual(run_threads(announce, 4), []) | ||
| 204 | answer = self.tracker.scrape(info_hash) | ||
| 205 | self.assertEqual(answer[b"files"][info_hash][b"complete"], 4 * PER_THREAD) | ||
| 206 | |||
| 207 | |||
| 208 | if __name__ == "__main__": | ||
| 209 | unittest.main() | ||
diff --git a/tests/stubs.c b/tests/stubs.c index 09a7312..a8e6962 100644 --- a/tests/stubs.c +++ b/tests/stubs.c | |||
| @@ -16,8 +16,7 @@ void free_peerlist(ot_peerlist *peer_list) { (void)peer_list; } | |||
| 16 | /* ot_accesslist.c is built with WANT_ACCESSLIST_BLACK so that the hash parser | 16 | /* ot_accesslist.c is built with WANT_ACCESSLIST_BLACK so that the hash parser |
| 17 | is reachable. These two belong to accesslist_readfile and the reload worker, | 17 | is reachable. These two belong to accesslist_readfile and the reload worker, |
| 18 | neither of which any test drives. */ | 18 | neither of which any test drives. */ |
| 19 | time_t g_now_seconds; | 19 | _Atomic int g_opentracker_running; |
| 20 | volatile int g_opentracker_running; | ||
| 21 | 20 | ||
| 22 | /* ot_mutex.c wakes the main loop through the self pipe when a result is | 21 | /* ot_mutex.c wakes the main loop through the self pipe when a result is |
| 23 | ready. The queue tests never get that far. */ | 22 | ready. The queue tests never get that far. */ |
diff --git a/trackerlogic.c b/trackerlogic.c index 1f38f07..477c4a6 100644 --- a/trackerlogic.c +++ b/trackerlogic.c | |||
| @@ -117,8 +117,8 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 117 | } else | 117 | } else |
| 118 | clean_single_torrent(torrent); | 118 | clean_single_torrent(torrent); |
| 119 | 119 | ||
| 120 | torrent->peer_list6->base = g_now_minutes; | 120 | torrent->peer_list6->base = time_now_minutes(); |
| 121 | torrent->peer_list4->base = g_now_minutes; | 121 | torrent->peer_list4->base = time_now_minutes(); |
| 122 | 122 | ||
| 123 | peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; | 123 | peer_list = peer_size == OT_PEER_SIZE6 ? torrent->peer_list6 : torrent->peer_list4; |
| 124 | 124 | ||
| @@ -149,16 +149,16 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 149 | peer_list->peer_count++; | 149 | peer_list->peer_count++; |
| 150 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) { | 150 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) { |
| 151 | peer_list->down_count++; | 151 | peer_list->down_count++; |
| 152 | stats_issue_event(EVENT_COMPLETED, 0, (uintptr_t)ws); | 152 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); |
| 153 | } | 153 | } |
| 154 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING) | 154 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING) |
| 155 | peer_list->seed_count++; | 155 | peer_list->seed_count++; |
| 156 | 156 | ||
| 157 | } else { | 157 | } else { |
| 158 | stats_issue_event(EVENT_RENEW, 0, OT_PEERTIME(peer_dest, peer_size)); | 158 | stats_issue_event(ws, EVENT_RENEW, 0, OT_PEERTIME(peer_dest, peer_size)); |
| 159 | #ifdef WANT_SPOT_WOODPECKER | 159 | #ifdef WANT_SPOT_WOODPECKER |
| 160 | if ((OT_PEERTIME(peer_dest, peer_size) > 0) && (OT_PEERTIME(peer_dest, peer_size) < 20)) | 160 | if ((OT_PEERTIME(peer_dest, peer_size) > 0) && (OT_PEERTIME(peer_dest, peer_size) < 20)) |
| 161 | stats_issue_event(EVENT_WOODPECKER, 0, (uintptr_t)&ws->peer); | 161 | stats_issue_event(ws, EVENT_WOODPECKER, 0, (uintptr_t)&ws->peer); |
| 162 | #endif | 162 | #endif |
| 163 | #ifdef WANT_SYNC_LIVE | 163 | #ifdef WANT_SYNC_LIVE |
| 164 | /* Won't live sync peers that come back too fast. Only exception: | 164 | /* Won't live sync peers that come back too fast. Only exception: |
| @@ -176,7 +176,7 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 176 | peer_list->seed_count++; | 176 | peer_list->seed_count++; |
| 177 | if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)) { | 177 | if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)) { |
| 178 | peer_list->down_count++; | 178 | peer_list->down_count++; |
| 179 | stats_issue_event(EVENT_COMPLETED, 0, (uintptr_t)ws); | 179 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); |
| 180 | } | 180 | } |
| 181 | if (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) | 181 | if (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) |
| 182 | OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED; | 182 | OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED; |
| @@ -571,6 +571,13 @@ void trackerlogic_deinit(void) { | |||
| 571 | int delta_torrentcount = 0; | 571 | int delta_torrentcount = 0; |
| 572 | size_t bucket, j; | 572 | size_t bucket, j; |
| 573 | 573 | ||
| 574 | /* Stop the background workers first */ | ||
| 575 | stats_deinit(); | ||
| 576 | livesync_deinit(); | ||
| 577 | accesslist_deinit(); | ||
| 578 | fullscrape_deinit(); | ||
| 579 | clean_deinit(); | ||
| 580 | |||
| 574 | /* Free all torrents... */ | 581 | /* Free all torrents... */ |
| 575 | for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { | 582 | for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) { |
| 576 | ot_vector *torrents_list = mutex_bucket_lock(bucket); | 583 | ot_vector *torrents_list = mutex_bucket_lock(bucket); |
| @@ -586,12 +593,6 @@ void trackerlogic_deinit(void) { | |||
| 586 | mutex_bucket_unlock(bucket, delta_torrentcount); | 593 | mutex_bucket_unlock(bucket, delta_torrentcount); |
| 587 | } | 594 | } |
| 588 | 595 | ||
| 589 | /* Deinitialise background worker threads */ | ||
| 590 | stats_deinit(); | ||
| 591 | livesync_deinit(); | ||
| 592 | accesslist_deinit(); | ||
| 593 | fullscrape_deinit(); | ||
| 594 | clean_deinit(); | ||
| 595 | /* Release mutexes */ | 596 | /* Release mutexes */ |
| 596 | mutex_deinit(); | 597 | mutex_deinit(); |
| 597 | } | 598 | } |
diff --git a/trackerlogic.h b/trackerlogic.h index 63abf78..ad6c911 100644 --- a/trackerlogic.h +++ b/trackerlogic.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #ifndef OT_TRACKERLOGIC_H__ | 6 | #ifndef OT_TRACKERLOGIC_H__ |
| 7 | #define OT_TRACKERLOGIC_H__ | 7 | #define OT_TRACKERLOGIC_H__ |
| 8 | 8 | ||
| 9 | #include <stdatomic.h> | ||
| 9 | #include <stdint.h> | 10 | #include <stdint.h> |
| 10 | #include <stdlib.h> | 11 | #include <stdlib.h> |
| 11 | #include <sys/time.h> | 12 | #include <sys/time.h> |
| @@ -67,10 +68,28 @@ typedef struct { | |||
| 67 | on startup */ | 68 | on startup */ |
| 68 | #define RANDOMTORRENTS (1024 * 1024 * 1) | 69 | #define RANDOMTORRENTS (1024 * 1024 * 1) |
| 69 | 70 | ||
| 70 | /* From opentracker.c */ | 71 | #if defined(CLOCK_REALTIME_COARSE) |
| 71 | extern time_t g_now_seconds; | 72 | #define OT_CLOCK_CHEAP CLOCK_REALTIME_COARSE /* Linux */ |
| 72 | extern volatile int g_opentracker_running; | 73 | #elif defined(CLOCK_REALTIME_FAST) |
| 73 | #define g_now_minutes (g_now_seconds / 60) | 74 | #define OT_CLOCK_CHEAP CLOCK_REALTIME_FAST /* the BSDs */ |
| 75 | #else | ||
| 76 | #define OT_CLOCK_CHEAP CLOCK_REALTIME /* macOS and whatever else */ | ||
| 77 | #endif | ||
| 78 | |||
| 79 | static inline ot_time time_now_seconds(void) { | ||
| 80 | struct timespec now; | ||
| 81 | clock_gettime(OT_CLOCK_CHEAP, &now); | ||
| 82 | return (ot_time)now.tv_sec; | ||
| 83 | } | ||
| 84 | |||
| 85 | static inline ot_time time_now_minutes(void) { return time_now_seconds() / 60; } | ||
| 86 | |||
| 87 | /* For the deadlines the tracker sets on itself, which read better as the | ||
| 88 | span they allow than as an addition to the current second */ | ||
| 89 | static inline ot_time time_future_seconds(int span) { return time_now_seconds() + span; } | ||
| 90 | /* Cleared from the interrupt handler and read by every worker */ | ||
| 91 | extern _Atomic int g_opentracker_running; | ||
| 92 | |||
| 74 | 93 | ||
| 75 | extern uint32_t g_tracker_id; | 94 | extern uint32_t g_tracker_id; |
| 76 | typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG; | 95 | typedef enum { FLAG_TCP, FLAG_UDP, FLAG_MCA, FLAG_SELFPIPE } PROTO_FLAG; |
| @@ -129,8 +148,56 @@ struct ot_peerlist { | |||
| 129 | }; | 148 | }; |
| 130 | #define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list)->peers.size > (peer_list)->peers.space) | 149 | #define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list)->peers.size > (peer_list)->peers.space) |
| 131 | 150 | ||
| 151 | /* The http replies the tracker counts when it has to refuse a request. */ | ||
| 152 | enum { | ||
| 153 | CODE_HTTPERROR_302, | ||
| 154 | CODE_HTTPERROR_400, | ||
| 155 | CODE_HTTPERROR_400_PARAM, | ||
| 156 | CODE_HTTPERROR_400_COMPACT, | ||
| 157 | CODE_HTTPERROR_402_NOTMODEST, | ||
| 158 | CODE_HTTPERROR_402_PAYMENT_REQUIRED, | ||
| 159 | CODE_HTTPERROR_403_IP, | ||
| 160 | CODE_HTTPERROR_404, | ||
| 161 | CODE_HTTPERROR_500, | ||
| 162 | |||
| 163 | CODE_HTTPERROR_COUNT | ||
| 164 | }; | ||
| 165 | |||
| 166 | /* One statistic. Used when workers report stats to their workstruct */ | ||
| 167 | typedef _Atomic unsigned long long ot_counter; | ||
| 168 | |||
| 169 | /* What each thread counts for itself. The two runs at the end are indexed by | ||
| 170 | the http error code and by the age in minutes of a renewed peer */ | ||
| 171 | typedef enum { | ||
| 172 | OT_COUNTER_TCP_CONNECTIONS, | ||
| 173 | OT_COUNTER_UDP_CONNECTIONS, | ||
| 174 | OT_COUNTER_TCP_ANNOUNCES, | ||
| 175 | OT_COUNTER_UDP_ANNOUNCES, | ||
| 176 | OT_COUNTER_TCP_SCRAPES, | ||
| 177 | OT_COUNTER_UDP_SCRAPES, | ||
| 178 | OT_COUNTER_TCP_CONNECTS, | ||
| 179 | OT_COUNTER_UDP_CONNECTS, | ||
| 180 | OT_COUNTER_UDP_CONNID_MISSMATCH, | ||
| 181 | OT_COUNTER_COMPLETED, | ||
| 182 | OT_COUNTER_FULLSCRAPES, | ||
| 183 | OT_COUNTER_FULLSCRAPE_REQUESTS, | ||
| 184 | OT_COUNTER_FULLSCRAPE_BYTES, | ||
| 185 | OT_COUNTER_SYNCS, | ||
| 186 | OT_COUNTER_STALLS, | ||
| 187 | |||
| 188 | OT_COUNTER_FAILED, /* CODE_HTTPERROR_COUNT of them */ | ||
| 189 | OT_COUNTER_RENEWED = OT_COUNTER_FAILED + CODE_HTTPERROR_COUNT, /* OT_PEER_TIMEOUT of them */ | ||
| 190 | |||
| 191 | OT_COUNTER_COUNT = OT_COUNTER_RENEWED + OT_PEER_TIMEOUT | ||
| 192 | } ot_counter_index; | ||
| 193 | |||
| 132 | struct ot_workstruct { | 194 | struct ot_workstruct { |
| 133 | /* Thread specific, static */ | 195 | /* Thread specific, static */ |
| 196 | |||
| 197 | /* This thread's own statistics, and the link ot_stats keeps them on */ | ||
| 198 | ot_counter counters[OT_COUNTER_COUNT]; | ||
| 199 | struct ot_workstruct *counters_next; | ||
| 200 | |||
| 134 | char *inbuf; | 201 | char *inbuf; |
| 135 | #define G_INBUF_SIZE 8192 | 202 | #define G_INBUF_SIZE 8192 |
| 136 | char *outbuf; | 203 | char *outbuf; |
