Coverage Report

Created: 2026-01-25 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/frr_pthread.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * Utilities and interfaces for managing POSIX threads within FRR.
4
 * Copyright (C) 2017  Cumulus Networks, Inc.
5
 */
6
7
#include <zebra.h>
8
#include <pthread.h>
9
#ifdef HAVE_PTHREAD_NP_H
10
#include <pthread_np.h>
11
#endif
12
#include <sched.h>
13
14
#include "frr_pthread.h"
15
#include "memory.h"
16
#include "linklist.h"
17
#include "zlog.h"
18
#include "libfrr.h"
19
#include "libfrr_trace.h"
20
21
8
DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread");
22
8
DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives");
23
8
24
8
/* default frr_pthread start/stop routine prototypes */
25
8
static void *fpt_run(void *arg);
26
8
static int fpt_halt(struct frr_pthread *fpt, void **res);
27
8
28
8
/* misc sigs */
29
8
static void frr_pthread_destroy_nolock(struct frr_pthread *fpt);
30
8
31
8
/* default frr_pthread attributes */
32
8
const struct frr_pthread_attr frr_pthread_attr_default = {
33
8
  .start = fpt_run,
34
8
  .stop = fpt_halt,
35
8
};
36
8
37
8
/* list to keep track of all frr_pthreads */
38
8
static pthread_mutex_t frr_pthread_list_mtx = PTHREAD_MUTEX_INITIALIZER;
39
8
static struct list *frr_pthread_list;
40
8
41
8
/* ------------------------------------------------------------------------ */
42
8
43
8
void frr_pthread_init(void)
44
8
{
45
1
  frr_with_mutex (&frr_pthread_list_mtx) {
46
1
    frr_pthread_list = list_new();
47
1
  }
48
1
}
49
50
void frr_pthread_finish(void)
51
0
{
52
0
  frr_pthread_stop_all();
53
54
0
  frr_with_mutex (&frr_pthread_list_mtx) {
55
0
    struct listnode *n, *nn;
56
0
    struct frr_pthread *fpt;
57
58
0
    for (ALL_LIST_ELEMENTS(frr_pthread_list, n, nn, fpt)) {
59
0
      listnode_delete(frr_pthread_list, fpt);
60
0
      frr_pthread_destroy_nolock(fpt);
61
0
    }
62
63
0
    list_delete(&frr_pthread_list);
64
0
  }
65
0
}
66
67
struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr,
68
            const char *name, const char *os_name)
69
0
{
70
0
  struct frr_pthread *fpt = NULL;
71
72
0
  attr = attr ? attr : &frr_pthread_attr_default;
73
74
0
  fpt = XCALLOC(MTYPE_FRR_PTHREAD, sizeof(struct frr_pthread));
75
  /* initialize mutex */
76
0
  pthread_mutex_init(&fpt->mtx, NULL);
77
  /* create new thread master */
78
0
  fpt->master = event_master_create(name);
79
  /* set attributes */
80
0
  fpt->attr = *attr;
81
0
  name = (name ? name : "Anonymous thread");
82
0
  fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
83
0
  if (os_name)
84
0
    strlcpy(fpt->os_name, os_name, OS_THREAD_NAMELEN);
85
0
  else
86
0
    strlcpy(fpt->os_name, name, OS_THREAD_NAMELEN);
87
  /* initialize startup synchronization primitives */
88
0
  fpt->running_cond_mtx = XCALLOC(
89
0
    MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
90
0
  fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM,
91
0
            sizeof(pthread_cond_t));
92
0
  pthread_mutex_init(fpt->running_cond_mtx, NULL);
93
0
  pthread_cond_init(fpt->running_cond, NULL);
94
95
0
  frr_with_mutex (&frr_pthread_list_mtx) {
96
0
    listnode_add(frr_pthread_list, fpt);
97
0
  }
98
99
0
  return fpt;
100
0
}
101
102
static void frr_pthread_destroy_nolock(struct frr_pthread *fpt)
103
0
{
104
0
  event_master_free(fpt->master);
105
0
  pthread_mutex_destroy(&fpt->mtx);
106
0
  pthread_mutex_destroy(fpt->running_cond_mtx);
107
0
  pthread_cond_destroy(fpt->running_cond);
108
0
  XFREE(MTYPE_FRR_PTHREAD, fpt->name);
109
0
  XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
110
0
  XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
111
0
  XFREE(MTYPE_FRR_PTHREAD, fpt);
112
0
}
113
114
void frr_pthread_destroy(struct frr_pthread *fpt)
115
0
{
116
0
  frr_with_mutex (&frr_pthread_list_mtx) {
117
0
    listnode_delete(frr_pthread_list, fpt);
118
0
  }
119
120
0
  frr_pthread_destroy_nolock(fpt);
121
0
}
122
123
int frr_pthread_set_name(struct frr_pthread *fpt)
124
0
{
125
0
  int ret = 0;
126
127
0
#ifdef HAVE_PTHREAD_SETNAME_NP
128
0
# ifdef GNU_LINUX
129
0
  ret = pthread_setname_np(fpt->thread, fpt->os_name);
130
# elif defined(__NetBSD__)
131
  ret = pthread_setname_np(fpt->thread, fpt->os_name, NULL);
132
# endif
133
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
134
  pthread_set_name_np(fpt->thread, fpt->os_name);
135
#endif
136
137
0
  return ret;
138
0
}
139
140
static void *frr_pthread_inner(void *arg)
141
0
{
142
0
  struct frr_pthread *fpt = arg;
143
144
0
  rcu_thread_start(fpt->rcu_thread);
145
0
  return fpt->attr.start(fpt);
146
0
}
147
148
int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
149
0
{
150
0
  int ret;
151
0
  sigset_t oldsigs, blocksigs;
152
153
0
  assert(frr_is_after_fork || !"trying to start thread before fork()");
154
155
  /* Ensure we never handle signals on a background thread by blocking
156
   * everything here (new thread inherits signal mask)
157
   */
158
0
  sigfillset(&blocksigs);
159
0
  pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs);
160
161
0
  frrtrace(1, frr_libfrr, frr_pthread_run, fpt->name);
162
163
0
  fpt->rcu_thread = rcu_thread_prepare();
164
0
  ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt);
165
166
  /* Restore caller's signals */
167
0
  pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
168
169
  /*
170
   * Per pthread_create(3), the contents of fpt->thread are undefined if
171
   * pthread_create() did not succeed. Reset this value to zero.
172
   */
173
0
  if (ret < 0) {
174
0
    rcu_thread_unprepare(fpt->rcu_thread);
175
0
    memset(&fpt->thread, 0x00, sizeof(fpt->thread));
176
0
  }
177
178
0
  return ret;
179
0
}
180
181
void frr_pthread_wait_running(struct frr_pthread *fpt)
182
0
{
183
0
  frr_with_mutex (fpt->running_cond_mtx) {
184
0
    while (!fpt->running)
185
0
      pthread_cond_wait(fpt->running_cond,
186
0
            fpt->running_cond_mtx);
187
0
  }
188
0
}
189
190
void frr_pthread_notify_running(struct frr_pthread *fpt)
191
0
{
192
0
  frr_with_mutex (fpt->running_cond_mtx) {
193
0
    fpt->running = true;
194
0
    pthread_cond_signal(fpt->running_cond);
195
0
  }
196
0
}
197
198
int frr_pthread_stop(struct frr_pthread *fpt, void **result)
199
0
{
200
0
  frrtrace(1, frr_libfrr, frr_pthread_stop, fpt->name);
201
202
0
  int ret = (*fpt->attr.stop)(fpt, result);
203
0
  memset(&fpt->thread, 0x00, sizeof(fpt->thread));
204
0
  return ret;
205
0
}
206
207
void frr_pthread_stop_all(void)
208
0
{
209
0
  frr_with_mutex (&frr_pthread_list_mtx) {
210
0
    struct listnode *n;
211
0
    struct frr_pthread *fpt;
212
0
    for (ALL_LIST_ELEMENTS_RO(frr_pthread_list, n, fpt)) {
213
0
      if (atomic_load_explicit(&fpt->running,
214
0
             memory_order_relaxed))
215
0
        frr_pthread_stop(fpt, NULL);
216
0
    }
217
0
  }
218
0
}
219
220
/*
221
 * ----------------------------------------------------------------------------
222
 * Default Event Loop
223
 * ----------------------------------------------------------------------------
224
 */
225
226
/* dummy task for sleeper pipe */
227
static void fpt_dummy(struct event *thread)
228
0
{
229
0
}
230
231
/* poison pill task to end event loop */
232
static void fpt_finish(struct event *thread)
233
0
{
234
0
  struct frr_pthread *fpt = EVENT_ARG(thread);
235
0
236
0
  atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
237
0
}
238
239
/* stop function, called from other threads to halt this one */
240
static int fpt_halt(struct frr_pthread *fpt, void **res)
241
0
{
242
0
  event_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
243
0
  pthread_join(fpt->thread, res);
244
245
0
  return 0;
246
0
}
247
248
/*
249
 * Entry pthread function & main event loop.
250
 *
251
 * Upon thread start the following actions occur:
252
 *
253
 * - frr_pthread's owner field is set to pthread ID.
254
 * - All signals are blocked (except for unblockable signals).
255
 * - Pthread's threadmaster is set to never handle pending signals
256
 * - Poker pipe for poll() is created and queued as I/O source
257
 * - The frr_pthread->running_cond condition variable is signalled to indicate
258
 *   that the previous actions have completed. It is not safe to assume any of
259
 *   the above have occurred before receiving this signal.
260
 *
261
 * After initialization is completed, the event loop begins running. Each tick,
262
 * the following actions are performed before running the usual event system
263
 * tick function:
264
 *
265
 * - Verify that the running boolean is set
266
 * - Verify that there are no pending cancellation requests
267
 * - Verify that there are tasks scheduled
268
 *
269
 * So long as the conditions are met, the event loop tick is run and the
270
 * returned task is executed.
271
 *
272
 * If any of these conditions are not met, the event loop exits, closes the
273
 * pipes and dies without running any cleanup functions.
274
 */
275
static void *fpt_run(void *arg)
276
0
{
277
0
  struct frr_pthread *fpt = arg;
278
0
  fpt->master->owner = pthread_self();
279
280
0
  zlog_tls_buffer_init();
281
282
0
  int sleeper[2];
283
0
  pipe(sleeper);
284
0
  event_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
285
286
0
  fpt->master->handle_signals = false;
287
288
0
  frr_pthread_set_name(fpt);
289
290
0
  frr_pthread_notify_running(fpt);
291
292
0
  struct event task;
293
0
  while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
294
0
    pthread_testcancel();
295
0
    if (event_fetch(fpt->master, &task)) {
296
0
      event_call(&task);
297
0
    }
298
0
  }
299
300
0
  close(sleeper[1]);
301
0
  close(sleeper[0]);
302
303
0
  zlog_tls_buffer_fini();
304
305
  return NULL;
306
0
}