do not recurse cleanup on shutdown (#19894)
* do not recurse cleanup on shutdown * make exit_initiated sigatomic_t and hide it behind a function * fix freebsd and windows
Costa Tsaousis committed
Mar 17, 2025 at 23:48 UTC
87dbbaadb625edb5d05c3605f7dd02fc0a18237c
20 files changed
+54
-44
src/collectors/cups.plugin/cups_plugin.c
+3
-3
@@ -243,7 +243,7 @@ int main(int argc, char **argv) {
243
for (iteration = 0; 1; iteration++) {
244
heartbeat_next(&hb);
245
246
- if (unlikely(exit_initiated))
246
+ if (unlikely(exit_initiated_get()))
247
break;
248
249
reset_metrics();
@@ -315,7 +315,7 @@ int main(int argc, char **argv) {
315
}
316
cupsFreeDests(num_dest_total, dests);
317
318
- if (unlikely(exit_initiated))
318
+ if (unlikely(exit_initiated_get()))
319
break;
320
321
cups_job_t *jobs, *curr_job;
@@ -410,7 +410,7 @@ int main(int argc, char **argv) {
410
411
fflush(stdout);
412
413
- if (unlikely(exit_initiated))
413
+ if (unlikely(exit_initiated_get()))
414
break;
415
416
// restart check (14400 seconds)
src/collectors/freebsd.plugin/plugin_freebsd.c
+1
-1
@@ -91,7 +91,7 @@ void *freebsd_main(void *ptr)
91
92
// initialize FreeBSD plugin
93
if (freebsd_plugin_init())
94
- netdata_cleanup_and_exit(EXIT_REASON_FATAL, NULL, NULL, NULL);
94
+ netdata_cleanup_and_exit_fatal(EXIT_REASON_FATAL);
95
96
// check the enabled status for each module
97
int i;
src/collectors/nfacct.plugin/plugin_nfacct.c
+1
-1
@@ -837,7 +837,7 @@ int main(int argc, char **argv) {
837
for(iteration = 0; 1; iteration++) {
838
usec_t dt = heartbeat_next(&hb);
839
840
- if(unlikely(exit_initiated)) break;
840
+ if(unlikely(exit_initiated_get())) break;
841
842
if(debug && iteration)
843
fprintf(stderr, "nfacct.plugin: iteration %zu, dt %"PRIu64" usec\n"
src/collectors/perf.plugin/perf_plugin.c
+1
-1
@@ -1325,7 +1325,7 @@ int main(int argc, char **argv) {
1325
for(iteration = 0; 1; iteration++) {
1326
usec_t dt = heartbeat_next(&hb);
1327
1328
- if (unlikely(exit_initiated))
1328
+ if (unlikely(exit_initiated_get()))
1329
break;
1330
1331
if (unlikely(debug && iteration))
src/collectors/xenstat.plugin/xenstat_plugin.c
+1
-1
@@ -1026,7 +1026,7 @@ int main(int argc, char **argv) {
1026
for(iteration = 0; 1; iteration++) {
1027
usec_t dt = heartbeat_next(&hb);
1028
1029
- if(unlikely(exit_initiated)) break;
1029
+ if(unlikely(exit_initiated_get())) break;
1030
1031
if(unlikely(debug && iteration))
1032
fprintf(stderr, "xenstat.plugin: iteration %zu, dt %lu usec\n", iteration, dt);
src/daemon/commands.c
+1
-1
@@ -164,7 +164,7 @@ static cmd_status_t cmd_exit_execute(char *args, char **message)
164
165
nd_log_limits_unlimited();
166
netdata_log_info("COMMAND: Cleaning up to exit.");
167
- netdata_cleanup_and_exit(EXIT_REASON_CMD_EXIT, NULL, NULL, NULL);
167
+ netdata_cleanup_and_exit_gracefully(EXIT_REASON_CMD_EXIT);
168
exit(0);
169
170
return CMD_STATUS_SUCCESS;
src/daemon/daemon-service.c
+1
-1
@@ -90,7 +90,7 @@ bool service_running(SERVICE_TYPE service) {
90
if (sth->type == SERVICE_THREAD_TYPE_NETDATA)
91
cancelled = nd_thread_signaled_to_cancel();
92
93
- return !sth->stop_immediately && !exit_initiated && !cancelled;
93
+ return !sth->stop_immediately && !exit_initiated_get() && !cancelled;
94
}
95
96
void service_signal_exit(SERVICE_TYPE service) {
src/daemon/daemon-shutdown.c
+20
-12
@@ -38,7 +38,7 @@ extern struct netdata_static_thread *static_threads;
38
39
void netdata_log_exit_reason(void) {
40
CLEAN_BUFFER *wb = buffer_create(0, NULL);
41
- EXIT_REASON_2buffer(wb, exit_initiated, ", ");
41
+ EXIT_REASON_2buffer(wb, exit_initiated_get(), ", ");
42
43
ND_LOG_STACK lgs[] = {
44
ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &netdata_exit_msgid),
@@ -46,7 +46,7 @@ void netdata_log_exit_reason(void) {
46
};
47
ND_LOG_STACK_PUSH(lgs);
48
49
- nd_log(NDLS_DAEMON, is_exit_reason_normal(exit_initiated) ? NDLP_NOTICE : NDLP_CRIT,
49
+ nd_log(NDLS_DAEMON, is_exit_reason_normal(exit_initiated_get()) ? NDLP_NOTICE : NDLP_CRIT,
50
"NETDATA SHUTDOWN: initializing shutdown with code due to: %s",
51
buffer_tostring(wb));
52
}
@@ -109,8 +109,7 @@ static void *rrdeng_exit_background(void *ptr) {
109
}
110
111
#ifdef ENABLE_DBENGINE
112
-static void rrdeng_flush_everything_and_wait(bool wait_flush, bool wait_collectors, bool dirty_only)
113
-{
112
+static void rrdeng_flush_everything_and_wait(bool wait_flush, bool wait_collectors, bool dirty_only) {
113
static size_t starting_size_to_flush = 0;
114
115
if(!pgc_hot_and_dirty_entries(main_cache))
@@ -172,9 +171,12 @@ static void rrdeng_flush_everything_and_wait(bool wait_flush, bool wait_collecto
171
}
172
#endif
173
175
-void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char *action_result, const char *action_data) {
174
+#if !defined(OS_WINDOWS)
175
+NORETURN
176
+#endif
177
+static void netdata_cleanup_and_exit(EXIT_REASON reason) {
178
exit_initiated_set(reason);
177
- int ret = is_exit_reason_normal(exit_initiated) ? 0 : 1;
179
+ int ret = is_exit_reason_normal(exit_initiated_get()) ? 0 : 1;
180
181
// don't recurse (due to a fatal, while exiting)
182
static bool run = false;
@@ -198,13 +200,9 @@ void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char
200
rrdeng_flush_everything_and_wait(false, false, true);
201
#endif
202
201
- // send the stat from our caller
202
- analytics_statistic_t statistic = { action, action_result, action_data };
203
- analytics_statistic_send(&statistic);
204
-
203
// notify we are exiting
206
- statistic = (analytics_statistic_t) {"EXIT", ret?"ERROR":"OK","-"};
207
- analytics_statistic_send(&statistic);
204
+ //analytics_statistic_t statistic = (analytics_statistic_t) {"EXIT", ret?"ERROR":"OK","-"};
205
+ //analytics_statistic_send(&statistic);
206
207
netdata_main_spawn_server_cleanup();
208
watcher_step_complete(WATCHER_STEP_ID_DESTROY_MAIN_SPAWN_SERVER);
@@ -393,3 +391,13 @@ void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char
391
}
392
#endif
393
}
394
+
395
+void netdata_cleanup_and_exit_gracefully(EXIT_REASON reason) {
396
+ exit_initiated_add(reason);
397
+ FUNCTION_RUN_ONCE();
398
+ netdata_cleanup_and_exit(reason);
399
+}
400
+
401
+void netdata_cleanup_and_exit_fatal(EXIT_REASON reason) {
402
+ netdata_cleanup_and_exit(reason);
403
+}
src/daemon/daemon-shutdown.h
+2
-5
@@ -10,10 +10,7 @@ void cancel_main_threads(void);
10
void abort_on_fatal_disable(void);
11
void abort_on_fatal_enable(void);
12
13
-#ifdef OS_WINDOWS
14
-void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char *action_result, const char *action_data);
15
-#else
16
-void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char *action_result, const char *action_data) NORETURN;
17
-#endif
13
+void netdata_cleanup_and_exit_gracefully(EXIT_REASON reason);
14
+void netdata_cleanup_and_exit_fatal(EXIT_REASON reason);
15
16
#endif //NETDATA_DAEMON_SHUTDOWN_H
src/daemon/daemon-status-file.c
+1
-1
@@ -614,7 +614,7 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
614
615
get_daemon_status_fields_from_system_info(&session_status);
616
617
- session_status.exit_reason = exit_initiated;
617
+ session_status.exit_reason = exit_initiated_get();
618
session_status.profile = nd_profile_detect_and_configure(false);
619
620
if(status != DAEMON_STATUS_NONE)
src/daemon/daemon-systemd-watcher.c
+1
-1
@@ -28,7 +28,7 @@ static int shutdown_event_handler(sd_bus_message *m, void *userdata __maybe_unus
28
shutdown ? "true" : "false");
29
30
if(shutdown)
31
- netdata_cleanup_and_exit(EXIT_REASON_SYSTEM_SHUTDOWN, NULL, NULL, NULL);
31
+ netdata_cleanup_and_exit_gracefully(EXIT_REASON_SYSTEM_SHUTDOWN);
32
33
return 0;
34
}
src/daemon/main.c
+1
-1
@@ -240,7 +240,7 @@ int unittest_prepare_rrd(const char **user) {
240
}
241
242
static void fatal_cleanup_and_exit_cb(void) {
243
- netdata_cleanup_and_exit(EXIT_REASON_FATAL, "fatal error", "exiting", NULL);
243
+ netdata_cleanup_and_exit_fatal(EXIT_REASON_FATAL);
244
exit(1);
245
}
246
src/daemon/signal-handler.c
+1
-1
@@ -237,7 +237,7 @@ static void process_triggered_signals(void) {
237
nd_log_limits_unlimited();
238
netdata_log_info("SIGNAL: Received %s. Cleaning up to exit...", name);
239
commands_exit();
240
- netdata_cleanup_and_exit(signals_waiting[i].reason, NULL, NULL, NULL);
240
+ netdata_cleanup_and_exit_gracefully(signals_waiting[i].reason);
241
exit(0);
242
break;
243
src/daemon/winsvc.cc
+1
-1
@@ -109,7 +109,7 @@ static void *call_netdata_cleanup(void *arg)
109
reason = EXIT_REASON_SERVICE_STOP;
110
break;
111
}
112
- netdata_cleanup_and_exit(reason, NULL, NULL, NULL);
112
+ netdata_cleanup_and_exit_gracefully(reason);
113
114
// Close event handle
115
netdata_service_log("Closing stop event handle...");
src/database/engine/cache.c
+1
-1
@@ -2312,7 +2312,7 @@ bool pgc_flush_pages(PGC *cache) {
2312
}
2313
2314
void pgc_page_hot_set_end_time_s(PGC *cache __maybe_unused, PGC_PAGE *page, time_t end_time_s, size_t additional_bytes) {
2315
- internal_fatal(!is_page_hot(page) && !exit_initiated,
2315
+ internal_fatal(!is_page_hot(page) && !exit_initiated_get(),
2316
"DBENGINE CACHE: end_time_s update on non-hot page");
2317
2318
internal_fatal(end_time_s < __atomic_load_n(&page->end_time_s, __ATOMIC_RELAXED),
src/database/engine/page.c
+2
-2
@@ -897,14 +897,14 @@ size_t pgd_append_point(
897
uint32_t expected_slot)
898
{
899
if (pg->states & PGD_STATE_SCHEDULED_FOR_FLUSHING) {
900
- if(exit_initiated == EXIT_REASON_NONE)
900
+ if(exit_initiated_get() == EXIT_REASON_NONE)
901
pgd_fatal(pg, "Data collection on page already scheduled for flushing");
902
else
903
return 0;
904
}
905
906
if (!(pg->states & PGD_STATE_CREATED_FROM_COLLECTOR)) {
907
- if(exit_initiated == EXIT_REASON_NONE)
907
+ if(exit_initiated_get() == EXIT_REASON_NONE)
908
pgd_fatal(pg, "DBENGINE: collection on page not created from a collector");
909
else
910
return 0;
src/health/rrdcalc.c
+1
-1
@@ -250,7 +250,7 @@ static void rrdcalc_link_to_rrdset(RRDCALC *rc) {
250
static void rrdcalc_unlink_from_rrdset(RRDCALC *rc, bool having_ll_wrlock) {
251
RRDSET *st = rc->rrdset;
252
253
- if (!exit_initiated) {
253
+ if (!exit_initiated_get()) {
254
RRDHOST *host = st->rrdhost;
255
256
time_t now = now_realtime_sec();
src/libnetdata/exit/exit_initiated.c
+11
-5
@@ -2,7 +2,7 @@
2
3
#include "../libnetdata.h"
4
5
-volatile EXIT_REASON exit_initiated = EXIT_REASON_NONE;
5
+static volatile sig_atomic_t exit_initiated = EXIT_REASON_NONE;
6
7
ENUM_STR_MAP_DEFINE(EXIT_REASON) = {
8
{ EXIT_REASON_SIGBUS, "signal-bus-error"},
@@ -108,15 +108,22 @@ void exit_initiated_init(void) {
108
self = os_get_file_metadata(self_path);
109
}
110
111
+ALWAYS_INLINE
112
+EXIT_REASON exit_initiated_get(void) {
113
+ return (EXIT_REASON)exit_initiated;
114
+}
115
+
116
void exit_initiated_add(EXIT_REASON reason) {
112
- exit_initiated |= reason;
117
+ exit_initiated |= (sig_atomic_t)reason;
118
}
119
120
void exit_initiated_set(EXIT_REASON reason) {
116
- if(exit_initiated == EXIT_REASON_NONE && !(reason & EXIT_REASON_SYSTEM_SHUTDOWN) && is_system_shutdown())
121
+ EXIT_REASON old = exit_initiated_get();
122
+
123
+ if(old == EXIT_REASON_NONE && !(reason & EXIT_REASON_SYSTEM_SHUTDOWN) && is_system_shutdown())
124
reason |= EXIT_REASON_SYSTEM_SHUTDOWN;
125
119
- if(exit_initiated == EXIT_REASON_NONE && self_path && OS_FILE_METADATA_OK(self)) {
126
+ if(old == EXIT_REASON_NONE && self_path && OS_FILE_METADATA_OK(self)) {
127
OS_FILE_METADATA self_now = os_get_file_metadata(self_path);
128
if(OS_FILE_METADATA_OK(self_now) && (self_now.modified_time != self.modified_time || self_now.size_bytes != self.size_bytes))
129
reason |= EXIT_REASON_UPDATE;
@@ -127,4 +134,3 @@ void exit_initiated_set(EXIT_REASON reason) {
134
// we will have all of them
135
exit_initiated_add(reason);
136
}
130
-
src/libnetdata/exit/exit_initiated.h
+1
-2
@@ -82,9 +82,8 @@ typedef enum {
82
typedef struct web_buffer BUFFER;
83
BITMAP_STR_DEFINE_FUNCTIONS_EXTERN(EXIT_REASON);
84
85
-extern volatile EXIT_REASON exit_initiated;
86
-
85
void exit_initiated_init(void);
86
+EXIT_REASON exit_initiated_get(void);
87
void exit_initiated_set(EXIT_REASON reason);
88
void exit_initiated_add(EXIT_REASON reason);
89
src/web/server/web_client.c
+2
-2
@@ -1185,13 +1185,13 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1185
w->response.data->content_type = CT_TEXT_PLAIN;
1186
buffer_flush(w->response.data);
1187
1188
- if(!exit_initiated)
1188
+ if(!exit_initiated_get())
1189
buffer_strcat(w->response.data, "ok, will do...");
1190
else
1191
buffer_strcat(w->response.data, "I am doing it already");
1192
1193
netdata_log_error("web request to exit received.");
1194
- netdata_cleanup_and_exit(EXIT_REASON_API_QUIT, NULL, NULL, NULL);
1194
+ netdata_cleanup_and_exit_gracefully(EXIT_REASON_API_QUIT);
1195
return HTTP_RESP_OK;
1196
}
1197
else if(unlikely(hash == hash_debug && strcmp(tok, "debug") == 0)) {