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