/src/frr/lib/frr_pthread.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * Utilities and interfaces for managing POSIX threads within FRR. |
4 | | * Copyright (C) 2017 Cumulus Networks, Inc. |
5 | | */ |
6 | | |
7 | | #include <zebra.h> |
8 | | #include <pthread.h> |
9 | | #ifdef HAVE_PTHREAD_NP_H |
10 | | #include <pthread_np.h> |
11 | | #endif |
12 | | #include <sched.h> |
13 | | |
14 | | #include "frr_pthread.h" |
15 | | #include "memory.h" |
16 | | #include "linklist.h" |
17 | | #include "zlog.h" |
18 | | #include "libfrr.h" |
19 | | #include "libfrr_trace.h" |
20 | | |
21 | 8 | DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread"); |
22 | 8 | DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives"); |
23 | 8 | |
24 | 8 | /* default frr_pthread start/stop routine prototypes */ |
25 | 8 | static void *fpt_run(void *arg); |
26 | 8 | static int fpt_halt(struct frr_pthread *fpt, void **res); |
27 | 8 | |
28 | 8 | /* misc sigs */ |
29 | 8 | static void frr_pthread_destroy_nolock(struct frr_pthread *fpt); |
30 | 8 | |
31 | 8 | /* default frr_pthread attributes */ |
32 | 8 | const struct frr_pthread_attr frr_pthread_attr_default = { |
33 | 8 | .start = fpt_run, |
34 | 8 | .stop = fpt_halt, |
35 | 8 | }; |
36 | 8 | |
37 | 8 | /* list to keep track of all frr_pthreads */ |
38 | 8 | static pthread_mutex_t frr_pthread_list_mtx = PTHREAD_MUTEX_INITIALIZER; |
39 | 8 | static struct list *frr_pthread_list; |
40 | 8 | |
41 | 8 | /* ------------------------------------------------------------------------ */ |
42 | 8 | |
43 | 8 | void frr_pthread_init(void) |
44 | 8 | { |
45 | 1 | frr_with_mutex (&frr_pthread_list_mtx) { |
46 | 1 | frr_pthread_list = list_new(); |
47 | 1 | } |
48 | 1 | } |
49 | | |
50 | | void frr_pthread_finish(void) |
51 | 0 | { |
52 | 0 | frr_pthread_stop_all(); |
53 | |
|
54 | 0 | frr_with_mutex (&frr_pthread_list_mtx) { |
55 | 0 | struct listnode *n, *nn; |
56 | 0 | struct frr_pthread *fpt; |
57 | |
|
58 | 0 | for (ALL_LIST_ELEMENTS(frr_pthread_list, n, nn, fpt)) { |
59 | 0 | listnode_delete(frr_pthread_list, fpt); |
60 | 0 | frr_pthread_destroy_nolock(fpt); |
61 | 0 | } |
62 | |
|
63 | 0 | list_delete(&frr_pthread_list); |
64 | 0 | } |
65 | 0 | } |
66 | | |
67 | | struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr, |
68 | | const char *name, const char *os_name) |
69 | 0 | { |
70 | 0 | struct frr_pthread *fpt = NULL; |
71 | |
|
72 | 0 | attr = attr ? attr : &frr_pthread_attr_default; |
73 | |
|
74 | 0 | fpt = XCALLOC(MTYPE_FRR_PTHREAD, sizeof(struct frr_pthread)); |
75 | | /* initialize mutex */ |
76 | 0 | pthread_mutex_init(&fpt->mtx, NULL); |
77 | | /* create new thread master */ |
78 | 0 | fpt->master = event_master_create(name); |
79 | | /* set attributes */ |
80 | 0 | fpt->attr = *attr; |
81 | 0 | name = (name ? name : "Anonymous thread"); |
82 | 0 | fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name); |
83 | 0 | if (os_name) |
84 | 0 | strlcpy(fpt->os_name, os_name, OS_THREAD_NAMELEN); |
85 | 0 | else |
86 | 0 | strlcpy(fpt->os_name, name, OS_THREAD_NAMELEN); |
87 | | /* initialize startup synchronization primitives */ |
88 | 0 | fpt->running_cond_mtx = XCALLOC( |
89 | 0 | MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t)); |
90 | 0 | fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM, |
91 | 0 | sizeof(pthread_cond_t)); |
92 | 0 | pthread_mutex_init(fpt->running_cond_mtx, NULL); |
93 | 0 | pthread_cond_init(fpt->running_cond, NULL); |
94 | |
|
95 | 0 | frr_with_mutex (&frr_pthread_list_mtx) { |
96 | 0 | listnode_add(frr_pthread_list, fpt); |
97 | 0 | } |
98 | |
|
99 | 0 | return fpt; |
100 | 0 | } |
101 | | |
102 | | static void frr_pthread_destroy_nolock(struct frr_pthread *fpt) |
103 | 0 | { |
104 | 0 | event_master_free(fpt->master); |
105 | 0 | pthread_mutex_destroy(&fpt->mtx); |
106 | 0 | pthread_mutex_destroy(fpt->running_cond_mtx); |
107 | 0 | pthread_cond_destroy(fpt->running_cond); |
108 | 0 | XFREE(MTYPE_FRR_PTHREAD, fpt->name); |
109 | 0 | XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx); |
110 | 0 | XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond); |
111 | 0 | XFREE(MTYPE_FRR_PTHREAD, fpt); |
112 | 0 | } |
113 | | |
114 | | void frr_pthread_destroy(struct frr_pthread *fpt) |
115 | 0 | { |
116 | 0 | frr_with_mutex (&frr_pthread_list_mtx) { |
117 | 0 | listnode_delete(frr_pthread_list, fpt); |
118 | 0 | } |
119 | |
|
120 | 0 | frr_pthread_destroy_nolock(fpt); |
121 | 0 | } |
122 | | |
123 | | int frr_pthread_set_name(struct frr_pthread *fpt) |
124 | 0 | { |
125 | 0 | int ret = 0; |
126 | |
|
127 | 0 | #ifdef HAVE_PTHREAD_SETNAME_NP |
128 | 0 | # ifdef GNU_LINUX |
129 | 0 | ret = pthread_setname_np(fpt->thread, fpt->os_name); |
130 | | # elif defined(__NetBSD__) |
131 | | ret = pthread_setname_np(fpt->thread, fpt->os_name, NULL); |
132 | | # endif |
133 | | #elif defined(HAVE_PTHREAD_SET_NAME_NP) |
134 | | pthread_set_name_np(fpt->thread, fpt->os_name); |
135 | | #endif |
136 | |
|
137 | 0 | return ret; |
138 | 0 | } |
139 | | |
140 | | static void *frr_pthread_inner(void *arg) |
141 | 0 | { |
142 | 0 | struct frr_pthread *fpt = arg; |
143 | |
|
144 | 0 | rcu_thread_start(fpt->rcu_thread); |
145 | 0 | return fpt->attr.start(fpt); |
146 | 0 | } |
147 | | |
148 | | int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr) |
149 | 0 | { |
150 | 0 | int ret; |
151 | 0 | sigset_t oldsigs, blocksigs; |
152 | |
|
153 | 0 | assert(frr_is_after_fork || !"trying to start thread before fork()"); |
154 | | |
155 | | /* Ensure we never handle signals on a background thread by blocking |
156 | | * everything here (new thread inherits signal mask) |
157 | | */ |
158 | 0 | sigfillset(&blocksigs); |
159 | 0 | pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs); |
160 | |
|
161 | 0 | frrtrace(1, frr_libfrr, frr_pthread_run, fpt->name); |
162 | |
|
163 | 0 | fpt->rcu_thread = rcu_thread_prepare(); |
164 | 0 | ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt); |
165 | | |
166 | | /* Restore caller's signals */ |
167 | 0 | pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); |
168 | | |
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 | | */ |
173 | 0 | if (ret < 0) { |
174 | 0 | rcu_thread_unprepare(fpt->rcu_thread); |
175 | 0 | memset(&fpt->thread, 0x00, sizeof(fpt->thread)); |
176 | 0 | } |
177 | |
|
178 | 0 | return ret; |
179 | 0 | } |
180 | | |
181 | | void frr_pthread_wait_running(struct frr_pthread *fpt) |
182 | 0 | { |
183 | 0 | frr_with_mutex (fpt->running_cond_mtx) { |
184 | 0 | while (!fpt->running) |
185 | 0 | pthread_cond_wait(fpt->running_cond, |
186 | 0 | fpt->running_cond_mtx); |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | | void frr_pthread_notify_running(struct frr_pthread *fpt) |
191 | 0 | { |
192 | 0 | frr_with_mutex (fpt->running_cond_mtx) { |
193 | 0 | fpt->running = true; |
194 | 0 | pthread_cond_signal(fpt->running_cond); |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | | int frr_pthread_stop(struct frr_pthread *fpt, void **result) |
199 | 0 | { |
200 | 0 | frrtrace(1, frr_libfrr, frr_pthread_stop, fpt->name); |
201 | |
|
202 | 0 | int ret = (*fpt->attr.stop)(fpt, result); |
203 | 0 | memset(&fpt->thread, 0x00, sizeof(fpt->thread)); |
204 | 0 | return ret; |
205 | 0 | } |
206 | | |
207 | | void frr_pthread_stop_all(void) |
208 | 0 | { |
209 | 0 | frr_with_mutex (&frr_pthread_list_mtx) { |
210 | 0 | struct listnode *n; |
211 | 0 | struct frr_pthread *fpt; |
212 | 0 | for (ALL_LIST_ELEMENTS_RO(frr_pthread_list, n, fpt)) { |
213 | 0 | if (atomic_load_explicit(&fpt->running, |
214 | 0 | memory_order_relaxed)) |
215 | 0 | frr_pthread_stop(fpt, NULL); |
216 | 0 | } |
217 | 0 | } |
218 | 0 | } |
219 | | |
220 | | /* |
221 | | * ---------------------------------------------------------------------------- |
222 | | * Default Event Loop |
223 | | * ---------------------------------------------------------------------------- |
224 | | */ |
225 | | |
226 | | /* dummy task for sleeper pipe */ |
227 | | static void fpt_dummy(struct event *thread) |
228 | 0 | { |
229 | 0 | } |
230 | | |
231 | | /* poison pill task to end event loop */ |
232 | | static void fpt_finish(struct event *thread) |
233 | 0 | { |
234 | 0 | struct frr_pthread *fpt = EVENT_ARG(thread); |
235 | 0 |
|
236 | 0 | atomic_store_explicit(&fpt->running, false, memory_order_relaxed); |
237 | 0 | } |
238 | | |
239 | | /* stop function, called from other threads to halt this one */ |
240 | | static int fpt_halt(struct frr_pthread *fpt, void **res) |
241 | 0 | { |
242 | 0 | event_add_event(fpt->master, &fpt_finish, fpt, 0, NULL); |
243 | 0 | pthread_join(fpt->thread, res); |
244 | |
|
245 | 0 | return 0; |
246 | 0 | } |
247 | | |
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 | | */ |
275 | | static void *fpt_run(void *arg) |
276 | 0 | { |
277 | 0 | struct frr_pthread *fpt = arg; |
278 | 0 | fpt->master->owner = pthread_self(); |
279 | |
|
280 | 0 | zlog_tls_buffer_init(); |
281 | |
|
282 | 0 | int sleeper[2]; |
283 | 0 | pipe(sleeper); |
284 | 0 | event_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL); |
285 | |
|
286 | 0 | fpt->master->handle_signals = false; |
287 | |
|
288 | 0 | frr_pthread_set_name(fpt); |
289 | |
|
290 | 0 | frr_pthread_notify_running(fpt); |
291 | |
|
292 | 0 | struct event task; |
293 | 0 | while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) { |
294 | 0 | pthread_testcancel(); |
295 | 0 | if (event_fetch(fpt->master, &task)) { |
296 | 0 | event_call(&task); |
297 | 0 | } |
298 | 0 | } |
299 | |
|
300 | 0 | close(sleeper[1]); |
301 | 0 | close(sleeper[0]); |
302 | |
|
303 | 0 | zlog_tls_buffer_fini(); |
304 | |
|
305 | | return NULL; |
306 | 0 | } |