]> git.proxmox.com Git - mirror_frr.git/blame - lib/frr_pthread.c
tools: fix frr-reload.py daemon option
[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"
98f14af8 31
eaf58ba9
DL
32DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread")
33DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives")
98f14af8 34
a45dc974
QY
35/* default frr_pthread start/stop routine prototypes */
36static void *fpt_run(void *arg);
37static int fpt_halt(struct frr_pthread *fpt, void **res);
98f14af8 38
9af949cc
QY
39/* misc sigs */
40static void frr_pthread_destroy_nolock(struct frr_pthread *fpt);
41
a45dc974 42/* default frr_pthread attributes */
2b64873d 43const struct frr_pthread_attr frr_pthread_attr_default = {
a45dc974
QY
44 .start = fpt_run,
45 .stop = fpt_halt,
a45dc974
QY
46};
47
1ac267a2
DL
48/* list to keep track of all frr_pthreads */
49static pthread_mutex_t frr_pthread_list_mtx = PTHREAD_MUTEX_INITIALIZER;
50static struct list *frr_pthread_list;
a45dc974 51
98f14af8
QY
52/* ------------------------------------------------------------------------ */
53
4d762f26 54void frr_pthread_init(void)
98f14af8 55{
00dffa8c 56 frr_with_mutex(&frr_pthread_list_mtx) {
1ac267a2 57 frr_pthread_list = list_new();
d62a17ae 58 }
98f14af8
QY
59}
60
4d762f26 61void frr_pthread_finish(void)
98f14af8 62{
54baf432
QY
63 frr_pthread_stop_all();
64
00dffa8c 65 frr_with_mutex(&frr_pthread_list_mtx) {
9af949cc
QY
66 struct listnode *n, *nn;
67 struct frr_pthread *fpt;
68
69 for (ALL_LIST_ELEMENTS(frr_pthread_list, n, nn, fpt)) {
70 listnode_delete(frr_pthread_list, fpt);
71 frr_pthread_destroy_nolock(fpt);
72 }
73
6a154c88 74 list_delete(&frr_pthread_list);
d62a17ae 75 }
98f14af8
QY
76}
77
2b64873d 78struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr,
57019528 79 const char *name, const char *os_name)
98f14af8 80{
d62a17ae 81 struct frr_pthread *fpt = NULL;
82
a45dc974
QY
83 attr = attr ? attr : &frr_pthread_attr_default;
84
1ac267a2
DL
85 fpt = XCALLOC(MTYPE_FRR_PTHREAD, sizeof(struct frr_pthread));
86 /* initialize mutex */
87 pthread_mutex_init(&fpt->mtx, NULL);
88 /* create new thread master */
89 fpt->master = thread_master_create(name);
90 /* set attributes */
91 fpt->attr = *attr;
92 name = (name ? name : "Anonymous thread");
93 fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
94 if (os_name)
e77cc2a9 95 strlcpy(fpt->os_name, os_name, OS_THREAD_NAMELEN);
b8dccd94 96 else
e77cc2a9 97 strlcpy(fpt->os_name, name, OS_THREAD_NAMELEN);
1ac267a2
DL
98 /* initialize startup synchronization primitives */
99 fpt->running_cond_mtx = XCALLOC(
100 MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
101 fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM,
102 sizeof(pthread_cond_t));
103 pthread_mutex_init(fpt->running_cond_mtx, NULL);
104 pthread_cond_init(fpt->running_cond, NULL);
105
00dffa8c 106 frr_with_mutex(&frr_pthread_list_mtx) {
1ac267a2 107 listnode_add(frr_pthread_list, fpt);
d62a17ae 108 }
d62a17ae 109
110 return fpt;
98f14af8
QY
111}
112
9af949cc 113static void frr_pthread_destroy_nolock(struct frr_pthread *fpt)
98f14af8 114{
54baf432 115 thread_master_free(fpt->master);
d8a8a8de 116 pthread_mutex_destroy(&fpt->mtx);
a45dc974
QY
117 pthread_mutex_destroy(fpt->running_cond_mtx);
118 pthread_cond_destroy(fpt->running_cond);
0a22ddfb 119 XFREE(MTYPE_FRR_PTHREAD, fpt->name);
a45dc974
QY
120 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
121 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
d62a17ae 122 XFREE(MTYPE_FRR_PTHREAD, fpt);
98f14af8
QY
123}
124
9af949cc
QY
125void frr_pthread_destroy(struct frr_pthread *fpt)
126{
127 frr_with_mutex(&frr_pthread_list_mtx) {
128 listnode_delete(frr_pthread_list, fpt);
129 }
130
131 frr_pthread_destroy_nolock(fpt);
132}
133
c80bedb8 134int frr_pthread_set_name(struct frr_pthread *fpt)
d8a8a8de 135{
57019528
CS
136 int ret = 0;
137
e9d938b8
DL
138#ifdef HAVE_PTHREAD_SETNAME_NP
139# ifdef GNU_LINUX
c80bedb8 140 ret = pthread_setname_np(fpt->thread, fpt->os_name);
a64c953a 141# elif defined(__NetBSD__)
c80bedb8 142 ret = pthread_setname_np(fpt->thread, fpt->os_name, NULL);
e9d938b8
DL
143# endif
144#elif defined(HAVE_PTHREAD_SET_NAME_NP)
c80bedb8 145 pthread_set_name_np(fpt->thread, fpt->os_name);
57019528 146#endif
57019528
CS
147
148 return ret;
d8a8a8de
QY
149}
150
3e41733f
DL
151static void *frr_pthread_inner(void *arg)
152{
153 struct frr_pthread *fpt = arg;
154
155 rcu_thread_start(fpt->rcu_thread);
156 return fpt->attr.start(fpt);
157}
158
a45dc974 159int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
98f14af8 160{
d62a17ae 161 int ret;
98f14af8 162
3e41733f
DL
163 fpt->rcu_thread = rcu_thread_prepare();
164 ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt);
98f14af8 165
a45dc974
QY
166 /*
167 * Per pthread_create(3), the contents of fpt->thread are undefined if
168 * pthread_create() did not succeed. Reset this value to zero.
169 */
3e41733f
DL
170 if (ret < 0) {
171 rcu_thread_unprepare(fpt->rcu_thread);
d62a17ae 172 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
3e41733f 173 }
98f14af8 174
d62a17ae 175 return ret;
98f14af8
QY
176}
177
a45dc974 178void frr_pthread_wait_running(struct frr_pthread *fpt)
98f14af8 179{
00dffa8c 180 frr_with_mutex(fpt->running_cond_mtx) {
a45dc974
QY
181 while (!fpt->running)
182 pthread_cond_wait(fpt->running_cond,
183 fpt->running_cond_mtx);
184 }
98f14af8
QY
185}
186
a45dc974 187void frr_pthread_notify_running(struct frr_pthread *fpt)
98f14af8 188{
00dffa8c 189 frr_with_mutex(fpt->running_cond_mtx) {
a45dc974
QY
190 fpt->running = true;
191 pthread_cond_signal(fpt->running_cond);
192 }
98f14af8
QY
193}
194
a45dc974
QY
195int frr_pthread_stop(struct frr_pthread *fpt, void **result)
196{
197 int ret = (*fpt->attr.stop)(fpt, result);
198 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
199 return ret;
200}
201
4d762f26 202void frr_pthread_stop_all(void)
98f14af8 203{
00dffa8c 204 frr_with_mutex(&frr_pthread_list_mtx) {
1ac267a2
DL
205 struct listnode *n;
206 struct frr_pthread *fpt;
54baf432
QY
207 for (ALL_LIST_ELEMENTS_RO(frr_pthread_list, n, fpt)) {
208 if (atomic_load_explicit(&fpt->running,
209 memory_order_relaxed))
210 frr_pthread_stop(fpt, NULL);
211 }
d62a17ae 212 }
98f14af8 213}
b2140cb7 214
a45dc974
QY
215/*
216 * ----------------------------------------------------------------------------
217 * Default Event Loop
218 * ----------------------------------------------------------------------------
219 */
220
221/* dummy task for sleeper pipe */
222static int fpt_dummy(struct thread *thread)
223{
224 return 0;
225}
226
227/* poison pill task to end event loop */
228static int fpt_finish(struct thread *thread)
229{
230 struct frr_pthread *fpt = THREAD_ARG(thread);
985e36a6 231
a45dc974
QY
232 atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
233 return 0;
234}
235
236/* stop function, called from other threads to halt this one */
237static int fpt_halt(struct frr_pthread *fpt, void **res)
238{
239 thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
240 pthread_join(fpt->thread, res);
a45dc974
QY
241
242 return 0;
243}
244
a6275055
QY
245/*
246 * Entry pthread function & main event loop.
247 *
248 * Upon thread start the following actions occur:
249 *
250 * - frr_pthread's owner field is set to pthread ID.
251 * - All signals are blocked (except for unblockable signals).
252 * - Pthread's threadmaster is set to never handle pending signals
253 * - Poker pipe for poll() is created and queued as I/O source
254 * - The frr_pthread->running_cond condition variable is signalled to indicate
255 * that the previous actions have completed. It is not safe to assume any of
256 * the above have occurred before receiving this signal.
257 *
258 * After initialization is completed, the event loop begins running. Each tick,
259 * the following actions are performed before running the usual event system
260 * tick function:
261 *
262 * - Verify that the running boolean is set
263 * - Verify that there are no pending cancellation requests
264 * - Verify that there are tasks scheduled
265 *
266 * So long as the conditions are met, the event loop tick is run and the
267 * returned task is executed.
268 *
269 * If any of these conditions are not met, the event loop exits, closes the
270 * pipes and dies without running any cleanup functions.
271 */
a45dc974
QY
272static void *fpt_run(void *arg)
273{
274 struct frr_pthread *fpt = arg;
275 fpt->master->owner = pthread_self();
276
0bdeb5e5
DL
277 zlog_tls_buffer_init();
278
a45dc974
QY
279 int sleeper[2];
280 pipe(sleeper);
281 thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
282
283 fpt->master->handle_signals = false;
284
c80bedb8 285 frr_pthread_set_name(fpt);
57019528 286
a45dc974
QY
287 frr_pthread_notify_running(fpt);
288
289 struct thread task;
290 while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
a6275055 291 pthread_testcancel();
a45dc974
QY
292 if (thread_fetch(fpt->master, &task)) {
293 thread_call(&task);
294 }
295 }
296
297 close(sleeper[1]);
298 close(sleeper[0]);
299
0bdeb5e5
DL
300 zlog_tls_buffer_fini();
301
a45dc974
QY
302 return NULL;
303}