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 *      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
49static void PTW32_CDECL
50ptw32_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
82int
83sem_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