xref: /dragonfly/lib/libthread_xu/thread/thr_sem.c (revision f746689a)
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 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <pthread.h>
40 #include <stdlib.h>
41 #include <time.h>
42 #include "un-namespace.h"
43 #include "thr_private.h"
44 
45 /*
46  * Semaphore definitions.
47  */
48 struct sem {
49 #define	SEM_MAGIC	((u_int32_t) 0x09fa4012)
50 	u_int32_t		magic;
51 	volatile umtx_t		count;
52 	int			semid;	/* kernel based semaphore id. */
53 };
54 
55 static inline int
56 sem_check_validity(sem_t *sem)
57 {
58 
59 	if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
60 		return (0);
61 	else {
62 		errno = EINVAL;
63 		return (-1);
64 	}
65 }
66 
67 static sem_t
68 sem_alloc(unsigned int value, int semid)
69 {
70 	sem_t sem;
71 
72 	if (value > SEM_VALUE_MAX) {
73 		errno = EINVAL;
74 		return (NULL);
75 	}
76 
77 	sem = (sem_t)malloc(sizeof(struct sem));
78 	if (sem == NULL) {
79 		errno = ENOSPC;
80 		return (NULL);
81 	}
82 	sem->magic = SEM_MAGIC;
83 	sem->count = (u_int32_t)value;
84 	sem->semid = semid;
85 	return (sem);
86 }
87 
88 int
89 _sem_init(sem_t *sem, int pshared, unsigned int value)
90 {
91 	if (pshared != 0) {
92 		/*
93 		 * We really can support pshared, but sem_t was
94 		 * defined as a pointer, if it is a structure,
95 		 * it will work between processes.
96 		 */
97 		errno = EPERM;
98 		return (-1);
99 	}
100 
101 	(*sem) = sem_alloc(value, -1);
102 	if ((*sem) == NULL)
103 		return (-1);
104 	return (0);
105 }
106 
107 int
108 _sem_destroy(sem_t *sem)
109 {
110 	if (sem_check_validity(sem) != 0)
111 		return (-1);
112 
113 	free(*sem);
114 	return (0);
115 }
116 
117 int
118 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
119 {
120 	if (sem_check_validity(sem) != 0)
121 		return (-1);
122 
123 	*sval = (*sem)->count;
124 	return (0);
125 }
126 
127 int
128 _sem_trywait(sem_t *sem)
129 {
130 	int val;
131 
132 	if (sem_check_validity(sem) != 0)
133 		return (-1);
134 
135 	while ((val = (*sem)->count) > 0) {
136 		if (atomic_cmpset_int(&(*sem)->count, val, val - 1))
137 			return (0);
138 	}
139 	errno = EAGAIN;
140 	return (-1);
141 }
142 
143 int
144 _sem_wait(sem_t *sem)
145 {
146 	struct pthread *curthread;
147 	int val, oldcancel, retval;
148 
149 	if (sem_check_validity(sem) != 0)
150 		return (-1);
151 
152 	curthread = tls_get_curthread();
153 	_pthread_testcancel();
154 	do {
155 		while ((val = (*sem)->count) > 0) {
156 			if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
157 				return (0);
158 		}
159 		oldcancel = _thr_cancel_enter(curthread);
160 		retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0);
161 		_thr_cancel_leave(curthread, oldcancel);
162 	} while (retval == 0);
163 	errno = retval;
164 	return (-1);
165 }
166 
167 #if 0
168 int
169 _sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime)
170 {
171 	struct timespec ts, ts2;
172 	struct pthread *curthread;
173 	int val, oldcancel, retval;
174 
175 	if (sem_check_validity(sem) != 0)
176 		return (-1);
177 
178 	curthread = tls_get_curthread();
179 
180 	/*
181 	 * The timeout argument is only supposed to
182 	 * be checked if the thread would have blocked.
183 	 */
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 		if (abstime == NULL) {
191 			errno = EINVAL;
192 			return (-1);
193 		}
194 		clock_gettime(CLOCK_REALTIME, &ts);
195 		TIMESPEC_SUB(&ts2, abstime, &ts);
196 		oldcancel = _thr_cancel_enter(curthread);
197 		retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2,
198 					CLOCK_REALTIME);
199 		_thr_cancel_leave(curthread, oldcancel);
200 	} while (retval == 0);
201 	errno = retval;
202 	return (-1);
203 }
204 #endif
205 
206 int
207 _sem_post(sem_t *sem)
208 {
209 	int val;
210 
211 	if (sem_check_validity(sem) != 0)
212 		return (-1);
213 
214 	/*
215 	 * sem_post() is required to be safe to call from within
216 	 * signal handlers, these code should work as that.
217 	 */
218 	do {
219 		val = (*sem)->count;
220 	} while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1));
221 	_thr_umtx_wake(&(*sem)->count, val + 1);
222 	return (0);
223 }
224 
225 __strong_reference(_sem_destroy, sem_destroy);
226 __strong_reference(_sem_getvalue, sem_getvalue);
227 __strong_reference(_sem_init, sem_init);
228 __strong_reference(_sem_trywait, sem_trywait);
229 __strong_reference(_sem_wait, sem_wait);
230 #if 0
231 __strong_reference(_sem_timedwait, sem_timedwait);
232 #endif
233 __strong_reference(_sem_post, sem_post);
234 
235