2 * Utilities and interfaces for managing POSIX threads within FRR.
3 * Copyright (C) 2017 Cumulus Networks, Inc.
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.
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.
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
22 #ifdef HAVE_PTHREAD_NP_H
23 #include <pthread_np.h>
27 #include "frr_pthread.h"
31 DEFINE_MTYPE(LIB
, FRR_PTHREAD
, "FRR POSIX Thread");
32 DEFINE_MTYPE(LIB
, PTHREAD_PRIM
, "POSIX synchronization primitives");
34 /* id for next created pthread */
35 static _Atomic
uint32_t next_id
= 0;
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
);
41 /* default frr_pthread attributes */
42 struct frr_pthread_attr frr_pthread_attr_default
= {
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
;
52 /* frr_pthread_hash->hash_cmp */
53 static int frr_pthread_hash_cmp(const void *value1
, const void *value2
)
55 const struct frr_pthread
*tq1
= value1
;
56 const struct frr_pthread
*tq2
= value2
;
58 return (tq1
->attr
.id
== tq2
->attr
.id
);
61 /* frr_pthread_hash->hash_key */
62 static unsigned int frr_pthread_hash_key(void *value
)
64 return ((struct frr_pthread
*)value
)->attr
.id
;
67 /* ------------------------------------------------------------------------ */
69 void frr_pthread_init()
71 pthread_mutex_lock(&frr_pthread_hash_mtx
);
73 frr_pthread_hash
= hash_create(frr_pthread_hash_key
,
74 frr_pthread_hash_cmp
, NULL
);
76 pthread_mutex_unlock(&frr_pthread_hash_mtx
);
79 void frr_pthread_finish()
81 pthread_mutex_lock(&frr_pthread_hash_mtx
);
83 hash_clean(frr_pthread_hash
,
84 (void (*)(void *))frr_pthread_destroy
);
85 hash_free(frr_pthread_hash
);
87 pthread_mutex_unlock(&frr_pthread_hash_mtx
);
90 struct frr_pthread
*frr_pthread_new(struct frr_pthread_attr
*attr
,
91 const char *name
, const char *os_name
)
93 static struct frr_pthread holder
= {};
94 struct frr_pthread
*fpt
= NULL
;
96 attr
= attr
? attr
: &frr_pthread_attr_default
;
98 pthread_mutex_lock(&frr_pthread_hash_mtx
);
100 holder
.attr
.id
= attr
->id
;
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
);
111 name
= (name
? name
: "Anonymous thread");
112 fpt
->name
= XSTRDUP(MTYPE_FRR_PTHREAD
, name
);
114 snprintf(fpt
->os_name
, OS_THREAD_NAMELEN
,
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
);
126 /* insert into global thread hash */
127 hash_get(frr_pthread_hash
, fpt
, hash_alloc_intern
);
130 pthread_mutex_unlock(&frr_pthread_hash_mtx
);
135 void frr_pthread_destroy(struct frr_pthread
*fpt
)
137 thread_master_free(fpt
->master
);
139 pthread_mutex_destroy(&fpt
->mtx
);
140 pthread_mutex_destroy(fpt
->running_cond_mtx
);
141 pthread_cond_destroy(fpt
->running_cond
);
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
);
149 int frr_pthread_set_name(struct frr_pthread
*fpt
, const char *name
,
155 pthread_mutex_lock(&fpt
->mtx
);
158 XFREE(MTYPE_FRR_PTHREAD
, fpt
->name
);
159 fpt
->name
= XSTRDUP(MTYPE_FRR_PTHREAD
, name
);
161 pthread_mutex_unlock(&fpt
->mtx
);
162 thread_master_set_name(fpt
->master
, 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
171 ret
= pthread_setname_np(fpt
->thread
, fpt
->os_name
);
173 ret
= pthread_setname_np(fpt
->thread
, fpt
->os_name
, NULL
);
175 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
176 pthread_set_name_np(fpt
->thread
, fpt
->os_name
);
183 struct frr_pthread
*frr_pthread_get(uint32_t id
)
185 static struct frr_pthread holder
= {};
186 struct frr_pthread
*fpt
;
188 pthread_mutex_lock(&frr_pthread_hash_mtx
);
191 fpt
= hash_lookup(frr_pthread_hash
, &holder
);
193 pthread_mutex_unlock(&frr_pthread_hash_mtx
);
198 int frr_pthread_run(struct frr_pthread
*fpt
, const pthread_attr_t
*attr
)
202 ret
= pthread_create(&fpt
->thread
, attr
, fpt
->attr
.start
, fpt
);
205 * Per pthread_create(3), the contents of fpt->thread are undefined if
206 * pthread_create() did not succeed. Reset this value to zero.
209 memset(&fpt
->thread
, 0x00, sizeof(fpt
->thread
));
214 void frr_pthread_wait_running(struct frr_pthread
*fpt
)
216 pthread_mutex_lock(fpt
->running_cond_mtx
);
218 while (!fpt
->running
)
219 pthread_cond_wait(fpt
->running_cond
,
220 fpt
->running_cond_mtx
);
222 pthread_mutex_unlock(fpt
->running_cond_mtx
);
225 void frr_pthread_notify_running(struct frr_pthread
*fpt
)
227 pthread_mutex_lock(fpt
->running_cond_mtx
);
230 pthread_cond_signal(fpt
->running_cond
);
232 pthread_mutex_unlock(fpt
->running_cond_mtx
);
235 int frr_pthread_stop(struct frr_pthread
*fpt
, void **result
)
237 int ret
= (*fpt
->attr
.stop
)(fpt
, result
);
238 memset(&fpt
->thread
, 0x00, sizeof(fpt
->thread
));
243 * Callback for hash_iterate to stop all frr_pthread's.
245 static void frr_pthread_stop_all_iter(struct hash_backet
*hb
, void *arg
)
247 struct frr_pthread
*fpt
= hb
->data
;
248 frr_pthread_stop(fpt
, NULL
);
251 void frr_pthread_stop_all()
253 pthread_mutex_lock(&frr_pthread_hash_mtx
);
255 hash_iterate(frr_pthread_hash
, frr_pthread_stop_all_iter
, NULL
);
257 pthread_mutex_unlock(&frr_pthread_hash_mtx
);
260 uint32_t frr_pthread_get_id(void)
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));
269 void frr_pthread_yield(void)
275 * ----------------------------------------------------------------------------
277 * ----------------------------------------------------------------------------
280 /* dummy task for sleeper pipe */
281 static int fpt_dummy(struct thread
*thread
)
286 /* poison pill task to end event loop */
287 static int fpt_finish(struct thread
*thread
)
289 struct frr_pthread
*fpt
= THREAD_ARG(thread
);
291 atomic_store_explicit(&fpt
->running
, false, memory_order_relaxed
);
295 /* stop function, called from other threads to halt this one */
296 static int fpt_halt(struct frr_pthread
*fpt
, void **res
)
298 thread_add_event(fpt
->master
, &fpt_finish
, fpt
, 0, NULL
);
299 pthread_join(fpt
->thread
, res
);
305 * Entry pthread function & main event loop.
307 * Upon thread start the following actions occur:
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.
317 * After initialization is completed, the event loop begins running. Each tick,
318 * the following actions are performed before running the usual event system
321 * - Verify that the running boolean is set
322 * - Verify that there are no pending cancellation requests
323 * - Verify that there are tasks scheduled
325 * So long as the conditions are met, the event loop tick is run and the
326 * returned task is executed.
328 * If any of these conditions are not met, the event loop exits, closes the
329 * pipes and dies without running any cleanup functions.
331 static void *fpt_run(void *arg
)
333 struct frr_pthread
*fpt
= arg
;
334 fpt
->master
->owner
= pthread_self();
338 thread_add_read(fpt
->master
, &fpt_dummy
, NULL
, sleeper
[0], NULL
);
340 fpt
->master
->handle_signals
= false;
343 frr_pthread_set_name(fpt
, NULL
, fpt
->os_name
);
345 frr_pthread_notify_running(fpt
);
348 while (atomic_load_explicit(&fpt
->running
, memory_order_relaxed
)) {
349 pthread_testcancel();
350 if (thread_fetch(fpt
->master
, &task
)) {