xref: /freebsd/lib/libthr/thread/thr_sem.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
1 /*
2  * Copyright (C) 2000 Jason Evans <jasone@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(s), this list of conditions and the following disclaimer as
10  *    the first lines of this file unmodified other than the possible
11  *    addition of one or more copyright notices.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice(s), this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <semaphore.h>
35 #include <pthread.h>
36 #include "thr_private.h"
37 
38 #define _SEM_CHECK_VALIDITY(sem)		\
39 	if ((*(sem))->magic != SEM_MAGIC) {	\
40 		errno = EINVAL;			\
41 		retval = -1;			\
42 		goto RETURN;			\
43 	}
44 
45 __weak_reference(_sem_init, sem_init);
46 __weak_reference(_sem_destroy, sem_destroy);
47 __weak_reference(_sem_open, sem_open);
48 __weak_reference(_sem_close, sem_close);
49 __weak_reference(_sem_unlink, sem_unlink);
50 __weak_reference(_sem_wait, sem_wait);
51 __weak_reference(_sem_trywait, sem_trywait);
52 __weak_reference(_sem_post, sem_post);
53 __weak_reference(_sem_getvalue, sem_getvalue);
54 
55 
56 int
57 _sem_init(sem_t *sem, int pshared, unsigned int value)
58 {
59 	int	retval;
60 
61 	/*
62 	 * Range check the arguments.
63 	 */
64 	if (pshared != 0) {
65 		/*
66 		 * The user wants a semaphore that can be shared among
67 		 * processes, which this implementation can't do.  Sounds like a
68 		 * permissions problem to me (yeah right).
69 		 */
70 		errno = EPERM;
71 		retval = -1;
72 		goto RETURN;
73 	}
74 
75 	if (value > SEM_VALUE_MAX) {
76 		errno = EINVAL;
77 		retval = -1;
78 		goto RETURN;
79 	}
80 
81 	*sem = (sem_t)malloc(sizeof(struct sem));
82 	if (*sem == NULL) {
83 		errno = ENOSPC;
84 		retval = -1;
85 		goto RETURN;
86 	}
87 
88 	/*
89 	 * Initialize the semaphore.
90 	 */
91 	if (pthread_mutex_init(&(*sem)->lock, NULL) != 0) {
92 		free(*sem);
93 		errno = ENOSPC;
94 		retval = -1;
95 		goto RETURN;
96 	}
97 
98 	if (pthread_cond_init(&(*sem)->gtzero, NULL) != 0) {
99 		pthread_mutex_destroy(&(*sem)->lock);
100 		free(*sem);
101 		errno = ENOSPC;
102 		retval = -1;
103 		goto RETURN;
104 	}
105 
106 	(*sem)->count = (u_int32_t)value;
107 	(*sem)->nwaiters = 0;
108 	(*sem)->magic = SEM_MAGIC;
109 
110 	retval = 0;
111   RETURN:
112 	return retval;
113 }
114 
115 int
116 _sem_destroy(sem_t *sem)
117 {
118 	int	retval;
119 
120 	_SEM_CHECK_VALIDITY(sem);
121 
122 	/* Make sure there are no waiters. */
123 	pthread_mutex_lock(&(*sem)->lock);
124 	if ((*sem)->nwaiters > 0) {
125 		pthread_mutex_unlock(&(*sem)->lock);
126 		errno = EBUSY;
127 		retval = -1;
128 		goto RETURN;
129 	}
130 	pthread_mutex_unlock(&(*sem)->lock);
131 
132 	pthread_mutex_destroy(&(*sem)->lock);
133 	pthread_cond_destroy(&(*sem)->gtzero);
134 	(*sem)->magic = 0;
135 
136 	free(*sem);
137 
138 	retval = 0;
139   RETURN:
140 	return retval;
141 }
142 
143 sem_t *
144 _sem_open(const char *name, int oflag, ...)
145 {
146 	errno = ENOSYS;
147 	return SEM_FAILED;
148 }
149 
150 int
151 _sem_close(sem_t *sem)
152 {
153 	errno = ENOSYS;
154 	return -1;
155 }
156 
157 int
158 _sem_unlink(const char *name)
159 {
160 	errno = ENOSYS;
161 	return -1;
162 }
163 
164 int
165 _sem_wait(sem_t *sem)
166 {
167 	int	retval;
168 
169 	_thread_enter_cancellation_point();
170 
171 	_SEM_CHECK_VALIDITY(sem);
172 
173 	pthread_mutex_lock(&(*sem)->lock);
174 
175 	while ((*sem)->count == 0) {
176 		(*sem)->nwaiters++;
177 		pthread_cond_wait(&(*sem)->gtzero, &(*sem)->lock);
178 		(*sem)->nwaiters--;
179 	}
180 	(*sem)->count--;
181 
182 	pthread_mutex_unlock(&(*sem)->lock);
183 
184 	retval = 0;
185   RETURN:
186 	_thread_leave_cancellation_point();
187 	return retval;
188 }
189 
190 int
191 _sem_trywait(sem_t *sem)
192 {
193 	int	retval;
194 
195 	_SEM_CHECK_VALIDITY(sem);
196 
197 	pthread_mutex_lock(&(*sem)->lock);
198 
199 	if ((*sem)->count > 0) {
200 		(*sem)->count--;
201 		retval = 0;
202 	} else {
203 		errno = EAGAIN;
204 		retval = -1;
205 	}
206 
207 	pthread_mutex_unlock(&(*sem)->lock);
208 
209   RETURN:
210 	return retval;
211 }
212 
213 int
214 _sem_post(sem_t *sem)
215 {
216 	int	retval;
217 
218 	_SEM_CHECK_VALIDITY(sem);
219 
220 	/*
221 	 * sem_post() is required to be safe to call from within signal
222 	 * handlers.  Thus, we must defer signals.
223 	 */
224 	pthread_mutex_lock(&(*sem)->lock);
225 
226 	/* GIANT_LOCK(curthread); */
227 
228 	(*sem)->count++;
229 	if ((*sem)->nwaiters > 0)
230 		pthread_cond_signal(&(*sem)->gtzero);
231 
232 	/* GIANT_UNLOCK(curthread); */
233 
234 	pthread_mutex_unlock(&(*sem)->lock);
235 
236 	retval = 0;
237   RETURN:
238 	return retval;
239 }
240 
241 int
242 _sem_getvalue(sem_t *sem, int *sval)
243 {
244 	int	retval;
245 
246 	_SEM_CHECK_VALIDITY(sem);
247 
248 	pthread_mutex_lock(&(*sem)->lock);
249 	*sval = (int)(*sem)->count;
250 	pthread_mutex_unlock(&(*sem)->lock);
251 
252 	retval = 0;
253   RETURN:
254 	return retval;
255 }
256