2
3
#include "../libnetdata.h"
4
5
-static pthread_attr_t *netdata_threads_attr = NULL;
5
+#define nd_thread_status_get(nti) __atomic_load_n(&((nti)->options), __ATOMIC_ACQUIRE)
6
+#define nd_thread_status_check(nti, flag) (__atomic_load_n(&((nti)->options), __ATOMIC_ACQUIRE) & (flag))
7
+#define nd_thread_status_set(nti, flag) __atomic_or_fetch(&((nti)->options), flag, __ATOMIC_RELEASE)
8
+#define nd_thread_status_clear(nti, flag) __atomic_and_fetch(&((nti)->options), ~(flag), __ATOMIC_RELEASE)
9
7
-// ----------------------------------------------------------------------------
8
-// per thread data
10
+typedef void (*nd_thread_canceller)(void *data);
11
10
-typedef struct {
12
+struct nd_thread {
13
void *arg;
12
- char tag[NETDATA_THREAD_NAME_MAX + 1];
14
+ pid_t tid;
15
+ char tag[ND_THREAD_TAG_MAX + 1];
16
+ void *ret; // the return value of start routine
17
void *(*start_routine) (void *);
18
NETDATA_THREAD_OPTIONS options;
15
-} NETDATA_THREAD;
19
+ pthread_t thread;
20
+ bool cancel_atomic;
21
17
-static __thread NETDATA_THREAD *netdata_thread = NULL;
22
+#ifdef NETDATA_INTERNAL_CHECKS
23
+ // keep track of the locks currently held
24
+ // used to detect locks that are left locked during exit
25
+ int rwlocks_read_locks;
26
+ int rwlocks_write_locks;
27
+ int mutex_locks;
28
+ int spinlock_locks;
29
+ int rwspinlock_read_locks;
30
+ int rwspinlock_write_locks;
31
+#endif
32
19
-inline int netdata_thread_tag_exists(void) {
20
- return (netdata_thread && *netdata_thread->tag);
33
+ struct {
34
+ SPINLOCK spinlock;
35
+ nd_thread_canceller cb;
36
+ void *data;
37
+ } canceller;
38
+
39
+ struct nd_thread *prev, *next;
40
+};
41
+
42
+static struct {
43
+ struct {
44
+ SPINLOCK spinlock;
45
+ ND_THREAD *list;
46
+ } exited;
47
+
48
+ struct {
49
+ SPINLOCK spinlock;
50
+ ND_THREAD *list;
51
+ } running;
52
+
53
+ pthread_attr_t *attr;
54
+} threads_globals = {
55
+ .exited = {
56
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
57
+ .list = NULL,
58
+ },
59
+ .running = {
60
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
61
+ .list = NULL,
62
+ },
63
+ .attr = NULL,
64
+};
65
+
66
+static __thread ND_THREAD *_nd_thread_info = NULL;
67
+static __thread char _nd_thread_os_name[ND_THREAD_TAG_MAX + 1] = "";
68
+
69
+// --------------------------------------------------------------------------------------------------------------------
70
+// O/S abstraction
71
+
72
+// get the thread name from the operating system
73
+static inline void os_get_thread_name(char *out, size_t size) {
74
+#if defined(__FreeBSD__)
75
+ pthread_get_name_np(pthread_self(), out, size);
76
+ if(strcmp(_nd_thread_os_name, "netdata") == 0)
77
+ strncpyz(out, "MAIN", size - 1);
78
+#elif defined(HAVE_PTHREAD_GETNAME_NP)
79
+ pthread_getname_np(pthread_self(), out, size - 1);
80
+ if(strcmp(out, "netdata") == 0)
81
+ strncpyz(out, "MAIN", size - 1);
82
+#else
83
+ strncpyz(out, "MAIN", size - 1);
84
+#endif
85
}
86
23
-static const char *thread_name_get(bool recheck) {
24
- static __thread char threadname[NETDATA_THREAD_NAME_MAX + 1] = "";
25
-
26
- if(netdata_thread_tag_exists())
27
- strncpyz(threadname, netdata_thread->tag, NETDATA_THREAD_NAME_MAX);
28
- else {
29
- if(!recheck && threadname[0])
30
- return threadname;
31
-
87
+// set the thread name to the operating system
88
+static inline void os_set_thread_name(const char *name) {
89
#if defined(__FreeBSD__)
33
- pthread_get_name_np(pthread_self(), threadname, NETDATA_THREAD_NAME_MAX + 1);
34
- if(strcmp(threadname, "netdata") == 0)
35
- strncpyz(threadname, "MAIN", NETDATA_THREAD_NAME_MAX);
90
+ pthread_set_name_np(pthread_self(), name);
91
#elif defined(__APPLE__)
37
- strncpyz(threadname, "MAIN", NETDATA_THREAD_NAME_MAX);
38
-#elif defined(HAVE_PTHREAD_GETNAME_NP)
39
- pthread_getname_np(pthread_self(), threadname, NETDATA_THREAD_NAME_MAX + 1);
40
- if(strcmp(threadname, "netdata") == 0)
41
- strncpyz(threadname, "MAIN", NETDATA_THREAD_NAME_MAX);
92
+ pthread_setname_np(name);
93
#else
43
- strncpyz(threadname, "MAIN", NETDATA_THREAD_NAME_MAX);
94
+ pthread_setname_np(pthread_self(), name);
95
#endif
45
- }
46
-
47
- return threadname;
96
}
97
50
-const char *netdata_thread_tag(void) {
51
- return thread_name_get(false);
98
+// --------------------------------------------------------------------------------------------------------------------
99
+// internal API for managing names
100
+
101
+inline int nd_thread_has_tag(void) {
102
+ return (_nd_thread_info && _nd_thread_info->tag[0]);
103
}
104
54
-static size_t webrtc_id = 0;
55
-static __thread bool webrtc_name_set = false;
56
-void webrtc_set_thread_name(void) {
57
- if(!netdata_thread && !webrtc_name_set) {
58
- webrtc_name_set = true;
59
- char threadname[NETDATA_THREAD_NAME_MAX + 1];
105
+// For threads created by netdata, return the tag of the thread.
106
+// For threads created by others (libuv, webrtc, etc), return the tag of the operating system.
107
+// This caches the response, so that it won't query the operating system multiple times.
108
+static inline const char *nd_thread_get_name(bool recheck) {
109
+ if(nd_thread_has_tag())
110
+ return _nd_thread_info->tag;
111
61
-#if defined(__FreeBSD__)
62
- snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "WEBRTC[%zu]", __atomic_fetch_add(&webrtc_id, 1, __ATOMIC_RELAXED));
63
- pthread_set_name_np(pthread_self(), threadname);
64
-#elif defined(__APPLE__)
65
- snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "WEBRTC[%zu]", __atomic_fetch_add(&webrtc_id, 1, __ATOMIC_RELAXED));
66
- pthread_setname_np(threadname);
67
-#elif defined(HAVE_PTHREAD_GETNAME_NP)
68
- pthread_getname_np(pthread_self(), threadname, NETDATA_THREAD_NAME_MAX+1);
69
- if(strcmp(threadname, "netdata") == 0) {
70
- snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "WEBRTC[%zu]", __atomic_fetch_add(&webrtc_id, 1, __ATOMIC_RELAXED));
71
- pthread_setname_np(pthread_self(), threadname);
72
- }
73
-#else
74
- snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "WEBRTC[%zu]", __atomic_fetch_add(&webrtc_id, 1, __ATOMIC_RELAXED));
75
- pthread_setname_np(pthread_self(), threadname);
76
-#endif
112
+ if(!recheck && _nd_thread_os_name[0])
113
+ return _nd_thread_os_name;
114
78
- thread_name_get(true);
79
- }
115
+ os_get_thread_name(_nd_thread_os_name, sizeof(_nd_thread_os_name));
116
+
117
+ return _nd_thread_os_name;
118
}
119
82
-// ----------------------------------------------------------------------------
83
-// compatibility library functions
120
+const char *nd_thread_tag(void) {
121
+ return nd_thread_get_name(false);
122
+}
123
85
-static __thread pid_t gettid_cached_tid = 0;
86
-pid_t gettid(void) {
87
- pid_t tid = 0;
124
+void nd_thread_tag_set(const char *tag) {
125
+ if(!tag || !*tag) return;
126
89
- if(likely(gettid_cached_tid > 0))
90
- return gettid_cached_tid;
127
+ if(_nd_thread_info)
128
+ strncpyz(_nd_thread_info->tag, tag, sizeof(_nd_thread_info->tag) - 1);
129
92
-#ifdef __FreeBSD__
130
+ strncpyz(_nd_thread_os_name, tag, sizeof(_nd_thread_os_name) - 1);
131
94
- tid = (pid_t)pthread_getthreadid_np();
132
+ os_set_thread_name(_nd_thread_os_name);
133
+}
134
96
-#elif defined(__APPLE__)
135
+// --------------------------------------------------------------------------------------------------------------------
136
+
137
+static __thread bool libuv_name_set = false;
138
+void uv_thread_set_name_np(const char* name) {
139
+ if(libuv_name_set) return;
140
+
141
+ strncpyz(_nd_thread_os_name, name, sizeof(_nd_thread_os_name) - 1);
142
+ os_set_thread_name(_nd_thread_os_name);
143
+ libuv_name_set = true;
144
+}
145
98
- #if (defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
99
- uint64_t curthreadid;
100
- pthread_threadid_np(NULL, &curthreadid);
101
- tid = (pid_t)curthreadid;
102
- #else /* __MAC_OS_X_VERSION_MIN_REQUIRED */
103
- tid = (pid_t)pthread_self;
104
- #endif /* __MAC_OS_X_VERSION_MIN_REQUIRED */
146
+// --------------------------------------------------------------------------------------------------------------------
147
+
148
+static size_t webrtc_id = 0;
149
+static __thread bool webrtc_name_set = false;
150
+void webrtc_set_thread_name(void) {
151
+ if(_nd_thread_info || webrtc_name_set) return;
152
106
-#else /* __APPLE__*/
153
+ webrtc_name_set = true;
154
108
- tid = (pid_t)syscall(SYS_gettid);
155
+ char tmp[ND_THREAD_TAG_MAX + 1] = "";
156
+ os_get_thread_name(tmp, sizeof(tmp));
157
110
-#endif /* __FreeBSD__, __APPLE__*/
158
+ if(!tmp[0] || strcmp(tmp, "netdata") == 0) {
159
+ char name[ND_THREAD_TAG_MAX + 1];
160
+ snprintfz(name, ND_THREAD_TAG_MAX, "WEBRTC[%zu]", __atomic_fetch_add(&webrtc_id, 1, __ATOMIC_RELAXED));
161
+ os_set_thread_name(name);
162
+ }
163
112
- gettid_cached_tid = tid;
113
- return tid;
164
+ nd_thread_get_name(true);
165
}
166
116
-// ----------------------------------------------------------------------------
167
+// --------------------------------------------------------------------------------------------------------------------
168
+// locks tracking
169
+
170
+#ifdef NETDATA_INTERNAL_CHECKS
171
+void nd_thread_rwlock_read_locked(void) { if(_nd_thread_info) _nd_thread_info->rwlocks_read_locks++; }
172
+void nd_thread_rwlock_read_unlocked(void) { if(_nd_thread_info) _nd_thread_info->rwlocks_read_locks--; }
173
+void nd_thread_rwlock_write_locked(void) { if(_nd_thread_info) _nd_thread_info->rwlocks_write_locks++; }
174
+void nd_thread_rwlock_write_unlocked(void) { if(_nd_thread_info) _nd_thread_info->rwlocks_write_locks--; }
175
+void nd_thread_mutex_locked(void) { if(_nd_thread_info) _nd_thread_info->mutex_locks++; }
176
+void nd_thread_mutex_unlocked(void) { if(_nd_thread_info) _nd_thread_info->mutex_locks--; }
177
+void nd_thread_spinlock_locked(void) { if(_nd_thread_info) _nd_thread_info->spinlock_locks++; }
178
+void nd_thread_spinlock_unlocked(void) { if(_nd_thread_info) _nd_thread_info->spinlock_locks--; }
179
+void nd_thread_rwspinlock_read_locked(void) { if(_nd_thread_info) _nd_thread_info->rwspinlock_read_locks++; }
180
+void nd_thread_rwspinlock_read_unlocked(void) { if(_nd_thread_info) _nd_thread_info->rwspinlock_read_locks--; }
181
+void nd_thread_rwspinlock_write_locked(void) { if(_nd_thread_info) _nd_thread_info->rwspinlock_write_locks++; }
182
+void nd_thread_rwspinlock_write_unlocked(void) { if(_nd_thread_info) _nd_thread_info->rwspinlock_write_locks--; }
183
+#endif
184
+
185
+// --------------------------------------------------------------------------------------------------------------------
186
// early initialization
187
188
size_t netdata_threads_init(void) {
189
int i;
190
122
- // --------------------------------------------------------------------
123
- // get the required stack size of the threads of netdata
124
-
125
- if(!netdata_threads_attr) {
126
- netdata_threads_attr = callocz(1, sizeof(pthread_attr_t));
127
- i = pthread_attr_init(netdata_threads_attr);
191
+ if(!threads_globals.attr) {
192
+ threads_globals.attr = callocz(1, sizeof(pthread_attr_t));
193
+ i = pthread_attr_init(threads_globals.attr);
194
if (i != 0)
195
fatal("pthread_attr_init() failed with code %d.", i);
196
}
197
198
+ // get the required stack size of the threads of netdata
199
size_t stacksize = 0;
133
- i = pthread_attr_getstacksize(netdata_threads_attr, &stacksize);
200
+ i = pthread_attr_getstacksize(threads_globals.attr, &stacksize);
201
if(i != 0)
202
fatal("pthread_attr_getstacksize() failed with code %d.", i);
203
210
void netdata_threads_init_after_fork(size_t stacksize) {
211
int i;
212
146
- // ------------------------------------------------------------------------
213
// set pthread stack size
148
-
149
- if(netdata_threads_attr && stacksize > (size_t)PTHREAD_STACK_MIN) {
150
- i = pthread_attr_setstacksize(netdata_threads_attr, stacksize);
214
+ if(threads_globals.attr && stacksize > (size_t)PTHREAD_STACK_MIN) {
215
+ i = pthread_attr_setstacksize(threads_globals.attr, stacksize);
216
if(i != 0)
217
nd_log(NDLS_DAEMON, NDLP_WARNING, "pthread_attr_setstacksize() to %zu bytes, failed with code %d.", stacksize, i);
218
else
234
}
235
236
// ----------------------------------------------------------------------------
172
-// netdata_thread_create
237
238
void rrdset_thread_rda_free(void);
239
void sender_thread_buffer_free(void);
241
void service_exits(void);
242
void rrd_collector_finished(void);
243
180
-static void thread_cleanup(void *ptr) {
181
- if(netdata_thread != ptr) {
182
- NETDATA_THREAD *info = (NETDATA_THREAD *)ptr;
183
- nd_log(NDLS_DAEMON, NDLP_ERR, "THREADS: internal error - thread local variable does not match the one passed to this function. Expected thread '%s', passed thread '%s'", netdata_thread->tag, info->tag);
184
- }
244
+static void nd_thread_join_exited_detached_threads(void) {
245
+ while(1) {
246
+ spinlock_lock(&threads_globals.exited.spinlock);
247
186
- if(!(netdata_thread->options & NETDATA_THREAD_OPTION_DONT_LOG_CLEANUP))
187
- nd_log(NDLS_DAEMON, NDLP_DEBUG, "thread with task id %d finished", gettid());
248
+ ND_THREAD *nti = threads_globals.exited.list;
249
+ while (nti && nd_thread_status_check(nti, NETDATA_THREAD_OPTION_JOINABLE) == 0)
250
+ nti = nti->next;
251
189
- rrd_collector_finished();
190
- sender_thread_buffer_free();
191
- rrdset_thread_rda_free();
192
- query_target_free();
193
- thread_cache_destroy();
194
- service_exits();
195
- worker_unregister();
252
+ if(nti)
253
+ DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(threads_globals.exited.list, nti, prev, next);
254
197
- netdata_thread->tag[0] = '\0';
255
+ spinlock_unlock(&threads_globals.exited.spinlock);
256
199
- freez(netdata_thread);
200
- netdata_thread = NULL;
257
+ if(nti) {
258
+ nd_log(NDLS_DAEMON, NDLP_INFO, "Joining detached thread '%s', tid %d", nti->tag, nti->tid);
259
+ nd_thread_join(nti);
260
+ }
261
+ else
262
+ break;
263
+ }
264
}
265
203
-void netdata_thread_set_tag(const char *tag) {
204
- if(!tag || !*tag)
205
- return;
266
+static void nd_thread_exit(void *pptr) {
267
+ ND_THREAD *nti = CLEANUP_FUNCTION_GET_PTR(pptr);
268
207
- int ret = 0;
269
+ if(nti != _nd_thread_info || !nti || !_nd_thread_info) {
270
+ nd_log(NDLS_DAEMON, NDLP_ERR,
271
+ "THREADS: internal error - thread local variable does not match the one passed to this function. "
272
+ "Expected thread '%s', passed thread '%s'",
273
+ _nd_thread_info ? _nd_thread_info->tag : "(null)", nti ? nti->tag : "(null)");
274
209
- char threadname[NETDATA_THREAD_NAME_MAX+1];
210
- strncpyz(threadname, tag, NETDATA_THREAD_NAME_MAX);
275
+ if(!nti) nti = _nd_thread_info;
276
+ }
277
212
-#if defined(__FreeBSD__)
213
- pthread_set_name_np(pthread_self(), threadname);
214
-#elif defined(__APPLE__)
215
- ret = pthread_setname_np(threadname);
216
-#else
217
- ret = pthread_setname_np(pthread_self(), threadname);
218
-#endif
278
+ if(!nti) return;
279
220
- if (ret != 0)
221
- nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot set pthread name of %d to %s. ErrCode: %d", gettid(), threadname, ret);
222
- else
223
- nd_log(NDLS_DAEMON, NDLP_DEBUG, "set name of thread %d to %s", gettid(), threadname);
280
+ internal_fatal(nti->rwlocks_read_locks != 0,
281
+ "THREAD '%s' WITH PID %d HAS %d RWLOCKS READ ACQUIRED WHILE EXITING !!!",
282
+ (nti) ? nti->tag : "(unset)", gettid_cached(), nti->rwlocks_read_locks);
283
225
- if(netdata_thread) {
226
- strncpyz(netdata_thread->tag, threadname, sizeof(netdata_thread->tag) - 1);
227
- }
228
-}
284
+ internal_fatal(nti->rwlocks_write_locks != 0,
285
+ "THREAD '%s' WITH PID %d HAS %d RWLOCKS WRITE ACQUIRED WHILE EXITING !!!",
286
+ (nti) ? nti->tag : "(unset)", gettid_cached(), nti->rwlocks_write_locks);
287
230
-void uv_thread_set_name_np(uv_thread_t ut, const char* name) {
231
- int ret = 0;
288
+ internal_fatal(nti->mutex_locks != 0,
289
+ "THREAD '%s' WITH PID %d HAS %d MUTEXES ACQUIRED WHILE EXITING !!!",
290
+ (nti) ? nti->tag : "(unset)", gettid_cached(), nti->mutex_locks);
291
233
- char threadname[NETDATA_THREAD_NAME_MAX+1];
234
- strncpyz(threadname, name, NETDATA_THREAD_NAME_MAX);
292
+ internal_fatal(nti->spinlock_locks != 0,
293
+ "THREAD '%s' WITH PID %d HAS %d SPINLOCKS ACQUIRED WHILE EXITING !!!",
294
+ (nti) ? nti->tag : "(unset)", gettid_cached(), nti->spinlock_locks);
295
236
-#if defined(__FreeBSD__)
237
- pthread_set_name_np(ut ? ut : pthread_self(), threadname);
238
-#elif defined(__APPLE__)
239
- // Apple can only set its own name
240
- UNUSED(ut);
241
-#else
242
- ret = pthread_setname_np(ut ? ut : pthread_self(), threadname);
243
-#endif
296
+ internal_fatal(nti->rwspinlock_read_locks != 0,
297
+ "THREAD '%s' WITH PID %d HAS %d RWSPINLOCKS READ ACQUIRED WHILE EXITING !!!",
298
+ (nti) ? nti->tag : "(unset)", gettid_cached(), nti->rwspinlock_read_locks);
299
245
- thread_name_get(true);
300
+ internal_fatal(nti->rwspinlock_write_locks != 0,
301
+ "THREAD '%s' WITH PID %d HAS %d RWSPINLOCKS WRITE ACQUIRED WHILE EXITING !!!",
302
+ (nti) ? nti->tag : "(unset)", gettid_cached(), nti->rwspinlock_write_locks);
303
247
- if (ret)
248
- nd_log(NDLS_DAEMON, NDLP_NOTICE, "cannot set libuv thread name to %s. Err: %d", threadname, ret);
249
-}
304
+ if(nd_thread_status_check(nti, NETDATA_THREAD_OPTION_DONT_LOG_CLEANUP) != NETDATA_THREAD_OPTION_DONT_LOG_CLEANUP)
305
+ nd_log(NDLS_DAEMON, NDLP_DEBUG, "thread with task id %d finished", nti->tid);
306
251
-void os_thread_get_current_name_np(char threadname[NETDATA_THREAD_NAME_MAX + 1])
252
-{
253
- threadname[0] = '\0';
254
-#if defined(__FreeBSD__)
255
- pthread_get_name_np(pthread_self(), threadname, NETDATA_THREAD_NAME_MAX + 1);
256
-#elif defined(HAVE_PTHREAD_GETNAME_NP) /* Linux & macOS */
257
- (void)pthread_getname_np(pthread_self(), threadname, NETDATA_THREAD_NAME_MAX + 1);
258
-#endif
307
+ rrd_collector_finished();
308
+ sender_thread_buffer_free();
309
+ rrdset_thread_rda_free();
310
+ query_target_free();
311
+ thread_cache_destroy();
312
+ service_exits();
313
+ worker_unregister();
314
+
315
+ nd_thread_status_set(nti, NETDATA_THREAD_STATUS_FINISHED);
316
+
317
+ spinlock_lock(&threads_globals.running.spinlock);
318
+ DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(threads_globals.running.list, nti, prev, next);
319
+ spinlock_unlock(&threads_globals.running.spinlock);
320
+
321
+ if (nd_thread_status_check(nti, NETDATA_THREAD_OPTION_JOINABLE) != NETDATA_THREAD_OPTION_JOINABLE) {
322
+ spinlock_lock(&threads_globals.exited.spinlock);
323
+ DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(threads_globals.exited.list, nti, prev, next);
324
+ spinlock_unlock(&threads_globals.exited.spinlock);
325
+ }
326
}
327
261
-static void *netdata_thread_init(void *ptr) {
262
- netdata_thread = (NETDATA_THREAD *)ptr;
328
+static void *nd_thread_starting_point(void *ptr) {
329
+ ND_THREAD *nti = _nd_thread_info = (ND_THREAD *)ptr;
330
+ nd_thread_status_set(nti, NETDATA_THREAD_STATUS_STARTED);
331
+
332
+ nti->tid = gettid_cached();
333
+ nd_thread_tag_set(nti->tag);
334
264
- if(!(netdata_thread->options & NETDATA_THREAD_OPTION_DONT_LOG_STARTUP))
265
- nd_log(NDLS_DAEMON, NDLP_DEBUG, "thread created with task id %d", gettid());
335
+ if(nd_thread_status_check(nti, NETDATA_THREAD_OPTION_DONT_LOG_STARTUP) != NETDATA_THREAD_OPTION_DONT_LOG_STARTUP)
336
+ nd_log(NDLS_DAEMON, NDLP_DEBUG, "thread created with task id %d", gettid_cached());
337
338
if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
339
nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot set pthread cancel type to DEFERRED.");
341
if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
342
nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot set pthread cancel state to ENABLE.");
343
273
- netdata_thread_set_tag(netdata_thread->tag);
344
+ CLEANUP_FUNCTION_REGISTER(nd_thread_exit) cleanup_ptr = nti;
345
275
- if (!(netdata_thread->options & NETDATA_THREAD_OPTION_JOINABLE)) {
276
- int rc = pthread_detach(pthread_self());
277
- if (rc != 0)
278
- nd_log(NDLS_DAEMON, NDLP_WARNING,
279
- "cannot request detach of newly created %s thread. pthread_detach() failed with code %d",
280
- netdata_thread->tag, rc);
281
- }
346
+ // run the thread code
347
+ nti->ret = nti->start_routine(nti->arg);
348
+
349
+ return nti;
350
+}
351
+
352
+ND_THREAD *nd_thread_self(void) {
353
+ return _nd_thread_info;
354
+}
355
283
- void *ret = NULL;
284
- pthread_cleanup_push(thread_cleanup, ptr) {
285
- ret = netdata_thread->start_routine(netdata_thread->arg);
356
+bool nd_thread_is_me(ND_THREAD *nti) {
357
+ return nti && nti->thread == pthread_self();
358
+}
359
+
360
+ND_THREAD *nd_thread_create(const char *tag, NETDATA_THREAD_OPTIONS options, void *(*start_routine)(void *), void *arg) {
361
+ nd_thread_join_exited_detached_threads();
362
+
363
+ ND_THREAD *nti = callocz(1, sizeof(*nti));
364
+ spinlock_init(&nti->canceller.spinlock);
365
+ nti->arg = arg;
366
+ nti->start_routine = start_routine;
367
+ nti->options = options & NETDATA_THREAD_OPTIONS_ALL;
368
+ strncpyz(nti->tag, tag, ND_THREAD_TAG_MAX);
369
+
370
+ spinlock_lock(&threads_globals.running.spinlock);
371
+ DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(threads_globals.running.list, nti, prev, next);
372
+ spinlock_unlock(&threads_globals.running.spinlock);
373
+
374
+ int ret = pthread_create(&nti->thread, threads_globals.attr, nd_thread_starting_point, nti);
375
+ if(ret != 0) {
376
+ nd_log(NDLS_DAEMON, NDLP_ERR,
377
+ "failed to create new thread for %s. pthread_create() failed with code %d",
378
+ tag, ret);
379
+
380
+ spinlock_lock(&threads_globals.running.spinlock);
381
+ DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(threads_globals.running.list, nti, prev, next);
382
+ spinlock_unlock(&threads_globals.running.spinlock);
383
+ freez(nti);
384
+ return NULL;
385
}
287
- pthread_cleanup_pop(1);
386
289
- return ret;
387
+ return nti;
388
}
389
292
-int netdata_thread_create(netdata_thread_t *thread, const char *tag, NETDATA_THREAD_OPTIONS options, void *(*start_routine) (void *), void *arg) {
293
- NETDATA_THREAD *info = callocz(1, sizeof(NETDATA_THREAD));
294
- info->arg = arg;
295
- info->start_routine = start_routine;
296
- info->options = options;
297
- strncpyz(info->tag, tag, NETDATA_THREAD_NAME_MAX);
390
+// --------------------------------------------------------------------------------------------------------------------
391
299
- int ret = pthread_create(thread, netdata_threads_attr, netdata_thread_init, info);
300
- if(ret != 0)
301
- nd_log(NDLS_DAEMON, NDLP_ERR, "failed to create new thread for %s. pthread_create() failed with code %d", tag, ret);
392
+void nd_thread_register_canceller(nd_thread_canceller cb, void *data) {
393
+ ND_THREAD *nti = _nd_thread_info;
394
+ if(!nti) return;
395
303
- return ret;
396
+ spinlock_lock(&nti->canceller.spinlock);
397
+ nti->canceller.cb = cb;
398
+ nti->canceller.data = data;
399
+ spinlock_unlock(&nti->canceller.spinlock);
400
}
401
306
-// ----------------------------------------------------------------------------
307
-// netdata_thread_cancel
308
-#ifdef NETDATA_INTERNAL_CHECKS
309
-int netdata_thread_cancel_with_trace(netdata_thread_t thread, int line, const char *file, const char *function) {
310
-#else
311
-int netdata_thread_cancel(netdata_thread_t thread) {
312
-#endif
313
- int ret = pthread_cancel(thread);
314
- if(ret != 0)
315
-#ifdef NETDATA_INTERNAL_CHECKS
316
- nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot cancel thread. pthread_cancel() failed with code %d at %d@%s, function %s()", ret, line, file, function);
317
-#else
318
- nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot cancel thread. pthread_cancel() failed with code %d.", ret);
319
-#endif
402
+void nd_thread_signal_cancel(ND_THREAD *nti) {
403
+ if(!nti) return;
404
+
405
+ __atomic_store_n(&nti->cancel_atomic, true, __ATOMIC_RELAXED);
406
+
407
+ spinlock_lock(&nti->canceller.spinlock);
408
+ if(nti->canceller.cb)
409
+ nti->canceller.cb(nti->canceller.data);
410
+ spinlock_unlock(&nti->canceller.spinlock);
411
+}
412
321
- return ret;
413
+bool nd_thread_signaled_to_cancel(void) {
414
+ if(!_nd_thread_info) return false;
415
+ return __atomic_load_n(&_nd_thread_info->cancel_atomic, __ATOMIC_RELAXED);
416
}
417
418
// ----------------------------------------------------------------------------
325
-// netdata_thread_join
419
+// nd_thread_join
420
327
-int netdata_thread_join(netdata_thread_t thread, void **retval) {
328
- int ret = pthread_join(thread, retval);
421
+void nd_thread_join(ND_THREAD *nti) {
422
+ if(!nti) return;
423
+
424
+ int ret = pthread_join(nti->thread, NULL);
425
if(ret != 0)
426
nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot join thread. pthread_join() failed with code %d.", ret);
427
+ else {
428
+ nd_thread_status_set(nti, NETDATA_THREAD_STATUS_JOINED);
429
332
- return ret;
333
-}
334
-
335
-int netdata_thread_detach(pthread_t thread) {
336
- int ret = pthread_detach(thread);
337
- if(ret != 0)
338
- nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot detach thread. pthread_detach() failed with code %d.", ret);
430
+ spinlock_lock(&threads_globals.exited.spinlock);
431
+ if(nti->prev)
432
+ DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(threads_globals.exited.list, nti, prev, next);
433
+ spinlock_unlock(&threads_globals.exited.spinlock);
434
340
- return ret;
435
+ freez(nti);
436
+ }
437
}