Home | History | Annotate | Download | only in pthreads-win32
      1 /*
      2  * -------------------------------------------------------------
      3  *
      4  * Module: sem_timedwait.c
      5  *
      6  * Purpose:
      7  *	Semaphores aren't actually part of the PThreads standard.
      8  *	They are defined by the POSIX Standard:
      9  *
     10  *		POSIX 1003.1b-1993	(POSIX.1b)
     11  *
     12  * -------------------------------------------------------------
     13  *
     14  * --------------------------------------------------------------------------
     15  *
     16  *      Pthreads-win32 - POSIX Threads Library for Win32
     17  *      Copyright(C) 1998 John E. Bossom
     18  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
     19  *
     20  *      Contact Email: rpj (at) callisto.canberra.edu.au
     21  *
     22  *      The current list of contributors is contained
     23  *      in the file CONTRIBUTORS included with the source
     24  *      code distribution. The list can also be seen at the
     25  *      following World Wide Web location:
     26  *      http://sources.redhat.com/pthreads-win32/contributors.html
     27  *
     28  *      This library is free software; you can redistribute it and/or
     29  *      modify it under the terms of the GNU Lesser General Public
     30  *      License as published by the Free Software Foundation; either
     31  *      version 2 of the License, or (at your option) any later version.
     32  *
     33  *      This library is distributed in the hope that it will be useful,
     34  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
     35  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     36  *      Lesser General Public License for more details.
     37  *
     38  *      You should have received a copy of the GNU Lesser General Public
     39  *      License along with this library in the file COPYING.LIB;
     40  *      if not, write to the Free Software Foundation, Inc.,
     41  *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
     42  */
     43 
     44 #include "pthread.h"
     45 #include "semaphore.h"
     46 #include "implement.h"
     47 
     48 
     49 typedef struct {
     50   sem_t sem;
     51   int * resultPtr;
     52 } sem_timedwait_cleanup_args_t;
     53 
     54 
     55 static void PTW32_CDECL
     56 ptw32_sem_timedwait_cleanup (void * args)
     57 {
     58   sem_timedwait_cleanup_args_t * a = (sem_timedwait_cleanup_args_t *)args;
     59   sem_t s = a->sem;
     60 
     61   if (pthread_mutex_lock (&s->lock) == 0)
     62     {
     63       /*
     64        * We either timed out or were cancelled.
     65        * If someone has posted between then and now we try to take the semaphore.
     66        * Otherwise the semaphore count may be wrong after we
     67        * return. In the case of a cancellation, it is as if we
     68        * were cancelled just before we return (after taking the semaphore)
     69        * which is ok.
     70        */
     71       if (WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0)
     72 	{
     73 	  /* We got the semaphore on the second attempt */
     74 	  *(a->resultPtr) = 0;
     75 	}
     76       else
     77 	{
     78 	  /* Indicate we're no longer waiting */
     79 	  s->value++;
     80 #if defined(NEED_SEM)
     81 	  if (s->value > 0)
     82 	    {
     83 	      s->leftToUnblock = 0;
     84 	    }
     85 #else
     86           /*
     87            * Don't release the W32 sema, it doesn't need adjustment
     88            * because it doesn't record the number of waiters.
     89            */
     90 #endif
     91 	}
     92       (void) pthread_mutex_unlock (&s->lock);
     93     }
     94 }
     95 
     96 
     97 int
     98 sem_timedwait (sem_t * sem, const struct timespec *abstime)
     99      /*
    100       * ------------------------------------------------------
    101       * DOCPUBLIC
    102       *      This function waits on a semaphore possibly until
    103       *      'abstime' time.
    104       *
    105       * PARAMETERS
    106       *      sem
    107       *              pointer to an instance of sem_t
    108       *
    109       *      abstime
    110       *              pointer to an instance of struct timespec
    111       *
    112       * DESCRIPTION
    113       *      This function waits on a semaphore. If the
    114       *      semaphore value is greater than zero, it decreases
    115       *      its value by one. If the semaphore value is zero, then
    116       *      the calling thread (or process) is blocked until it can
    117       *      successfully decrease the value or until interrupted by
    118       *      a signal.
    119       *
    120       *      If 'abstime' is a NULL pointer then this function will
    121       *      block until it can successfully decrease the value or
    122       *      until interrupted by a signal.
    123       *
    124       * RESULTS
    125       *              0               successfully decreased semaphore,
    126       *              -1              failed, error in errno
    127       * ERRNO
    128       *              EINVAL          'sem' is not a valid semaphore,
    129       *              ENOSYS          semaphores are not supported,
    130       *              EINTR           the function was interrupted by a signal,
    131       *              EDEADLK         a deadlock condition was detected.
    132       *              ETIMEDOUT       abstime elapsed before success.
    133       *
    134       * ------------------------------------------------------
    135       */
    136 {
    137   int result = 0;
    138   sem_t s = *sem;
    139 
    140   pthread_testcancel();
    141 
    142   if (sem == NULL)
    143     {
    144       result = EINVAL;
    145     }
    146   else
    147     {
    148       DWORD milliseconds;
    149 
    150       if (abstime == NULL)
    151 	{
    152 	  milliseconds = INFINITE;
    153 	}
    154       else
    155 	{
    156 	  /*
    157 	   * Calculate timeout as milliseconds from current system time.
    158 	   */
    159 	  milliseconds = ptw32_relmillisecs (abstime);
    160 	}
    161 
    162       if ((result = pthread_mutex_lock (&s->lock)) == 0)
    163 	{
    164 	  int v;
    165 
    166 	  /* See sem_destroy.c
    167 	   */
    168 	  if (*sem == NULL)
    169 	    {
    170 	      (void) pthread_mutex_unlock (&s->lock);
    171 	      errno = EINVAL;
    172 	      return -1;
    173 	    }
    174 
    175 	  v = --s->value;
    176 	  (void) pthread_mutex_unlock (&s->lock);
    177 
    178 	  if (v < 0)
    179 	    {
    180 #if defined(NEED_SEM)
    181 	      int timedout;
    182 #endif
    183 	      sem_timedwait_cleanup_args_t cleanup_args;
    184 
    185 	      cleanup_args.sem = s;
    186 	      cleanup_args.resultPtr = &result;
    187 
    188 #if defined(_MSC_VER) && _MSC_VER < 1400
    189 #pragma inline_depth(0)
    190 #endif
    191 	      /* Must wait */
    192               pthread_cleanup_push(ptw32_sem_timedwait_cleanup, (void *) &cleanup_args);
    193 #if defined(NEED_SEM)
    194 	      timedout =
    195 #endif
    196 	      result = pthreadCancelableTimedWait (s->sem, milliseconds);
    197 	      pthread_cleanup_pop(result);
    198 #if defined(_MSC_VER) && _MSC_VER < 1400
    199 #pragma inline_depth()
    200 #endif
    201 
    202 #if defined(NEED_SEM)
    203 
    204 	      if (!timedout && pthread_mutex_lock (&s->lock) == 0)
    205 	        {
    206         	  if (*sem == NULL)
    207         	    {
    208         	      (void) pthread_mutex_unlock (&s->lock);
    209         	      errno = EINVAL;
    210         	      return -1;
    211         	    }
    212 
    213 	          if (s->leftToUnblock > 0)
    214 	            {
    215 		      --s->leftToUnblock;
    216 		      SetEvent(s->sem);
    217 		    }
    218 	          (void) pthread_mutex_unlock (&s->lock);
    219 	        }
    220 
    221 #endif /* NEED_SEM */
    222 
    223 	    }
    224 	}
    225 
    226     }
    227 
    228   if (result != 0)
    229     {
    230 
    231       errno = result;
    232       return -1;
    233 
    234     }
    235 
    236   return 0;
    237 
    238 }				/* sem_timedwait */
    239