From: Quentin Young Date: Sun, 16 Apr 2017 03:14:36 +0000 (+0000) Subject: lib: add pthread manager X-Git-Tag: reindent-master-before~207^2~1 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=98f14af8bf8340115049e0df4888b6acc8701ea5;p=mirror%2Ffrr.git lib: add pthread manager Adds infrastructure for keeping track of pthreads. The general idea is to maintain a daemon-wide table of all pthreads, running or not. A pthread is associated with its own thread master that can be used with existing thread.c code, which provides user-space timers, an event loop, non-blocking I/O callbacks and other facilities. Each frr_pthread has a unique identifier that can be used to fetch it from the table. This is to allow naming threads using a macro, for example: #define WRITE_THREAD 0 #define READ_THREAD 1 #define WORK_THREAD 2 The idea here is to be relatively flexible with regard to how daemons manage their collection of pthreads; the implementation could get away with just some #define'd constants, or keep a dynamically allocated data structure that provides organization, searching, prioritizing, etc. Overall this interface should provide a way to maintain the familiar thread.c userspace threading model while progressively introducing pthreads. Signed-off-by: Quentin Young --- diff --git a/lib/Makefile.am b/lib/Makefile.am index 75947e6146..ad8a488689 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -34,6 +34,7 @@ libfrr_la_SOURCES = \ strlcat.c \ module.c \ hook.c \ + frr_pthread.c \ # end BUILT_SOURCES = route_types.h gitversion.h command_parse.h command_lex.h @@ -74,6 +75,7 @@ pkginclude_HEADERS = \ module.h \ hook.h \ libfrr.h \ + frr_pthread.h \ # end noinst_HEADERS = \ diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c new file mode 100644 index 0000000000..0408bca096 --- /dev/null +++ b/lib/frr_pthread.c @@ -0,0 +1,184 @@ +/* + Utilities and interfaces for managing POSIX threads + Copyright (C) 2017 Cumulus Networks + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + */ + +#include +#include + +#include "frr_pthread.h" +#include "memory.h" +#include "hash.h" + +DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread"); + +static unsigned int next_id = 0; + +/* Hash table of all frr_pthreads along with synchronization primitive(s) and + * hash table callbacks. + * ------------------------------------------------------------------------ */ +static struct hash *pthread_table; +static pthread_mutex_t pthread_table_mtx = PTHREAD_MUTEX_INITIALIZER; + +/* pthread_table->hash_cmp */ +static int pthread_table_hash_cmp(const void *value1, const void *value2) +{ + const struct frr_pthread *tq1 = value1; + const struct frr_pthread *tq2 = value2; + + return (tq1->id == tq2->id); +} + +/* pthread_table->hash_key */ +static unsigned int pthread_table_hash_key(void *value) +{ + return ((struct frr_pthread *)value)->id; +} +/* ------------------------------------------------------------------------ */ + +void frr_pthread_init() +{ + pthread_mutex_lock(&pthread_table_mtx); + { + pthread_table = + hash_create(pthread_table_hash_key, pthread_table_hash_cmp); + } + pthread_mutex_unlock(&pthread_table_mtx); +} + +void frr_pthread_finish() +{ + pthread_mutex_lock(&pthread_table_mtx); + { + hash_clean(pthread_table, (void (*)(void *))frr_pthread_destroy); + hash_free(pthread_table); + } + pthread_mutex_unlock(&pthread_table_mtx); +} + +struct frr_pthread *frr_pthread_new(const char *name, unsigned int id, + void *(*start_routine) (void *), + int (*stop_routine) (void **, struct frr_pthread *)) +{ + static struct frr_pthread holder = { 0 }; + struct frr_pthread *fpt = NULL; + + pthread_mutex_lock(&pthread_table_mtx); + { + holder.id = id; + + if (!hash_lookup(pthread_table, &holder)) { + struct frr_pthread *fpt = + XCALLOC(MTYPE_FRR_PTHREAD, + sizeof(struct frr_pthread)); + fpt->id = id; + fpt->master = thread_master_create(); + fpt->start_routine = start_routine; + fpt->stop_routine = stop_routine; + fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name); + + hash_get(pthread_table, fpt, hash_alloc_intern); + } + } + pthread_mutex_unlock(&pthread_table_mtx); + + return fpt; +} + +void frr_pthread_destroy(struct frr_pthread *fpt) +{ + thread_master_free(fpt->master); + XFREE(MTYPE_FRR_PTHREAD, fpt->name); + XFREE(MTYPE_FRR_PTHREAD, fpt); +} + +struct frr_pthread *frr_pthread_get(unsigned int id) +{ + static struct frr_pthread holder = { 0 }; + struct frr_pthread *fpt; + + pthread_mutex_lock(&pthread_table_mtx); + { + holder.id = id; + fpt = hash_lookup(pthread_table, &holder); + } + pthread_mutex_unlock(&pthread_table_mtx); + + return fpt; +} + +int frr_pthread_run(unsigned int id, const pthread_attr_t * attr, void *arg) +{ + struct frr_pthread *fpt = frr_pthread_get(id); + int ret; + + if (!fpt) + return -1; + + ret = pthread_create(&fpt->thread, attr, fpt->start_routine, arg); + + /* Per pthread_create(3), the contents of fpt->thread are undefined if + * pthread_create() did not succeed. Reset this value to zero. */ + if (ret < 0) + memset(&fpt->thread, 0x00, sizeof(fpt->thread)); + + return ret; +} + +/** + * Calls the stop routine for the frr_pthread and resets any relevant fields. + * + * @param fpt - the frr_pthread to stop + * @param result - pointer to result pointer + * @return the return code from the stop routine + */ +static int frr_pthread_stop_actual(struct frr_pthread *fpt, void **result) +{ + int ret = (*fpt->stop_routine) (result, fpt); + memset(&fpt->thread, 0x00, sizeof(fpt->thread)); + return ret; +} + +int frr_pthread_stop(unsigned int id, void **result) +{ + struct frr_pthread *fpt = frr_pthread_get(id); + return frr_pthread_stop_actual(fpt, result); +} + +/** + * Callback for hash_iterate to stop all frr_pthread's. + */ +static void frr_pthread_stop_all_iter(struct hash_backet *hb, void *arg) +{ + struct frr_pthread *fpt = hb->data; + frr_pthread_stop_actual(fpt, NULL); +} + +void frr_pthread_stop_all() +{ + pthread_mutex_lock(&pthread_table_mtx); + { + hash_iterate(pthread_table, frr_pthread_stop_all_iter, NULL); + } + pthread_mutex_unlock(&pthread_table_mtx); +} + +unsigned int frr_pthread_get_id() +{ + return next_id++; +} diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h new file mode 100644 index 0000000000..b4954367f4 --- /dev/null +++ b/lib/frr_pthread.h @@ -0,0 +1,145 @@ +/* + Utilities and interfaces for managing POSIX threads + Copyright (C) 2017 Cumulus Networks + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + */ + +#ifndef _FRR_PTHREAD_H +#define _FRR_PTHREAD_H + +#include +#include "thread.h" + +struct frr_pthread { + + /* pthread id */ + pthread_t thread; + + /* frr thread identifier */ + unsigned int id; + + /* thread master for this pthread's thread.c event loop */ + struct thread_master *master; + + /* start routine */ + void *(*start_routine) (void *); + + /* stop routine */ + int (*stop_routine) (void **, struct frr_pthread *); + + /* the (hopefully descriptive) name of this thread */ + char *name; +}; + +/* Initializes this module. + * + * Must be called before using any of the other functions. + */ +void frr_pthread_init(void); + +/* Uninitializes this module. + * + * Destroys all registered frr_pthread's and internal data structures. + * + * It is safe to call frr_pthread_init() after this function to reinitialize + * the module. + */ +void frr_pthread_finish(void); + +/* Creates a new frr_pthread. + * + * If the provided ID is already assigned to an existing frr_pthread, the + * return value will be NULL. + * + * @param name - the name of the thread. Doesn't have to be unique, but it + * probably should be. This value is copied and may be safely free'd upon + * return. + * + * @param id - the integral ID of the thread. MUST be unique. The caller may + * use this id to retrieve the thread. + * + * @param start_routine - start routine for the pthread, will be passed to + * pthread_create (see those docs for details) + * + * @param stop_routine - stop routine for the pthread, called to terminate the + * thread. This function should gracefully stop the pthread and clean up any + * thread-specific resources. The passed pointer is used to return a data + * result. + * + * @return the created frr_pthread upon success, or NULL upon failure + */ +struct frr_pthread *frr_pthread_new(const char *name, unsigned int id, + void *(*start_routine) (void *), + int (*stop_routine) (void **, struct frr_pthread *)); + +/* Destroys an frr_pthread. + * + * Assumes that the associated pthread, if any, has already terminated. + * + * @param fpt - the frr_pthread to destroy + */ +void frr_pthread_destroy(struct frr_pthread *fpt); + +/* Gets an existing frr_pthread by its id. + * + * @return frr_thread associated with the provided id, or NULL on error + */ +struct frr_pthread *frr_pthread_get(unsigned int id); + +/* Creates a new pthread and binds it to a frr_pthread. + * + * This function is a wrapper for pthread_create. The first parameter is the + * frr_pthread to bind the created pthread to. All subsequent arguments are + * passed unmodified to pthread_create(). + * + * This function returns the same code as pthread_create(). If the value is + * zero, the provided frr_pthread is bound to a running POSIX thread. If the + * value is less than zero, the provided frr_pthread is guaranteed to be a + * clean instance that may be susbsequently passed to frr_pthread_run(). + * + * @param id - frr_pthread to bind the created pthread to + * @param attr - see pthread_create(3) + * @param arg - see pthread_create(3) + * + * @return see pthread_create(3) + */ +int frr_pthread_run(unsigned int id, const pthread_attr_t * attr, void *arg); + +/* Stops an frr_pthread with a result. + * + * @param id - frr_pthread to stop + * @param result - where to store the thread's result, if any. May be NULL if a + * result is not needed. + */ +int frr_pthread_stop(unsigned int id, void **result); + +/* Stops all frr_pthread's. */ +void frr_pthread_stop_all(void); + +/* Returns a unique identifier for use with frr_pthread_new(). + * + * Internally, this is an integer that increments after each call to this + * function. Because the number of pthreads created should never exceed INT_MAX + * during the life of the program, there is no overflow protection. If by + * chance this function returns an ID which is already in use, + * frr_pthread_new() will fail when it is provided. + * + * @return unique identifier + */ +unsigned int frr_pthread_get_id(void); + +#endif /* _FRR_PTHREAD_H */