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@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  *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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
ptw32_sem_timedwait_cleanup(void * args)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
sem_timedwait(sem_t * sem,const struct timespec * abstime)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