2 * Copyright (c) 2004-2006, Maxime Henrion <mux@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * This API is a wrapper around the pthread(3) API, which mainly
40 * allows me to wait for multiple threads to exit. We use a
41 * condition variable to signal a thread's death. All threads
42 * created with this API have a common entry/exit point, so we
43 * don't need to add any code in the threads themselves.
46 /* Structure describing a thread. */
49 void *(*start)(void *);
51 struct threads *threads;
52 LIST_ENTRY(thread) runlist;
53 STAILQ_ENTRY(thread) deadlist;
56 /* A set of threads. */
58 pthread_mutex_t threads_mtx;
59 pthread_cond_t thread_exited;
60 LIST_HEAD(, thread) threads_running;
61 STAILQ_HEAD(, thread) threads_dead;
64 static void *thread_start(void *); /* Common entry point for threads. */
66 static void threads_lock(struct threads *);
67 static void threads_unlock(struct threads *);
70 threads_lock(struct threads *tds)
74 error = pthread_mutex_lock(&tds->threads_mtx);
79 threads_unlock(struct threads *tds)
83 error = pthread_mutex_unlock(&tds->threads_mtx);
87 /* Create a new set of threads. */
93 tds = xmalloc(sizeof(struct threads));
94 pthread_mutex_init(&tds->threads_mtx, NULL);
95 pthread_cond_init(&tds->thread_exited, NULL);
96 LIST_INIT(&tds->threads_running);
97 STAILQ_INIT(&tds->threads_dead);
101 /* Create a new thread in this set. */
103 threads_create(struct threads *tds, void *(*start)(void *), void *data)
109 td = xmalloc(sizeof(struct thread));
113 /* We don't use pthread_join() to wait for the threads to finish. */
114 pthread_attr_init(&attr);
115 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
117 error = pthread_create(&td->thread, &attr, thread_start, td);
119 err(1, "pthread_create");
120 LIST_INSERT_HEAD(&tds->threads_running, td, runlist);
124 /* Wait for a thread in the set to exit, and return its data pointer. */
126 threads_wait(struct threads *tds)
132 while (STAILQ_EMPTY(&tds->threads_dead)) {
133 assert(!LIST_EMPTY(&tds->threads_running));
134 pthread_cond_wait(&tds->thread_exited, &tds->threads_mtx);
136 td = STAILQ_FIRST(&tds->threads_dead);
137 STAILQ_REMOVE_HEAD(&tds->threads_dead, deadlist);
144 /* Free a threads set. */
146 threads_free(struct threads *tds)
149 assert(LIST_EMPTY(&tds->threads_running));
150 assert(STAILQ_EMPTY(&tds->threads_dead));
151 pthread_cond_destroy(&tds->thread_exited);
152 pthread_mutex_destroy(&tds->threads_mtx);
157 * Common entry point for threads. This just calls the real start
158 * routine, and then signals the thread's death, after having
159 * removed the thread from the list.
162 thread_start(void *data)
171 LIST_REMOVE(td, runlist);
172 STAILQ_INSERT_TAIL(&tds->threads_dead, td, deadlist);
173 pthread_cond_signal(&tds->thread_exited);