summaryrefslogtreecommitdiffstats
path: root/usr.bin/csup/threads.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/csup/threads.c')
-rw-r--r--usr.bin/csup/threads.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/usr.bin/csup/threads.c b/usr.bin/csup/threads.c
new file mode 100644
index 0000000..46a9860
--- /dev/null
+++ b/usr.bin/csup/threads.c
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 2004-2006, Maxime Henrion <mux@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "misc.h"
+#include "queue.h"
+#include "threads.h"
+
+/*
+ * This API is a wrapper around the pthread(3) API, which mainly
+ * allows me to wait for multiple threads to exit. We use a
+ * condition variable to signal a thread's death. All threads
+ * created with this API have a common entry/exit point, so we
+ * don't need to add any code in the threads themselves.
+ */
+
+/* Structure describing a thread. */
+struct thread {
+ pthread_t thread;
+ void *(*start)(void *);
+ void *data;
+ struct threads *threads;
+ LIST_ENTRY(thread) runlist;
+ STAILQ_ENTRY(thread) deadlist;
+};
+
+/* A set of threads. */
+struct threads {
+ pthread_mutex_t threads_mtx;
+ pthread_cond_t thread_exited;
+ LIST_HEAD(, thread) threads_running;
+ STAILQ_HEAD(, thread) threads_dead;
+};
+
+static void *thread_start(void *); /* Common entry point for threads. */
+
+static void threads_lock(struct threads *);
+static void threads_unlock(struct threads *);
+
+static void
+threads_lock(struct threads *tds)
+{
+ int error;
+
+ error = pthread_mutex_lock(&tds->threads_mtx);
+ assert(!error);
+}
+
+static void
+threads_unlock(struct threads *tds)
+{
+ int error;
+
+ error = pthread_mutex_unlock(&tds->threads_mtx);
+ assert(!error);
+}
+
+/* Create a new set of threads. */
+struct threads *
+threads_new(void)
+{
+ struct threads *tds;
+
+ tds = xmalloc(sizeof(struct threads));
+ pthread_mutex_init(&tds->threads_mtx, NULL);
+ pthread_cond_init(&tds->thread_exited, NULL);
+ LIST_INIT(&tds->threads_running);
+ STAILQ_INIT(&tds->threads_dead);
+ return (tds);
+}
+
+/* Create a new thread in this set. */
+void
+threads_create(struct threads *tds, void *(*start)(void *), void *data)
+{
+ pthread_attr_t attr;
+ struct thread *td;
+ int error;
+
+ td = xmalloc(sizeof(struct thread));
+ td->threads = tds;
+ td->start = start;
+ td->data = data;
+ /* We don't use pthread_join() to wait for the threads to finish. */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ threads_lock(tds);
+ error = pthread_create(&td->thread, &attr, thread_start, td);
+ if (error)
+ err(1, "pthread_create");
+ LIST_INSERT_HEAD(&tds->threads_running, td, runlist);
+ threads_unlock(tds);
+}
+
+/* Wait for a thread in the set to exit, and return its data pointer. */
+void *
+threads_wait(struct threads *tds)
+{
+ struct thread *td;
+ void *data;
+
+ threads_lock(tds);
+ while (STAILQ_EMPTY(&tds->threads_dead)) {
+ assert(!LIST_EMPTY(&tds->threads_running));
+ pthread_cond_wait(&tds->thread_exited, &tds->threads_mtx);
+ }
+ td = STAILQ_FIRST(&tds->threads_dead);
+ STAILQ_REMOVE_HEAD(&tds->threads_dead, deadlist);
+ threads_unlock(tds);
+ data = td->data;
+ free(td);
+ return (data);
+}
+
+/* Free a threads set. */
+void
+threads_free(struct threads *tds)
+{
+
+ assert(LIST_EMPTY(&tds->threads_running));
+ assert(STAILQ_EMPTY(&tds->threads_dead));
+ pthread_cond_destroy(&tds->thread_exited);
+ pthread_mutex_destroy(&tds->threads_mtx);
+ free(tds);
+}
+
+/*
+ * Common entry point for threads. This just calls the real start
+ * routine, and then signals the thread's death, after having
+ * removed the thread from the list.
+ */
+static void *
+thread_start(void *data)
+{
+ struct threads *tds;
+ struct thread *td;
+
+ td = data;
+ tds = td->threads;
+ td->start(td->data);
+ threads_lock(tds);
+ LIST_REMOVE(td, runlist);
+ STAILQ_INSERT_TAIL(&tds->threads_dead, td, deadlist);
+ pthread_cond_signal(&tds->thread_exited);
+ threads_unlock(tds);
+ return (NULL);
+}
OpenPOWER on IntegriCloud