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