]> git.proxmox.com Git - mirror_frr.git/blob - lib/frr_pthread.c
Merge pull request #1880 from pguibert6WIND/enforce_vrf_netns_enable
[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 #include <sched.h>
23
24 #include "frr_pthread.h"
25 #include "memory.h"
26 #include "hash.h"
27
28 DEFINE_MTYPE(LIB, FRR_PTHREAD, "FRR POSIX Thread");
29 DEFINE_MTYPE(LIB, PTHREAD_PRIM, "POSIX synchronization primitives");
30
31 /* id for next created pthread */
32 static _Atomic uint32_t next_id = 0;
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 .id = 0,
41 .start = fpt_run,
42 .stop = fpt_halt,
43 };
44
45 /* hash table to keep track of all frr_pthreads */
46 static struct hash *frr_pthread_hash;
47 static pthread_mutex_t frr_pthread_hash_mtx = PTHREAD_MUTEX_INITIALIZER;
48
49 /* frr_pthread_hash->hash_cmp */
50 static int frr_pthread_hash_cmp(const void *value1, const void *value2)
51 {
52 const struct frr_pthread *tq1 = value1;
53 const struct frr_pthread *tq2 = value2;
54
55 return (tq1->attr.id == tq2->attr.id);
56 }
57
58 /* frr_pthread_hash->hash_key */
59 static unsigned int frr_pthread_hash_key(void *value)
60 {
61 return ((struct frr_pthread *)value)->attr.id;
62 }
63
64 /* ------------------------------------------------------------------------ */
65
66 void frr_pthread_init()
67 {
68 pthread_mutex_lock(&frr_pthread_hash_mtx);
69 {
70 frr_pthread_hash = hash_create(frr_pthread_hash_key,
71 frr_pthread_hash_cmp, NULL);
72 }
73 pthread_mutex_unlock(&frr_pthread_hash_mtx);
74 }
75
76 void frr_pthread_finish()
77 {
78 pthread_mutex_lock(&frr_pthread_hash_mtx);
79 {
80 hash_clean(frr_pthread_hash,
81 (void (*)(void *))frr_pthread_destroy);
82 hash_free(frr_pthread_hash);
83 }
84 pthread_mutex_unlock(&frr_pthread_hash_mtx);
85 }
86
87 struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr,
88 const char *name)
89 {
90 static struct frr_pthread holder = {};
91 struct frr_pthread *fpt = NULL;
92
93 attr = attr ? attr : &frr_pthread_attr_default;
94
95 pthread_mutex_lock(&frr_pthread_hash_mtx);
96 {
97 holder.attr.id = attr->id;
98
99 if (!hash_lookup(frr_pthread_hash, &holder)) {
100 fpt = XCALLOC(MTYPE_FRR_PTHREAD,
101 sizeof(struct frr_pthread));
102 /* initialize mutex */
103 pthread_mutex_init(&fpt->mtx, NULL);
104 /* create new thread master */
105 fpt->master = thread_master_create(name);
106 /* set attributes */
107 fpt->attr = *attr;
108 name = (name ? name : "Anonymous thread");
109 fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
110 if (attr == &frr_pthread_attr_default)
111 fpt->attr.id = frr_pthread_get_id();
112 /* initialize startup synchronization primitives */
113 fpt->running_cond_mtx = XCALLOC(
114 MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
115 fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM,
116 sizeof(pthread_cond_t));
117 pthread_mutex_init(fpt->running_cond_mtx, NULL);
118 pthread_cond_init(fpt->running_cond, NULL);
119
120 /* insert into global thread hash */
121 hash_get(frr_pthread_hash, fpt, hash_alloc_intern);
122 }
123 }
124 pthread_mutex_unlock(&frr_pthread_hash_mtx);
125
126 return fpt;
127 }
128
129 void frr_pthread_destroy(struct frr_pthread *fpt)
130 {
131 thread_master_free(fpt->master);
132
133 pthread_mutex_destroy(&fpt->mtx);
134 pthread_mutex_destroy(fpt->running_cond_mtx);
135 pthread_cond_destroy(fpt->running_cond);
136 if (fpt->name)
137 XFREE(MTYPE_FRR_PTHREAD, fpt->name);
138 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
139 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
140 XFREE(MTYPE_FRR_PTHREAD, fpt);
141 }
142
143 void frr_pthread_set_name(struct frr_pthread *fpt, const char *name)
144 {
145 pthread_mutex_lock(&fpt->mtx);
146 {
147 if (fpt->name)
148 XFREE(MTYPE_FRR_PTHREAD, fpt->name);
149 fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
150 }
151 pthread_mutex_unlock(&fpt->mtx);
152 thread_master_set_name(fpt->master, name);
153 }
154
155 struct frr_pthread *frr_pthread_get(uint32_t id)
156 {
157 static struct frr_pthread holder = {};
158 struct frr_pthread *fpt;
159
160 pthread_mutex_lock(&frr_pthread_hash_mtx);
161 {
162 holder.attr.id = id;
163 fpt = hash_lookup(frr_pthread_hash, &holder);
164 }
165 pthread_mutex_unlock(&frr_pthread_hash_mtx);
166
167 return fpt;
168 }
169
170 int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
171 {
172 int ret;
173
174 ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt);
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 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
182
183 return ret;
184 }
185
186 void frr_pthread_wait_running(struct frr_pthread *fpt)
187 {
188 pthread_mutex_lock(fpt->running_cond_mtx);
189 {
190 while (!fpt->running)
191 pthread_cond_wait(fpt->running_cond,
192 fpt->running_cond_mtx);
193 }
194 pthread_mutex_unlock(fpt->running_cond_mtx);
195 }
196
197 void frr_pthread_notify_running(struct frr_pthread *fpt)
198 {
199 pthread_mutex_lock(fpt->running_cond_mtx);
200 {
201 fpt->running = true;
202 pthread_cond_signal(fpt->running_cond);
203 }
204 pthread_mutex_unlock(fpt->running_cond_mtx);
205 }
206
207 int frr_pthread_stop(struct frr_pthread *fpt, void **result)
208 {
209 int ret = (*fpt->attr.stop)(fpt, result);
210 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
211 return ret;
212 }
213
214 /*
215 * Callback for hash_iterate to stop all frr_pthread's.
216 */
217 static void frr_pthread_stop_all_iter(struct hash_backet *hb, void *arg)
218 {
219 struct frr_pthread *fpt = hb->data;
220 frr_pthread_stop(fpt, NULL);
221 }
222
223 void frr_pthread_stop_all()
224 {
225 pthread_mutex_lock(&frr_pthread_hash_mtx);
226 {
227 hash_iterate(frr_pthread_hash, frr_pthread_stop_all_iter, NULL);
228 }
229 pthread_mutex_unlock(&frr_pthread_hash_mtx);
230 }
231
232 uint32_t frr_pthread_get_id(void)
233 {
234 _Atomic uint32_t nxid;
235 nxid = atomic_fetch_add_explicit(&next_id, 1, memory_order_seq_cst);
236 /* just a sanity check, this should never happen */
237 assert(nxid <= (UINT32_MAX - 1));
238 return nxid;
239 }
240
241 void frr_pthread_yield(void)
242 {
243 (void)sched_yield();
244 }
245
246 /*
247 * ----------------------------------------------------------------------------
248 * Default Event Loop
249 * ----------------------------------------------------------------------------
250 */
251
252 /* dummy task for sleeper pipe */
253 static int fpt_dummy(struct thread *thread)
254 {
255 return 0;
256 }
257
258 /* poison pill task to end event loop */
259 static int fpt_finish(struct thread *thread)
260 {
261 struct frr_pthread *fpt = THREAD_ARG(thread);
262
263 atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
264 return 0;
265 }
266
267 /* stop function, called from other threads to halt this one */
268 static int fpt_halt(struct frr_pthread *fpt, void **res)
269 {
270 thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
271 pthread_join(fpt->thread, res);
272 fpt = NULL;
273
274 return 0;
275 }
276
277 /* entry pthread function & main event loop */
278 static void *fpt_run(void *arg)
279 {
280 struct frr_pthread *fpt = arg;
281 fpt->master->owner = pthread_self();
282
283 int sleeper[2];
284 pipe(sleeper);
285 thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
286
287 fpt->master->handle_signals = false;
288
289 frr_pthread_notify_running(fpt);
290
291 struct thread task;
292 while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
293 if (thread_fetch(fpt->master, &task)) {
294 thread_call(&task);
295 }
296 }
297
298 close(sleeper[1]);
299 close(sleeper[0]);
300
301 return NULL;
302 }