back to topotato report
topotato coverage report
Current view: top level - lib - frr_pthread.c (source / functions) Hit Total Coverage
Test: test_bgp_minimum_holdtime.py::TestBGPMinimumHoldtime Lines: 119 124 96.0 %
Date: 2023-02-24 18:37:25 Functions: 19 20 95.0 %

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

Generated by: LCOV version v1.16-topotato