1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Utilities and interfaces for managing POSIX threads within FRR.
4 * Copyright (C) 2017 Cumulus Networks, Inc.
9 #ifdef HAVE_PTHREAD_NP_H
10 #include <pthread_np.h>
14 #include "frr_pthread.h"
19 #include "libfrr_trace.h"
21 DEFINE_MTYPE_STATIC(LIB
, FRR_PTHREAD
, "FRR POSIX Thread");
22 DEFINE_MTYPE_STATIC(LIB
, PTHREAD_PRIM
, "POSIX sync primitives");
24 /* default frr_pthread start/stop routine prototypes */
25 static void *fpt_run(void *arg
);
26 static int fpt_halt(struct frr_pthread
*fpt
, void **res
);
29 static void frr_pthread_destroy_nolock(struct frr_pthread
*fpt
);
31 /* default frr_pthread attributes */
32 const struct frr_pthread_attr frr_pthread_attr_default
= {
37 /* list to keep track of all frr_pthreads */
38 static pthread_mutex_t frr_pthread_list_mtx
= PTHREAD_MUTEX_INITIALIZER
;
39 static struct list
*frr_pthread_list
;
41 /* ------------------------------------------------------------------------ */
43 void frr_pthread_init(void)
45 frr_with_mutex (&frr_pthread_list_mtx
) {
46 frr_pthread_list
= list_new();
50 void frr_pthread_finish(void)
52 frr_pthread_stop_all();
54 frr_with_mutex (&frr_pthread_list_mtx
) {
55 struct listnode
*n
, *nn
;
56 struct frr_pthread
*fpt
;
58 for (ALL_LIST_ELEMENTS(frr_pthread_list
, n
, nn
, fpt
)) {
59 listnode_delete(frr_pthread_list
, fpt
);
60 frr_pthread_destroy_nolock(fpt
);
63 list_delete(&frr_pthread_list
);
67 struct frr_pthread
*frr_pthread_new(const struct frr_pthread_attr
*attr
,
68 const char *name
, const char *os_name
)
70 struct frr_pthread
*fpt
= NULL
;
72 attr
= attr
? attr
: &frr_pthread_attr_default
;
74 fpt
= XCALLOC(MTYPE_FRR_PTHREAD
, sizeof(struct frr_pthread
));
75 /* initialize mutex */
76 pthread_mutex_init(&fpt
->mtx
, NULL
);
77 /* create new thread master */
78 fpt
->master
= thread_master_create(name
);
81 name
= (name
? name
: "Anonymous thread");
82 fpt
->name
= XSTRDUP(MTYPE_FRR_PTHREAD
, name
);
84 strlcpy(fpt
->os_name
, os_name
, OS_THREAD_NAMELEN
);
86 strlcpy(fpt
->os_name
, name
, OS_THREAD_NAMELEN
);
87 /* initialize startup synchronization primitives */
88 fpt
->running_cond_mtx
= XCALLOC(
89 MTYPE_PTHREAD_PRIM
, sizeof(pthread_mutex_t
));
90 fpt
->running_cond
= XCALLOC(MTYPE_PTHREAD_PRIM
,
91 sizeof(pthread_cond_t
));
92 pthread_mutex_init(fpt
->running_cond_mtx
, NULL
);
93 pthread_cond_init(fpt
->running_cond
, NULL
);
95 frr_with_mutex (&frr_pthread_list_mtx
) {
96 listnode_add(frr_pthread_list
, fpt
);
102 static void frr_pthread_destroy_nolock(struct frr_pthread
*fpt
)
104 thread_master_free(fpt
->master
);
105 pthread_mutex_destroy(&fpt
->mtx
);
106 pthread_mutex_destroy(fpt
->running_cond_mtx
);
107 pthread_cond_destroy(fpt
->running_cond
);
108 XFREE(MTYPE_FRR_PTHREAD
, fpt
->name
);
109 XFREE(MTYPE_PTHREAD_PRIM
, fpt
->running_cond_mtx
);
110 XFREE(MTYPE_PTHREAD_PRIM
, fpt
->running_cond
);
111 XFREE(MTYPE_FRR_PTHREAD
, fpt
);
114 void frr_pthread_destroy(struct frr_pthread
*fpt
)
116 frr_with_mutex (&frr_pthread_list_mtx
) {
117 listnode_delete(frr_pthread_list
, fpt
);
120 frr_pthread_destroy_nolock(fpt
);
123 int frr_pthread_set_name(struct frr_pthread
*fpt
)
127 #ifdef HAVE_PTHREAD_SETNAME_NP
129 ret
= pthread_setname_np(fpt
->thread
, fpt
->os_name
);
130 # elif defined(__NetBSD__)
131 ret
= pthread_setname_np(fpt
->thread
, fpt
->os_name
, NULL
);
133 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
134 pthread_set_name_np(fpt
->thread
, fpt
->os_name
);
140 static void *frr_pthread_inner(void *arg
)
142 struct frr_pthread
*fpt
= arg
;
144 rcu_thread_start(fpt
->rcu_thread
);
145 return fpt
->attr
.start(fpt
);
148 int frr_pthread_run(struct frr_pthread
*fpt
, const pthread_attr_t
*attr
)
151 sigset_t oldsigs
, blocksigs
;
153 assert(frr_is_after_fork
|| !"trying to start thread before fork()");
155 /* Ensure we never handle signals on a background thread by blocking
156 * everything here (new thread inherits signal mask)
158 sigfillset(&blocksigs
);
159 pthread_sigmask(SIG_BLOCK
, &blocksigs
, &oldsigs
);
161 frrtrace(1, frr_libfrr
, frr_pthread_run
, fpt
->name
);
163 fpt
->rcu_thread
= rcu_thread_prepare();
164 ret
= pthread_create(&fpt
->thread
, attr
, frr_pthread_inner
, fpt
);
166 /* Restore caller's signals */
167 pthread_sigmask(SIG_SETMASK
, &oldsigs
, NULL
);
170 * Per pthread_create(3), the contents of fpt->thread are undefined if
171 * pthread_create() did not succeed. Reset this value to zero.
174 rcu_thread_unprepare(fpt
->rcu_thread
);
175 memset(&fpt
->thread
, 0x00, sizeof(fpt
->thread
));
181 void frr_pthread_wait_running(struct frr_pthread
*fpt
)
183 frr_with_mutex (fpt
->running_cond_mtx
) {
184 while (!fpt
->running
)
185 pthread_cond_wait(fpt
->running_cond
,
186 fpt
->running_cond_mtx
);
190 void frr_pthread_notify_running(struct frr_pthread
*fpt
)
192 frr_with_mutex (fpt
->running_cond_mtx
) {
194 pthread_cond_signal(fpt
->running_cond
);
198 int frr_pthread_stop(struct frr_pthread
*fpt
, void **result
)
200 frrtrace(1, frr_libfrr
, frr_pthread_stop
, fpt
->name
);
202 int ret
= (*fpt
->attr
.stop
)(fpt
, result
);
203 memset(&fpt
->thread
, 0x00, sizeof(fpt
->thread
));
207 void frr_pthread_stop_all(void)
209 frr_with_mutex (&frr_pthread_list_mtx
) {
211 struct frr_pthread
*fpt
;
212 for (ALL_LIST_ELEMENTS_RO(frr_pthread_list
, n
, fpt
)) {
213 if (atomic_load_explicit(&fpt
->running
,
214 memory_order_relaxed
))
215 frr_pthread_stop(fpt
, NULL
);
221 * ----------------------------------------------------------------------------
223 * ----------------------------------------------------------------------------
226 /* dummy task for sleeper pipe */
227 static void fpt_dummy(struct thread
*thread
)
231 /* poison pill task to end event loop */
232 static void fpt_finish(struct thread
*thread
)
234 struct frr_pthread
*fpt
= THREAD_ARG(thread
);
236 atomic_store_explicit(&fpt
->running
, false, memory_order_relaxed
);
239 /* stop function, called from other threads to halt this one */
240 static int fpt_halt(struct frr_pthread
*fpt
, void **res
)
242 thread_add_event(fpt
->master
, &fpt_finish
, fpt
, 0, NULL
);
243 pthread_join(fpt
->thread
, res
);
249 * Entry pthread function & main event loop.
251 * Upon thread start the following actions occur:
253 * - frr_pthread's owner field is set to pthread ID.
254 * - All signals are blocked (except for unblockable signals).
255 * - Pthread's threadmaster is set to never handle pending signals
256 * - Poker pipe for poll() is created and queued as I/O source
257 * - The frr_pthread->running_cond condition variable is signalled to indicate
258 * that the previous actions have completed. It is not safe to assume any of
259 * the above have occurred before receiving this signal.
261 * After initialization is completed, the event loop begins running. Each tick,
262 * the following actions are performed before running the usual event system
265 * - Verify that the running boolean is set
266 * - Verify that there are no pending cancellation requests
267 * - Verify that there are tasks scheduled
269 * So long as the conditions are met, the event loop tick is run and the
270 * returned task is executed.
272 * If any of these conditions are not met, the event loop exits, closes the
273 * pipes and dies without running any cleanup functions.
275 static void *fpt_run(void *arg
)
277 struct frr_pthread
*fpt
= arg
;
278 fpt
->master
->owner
= pthread_self();
280 zlog_tls_buffer_init();
284 thread_add_read(fpt
->master
, &fpt_dummy
, NULL
, sleeper
[0], NULL
);
286 fpt
->master
->handle_signals
= false;
288 frr_pthread_set_name(fpt
);
290 frr_pthread_notify_running(fpt
);
293 while (atomic_load_explicit(&fpt
->running
, memory_order_relaxed
)) {
294 pthread_testcancel();
295 if (thread_fetch(fpt
->master
, &task
)) {
303 zlog_tls_buffer_fini();