]>
Commit | Line | Data |
---|---|---|
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 | 28 | DEFINE_MTYPE(LIB, FRR_PTHREAD, "FRR POSIX Thread"); |
0bbb9e72 | 29 | DEFINE_MTYPE(LIB, PTHREAD_PRIM, "POSIX synchronization primitives"); |
98f14af8 | 30 | |
a45dc974 | 31 | /* id for next created pthread */ |
d8a8a8de | 32 | static _Atomic uint32_t next_id = 0; |
98f14af8 | 33 | |
a45dc974 QY |
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); | |
98f14af8 | 37 | |
a45dc974 QY |
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, | |
a45dc974 QY |
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) | |
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 */ |
59 | static 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 | ||
66 | void 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 | ||
76 | void 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 |
87 | struct 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 | ||
129 | void 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 |
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 | ||
b1087f35 | 155 | struct 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 | 170 | int 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 | 186 | void 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 | 197 | void 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 |
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 | /* | |
98f14af8 QY |
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 | { | |
d62a17ae | 219 | struct frr_pthread *fpt = hb->data; |
a45dc974 | 220 | frr_pthread_stop(fpt, NULL); |
98f14af8 QY |
221 | } |
222 | ||
223 | void 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 | 232 | uint32_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 | |
241 | void 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 */ | |
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); | |
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 */ | |
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 | } |