master
c 232 lines 7.18 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "daemon-service.h"
4
5 typedef struct service_thread {
6 pid_t tid;
7 SERVICE_TYPE services;
8 char name[ND_THREAD_TAG_MAX + 1];
9 bool cancelled;
10
11 ND_THREAD *netdata_thread;
12
13 force_quit_t force_quit_callback;
14 request_quit_t request_quit_callback;
15 void *data;
16 } SERVICE_THREAD;
17
18 struct service_globals {
19 SPINLOCK lock;
20 Pvoid_t pid_judy;
21 } service_globals = {
22 .pid_judy = NULL,
23 };
24
25 SERVICE_THREAD *service_register(request_quit_t request_quit_callback, force_quit_t force_quit_callback, void *data)
26 {
27 SERVICE_THREAD *sth = NULL;
28 pid_t tid = gettid_cached();
29
30 spinlock_lock(&service_globals.lock);
31 Pvoid_t *PValue = JudyLIns(&service_globals.pid_judy, tid, PJE0);
32 if(!*PValue) {
33 sth = callocz(1, sizeof(SERVICE_THREAD));
34 sth->tid = tid;
35 sth->request_quit_callback = request_quit_callback;
36 sth->force_quit_callback = force_quit_callback;
37 sth->data = data;
38 *PValue = sth;
39 sth->netdata_thread = nd_thread_self();
40
41 const char *name = nd_thread_tag();
42 if(!name) name = "";
43 strncpyz(sth->name, name, sizeof(sth->name) - 1);
44 }
45 else {
46 sth = *PValue;
47 }
48 spinlock_unlock(&service_globals.lock);
49
50 return sth;
51 }
52
53 void service_exits(void) {
54 pid_t tid = gettid_cached();
55
56 spinlock_lock(&service_globals.lock);
57 Pvoid_t *PValue = JudyLGet(service_globals.pid_judy, tid, PJE0);
58 if(PValue) {
59 freez(*PValue);
60 JudyLDel(&service_globals.pid_judy, tid, PJE0);
61 }
62 spinlock_unlock(&service_globals.lock);
63 }
64
65 bool service_running(SERVICE_TYPE service) {
66 static __thread SERVICE_THREAD *sth = NULL;
67
68 if(unlikely(!sth))
69 sth = service_register(NULL, NULL, NULL);
70
71 sth->services |= service;
72
73 return !nd_thread_signaled_to_cancel() && !exit_initiated_get();
74 }
75
76 void service_signal_exit(SERVICE_TYPE service) {
77 spinlock_lock(&service_globals.lock);
78
79 Pvoid_t *PValue;
80 Word_t tid = 0;
81 bool first = true;
82 while((PValue = JudyLFirstThenNext(service_globals.pid_judy, &tid, &first))) {
83 SERVICE_THREAD *sth = *PValue;
84
85 if((sth->services & service)) {
86 nd_thread_signal_cancel(sth->netdata_thread);
87 nd_log_daemon(NDLP_DEBUG, "SERVICE: Signal to stop : %s", sth->name);
88 if(sth->request_quit_callback) {
89 spinlock_unlock(&service_globals.lock);
90 sth->request_quit_callback(sth->data);
91 spinlock_lock(&service_globals.lock);
92 }
93 }
94 }
95 spinlock_unlock(&service_globals.lock);
96 }
97
98 static void service_to_buffer(BUFFER *wb, SERVICE_TYPE service) {
99 if(service & SERVICE_COLLECTORS)
100 buffer_strcat(wb, "COLLECTORS ");
101 if(service & SERVICE_REPLICATION)
102 buffer_strcat(wb, "REPLICATION ");
103 if(service & ABILITY_WEB_REQUESTS)
104 buffer_strcat(wb, "WEB_REQUESTS ");
105 if(service & SERVICE_WEB_SERVER)
106 buffer_strcat(wb, "WEB_SERVER ");
107 if(service & SERVICE_ACLK)
108 buffer_strcat(wb, "ACLK ");
109 if(service & SERVICE_HEALTH)
110 buffer_strcat(wb, "HEALTH ");
111 if(service & SERVICE_STREAMING)
112 buffer_strcat(wb, "STREAMING ");
113 if(service & ABILITY_STREAMING_CONNECTIONS)
114 buffer_strcat(wb, "STREAMING_CONNECTIONS ");
115 if(service & SERVICE_CONTEXT)
116 buffer_strcat(wb, "CONTEXT ");
117 if(service & SERVICE_ANALYTICS)
118 buffer_strcat(wb, "ANALYTICS ");
119 if(service & SERVICE_EXPORTERS)
120 buffer_strcat(wb, "EXPORTERS ");
121 if(service & SERVICE_HTTPD)
122 buffer_strcat(wb, "HTTPD ");
123 }
124
125 bool service_wait_exit(SERVICE_TYPE service, usec_t timeout_ut) {
126 BUFFER *service_list = buffer_create(1024, NULL);
127 BUFFER *thread_list = buffer_create(1024, NULL);
128 usec_t started_ut = now_monotonic_usec(), ended_ut;
129 size_t running;
130 SERVICE_TYPE running_services = 0;
131
132 // cancel the threads
133 running = 0;
134 running_services = 0;
135 {
136 buffer_flush(thread_list);
137
138 spinlock_lock(&service_globals.lock);
139
140 Pvoid_t *PValue;
141 Word_t tid = 0;
142 bool first = true;
143 while((PValue = JudyLFirstThenNext(service_globals.pid_judy, &tid, &first))) {
144 SERVICE_THREAD *sth = *PValue;
145 if(sth->services & service && sth->tid != gettid_cached() && !sth->cancelled) {
146 sth->cancelled = true;
147 nd_thread_signal_cancel(sth->netdata_thread);
148 if(running)
149 buffer_strcat(thread_list, ", ");
150
151 buffer_sprintf(thread_list, "'%s' (%d)", sth->name, sth->tid);
152
153 running++;
154 running_services |= sth->services & service;
155
156 if(sth->force_quit_callback) {
157 spinlock_unlock(&service_globals.lock);
158 sth->force_quit_callback(sth->data);
159 spinlock_lock(&service_globals.lock);
160 continue;
161 }
162 }
163 }
164
165 spinlock_unlock(&service_globals.lock);
166 }
167
168 service_signal_exit(service);
169
170 // signal them to stop
171 size_t last_running = 0;
172 usec_t sleep_ut = 50 * USEC_PER_MS;
173 size_t log_countdown_ut = sleep_ut;
174 do {
175 last_running = running;
176 running = 0;
177 running_services = 0;
178 buffer_flush(thread_list);
179
180 spinlock_lock(&service_globals.lock);
181
182 Pvoid_t *PValue;
183 Word_t tid = 0;
184 bool first = true;
185 while((PValue = JudyLFirstThenNext(service_globals.pid_judy, &tid, &first))) {
186 SERVICE_THREAD *sth = *PValue;
187 if(sth->services & service && sth->tid != gettid_cached()) {
188 if(running)
189 buffer_strcat(thread_list, ", ");
190
191 buffer_sprintf(thread_list, "'%s' (%d)", sth->name, sth->tid);
192
193 running_services |= sth->services & service;
194 running++;
195 }
196 }
197
198 spinlock_unlock(&service_globals.lock);
199
200 if(running) {
201 log_countdown_ut -= (log_countdown_ut >= sleep_ut) ? sleep_ut : log_countdown_ut;
202 if(log_countdown_ut == 0 || running != last_running) {
203 log_countdown_ut = 20 * sleep_ut;
204
205 buffer_flush(service_list);
206 service_to_buffer(service_list, running_services);
207 netdata_log_info("SERVICE CONTROL: waiting for the following %zu services [ %s] to exit: %s",
208 running, buffer_tostring(service_list),
209 running <= 10 ? buffer_tostring(thread_list) : "");
210 }
211
212 sleep_usec(sleep_ut);
213 }
214
215 ended_ut = now_monotonic_usec();
216 } while(running && ((ended_ut - started_ut) < timeout_ut));
217
218 if(running) {
219 buffer_flush(service_list);
220 service_to_buffer(service_list, running_services);
221 netdata_log_info("SERVICE CONTROL: "
222 "the following %zu service(s) [ %s] take too long to exit: %s; "
223 "giving up on them...",
224 running, buffer_tostring(service_list),
225 buffer_tostring(thread_list));
226 }
227
228 buffer_free(thread_list);
229 buffer_free(service_list);
230
231 return (running == 0);
232 }