1 /*
2  * -------------------------------------------------------------
3  *
4  * Module: sem_wait.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 static void PTW32_CDECL
ptw32_sem_wait_cleanup(void * sem)50 ptw32_sem_wait_cleanup(void * sem)
51 {
52   sem_t s = (sem_t) sem;
53 
54   if (pthread_mutex_lock (&s->lock) == 0)
55     {
56       /*
57        * If sema is destroyed do nothing, otherwise:-
58        * If the sema is posted between us being cancelled and us locking
59        * the sema again above then we need to consume that post but cancel
60        * anyway. If we don't get the semaphore we indicate that we're no
61        * longer waiting.
62        */
63       if (*((sem_t *)sem) != NULL && !(WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0))
64 	{
65 	  ++s->value;
66 #if defined(NEED_SEM)
67 	  if (s->value > 0)
68 	    {
69 	      s->leftToUnblock = 0;
70 	    }
71 #else
72 	  /*
73 	   * Don't release the W32 sema, it doesn't need adjustment
74 	   * because it doesn't record the number of waiters.
75 	   */
76 #endif /* NEED_SEM */
77 	}
78       (void) pthread_mutex_unlock (&s->lock);
79     }
80 }
81 
82 int
sem_wait(sem_t * sem)83 sem_wait (sem_t * sem)
84      /*
85       * ------------------------------------------------------
86       * DOCPUBLIC
87       *      This function  waits on a semaphore.
88       *
89       * PARAMETERS
90       *      sem
91       *              pointer to an instance of sem_t
92       *
93       * DESCRIPTION
94       *      This function waits on a semaphore. If the
95       *      semaphore value is greater than zero, it decreases
96       *      its value by one. If the semaphore value is zero, then
97       *      the calling thread (or process) is blocked until it can
98       *      successfully decrease the value or until interrupted by
99       *      a signal.
100       *
101       * RESULTS
102       *              0               successfully decreased semaphore,
103       *              -1              failed, error in errno
104       * ERRNO
105       *              EINVAL          'sem' is not a valid semaphore,
106       *              ENOSYS          semaphores are not supported,
107       *              EINTR           the function was interrupted by a signal,
108       *              EDEADLK         a deadlock condition was detected.
109       *
110       * ------------------------------------------------------
111       */
112 {
113   int result = 0;
114   sem_t s = *sem;
115 
116   pthread_testcancel();
117 
118   if (s == NULL)
119     {
120       result = EINVAL;
121     }
122   else
123     {
124       if ((result = pthread_mutex_lock (&s->lock)) == 0)
125 	{
126 	  int v;
127 
128 	  /* See sem_destroy.c
129 	   */
130 	  if (*sem == NULL)
131 	    {
132 	      (void) pthread_mutex_unlock (&s->lock);
133 	      errno = EINVAL;
134 	      return -1;
135 	    }
136 
137           v = --s->value;
138 	  (void) pthread_mutex_unlock (&s->lock);
139 
140 	  if (v < 0)
141 	    {
142 #if defined(_MSC_VER) && _MSC_VER < 1400
143 #pragma inline_depth(0)
144 #endif
145 	      /* Must wait */
146 	      pthread_cleanup_push(ptw32_sem_wait_cleanup, (void *) s);
147 	      result = pthreadCancelableWait (s->sem);
148 	      /* Cleanup if we're canceled or on any other error */
149 	      pthread_cleanup_pop(result);
150 #if defined(_MSC_VER) && _MSC_VER < 1400
151 #pragma inline_depth()
152 #endif
153 	    }
154 #if defined(NEED_SEM)
155 
156 	  if (!result && pthread_mutex_lock (&s->lock) == 0)
157 	    {
158 	      if (*sem == NULL)
159 	        {
160 	          (void) pthread_mutex_unlock (&s->lock);
161 	          errno = EINVAL;
162 	          return -1;
163 	        }
164 
165 	      if (s->leftToUnblock > 0)
166 		{
167 		  --s->leftToUnblock;
168 		  SetEvent(s->sem);
169 		}
170 	      (void) pthread_mutex_unlock (&s->lock);
171 	    }
172 
173 #endif /* NEED_SEM */
174 
175 	}
176 
177     }
178 
179   if (result != 0)
180     {
181       errno = result;
182       return -1;
183     }
184 
185   return 0;
186 
187 }				/* sem_wait */
188