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