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