Make all threads joinable and join on agent shutdown (#20228)
Make all threads joinable / join on exit
Stelios Fragkakis committed
May 6, 2025 at 16:57 UTC
cb0cc8361bd66592e8e0370fdd082546ea158195
22 files changed
+71
-75
src/collectors/ebpf.plugin/ebpf.c
+2
-2
@@ -4244,7 +4244,7 @@ static void ebpf_initialize_data_sharing()
4244
switch (integration_with_collectors) {
4245
case NETDATA_EBPF_INTEGRATION_SOCKET: {
4246
socket_ipc =
4247
- nd_thread_create("ebpf_socket_ipc", NETDATA_THREAD_OPTION_DEFAULT, ebpf_socket_thread_ipc, NULL);
4247
+ nd_thread_create("ebpf_socket_ipc", NETDATA_THREAD_OPTION_JOINABLE, ebpf_socket_thread_ipc, NULL);
4248
break;
4249
}
4250
case NETDATA_EBPF_INTEGRATION_SHM:
@@ -4389,7 +4389,7 @@ int main(int argc, char **argv)
4389
cgroup_integration_thread.start_routine = ebpf_cgroup_integration;
4390
4391
cgroup_integration_thread.thread =
4392
- nd_thread_create(cgroup_integration_thread.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_cgroup_integration, NULL);
4392
+ nd_thread_create(cgroup_integration_thread.name, NETDATA_THREAD_OPTION_JOINABLE, ebpf_cgroup_integration, NULL);
4393
4394
ebpf_initialize_data_sharing();
4395
src/collectors/ebpf.plugin/ebpf_cachestat.c
+1
-1
@@ -1760,7 +1760,7 @@ void *ebpf_cachestat_thread(void *ptr)
1760
pthread_mutex_unlock(&lock);
1761
1762
ebpf_read_cachestat.thread =
1763
- nd_thread_create(ebpf_read_cachestat.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_cachestat_thread, em);
1763
+ nd_thread_create(ebpf_read_cachestat.name, NETDATA_THREAD_OPTION_JOINABLE, ebpf_read_cachestat_thread, em);
1764
1765
cachestat_collector(em);
1766
src/collectors/ebpf.plugin/ebpf_dcstat.c
+1
-1
@@ -1532,7 +1532,7 @@ void *ebpf_dcstat_thread(void *ptr)
1532
pthread_mutex_unlock(&lock);
1533
1534
ebpf_read_dcstat.thread =
1535
- nd_thread_create(ebpf_read_dcstat.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_dcstat_thread, em);
1535
+ nd_thread_create(ebpf_read_dcstat.name, NETDATA_THREAD_OPTION_JOINABLE, ebpf_read_dcstat_thread, em);
1536
1537
dcstat_collector(em);
1538
src/collectors/ebpf.plugin/ebpf_fd.c
+1
-1
@@ -1558,7 +1558,7 @@ void *ebpf_fd_thread(void *ptr)
1558
1559
pthread_mutex_unlock(&lock);
1560
1561
- ebpf_read_fd.thread = nd_thread_create(ebpf_read_fd.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_fd_thread, em);
1561
+ ebpf_read_fd.thread = nd_thread_create(ebpf_read_fd.name, NETDATA_THREAD_OPTION_JOINABLE, ebpf_read_fd_thread, em);
1562
1563
fd_collector(em);
1564
src/collectors/ebpf.plugin/ebpf_functions.c
+1
-1
@@ -31,7 +31,7 @@ static int ebpf_function_start_thread(ebpf_module_t *em, int period)
31
netdata_log_info("Starting thread %s with lifetime = %d", em->info.thread_name, period);
32
#endif
33
34
- st->thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, em);
34
+ st->thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_JOINABLE, st->start_routine, em);
35
return st->thread ? 0 : 1;
36
}
37
src/collectors/ebpf.plugin/ebpf_shm.c
+1
-1
@@ -1395,7 +1395,7 @@ void *ebpf_shm_thread(void *ptr)
1395
pthread_mutex_unlock(&lock);
1396
1397
ebpf_read_shm.thread =
1398
- nd_thread_create(ebpf_read_shm.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_shm_thread, em);
1398
+ nd_thread_create(ebpf_read_shm.name, NETDATA_THREAD_OPTION_JOINABLE, ebpf_read_shm_thread, em);
1399
1400
shm_collector(em);
1401
src/collectors/ebpf.plugin/ebpf_socket.c
+1
-1
@@ -3051,7 +3051,7 @@ void *ebpf_socket_thread(void *ptr)
3051
NETDATA_MAX_SOCKET_VECTOR);
3052
3053
ebpf_read_socket.thread =
3054
- nd_thread_create(ebpf_read_socket.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_socket_thread, em);
3054
+ nd_thread_create(ebpf_read_socket.name, NETDATA_THREAD_OPTION_JOINABLE, ebpf_read_socket_thread, em);
3055
3056
pthread_mutex_lock(&lock);
3057
ebpf_socket_create_global_charts(em);
src/collectors/ebpf.plugin/ebpf_swap.c
+1
-1
@@ -1221,7 +1221,7 @@ void *ebpf_swap_thread(void *ptr)
1221
pthread_mutex_unlock(&lock);
1222
1223
ebpf_read_swap.thread =
1224
- nd_thread_create(ebpf_read_swap.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_swap_thread, em);
1224
+ nd_thread_create(ebpf_read_swap.name, NETDATA_THREAD_OPTION_JOINABLE, ebpf_read_swap_thread, em);
1225
1226
swap_collector(em);
1227
src/collectors/ebpf.plugin/ebpf_vfs.c
+1
-1
@@ -2958,7 +2958,7 @@ void *ebpf_vfs_thread(void *ptr)
2958
pthread_mutex_unlock(&lock);
2959
2960
ebpf_read_vfs.thread =
2961
- nd_thread_create(ebpf_read_vfs.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_vfs_thread, em);
2961
+ nd_thread_create(ebpf_read_vfs.name, NETDATA_THREAD_OPTION_JOINABLE, ebpf_read_vfs_thread, em);
2962
2963
vfs_collector(em);
2964
src/collectors/freeipmi.plugin/freeipmi_plugin.c
+2
-2
@@ -1982,9 +1982,9 @@ int main (int argc, char **argv) {
1982
},
1983
};
1984
1985
- nd_thread_create("IPMI[sensors]", NETDATA_THREAD_OPTION_DONT_LOG, netdata_ipmi_collection_thread, &sensors_data);
1985
+ nd_thread_create("IPMI[sensors]", NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, netdata_ipmi_collection_thread, &sensors_data);
1986
if(netdata_do_sel)
1987
- nd_thread_create("IPMI[sel]", NETDATA_THREAD_OPTION_DONT_LOG, netdata_ipmi_collection_thread, &sel_data);
1987
+ nd_thread_create("IPMI[sel]", NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, netdata_ipmi_collection_thread, &sel_data);
1988
1989
// ------------------------------------------------------------------------
1990
// the main loop
src/collectors/statsd.plugin/statsd.c
+1
-1
@@ -2613,7 +2613,7 @@ void *statsd_main(void *ptr) {
2613
char tag[NETDATA_THREAD_TAG_MAX + 1];
2614
snprintfz(tag, NETDATA_THREAD_TAG_MAX, "STATSD_IN[%d]", i + 1);
2615
spinlock_init(&statsd.collection_threads_status[i].spinlock);
2616
- statsd.collection_threads_status[i].thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT,
2616
+ statsd.collection_threads_status[i].thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_JOINABLE,
2617
statsd_collector_thread, &statsd.collection_threads_status[i]);
2618
}
2619
src/collectors/systemd-journal.plugin/systemd-main.c
+1
-1
@@ -67,7 +67,7 @@ int main(int argc __maybe_unused, char **argv __maybe_unused)
67
// ------------------------------------------------------------------------
68
// watcher thread
69
70
- nd_thread_create("SDWATCH", NETDATA_THREAD_OPTION_DONT_LOG, nd_journal_watcher_main, NULL);
70
+ nd_thread_create("SDWATCH", NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, nd_journal_watcher_main, NULL);
71
72
// ------------------------------------------------------------------------
73
// the event loop for functions
src/daemon/daemon-shutdown.c
+7
-27
@@ -70,8 +70,7 @@ void cancel_main_threads(void) {
70
if (!static_threads)
71
return;
72
73
- int i, found = 0;
74
- usec_t max = 5 * USEC_PER_SEC, step = 100000;
73
+ int i;
74
for (i = 0; static_threads[i].name != NULL ; i++) {
75
if (static_threads[i].enabled == NETDATA_MAIN_THREAD_RUNNING) {
76
if (static_threads[i].thread) {
@@ -81,35 +80,15 @@ void cancel_main_threads(void) {
80
netdata_log_info("EXIT: No thread running (marking as EXITED): %s", static_threads[i].name);
81
static_threads[i].enabled = NETDATA_MAIN_THREAD_EXITED;
82
}
84
- found++;
83
}
84
}
85
88
- while(found && max > 0) {
89
- max -= step;
90
- netdata_log_info("Waiting %d threads to finish...", found);
91
- sleep_usec(step);
92
- found = 0;
93
- for (i = 0; static_threads[i].name != NULL ; i++) {
94
- if (static_threads[i].enabled == NETDATA_MAIN_THREAD_EXITED)
95
- continue;
96
-
97
- // Don't wait ourselves.
98
- if (nd_thread_is_me(static_threads[i].thread))
99
- continue;
100
-
101
- found++;
102
- }
103
- }
104
-
105
- if(found) {
106
- for (i = 0; static_threads[i].name != NULL ; i++) {
107
- if (static_threads[i].enabled != NETDATA_MAIN_THREAD_EXITED)
108
- netdata_log_error("Main thread %s takes too long to exit. Giving up...", static_threads[i].name);
109
- }
86
+ for (i = 0; static_threads[i].name != NULL ; i++) {
87
+ struct netdata_static_thread *st = &static_threads[i];
88
+ if(st->thread && !nd_thread_is_me(static_threads[i].thread))
89
+ nd_thread_join(st->thread);
90
}
111
- else
112
- netdata_log_info("All threads finished.");
91
+ netdata_log_info("All threads finished.");
92
93
freez(static_threads);
94
static_threads = NULL;
@@ -315,6 +294,7 @@ static void netdata_cleanup_and_exit(EXIT_REASON reason, bool abnormal, bool exi
294
if (!abnormal)
295
add_agent_event(EVENT_AGENT_SHUTDOWN_TIME, (int64_t)(now_monotonic_usec() - shutdown_start_time));
296
297
+ nd_thread_join_threads();
298
sqlite_close_databases();
299
watcher_step_complete(WATCHER_STEP_ID_CLOSE_SQL_DATABASES);
300
sqlite_library_shutdown();
src/daemon/main.c
+2
-2
@@ -1069,7 +1069,7 @@ int netdata_main(int argc, char **argv) {
1069
1070
if(st->enabled) {
1071
netdata_log_debug(D_SYSTEM, "Starting thread %s.", st->name);
1072
- st->thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, st);
1072
+ st->thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_JOINABLE, st->start_routine, st);
1073
}
1074
else
1075
netdata_log_debug(D_SYSTEM, "Not starting thread %s.", st->name);
@@ -1120,7 +1120,7 @@ int netdata_main(int argc, char **argv) {
1120
struct netdata_static_thread *st = &static_threads[i];
1121
st->enabled = 1;
1122
netdata_log_debug(D_SYSTEM, "Starting thread %s.", st->name);
1123
- st->thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, st);
1123
+ st->thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_JOINABLE, st->start_routine, st);
1124
}
1125
}
1126
}
src/exporting/exporting_engine.c
+1
@@ -222,6 +222,7 @@ void *exporting_main(void *ptr)
222
return NULL;
223
#endif
224
}
225
+ service_exits();
226
227
cleanup:
228
return NULL;
src/libnetdata/functions_evloop/functions_evloop.c
+2
-2
@@ -362,12 +362,12 @@ struct functions_evloop_globals *functions_evloop_init(size_t worker_threads, co
362
363
char tag_buffer[NETDATA_THREAD_TAG_MAX + 1];
364
snprintfz(tag_buffer, NETDATA_THREAD_TAG_MAX, "%s_READER", wg->tag);
365
- wg->reader_thread = nd_thread_create(tag_buffer, NETDATA_THREAD_OPTION_DONT_LOG,
365
+ wg->reader_thread = nd_thread_create(tag_buffer, NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE,
366
rrd_functions_worker_globals_reader_main, wg);
367
368
for(size_t i = 0; i < wg->workers ; i++) {
369
snprintfz(tag_buffer, NETDATA_THREAD_TAG_MAX, "%s_WORK[%zu]", wg->tag, i+1);
370
- wg->worker_threads[i] = nd_thread_create(tag_buffer, NETDATA_THREAD_OPTION_DONT_LOG,
370
+ wg->worker_threads[i] = nd_thread_create(tag_buffer, NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE,
371
rrd_functions_worker_globals_worker_main, wg);
372
}
373
src/libnetdata/threads/threads.c
+38
-26
@@ -261,28 +261,25 @@ void query_target_free(void);
261
void service_exits(void);
262
void rrd_collector_finished(void);
263
264
-static void nd_thread_join_exited_detached_threads(void) {
265
- while(1) {
264
+void nd_thread_join_threads()
265
+{
266
+ ND_THREAD *nti;
267
+ do {
268
spinlock_lock(&threads_globals.exited.spinlock);
269
268
- ND_THREAD *nti = threads_globals.exited.list;
269
- while (nti && nd_thread_status_check(nti, NETDATA_THREAD_OPTION_JOINABLE) == 0)
270
- nti = nti->next;
270
+ nti = threads_globals.exited.list;
271
272
- if(nti) {
272
+ if (nti) {
273
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(threads_globals.exited.list, nti, prev, next);
274
nti->list = ND_THREAD_LIST_NONE;
275
}
276
277
spinlock_unlock(&threads_globals.exited.spinlock);
278
279
- if(nti) {
280
- nd_log(NDLS_DAEMON, NDLP_INFO, "Joining detached thread '%s', tid %d", nti->tag, nti->tid);
281
- nd_thread_join(nti);
282
- }
283
- else
284
- break;
285
- }
279
+ // handles null
280
+ nd_thread_join(nti);
281
+
282
+ } while (nti);
283
}
284
285
static void nd_thread_exit(ND_THREAD *nti) {
@@ -342,12 +339,12 @@ static void nd_thread_exit(ND_THREAD *nti) {
339
}
340
spinlock_unlock(&threads_globals.running.spinlock);
341
345
- if (nd_thread_status_check(nti, NETDATA_THREAD_OPTION_JOINABLE) != NETDATA_THREAD_OPTION_JOINABLE) {
346
- spinlock_lock(&threads_globals.exited.spinlock);
347
- DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(threads_globals.exited.list, nti, prev, next);
348
- nti->list = ND_THREAD_LIST_EXITED;
349
- spinlock_unlock(&threads_globals.exited.spinlock);
350
- }
342
+ //if (nd_thread_status_check(nti, NETDATA_THREAD_OPTION_JOINABLE) != NETDATA_THREAD_OPTION_JOINABLE) {
343
+ spinlock_lock(&threads_globals.exited.spinlock);
344
+ DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(threads_globals.exited.list, nti, prev, next);
345
+ nti->list = ND_THREAD_LIST_EXITED;
346
+ spinlock_unlock(&threads_globals.exited.spinlock);
347
+ //}
348
}
349
350
static void *nd_thread_starting_point(void *ptr) {
@@ -388,16 +385,18 @@ bool nd_thread_is_me(ND_THREAD *nti) {
385
return nti && nti->thread == pthread_self();
386
}
387
391
-ND_THREAD *nd_thread_create(const char *tag, NETDATA_THREAD_OPTIONS options, void *(*start_routine)(void *), void *arg) {
392
- nd_thread_join_exited_detached_threads();
393
-
388
+ND_THREAD *nd_thread_create(const char *tag, NETDATA_THREAD_OPTIONS options, void *(*start_routine)(void *), void *arg)
389
+{
390
ND_THREAD *nti = callocz(1, sizeof(*nti));
391
spinlock_init(&nti->canceller.spinlock);
392
nti->arg = arg;
393
nti->start_routine = start_routine;
398
- nti->options = options & NETDATA_THREAD_OPTIONS_ALL;
394
+ nti->options = (options & NETDATA_THREAD_OPTIONS_ALL);
395
strncpyz(nti->tag, tag, ND_THREAD_TAG_MAX);
396
397
+ if ((options & NETDATA_THREAD_OPTION_JOINABLE) == 0)
398
+ nd_log_daemon(NDLP_INFO, "WARNING: Creating detached thread '%s'", tag);
399
+
400
int ret = pthread_create(&nti->thread, &threads_globals.attr, nd_thread_starting_point, nti);
401
if(ret != 0) {
402
nd_log(NDLS_DAEMON, NDLP_ERR,
@@ -447,7 +446,15 @@ int nd_thread_join(ND_THREAD *nti) {
446
if(!nti)
447
return ESRCH;
448
450
- int ret = pthread_join(nti->thread, NULL);
449
+ if(nd_thread_status_check(nti, NETDATA_THREAD_STATUS_JOINED)) {
450
+ freez(nti);
451
+ return 0;
452
+ }
453
+
454
+ int ret = 0;
455
+ bool joinable = nd_thread_status_check(nti, NETDATA_THREAD_OPTION_JOINABLE);
456
+ if (joinable)
457
+ ret = pthread_join(nti->thread, NULL);
458
if(ret != 0) {
459
// we can't join the thread
460
@@ -457,8 +464,10 @@ int nd_thread_join(ND_THREAD *nti) {
464
}
465
else {
466
// we successfully joined the thread
467
+ if (joinable)
468
+ nd_log(NDLS_DAEMON, NDLP_DEBUG, "Joining thread '%s', tid %d", nti->tag, nti->tid);
469
461
- nd_thread_status_set(nti, NETDATA_THREAD_STATUS_JOINED);
470
+ nd_thread_status_set(nti, NETDATA_THREAD_STATUS_JOINED);
471
472
spinlock_lock(&threads_globals.running.spinlock);
473
if(nti->list == ND_THREAD_LIST_RUNNING) {
@@ -474,7 +483,10 @@ int nd_thread_join(ND_THREAD *nti) {
483
}
484
spinlock_unlock(&threads_globals.exited.spinlock);
485
477
- freez(nti);
486
+ if (joinable)
487
+ freez(nti);
488
+ else
489
+ nti->thread = 0;
490
}
491
492
return ret;
src/libnetdata/threads/threads.h
+1
@@ -116,6 +116,7 @@ void nd_thread_rwspinlock_write_unlocked(void);
116
#endif
117
118
void nd_thread_can_run_sql(bool exclude);
119
+void nd_thread_join_threads();
120
bool nd_thread_runs_sql(void);
121
122
#endif //NETDATA_THREADS_H
src/plugins.d/plugins_d.c
+3
-1
@@ -119,6 +119,7 @@ static void pluginsd_worker_thread_handle_error(struct plugind *cd, int worker_r
119
120
#undef SERIAL_FAILURES_THRESHOLD
121
122
+
123
static void *pluginsd_worker_thread(void *arg) {
124
struct plugind *cd = (struct plugind *) arg;
125
@@ -399,7 +400,7 @@ void *pluginsd_main(void *ptr) {
400
snprintfz(tag, NETDATA_THREAD_TAG_MAX, "PD[%s]", pluginname);
401
402
// spawn a new thread for it
402
- cd->unsafe.thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT | NETDATA_THREAD_OPTION_JOINABLE,
403
+ cd->unsafe.thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_JOINABLE,
404
pluginsd_worker_thread, cd);
405
}
406
}
@@ -411,5 +412,6 @@ void *pluginsd_main(void *ptr) {
412
pluginsd_sleep(scan_frequency);
413
}
414
415
+ service_exits();
416
return NULL;
417
}
src/streaming/stream-connector.c
+1
-1
@@ -684,7 +684,7 @@ bool stream_connector_init(struct sender_state *s) {
684
snprintfz(tag, NETDATA_THREAD_TAG_MAX, THREAD_TAG_STREAM_SENDER "-CN" "[%d]",
685
sc->id);
686
687
- sc->thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT, stream_connector_thread, sc);
687
+ sc->thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_JOINABLE, stream_connector_thread, sc);
688
if (!sc->thread)
689
nd_log_daemon(NDLP_ERR,
690
"STREAM CONNECT '%s': failed to create new thread for client.",
src/streaming/stream-thread.c
+1
-1
@@ -728,7 +728,7 @@ static struct stream_thread * stream_thread_assign_and_start(RRDHOST *host) {
728
char tag[NETDATA_THREAD_TAG_MAX + 1];
729
snprintfz(tag, NETDATA_THREAD_TAG_MAX, THREAD_TAG_STREAM "[%zu]", sth->id);
730
731
- sth->thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT, stream_thread, sth);
731
+ sth->thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_JOINABLE, stream_thread, sth);
732
if (!sth->thread)
733
nd_log(NDLS_DAEMON, NDLP_ERR, "STREAM THREAD[%zu]: failed to create new thread for client.", sth->id);
734
}
src/web/server/static/static-threaded.c
+1
-1
@@ -384,7 +384,7 @@ void *socket_listen_main_static_threaded(void *ptr) {
384
char tag[50 + 1];
385
snprintfz(tag, sizeof(tag) - 1, "WEB[%d]", i+1);
386
387
- static_workers_private_data[i].thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT,
387
+ static_workers_private_data[i].thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_JOINABLE,
388
socket_listen_main_static_threaded_worker,
389
(void *)&static_workers_private_data[i]);
390
}