]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: add pthread manager
authorQuentin Young <qlyoung@cumulusnetworks.com>
Sun, 16 Apr 2017 03:14:36 +0000 (03:14 +0000)
committerQuentin Young <qlyoung@cumulusnetworks.com>
Fri, 28 Apr 2017 22:43:42 +0000 (22:43 +0000)
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>
lib/Makefile.am
lib/frr_pthread.c [new file with mode: 0644]
lib/frr_pthread.h [new file with mode: 0644]

index 75947e61465f1a7c2e3d26dedbd2a28a97ca113e..ad8a488689e17eb1ce21222b69aa9a0a2d915781 100644 (file)
@@ -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 (file)
index 0000000..0408bca
--- /dev/null
@@ -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++;
+}
diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h
new file mode 100644 (file)
index 0000000..b495436
--- /dev/null
@@ -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 */