Merge pull request #273 from joncham/bug-getpid
[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         thr_ret = _wapi_handle_lock_handle (handle);
186         g_assert (thr_ret == 0);
187         
188         if (initial != 0) {
189                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
190         }
191
192         DEBUG ("%s: Created semaphore handle %p initial %d max %d",
193                    __func__, handle, initial, max);
194
195         thr_ret = _wapi_handle_unlock_handle (handle);
196         g_assert (thr_ret == 0);
197
198         return(handle);
199 }
200
201 static gpointer namedsem_create (WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name G_GNUC_UNUSED)
202 {
203         struct _WapiHandle_namedsem namedsem_handle = {{{0}}, 0};
204         gpointer handle;
205         gchar *utf8_name;
206         int thr_ret;
207         gpointer ret = NULL;
208         guint32 namelen;
209         gint32 offset;
210         
211         /* w32 seems to guarantee that opening named objects can't
212          * race each other
213          */
214         thr_ret = _wapi_namespace_lock ();
215         g_assert (thr_ret == 0);
216         
217         /* Need to blow away any old errors here, because code tests
218          * for ERROR_ALREADY_EXISTS on success (!) to see if a
219          * semaphore was freshly created
220          */
221         SetLastError (ERROR_SUCCESS);
222
223         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
224         
225         DEBUG ("%s: Creating named sem [%s]", __func__, utf8_name);
226
227         offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
228                                                 utf8_name);
229         if (offset == -1) {
230                 /* The name has already been used for a different
231                  * object.
232                  */
233                 SetLastError (ERROR_INVALID_HANDLE);
234                 goto cleanup;
235         } else if (offset != 0) {
236                 /* Not an error, but this is how the caller is
237                  * informed that the semaphore wasn't freshly created
238                  */
239                 SetLastError (ERROR_ALREADY_EXISTS);
240         }
241         /* Fall through to create the semaphore handle */
242
243         if (offset == 0) {
244                 /* A new named semaphore, so create both the private
245                  * and shared parts
246                  */
247                 if (strlen (utf8_name) < MAX_PATH) {
248                         namelen = strlen (utf8_name);
249                 } else {
250                         namelen = MAX_PATH;
251                 }
252         
253                 memcpy (&namedsem_handle.sharedns.name, utf8_name, namelen);
254         
255                 namedsem_handle.val = initial;
256                 namedsem_handle.max = max;
257
258                 handle = _wapi_handle_new (WAPI_HANDLE_NAMEDSEM,
259                                            &namedsem_handle);
260         } else {
261                 /* A new reference to an existing named semaphore, so
262                  * just create the private part
263                  */
264                 handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM,
265                                                        offset, TRUE);
266         }
267         
268         if (handle == _WAPI_HANDLE_INVALID) {
269                 g_warning ("%s: error creating named sem handle", __func__);
270                 SetLastError (ERROR_GEN_FAILURE);
271                 goto cleanup;
272         }
273         ret = handle;
274         
275         if (offset == 0) {
276                 /* Set the initial state, as this is a completely new
277                  * handle
278                  */
279                 thr_ret = _wapi_handle_lock_shared_handles ();
280                 g_assert (thr_ret == 0);
281                 
282                 if (initial != 0) {
283                         _wapi_shared_handle_set_signal_state (handle, TRUE);
284                 }
285                 
286                 _wapi_handle_unlock_shared_handles ();
287         }
288         
289         DEBUG ("%s: returning named sem handle %p", __func__, handle);
290
291 cleanup:
292         g_free (utf8_name);
293         
294         _wapi_namespace_unlock (NULL);
295         
296         return (ret);
297 }
298
299
300 /**
301  * CreateSemaphore:
302  * @security: Ignored for now.
303  * @initial: The initial count for the semaphore.  The value must be
304  * greater than or equal to zero, and less than or equal to @max.
305  * @max: The maximum count for this semaphore.  The value must be
306  * greater than zero.
307  * @name: Pointer to a string specifying the name of this semaphore,
308  * or %NULL.  Currently ignored.
309  *
310  * Creates a new semaphore handle.  A semaphore is signalled when its
311  * count is greater than zero, and unsignalled otherwise.  The count
312  * is decreased by one whenever a wait function releases a thread that
313  * was waiting for the semaphore.  The count is increased by calling
314  * ReleaseSemaphore().
315  *
316  * Return value: a new handle, or NULL
317  */
318 gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name)
319 {
320         mono_once (&sem_ops_once, sem_ops_init);
321         
322         if (max <= 0) {
323                 DEBUG ("%s: max <= 0", __func__);
324
325                 SetLastError (ERROR_INVALID_PARAMETER);
326                 return(NULL);
327         }
328         
329         if (initial > max || initial < 0) {
330                 DEBUG ("%s: initial>max or < 0", __func__);
331
332                 SetLastError (ERROR_INVALID_PARAMETER);
333                 return(NULL);
334         }
335
336         if (name == NULL) {
337                 return (sem_create (security, initial, max));
338         } else {
339                 return (namedsem_create (security, initial, max, name));
340         }
341 }
342
343 static gboolean sem_release (gpointer handle, gint32 count, gint32 *prevcount)
344 {
345         struct _WapiHandle_sem *sem_handle;
346         gboolean ok;
347         gboolean ret=FALSE;
348         int thr_ret;
349         
350         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
351                                   (gpointer *)&sem_handle);
352         if (ok == FALSE) {
353                 g_warning ("%s: error looking up sem handle %p", __func__,
354                            handle);
355                 return(FALSE);
356         }
357
358         thr_ret = _wapi_handle_lock_handle (handle);
359         g_assert (thr_ret == 0);
360
361         DEBUG ("%s: sem %p val %d count %d", __func__, handle,
362                    sem_handle->val, count);
363         
364         /* Do this before checking for count overflow, because overflowing max
365          * is a listed technique for finding the current value
366          */
367         if (prevcount != NULL) {
368                 *prevcount = sem_handle->val;
369         }
370         
371         /* No idea why max is signed, but thats the spec :-( */
372         if (sem_handle->val + count > (guint32)sem_handle->max) {
373                 DEBUG ("%s: sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count);
374
375                 goto end;
376         }
377         
378         sem_handle->val += count;
379         _wapi_handle_set_signal_state (handle, TRUE, TRUE);
380         
381         ret = TRUE;
382
383         DEBUG ("%s: sem %p val now %d", __func__, handle, sem_handle->val);
384         
385 end:
386         thr_ret = _wapi_handle_unlock_handle (handle);
387         g_assert (thr_ret == 0);
388
389         return(ret);
390 }
391
392 static gboolean namedsem_release (gpointer handle, gint32 count,
393                                   gint32 *prevcount)
394 {
395         struct _WapiHandle_namedsem *sem_handle;
396         gboolean ok;
397         gboolean ret=FALSE;
398         int thr_ret;
399         
400         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM,
401                                   (gpointer *)&sem_handle);
402         if (ok == FALSE) {
403                 g_warning ("%s: error looking up sem handle %p", __func__,
404                            handle);
405                 return(FALSE);
406         }
407
408         thr_ret = _wapi_handle_lock_shared_handles ();
409         g_assert (thr_ret == 0);
410
411         DEBUG("%s: named sem %p val %d count %d", __func__, handle,
412                   sem_handle->val, count);
413         
414         /* Do this before checking for count overflow, because overflowing max
415          * is a listed technique for finding the current value
416          */
417         if (prevcount != NULL) {
418                 *prevcount = sem_handle->val;
419         }
420         
421         /* No idea why max is signed, but thats the spec :-( */
422         if (sem_handle->val + count > (guint32)sem_handle->max) {
423                 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);
424
425                 goto end;
426         }
427         
428         sem_handle->val += count;
429         _wapi_shared_handle_set_signal_state (handle, TRUE);
430         
431         ret = TRUE;
432
433         DEBUG("%s: named sem %p val now %d", __func__, handle,
434                   sem_handle->val);
435         
436 end:
437         _wapi_handle_unlock_shared_handles ();
438
439         return(ret);
440 }
441
442 /**
443  * ReleaseSemaphore:
444  * @handle: The semaphore handle to release.
445  * @count: The amount by which the semaphore's count should be
446  * increased.
447  * @prevcount: Pointer to a location to store the previous count of
448  * the semaphore, or %NULL.
449  *
450  * Increases the count of semaphore @handle by @count.
451  *
452  * Return value: %TRUE on success, %FALSE otherwise.
453  */
454 gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount)
455 {
456         WapiHandleType type;
457         
458         if (handle == NULL) {
459                 SetLastError (ERROR_INVALID_HANDLE);
460                 return (FALSE);
461         }
462         
463         type = _wapi_handle_type (handle);
464         
465         if (sem_ops[type].release == NULL) {
466                 SetLastError (ERROR_INVALID_HANDLE);
467                 return (FALSE);
468         }
469         
470         return (sem_ops[type].release (handle, count, prevcount));
471 }
472
473 gpointer OpenSemaphore (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED,
474                         const gunichar2 *name)
475 {
476         gpointer handle;
477         gchar *utf8_name;
478         int thr_ret;
479         gpointer ret = NULL;
480         gint32 offset;
481
482         mono_once (&sem_ops_once, sem_ops_init);
483         
484         /* w32 seems to guarantee that opening named objects can't
485          * race each other
486          */
487         thr_ret = _wapi_namespace_lock ();
488         g_assert (thr_ret == 0);
489         
490         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
491         
492         DEBUG ("%s: Opening named sem [%s]", __func__, utf8_name);
493
494         offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
495                                                 utf8_name);
496         if (offset == -1) {
497                 /* The name has already been used for a different
498                  * object.
499                  */
500                 SetLastError (ERROR_INVALID_HANDLE);
501                 goto cleanup;
502         } else if (offset == 0) {
503                 /* This name doesn't exist */
504                 SetLastError (ERROR_FILE_NOT_FOUND);    /* yes, really */
505                 goto cleanup;
506         }
507
508         /* A new reference to an existing named semaphore, so just
509          * create the private part
510          */
511         handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM, offset,
512                                                TRUE);
513         
514         if (handle == _WAPI_HANDLE_INVALID) {
515                 g_warning ("%s: error opening named sem handle", __func__);
516                 SetLastError (ERROR_GEN_FAILURE);
517                 goto cleanup;
518         }
519         ret = handle;
520         
521         DEBUG ("%s: returning named sem handle %p", __func__, handle);
522
523 cleanup:
524         g_free (utf8_name);
525         
526         _wapi_namespace_unlock (NULL);
527         
528         return (ret);
529 }