xref: /openbsd-src/lib/librthread/rthread_sem.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: rthread_sem.c,v 1.7 2012/03/03 11:09:19 guenther Exp $ */
2 /*
3  * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <errno.h>
22 
23 #include <pthread.h>
24 
25 #include "rthread.h"
26 
27 /*
28  * Internal implementation of semaphores
29  */
30 int
31 _sem_wait(sem_t sem, int tryonly, const struct timespec *abstime,
32     int *delayed_cancel)
33 {
34 	int r;
35 
36 	_spinlock(&sem->lock);
37 	if (sem->value) {
38 		sem->value--;
39 		r = 0;
40 	} else if (tryonly) {
41 		r = EAGAIN;
42 	} else {
43 		sem->waitcount++;
44 		do {
45 			r = __thrsleep(&sem->waitcount, CLOCK_REALTIME,
46 			    abstime, &sem->lock, delayed_cancel);
47 			_spinlock(&sem->lock);
48 			/* ignore interruptions other than cancelation */
49 			if (r == EINTR && (delayed_cancel == NULL ||
50 			    *delayed_cancel == 0))
51 				r = 0;
52 		} while (r == 0 && sem->value == 0);
53 		sem->waitcount--;
54 		if (r == 0)
55 			sem->value--;
56 	}
57 	_spinunlock(&sem->lock);
58 	return (r);
59 }
60 
61 /* always increment count */
62 int
63 _sem_post(sem_t sem)
64 {
65 	int rv = 0;
66 
67 	_spinlock(&sem->lock);
68 	sem->value++;
69 	if (sem->waitcount) {
70 		__thrwakeup(&sem->waitcount, 1);
71 		rv = 1;
72 	}
73 	_spinunlock(&sem->lock);
74 	return (rv);
75 }
76 
77 /*
78  * exported semaphores
79  */
80 int
81 sem_init(sem_t *semp, int pshared, unsigned int value)
82 {
83 	sem_t sem;
84 
85 	if (pshared) {
86 		errno = EPERM;
87 		return (-1);
88 	}
89 
90 	if (value > SEM_VALUE_MAX) {
91 		errno = EINVAL;
92 		return (-1);
93 	}
94 
95 	sem = calloc(1, sizeof(*sem));
96 	if (!sem) {
97 		errno = ENOSPC;
98 		return (-1);
99 	}
100 	sem->lock = _SPINLOCK_UNLOCKED;
101 	sem->value = value;
102 	*semp = sem;
103 
104 	return (0);
105 }
106 
107 int
108 sem_destroy(sem_t *semp)
109 {
110 	if (!semp || !*semp) {
111 		errno = EINVAL;
112 		return (-1);
113 	}
114 
115 	if ((*semp)->waitcount) {
116 #define MSG "sem_destroy on semaphore with waiters!\n"
117 		write(2, MSG, sizeof(MSG) - 1);
118 #undef MSG
119 		errno = EBUSY;
120 		return (-1);
121 	}
122 	free(*semp);
123 	*semp = NULL;
124 
125 	return (0);
126 }
127 
128 int
129 sem_getvalue(sem_t *semp, int *sval)
130 {
131 	sem_t sem = *semp;
132 
133 	if (!semp || !*semp) {
134 		errno = EINVAL;
135 		return (-1);
136 	}
137 
138 	_spinlock(&sem->lock);
139 	*sval = sem->value;
140 	_spinunlock(&sem->lock);
141 
142 	return (0);
143 }
144 
145 int
146 sem_post(sem_t *semp)
147 {
148 	sem_t sem = *semp;
149 
150 	if (!semp || !*semp) {
151 		errno = EINVAL;
152 		return (-1);
153 	}
154 
155 	_sem_post(sem);
156 
157 	return (0);
158 }
159 
160 int
161 sem_wait(sem_t *semp)
162 {
163 	sem_t sem = *semp;
164 	pthread_t self = pthread_self();
165 	int r;
166 
167 	if (!semp || !*semp) {
168 		errno = EINVAL;
169 		return (-1);
170 	}
171 
172 	_enter_delayed_cancel(self);
173 	r = _sem_wait(sem, 0, NULL, &self->delayed_cancel);
174 	_leave_delayed_cancel(self, r);
175 
176 	if (r) {
177 		errno = r;
178 		return (-1);
179 	}
180 
181 	return (0);
182 }
183 
184 int
185 sem_timedwait(sem_t *semp, const struct timespec *abstime)
186 {
187 	sem_t sem = *semp;
188 	pthread_t self = pthread_self();
189 	int r;
190 
191 	if (!semp || !*semp) {
192 		errno = EINVAL;
193 		return (-1);
194 	}
195 
196 	_enter_delayed_cancel(self);
197 	r = _sem_wait(sem, 0, abstime, &self->delayed_cancel);
198 	_leave_delayed_cancel(self, r);
199 
200 	if (r) {
201 		errno = r == EWOULDBLOCK ? ETIMEDOUT : r;
202 		return (-1);
203 	}
204 
205 	return (0);
206 }
207 
208 int
209 sem_trywait(sem_t *semp)
210 {
211 	sem_t sem = *semp;
212 	int r;
213 
214 	if (!semp || !*semp) {
215 		errno = EINVAL;
216 		return (-1);
217 	}
218 
219 	r = _sem_wait(sem, 1, NULL, NULL);
220 
221 	if (r) {
222 		errno = r;
223 		return (-1);
224 	}
225 
226 	return (0);
227 }
228 
229 /* ARGSUSED */
230 sem_t *
231 sem_open(const char *name __unused, int oflag __unused, ...)
232 {
233 	errno = ENOSYS;
234 	return (SEM_FAILED);
235 }
236 
237 /* ARGSUSED */
238 int
239 sem_close(sem_t *sem __unused)
240 {
241 	errno = ENOSYS;
242 	return (-1);
243 }
244 
245 /* ARGSUSED */
246 int
247 sem_unlink(const char *name __unused)
248 {
249 	errno = ENOSYS;
250 	return (-1);
251 }
252 
253