1156230Smux/*- 2156230Smux * Copyright (c) 2004-2006, Maxime Henrion <mux@FreeBSD.org> 3156230Smux * All rights reserved. 4156230Smux * 5156230Smux * Redistribution and use in source and binary forms, with or without 6156230Smux * modification, are permitted provided that the following conditions 7156230Smux * are met: 8156230Smux * 1. Redistributions of source code must retain the above copyright 9156230Smux * notice, this list of conditions and the following disclaimer. 10156230Smux * 2. Redistributions in binary form must reproduce the above copyright 11156230Smux * notice, this list of conditions and the following disclaimer in the 12156230Smux * documentation and/or other materials provided with the distribution. 13156230Smux * 14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17156230Smux * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24156230Smux * SUCH DAMAGE. 25156230Smux * 26156230Smux * $FreeBSD$ 27156230Smux */ 28156230Smux 29156230Smux#include <assert.h> 30156230Smux#include <err.h> 31156230Smux#include <pthread.h> 32156230Smux#include <stdlib.h> 33156230Smux 34156230Smux#include "misc.h" 35156230Smux#include "queue.h" 36156230Smux#include "threads.h" 37156230Smux 38156230Smux/* 39156230Smux * This API is a wrapper around the pthread(3) API, which mainly 40156230Smux * allows me to wait for multiple threads to exit. We use a 41156230Smux * condition variable to signal a thread's death. All threads 42156230Smux * created with this API have a common entry/exit point, so we 43156230Smux * don't need to add any code in the threads themselves. 44156230Smux */ 45156230Smux 46156230Smux/* Structure describing a thread. */ 47156230Smuxstruct thread { 48156230Smux pthread_t thread; 49156230Smux void *(*start)(void *); 50156230Smux void *data; 51156230Smux struct threads *threads; 52156230Smux LIST_ENTRY(thread) runlist; 53156230Smux STAILQ_ENTRY(thread) deadlist; 54156230Smux}; 55156230Smux 56156230Smux/* A set of threads. */ 57156230Smuxstruct threads { 58156230Smux pthread_mutex_t threads_mtx; 59156230Smux pthread_cond_t thread_exited; 60156230Smux LIST_HEAD(, thread) threads_running; 61156230Smux STAILQ_HEAD(, thread) threads_dead; 62156230Smux}; 63156230Smux 64156230Smuxstatic void *thread_start(void *); /* Common entry point for threads. */ 65156230Smux 66156230Smuxstatic void threads_lock(struct threads *); 67156230Smuxstatic void threads_unlock(struct threads *); 68156230Smux 69156230Smuxstatic void 70156230Smuxthreads_lock(struct threads *tds) 71156230Smux{ 72156230Smux int error; 73156230Smux 74156230Smux error = pthread_mutex_lock(&tds->threads_mtx); 75156230Smux assert(!error); 76156230Smux} 77156230Smux 78156230Smuxstatic void 79156230Smuxthreads_unlock(struct threads *tds) 80156230Smux{ 81156230Smux int error; 82156230Smux 83156230Smux error = pthread_mutex_unlock(&tds->threads_mtx); 84156230Smux assert(!error); 85156230Smux} 86156230Smux 87156230Smux/* Create a new set of threads. */ 88156230Smuxstruct threads * 89156230Smuxthreads_new(void) 90156230Smux{ 91156230Smux struct threads *tds; 92156230Smux 93156230Smux tds = xmalloc(sizeof(struct threads)); 94156230Smux pthread_mutex_init(&tds->threads_mtx, NULL); 95156230Smux pthread_cond_init(&tds->thread_exited, NULL); 96156230Smux LIST_INIT(&tds->threads_running); 97156230Smux STAILQ_INIT(&tds->threads_dead); 98156230Smux return (tds); 99156230Smux} 100156230Smux 101156230Smux/* Create a new thread in this set. */ 102156230Smuxvoid 103156230Smuxthreads_create(struct threads *tds, void *(*start)(void *), void *data) 104156230Smux{ 105156230Smux pthread_attr_t attr; 106156230Smux struct thread *td; 107156230Smux int error; 108156230Smux 109156230Smux td = xmalloc(sizeof(struct thread)); 110156230Smux td->threads = tds; 111156230Smux td->start = start; 112156230Smux td->data = data; 113156230Smux /* We don't use pthread_join() to wait for the threads to finish. */ 114156230Smux pthread_attr_init(&attr); 115156230Smux pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 116156230Smux threads_lock(tds); 117156230Smux error = pthread_create(&td->thread, &attr, thread_start, td); 118156230Smux if (error) 119156230Smux err(1, "pthread_create"); 120156230Smux LIST_INSERT_HEAD(&tds->threads_running, td, runlist); 121156230Smux threads_unlock(tds); 122156230Smux} 123156230Smux 124156230Smux/* Wait for a thread in the set to exit, and return its data pointer. */ 125156230Smuxvoid * 126156230Smuxthreads_wait(struct threads *tds) 127156230Smux{ 128156230Smux struct thread *td; 129156230Smux void *data; 130156230Smux 131156230Smux threads_lock(tds); 132156230Smux while (STAILQ_EMPTY(&tds->threads_dead)) { 133156230Smux assert(!LIST_EMPTY(&tds->threads_running)); 134156230Smux pthread_cond_wait(&tds->thread_exited, &tds->threads_mtx); 135156230Smux } 136156230Smux td = STAILQ_FIRST(&tds->threads_dead); 137156230Smux STAILQ_REMOVE_HEAD(&tds->threads_dead, deadlist); 138156230Smux threads_unlock(tds); 139156230Smux data = td->data; 140156230Smux free(td); 141156230Smux return (data); 142156230Smux} 143156230Smux 144156230Smux/* Free a threads set. */ 145156230Smuxvoid 146156230Smuxthreads_free(struct threads *tds) 147156230Smux{ 148156230Smux 149156230Smux assert(LIST_EMPTY(&tds->threads_running)); 150156230Smux assert(STAILQ_EMPTY(&tds->threads_dead)); 151156230Smux pthread_cond_destroy(&tds->thread_exited); 152156230Smux pthread_mutex_destroy(&tds->threads_mtx); 153156230Smux free(tds); 154156230Smux} 155156230Smux 156156230Smux/* 157156230Smux * Common entry point for threads. This just calls the real start 158156230Smux * routine, and then signals the thread's death, after having 159156230Smux * removed the thread from the list. 160156230Smux */ 161156230Smuxstatic void * 162156230Smuxthread_start(void *data) 163156230Smux{ 164156230Smux struct threads *tds; 165156230Smux struct thread *td; 166156230Smux 167156230Smux td = data; 168156230Smux tds = td->threads; 169156230Smux td->start(td->data); 170156230Smux threads_lock(tds); 171156230Smux LIST_REMOVE(td, runlist); 172156230Smux STAILQ_INSERT_TAIL(&tds->threads_dead, td, deadlist); 173156230Smux pthread_cond_signal(&tds->thread_exited); 174156230Smux threads_unlock(tds); 175156230Smux return (NULL); 176156230Smux} 177