root / prex-0.9.0 / sys / sync / sem.c @ 03e9c04a
History | View | Annotate | Download (7.03 KB)
1 |
/*-
|
---|---|
2 |
* Copyright (c) 2005-2007, Kohsuke Ohtani
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions
|
7 |
* are met:
|
8 |
* 1. Redistributions of source code must retain the above copyright
|
9 |
* notice, this list of conditions and the following disclaimer.
|
10 |
* 2. Redistributions in binary form must reproduce the above copyright
|
11 |
* notice, this list of conditions and the following disclaimer in the
|
12 |
* documentation and/or other materials provided with the distribution.
|
13 |
* 3. Neither the name of the author nor the names of any co-contributors
|
14 |
* may be used to endorse or promote products derived from this software
|
15 |
* without specific prior written permission.
|
16 |
*
|
17 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
18 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
21 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
23 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
24 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
26 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
27 |
* SUCH DAMAGE.
|
28 |
*/
|
29 |
|
30 |
/*
|
31 |
* sem.c - semaphore support
|
32 |
*/
|
33 |
|
34 |
#include <kernel.h> |
35 |
#include <event.h> |
36 |
#include <sched.h> |
37 |
#include <kmem.h> |
38 |
#include <task.h> |
39 |
#include <sync.h> |
40 |
|
41 |
/* forward declarations */
|
42 |
static int sem_valid(sem_t); |
43 |
static void sem_release(sem_t); |
44 |
static void sem_reference(sem_t); |
45 |
static int sem_copyin(sem_t *, sem_t *); |
46 |
|
47 |
static struct sem *sem_list = NULL; /* list head of semaphore list */ |
48 |
|
49 |
/*
|
50 |
* sem_init - initialize a semaphore; required before use.
|
51 |
*
|
52 |
* sem_init() creates a new semaphore if the specified
|
53 |
* semaphore does not exist yet. If the semaphore already
|
54 |
* exists, it is re-initialized only if nobody is waiting for
|
55 |
* it. The initial semaphore value is set to the requested
|
56 |
* value. A semaphore can be shared among different tasks.
|
57 |
*/
|
58 |
int
|
59 |
sem_init(sem_t *sp, u_int value) |
60 |
{ |
61 |
task_t self = curtask; |
62 |
sem_t s; |
63 |
|
64 |
/*
|
65 |
* A couple of quick sanity checks.
|
66 |
*/
|
67 |
if (self->nsyncs >= MAXSYNCS)
|
68 |
return EAGAIN;
|
69 |
if (value > MAXSEMVAL)
|
70 |
return EINVAL;
|
71 |
if (copyin(sp, &s, sizeof(sp))) |
72 |
return EFAULT;
|
73 |
|
74 |
/*
|
75 |
* An application can call sem_init() to reset the
|
76 |
* value of existing semaphore. So, we have to check
|
77 |
* whether the semaphore is already allocated.
|
78 |
*/
|
79 |
sched_lock(); |
80 |
if (sem_valid(s)) {
|
81 |
/*
|
82 |
* Semaphore already exists.
|
83 |
*/
|
84 |
if (s->owner != self) {
|
85 |
sched_unlock(); |
86 |
return EINVAL;
|
87 |
} |
88 |
if (event_waiting(&s->event)) {
|
89 |
sched_unlock(); |
90 |
return EBUSY;
|
91 |
} |
92 |
s->value = value; |
93 |
|
94 |
} else {
|
95 |
/*
|
96 |
* Create new semaphore.
|
97 |
*/
|
98 |
if ((s = kmem_alloc(sizeof(struct sem))) == NULL) { |
99 |
sched_unlock(); |
100 |
return ENOSPC;
|
101 |
} |
102 |
if (copyout(&s, sp, sizeof(s))) { |
103 |
kmem_free(s); |
104 |
sched_unlock(); |
105 |
return EFAULT;
|
106 |
} |
107 |
event_init(&s->event, "semaphore");
|
108 |
s->owner = self; |
109 |
s->refcnt = 1;
|
110 |
s->value = value; |
111 |
|
112 |
list_insert(&self->sems, &s->task_link); |
113 |
self->nsyncs++; |
114 |
s->next = sem_list; |
115 |
sem_list = s; |
116 |
} |
117 |
sched_unlock(); |
118 |
return 0; |
119 |
} |
120 |
|
121 |
/*
|
122 |
* Destroy a semaphore.
|
123 |
* If some thread is waiting for the specified semaphore,
|
124 |
* this routine fails with EBUSY.
|
125 |
*/
|
126 |
int
|
127 |
sem_destroy(sem_t *sp) |
128 |
{ |
129 |
sem_t s; |
130 |
|
131 |
sched_lock(); |
132 |
if (sem_copyin(sp, &s) || s->owner != curtask) {
|
133 |
sched_unlock(); |
134 |
return EINVAL;
|
135 |
} |
136 |
if (event_waiting(&s->event) || s->value == 0) { |
137 |
sched_unlock(); |
138 |
return EBUSY;
|
139 |
} |
140 |
sem_release(s); |
141 |
sched_unlock(); |
142 |
return 0; |
143 |
} |
144 |
|
145 |
/*
|
146 |
* sem_wait - lock a semaphore.
|
147 |
*
|
148 |
* The value of timeout is msec unit. 0 for no timeout.
|
149 |
*
|
150 |
* sem_wait() locks the semaphore referred by sem only if the
|
151 |
* semaphore value is currently positive. The thread will
|
152 |
* sleep while the semaphore value is zero. It decrements the
|
153 |
* semaphore value in return.
|
154 |
*
|
155 |
* If waiting thread receives any exception, this routine
|
156 |
* returns with EINTR in order to invoke exception
|
157 |
* handler. But, an application assumes this call does NOT
|
158 |
* return with an error. So, the system call stub routine will
|
159 |
* automatically call sem_wait again if it gets EINTR.
|
160 |
*/
|
161 |
int
|
162 |
sem_wait(sem_t *sp, u_long timeout) |
163 |
{ |
164 |
sem_t s; |
165 |
int rc, error = 0; |
166 |
|
167 |
sched_lock(); |
168 |
if (sem_copyin(sp, &s)) {
|
169 |
sched_unlock(); |
170 |
return EINVAL;
|
171 |
} |
172 |
sem_reference(s); |
173 |
|
174 |
while (s->value == 0) { |
175 |
rc = sched_tsleep(&s->event, timeout); |
176 |
if (rc == SLP_TIMEOUT) {
|
177 |
error = ETIMEDOUT; |
178 |
break;
|
179 |
} |
180 |
if (rc == SLP_INTR) {
|
181 |
error = EINTR; |
182 |
break;
|
183 |
} |
184 |
/*
|
185 |
* We have to check the semaphore value again
|
186 |
* because another thread may run and acquire
|
187 |
* the semaphore before us.
|
188 |
*/
|
189 |
} |
190 |
if (!error)
|
191 |
s->value--; |
192 |
|
193 |
sem_release(s); |
194 |
sched_unlock(); |
195 |
return error;
|
196 |
} |
197 |
|
198 |
/*
|
199 |
* Try to lock a semaphore.
|
200 |
* If the semaphore is already locked, it just returns EAGAIN.
|
201 |
*/
|
202 |
int
|
203 |
sem_trywait(sem_t *sp) |
204 |
{ |
205 |
sem_t s; |
206 |
|
207 |
sched_lock(); |
208 |
if (sem_copyin(sp, &s)) {
|
209 |
sched_unlock(); |
210 |
return EINVAL;
|
211 |
} |
212 |
if (s->value == 0) { |
213 |
sched_unlock(); |
214 |
return EAGAIN;
|
215 |
} |
216 |
s->value--; |
217 |
sched_unlock(); |
218 |
return 0; |
219 |
} |
220 |
|
221 |
/*
|
222 |
* Unlock a semaphore.
|
223 |
*
|
224 |
* If the semaphore value becomes non zero, then one of
|
225 |
* the threads blocked waiting for the semaphore will be
|
226 |
* unblocked. This is non-blocking operation.
|
227 |
*/
|
228 |
int
|
229 |
sem_post(sem_t *sp) |
230 |
{ |
231 |
sem_t s; |
232 |
|
233 |
sched_lock(); |
234 |
if (sem_copyin(sp, &s)) {
|
235 |
sched_unlock(); |
236 |
return EINVAL;
|
237 |
} |
238 |
if (s->value >= MAXSEMVAL) {
|
239 |
sched_unlock(); |
240 |
return ERANGE;
|
241 |
} |
242 |
s->value++; |
243 |
if (s->value > 0) |
244 |
sched_wakeone(&s->event); |
245 |
|
246 |
sched_unlock(); |
247 |
return 0; |
248 |
} |
249 |
|
250 |
/*
|
251 |
* Get the semaphore value.
|
252 |
*/
|
253 |
int
|
254 |
sem_getvalue(sem_t *sp, u_int *value) |
255 |
{ |
256 |
sem_t s; |
257 |
|
258 |
sched_lock(); |
259 |
if (sem_copyin(sp, &s)) {
|
260 |
sched_unlock(); |
261 |
return EINVAL;
|
262 |
} |
263 |
if (copyout(&s->value, value, sizeof(s->value))) { |
264 |
sched_unlock(); |
265 |
return EFAULT;
|
266 |
} |
267 |
sched_unlock(); |
268 |
return 0; |
269 |
} |
270 |
|
271 |
/*
|
272 |
* Take out a reference on a semaphore.
|
273 |
*/
|
274 |
static void |
275 |
sem_reference(sem_t s) |
276 |
{ |
277 |
|
278 |
s->refcnt++; |
279 |
} |
280 |
|
281 |
/*
|
282 |
* Release a reference on a semaphore. If this is the last
|
283 |
* reference, the semaphore data structure is deallocated.
|
284 |
*/
|
285 |
static void |
286 |
sem_release(sem_t s) |
287 |
{ |
288 |
sem_t *sp; |
289 |
|
290 |
if (--s->refcnt > 0) |
291 |
return;
|
292 |
|
293 |
list_remove(&s->task_link); |
294 |
s->owner->nsyncs--; |
295 |
|
296 |
for (sp = &sem_list; *sp; sp = &(*sp)->next) {
|
297 |
if (*sp == s) {
|
298 |
*sp = s->next; |
299 |
break;
|
300 |
} |
301 |
} |
302 |
kmem_free(s); |
303 |
} |
304 |
|
305 |
void
|
306 |
sem_cleanup(task_t task) |
307 |
{ |
308 |
list_t head, n; |
309 |
sem_t s; |
310 |
|
311 |
head = &task->sems; |
312 |
for (n = list_first(head); n != head; n = list_next(n)) {
|
313 |
s = list_entry(n, struct sem, task_link);
|
314 |
sem_release(s); |
315 |
} |
316 |
} |
317 |
|
318 |
static int |
319 |
sem_valid(sem_t s) |
320 |
{ |
321 |
sem_t tmp; |
322 |
|
323 |
for (tmp = sem_list; tmp; tmp = tmp->next) {
|
324 |
if (tmp == s)
|
325 |
return 1; |
326 |
} |
327 |
return 0; |
328 |
} |
329 |
|
330 |
/*
|
331 |
* sem_copyin - copy a semaphore from user space.
|
332 |
*
|
333 |
* It also checks whether the passed semaphore is valid.
|
334 |
*/
|
335 |
static int |
336 |
sem_copyin(sem_t *usp, sem_t *ksp) |
337 |
{ |
338 |
sem_t s; |
339 |
|
340 |
if (copyin(usp, &s, sizeof(usp)) || !sem_valid(s)) |
341 |
return EINVAL;
|
342 |
|
343 |
*ksp = s; |
344 |
return 0; |
345 |
} |