back to topotato report
topotato coverage report
Current view: top level - lib - frr_pthread.c (source / functions) Hit Total Coverage
Test: aggregated run ( view descriptions ) Lines: 119 124 96.0 %
Date: 2023-02-24 14:41:08 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         586 : DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread");
      35         586 : 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         190 : void frr_pthread_init(void)
      57             : {
      58         190 :         frr_with_mutex (&frr_pthread_list_mtx) {
      59         190 :                 frr_pthread_list = list_new();
      60             :         }
      61         190 : }
      62             : 
      63         190 : void frr_pthread_finish(void)
      64             : {
      65         190 :         frr_pthread_stop_all();
      66             : 
      67         380 :         frr_with_mutex (&frr_pthread_list_mtx) {
      68         190 :                 struct listnode *n, *nn;
      69         190 :                 struct frr_pthread *fpt;
      70             : 
      71         448 :                 for (ALL_LIST_ELEMENTS(frr_pthread_list, n, nn, fpt)) {
      72          68 :                         listnode_delete(frr_pthread_list, fpt);
      73          68 :                         frr_pthread_destroy_nolock(fpt);
      74             :                 }
      75             : 
      76         190 :                 list_delete(&frr_pthread_list);
      77             :         }
      78         190 : }
      79             : 
      80         397 : struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr,
      81             :                                     const char *name, const char *os_name)
      82             : {
      83         397 :         struct frr_pthread *fpt = NULL;
      84             : 
      85         397 :         attr = attr ? attr : &frr_pthread_attr_default;
      86             : 
      87         397 :         fpt = XCALLOC(MTYPE_FRR_PTHREAD, sizeof(struct frr_pthread));
      88             :         /* initialize mutex */
      89         397 :         pthread_mutex_init(&fpt->mtx, NULL);
      90             :         /* create new thread master */
      91         397 :         fpt->master = thread_master_create(name);
      92             :         /* set attributes */
      93         397 :         fpt->attr = *attr;
      94         397 :         name = (name ? name : "Anonymous thread");
      95         397 :         fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
      96         397 :         if (os_name)
      97         397 :                 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         397 :         fpt->running_cond_mtx = XCALLOC(
     102             :                 MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
     103         397 :         fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM,
     104             :                                     sizeof(pthread_cond_t));
     105         397 :         pthread_mutex_init(fpt->running_cond_mtx, NULL);
     106         397 :         pthread_cond_init(fpt->running_cond, NULL);
     107             : 
     108         794 :         frr_with_mutex (&frr_pthread_list_mtx) {
     109         397 :                 listnode_add(frr_pthread_list, fpt);
     110             :         }
     111             : 
     112         397 :         return fpt;
     113             : }
     114             : 
     115         397 : static void frr_pthread_destroy_nolock(struct frr_pthread *fpt)
     116             : {
     117         397 :         thread_master_free(fpt->master);
     118         397 :         pthread_mutex_destroy(&fpt->mtx);
     119         397 :         pthread_mutex_destroy(fpt->running_cond_mtx);
     120         397 :         pthread_cond_destroy(fpt->running_cond);
     121         397 :         XFREE(MTYPE_FRR_PTHREAD, fpt->name);
     122         397 :         XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
     123         397 :         XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
     124         397 :         XFREE(MTYPE_FRR_PTHREAD, fpt);
     125         397 : }
     126             : 
     127         329 : void frr_pthread_destroy(struct frr_pthread *fpt)
     128             : {
     129         658 :         frr_with_mutex (&frr_pthread_list_mtx) {
     130         329 :                 listnode_delete(frr_pthread_list, fpt);
     131             :         }
     132             : 
     133         329 :         frr_pthread_destroy_nolock(fpt);
     134         329 : }
     135             : 
     136         397 : int frr_pthread_set_name(struct frr_pthread *fpt)
     137             : {
     138         397 :         int ret = 0;
     139             : 
     140             : #ifdef HAVE_PTHREAD_SETNAME_NP
     141             : # ifdef GNU_LINUX
     142         397 :         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         397 :         return ret;
     151             : }
     152             : 
     153         397 : static void *frr_pthread_inner(void *arg)
     154             : {
     155         397 :         struct frr_pthread *fpt = arg;
     156             : 
     157         397 :         rcu_thread_start(fpt->rcu_thread);
     158         397 :         return fpt->attr.start(fpt);
     159             : }
     160             : 
     161         397 : int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
     162             : {
     163         397 :         int ret;
     164         397 :         sigset_t oldsigs, blocksigs;
     165             : 
     166         397 :         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         397 :         sigfillset(&blocksigs);
     172         397 :         pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs);
     173             : 
     174         397 :         frrtrace(1, frr_libfrr, frr_pthread_run, fpt->name);
     175             : 
     176         397 :         fpt->rcu_thread = rcu_thread_prepare();
     177         397 :         ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt);
     178             : 
     179             :         /* Restore caller's signals */
     180         397 :         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         397 :         if (ret < 0) {
     187           0 :                 rcu_thread_unprepare(fpt->rcu_thread);
     188           0 :                 memset(&fpt->thread, 0x00, sizeof(fpt->thread));
     189             :         }
     190             : 
     191         397 :         return ret;
     192             : }
     193             : 
     194          68 : void frr_pthread_wait_running(struct frr_pthread *fpt)
     195             : {
     196         136 :         frr_with_mutex (fpt->running_cond_mtx) {
     197         105 :                 while (!fpt->running)
     198          37 :                         pthread_cond_wait(fpt->running_cond,
     199             :                                           fpt->running_cond_mtx);
     200             :         }
     201          68 : }
     202             : 
     203         397 : void frr_pthread_notify_running(struct frr_pthread *fpt)
     204             : {
     205         794 :         frr_with_mutex (fpt->running_cond_mtx) {
     206         397 :                 fpt->running = true;
     207         397 :                 pthread_cond_signal(fpt->running_cond);
     208             :         }
     209         397 : }
     210             : 
     211         397 : int frr_pthread_stop(struct frr_pthread *fpt, void **result)
     212             : {
     213         397 :         frrtrace(1, frr_libfrr, frr_pthread_stop, fpt->name);
     214             : 
     215         397 :         int ret = (*fpt->attr.stop)(fpt, result);
     216         397 :         memset(&fpt->thread, 0x00, sizeof(fpt->thread));
     217         397 :         return ret;
     218             : }
     219             : 
     220         224 : void frr_pthread_stop_all(void)
     221             : {
     222         448 :         frr_with_mutex (&frr_pthread_list_mtx) {
     223         224 :                 struct listnode *n;
     224         224 :                 struct frr_pthread *fpt;
     225         584 :                 for (ALL_LIST_ELEMENTS_RO(frr_pthread_list, n, fpt)) {
     226         136 :                         if (atomic_load_explicit(&fpt->running,
     227             :                                                  memory_order_relaxed))
     228          68 :                                 frr_pthread_stop(fpt, NULL);
     229             :                 }
     230             :         }
     231         224 : }
     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         242 : static void fpt_finish(struct thread *thread)
     246             : {
     247         242 :         struct frr_pthread *fpt = THREAD_ARG(thread);
     248             : 
     249         242 :         atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
     250         242 : }
     251             : 
     252             : /* stop function, called from other threads to halt this one */
     253         363 : static int fpt_halt(struct frr_pthread *fpt, void **res)
     254             : {
     255         363 :         thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
     256         363 :         pthread_join(fpt->thread, res);
     257             : 
     258         363 :         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         363 : static void *fpt_run(void *arg)
     289             : {
     290         363 :         struct frr_pthread *fpt = arg;
     291         363 :         fpt->master->owner = pthread_self();
     292             : 
     293         363 :         zlog_tls_buffer_init();
     294             : 
     295         363 :         int sleeper[2];
     296         363 :         pipe(sleeper);
     297         363 :         thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
     298             : 
     299         363 :         fpt->master->handle_signals = false;
     300             : 
     301         363 :         frr_pthread_set_name(fpt);
     302             : 
     303         363 :         frr_pthread_notify_running(fpt);
     304             : 
     305         363 :         struct thread task;
     306        5683 :         while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
     307        4957 :                 pthread_testcancel();
     308        4956 :                 if (thread_fetch(fpt->master, &task)) {
     309        4957 :                         thread_call(&task);
     310             :                 }
     311             :         }
     312             : 
     313         363 :         close(sleeper[1]);
     314         363 :         close(sleeper[0]);
     315             : 
     316         363 :         zlog_tls_buffer_fini();
     317             : 
     318         363 :         return NULL;
     319             : }

Generated by: LCOV version v1.16-topotato