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