xref: /dflybsd-src/lib/libthread_xu/thread/thr_sem.c (revision c2576c10ab1201aa47eb10096e226be7b357d01d)
1 /*
2  * Copyright (C) 2005 David Xu <davidxu@freebsd.org>.
3  * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice(s), this list of conditions and the following disclaimer as
11  *    the first lines of this file unmodified other than the possible
12  *    addition of one or more copyright notices.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice(s), this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $FreeBSD: src/lib/libpthread/thread/thr_sem.c,v 1.16 2004/12/18 18:07:37 deischen Exp $
31  * $DragonFly: src/lib/libthread_xu/thread/thr_sem.c,v 1.1 2005/02/01 12:38:27 davidxu Exp $
32  */
33 
34 #include <sys/queue.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <pthread.h>
38 #include <semaphore.h>
39 #include <stdlib.h>
40 #include <time.h>
41 
42 #include "thr_private.h"
43 
44 __weak_reference(_sem_close, sem_close);
45 __weak_reference(_sem_destroy, sem_destroy);
46 __weak_reference(_sem_getvalue, sem_getvalue);
47 __weak_reference(_sem_init, sem_init);
48 __weak_reference(_sem_open, sem_open);
49 __weak_reference(_sem_trywait, sem_trywait);
50 __weak_reference(_sem_wait, sem_wait);
51 __weak_reference(_sem_timedwait, sem_timedwait);
52 __weak_reference(_sem_post, sem_post);
53 __weak_reference(_sem_unlink, sem_unlink);
54 
55 /*
56  * Semaphore definitions.
57  */
58 struct sem {
59 #define	SEM_MAGIC	((u_int32_t) 0x09fa4012)
60 	u_int32_t		magic;
61 	volatile umtx_t		count;
62 	int			semid;	/* kernel based semaphore id. */
63 };
64 
65 static inline int
66 sem_check_validity(sem_t *sem)
67 {
68 
69 	if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
70 		return (0);
71 	else {
72 		errno = EINVAL;
73 		return (-1);
74 	}
75 }
76 
77 static sem_t
78 sem_alloc(unsigned int value, int semid)
79 {
80 	sem_t sem;
81 
82 	if (value > SEM_VALUE_MAX) {
83 		errno = EINVAL;
84 		return (NULL);
85 	}
86 
87 	sem = (sem_t)malloc(sizeof(struct sem));
88 	if (sem == NULL) {
89 		errno = ENOSPC;
90 		return (NULL);
91 	}
92 	sem->magic = SEM_MAGIC;
93 	sem->count = (u_int32_t)value;
94 	sem->semid = semid;
95 	return (sem);
96 }
97 
98 int
99 _sem_init(sem_t *sem, int pshared, unsigned int value)
100 {
101 	if (pshared != 0) {
102 		/*
103 		 * We really can support pshared, but sem_t was
104 		 * defined as a pointer, if it is a structure,
105 		 * it will work between processes.
106 		 */
107 		errno = EPERM;
108 		return (-1);
109 	}
110 
111 	(*sem) = sem_alloc(value, -1);
112 	if ((*sem) == NULL)
113 		return (-1);
114 	return (0);
115 }
116 
117 int
118 _sem_destroy(sem_t *sem)
119 {
120 	if (sem_check_validity(sem) != 0)
121 		return (-1);
122 
123 	free(*sem);
124 	return (0);
125 }
126 
127 sem_t *
128 _sem_open(const char *name, int oflag, ...)
129 {
130 	errno = ENOSYS;
131 	return SEM_FAILED;
132 }
133 
134 int
135 _sem_close(sem_t *sem)
136 {
137 	errno = ENOSYS;
138 	return -1;
139 }
140 
141 int
142 _sem_unlink(const char *name)
143 {
144 	errno = ENOSYS;
145 	return -1;
146 }
147 
148 int
149 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
150 {
151 	if (sem_check_validity(sem) != 0)
152 		return (-1);
153 
154 	*sval = (*sem)->count;
155 	return (0);
156 }
157 
158 int
159 _sem_trywait(sem_t *sem)
160 {
161 	int val;
162 
163 	if (sem_check_validity(sem) != 0)
164 		return (-1);
165 
166 	while ((val = (*sem)->count) > 0) {
167 		if (atomic_cmpset_int(&(*sem)->count, val, val - 1))
168 			return (0);
169 	}
170 	errno = EAGAIN;
171 	return (-1);
172 }
173 
174 int
175 _sem_wait(sem_t *sem)
176 {
177 	struct pthread *curthread;
178 	int val, oldcancel, retval;
179 
180 	if (sem_check_validity(sem) != 0)
181 		return (-1);
182 
183 	curthread = _get_curthread();
184 	_pthread_testcancel();
185 	do {
186 		while ((val = (*sem)->count) > 0) {
187 			if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
188 				return (0);
189 		}
190 		oldcancel = _thr_cancel_enter(curthread);
191 		retval = _thr_umtx_wait(&(*sem)->count, 0, NULL);
192 		_thr_cancel_leave(curthread, oldcancel);
193 	} while (retval == 0);
194 	errno = retval;
195 	return (-1);
196 }
197 
198 int
199 _sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime)
200 {
201 	struct timespec ts, ts2;
202 	struct pthread *curthread;
203 	int val, oldcancel, retval;
204 
205 	if (sem_check_validity(sem) != 0)
206 		return (-1);
207 
208 	curthread = _get_curthread();
209 
210 	/*
211 	 * The timeout argument is only supposed to
212 	 * be checked if the thread would have blocked.
213 	 */
214 	_pthread_testcancel();
215 	do {
216 		while ((val = (*sem)->count) > 0) {
217 			if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
218 				return (0);
219 		}
220 		if (abstime == NULL) {
221 			errno = EINVAL;
222 			return (-1);
223 		}
224 		clock_gettime(CLOCK_REALTIME, &ts);
225 		TIMESPEC_SUB(&ts2, abstime, &ts);
226 		oldcancel = _thr_cancel_enter(curthread);
227 		retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2);
228 		_thr_cancel_leave(curthread, oldcancel);
229 	} while (retval == 0);
230 	errno = retval;
231 	return (-1);
232 }
233 
234 int
235 _sem_post(sem_t *sem)
236 {
237 	int val;
238 
239 	if (sem_check_validity(sem) != 0)
240 		return (-1);
241 
242 	/*
243 	 * sem_post() is required to be safe to call from within
244 	 * signal handlers, these code should work as that.
245 	 */
246 	do {
247 		val = (*sem)->count;
248 	} while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1));
249 	_thr_umtx_wake(&(*sem)->count, val + 1);
250 	return (0);
251 }
252