mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-15 09:20:25 +00:00
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 <qlyoung@cumulusnetworks.com>
This commit is contained in:
parent
2c70efaed1
commit
98f14af8bf
@ -34,6 +34,7 @@ libfrr_la_SOURCES = \
|
|||||||
strlcat.c \
|
strlcat.c \
|
||||||
module.c \
|
module.c \
|
||||||
hook.c \
|
hook.c \
|
||||||
|
frr_pthread.c \
|
||||||
# end
|
# end
|
||||||
|
|
||||||
BUILT_SOURCES = route_types.h gitversion.h command_parse.h command_lex.h
|
BUILT_SOURCES = route_types.h gitversion.h command_parse.h command_lex.h
|
||||||
@ -74,6 +75,7 @@ pkginclude_HEADERS = \
|
|||||||
module.h \
|
module.h \
|
||||||
hook.h \
|
hook.h \
|
||||||
libfrr.h \
|
libfrr.h \
|
||||||
|
frr_pthread.h \
|
||||||
# end
|
# end
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
|
184
lib/frr_pthread.c
Normal file
184
lib/frr_pthread.c
Normal file
@ -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 <zebra.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#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++;
|
||||||
|
}
|
145
lib/frr_pthread.h
Normal file
145
lib/frr_pthread.h
Normal file
@ -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 <pthread.h>
|
||||||
|
#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 */
|
Loading…
Reference in New Issue
Block a user