Merge pull request #2260 from xmcclure/fix-testwaitany
[mono.git] / mono / io-layer / semaphores.c
1 /*
2  * semaphores.c:  Semaphore handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <pthread.h>
13 #ifdef HAVE_SEMAPHORE_H
14 #include <semaphore.h>
15 #endif
16 #include <errno.h>
17 #include <string.h>
18 #include <sys/time.h>
19
20 #include <mono/io-layer/wapi.h>
21 #include <mono/io-layer/wapi-private.h>
22 #include <mono/io-layer/handles-private.h>
23 #include <mono/io-layer/semaphore-private.h>
24 #include <mono/utils/mono-once.h>
25
26 #if 0
27 #define DEBUG(...) g_message(__VA_ARGS__)
28 #else
29 #define DEBUG(...)
30 #endif
31
32 static void sema_signal(gpointer handle);
33 static gboolean sema_own (gpointer handle);
34
35 static void namedsema_signal (gpointer handle);
36 static gboolean namedsema_own (gpointer handle);
37
38 struct _WapiHandleOps _wapi_sem_ops = {
39         NULL,                   /* close */
40         sema_signal,            /* signal */
41         sema_own,               /* own */
42         NULL,                   /* is_owned */
43         NULL,                   /* special_wait */
44         NULL                    /* prewait */
45 };
46
47 void _wapi_sem_details (gpointer handle_info)
48 {
49         struct _WapiHandle_sem *sem = (struct _WapiHandle_sem *)handle_info;
50         
51         g_print ("val: %5u, max: %5d", sem->val, sem->max);
52 }
53
54 struct _WapiHandleOps _wapi_namedsem_ops = {
55         NULL,                   /* close */
56         namedsema_signal,       /* signal */
57         namedsema_own,          /* own */
58         NULL,                   /* is_owned */
59         NULL,                   /* special_wait */
60         NULL                    /* prewait */
61 };
62
63 static gboolean sem_release (gpointer handle, gint32 count, gint32 *prev);
64 static gboolean namedsem_release (gpointer handle, gint32 count, gint32 *prev);
65
66 static struct 
67 {
68         gboolean (*release)(gpointer handle, gint32 count, gint32 *prev);
69 } sem_ops[WAPI_HANDLE_COUNT] = {
70         {NULL},
71         {NULL},
72         {NULL},
73         {NULL},
74         {sem_release},
75         {NULL},
76         {NULL},
77         {NULL},
78         {NULL},
79         {NULL},
80         {NULL},
81         {NULL},
82         {namedsem_release},
83 };
84
85 static mono_once_t sem_ops_once=MONO_ONCE_INIT;
86
87 static void sem_ops_init (void)
88 {
89         _wapi_handle_register_capabilities (WAPI_HANDLE_SEM,
90                                             WAPI_HANDLE_CAP_WAIT |
91                                             WAPI_HANDLE_CAP_SIGNAL);
92         _wapi_handle_register_capabilities (WAPI_HANDLE_NAMEDSEM,
93                                             WAPI_HANDLE_CAP_WAIT |
94                                             WAPI_HANDLE_CAP_SIGNAL);
95 }
96
97 static void sema_signal(gpointer handle)
98 {
99         ReleaseSemaphore(handle, 1, NULL);
100 }
101
102 static gboolean sema_own (gpointer handle)
103 {
104         struct _WapiHandle_sem *sem_handle;
105         gboolean ok;
106         
107         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
108                                 (gpointer *)&sem_handle);
109         if(ok==FALSE) {
110                 g_warning ("%s: error looking up sem handle %p", __func__,
111                            handle);
112                 return(FALSE);
113         }
114         
115         DEBUG("%s: owning sem handle %p", __func__, handle);
116
117         sem_handle->val--;
118         
119         DEBUG ("%s: sem %p val now %d", __func__, handle, sem_handle->val);
120
121         if(sem_handle->val==0) {
122                 _wapi_handle_set_signal_state (handle, FALSE, FALSE);
123         }
124
125         return(TRUE);
126 }
127
128 static void namedsema_signal (gpointer handle)
129 {
130         ReleaseSemaphore (handle, 1, NULL);
131 }
132
133 /* NB, always called with the shared handle lock held */
134 static gboolean namedsema_own (gpointer handle)
135 {
136         struct _WapiHandle_namedsem *namedsem_handle;
137         gboolean ok;
138         
139         DEBUG ("%s: owning named sem handle %p", __func__, handle);
140
141         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM,
142                                   (gpointer *)&namedsem_handle);
143         if (ok == FALSE) {
144                 g_warning ("%s: error looking up named sem handle %p",
145                            __func__, handle);
146                 return (FALSE);
147         }
148         
149         namedsem_handle->val--;
150         
151         DEBUG ("%s: named sem %p val now %d", __func__, handle,
152                    namedsem_handle->val);
153
154         if (namedsem_handle->val == 0) {
155                 _wapi_shared_handle_set_signal_state (handle, FALSE);
156         }
157         
158         return (TRUE);
159 }
160 static gpointer sem_create (WapiSecurityAttributes *security G_GNUC_UNUSED,
161                             gint32 initial, gint32 max)
162 {
163         struct _WapiHandle_sem sem_handle = {0};
164         gpointer handle;
165         int thr_ret;
166         
167         /* Need to blow away any old errors here, because code tests
168          * for ERROR_ALREADY_EXISTS on success (!) to see if a
169          * semaphore was freshly created
170          */
171         SetLastError (ERROR_SUCCESS);
172         
173         sem_handle.val = initial;
174         sem_handle.max = max;
175
176         handle = _wapi_handle_new (WAPI_HANDLE_SEM, &sem_handle);
177         if (handle == _WAPI_HANDLE_INVALID) {
178                 g_warning ("%s: error creating semaphore handle", __func__);
179                 SetLastError (ERROR_GEN_FAILURE);
180                 return(NULL);
181         }
182
183         thr_ret = _wapi_handle_lock_handle (handle);
184         g_assert (thr_ret == 0);
185         
186         if (initial != 0) {
187                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
188         }
189
190         DEBUG ("%s: Created semaphore handle %p initial %d max %d",
191                    __func__, handle, initial, max);
192
193         thr_ret = _wapi_handle_unlock_handle (handle);
194         g_assert (thr_ret == 0);
195
196         return(handle);
197 }
198
199 static gpointer namedsem_create (WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name G_GNUC_UNUSED)
200 {
201         struct _WapiHandle_namedsem namedsem_handle = {{{0}}, 0};
202         gpointer handle;
203         gchar *utf8_name;
204         int thr_ret;
205         gpointer ret = NULL;
206         guint32 namelen;
207         gint32 offset;
208         
209         /* w32 seems to guarantee that opening named objects can't
210          * race each other
211          */
212         thr_ret = _wapi_namespace_lock ();
213         g_assert (thr_ret == 0);
214         
215         /* Need to blow away any old errors here, because code tests
216          * for ERROR_ALREADY_EXISTS on success (!) to see if a
217          * semaphore was freshly created
218          */
219         SetLastError (ERROR_SUCCESS);
220
221         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
222         
223         DEBUG ("%s: Creating named sem [%s]", __func__, utf8_name);
224
225         offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
226                                                 utf8_name);
227         if (offset == -1) {
228                 /* The name has already been used for a different
229                  * object.
230                  */
231                 SetLastError (ERROR_INVALID_HANDLE);
232                 goto cleanup;
233         } else if (offset != 0) {
234                 /* Not an error, but this is how the caller is
235                  * informed that the semaphore wasn't freshly created
236                  */
237                 SetLastError (ERROR_ALREADY_EXISTS);
238         }
239         /* Fall through to create the semaphore handle */
240
241         if (offset == 0) {
242                 /* A new named semaphore, so create both the private
243                  * and shared parts
244                  */
245                 if (strlen (utf8_name) < MAX_PATH) {
246                         namelen = strlen (utf8_name);
247                 } else {
248                         namelen = MAX_PATH;
249                 }
250         
251                 memcpy (&namedsem_handle.sharedns.name, utf8_name, namelen);
252         
253                 namedsem_handle.val = initial;
254                 namedsem_handle.max = max;
255
256                 handle = _wapi_handle_new (WAPI_HANDLE_NAMEDSEM,
257                                            &namedsem_handle);
258         } else {
259                 /* A new reference to an existing named semaphore, so
260                  * just create the private part
261                  */
262                 handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM,
263                                                        offset, TRUE);
264         }
265         
266         if (handle == _WAPI_HANDLE_INVALID) {
267                 g_warning ("%s: error creating named sem handle", __func__);
268                 SetLastError (ERROR_GEN_FAILURE);
269                 goto cleanup;
270         }
271         ret = handle;
272         
273         if (offset == 0) {
274                 /* Set the initial state, as this is a completely new
275                  * handle
276                  */
277                 thr_ret = _wapi_handle_lock_shared_handles ();
278                 g_assert (thr_ret == 0);
279                 
280                 if (initial != 0) {
281                         _wapi_shared_handle_set_signal_state (handle, TRUE);
282                 }
283                 
284                 _wapi_handle_unlock_shared_handles ();
285         }
286         
287         DEBUG ("%s: returning named sem handle %p", __func__, handle);
288
289 cleanup:
290         g_free (utf8_name);
291         
292         _wapi_namespace_unlock (NULL);
293         
294         return (ret);
295 }
296
297
298 /**
299  * CreateSemaphore:
300  * @security: Ignored for now.
301  * @initial: The initial count for the semaphore.  The value must be
302  * greater than or equal to zero, and less than or equal to @max.
303  * @max: The maximum count for this semaphore.  The value must be
304  * greater than zero.
305  * @name: Pointer to a string specifying the name of this semaphore,
306  * or %NULL.  Currently ignored.
307  *
308  * Creates a new semaphore handle.  A semaphore is signalled when its
309  * count is greater than zero, and unsignalled otherwise.  The count
310  * is decreased by one whenever a wait function releases a thread that
311  * was waiting for the semaphore.  The count is increased by calling
312  * ReleaseSemaphore().
313  *
314  * Return value: a new handle, or NULL
315  */
316 gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name)
317 {
318         mono_once (&sem_ops_once, sem_ops_init);
319         
320         if (max <= 0) {
321                 DEBUG ("%s: max <= 0", __func__);
322
323                 SetLastError (ERROR_INVALID_PARAMETER);
324                 return(NULL);
325         }
326         
327         if (initial > max || initial < 0) {
328                 DEBUG ("%s: initial>max or < 0", __func__);
329
330                 SetLastError (ERROR_INVALID_PARAMETER);
331                 return(NULL);
332         }
333
334         if (name == NULL) {
335                 return (sem_create (security, initial, max));
336         } else {
337                 return (namedsem_create (security, initial, max, name));
338         }
339 }
340
341 static gboolean sem_release (gpointer handle, gint32 count, gint32 *prevcount)
342 {
343         struct _WapiHandle_sem *sem_handle;
344         gboolean ok;
345         gboolean ret=FALSE;
346         int thr_ret;
347         
348         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
349                                   (gpointer *)&sem_handle);
350         if (ok == FALSE) {
351                 g_warning ("%s: error looking up sem handle %p", __func__,
352                            handle);
353                 return(FALSE);
354         }
355
356         thr_ret = _wapi_handle_lock_handle (handle);
357         g_assert (thr_ret == 0);
358
359         DEBUG ("%s: sem %p val %d count %d", __func__, handle,
360                    sem_handle->val, count);
361         
362         /* Do this before checking for count overflow, because overflowing max
363          * is a listed technique for finding the current value
364          */
365         if (prevcount != NULL) {
366                 *prevcount = sem_handle->val;
367         }
368         
369         /* No idea why max is signed, but thats the spec :-( */
370         if (sem_handle->val + count > (guint32)sem_handle->max) {
371                 DEBUG ("%s: sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count);
372
373                 goto end;
374         }
375         
376         sem_handle->val += count;
377         _wapi_handle_set_signal_state (handle, TRUE, TRUE);
378         
379         ret = TRUE;
380
381         DEBUG ("%s: sem %p val now %d", __func__, handle, sem_handle->val);
382         
383 end:
384         thr_ret = _wapi_handle_unlock_handle (handle);
385         g_assert (thr_ret == 0);
386
387         return(ret);
388 }
389
390 static gboolean namedsem_release (gpointer handle, gint32 count,
391                                   gint32 *prevcount)
392 {
393         struct _WapiHandle_namedsem *sem_handle;
394         gboolean ok;
395         gboolean ret=FALSE;
396         int thr_ret;
397         
398         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM,
399                                   (gpointer *)&sem_handle);
400         if (ok == FALSE) {
401                 g_warning ("%s: error looking up sem handle %p", __func__,
402                            handle);
403                 return(FALSE);
404         }
405
406         thr_ret = _wapi_handle_lock_shared_handles ();
407         g_assert (thr_ret == 0);
408
409         DEBUG("%s: named sem %p val %d count %d", __func__, handle,
410                   sem_handle->val, count);
411         
412         /* Do this before checking for count overflow, because overflowing max
413          * is a listed technique for finding the current value
414          */
415         if (prevcount != NULL) {
416                 *prevcount = sem_handle->val;
417         }
418         
419         /* No idea why max is signed, but thats the spec :-( */
420         if (sem_handle->val + count > (guint32)sem_handle->max) {
421                 DEBUG ("%s: named sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count);
422
423                 goto end;
424         }
425         
426         sem_handle->val += count;
427         _wapi_shared_handle_set_signal_state (handle, TRUE);
428         
429         ret = TRUE;
430
431         DEBUG("%s: named sem %p val now %d", __func__, handle,
432                   sem_handle->val);
433         
434 end:
435         _wapi_handle_unlock_shared_handles ();
436
437         return(ret);
438 }
439
440 /**
441  * ReleaseSemaphore:
442  * @handle: The semaphore handle to release.
443  * @count: The amount by which the semaphore's count should be
444  * increased.
445  * @prevcount: Pointer to a location to store the previous count of
446  * the semaphore, or %NULL.
447  *
448  * Increases the count of semaphore @handle by @count.
449  *
450  * Return value: %TRUE on success, %FALSE otherwise.
451  */
452 gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount)
453 {
454         WapiHandleType type;
455         
456         if (handle == NULL) {
457                 SetLastError (ERROR_INVALID_HANDLE);
458                 return (FALSE);
459         }
460         
461         type = _wapi_handle_type (handle);
462         
463         if (sem_ops[type].release == NULL) {
464                 SetLastError (ERROR_INVALID_HANDLE);
465                 return (FALSE);
466         }
467         
468         return (sem_ops[type].release (handle, count, prevcount));
469 }
470
471 gpointer OpenSemaphore (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED,
472                         const gunichar2 *name)
473 {
474         gpointer handle;
475         gchar *utf8_name;
476         int thr_ret;
477         gpointer ret = NULL;
478         gint32 offset;
479
480         mono_once (&sem_ops_once, sem_ops_init);
481         
482         /* w32 seems to guarantee that opening named objects can't
483          * race each other
484          */
485         thr_ret = _wapi_namespace_lock ();
486         g_assert (thr_ret == 0);
487         
488         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
489         
490         DEBUG ("%s: Opening named sem [%s]", __func__, utf8_name);
491
492         offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
493                                                 utf8_name);
494         if (offset == -1) {
495                 /* The name has already been used for a different
496                  * object.
497                  */
498                 SetLastError (ERROR_INVALID_HANDLE);
499                 goto cleanup;
500         } else if (offset == 0) {
501                 /* This name doesn't exist */
502                 SetLastError (ERROR_FILE_NOT_FOUND);    /* yes, really */
503                 goto cleanup;
504         }
505
506         /* A new reference to an existing named semaphore, so just
507          * create the private part
508          */
509         handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM, offset,
510                                                TRUE);
511         
512         if (handle == _WAPI_HANDLE_INVALID) {
513                 g_warning ("%s: error opening named sem handle", __func__);
514                 SetLastError (ERROR_GEN_FAILURE);
515                 goto cleanup;
516         }
517         ret = handle;
518         
519         DEBUG ("%s: returning named sem handle %p", __func__, handle);
520
521 cleanup:
522         g_free (utf8_name);
523         
524         _wapi_namespace_unlock (NULL);
525         
526         return (ret);
527 }