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