Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / w32semaphore-unix.c
1 /**
2  * \file
3  * Runtime support for managed Semaphore on Unix
4  *
5  * Author:
6  *      Ludovic Henry (luhenry@microsoft.com)
7  *
8  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9  */
10
11 #include "w32semaphore.h"
12
13 #include "w32error.h"
14 #include "w32handle-namespace.h"
15 #include "mono/utils/mono-logger-internals.h"
16 #include "mono/metadata/w32handle.h"
17
18 #define MAX_PATH 260
19
20 typedef struct {
21         guint32 val;
22         gint32 max;
23 } MonoW32HandleSemaphore;
24
25 struct MonoW32HandleNamedSemaphore {
26         MonoW32HandleSemaphore s;
27         MonoW32HandleNamespace sharedns;
28 };
29
30 static void sem_handle_signal (gpointer handle, MonoW32HandleType type, MonoW32HandleSemaphore *sem_handle)
31 {
32         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: signalling %s handle %p",
33                 __func__, mono_w32handle_get_typename (type), handle);
34
35         /* No idea why max is signed, but thats the spec :-( */
36         if (sem_handle->val + 1 > (guint32)sem_handle->max) {
37                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p val %d count %d max %d, max value would be exceeded",
38                         __func__, mono_w32handle_get_typename (type), handle, sem_handle->val, 1, sem_handle->max);
39         } else {
40                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p val %d count %d max %d",
41                         __func__, mono_w32handle_get_typename (type), handle, sem_handle->val, 1, sem_handle->max);
42
43                 sem_handle->val += 1;
44                 mono_w32handle_set_signal_state (handle, TRUE, TRUE);
45         }
46 }
47
48 static gboolean sem_handle_own (gpointer handle, MonoW32HandleType type, gboolean *abandoned)
49 {
50         MonoW32HandleSemaphore *sem_handle;
51
52         *abandoned = FALSE;
53
54         if (!mono_w32handle_lookup (handle, type, (gpointer *)&sem_handle)) {
55                 g_warning ("%s: error looking up %s handle %p",
56                         __func__, mono_w32handle_get_typename (type), handle);
57                 return FALSE;
58         }
59
60         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: owning %s handle %p",
61                 __func__, mono_w32handle_get_typename (type), handle);
62
63         sem_handle->val--;
64
65         if (sem_handle->val == 0)
66                 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
67
68         return TRUE;
69 }
70
71 static void sema_signal(gpointer handle, gpointer handle_specific)
72 {
73         sem_handle_signal (handle, MONO_W32HANDLE_SEM, (MonoW32HandleSemaphore*) handle_specific);
74 }
75
76 static gboolean sema_own (gpointer handle, gboolean *abandoned)
77 {
78         return sem_handle_own (handle, MONO_W32HANDLE_SEM, abandoned);
79 }
80
81 static void namedsema_signal (gpointer handle, gpointer handle_specific)
82 {
83         sem_handle_signal (handle, MONO_W32HANDLE_NAMEDSEM, (MonoW32HandleSemaphore*) handle_specific);
84 }
85
86 /* NB, always called with the shared handle lock held */
87 static gboolean namedsema_own (gpointer handle, gboolean *abandoned)
88 {
89         return sem_handle_own (handle, MONO_W32HANDLE_NAMEDSEM, abandoned);
90 }
91
92 static void sema_details (gpointer data)
93 {
94         MonoW32HandleSemaphore *sem = (MonoW32HandleSemaphore *)data;
95         g_print ("val: %5u, max: %5d", sem->val, sem->max);
96 }
97
98 static void namedsema_details (gpointer data)
99 {
100         MonoW32HandleNamedSemaphore *namedsem = (MonoW32HandleNamedSemaphore *)data;
101         g_print ("val: %5u, max: %5d, name: \"%s\"", namedsem->s.val, namedsem->s.max, namedsem->sharedns.name);
102 }
103
104 static const gchar* sema_typename (void)
105 {
106         return "Semaphore";
107 }
108
109 static gsize sema_typesize (void)
110 {
111         return sizeof (MonoW32HandleSemaphore);
112 }
113
114 static const gchar* namedsema_typename (void)
115 {
116         return "N.Semaphore";
117 }
118
119 static gsize namedsema_typesize (void)
120 {
121         return sizeof (MonoW32HandleNamedSemaphore);
122 }
123
124 void
125 mono_w32semaphore_init (void)
126 {
127         static MonoW32HandleOps sem_ops = {
128                 NULL,                   /* close */
129                 sema_signal,            /* signal */
130                 sema_own,               /* own */
131                 NULL,                   /* is_owned */
132                 NULL,                   /* special_wait */
133                 NULL,                   /* prewait */
134                 sema_details,   /* details */
135                 sema_typename,  /* typename */
136                 sema_typesize,  /* typesize */
137         };
138
139         static MonoW32HandleOps namedsem_ops = {
140                 NULL,                   /* close */
141                 namedsema_signal,       /* signal */
142                 namedsema_own,          /* own */
143                 NULL,                   /* is_owned */
144                 NULL,                   /* special_wait */
145                 NULL,                   /* prewait */
146                 namedsema_details,      /* details */
147                 namedsema_typename,     /* typename */
148                 namedsema_typesize,     /* typesize */
149         };
150
151         mono_w32handle_register_ops (MONO_W32HANDLE_SEM,      &sem_ops);
152         mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDSEM, &namedsem_ops);
153
154         mono_w32handle_register_capabilities (MONO_W32HANDLE_SEM,
155                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL));
156         mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDSEM,
157                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL));
158 }
159
160 static gpointer
161 sem_handle_create (MonoW32HandleSemaphore *sem_handle, MonoW32HandleType type, gint32 initial, gint32 max)
162 {
163         gpointer handle;
164
165         sem_handle->val = initial;
166         sem_handle->max = max;
167
168         handle = mono_w32handle_new (type, sem_handle);
169         if (handle == INVALID_HANDLE_VALUE) {
170                 g_warning ("%s: error creating %s handle",
171                         __func__, mono_w32handle_get_typename (type));
172                 mono_w32error_set_last (ERROR_GEN_FAILURE);
173                 return NULL;
174         }
175
176         mono_w32handle_lock_handle (handle);
177
178         if (initial != 0)
179                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
180
181         mono_w32handle_unlock_handle (handle);
182
183         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
184                 __func__, mono_w32handle_get_typename (type), handle);
185
186         return handle;
187 }
188
189 static gpointer
190 sem_create (gint32 initial, gint32 max)
191 {
192         MonoW32HandleSemaphore sem_handle;
193         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle, initial %d max %d",
194                 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_SEM), initial, max);
195         return sem_handle_create (&sem_handle, MONO_W32HANDLE_SEM, initial, max);
196 }
197
198 static gpointer
199 namedsem_create (gint32 initial, gint32 max, const gunichar2 *name)
200 {
201         gpointer handle;
202         gchar *utf8_name;
203
204         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle, initial %d max %d name \"%s\"",
205                     __func__, mono_w32handle_get_typename (MONO_W32HANDLE_NAMEDSEM), initial, max, (const char*)name);
206
207         /* w32 seems to guarantee that opening named objects can't race each other */
208         mono_w32handle_namespace_lock ();
209
210         glong utf8_len = 0;
211         utf8_name = g_utf16_to_utf8 (name, -1, NULL, &utf8_len, NULL);
212
213         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating named sem name [%s] initial %d max %d", __func__, utf8_name, initial, max);
214
215         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDSEM, utf8_name);
216         if (handle == INVALID_HANDLE_VALUE) {
217                 /* The name has already been used for a different object. */
218                 handle = NULL;
219                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
220         } else if (handle) {
221                 /* Not an error, but this is how the caller is informed that the semaphore wasn't freshly created */
222                 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
223
224                 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
225         } else {
226                 /* A new named semaphore */
227                 MonoW32HandleNamedSemaphore namedsem_handle;
228
229                 size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH;
230                 memcpy (&namedsem_handle.sharedns.name [0], utf8_name, len);
231                 namedsem_handle.sharedns.name [len] = '\0';
232
233                 handle = sem_handle_create ((MonoW32HandleSemaphore*) &namedsem_handle, MONO_W32HANDLE_NAMEDSEM, initial, max);
234         }
235
236         g_free (utf8_name);
237
238         mono_w32handle_namespace_unlock ();
239
240         return handle;
241 }
242
243 gpointer
244 ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount, gint32 maximumCount, MonoString *name, gint32 *error)
245
246         gpointer sem;
247
248         if (maximumCount <= 0) {
249                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: maximumCount <= 0", __func__);
250
251                 *error = ERROR_INVALID_PARAMETER;
252                 return NULL;
253         }
254
255         if (initialCount > maximumCount || initialCount < 0) {
256                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: initialCount > maximumCount or < 0", __func__);
257
258                 *error = ERROR_INVALID_PARAMETER;
259                 return NULL;
260         }
261
262         /* Need to blow away any old errors here, because code tests
263          * for ERROR_ALREADY_EXISTS on success (!) to see if a
264          * semaphore was freshly created
265          */
266         mono_w32error_set_last (ERROR_SUCCESS);
267
268         if (!name)
269                 sem = sem_create (initialCount, maximumCount);
270         else
271                 sem = namedsem_create (initialCount, maximumCount, mono_string_chars (name));
272
273         *error = mono_w32error_get_last ();
274
275         return sem;
276 }
277
278 MonoBoolean
279 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (gpointer handle, gint32 releaseCount, gint32 *prevcount)
280 {
281         MonoW32HandleType type;
282         MonoW32HandleSemaphore *sem_handle;
283         MonoBoolean ret;
284
285         if (!handle) {
286                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
287                 return FALSE;
288         }
289
290         switch (type = mono_w32handle_get_type (handle)) {
291         case MONO_W32HANDLE_SEM:
292         case MONO_W32HANDLE_NAMEDSEM:
293                 break;
294         default:
295                 mono_w32error_set_last (ERROR_INVALID_HANDLE);
296                 return FALSE;
297         }
298
299         if (!mono_w32handle_lookup (handle, type, (gpointer *)&sem_handle)) {
300                 g_warning ("%s: error looking up sem handle %p", __func__, handle);
301                 return FALSE;
302         }
303
304         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p",
305                 __func__, mono_w32handle_get_typename (type), handle);
306
307         mono_w32handle_lock_handle (handle);
308
309         /* Do this before checking for count overflow, because overflowing
310          * max is a listed technique for finding the current value */
311         if (prevcount)
312                 *prevcount = sem_handle->val;
313
314         /* No idea why max is signed, but thats the spec :-( */
315         if (sem_handle->val + releaseCount > (guint32)sem_handle->max) {
316                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p val %d count %d max %d, max value would be exceeded",
317                         __func__, mono_w32handle_get_typename (type), handle, sem_handle->val, releaseCount, sem_handle->max);
318
319                 ret = FALSE;
320         } else {
321                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p val %d count %d max %d",
322                         __func__, mono_w32handle_get_typename (type), handle, sem_handle->val, releaseCount, sem_handle->max);
323
324                 sem_handle->val += releaseCount;
325                 mono_w32handle_set_signal_state (handle, TRUE, TRUE);
326
327                 ret = TRUE;
328         }
329
330         mono_w32handle_unlock_handle (handle);
331
332         return ret;
333 }
334
335 gpointer
336 ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString *name, gint32 rights, gint32 *error)
337 {
338         gpointer handle;
339         gchar *utf8_name;
340
341         *error = ERROR_SUCCESS;
342
343         /* w32 seems to guarantee that opening named objects can't race each other */
344         mono_w32handle_namespace_lock ();
345
346         utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
347
348         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named sem [%s]", __func__, utf8_name);
349
350         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDSEM, utf8_name);
351         if (handle == INVALID_HANDLE_VALUE) {
352                 /* The name has already been used for a different object. */
353                 *error = ERROR_INVALID_HANDLE;
354                 goto cleanup;
355         } else if (!handle) {
356                 /* This name doesn't exist */
357                 *error = ERROR_FILE_NOT_FOUND;
358                 goto cleanup;
359         }
360
361         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named sem handle %p", __func__, handle);
362
363 cleanup:
364         g_free (utf8_name);
365
366         mono_w32handle_namespace_unlock ();
367
368         return handle;
369 }
370
371 MonoW32HandleNamespace*
372 mono_w32semaphore_get_namespace (MonoW32HandleNamedSemaphore *semaphore)
373 {
374         return &semaphore->sharedns;
375 }