Prex Home / Browse Source - Prex Version: 0.9.0

root/sys/sync/sem.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sem_init
  2. sem_destroy
  3. sem_wait
  4. sem_trywait
  5. sem_post
  6. sem_getvalue
  7. sem_reference
  8. sem_release
  9. sem_cleanup
  10. sem_valid
  11. sem_copyin

   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 }

/* [<][>][^][v][top][bottom][index][help] */