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 
32 DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread")
33 DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives")
34 
35 /* default frr_pthread start/stop routine prototypes */
36 static void *fpt_run(void *arg);
37 static int fpt_halt(struct frr_pthread *fpt, void **res);
38 
39 /* misc sigs */
40 static void frr_pthread_destroy_nolock(struct frr_pthread *fpt);
41 
42 /* default frr_pthread attributes */
43 const struct frr_pthread_attr frr_pthread_attr_default = {
44 	.start = fpt_run,
45 	.stop = fpt_halt,
46 };
47 
48 /* list to keep track of all frr_pthreads */
49 static pthread_mutex_t frr_pthread_list_mtx = PTHREAD_MUTEX_INITIALIZER;
50 static struct list *frr_pthread_list;
51 
52 /* ------------------------------------------------------------------------ */
53 
frr_pthread_init(void)54 void frr_pthread_init(void)
55 {
56 	frr_with_mutex(&frr_pthread_list_mtx) {
57 		frr_pthread_list = list_new();
58 	}
59 }
60 
frr_pthread_finish(void)61 void frr_pthread_finish(void)
62 {
63 	frr_pthread_stop_all();
64 
65 	frr_with_mutex(&frr_pthread_list_mtx) {
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 
74 		list_delete(&frr_pthread_list);
75 	}
76 }
77 
frr_pthread_new(const struct frr_pthread_attr * attr,const char * name,const char * os_name)78 struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr,
79 				    const char *name, const char *os_name)
80 {
81 	struct frr_pthread *fpt = NULL;
82 
83 	attr = attr ? attr : &frr_pthread_attr_default;
84 
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)
95 		strlcpy(fpt->os_name, os_name, OS_THREAD_NAMELEN);
96 	else
97 		strlcpy(fpt->os_name, name, OS_THREAD_NAMELEN);
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 
106 	frr_with_mutex(&frr_pthread_list_mtx) {
107 		listnode_add(frr_pthread_list, fpt);
108 	}
109 
110 	return fpt;
111 }
112 
frr_pthread_destroy_nolock(struct frr_pthread * fpt)113 static void frr_pthread_destroy_nolock(struct frr_pthread *fpt)
114 {
115 	thread_master_free(fpt->master);
116 	pthread_mutex_destroy(&fpt->mtx);
117 	pthread_mutex_destroy(fpt->running_cond_mtx);
118 	pthread_cond_destroy(fpt->running_cond);
119 	XFREE(MTYPE_FRR_PTHREAD, fpt->name);
120 	XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
121 	XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
122 	XFREE(MTYPE_FRR_PTHREAD, fpt);
123 }
124 
frr_pthread_destroy(struct frr_pthread * fpt)125 void 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 
frr_pthread_set_name(struct frr_pthread * fpt)134 int frr_pthread_set_name(struct frr_pthread *fpt)
135 {
136 	int ret = 0;
137 
138 #ifdef HAVE_PTHREAD_SETNAME_NP
139 # ifdef GNU_LINUX
140 	ret = pthread_setname_np(fpt->thread, fpt->os_name);
141 # elif defined(__NetBSD__)
142 	ret = pthread_setname_np(fpt->thread, fpt->os_name, NULL);
143 # endif
144 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
145 	pthread_set_name_np(fpt->thread, fpt->os_name);
146 #endif
147 
148 	return ret;
149 }
150 
frr_pthread_inner(void * arg)151 static 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 
frr_pthread_run(struct frr_pthread * fpt,const pthread_attr_t * attr)159 int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
160 {
161 	int ret;
162 	sigset_t oldsigs, blocksigs;
163 
164 	/* Ensure we never handle signals on a background thread by blocking
165 	 * everything here (new thread inherits signal mask)
166 	 */
167 	sigfillset(&blocksigs);
168 	pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs);
169 
170 	fpt->rcu_thread = rcu_thread_prepare();
171 	ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt);
172 
173 	/* Restore caller's signals */
174 	pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
175 
176 	/*
177 	 * Per pthread_create(3), the contents of fpt->thread are undefined if
178 	 * pthread_create() did not succeed. Reset this value to zero.
179 	 */
180 	if (ret < 0) {
181 		rcu_thread_unprepare(fpt->rcu_thread);
182 		memset(&fpt->thread, 0x00, sizeof(fpt->thread));
183 	}
184 
185 	return ret;
186 }
187 
frr_pthread_wait_running(struct frr_pthread * fpt)188 void frr_pthread_wait_running(struct frr_pthread *fpt)
189 {
190 	frr_with_mutex(fpt->running_cond_mtx) {
191 		while (!fpt->running)
192 			pthread_cond_wait(fpt->running_cond,
193 					  fpt->running_cond_mtx);
194 	}
195 }
196 
frr_pthread_notify_running(struct frr_pthread * fpt)197 void frr_pthread_notify_running(struct frr_pthread *fpt)
198 {
199 	frr_with_mutex(fpt->running_cond_mtx) {
200 		fpt->running = true;
201 		pthread_cond_signal(fpt->running_cond);
202 	}
203 }
204 
frr_pthread_stop(struct frr_pthread * fpt,void ** result)205 int frr_pthread_stop(struct frr_pthread *fpt, void **result)
206 {
207 	int ret = (*fpt->attr.stop)(fpt, result);
208 	memset(&fpt->thread, 0x00, sizeof(fpt->thread));
209 	return ret;
210 }
211 
frr_pthread_stop_all(void)212 void frr_pthread_stop_all(void)
213 {
214 	frr_with_mutex(&frr_pthread_list_mtx) {
215 		struct listnode *n;
216 		struct frr_pthread *fpt;
217 		for (ALL_LIST_ELEMENTS_RO(frr_pthread_list, n, fpt)) {
218 			if (atomic_load_explicit(&fpt->running,
219 						 memory_order_relaxed))
220 				frr_pthread_stop(fpt, NULL);
221 		}
222 	}
223 }
224 
225 /*
226  * ----------------------------------------------------------------------------
227  * Default Event Loop
228  * ----------------------------------------------------------------------------
229  */
230 
231 /* dummy task for sleeper pipe */
fpt_dummy(struct thread * thread)232 static int fpt_dummy(struct thread *thread)
233 {
234 	return 0;
235 }
236 
237 /* poison pill task to end event loop */
fpt_finish(struct thread * thread)238 static int fpt_finish(struct thread *thread)
239 {
240 	struct frr_pthread *fpt = THREAD_ARG(thread);
241 
242 	atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
243 	return 0;
244 }
245 
246 /* stop function, called from other threads to halt this one */
fpt_halt(struct frr_pthread * fpt,void ** res)247 static int fpt_halt(struct frr_pthread *fpt, void **res)
248 {
249 	thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
250 	pthread_join(fpt->thread, res);
251 
252 	return 0;
253 }
254 
255 /*
256  * Entry pthread function & main event loop.
257  *
258  * Upon thread start the following actions occur:
259  *
260  * - frr_pthread's owner field is set to pthread ID.
261  * - All signals are blocked (except for unblockable signals).
262  * - Pthread's threadmaster is set to never handle pending signals
263  * - Poker pipe for poll() is created and queued as I/O source
264  * - The frr_pthread->running_cond condition variable is signalled to indicate
265  *   that the previous actions have completed. It is not safe to assume any of
266  *   the above have occurred before receiving this signal.
267  *
268  * After initialization is completed, the event loop begins running. Each tick,
269  * the following actions are performed before running the usual event system
270  * tick function:
271  *
272  * - Verify that the running boolean is set
273  * - Verify that there are no pending cancellation requests
274  * - Verify that there are tasks scheduled
275  *
276  * So long as the conditions are met, the event loop tick is run and the
277  * returned task is executed.
278  *
279  * If any of these conditions are not met, the event loop exits, closes the
280  * pipes and dies without running any cleanup functions.
281  */
fpt_run(void * arg)282 static void *fpt_run(void *arg)
283 {
284 	struct frr_pthread *fpt = arg;
285 	fpt->master->owner = pthread_self();
286 
287 	zlog_tls_buffer_init();
288 
289 	int sleeper[2];
290 	pipe(sleeper);
291 	thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
292 
293 	fpt->master->handle_signals = false;
294 
295 	frr_pthread_set_name(fpt);
296 
297 	frr_pthread_notify_running(fpt);
298 
299 	struct thread task;
300 	while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
301 		pthread_testcancel();
302 		if (thread_fetch(fpt->master, &task)) {
303 			thread_call(&task);
304 		}
305 	}
306 
307 	close(sleeper[1]);
308 	close(sleeper[0]);
309 
310 	zlog_tls_buffer_fini();
311 
312 	return NULL;
313 }
314