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