xref: /dragonfly/lib/libthread_xu/thread/thr_sem.c (revision 783d47c4)
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 
31 #include "namespace.h"
32 #include <machine/tls.h>
33 #include <sys/semaphore.h>
34 #include <sys/mman.h>
35 
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <pthread.h>
39 #include <stdlib.h>
40 #include <time.h>
41 #include "un-namespace.h"
42 #include "thr_private.h"
43 
44 /*
45  * Semaphore definitions.
46  */
47 struct sem {
48 	u_int32_t		magic;
49 	volatile umtx_t		count;
50 	int			semid;
51 	int			unused;	/* pad */
52 };
53 
54 #define	SEM_MAGIC	((u_int32_t) 0x09fa4012)
55 
56 #define SEMID_LWP	0
57 #define SEMID_FORK	1
58 
59 static inline int
60 sem_check_validity(sem_t *sem)
61 {
62 
63 	if ((sem != NULL) && (*sem != NULL) && ((*sem)->magic == SEM_MAGIC)) {
64 		return (0);
65 	} else {
66 		errno = EINVAL;
67 		return (-1);
68 	}
69 }
70 
71 static sem_t
72 sem_alloc(unsigned int value, int pshared)
73 {
74 	sem_t sem;
75 	int semid;
76 
77 	if (value > SEM_VALUE_MAX) {
78 		errno = EINVAL;
79 		return (NULL);
80 	}
81 	if (pshared) {
82 		static __thread sem_t sem_base;
83 		static __thread int sem_count;
84 
85 		if (sem_base == NULL) {
86 			sem_base = mmap(NULL, getpagesize(),
87 					PROT_READ | PROT_WRITE,
88 					MAP_ANON | MAP_SHARED,
89 					-1, 0);
90 			sem_count = getpagesize() / sizeof(*sem);
91 		}
92 		sem = sem_base++;
93 		if (--sem_count == 0)
94 			sem_base = NULL;
95 		semid = SEMID_FORK;
96 	} else {
97 		sem = malloc(sizeof(struct sem));
98 		semid = SEMID_LWP;
99 	}
100 	if (sem == NULL) {
101 		errno = ENOSPC;
102 		return (NULL);
103 	}
104 	sem->magic = SEM_MAGIC;
105 	sem->count = (u_int32_t)value;
106 	sem->semid = semid;
107 	return (sem);
108 }
109 
110 int
111 _sem_init(sem_t *sem, int pshared, unsigned int value)
112 {
113 	(*sem) = sem_alloc(value, pshared);
114 	if ((*sem) == NULL)
115 		return (-1);
116 	return (0);
117 }
118 
119 int
120 _sem_destroy(sem_t *sem)
121 {
122 	if (sem_check_validity(sem) != 0)
123 		return (-1);
124 
125 	(*sem)->magic = 0;
126 
127 	switch ((*sem)->semid) {
128 	case SEMID_LWP:
129 		free(*sem);
130 		break;
131 	case SEMID_FORK:
132 		/* memory is left intact */
133 		break;
134 	}
135 	return (0);
136 }
137 
138 int
139 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
140 {
141 	if (sem_check_validity(sem) != 0)
142 		return (-1);
143 
144 	*sval = (*sem)->count;
145 	return (0);
146 }
147 
148 int
149 _sem_trywait(sem_t *sem)
150 {
151 	int val;
152 
153 	if (sem_check_validity(sem) != 0)
154 		return (-1);
155 
156 	while ((val = (*sem)->count) > 0) {
157 		if (atomic_cmpset_int(&(*sem)->count, val, val - 1))
158 			return (0);
159 	}
160 	errno = EAGAIN;
161 	return (-1);
162 }
163 
164 int
165 _sem_wait(sem_t *sem)
166 {
167 	struct pthread *curthread;
168 	int val, oldcancel, retval;
169 
170 	if (sem_check_validity(sem) != 0)
171 		return (-1);
172 
173 	curthread = tls_get_curthread();
174 	_pthread_testcancel();
175 	do {
176 		while ((val = (*sem)->count) > 0) {
177 			if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
178 				return (0);
179 		}
180 		oldcancel = _thr_cancel_enter(curthread);
181 		retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0);
182 		_thr_cancel_leave(curthread, oldcancel);
183 	} while (retval == 0);
184 	errno = retval;
185 	return (-1);
186 }
187 
188 #if 0
189 int
190 _sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime)
191 {
192 	struct timespec ts, ts2;
193 	struct pthread *curthread;
194 	int val, oldcancel, retval;
195 
196 	if (sem_check_validity(sem) != 0)
197 		return (-1);
198 
199 	curthread = tls_get_curthread();
200 
201 	/*
202 	 * The timeout argument is only supposed to
203 	 * be checked if the thread would have blocked.
204 	 */
205 	_pthread_testcancel();
206 	do {
207 		while ((val = (*sem)->count) > 0) {
208 			if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
209 				return (0);
210 		}
211 		if (abstime == NULL) {
212 			errno = EINVAL;
213 			return (-1);
214 		}
215 		clock_gettime(CLOCK_REALTIME, &ts);
216 		TIMESPEC_SUB(&ts2, abstime, &ts);
217 		oldcancel = _thr_cancel_enter(curthread);
218 		retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2,
219 					CLOCK_REALTIME);
220 		_thr_cancel_leave(curthread, oldcancel);
221 	} while (retval == 0);
222 	errno = retval;
223 	return (-1);
224 }
225 #endif
226 
227 int
228 _sem_post(sem_t *sem)
229 {
230 	int val;
231 
232 	if (sem_check_validity(sem) != 0)
233 		return (-1);
234 
235 	/*
236 	 * sem_post() is required to be safe to call from within
237 	 * signal handlers, these code should work as that.
238 	 */
239 	do {
240 		val = (*sem)->count;
241 	} while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1));
242 	_thr_umtx_wake(&(*sem)->count, val + 1);
243 	return (0);
244 }
245 
246 sem_t *
247 _sem_open(__unused const char *name, __unused int oflag, ...)
248 {
249 	errno = ENOSYS;
250 	return (SEM_FAILED);
251 }
252 
253 int
254 _sem_close(__unused sem_t *sem)
255 {
256 	errno = ENOSYS;
257 	return (-1);
258 }
259 
260 int
261 _sem_unlink(__unused const char *name)
262 {
263 	errno = ENOSYS;
264 	return (-1);
265 }
266 
267 __strong_reference(_sem_destroy, sem_destroy);
268 __strong_reference(_sem_getvalue, sem_getvalue);
269 __strong_reference(_sem_init, sem_init);
270 __strong_reference(_sem_trywait, sem_trywait);
271 __strong_reference(_sem_wait, sem_wait);
272 #if 0
273 __strong_reference(_sem_timedwait, sem_timedwait);
274 #endif
275 __strong_reference(_sem_post, sem_post);
276 __strong_reference(_sem_open, sem_open);
277 __strong_reference(_sem_close, sem_close);
278 __strong_reference(_sem_unlink, sem_unlink);
279 
280