2
3
#include "common.h"
4
#include "buildinfo.h"
5
-#include "daemon/watcher.h"
5
+#include "daemon/daemon-shutdown-watcher.h"
6
#include "static_threads.h"
7
#include "web/api/queries/backfill.h"
8
29
time_t netdata_start_time = 0;
30
struct netdata_static_thread *static_threads;
31
32
-struct config netdata_config = APPCONFIG_INITIALIZER;
33
-
34
-typedef struct service_thread {
35
- pid_t tid;
36
- SERVICE_THREAD_TYPE type;
37
- SERVICE_TYPE services;
38
- char name[ND_THREAD_TAG_MAX + 1];
39
- bool stop_immediately;
40
- bool cancelled;
41
-
42
- union {
43
- ND_THREAD *netdata_thread;
44
- uv_thread_t uv_thread;
45
- };
46
-
47
- force_quit_t force_quit_callback;
48
- request_quit_t request_quit_callback;
49
- void *data;
50
-} SERVICE_THREAD;
51
-
52
-struct service_globals {
53
- SPINLOCK lock;
54
- Pvoid_t pid_judy;
55
-} service_globals = {
56
- .pid_judy = NULL,
57
-};
58
-
59
-SERVICE_THREAD *service_register(SERVICE_THREAD_TYPE thread_type, request_quit_t request_quit_callback, force_quit_t force_quit_callback, void *data, bool update __maybe_unused) {
60
- SERVICE_THREAD *sth = NULL;
61
- pid_t tid = gettid_cached();
62
-
63
- spinlock_lock(&service_globals.lock);
64
- Pvoid_t *PValue = JudyLIns(&service_globals.pid_judy, tid, PJE0);
65
- if(!*PValue) {
66
- sth = callocz(1, sizeof(SERVICE_THREAD));
67
- sth->tid = tid;
68
- sth->type = thread_type;
69
- sth->request_quit_callback = request_quit_callback;
70
- sth->force_quit_callback = force_quit_callback;
71
- sth->data = data;
72
- *PValue = sth;
73
-
74
- switch(thread_type) {
75
- default:
76
- case SERVICE_THREAD_TYPE_NETDATA:
77
- sth->netdata_thread = nd_thread_self();
78
- break;
79
-
80
- case SERVICE_THREAD_TYPE_EVENT_LOOP:
81
- case SERVICE_THREAD_TYPE_LIBUV:
82
- sth->uv_thread = uv_thread_self();
83
- break;
84
- }
85
-
86
- const char *name = nd_thread_tag();
87
- if(!name) name = "";
88
- strncpyz(sth->name, name, sizeof(sth->name) - 1);
89
- }
90
- else {
91
- sth = *PValue;
92
- }
93
- spinlock_unlock(&service_globals.lock);
94
-
95
- return sth;
96
-}
97
-
98
-void service_exits(void) {
99
- pid_t tid = gettid_cached();
100
-
101
- spinlock_lock(&service_globals.lock);
102
- Pvoid_t *PValue = JudyLGet(service_globals.pid_judy, tid, PJE0);
103
- if(PValue) {
104
- freez(*PValue);
105
- JudyLDel(&service_globals.pid_judy, tid, PJE0);
106
- }
107
- spinlock_unlock(&service_globals.lock);
108
-}
109
-
110
-bool service_running(SERVICE_TYPE service) {
111
- static __thread SERVICE_THREAD *sth = NULL;
112
-
113
- if(unlikely(!sth))
114
- sth = service_register(SERVICE_THREAD_TYPE_NETDATA, NULL, NULL, NULL, false);
115
-
116
- sth->services |= service;
117
-
118
- bool cancelled = false;
119
- if (sth->type == SERVICE_THREAD_TYPE_NETDATA)
120
- cancelled = nd_thread_signaled_to_cancel();
121
-
122
- return !sth->stop_immediately && !netdata_exit && !cancelled;
123
-}
124
-
125
-void service_signal_exit(SERVICE_TYPE service) {
126
- spinlock_lock(&service_globals.lock);
127
-
128
- Pvoid_t *PValue;
129
- Word_t tid = 0;
130
- bool first = true;
131
- while((PValue = JudyLFirstThenNext(service_globals.pid_judy, &tid, &first))) {
132
- SERVICE_THREAD *sth = *PValue;
133
-
134
- if((sth->services & service)) {
135
- sth->stop_immediately = true;
136
-
137
- switch(sth->type) {
138
- default:
139
- case SERVICE_THREAD_TYPE_NETDATA:
140
- nd_thread_signal_cancel(sth->netdata_thread);
141
- break;
142
-
143
- case SERVICE_THREAD_TYPE_EVENT_LOOP:
144
- case SERVICE_THREAD_TYPE_LIBUV:
145
- break;
146
- }
147
-
148
- if(sth->request_quit_callback) {
149
- spinlock_unlock(&service_globals.lock);
150
- sth->request_quit_callback(sth->data);
151
- spinlock_lock(&service_globals.lock);
152
- }
153
- }
154
- }
155
-
156
- spinlock_unlock(&service_globals.lock);
157
-}
158
-
159
-static void service_to_buffer(BUFFER *wb, SERVICE_TYPE service) {
160
- if(service & SERVICE_MAINTENANCE)
161
- buffer_strcat(wb, "MAINTENANCE ");
162
- if(service & SERVICE_COLLECTORS)
163
- buffer_strcat(wb, "COLLECTORS ");
164
- if(service & SERVICE_REPLICATION)
165
- buffer_strcat(wb, "REPLICATION ");
166
- if(service & ABILITY_DATA_QUERIES)
167
- buffer_strcat(wb, "DATA_QUERIES ");
168
- if(service & ABILITY_WEB_REQUESTS)
169
- buffer_strcat(wb, "WEB_REQUESTS ");
170
- if(service & SERVICE_WEB_SERVER)
171
- buffer_strcat(wb, "WEB_SERVER ");
172
- if(service & SERVICE_ACLK)
173
- buffer_strcat(wb, "ACLK ");
174
- if(service & SERVICE_HEALTH)
175
- buffer_strcat(wb, "HEALTH ");
176
- if(service & SERVICE_STREAMING)
177
- buffer_strcat(wb, "STREAMING ");
178
- if(service & ABILITY_STREAMING_CONNECTIONS)
179
- buffer_strcat(wb, "STREAMING_CONNECTIONS ");
180
- if(service & SERVICE_CONTEXT)
181
- buffer_strcat(wb, "CONTEXT ");
182
- if(service & SERVICE_ANALYTICS)
183
- buffer_strcat(wb, "ANALYTICS ");
184
- if(service & SERVICE_EXPORTERS)
185
- buffer_strcat(wb, "EXPORTERS ");
186
- if(service & SERVICE_HTTPD)
187
- buffer_strcat(wb, "HTTPD ");
188
-}
189
-
190
-static bool service_wait_exit(SERVICE_TYPE service, usec_t timeout_ut) {
191
- BUFFER *service_list = buffer_create(1024, NULL);
192
- BUFFER *thread_list = buffer_create(1024, NULL);
193
- usec_t started_ut = now_monotonic_usec(), ended_ut;
194
- size_t running;
195
- SERVICE_TYPE running_services = 0;
196
-
197
- // cancel the threads
198
- running = 0;
199
- running_services = 0;
200
- {
201
- buffer_flush(thread_list);
202
-
203
- spinlock_lock(&service_globals.lock);
204
-
205
- Pvoid_t *PValue;
206
- Word_t tid = 0;
207
- bool first = true;
208
- while((PValue = JudyLFirstThenNext(service_globals.pid_judy, &tid, &first))) {
209
- SERVICE_THREAD *sth = *PValue;
210
- if(sth->services & service && sth->tid != gettid_cached() && !sth->cancelled) {
211
- sth->cancelled = true;
212
-
213
- switch(sth->type) {
214
- default:
215
- case SERVICE_THREAD_TYPE_NETDATA:
216
- nd_thread_signal_cancel(sth->netdata_thread);
217
- break;
218
-
219
- case SERVICE_THREAD_TYPE_EVENT_LOOP:
220
- case SERVICE_THREAD_TYPE_LIBUV:
221
- break;
222
- }
223
-
224
- if(running)
225
- buffer_strcat(thread_list, ", ");
226
-
227
- buffer_sprintf(thread_list, "'%s' (%d)", sth->name, sth->tid);
228
-
229
- running++;
230
- running_services |= sth->services & service;
231
-
232
- if(sth->force_quit_callback) {
233
- spinlock_unlock(&service_globals.lock);
234
- sth->force_quit_callback(sth->data);
235
- spinlock_lock(&service_globals.lock);
236
- continue;
237
- }
238
- }
239
- }
240
-
241
- spinlock_unlock(&service_globals.lock);
242
- }
243
-
244
- service_signal_exit(service);
245
-
246
- // signal them to stop
247
- size_t last_running = 0;
248
- size_t stale_time_ut = 0;
249
- usec_t sleep_ut = 50 * USEC_PER_MS;
250
- size_t log_countdown_ut = sleep_ut;
251
- do {
252
- if(running != last_running)
253
- stale_time_ut = 0;
254
-
255
- last_running = running;
256
- running = 0;
257
- running_services = 0;
258
- buffer_flush(thread_list);
259
-
260
- spinlock_lock(&service_globals.lock);
261
-
262
- Pvoid_t *PValue;
263
- Word_t tid = 0;
264
- bool first = true;
265
- while((PValue = JudyLFirstThenNext(service_globals.pid_judy, &tid, &first))) {
266
- SERVICE_THREAD *sth = *PValue;
267
- if(sth->services & service && sth->tid != gettid_cached()) {
268
- if(running)
269
- buffer_strcat(thread_list, ", ");
270
-
271
- buffer_sprintf(thread_list, "'%s' (%d)", sth->name, sth->tid);
272
-
273
- running_services |= sth->services & service;
274
- running++;
275
- }
276
- }
277
-
278
- spinlock_unlock(&service_globals.lock);
279
-
280
- if(running) {
281
- log_countdown_ut -= (log_countdown_ut >= sleep_ut) ? sleep_ut : log_countdown_ut;
282
- if(log_countdown_ut == 0 || running != last_running) {
283
- log_countdown_ut = 20 * sleep_ut;
284
-
285
- buffer_flush(service_list);
286
- service_to_buffer(service_list, running_services);
287
- netdata_log_info("SERVICE CONTROL: waiting for the following %zu services [ %s] to exit: %s",
288
- running, buffer_tostring(service_list),
289
- running <= 10 ? buffer_tostring(thread_list) : "");
290
- }
291
-
292
- sleep_usec(sleep_ut);
293
- stale_time_ut += sleep_ut;
294
- }
295
-
296
- ended_ut = now_monotonic_usec();
297
- } while(running && (ended_ut - started_ut < timeout_ut || stale_time_ut < timeout_ut));
298
-
299
- if(running) {
300
- buffer_flush(service_list);
301
- service_to_buffer(service_list, running_services);
302
- netdata_log_info("SERVICE CONTROL: "
303
- "the following %zu service(s) [ %s] take too long to exit: %s; "
304
- "giving up on them...",
305
- running, buffer_tostring(service_list),
306
- buffer_tostring(thread_list));
307
- }
308
-
309
- buffer_free(thread_list);
310
- buffer_free(service_list);
311
-
312
- return (running == 0);
313
-}
314
-
315
-void web_client_cache_destroy(void);
316
-
317
-void netdata_cleanup_and_exit(int ret, const char *action, const char *action_result, const char *action_data) {
318
- netdata_exit = 1;
319
-
320
- usec_t shutdown_start_time = now_monotonic_usec();
321
- watcher_shutdown_begin();
322
-
323
- nd_log_limits_unlimited();
324
- netdata_log_info("NETDATA SHUTDOWN: initializing shutdown with code %d...", ret);
325
-
326
- // send the stat from our caller
327
- analytics_statistic_t statistic = { action, action_result, action_data };
328
- analytics_statistic_send(&statistic);
329
-
330
- // notify we are exiting
331
- statistic = (analytics_statistic_t) {"EXIT", ret?"ERROR":"OK","-"};
332
- analytics_statistic_send(&statistic);
333
-
334
- char agent_crash_file[FILENAME_MAX + 1];
335
- char agent_incomplete_shutdown_file[FILENAME_MAX + 1];
336
- snprintfz(agent_crash_file, FILENAME_MAX, "%s/.agent_crash", netdata_configured_varlib_dir);
337
- snprintfz(agent_incomplete_shutdown_file, FILENAME_MAX, "%s/.agent_incomplete_shutdown", netdata_configured_varlib_dir);
338
- (void) rename(agent_crash_file, agent_incomplete_shutdown_file);
339
- watcher_step_complete(WATCHER_STEP_ID_CREATE_SHUTDOWN_FILE);
340
-
341
- netdata_main_spawn_server_cleanup();
342
- watcher_step_complete(WATCHER_STEP_ID_DESTROY_MAIN_SPAWN_SERVER);
343
-
344
-#ifdef ENABLE_DBENGINE
345
- if(dbengine_enabled) {
346
- for (size_t tier = 0; tier < storage_tiers; tier++)
347
- rrdeng_exit_mode(multidb_ctx[tier]);
348
- }
349
-#endif
350
- watcher_step_complete(WATCHER_STEP_ID_DBENGINE_EXIT_MODE);
351
-
352
- webrtc_close_all_connections();
353
- watcher_step_complete(WATCHER_STEP_ID_CLOSE_WEBRTC_CONNECTIONS);
354
-
355
- service_signal_exit(SERVICE_MAINTENANCE | ABILITY_DATA_QUERIES | ABILITY_WEB_REQUESTS |
356
- ABILITY_STREAMING_CONNECTIONS | SERVICE_ACLK);
357
- watcher_step_complete(WATCHER_STEP_ID_DISABLE_MAINTENANCE_NEW_QUERIES_NEW_WEB_REQUESTS_NEW_STREAMING_CONNECTIONS_AND_ACLK);
358
-
359
- service_wait_exit(SERVICE_MAINTENANCE, 3 * USEC_PER_SEC);
360
- watcher_step_complete(WATCHER_STEP_ID_STOP_MAINTENANCE_THREAD);
361
-
362
- service_wait_exit(SERVICE_EXPORTERS | SERVICE_HEALTH | SERVICE_WEB_SERVER | SERVICE_HTTPD, 3 * USEC_PER_SEC);
363
- watcher_step_complete(WATCHER_STEP_ID_STOP_EXPORTERS_HEALTH_AND_WEB_SERVERS_THREADS);
364
-
365
- stream_threads_cancel();
366
- service_wait_exit(SERVICE_COLLECTORS | SERVICE_STREAMING, 3 * USEC_PER_SEC);
367
- watcher_step_complete(WATCHER_STEP_ID_STOP_COLLECTORS_AND_STREAMING_THREADS);
368
-
369
- service_wait_exit(SERVICE_REPLICATION, 3 * USEC_PER_SEC);
370
- watcher_step_complete(WATCHER_STEP_ID_STOP_REPLICATION_THREADS);
371
-
372
- metadata_sync_shutdown_prepare();
373
- watcher_step_complete(WATCHER_STEP_ID_PREPARE_METASYNC_SHUTDOWN);
374
-
375
- ml_stop_threads();
376
- ml_fini();
377
- watcher_step_complete(WATCHER_STEP_ID_DISABLE_ML_DETECTION_AND_TRAINING_THREADS);
378
-
379
- service_wait_exit(SERVICE_CONTEXT, 3 * USEC_PER_SEC);
380
- watcher_step_complete(WATCHER_STEP_ID_STOP_CONTEXT_THREAD);
381
-
382
- web_client_cache_destroy();
383
- watcher_step_complete(WATCHER_STEP_ID_CLEAR_WEB_CLIENT_CACHE);
384
-
385
- service_wait_exit(SERVICE_ACLK, 3 * USEC_PER_SEC);
386
- watcher_step_complete(WATCHER_STEP_ID_STOP_ACLK_THREADS);
387
-
388
- service_wait_exit(~0, 10 * USEC_PER_SEC);
389
- watcher_step_complete(WATCHER_STEP_ID_STOP_ALL_REMAINING_WORKER_THREADS);
390
-
391
- cancel_main_threads();
392
- watcher_step_complete(WATCHER_STEP_ID_CANCEL_MAIN_THREADS);
393
-
394
- if (ret)
395
- {
396
- watcher_step_complete(WATCHER_STEP_ID_FLUSH_DBENGINE_TIERS);
397
- watcher_step_complete(WATCHER_STEP_ID_STOP_COLLECTION_FOR_ALL_HOSTS);
398
- watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
399
- watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING);
400
- watcher_step_complete(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
401
- watcher_step_complete(WATCHER_STEP_ID_STOP_METASYNC_THREADS);
402
- }
403
- else
404
- {
405
- // exit cleanly
406
-
407
-#ifdef ENABLE_DBENGINE
408
- if(dbengine_enabled) {
409
- nd_log(NDLS_DAEMON, NDLP_INFO, "Preparing DBENGINE shutdown...");
410
- for (size_t tier = 0; tier < storage_tiers; tier++)
411
- rrdeng_prepare_exit(multidb_ctx[tier]);
412
-
413
- struct pgc_statistics pgc_main_stats = pgc_get_statistics(main_cache);
414
- nd_log(NDLS_DAEMON, NDLP_INFO, "Waiting for DBENGINE to commit unsaved data to disk (%zu pages, %zu bytes)...",
415
- pgc_main_stats.queues[PGC_QUEUE_HOT].entries + pgc_main_stats.queues[PGC_QUEUE_DIRTY].entries,
416
- pgc_main_stats.queues[PGC_QUEUE_HOT].size + pgc_main_stats.queues[PGC_QUEUE_DIRTY].size);
417
-
418
- bool finished_tiers[RRD_STORAGE_TIERS] = { 0 };
419
- size_t waiting_tiers, iterations = 0;
420
- do {
421
- waiting_tiers = 0;
422
- iterations++;
423
-
424
- for (size_t tier = 0; tier < storage_tiers; tier++) {
425
- if (!multidb_ctx[tier] || finished_tiers[tier])
426
- continue;
427
-
428
- waiting_tiers++;
429
- if (completion_timedwait_for(&multidb_ctx[tier]->quiesce.completion, 1)) {
430
- completion_destroy(&multidb_ctx[tier]->quiesce.completion);
431
- finished_tiers[tier] = true;
432
- waiting_tiers--;
433
- nd_log(NDLS_DAEMON, NDLP_INFO, "DBENGINE tier %zu finished!", tier);
434
- }
435
- else if(iterations % 10 == 0) {
436
- pgc_main_stats = pgc_get_statistics(main_cache);
437
- nd_log(NDLS_DAEMON, NDLP_INFO,
438
- "Still waiting for DBENGINE tier %zu to finish "
439
- "(cache still has %zu pages, %zu bytes hot, for all tiers)...",
440
- tier,
441
- pgc_main_stats.queues[PGC_QUEUE_HOT].entries + pgc_main_stats.queues[PGC_QUEUE_DIRTY].entries,
442
- pgc_main_stats.queues[PGC_QUEUE_HOT].size + pgc_main_stats.queues[PGC_QUEUE_DIRTY].size);
443
- }
444
- }
445
- } while(waiting_tiers);
446
- nd_log(NDLS_DAEMON, NDLP_INFO, "DBENGINE shutdown completed...");
447
- }
448
-#endif
449
- watcher_step_complete(WATCHER_STEP_ID_FLUSH_DBENGINE_TIERS);
450
-
451
- rrd_finalize_collection_for_all_hosts();
452
- watcher_step_complete(WATCHER_STEP_ID_STOP_COLLECTION_FOR_ALL_HOSTS);
453
-
454
-#ifdef ENABLE_DBENGINE
455
- if(dbengine_enabled) {
456
- size_t running = 1;
457
- size_t count = 10;
458
- while(running && count) {
459
- running = 0;
460
- for (size_t tier = 0; tier < storage_tiers; tier++)
461
- running += rrdeng_collectors_running(multidb_ctx[tier]);
462
-
463
- if (running) {
464
- nd_log_limit_static_thread_var(erl, 1, 100 * USEC_PER_MS);
465
- nd_log_limit(&erl, NDLS_DAEMON, NDLP_NOTICE, "waiting for %zu collectors to finish", running);
466
- }
467
- count--;
468
- }
469
- watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
470
-
471
- while (pgc_hot_and_dirty_entries(main_cache)) {
472
- pgc_flush_all_hot_and_dirty_pages(main_cache, PGC_SECTION_ALL);
473
- sleep_usec(100 * USEC_PER_MS);
474
- }
475
- watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING);
476
-
477
- for (size_t tier = 0; tier < storage_tiers; tier++)
478
- rrdeng_exit(multidb_ctx[tier]);
479
- rrdeng_enq_cmd(NULL, RRDENG_OPCODE_SHUTDOWN_EVLOOP, NULL, NULL, STORAGE_PRIORITY_BEST_EFFORT, NULL, NULL);
480
- watcher_step_complete(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
481
- }
482
- else {
483
- // Skip these steps
484
- watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
485
- watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING);
486
- watcher_step_complete(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
487
- }
488
-#else
489
- // Skip these steps
490
- watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_COLLECTORS_TO_FINISH);
491
- watcher_step_complete(WATCHER_STEP_ID_WAIT_FOR_DBENGINE_MAIN_CACHE_TO_FINISH_FLUSHING);
492
- watcher_step_complete(WATCHER_STEP_ID_STOP_DBENGINE_TIERS);
493
-#endif
494
-
495
- metadata_sync_shutdown();
496
- watcher_step_complete(WATCHER_STEP_ID_STOP_METASYNC_THREADS);
497
- }
498
-
499
- // Don't register a shutdown event if we crashed
500
- if (!ret)
501
- add_agent_event(EVENT_AGENT_SHUTDOWN_TIME, (int64_t)(now_monotonic_usec() - shutdown_start_time));
502
- sqlite_close_databases();
503
- watcher_step_complete(WATCHER_STEP_ID_CLOSE_SQL_DATABASES);
504
- sqlite_library_shutdown();
505
-
506
-
507
- // unlink the pid
508
- if(pidfile && *pidfile) {
509
- if(unlink(pidfile) != 0)
510
- netdata_log_error("EXIT: cannot unlink pidfile '%s'.", pidfile);
511
- }
512
- watcher_step_complete(WATCHER_STEP_ID_REMOVE_PID_FILE);
513
-
514
- netdata_ssl_cleanup();
515
- watcher_step_complete(WATCHER_STEP_ID_FREE_OPENSSL_STRUCTURES);
516
-
517
- (void) unlink(agent_incomplete_shutdown_file);
518
- watcher_step_complete(WATCHER_STEP_ID_REMOVE_INCOMPLETE_SHUTDOWN_FILE);
519
-
520
- watcher_shutdown_end();
521
- watcher_thread_stop();
522
- curl_global_cleanup();
523
-
524
-#ifdef OS_WINDOWS
525
- return;
526
-#endif
527
-
528
-#ifdef ENABLE_SENTRY
529
- nd_sentry_fini();
530
-#endif
531
-
532
- exit(ret);
533
-}
534
-
32
static void set_nofile_limit(struct rlimit *rl) {
33
// get the num files allowed
34
if(getrlimit(RLIMIT_NOFILE, rl) != 0) {
37
}
38
39
netdata_log_info("resources control: allowed file descriptors: soft = %zu, max = %zu",
543
- (size_t) rl->rlim_cur, (size_t) rl->rlim_max);
40
+ (size_t) rl->rlim_cur, (size_t) rl->rlim_max);
41
42
// make the soft/hard limits equal
43
rl->rlim_cur = rl->rlim_max;
55
netdata_log_error("Number of open file descriptors allowed for this process is too low (RLIMIT_NOFILE=%zu)", (size_t)rl->rlim_cur);
56
}
57
561
-void cancel_main_threads() {
562
- nd_log_limits_unlimited();
563
-
564
- if (!static_threads)
565
- return;
566
-
567
- int i, found = 0;
568
- usec_t max = 5 * USEC_PER_SEC, step = 100000;
569
- for (i = 0; static_threads[i].name != NULL ; i++) {
570
- if (static_threads[i].enabled == NETDATA_MAIN_THREAD_RUNNING) {
571
- if (static_threads[i].thread) {
572
- netdata_log_info("EXIT: Stopping main thread: %s", static_threads[i].name);
573
- nd_thread_signal_cancel(static_threads[i].thread);
574
- } else {
575
- netdata_log_info("EXIT: No thread running (marking as EXITED): %s", static_threads[i].name);
576
- static_threads[i].enabled = NETDATA_MAIN_THREAD_EXITED;
577
- }
578
- found++;
579
- }
580
- }
581
-
582
- while(found && max > 0) {
583
- max -= step;
584
- netdata_log_info("Waiting %d threads to finish...", found);
585
- sleep_usec(step);
586
- found = 0;
587
- for (i = 0; static_threads[i].name != NULL ; i++) {
588
- if (static_threads[i].enabled == NETDATA_MAIN_THREAD_EXITED)
589
- continue;
590
-
591
- // Don't wait ourselves.
592
- if (nd_thread_is_me(static_threads[i].thread))
593
- continue;
594
-
595
- found++;
596
- }
597
- }
598
-
599
- if(found) {
600
- for (i = 0; static_threads[i].name != NULL ; i++) {
601
- if (static_threads[i].enabled != NETDATA_MAIN_THREAD_EXITED)
602
- netdata_log_error("Main thread %s takes too long to exit. Giving up...", static_threads[i].name);
603
- }
604
- }
605
- else
606
- netdata_log_info("All threads finished.");
607
-
608
- freez(static_threads);
609
- static_threads = NULL;
610
-}
611
-
58
static const struct option_def {
59
const char val;
60
const char *description;