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