]> git.proxmox.com Git - mirror_frr.git/blame - lib/frr_pthread.c
Merge pull request #11003 from anlancs/bgpd-mh-trival-remove
[mirror_frr.git] / lib / frr_pthread.c
CommitLineData
98f14af8 1/*
a45dc974 2 * Utilities and interfaces for managing POSIX threads within FRR.
d8a8a8de 3 * Copyright (C) 2017 Cumulus Networks, Inc.
896014f4
DL
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
98f14af8
QY
18 */
19
20#include <zebra.h>
21#include <pthread.h>
324be174
DL
22#ifdef HAVE_PTHREAD_NP_H
23#include <pthread_np.h>
24#endif
b2140cb7 25#include <sched.h>
98f14af8
QY
26
27#include "frr_pthread.h"
28#include "memory.h"
1ac267a2 29#include "linklist.h"
0bdeb5e5 30#include "zlog.h"
38554d3a 31#include "libfrr.h"
912d45a1 32#include "libfrr_trace.h"
98f14af8 33
bf8d3d6a
DL
34DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread");
35DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives");
98f14af8 36
a45dc974
QY
37/* default frr_pthread start/stop routine prototypes */
38static void *fpt_run(void *arg);
39static int fpt_halt(struct frr_pthread *fpt, void **res);
98f14af8 40
9af949cc
QY
41/* misc sigs */
42static void frr_pthread_destroy_nolock(struct frr_pthread *fpt);
43
a45dc974 44/* default frr_pthread attributes */
2b64873d 45const struct frr_pthread_attr frr_pthread_attr_default = {
a45dc974
QY
46 .start = fpt_run,
47 .stop = fpt_halt,
a45dc974
QY
48};
49
1ac267a2
DL
50/* list to keep track of all frr_pthreads */
51static pthread_mutex_t frr_pthread_list_mtx = PTHREAD_MUTEX_INITIALIZER;
52static struct list *frr_pthread_list;
a45dc974 53
98f14af8
QY
54/* ------------------------------------------------------------------------ */
55
4d762f26 56void frr_pthread_init(void)
98f14af8 57{
00dffa8c 58 frr_with_mutex(&frr_pthread_list_mtx) {
1ac267a2 59 frr_pthread_list = list_new();
d62a17ae 60 }
98f14af8
QY
61}
62
4d762f26 63void frr_pthread_finish(void)
98f14af8 64{
54baf432
QY
65 frr_pthread_stop_all();
66
00dffa8c 67 frr_with_mutex(&frr_pthread_list_mtx) {
9af949cc
QY
68 struct listnode *n, *nn;
69 struct frr_pthread *fpt;
70
71 for (ALL_LIST_ELEMENTS(frr_pthread_list, n, nn, fpt)) {
72 listnode_delete(frr_pthread_list, fpt);
73 frr_pthread_destroy_nolock(fpt);
74 }
75
6a154c88 76 list_delete(&frr_pthread_list);
d62a17ae 77 }
98f14af8
QY
78}
79
2b64873d 80struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr,
57019528 81 const char *name, const char *os_name)
98f14af8 82{
d62a17ae 83 struct frr_pthread *fpt = NULL;
84
a45dc974
QY
85 attr = attr ? attr : &frr_pthread_attr_default;
86
1ac267a2
DL
87 fpt = XCALLOC(MTYPE_FRR_PTHREAD, sizeof(struct frr_pthread));
88 /* initialize mutex */
89 pthread_mutex_init(&fpt->mtx, NULL);
90 /* create new thread master */
91 fpt->master = thread_master_create(name);
92 /* set attributes */
93 fpt->attr = *attr;
94 name = (name ? name : "Anonymous thread");
95 fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
96 if (os_name)
e77cc2a9 97 strlcpy(fpt->os_name, os_name, OS_THREAD_NAMELEN);
b8dccd94 98 else
e77cc2a9 99 strlcpy(fpt->os_name, name, OS_THREAD_NAMELEN);
1ac267a2
DL
100 /* initialize startup synchronization primitives */
101 fpt->running_cond_mtx = XCALLOC(
102 MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
103 fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM,
104 sizeof(pthread_cond_t));
105 pthread_mutex_init(fpt->running_cond_mtx, NULL);
106 pthread_cond_init(fpt->running_cond, NULL);
107
00dffa8c 108 frr_with_mutex(&frr_pthread_list_mtx) {
1ac267a2 109 listnode_add(frr_pthread_list, fpt);
d62a17ae 110 }
d62a17ae 111
112 return fpt;
98f14af8
QY
113}
114
9af949cc 115static void frr_pthread_destroy_nolock(struct frr_pthread *fpt)
98f14af8 116{
54baf432 117 thread_master_free(fpt->master);
d8a8a8de 118 pthread_mutex_destroy(&fpt->mtx);
a45dc974
QY
119 pthread_mutex_destroy(fpt->running_cond_mtx);
120 pthread_cond_destroy(fpt->running_cond);
0a22ddfb 121 XFREE(MTYPE_FRR_PTHREAD, fpt->name);
a45dc974
QY
122 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
123 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
d62a17ae 124 XFREE(MTYPE_FRR_PTHREAD, fpt);
98f14af8
QY
125}
126
9af949cc
QY
127void frr_pthread_destroy(struct frr_pthread *fpt)
128{
129 frr_with_mutex(&frr_pthread_list_mtx) {
130 listnode_delete(frr_pthread_list, fpt);
131 }
132
133 frr_pthread_destroy_nolock(fpt);
134}
135
c80bedb8 136int frr_pthread_set_name(struct frr_pthread *fpt)
d8a8a8de 137{
57019528
CS
138 int ret = 0;
139
e9d938b8
DL
140#ifdef HAVE_PTHREAD_SETNAME_NP
141# ifdef GNU_LINUX
c80bedb8 142 ret = pthread_setname_np(fpt->thread, fpt->os_name);
a64c953a 143# elif defined(__NetBSD__)
c80bedb8 144 ret = pthread_setname_np(fpt->thread, fpt->os_name, NULL);
e9d938b8
DL
145# endif
146#elif defined(HAVE_PTHREAD_SET_NAME_NP)
c80bedb8 147 pthread_set_name_np(fpt->thread, fpt->os_name);
57019528 148#endif
57019528
CS
149
150 return ret;
d8a8a8de
QY
151}
152
3e41733f
DL
153static void *frr_pthread_inner(void *arg)
154{
155 struct frr_pthread *fpt = arg;
156
157 rcu_thread_start(fpt->rcu_thread);
158 return fpt->attr.start(fpt);
159}
160
a45dc974 161int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
98f14af8 162{
d62a17ae 163 int ret;
f4635e33
MS
164 sigset_t oldsigs, blocksigs;
165
38554d3a
DL
166 assert(frr_is_after_fork || !"trying to start thread before fork()");
167
f4635e33
MS
168 /* Ensure we never handle signals on a background thread by blocking
169 * everything here (new thread inherits signal mask)
170 */
171 sigfillset(&blocksigs);
172 pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs);
98f14af8 173
c7bb4f00 174 frrtrace(1, frr_libfrr, frr_pthread_run, fpt->name);
87879a5e 175
3e41733f
DL
176 fpt->rcu_thread = rcu_thread_prepare();
177 ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt);
98f14af8 178
f4635e33
MS
179 /* Restore caller's signals */
180 pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
181
a45dc974
QY
182 /*
183 * Per pthread_create(3), the contents of fpt->thread are undefined if
184 * pthread_create() did not succeed. Reset this value to zero.
185 */
3e41733f
DL
186 if (ret < 0) {
187 rcu_thread_unprepare(fpt->rcu_thread);
d62a17ae 188 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
3e41733f 189 }
98f14af8 190
d62a17ae 191 return ret;
98f14af8
QY
192}
193
a45dc974 194void frr_pthread_wait_running(struct frr_pthread *fpt)
98f14af8 195{
00dffa8c 196 frr_with_mutex(fpt->running_cond_mtx) {
a45dc974
QY
197 while (!fpt->running)
198 pthread_cond_wait(fpt->running_cond,
199 fpt->running_cond_mtx);
200 }
98f14af8
QY
201}
202
a45dc974 203void frr_pthread_notify_running(struct frr_pthread *fpt)
98f14af8 204{
00dffa8c 205 frr_with_mutex(fpt->running_cond_mtx) {
a45dc974
QY
206 fpt->running = true;
207 pthread_cond_signal(fpt->running_cond);
208 }
98f14af8
QY
209}
210
a45dc974
QY
211int frr_pthread_stop(struct frr_pthread *fpt, void **result)
212{
c7bb4f00 213 frrtrace(1, frr_libfrr, frr_pthread_stop, fpt->name);
87879a5e 214
a45dc974
QY
215 int ret = (*fpt->attr.stop)(fpt, result);
216 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
217 return ret;
218}
219
4d762f26 220void frr_pthread_stop_all(void)
98f14af8 221{
00dffa8c 222 frr_with_mutex(&frr_pthread_list_mtx) {
1ac267a2
DL
223 struct listnode *n;
224 struct frr_pthread *fpt;
54baf432
QY
225 for (ALL_LIST_ELEMENTS_RO(frr_pthread_list, n, fpt)) {
226 if (atomic_load_explicit(&fpt->running,
227 memory_order_relaxed))
228 frr_pthread_stop(fpt, NULL);
229 }
d62a17ae 230 }
98f14af8 231}
b2140cb7 232
a45dc974
QY
233/*
234 * ----------------------------------------------------------------------------
235 * Default Event Loop
236 * ----------------------------------------------------------------------------
237 */
238
239/* dummy task for sleeper pipe */
cc9f21da 240static void fpt_dummy(struct thread *thread)
a45dc974 241{
a45dc974
QY
242}
243
244/* poison pill task to end event loop */
cc9f21da 245static void fpt_finish(struct thread *thread)
a45dc974
QY
246{
247 struct frr_pthread *fpt = THREAD_ARG(thread);
985e36a6 248
a45dc974 249 atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
a45dc974
QY
250}
251
252/* stop function, called from other threads to halt this one */
253static int fpt_halt(struct frr_pthread *fpt, void **res)
254{
255 thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
256 pthread_join(fpt->thread, res);
a45dc974
QY
257
258 return 0;
259}
260
a6275055
QY
261/*
262 * Entry pthread function & main event loop.
263 *
264 * Upon thread start the following actions occur:
265 *
266 * - frr_pthread's owner field is set to pthread ID.
267 * - All signals are blocked (except for unblockable signals).
268 * - Pthread's threadmaster is set to never handle pending signals
269 * - Poker pipe for poll() is created and queued as I/O source
270 * - The frr_pthread->running_cond condition variable is signalled to indicate
271 * that the previous actions have completed. It is not safe to assume any of
272 * the above have occurred before receiving this signal.
273 *
274 * After initialization is completed, the event loop begins running. Each tick,
275 * the following actions are performed before running the usual event system
276 * tick function:
277 *
278 * - Verify that the running boolean is set
279 * - Verify that there are no pending cancellation requests
280 * - Verify that there are tasks scheduled
281 *
282 * So long as the conditions are met, the event loop tick is run and the
283 * returned task is executed.
284 *
285 * If any of these conditions are not met, the event loop exits, closes the
286 * pipes and dies without running any cleanup functions.
287 */
a45dc974
QY
288static void *fpt_run(void *arg)
289{
290 struct frr_pthread *fpt = arg;
291 fpt->master->owner = pthread_self();
292
0bdeb5e5
DL
293 zlog_tls_buffer_init();
294
a45dc974
QY
295 int sleeper[2];
296 pipe(sleeper);
297 thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
298
299 fpt->master->handle_signals = false;
300
c80bedb8 301 frr_pthread_set_name(fpt);
57019528 302
a45dc974
QY
303 frr_pthread_notify_running(fpt);
304
305 struct thread task;
306 while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
a6275055 307 pthread_testcancel();
a45dc974
QY
308 if (thread_fetch(fpt->master, &task)) {
309 thread_call(&task);
310 }
311 }
312
313 close(sleeper[1]);
314 close(sleeper[0]);
315
0bdeb5e5
DL
316 zlog_tls_buffer_fini();
317
a45dc974
QY
318 return NULL;
319}