• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2    Copyright (c) 2011-2016  mingw-w64 project
3 
4    Permission is hereby granted, free of charge, to any person obtaining a
5    copy of this software and associated documentation files (the "Software"),
6    to deal in the Software without restriction, including without limitation
7    the rights to use, copy, modify, merge, publish, distribute, sublicense,
8    and/or sell copies of the Software, and to permit persons to whom the
9    Software is furnished to do so, subject to the following conditions:
10 
11    The above copyright notice and this permission notice shall be included in
12    all copies or substantial portions of the Software.
13 
14    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20    DEALINGS IN THE SOFTWARE.
21 */
22 
23 #include <windows.h>
24 #include <stdio.h>
25 #include <malloc.h>
26 #include "pthread.h"
27 #include "thread.h"
28 #include "misc.h"
29 #include "semaphore.h"
30 #include "sem.h"
31 #include "ref.h"
32 
33 int do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout);
34 
35 static int
sem_result(int res)36 sem_result (int res)
37 {
38   if (res != 0) {
39     errno = res;
40     return -1;
41   }
42   return 0;
43 }
44 
45 int
sem_init(sem_t * sem,int pshared,unsigned int value)46 sem_init (sem_t *sem, int pshared, unsigned int value)
47 {
48   _sem_t *sv;
49 
50   if (!sem || value > (unsigned int)SEM_VALUE_MAX)
51     return sem_result (EINVAL);
52   if (pshared != PTHREAD_PROCESS_PRIVATE)
53     return sem_result (EPERM);
54 
55   if (!(sv = (sem_t) calloc (1,sizeof (*sv))))
56     return sem_result (ENOMEM);
57 
58   sv->value = value;
59   if (pthread_mutex_init (&sv->vlock, NULL) != 0)
60     {
61       free (sv);
62       return sem_result (ENOSPC);
63     }
64   if ((sv->s = CreateSemaphore (NULL, 0, SEM_VALUE_MAX, NULL)) == NULL)
65     {
66       pthread_mutex_destroy (&sv->vlock);
67       free (sv);
68       return sem_result (ENOSPC);
69     }
70 
71   sv->valid = LIFE_SEM;
72   *sem = sv;
73   return 0;
74 }
75 
76 int
sem_destroy(sem_t * sem)77 sem_destroy (sem_t *sem)
78 {
79   int r;
80   _sem_t *sv = NULL;
81 
82   if (!sem || (sv = *sem) == NULL)
83     return sem_result (EINVAL);
84   if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
85     return sem_result (r);
86 
87 #if 0
88   /* We don't wait for destroying a semaphore ...
89      or?  */
90   if (sv->value < 0)
91     {
92       pthread_mutex_unlock (&sv->vlock);
93       return sem_result (EBUSY);
94     }
95 #endif
96 
97   if (!CloseHandle (sv->s))
98     {
99       pthread_mutex_unlock (&sv->vlock);
100       return sem_result (EINVAL);
101     }
102   *sem = NULL;
103   sv->value = SEM_VALUE_MAX;
104   pthread_mutex_unlock(&sv->vlock);
105   Sleep (0);
106   while (pthread_mutex_destroy (&sv->vlock) == EBUSY)
107     Sleep (0);
108   sv->valid = DEAD_SEM;
109   free (sv);
110   return 0;
111 }
112 
113 static int
sem_std_enter(sem_t * sem,_sem_t ** svp,int do_test)114 sem_std_enter (sem_t *sem,_sem_t **svp, int do_test)
115 {
116   int r;
117   _sem_t *sv;
118 
119   if (do_test)
120     pthread_testcancel ();
121   if (!sem)
122     return sem_result (EINVAL);
123   sv = *sem;
124   if (sv == NULL)
125     return sem_result (EINVAL);
126 
127   if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
128     return sem_result (r);
129 
130   if (*sem == NULL)
131     {
132       pthread_mutex_unlock(&sv->vlock);
133       return sem_result (EINVAL);
134     }
135   *svp = sv;
136   return 0;
137 }
138 
139 int
sem_trywait(sem_t * sem)140 sem_trywait (sem_t *sem)
141 {
142   _sem_t *sv;
143 
144   if (sem_std_enter (sem, &sv, 0) != 0)
145     return -1;
146   if (sv->value <= 0)
147     {
148       pthread_mutex_unlock (&sv->vlock);
149       return sem_result (EAGAIN);
150     }
151   sv->value--;
152   pthread_mutex_unlock (&sv->vlock);
153 
154   return 0;
155 }
156 
157 struct sSemTimedWait
158 {
159   sem_t *p;
160   int *ret;
161 };
162 
163 static void
clean_wait_sem(void * s)164 clean_wait_sem (void *s)
165 {
166   struct sSemTimedWait *p = (struct sSemTimedWait *) s;
167   _sem_t *sv = NULL;
168 
169   if (sem_std_enter (p->p, &sv, 0) != 0)
170     return;
171 
172   if (WaitForSingleObject (sv->s, 0) != WAIT_OBJECT_0)
173     InterlockedIncrement (&sv->value);
174   else if (p->ret)
175     p->ret[0] = 0;
176   pthread_mutex_unlock (&sv->vlock);
177 }
178 
179 int
sem_wait(sem_t * sem)180 sem_wait (sem_t *sem)
181 {
182   long cur_v;
183   int ret = 0;
184   _sem_t *sv;
185   HANDLE semh;
186   struct sSemTimedWait arg;
187 
188   if (sem_std_enter (sem, &sv, 1) != 0)
189     return -1;
190 
191   arg.ret = &ret;
192   arg.p = sem;
193   InterlockedDecrement (&sv->value);
194   cur_v = sv->value;
195   semh = sv->s;
196   pthread_mutex_unlock (&sv->vlock);
197 
198   if (cur_v >= 0)
199     return 0;
200   else
201     {
202       pthread_cleanup_push (clean_wait_sem, (void *) &arg);
203       ret = do_sema_b_wait_intern (semh, 2, INFINITE);
204       pthread_cleanup_pop (ret);
205       if (ret == EINVAL)
206         return 0;
207     }
208 
209   if (!ret)
210     return 0;
211 
212   return sem_result (ret);
213 }
214 
215 int
sem_timedwait(sem_t * sem,const struct timespec * t)216 sem_timedwait (sem_t *sem, const struct timespec *t)
217 {
218   int cur_v, ret = 0;
219   DWORD dwr;
220   HANDLE semh;
221   _sem_t *sv;
222   struct sSemTimedWait arg;
223 
224   if (!t)
225     return sem_wait (sem);
226   dwr = dwMilliSecs(_pthread_rel_time_in_ms (t));
227 
228   if (sem_std_enter (sem, &sv, 1) != 0)
229     return -1;
230 
231   arg.ret = &ret;
232   arg.p = sem;
233   InterlockedDecrement (&sv->value);
234   cur_v = sv->value;
235   semh = sv->s;
236   pthread_mutex_unlock(&sv->vlock);
237 
238   if (cur_v >= 0)
239     return 0;
240   else
241     {
242       pthread_cleanup_push (clean_wait_sem, (void *) &arg);
243       ret = do_sema_b_wait_intern (semh, 2, dwr);
244       pthread_cleanup_pop (ret);
245       if (ret == EINVAL)
246         return 0;
247     }
248 
249   if (!ret)
250     return 0;
251   return sem_result (ret);
252 }
253 
254 int
sem_post(sem_t * sem)255 sem_post (sem_t *sem)
256 {
257   _sem_t *sv;;
258 
259   if (sem_std_enter (sem, &sv, 0) != 0)
260     return -1;
261 
262   if (sv->value >= SEM_VALUE_MAX)
263     {
264       pthread_mutex_unlock (&sv->vlock);
265       return sem_result (ERANGE);
266     }
267   InterlockedIncrement (&sv->value);
268   if (sv->value > 0 || ReleaseSemaphore (sv->s, 1, NULL))
269     {
270       pthread_mutex_unlock (&sv->vlock);
271       return 0;
272     }
273   InterlockedDecrement (&sv->value);
274   pthread_mutex_unlock (&sv->vlock);
275 
276   return sem_result (EINVAL);
277 }
278 
279 int
sem_post_multiple(sem_t * sem,int count)280 sem_post_multiple (sem_t *sem, int count)
281 {
282   int waiters_count;
283   _sem_t *sv;;
284 
285   if (count <= 0)
286     return sem_result (EINVAL);
287   if (sem_std_enter (sem, &sv, 0) != 0)
288     return -1;
289 
290   if (sv->value > (SEM_VALUE_MAX - count))
291   {
292     pthread_mutex_unlock (&sv->vlock);
293     return sem_result (ERANGE);
294   }
295   waiters_count = -sv->value;
296   sv->value += count;
297   /*InterlockedExchangeAdd((long*)&sv->value, (long) count);*/
298   if (waiters_count <= 0
299       || ReleaseSemaphore (sv->s,
300 			   (waiters_count < count ? waiters_count
301 			   			  : count), NULL))
302   {
303     pthread_mutex_unlock(&sv->vlock);
304     return 0;
305   }
306   /*InterlockedExchangeAdd((long*)&sv->value, -((long) count));*/
307   sv->value -= count;
308   pthread_mutex_unlock(&sv->vlock);
309   return sem_result (EINVAL);
310 }
311 
312 sem_t *
sem_open(const char * name,int oflag,mode_t mode,unsigned int value)313 sem_open (const char *name, int oflag, mode_t mode, unsigned int value)
314 {
315   sem_result (ENOSYS);
316   return NULL;
317 }
318 
319 int
sem_close(sem_t * sem)320 sem_close (sem_t *sem)
321 {
322   return sem_result (ENOSYS);
323 }
324 
325 int
sem_unlink(const char * name)326 sem_unlink (const char *name)
327 {
328   return sem_result (ENOSYS);
329 }
330 
331 int
sem_getvalue(sem_t * sem,int * sval)332 sem_getvalue (sem_t *sem, int *sval)
333 {
334   _sem_t *sv;
335   int r;
336 
337   if (!sval)
338     return sem_result (EINVAL);
339 
340   if (!sem || (sv = *sem) == NULL)
341     return sem_result (EINVAL);
342 
343   if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
344     return sem_result (r);
345   if (*sem == NULL)
346     {
347       pthread_mutex_unlock (&sv->vlock);
348       return sem_result (EINVAL);
349     }
350 
351   *sval = (int) sv->value;
352   pthread_mutex_unlock (&sv->vlock);
353   return 0;
354 }
355