]> git.proxmox.com Git - mirror_frr.git/blame - lib/frr_pthread.c
Merge pull request #2700 from sworleys/Netlink-Prefix-Len-Check
[mirror_frr.git] / lib / frr_pthread.c
CommitLineData
98f14af8 1/*
a45dc974 2 * Utilities and interfaces for managing POSIX threads within FRR.
d8a8a8de 3 * Copyright (C) 2017 Cumulus Networks, Inc.
896014f4
DL
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
98f14af8
QY
18 */
19
20#include <zebra.h>
21#include <pthread.h>
b2140cb7 22#include <sched.h>
98f14af8
QY
23
24#include "frr_pthread.h"
25#include "memory.h"
26#include "hash.h"
27
a45dc974 28DEFINE_MTYPE(LIB, FRR_PTHREAD, "FRR POSIX Thread");
0bbb9e72 29DEFINE_MTYPE(LIB, PTHREAD_PRIM, "POSIX synchronization primitives");
98f14af8 30
a45dc974 31/* id for next created pthread */
d8a8a8de 32static _Atomic uint32_t next_id = 0;
98f14af8 33
a45dc974
QY
34/* default frr_pthread start/stop routine prototypes */
35static void *fpt_run(void *arg);
36static int fpt_halt(struct frr_pthread *fpt, void **res);
98f14af8 37
a45dc974
QY
38/* default frr_pthread attributes */
39struct frr_pthread_attr frr_pthread_attr_default = {
40 .id = 0,
41 .start = fpt_run,
42 .stop = fpt_halt,
a45dc974
QY
43};
44
45/* hash table to keep track of all frr_pthreads */
46static struct hash *frr_pthread_hash;
47static pthread_mutex_t frr_pthread_hash_mtx = PTHREAD_MUTEX_INITIALIZER;
48
49/* frr_pthread_hash->hash_cmp */
50static int frr_pthread_hash_cmp(const void *value1, const void *value2)
98f14af8 51{
d62a17ae 52 const struct frr_pthread *tq1 = value1;
53 const struct frr_pthread *tq2 = value2;
98f14af8 54
a45dc974 55 return (tq1->attr.id == tq2->attr.id);
98f14af8
QY
56}
57
a45dc974
QY
58/* frr_pthread_hash->hash_key */
59static unsigned int frr_pthread_hash_key(void *value)
98f14af8 60{
a45dc974 61 return ((struct frr_pthread *)value)->attr.id;
98f14af8 62}
a45dc974 63
98f14af8
QY
64/* ------------------------------------------------------------------------ */
65
66void frr_pthread_init()
67{
a45dc974 68 pthread_mutex_lock(&frr_pthread_hash_mtx);
d62a17ae 69 {
a45dc974
QY
70 frr_pthread_hash = hash_create(frr_pthread_hash_key,
71 frr_pthread_hash_cmp, NULL);
d62a17ae 72 }
a45dc974 73 pthread_mutex_unlock(&frr_pthread_hash_mtx);
98f14af8
QY
74}
75
76void frr_pthread_finish()
77{
a45dc974 78 pthread_mutex_lock(&frr_pthread_hash_mtx);
d62a17ae 79 {
a45dc974 80 hash_clean(frr_pthread_hash,
d62a17ae 81 (void (*)(void *))frr_pthread_destroy);
a45dc974 82 hash_free(frr_pthread_hash);
d62a17ae 83 }
a45dc974 84 pthread_mutex_unlock(&frr_pthread_hash_mtx);
98f14af8
QY
85}
86
d8a8a8de
QY
87struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr,
88 const char *name)
98f14af8 89{
d8a8a8de 90 static struct frr_pthread holder = {};
d62a17ae 91 struct frr_pthread *fpt = NULL;
92
a45dc974
QY
93 attr = attr ? attr : &frr_pthread_attr_default;
94
95 pthread_mutex_lock(&frr_pthread_hash_mtx);
d62a17ae 96 {
a45dc974
QY
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));
d8a8a8de
QY
102 /* initialize mutex */
103 pthread_mutex_init(&fpt->mtx, NULL);
a45dc974 104 /* create new thread master */
d8a8a8de 105 fpt->master = thread_master_create(name);
a45dc974
QY
106 /* set attributes */
107 fpt->attr = *attr;
d8a8a8de
QY
108 name = (name ? name : "Anonymous thread");
109 fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
a45dc974
QY
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);
d62a17ae 122 }
123 }
a45dc974 124 pthread_mutex_unlock(&frr_pthread_hash_mtx);
d62a17ae 125
126 return fpt;
98f14af8
QY
127}
128
129void frr_pthread_destroy(struct frr_pthread *fpt)
130{
d62a17ae 131 thread_master_free(fpt->master);
a45dc974 132
d8a8a8de 133 pthread_mutex_destroy(&fpt->mtx);
a45dc974
QY
134 pthread_mutex_destroy(fpt->running_cond_mtx);
135 pthread_cond_destroy(fpt->running_cond);
d8a8a8de
QY
136 if (fpt->name)
137 XFREE(MTYPE_FRR_PTHREAD, fpt->name);
a45dc974
QY
138 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
139 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
d62a17ae 140 XFREE(MTYPE_FRR_PTHREAD, fpt);
98f14af8
QY
141}
142
d8a8a8de
QY
143void 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
b1087f35 155struct frr_pthread *frr_pthread_get(uint32_t id)
98f14af8 156{
d8a8a8de 157 static struct frr_pthread holder = {};
d62a17ae 158 struct frr_pthread *fpt;
98f14af8 159
a45dc974 160 pthread_mutex_lock(&frr_pthread_hash_mtx);
d62a17ae 161 {
a45dc974
QY
162 holder.attr.id = id;
163 fpt = hash_lookup(frr_pthread_hash, &holder);
d62a17ae 164 }
a45dc974 165 pthread_mutex_unlock(&frr_pthread_hash_mtx);
98f14af8 166
d62a17ae 167 return fpt;
98f14af8
QY
168}
169
a45dc974 170int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
98f14af8 171{
d62a17ae 172 int ret;
98f14af8 173
a45dc974 174 ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt);
98f14af8 175
a45dc974
QY
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 */
d62a17ae 180 if (ret < 0)
181 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
98f14af8 182
d62a17ae 183 return ret;
98f14af8
QY
184}
185
a45dc974 186void frr_pthread_wait_running(struct frr_pthread *fpt)
98f14af8 187{
a45dc974
QY
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);
98f14af8
QY
195}
196
a45dc974 197void frr_pthread_notify_running(struct frr_pthread *fpt)
98f14af8 198{
a45dc974
QY
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);
98f14af8
QY
205}
206
a45dc974
QY
207int 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/*
98f14af8
QY
215 * Callback for hash_iterate to stop all frr_pthread's.
216 */
217static void frr_pthread_stop_all_iter(struct hash_backet *hb, void *arg)
218{
d62a17ae 219 struct frr_pthread *fpt = hb->data;
a45dc974 220 frr_pthread_stop(fpt, NULL);
98f14af8
QY
221}
222
223void frr_pthread_stop_all()
224{
a45dc974 225 pthread_mutex_lock(&frr_pthread_hash_mtx);
d62a17ae 226 {
a45dc974 227 hash_iterate(frr_pthread_hash, frr_pthread_stop_all_iter, NULL);
d62a17ae 228 }
a45dc974 229 pthread_mutex_unlock(&frr_pthread_hash_mtx);
98f14af8
QY
230}
231
985e36a6 232uint32_t frr_pthread_get_id(void)
98f14af8 233{
d8a8a8de
QY
234 _Atomic uint32_t nxid;
235 nxid = atomic_fetch_add_explicit(&next_id, 1, memory_order_seq_cst);
a45dc974 236 /* just a sanity check, this should never happen */
b1087f35 237 assert(nxid <= (UINT32_MAX - 1));
d8a8a8de 238 return nxid;
98f14af8 239}
b2140cb7
DS
240
241void frr_pthread_yield(void)
242{
243 (void)sched_yield();
244}
a45dc974
QY
245
246/*
247 * ----------------------------------------------------------------------------
248 * Default Event Loop
249 * ----------------------------------------------------------------------------
250 */
251
252/* dummy task for sleeper pipe */
253static int fpt_dummy(struct thread *thread)
254{
255 return 0;
256}
257
258/* poison pill task to end event loop */
259static int fpt_finish(struct thread *thread)
260{
261 struct frr_pthread *fpt = THREAD_ARG(thread);
985e36a6 262
a45dc974
QY
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 */
268static 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);
a45dc974
QY
272
273 return 0;
274}
275
276/* entry pthread function & main event loop */
277static void *fpt_run(void *arg)
278{
279 struct frr_pthread *fpt = arg;
280 fpt->master->owner = pthread_self();
281
282 int sleeper[2];
283 pipe(sleeper);
284 thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
285
286 fpt->master->handle_signals = false;
287
288 frr_pthread_notify_running(fpt);
289
290 struct thread task;
291 while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
292 if (thread_fetch(fpt->master, &task)) {
293 thread_call(&task);
294 }
295 }
296
297 close(sleeper[1]);
298 close(sleeper[0]);
299
300 return NULL;
301}