]> git.proxmox.com Git - mirror_frr.git/blame - lib/frr_pthread.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / lib / frr_pthread.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
98f14af8 2/*
a45dc974 3 * Utilities and interfaces for managing POSIX threads within FRR.
d8a8a8de 4 * Copyright (C) 2017 Cumulus Networks, Inc.
98f14af8
QY
5 */
6
7#include <zebra.h>
8#include <pthread.h>
324be174
DL
9#ifdef HAVE_PTHREAD_NP_H
10#include <pthread_np.h>
11#endif
b2140cb7 12#include <sched.h>
98f14af8
QY
13
14#include "frr_pthread.h"
15#include "memory.h"
1ac267a2 16#include "linklist.h"
0bdeb5e5 17#include "zlog.h"
38554d3a 18#include "libfrr.h"
912d45a1 19#include "libfrr_trace.h"
98f14af8 20
bf8d3d6a
DL
21DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread");
22DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives");
98f14af8 23
a45dc974
QY
24/* default frr_pthread start/stop routine prototypes */
25static void *fpt_run(void *arg);
26static int fpt_halt(struct frr_pthread *fpt, void **res);
98f14af8 27
9af949cc
QY
28/* misc sigs */
29static void frr_pthread_destroy_nolock(struct frr_pthread *fpt);
30
a45dc974 31/* default frr_pthread attributes */
2b64873d 32const struct frr_pthread_attr frr_pthread_attr_default = {
a45dc974
QY
33 .start = fpt_run,
34 .stop = fpt_halt,
a45dc974
QY
35};
36
1ac267a2
DL
37/* list to keep track of all frr_pthreads */
38static pthread_mutex_t frr_pthread_list_mtx = PTHREAD_MUTEX_INITIALIZER;
39static struct list *frr_pthread_list;
a45dc974 40
98f14af8
QY
41/* ------------------------------------------------------------------------ */
42
4d762f26 43void frr_pthread_init(void)
98f14af8 44{
cb1991af 45 frr_with_mutex (&frr_pthread_list_mtx) {
1ac267a2 46 frr_pthread_list = list_new();
d62a17ae 47 }
98f14af8
QY
48}
49
4d762f26 50void frr_pthread_finish(void)
98f14af8 51{
54baf432
QY
52 frr_pthread_stop_all();
53
cb1991af 54 frr_with_mutex (&frr_pthread_list_mtx) {
9af949cc
QY
55 struct listnode *n, *nn;
56 struct frr_pthread *fpt;
57
58 for (ALL_LIST_ELEMENTS(frr_pthread_list, n, nn, fpt)) {
59 listnode_delete(frr_pthread_list, fpt);
60 frr_pthread_destroy_nolock(fpt);
61 }
62
6a154c88 63 list_delete(&frr_pthread_list);
d62a17ae 64 }
98f14af8
QY
65}
66
2b64873d 67struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr,
57019528 68 const char *name, const char *os_name)
98f14af8 69{
d62a17ae 70 struct frr_pthread *fpt = NULL;
71
a45dc974
QY
72 attr = attr ? attr : &frr_pthread_attr_default;
73
1ac267a2
DL
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 */
ce50d11c 78 fpt->master = event_master_create(name);
1ac267a2
DL
79 /* set attributes */
80 fpt->attr = *attr;
81 name = (name ? name : "Anonymous thread");
82 fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
83 if (os_name)
e77cc2a9 84 strlcpy(fpt->os_name, os_name, OS_THREAD_NAMELEN);
b8dccd94 85 else
e77cc2a9 86 strlcpy(fpt->os_name, name, OS_THREAD_NAMELEN);
1ac267a2
DL
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);
94
cb1991af 95 frr_with_mutex (&frr_pthread_list_mtx) {
1ac267a2 96 listnode_add(frr_pthread_list, fpt);
d62a17ae 97 }
d62a17ae 98
99 return fpt;
98f14af8
QY
100}
101
9af949cc 102static void frr_pthread_destroy_nolock(struct frr_pthread *fpt)
98f14af8 103{
ce50d11c 104 event_master_free(fpt->master);
d8a8a8de 105 pthread_mutex_destroy(&fpt->mtx);
a45dc974
QY
106 pthread_mutex_destroy(fpt->running_cond_mtx);
107 pthread_cond_destroy(fpt->running_cond);
0a22ddfb 108 XFREE(MTYPE_FRR_PTHREAD, fpt->name);
a45dc974
QY
109 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
110 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
d62a17ae 111 XFREE(MTYPE_FRR_PTHREAD, fpt);
98f14af8
QY
112}
113
9af949cc
QY
114void frr_pthread_destroy(struct frr_pthread *fpt)
115{
cb1991af 116 frr_with_mutex (&frr_pthread_list_mtx) {
9af949cc
QY
117 listnode_delete(frr_pthread_list, fpt);
118 }
119
120 frr_pthread_destroy_nolock(fpt);
121}
122
c80bedb8 123int frr_pthread_set_name(struct frr_pthread *fpt)
d8a8a8de 124{
57019528
CS
125 int ret = 0;
126
e9d938b8
DL
127#ifdef HAVE_PTHREAD_SETNAME_NP
128# ifdef GNU_LINUX
c80bedb8 129 ret = pthread_setname_np(fpt->thread, fpt->os_name);
a64c953a 130# elif defined(__NetBSD__)
c80bedb8 131 ret = pthread_setname_np(fpt->thread, fpt->os_name, NULL);
e9d938b8
DL
132# endif
133#elif defined(HAVE_PTHREAD_SET_NAME_NP)
c80bedb8 134 pthread_set_name_np(fpt->thread, fpt->os_name);
57019528 135#endif
57019528
CS
136
137 return ret;
d8a8a8de
QY
138}
139
3e41733f
DL
140static void *frr_pthread_inner(void *arg)
141{
142 struct frr_pthread *fpt = arg;
143
144 rcu_thread_start(fpt->rcu_thread);
145 return fpt->attr.start(fpt);
146}
147
a45dc974 148int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
98f14af8 149{
d62a17ae 150 int ret;
f4635e33
MS
151 sigset_t oldsigs, blocksigs;
152
38554d3a
DL
153 assert(frr_is_after_fork || !"trying to start thread before fork()");
154
f4635e33
MS
155 /* Ensure we never handle signals on a background thread by blocking
156 * everything here (new thread inherits signal mask)
157 */
158 sigfillset(&blocksigs);
159 pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs);
98f14af8 160
c7bb4f00 161 frrtrace(1, frr_libfrr, frr_pthread_run, fpt->name);
87879a5e 162
3e41733f
DL
163 fpt->rcu_thread = rcu_thread_prepare();
164 ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt);
98f14af8 165
f4635e33
MS
166 /* Restore caller's signals */
167 pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
168
a45dc974
QY
169 /*
170 * Per pthread_create(3), the contents of fpt->thread are undefined if
171 * pthread_create() did not succeed. Reset this value to zero.
172 */
3e41733f
DL
173 if (ret < 0) {
174 rcu_thread_unprepare(fpt->rcu_thread);
d62a17ae 175 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
3e41733f 176 }
98f14af8 177
d62a17ae 178 return ret;
98f14af8
QY
179}
180
a45dc974 181void frr_pthread_wait_running(struct frr_pthread *fpt)
98f14af8 182{
cb1991af 183 frr_with_mutex (fpt->running_cond_mtx) {
a45dc974
QY
184 while (!fpt->running)
185 pthread_cond_wait(fpt->running_cond,
186 fpt->running_cond_mtx);
187 }
98f14af8
QY
188}
189
a45dc974 190void frr_pthread_notify_running(struct frr_pthread *fpt)
98f14af8 191{
cb1991af 192 frr_with_mutex (fpt->running_cond_mtx) {
a45dc974
QY
193 fpt->running = true;
194 pthread_cond_signal(fpt->running_cond);
195 }
98f14af8
QY
196}
197
a45dc974
QY
198int frr_pthread_stop(struct frr_pthread *fpt, void **result)
199{
c7bb4f00 200 frrtrace(1, frr_libfrr, frr_pthread_stop, fpt->name);
87879a5e 201
a45dc974
QY
202 int ret = (*fpt->attr.stop)(fpt, result);
203 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
204 return ret;
205}
206
4d762f26 207void frr_pthread_stop_all(void)
98f14af8 208{
cb1991af 209 frr_with_mutex (&frr_pthread_list_mtx) {
1ac267a2
DL
210 struct listnode *n;
211 struct frr_pthread *fpt;
54baf432
QY
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);
216 }
d62a17ae 217 }
98f14af8 218}
b2140cb7 219
a45dc974
QY
220/*
221 * ----------------------------------------------------------------------------
222 * Default Event Loop
223 * ----------------------------------------------------------------------------
224 */
225
226/* dummy task for sleeper pipe */
e6685141 227static void fpt_dummy(struct event *thread)
a45dc974 228{
a45dc974
QY
229}
230
231/* poison pill task to end event loop */
e6685141 232static void fpt_finish(struct event *thread)
a45dc974 233{
e16d030c 234 struct frr_pthread *fpt = EVENT_ARG(thread);
985e36a6 235
a45dc974 236 atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
a45dc974
QY
237}
238
239/* stop function, called from other threads to halt this one */
240static int fpt_halt(struct frr_pthread *fpt, void **res)
241{
907a2395 242 event_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
a45dc974 243 pthread_join(fpt->thread, res);
a45dc974
QY
244
245 return 0;
246}
247
a6275055
QY
248/*
249 * Entry pthread function & main event loop.
250 *
251 * Upon thread start the following actions occur:
252 *
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.
260 *
261 * After initialization is completed, the event loop begins running. Each tick,
262 * the following actions are performed before running the usual event system
263 * tick function:
264 *
265 * - Verify that the running boolean is set
266 * - Verify that there are no pending cancellation requests
267 * - Verify that there are tasks scheduled
268 *
269 * So long as the conditions are met, the event loop tick is run and the
270 * returned task is executed.
271 *
272 * If any of these conditions are not met, the event loop exits, closes the
273 * pipes and dies without running any cleanup functions.
274 */
a45dc974
QY
275static void *fpt_run(void *arg)
276{
277 struct frr_pthread *fpt = arg;
278 fpt->master->owner = pthread_self();
279
0bdeb5e5
DL
280 zlog_tls_buffer_init();
281
a45dc974
QY
282 int sleeper[2];
283 pipe(sleeper);
907a2395 284 event_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
a45dc974
QY
285
286 fpt->master->handle_signals = false;
287
c80bedb8 288 frr_pthread_set_name(fpt);
57019528 289
a45dc974
QY
290 frr_pthread_notify_running(fpt);
291
e6685141 292 struct event task;
a45dc974 293 while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
a6275055 294 pthread_testcancel();
de2754be
DS
295 if (event_fetch(fpt->master, &task)) {
296 event_call(&task);
a45dc974
QY
297 }
298 }
299
300 close(sleeper[1]);
301 close(sleeper[0]);
302
0bdeb5e5
DL
303 zlog_tls_buffer_fini();
304
a45dc974
QY
305 return NULL;
306}