]> git.proxmox.com Git - mirror_frr.git/blob - lib/frr_pthread.c
Merge pull request #2985 from patrasar/Fix_1636
[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 "hash.h"
30
31 DEFINE_MTYPE(LIB, FRR_PTHREAD, "FRR POSIX Thread");
32 DEFINE_MTYPE(LIB, PTHREAD_PRIM, "POSIX synchronization primitives");
33
34 /* id for next created pthread */
35 static _Atomic uint32_t next_id = 0;
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 /* default frr_pthread attributes */
42 struct frr_pthread_attr frr_pthread_attr_default = {
43 .id = 0,
44 .start = fpt_run,
45 .stop = fpt_halt,
46 };
47
48 /* hash table to keep track of all frr_pthreads */
49 static struct hash *frr_pthread_hash;
50 static pthread_mutex_t frr_pthread_hash_mtx = PTHREAD_MUTEX_INITIALIZER;
51
52 /* frr_pthread_hash->hash_cmp */
53 static int frr_pthread_hash_cmp(const void *value1, const void *value2)
54 {
55 const struct frr_pthread *tq1 = value1;
56 const struct frr_pthread *tq2 = value2;
57
58 return (tq1->attr.id == tq2->attr.id);
59 }
60
61 /* frr_pthread_hash->hash_key */
62 static unsigned int frr_pthread_hash_key(void *value)
63 {
64 return ((struct frr_pthread *)value)->attr.id;
65 }
66
67 /* ------------------------------------------------------------------------ */
68
69 void frr_pthread_init()
70 {
71 pthread_mutex_lock(&frr_pthread_hash_mtx);
72 {
73 frr_pthread_hash = hash_create(frr_pthread_hash_key,
74 frr_pthread_hash_cmp, NULL);
75 }
76 pthread_mutex_unlock(&frr_pthread_hash_mtx);
77 }
78
79 void frr_pthread_finish()
80 {
81 pthread_mutex_lock(&frr_pthread_hash_mtx);
82 {
83 hash_clean(frr_pthread_hash,
84 (void (*)(void *))frr_pthread_destroy);
85 hash_free(frr_pthread_hash);
86 }
87 pthread_mutex_unlock(&frr_pthread_hash_mtx);
88 }
89
90 struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr,
91 const char *name, const char *os_name)
92 {
93 static struct frr_pthread holder = {};
94 struct frr_pthread *fpt = NULL;
95
96 attr = attr ? attr : &frr_pthread_attr_default;
97
98 pthread_mutex_lock(&frr_pthread_hash_mtx);
99 {
100 holder.attr.id = attr->id;
101
102 if (!hash_lookup(frr_pthread_hash, &holder)) {
103 fpt = XCALLOC(MTYPE_FRR_PTHREAD,
104 sizeof(struct frr_pthread));
105 /* initialize mutex */
106 pthread_mutex_init(&fpt->mtx, NULL);
107 /* create new thread master */
108 fpt->master = thread_master_create(name);
109 /* set attributes */
110 fpt->attr = *attr;
111 name = (name ? name : "Anonymous thread");
112 fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
113 if (os_name)
114 snprintf(fpt->os_name, OS_THREAD_NAMELEN,
115 "%s", os_name);
116 if (attr == &frr_pthread_attr_default)
117 fpt->attr.id = frr_pthread_get_id();
118 /* initialize startup synchronization primitives */
119 fpt->running_cond_mtx = XCALLOC(
120 MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
121 fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM,
122 sizeof(pthread_cond_t));
123 pthread_mutex_init(fpt->running_cond_mtx, NULL);
124 pthread_cond_init(fpt->running_cond, NULL);
125
126 /* insert into global thread hash */
127 hash_get(frr_pthread_hash, fpt, hash_alloc_intern);
128 }
129 }
130 pthread_mutex_unlock(&frr_pthread_hash_mtx);
131
132 return fpt;
133 }
134
135 void frr_pthread_destroy(struct frr_pthread *fpt)
136 {
137 thread_master_free(fpt->master);
138
139 pthread_mutex_destroy(&fpt->mtx);
140 pthread_mutex_destroy(fpt->running_cond_mtx);
141 pthread_cond_destroy(fpt->running_cond);
142 if (fpt->name)
143 XFREE(MTYPE_FRR_PTHREAD, fpt->name);
144 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
145 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
146 XFREE(MTYPE_FRR_PTHREAD, fpt);
147 }
148
149 int frr_pthread_set_name(struct frr_pthread *fpt, const char *name,
150 const char *os_name)
151 {
152 int ret = 0;
153
154 if (name) {
155 pthread_mutex_lock(&fpt->mtx);
156 {
157 if (fpt->name)
158 XFREE(MTYPE_FRR_PTHREAD, fpt->name);
159 fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
160 }
161 pthread_mutex_unlock(&fpt->mtx);
162 thread_master_set_name(fpt->master, name);
163 }
164
165 if (os_name) {
166 pthread_mutex_lock(&fpt->mtx);
167 snprintf(fpt->os_name, OS_THREAD_NAMELEN, "%s", os_name);
168 pthread_mutex_unlock(&fpt->mtx);
169 #ifdef HAVE_PTHREAD_SETNAME_NP
170 # ifdef GNU_LINUX
171 ret = pthread_setname_np(fpt->thread, fpt->os_name);
172 # else /* NetBSD */
173 ret = pthread_setname_np(fpt->thread, fpt->os_name, NULL);
174 # endif
175 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
176 pthread_set_name_np(fpt->thread, fpt->os_name);
177 #endif
178 }
179
180 return ret;
181 }
182
183 struct frr_pthread *frr_pthread_get(uint32_t id)
184 {
185 static struct frr_pthread holder = {};
186 struct frr_pthread *fpt;
187
188 pthread_mutex_lock(&frr_pthread_hash_mtx);
189 {
190 holder.attr.id = id;
191 fpt = hash_lookup(frr_pthread_hash, &holder);
192 }
193 pthread_mutex_unlock(&frr_pthread_hash_mtx);
194
195 return fpt;
196 }
197
198 int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
199 {
200 int ret;
201
202 ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt);
203
204 /*
205 * Per pthread_create(3), the contents of fpt->thread are undefined if
206 * pthread_create() did not succeed. Reset this value to zero.
207 */
208 if (ret < 0)
209 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
210
211 return ret;
212 }
213
214 void frr_pthread_wait_running(struct frr_pthread *fpt)
215 {
216 pthread_mutex_lock(fpt->running_cond_mtx);
217 {
218 while (!fpt->running)
219 pthread_cond_wait(fpt->running_cond,
220 fpt->running_cond_mtx);
221 }
222 pthread_mutex_unlock(fpt->running_cond_mtx);
223 }
224
225 void frr_pthread_notify_running(struct frr_pthread *fpt)
226 {
227 pthread_mutex_lock(fpt->running_cond_mtx);
228 {
229 fpt->running = true;
230 pthread_cond_signal(fpt->running_cond);
231 }
232 pthread_mutex_unlock(fpt->running_cond_mtx);
233 }
234
235 int frr_pthread_stop(struct frr_pthread *fpt, void **result)
236 {
237 int ret = (*fpt->attr.stop)(fpt, result);
238 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
239 return ret;
240 }
241
242 /*
243 * Callback for hash_iterate to stop all frr_pthread's.
244 */
245 static void frr_pthread_stop_all_iter(struct hash_backet *hb, void *arg)
246 {
247 struct frr_pthread *fpt = hb->data;
248 frr_pthread_stop(fpt, NULL);
249 }
250
251 void frr_pthread_stop_all()
252 {
253 pthread_mutex_lock(&frr_pthread_hash_mtx);
254 {
255 hash_iterate(frr_pthread_hash, frr_pthread_stop_all_iter, NULL);
256 }
257 pthread_mutex_unlock(&frr_pthread_hash_mtx);
258 }
259
260 uint32_t frr_pthread_get_id(void)
261 {
262 _Atomic uint32_t nxid;
263 nxid = atomic_fetch_add_explicit(&next_id, 1, memory_order_seq_cst);
264 /* just a sanity check, this should never happen */
265 assert(nxid <= (UINT32_MAX - 1));
266 return nxid;
267 }
268
269 void frr_pthread_yield(void)
270 {
271 (void)sched_yield();
272 }
273
274 /*
275 * ----------------------------------------------------------------------------
276 * Default Event Loop
277 * ----------------------------------------------------------------------------
278 */
279
280 /* dummy task for sleeper pipe */
281 static int fpt_dummy(struct thread *thread)
282 {
283 return 0;
284 }
285
286 /* poison pill task to end event loop */
287 static int fpt_finish(struct thread *thread)
288 {
289 struct frr_pthread *fpt = THREAD_ARG(thread);
290
291 atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
292 return 0;
293 }
294
295 /* stop function, called from other threads to halt this one */
296 static int fpt_halt(struct frr_pthread *fpt, void **res)
297 {
298 thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
299 pthread_join(fpt->thread, res);
300
301 return 0;
302 }
303
304 /*
305 * Entry pthread function & main event loop.
306 *
307 * Upon thread start the following actions occur:
308 *
309 * - frr_pthread's owner field is set to pthread ID.
310 * - All signals are blocked (except for unblockable signals).
311 * - Pthread's threadmaster is set to never handle pending signals
312 * - Poker pipe for poll() is created and queued as I/O source
313 * - The frr_pthread->running_cond condition variable is signalled to indicate
314 * that the previous actions have completed. It is not safe to assume any of
315 * the above have occurred before receiving this signal.
316 *
317 * After initialization is completed, the event loop begins running. Each tick,
318 * the following actions are performed before running the usual event system
319 * tick function:
320 *
321 * - Verify that the running boolean is set
322 * - Verify that there are no pending cancellation requests
323 * - Verify that there are tasks scheduled
324 *
325 * So long as the conditions are met, the event loop tick is run and the
326 * returned task is executed.
327 *
328 * If any of these conditions are not met, the event loop exits, closes the
329 * pipes and dies without running any cleanup functions.
330 */
331 static void *fpt_run(void *arg)
332 {
333 struct frr_pthread *fpt = arg;
334 fpt->master->owner = pthread_self();
335
336 int sleeper[2];
337 pipe(sleeper);
338 thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
339
340 fpt->master->handle_signals = false;
341
342 if (fpt->os_name[0])
343 frr_pthread_set_name(fpt, NULL, fpt->os_name);
344
345 frr_pthread_notify_running(fpt);
346
347 struct thread task;
348 while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
349 pthread_testcancel();
350 if (thread_fetch(fpt->master, &task)) {
351 thread_call(&task);
352 }
353 }
354
355 close(sleeper[1]);
356 close(sleeper[0]);
357
358 return NULL;
359 }