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"
32 DEFINE_MTYPE_STATIC(LIB
, FRR_PTHREAD
, "FRR POSIX Thread")
33 DEFINE_MTYPE_STATIC(LIB
, PTHREAD_PRIM
, "POSIX sync primitives")
35 /* default frr_pthread start/stop routine prototypes */
36 static void *fpt_run(void *arg
);
37 static int fpt_halt(struct frr_pthread
*fpt
, void **res
);
40 static void frr_pthread_destroy_nolock(struct frr_pthread
*fpt
);
42 /* default frr_pthread attributes */
43 const struct frr_pthread_attr frr_pthread_attr_default
= {
48 /* list to keep track of all frr_pthreads */
49 static pthread_mutex_t frr_pthread_list_mtx
= PTHREAD_MUTEX_INITIALIZER
;
50 static struct list
*frr_pthread_list
;
52 /* ------------------------------------------------------------------------ */
54 void frr_pthread_init(void)
56 frr_with_mutex(&frr_pthread_list_mtx
) {
57 frr_pthread_list
= list_new();
61 void frr_pthread_finish(void)
63 frr_pthread_stop_all();
65 frr_with_mutex(&frr_pthread_list_mtx
) {
66 struct listnode
*n
, *nn
;
67 struct frr_pthread
*fpt
;
69 for (ALL_LIST_ELEMENTS(frr_pthread_list
, n
, nn
, fpt
)) {
70 listnode_delete(frr_pthread_list
, fpt
);
71 frr_pthread_destroy_nolock(fpt
);
74 list_delete(&frr_pthread_list
);
78 struct frr_pthread
*frr_pthread_new(const struct frr_pthread_attr
*attr
,
79 const char *name
, const char *os_name
)
81 struct frr_pthread
*fpt
= NULL
;
83 attr
= attr
? attr
: &frr_pthread_attr_default
;
85 fpt
= XCALLOC(MTYPE_FRR_PTHREAD
, sizeof(struct frr_pthread
));
86 /* initialize mutex */
87 pthread_mutex_init(&fpt
->mtx
, NULL
);
88 /* create new thread master */
89 fpt
->master
= thread_master_create(name
);
92 name
= (name
? name
: "Anonymous thread");
93 fpt
->name
= XSTRDUP(MTYPE_FRR_PTHREAD
, name
);
95 strlcpy(fpt
->os_name
, os_name
, OS_THREAD_NAMELEN
);
97 strlcpy(fpt
->os_name
, name
, OS_THREAD_NAMELEN
);
98 /* initialize startup synchronization primitives */
99 fpt
->running_cond_mtx
= XCALLOC(
100 MTYPE_PTHREAD_PRIM
, sizeof(pthread_mutex_t
));
101 fpt
->running_cond
= XCALLOC(MTYPE_PTHREAD_PRIM
,
102 sizeof(pthread_cond_t
));
103 pthread_mutex_init(fpt
->running_cond_mtx
, NULL
);
104 pthread_cond_init(fpt
->running_cond
, NULL
);
106 frr_with_mutex(&frr_pthread_list_mtx
) {
107 listnode_add(frr_pthread_list
, fpt
);
113 static void frr_pthread_destroy_nolock(struct frr_pthread
*fpt
)
115 thread_master_free(fpt
->master
);
116 pthread_mutex_destroy(&fpt
->mtx
);
117 pthread_mutex_destroy(fpt
->running_cond_mtx
);
118 pthread_cond_destroy(fpt
->running_cond
);
119 XFREE(MTYPE_FRR_PTHREAD
, fpt
->name
);
120 XFREE(MTYPE_PTHREAD_PRIM
, fpt
->running_cond_mtx
);
121 XFREE(MTYPE_PTHREAD_PRIM
, fpt
->running_cond
);
122 XFREE(MTYPE_FRR_PTHREAD
, fpt
);
125 void frr_pthread_destroy(struct frr_pthread
*fpt
)
127 frr_with_mutex(&frr_pthread_list_mtx
) {
128 listnode_delete(frr_pthread_list
, fpt
);
131 frr_pthread_destroy_nolock(fpt
);
134 int frr_pthread_set_name(struct frr_pthread
*fpt
)
138 #ifdef HAVE_PTHREAD_SETNAME_NP
140 ret
= pthread_setname_np(fpt
->thread
, fpt
->os_name
);
141 # elif defined(__NetBSD__)
142 ret
= pthread_setname_np(fpt
->thread
, fpt
->os_name
, NULL
);
144 #elif defined(HAVE_PTHREAD_SET_NAME_NP)
145 pthread_set_name_np(fpt
->thread
, fpt
->os_name
);
151 static void *frr_pthread_inner(void *arg
)
153 struct frr_pthread
*fpt
= arg
;
155 rcu_thread_start(fpt
->rcu_thread
);
156 return fpt
->attr
.start(fpt
);
159 int frr_pthread_run(struct frr_pthread
*fpt
, const pthread_attr_t
*attr
)
163 fpt
->rcu_thread
= rcu_thread_prepare();
164 ret
= pthread_create(&fpt
->thread
, attr
, frr_pthread_inner
, fpt
);
167 * Per pthread_create(3), the contents of fpt->thread are undefined if
168 * pthread_create() did not succeed. Reset this value to zero.
171 rcu_thread_unprepare(fpt
->rcu_thread
);
172 memset(&fpt
->thread
, 0x00, sizeof(fpt
->thread
));
178 void frr_pthread_wait_running(struct frr_pthread
*fpt
)
180 frr_with_mutex(fpt
->running_cond_mtx
) {
181 while (!fpt
->running
)
182 pthread_cond_wait(fpt
->running_cond
,
183 fpt
->running_cond_mtx
);
187 void frr_pthread_notify_running(struct frr_pthread
*fpt
)
189 frr_with_mutex(fpt
->running_cond_mtx
) {
191 pthread_cond_signal(fpt
->running_cond
);
195 int frr_pthread_stop(struct frr_pthread
*fpt
, void **result
)
197 int ret
= (*fpt
->attr
.stop
)(fpt
, result
);
198 memset(&fpt
->thread
, 0x00, sizeof(fpt
->thread
));
202 void frr_pthread_stop_all(void)
204 frr_with_mutex(&frr_pthread_list_mtx
) {
206 struct frr_pthread
*fpt
;
207 for (ALL_LIST_ELEMENTS_RO(frr_pthread_list
, n
, fpt
)) {
208 if (atomic_load_explicit(&fpt
->running
,
209 memory_order_relaxed
))
210 frr_pthread_stop(fpt
, NULL
);
216 * ----------------------------------------------------------------------------
218 * ----------------------------------------------------------------------------
221 /* dummy task for sleeper pipe */
222 static int fpt_dummy(struct thread
*thread
)
227 /* poison pill task to end event loop */
228 static int fpt_finish(struct thread
*thread
)
230 struct frr_pthread
*fpt
= THREAD_ARG(thread
);
232 atomic_store_explicit(&fpt
->running
, false, memory_order_relaxed
);
236 /* stop function, called from other threads to halt this one */
237 static int fpt_halt(struct frr_pthread
*fpt
, void **res
)
239 thread_add_event(fpt
->master
, &fpt_finish
, fpt
, 0, NULL
);
240 pthread_join(fpt
->thread
, res
);
246 * Entry pthread function & main event loop.
248 * Upon thread start the following actions occur:
250 * - frr_pthread's owner field is set to pthread ID.
251 * - All signals are blocked (except for unblockable signals).
252 * - Pthread's threadmaster is set to never handle pending signals
253 * - Poker pipe for poll() is created and queued as I/O source
254 * - The frr_pthread->running_cond condition variable is signalled to indicate
255 * that the previous actions have completed. It is not safe to assume any of
256 * the above have occurred before receiving this signal.
258 * After initialization is completed, the event loop begins running. Each tick,
259 * the following actions are performed before running the usual event system
262 * - Verify that the running boolean is set
263 * - Verify that there are no pending cancellation requests
264 * - Verify that there are tasks scheduled
266 * So long as the conditions are met, the event loop tick is run and the
267 * returned task is executed.
269 * If any of these conditions are not met, the event loop exits, closes the
270 * pipes and dies without running any cleanup functions.
272 static void *fpt_run(void *arg
)
274 struct frr_pthread
*fpt
= arg
;
275 fpt
->master
->owner
= pthread_self();
277 zlog_tls_buffer_init();
281 thread_add_read(fpt
->master
, &fpt_dummy
, NULL
, sleeper
[0], NULL
);
283 fpt
->master
->handle_signals
= false;
285 frr_pthread_set_name(fpt
);
287 frr_pthread_notify_running(fpt
);
290 while (atomic_load_explicit(&fpt
->running
, memory_order_relaxed
)) {
291 pthread_testcancel();
292 if (thread_fetch(fpt
->master
, &task
)) {
300 zlog_tls_buffer_fini();