Back to index

glibc  2.9
semaphore.c
Go to the documentation of this file.
00001 /* Linuxthreads - a simple clone()-based implementation of Posix        */
00002 /* threads for Linux.                                                   */
00003 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
00004 /*                                                                      */
00005 /* This program is free software; you can redistribute it and/or        */
00006 /* modify it under the terms of the GNU Library General Public License  */
00007 /* as published by the Free Software Foundation; either version 2       */
00008 /* of the License, or (at your option) any later version.               */
00009 /*                                                                      */
00010 /* This program is distributed in the hope that it will be useful,      */
00011 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
00012 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
00013 /* GNU Library General Public License for more details.                 */
00014 
00015 /* Semaphores a la POSIX 1003.1b */
00016 
00017 #include <errno.h>
00018 #include "pthread.h"
00019 #include "semaphore.h"
00020 #include "internals.h"
00021 #include "spinlock.h"
00022 #include "restart.h"
00023 #include "queue.h"
00024 #include <shlib-compat.h>
00025 #include <not-cancel.h>
00026 
00027 int __new_sem_init(sem_t *sem, int pshared, unsigned int value)
00028 {
00029   if (value > SEM_VALUE_MAX) {
00030     errno = EINVAL;
00031     return -1;
00032   }
00033   if (pshared) {
00034     errno = ENOSYS;
00035     return -1;
00036   }
00037   __pthread_init_lock(&sem->__sem_lock);
00038   sem->__sem_value = value;
00039   sem->__sem_waiting = NULL;
00040   return 0;
00041 }
00042 
00043 /* Function called by pthread_cancel to remove the thread from
00044    waiting inside __new_sem_wait. */
00045 
00046 static int new_sem_extricate_func(void *obj, pthread_descr th)
00047 {
00048   volatile pthread_descr self = thread_self();
00049   sem_t *sem = obj;
00050   int did_remove = 0;
00051 
00052   __pthread_lock(&sem->__sem_lock, self);
00053   did_remove = remove_from_queue(&sem->__sem_waiting, th);
00054   __pthread_unlock(&sem->__sem_lock);
00055 
00056   return did_remove;
00057 }
00058 
00059 int __new_sem_wait(sem_t * sem)
00060 {
00061   volatile pthread_descr self = thread_self();
00062   pthread_extricate_if extr;
00063   int already_canceled = 0;
00064   int spurious_wakeup_count;
00065 
00066   /* Set up extrication interface */
00067   extr.pu_object = sem;
00068   extr.pu_extricate_func = new_sem_extricate_func;
00069 
00070   __pthread_lock(&sem->__sem_lock, self);
00071   if (sem->__sem_value > 0) {
00072     sem->__sem_value--;
00073     __pthread_unlock(&sem->__sem_lock);
00074     return 0;
00075   }
00076   /* Register extrication interface */
00077   THREAD_SETMEM(self, p_sem_avail, 0);
00078   __pthread_set_own_extricate_if(self, &extr);
00079   /* Enqueue only if not already cancelled. */
00080   if (!(THREAD_GETMEM(self, p_canceled)
00081       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
00082     enqueue(&sem->__sem_waiting, self);
00083   else
00084     already_canceled = 1;
00085   __pthread_unlock(&sem->__sem_lock);
00086 
00087   if (already_canceled) {
00088     __pthread_set_own_extricate_if(self, 0);
00089     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00090   }
00091 
00092   /* Wait for sem_post or cancellation, or fall through if already canceled */
00093   spurious_wakeup_count = 0;
00094   while (1)
00095     {
00096       suspend(self);
00097       if (THREAD_GETMEM(self, p_sem_avail) == 0
00098          && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
00099              || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
00100        {
00101          /* Count resumes that don't belong to us. */
00102          spurious_wakeup_count++;
00103          continue;
00104        }
00105       break;
00106     }
00107   __pthread_set_own_extricate_if(self, 0);
00108 
00109   /* Terminate only if the wakeup came from cancellation. */
00110   /* Otherwise ignore cancellation because we got the semaphore. */
00111 
00112   if (THREAD_GETMEM(self, p_woken_by_cancel)
00113       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
00114     THREAD_SETMEM(self, p_woken_by_cancel, 0);
00115     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00116   }
00117   /* We got the semaphore */
00118   return 0;
00119 }
00120 
00121 int __new_sem_trywait(sem_t * sem)
00122 {
00123   int retval;
00124 
00125   __pthread_lock(&sem->__sem_lock, NULL);
00126   if (sem->__sem_value == 0) {
00127     errno = EAGAIN;
00128     retval = -1;
00129   } else {
00130     sem->__sem_value--;
00131     retval = 0;
00132   }
00133   __pthread_unlock(&sem->__sem_lock);
00134   return retval;
00135 }
00136 
00137 int __new_sem_post(sem_t * sem)
00138 {
00139   pthread_descr self = thread_self();
00140   pthread_descr th;
00141   struct pthread_request request;
00142 
00143   if (THREAD_GETMEM(self, p_in_sighandler) == NULL) {
00144     __pthread_lock(&sem->__sem_lock, self);
00145     if (sem->__sem_waiting == NULL) {
00146       if (sem->__sem_value >= SEM_VALUE_MAX) {
00147         /* Overflow */
00148         errno = ERANGE;
00149         __pthread_unlock(&sem->__sem_lock);
00150         return -1;
00151       }
00152       sem->__sem_value++;
00153       __pthread_unlock(&sem->__sem_lock);
00154     } else {
00155       th = dequeue(&sem->__sem_waiting);
00156       __pthread_unlock(&sem->__sem_lock);
00157       th->p_sem_avail = 1;
00158       WRITE_MEMORY_BARRIER();
00159       restart(th);
00160     }
00161   } else {
00162     /* If we're in signal handler, delegate post operation to
00163        the thread manager. */
00164     if (__pthread_manager_request < 0) {
00165       if (__pthread_initialize_manager() < 0) {
00166         errno = EAGAIN;
00167         return -1;
00168       }
00169     }
00170     request.req_kind = REQ_POST;
00171     request.req_args.post = sem;
00172     TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request,
00173                                    (char *) &request, sizeof(request)));
00174   }
00175   return 0;
00176 }
00177 
00178 int __new_sem_getvalue(sem_t * sem, int * sval)
00179 {
00180   *sval = sem->__sem_value;
00181   return 0;
00182 }
00183 
00184 int __new_sem_destroy(sem_t * sem)
00185 {
00186   if (sem->__sem_waiting != NULL) {
00187     __set_errno (EBUSY);
00188     return -1;
00189   }
00190   return 0;
00191 }
00192 
00193 sem_t *sem_open(const char *name, int oflag, ...)
00194 {
00195   __set_errno (ENOSYS);
00196   return SEM_FAILED;
00197 }
00198 
00199 int sem_close(sem_t *sem)
00200 {
00201   __set_errno (ENOSYS);
00202   return -1;
00203 }
00204 
00205 int sem_unlink(const char *name)
00206 {
00207   __set_errno (ENOSYS);
00208   return -1;
00209 }
00210 
00211 int sem_timedwait(sem_t *sem, const struct timespec *abstime)
00212 {
00213   pthread_descr self = thread_self();
00214   pthread_extricate_if extr;
00215   int already_canceled = 0;
00216   int spurious_wakeup_count;
00217 
00218   __pthread_lock(&sem->__sem_lock, self);
00219   if (sem->__sem_value > 0) {
00220     --sem->__sem_value;
00221     __pthread_unlock(&sem->__sem_lock);
00222     return 0;
00223   }
00224 
00225   if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
00226     /* The standard requires that if the function would block and the
00227        time value is illegal, the function returns with an error.  */
00228     __pthread_unlock(&sem->__sem_lock);
00229     __set_errno (EINVAL);
00230     return -1;
00231   }
00232 
00233   /* Set up extrication interface */
00234   extr.pu_object = sem;
00235   extr.pu_extricate_func = new_sem_extricate_func;
00236 
00237   /* Register extrication interface */
00238   THREAD_SETMEM(self, p_sem_avail, 0);
00239   __pthread_set_own_extricate_if(self, &extr);
00240   /* Enqueue only if not already cancelled. */
00241   if (!(THREAD_GETMEM(self, p_canceled)
00242       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
00243     enqueue(&sem->__sem_waiting, self);
00244   else
00245     already_canceled = 1;
00246   __pthread_unlock(&sem->__sem_lock);
00247 
00248   if (already_canceled) {
00249     __pthread_set_own_extricate_if(self, 0);
00250     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00251   }
00252 
00253   spurious_wakeup_count = 0;
00254   while (1)
00255     {
00256       if (timedsuspend(self, abstime) == 0) {
00257        int was_on_queue;
00258 
00259        /* __pthread_lock will queue back any spurious restarts that
00260           may happen to it. */
00261 
00262        __pthread_lock(&sem->__sem_lock, self);
00263        was_on_queue = remove_from_queue(&sem->__sem_waiting, self);
00264        __pthread_unlock(&sem->__sem_lock);
00265 
00266        if (was_on_queue) {
00267          __pthread_set_own_extricate_if(self, 0);
00268          __set_errno (ETIMEDOUT);
00269          return -1;
00270        }
00271 
00272        /* Eat the outstanding restart() from the signaller */
00273        suspend(self);
00274       }
00275 
00276       if (THREAD_GETMEM(self, p_sem_avail) == 0
00277          && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
00278              || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
00279        {
00280          /* Count resumes that don't belong to us. */
00281          spurious_wakeup_count++;
00282          continue;
00283        }
00284       break;
00285     }
00286 
00287  __pthread_set_own_extricate_if(self, 0);
00288 
00289   /* Terminate only if the wakeup came from cancellation. */
00290   /* Otherwise ignore cancellation because we got the semaphore. */
00291 
00292   if (THREAD_GETMEM(self, p_woken_by_cancel)
00293       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
00294     THREAD_SETMEM(self, p_woken_by_cancel, 0);
00295     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00296   }
00297   /* We got the semaphore */
00298   return 0;
00299 }
00300 
00301 
00302 versioned_symbol (libpthread, __new_sem_init, sem_init, GLIBC_2_1);
00303 versioned_symbol (libpthread, __new_sem_wait, sem_wait, GLIBC_2_1);
00304 versioned_symbol (libpthread, __new_sem_trywait, sem_trywait, GLIBC_2_1);
00305 versioned_symbol (libpthread, __new_sem_post, sem_post, GLIBC_2_1);
00306 versioned_symbol (libpthread, __new_sem_getvalue, sem_getvalue, GLIBC_2_1);
00307 versioned_symbol (libpthread, __new_sem_destroy, sem_destroy, GLIBC_2_1);