Statistics
| Branch: | Revision:

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
}