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