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