]> git.proxmox.com Git - mirror_frr.git/blob - lib/frr_pthread.c
Merge branch 'master' into docs-user
[mirror_frr.git] / lib / frr_pthread.c
1 /*
2 * Utilities and interfaces for managing POSIX threads within FRR.
3 * Copyright (C) 2017 Cumulus Networks
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 unsigned int 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 .name = "Anonymous",
44 };
45
46 /* hash table to keep track of all frr_pthreads */
47 static struct hash *frr_pthread_hash;
48 static pthread_mutex_t frr_pthread_hash_mtx = PTHREAD_MUTEX_INITIALIZER;
49
50 /* frr_pthread_hash->hash_cmp */
51 static int frr_pthread_hash_cmp(const void *value1, const void *value2)
52 {
53 const struct frr_pthread *tq1 = value1;
54 const struct frr_pthread *tq2 = value2;
55
56 return (tq1->attr.id == tq2->attr.id);
57 }
58
59 /* frr_pthread_hash->hash_key */
60 static unsigned int frr_pthread_hash_key(void *value)
61 {
62 return ((struct frr_pthread *)value)->attr.id;
63 }
64
65 /* ------------------------------------------------------------------------ */
66
67 void frr_pthread_init()
68 {
69 pthread_mutex_lock(&frr_pthread_hash_mtx);
70 {
71 frr_pthread_hash = hash_create(frr_pthread_hash_key,
72 frr_pthread_hash_cmp, NULL);
73 }
74 pthread_mutex_unlock(&frr_pthread_hash_mtx);
75 }
76
77 void frr_pthread_finish()
78 {
79 pthread_mutex_lock(&frr_pthread_hash_mtx);
80 {
81 hash_clean(frr_pthread_hash,
82 (void (*)(void *))frr_pthread_destroy);
83 hash_free(frr_pthread_hash);
84 }
85 pthread_mutex_unlock(&frr_pthread_hash_mtx);
86 }
87
88 struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr)
89 {
90 static struct frr_pthread holder = {0};
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 /* create new thread master */
103 fpt->master = thread_master_create(attr->name);
104 /* set attributes */
105 fpt->attr = *attr;
106 if (attr == &frr_pthread_attr_default)
107 fpt->attr.id = frr_pthread_get_id();
108 /* initialize startup synchronization primitives */
109 fpt->running_cond_mtx = XCALLOC(
110 MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
111 fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM,
112 sizeof(pthread_cond_t));
113 pthread_mutex_init(fpt->running_cond_mtx, NULL);
114 pthread_cond_init(fpt->running_cond, NULL);
115
116 /* insert into global thread hash */
117 hash_get(frr_pthread_hash, fpt, hash_alloc_intern);
118 }
119 }
120 pthread_mutex_unlock(&frr_pthread_hash_mtx);
121
122 return fpt;
123 }
124
125 void frr_pthread_destroy(struct frr_pthread *fpt)
126 {
127 thread_master_free(fpt->master);
128
129 pthread_mutex_destroy(fpt->running_cond_mtx);
130 pthread_cond_destroy(fpt->running_cond);
131 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
132 XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
133 XFREE(MTYPE_FRR_PTHREAD, fpt);
134 }
135
136 struct frr_pthread *frr_pthread_get(unsigned int id)
137 {
138 static struct frr_pthread holder = {0};
139 struct frr_pthread *fpt;
140
141 pthread_mutex_lock(&frr_pthread_hash_mtx);
142 {
143 holder.attr.id = id;
144 fpt = hash_lookup(frr_pthread_hash, &holder);
145 }
146 pthread_mutex_unlock(&frr_pthread_hash_mtx);
147
148 return fpt;
149 }
150
151 int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
152 {
153 int ret;
154
155 ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt);
156
157 /*
158 * Per pthread_create(3), the contents of fpt->thread are undefined if
159 * pthread_create() did not succeed. Reset this value to zero.
160 */
161 if (ret < 0)
162 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
163
164 return ret;
165 }
166
167 void frr_pthread_wait_running(struct frr_pthread *fpt)
168 {
169 pthread_mutex_lock(fpt->running_cond_mtx);
170 {
171 while (!fpt->running)
172 pthread_cond_wait(fpt->running_cond,
173 fpt->running_cond_mtx);
174 }
175 pthread_mutex_unlock(fpt->running_cond_mtx);
176 }
177
178 void frr_pthread_notify_running(struct frr_pthread *fpt)
179 {
180 pthread_mutex_lock(fpt->running_cond_mtx);
181 {
182 fpt->running = true;
183 pthread_cond_signal(fpt->running_cond);
184 }
185 pthread_mutex_unlock(fpt->running_cond_mtx);
186 }
187
188 int frr_pthread_stop(struct frr_pthread *fpt, void **result)
189 {
190 int ret = (*fpt->attr.stop)(fpt, result);
191 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
192 return ret;
193 }
194
195 /*
196 * Callback for hash_iterate to stop all frr_pthread's.
197 */
198 static void frr_pthread_stop_all_iter(struct hash_backet *hb, void *arg)
199 {
200 struct frr_pthread *fpt = hb->data;
201 frr_pthread_stop(fpt, NULL);
202 }
203
204 void frr_pthread_stop_all()
205 {
206 pthread_mutex_lock(&frr_pthread_hash_mtx);
207 {
208 hash_iterate(frr_pthread_hash, frr_pthread_stop_all_iter, NULL);
209 }
210 pthread_mutex_unlock(&frr_pthread_hash_mtx);
211 }
212
213 unsigned int frr_pthread_get_id()
214 {
215 /* just a sanity check, this should never happen */
216 assert(next_id <= INT_MAX - 1);
217 return next_id++;
218 }
219
220 void frr_pthread_yield(void)
221 {
222 (void)sched_yield();
223 }
224
225 /*
226 * ----------------------------------------------------------------------------
227 * Default Event Loop
228 * ----------------------------------------------------------------------------
229 */
230
231 /* dummy task for sleeper pipe */
232 static int fpt_dummy(struct thread *thread)
233 {
234 return 0;
235 }
236
237 /* poison pill task to end event loop */
238 static int fpt_finish(struct thread *thread)
239 {
240 struct frr_pthread *fpt = THREAD_ARG(thread);
241 atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
242 return 0;
243 }
244
245 /* stop function, called from other threads to halt this one */
246 static int fpt_halt(struct frr_pthread *fpt, void **res)
247 {
248 thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
249 pthread_join(fpt->thread, res);
250 fpt = NULL;
251
252 return 0;
253 }
254
255 /* entry pthread function & main event loop */
256 static void *fpt_run(void *arg)
257 {
258 struct frr_pthread *fpt = arg;
259 fpt->master->owner = pthread_self();
260
261 int sleeper[2];
262 pipe(sleeper);
263 thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
264
265 fpt->master->handle_signals = false;
266
267 frr_pthread_notify_running(fpt);
268
269 struct thread task;
270 while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
271 if (thread_fetch(fpt->master, &task)) {
272 thread_call(&task);
273 }
274 }
275
276 close(sleeper[1]);
277 close(sleeper[0]);
278
279 return NULL;
280 }