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