]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.bin/csup/threads.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.bin / csup / threads.c
1 /*-
2  * Copyright (c) 2004-2006, Maxime Henrion <mux@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <assert.h>
30 #include <err.h>
31 #include <pthread.h>
32 #include <stdlib.h>
33
34 #include "misc.h"
35 #include "queue.h"
36 #include "threads.h"
37
38 /*
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.
44  */
45
46 /* Structure describing a thread. */
47 struct thread {
48         pthread_t thread;
49         void *(*start)(void *);
50         void *data;
51         struct threads *threads;
52         LIST_ENTRY(thread) runlist;
53         STAILQ_ENTRY(thread) deadlist;
54 };
55
56 /* A set of threads. */
57 struct 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;
62 };
63
64 static void     *thread_start(void *);  /* Common entry point for threads. */
65
66 static void      threads_lock(struct threads *);
67 static void      threads_unlock(struct threads *);
68
69 static void
70 threads_lock(struct threads *tds)
71 {
72         int error;
73
74         error = pthread_mutex_lock(&tds->threads_mtx);
75         assert(!error);
76 }
77
78 static void
79 threads_unlock(struct threads *tds)
80 {
81         int error;
82
83         error = pthread_mutex_unlock(&tds->threads_mtx);
84         assert(!error);
85 }
86
87 /* Create a new set of threads. */
88 struct threads *
89 threads_new(void)
90 {
91         struct threads *tds;
92
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);
98         return (tds);
99 }
100
101 /* Create a new thread in this set. */
102 void
103 threads_create(struct threads *tds, void *(*start)(void *), void *data)
104 {
105         pthread_attr_t attr;
106         struct thread *td;
107         int error;
108
109         td = xmalloc(sizeof(struct thread));
110         td->threads = tds;
111         td->start = start;
112         td->data = data;
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);
116         threads_lock(tds);
117         error = pthread_create(&td->thread, &attr, thread_start, td);
118         if (error)
119                 err(1, "pthread_create");
120         LIST_INSERT_HEAD(&tds->threads_running, td, runlist);
121         threads_unlock(tds);
122 }
123
124 /* Wait for a thread in the set to exit, and return its data pointer. */
125 void *
126 threads_wait(struct threads *tds)
127 {
128         struct thread *td;
129         void *data;
130
131         threads_lock(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);
135         }
136         td = STAILQ_FIRST(&tds->threads_dead);
137         STAILQ_REMOVE_HEAD(&tds->threads_dead, deadlist);
138         threads_unlock(tds);
139         data = td->data;
140         free(td);
141         return (data);
142 }
143
144 /* Free a threads set. */
145 void
146 threads_free(struct threads *tds)
147 {
148
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);
153         free(tds);
154 }
155
156 /*
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.
160  */
161 static void *
162 thread_start(void *data)
163 {
164         struct threads *tds;
165         struct thread *td;
166
167         td = data;
168         tds = td->threads;
169         td->start(td->data);
170         threads_lock(tds);
171         LIST_REMOVE(td, runlist);
172         STAILQ_INSERT_TAIL(&tds->threads_dead, td, deadlist);
173         pthread_cond_signal(&tds->thread_exited);
174         threads_unlock(tds);
175         return (NULL);
176 }