[runtime] Fix monitor exception throwing
[mono.git] / mono / metadata / w32event-unix.c
1 /*
2  * w32event-unix.c: Runtime support for managed Event on Unix
3  *
4  * Author:
5  *      Ludovic Henry (luhenry@microsoft.com)
6  *
7  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8  */
9
10 #include "w32event.h"
11
12 #include "w32handle-namespace.h"
13 #include "mono/io-layer/io-layer.h"
14 #include "mono/utils/mono-logger-internals.h"
15 #include "mono/metadata/w32handle.h"
16
17 typedef struct {
18         gboolean manual;
19         guint32 set_count;
20 } MonoW32HandleEvent;
21
22 struct MonoW32HandleNamedEvent {
23         MonoW32HandleEvent e;
24         MonoW32HandleNamespace sharedns;
25 };
26
27 static gboolean event_handle_own (gpointer handle, MonoW32HandleType type, guint32 *statuscode)
28 {
29         MonoW32HandleEvent *event_handle;
30         gboolean ok;
31
32         *statuscode = WAIT_OBJECT_0;
33
34         ok = mono_w32handle_lookup (handle, type, (gpointer *)&event_handle);
35         if (!ok) {
36                 g_warning ("%s: error looking up %s handle %p",
37                         __func__, mono_w32handle_ops_typename (type), handle);
38                 return FALSE;
39         }
40
41         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: owning %s handle %p",
42                 __func__, mono_w32handle_ops_typename (type), handle);
43
44         if (!event_handle->manual) {
45                 g_assert (event_handle->set_count > 0);
46                 event_handle->set_count --;
47
48                 if (event_handle->set_count == 0)
49                         mono_w32handle_set_signal_state (handle, FALSE, FALSE);
50         }
51
52         return TRUE;
53 }
54
55 static void event_signal(gpointer handle)
56 {
57         ves_icall_System_Threading_Events_SetEvent_internal (handle);
58 }
59
60 static gboolean event_own (gpointer handle, guint32 *statuscode)
61 {
62         return event_handle_own (handle, MONO_W32HANDLE_EVENT, statuscode);
63 }
64
65 static void namedevent_signal (gpointer handle)
66 {
67         ves_icall_System_Threading_Events_SetEvent_internal (handle);
68 }
69
70 /* NB, always called with the shared handle lock held */
71 static gboolean namedevent_own (gpointer handle, guint32 *statuscode)
72 {
73         return event_handle_own (handle, MONO_W32HANDLE_NAMEDEVENT, statuscode);
74 }
75
76 static void event_details (gpointer data)
77 {
78         MonoW32HandleEvent *event = (MonoW32HandleEvent *)data;
79         g_print ("manual: %s, set_count: %d",
80                 event->manual ? "TRUE" : "FALSE", event->set_count);
81 }
82
83 static void namedevent_details (gpointer data)
84 {
85         MonoW32HandleNamedEvent *namedevent = (MonoW32HandleNamedEvent *)data;
86         g_print ("manual: %s, set_count: %d, name: \"%s\"",
87                 namedevent->e.manual ? "TRUE" : "FALSE", namedevent->e.set_count, namedevent->sharedns.name);
88 }
89
90 static const gchar* event_typename (void)
91 {
92         return "Event";
93 }
94
95 static gsize event_typesize (void)
96 {
97         return sizeof (MonoW32HandleEvent);
98 }
99
100 static const gchar* namedevent_typename (void)
101 {
102         return "N.Event";
103 }
104
105 static gsize namedevent_typesize (void)
106 {
107         return sizeof (MonoW32HandleNamedEvent);
108 }
109
110 void
111 mono_w32event_init (void)
112 {
113         static MonoW32HandleOps event_ops = {
114                 NULL,                   /* close */
115                 event_signal,           /* signal */
116                 event_own,              /* own */
117                 NULL,                   /* is_owned */
118                 NULL,                   /* special_wait */
119                 NULL,                   /* prewait */
120                 event_details,  /* details */
121                 event_typename, /* typename */
122                 event_typesize, /* typesize */
123         };
124
125         static MonoW32HandleOps namedevent_ops = {
126                 NULL,                   /* close */
127                 namedevent_signal,      /* signal */
128                 namedevent_own,         /* own */
129                 NULL,                   /* is_owned */
130                 NULL,                   /* special_wait */
131                 NULL,                   /* prewait */
132                 namedevent_details,     /* details */
133                 namedevent_typename, /* typename */
134                 namedevent_typesize, /* typesize */
135         };
136
137         mono_w32handle_register_ops (MONO_W32HANDLE_EVENT,      &event_ops);
138         mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDEVENT, &namedevent_ops);
139
140         mono_w32handle_register_capabilities (MONO_W32HANDLE_EVENT,
141                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL));
142         mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDEVENT,
143                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL));
144 }
145
146 gpointer
147 mono_w32event_create (gboolean manual, gboolean initial)
148 {
149         gpointer handle;
150         gint32 error;
151
152         handle = ves_icall_System_Threading_Events_CreateEvent_internal (manual, initial, NULL, &error);
153         if (error != ERROR_SUCCESS)
154                 g_assert (!handle);
155
156         return handle;
157 }
158
159 void
160 mono_w32event_set (gpointer handle)
161 {
162         ves_icall_System_Threading_Events_SetEvent_internal (handle);
163 }
164
165 void
166 mono_w32event_reset (gpointer handle)
167 {
168         ves_icall_System_Threading_Events_ResetEvent_internal (handle);
169 }
170
171 static gpointer event_handle_create (MonoW32HandleEvent *event_handle, MonoW32HandleType type, gboolean manual, gboolean initial)
172 {
173         gpointer handle;
174         int thr_ret;
175
176         event_handle->manual = manual;
177         event_handle->set_count = (initial && !manual) ? 1 : 0;
178
179         handle = mono_w32handle_new (type, event_handle);
180         if (handle == INVALID_HANDLE_VALUE) {
181                 g_warning ("%s: error creating %s handle",
182                         __func__, mono_w32handle_ops_typename (type));
183                 SetLastError (ERROR_GEN_FAILURE);
184                 return NULL;
185         }
186
187         thr_ret = mono_w32handle_lock_handle (handle);
188         g_assert (thr_ret == 0);
189
190         if (initial)
191                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
192
193         thr_ret = mono_w32handle_unlock_handle (handle);
194         g_assert (thr_ret == 0);
195
196         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
197                 __func__, mono_w32handle_ops_typename (type), handle);
198
199         return handle;
200 }
201
202 static gpointer event_create (gboolean manual, gboolean initial)
203 {
204         MonoW32HandleEvent event_handle;
205         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
206                 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_EVENT));
207         return event_handle_create (&event_handle, MONO_W32HANDLE_EVENT, manual, initial);
208 }
209
210 static gpointer namedevent_create (gboolean manual, gboolean initial, const gunichar2 *name G_GNUC_UNUSED)
211 {
212         gpointer handle;
213         gchar *utf8_name;
214
215         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
216                 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_NAMEDEVENT));
217
218         /* w32 seems to guarantee that opening named objects can't race each other */
219         mono_w32handle_namespace_lock ();
220
221         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
222
223         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDEVENT, utf8_name);
224         if (handle == INVALID_HANDLE_VALUE) {
225                 /* The name has already been used for a different object. */
226                 handle = NULL;
227                 SetLastError (ERROR_INVALID_HANDLE);
228         } else if (handle) {
229                 /* Not an error, but this is how the caller is informed that the event wasn't freshly created */
230                 SetLastError (ERROR_ALREADY_EXISTS);
231
232                 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
233         } else {
234                 /* A new named event */
235                 MonoW32HandleNamedEvent namedevent_handle;
236
237                 strncpy (&namedevent_handle.sharedns.name [0], utf8_name, MAX_PATH);
238                 namedevent_handle.sharedns.name [MAX_PATH] = '\0';
239
240                 handle = event_handle_create ((MonoW32HandleEvent*) &namedevent_handle, MONO_W32HANDLE_NAMEDEVENT, manual, initial);
241         }
242
243         g_free (utf8_name);
244
245         mono_w32handle_namespace_unlock ();
246
247         return handle;
248 }
249
250 gpointer
251 ves_icall_System_Threading_Events_CreateEvent_internal (MonoBoolean manual, MonoBoolean initial, MonoString *name, gint32 *error)
252 {
253         gpointer event;
254
255         /* Need to blow away any old errors here, because code tests
256          * for ERROR_ALREADY_EXISTS on success (!) to see if an event
257          * was freshly created */
258         SetLastError (ERROR_SUCCESS);
259
260         event = name ? namedevent_create (manual, initial, mono_string_chars (name)) : event_create (manual, initial);
261
262         *error = GetLastError ();
263
264         return event;
265 }
266
267 gboolean
268 ves_icall_System_Threading_Events_SetEvent_internal (gpointer handle)
269 {
270         MonoW32HandleType type;
271         MonoW32HandleEvent *event_handle;
272         int thr_ret;
273
274         if (handle == NULL) {
275                 SetLastError (ERROR_INVALID_HANDLE);
276                 return(FALSE);
277         }
278
279         switch (type = mono_w32handle_get_type (handle)) {
280         case MONO_W32HANDLE_EVENT:
281         case MONO_W32HANDLE_NAMEDEVENT:
282                 break;
283         default:
284                 SetLastError (ERROR_INVALID_HANDLE);
285                 return FALSE;
286         }
287
288         if (!mono_w32handle_lookup (handle, type, (gpointer *)&event_handle)) {
289                 g_warning ("%s: error looking up %s handle %p",
290                         __func__, mono_w32handle_ops_typename (type), handle);
291                 return FALSE;
292         }
293
294         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: setting %s handle %p",
295                 __func__, mono_w32handle_ops_typename (type), handle);
296
297         thr_ret = mono_w32handle_lock_handle (handle);
298         g_assert (thr_ret == 0);
299
300         if (!event_handle->manual) {
301                 event_handle->set_count = 1;
302                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
303         } else {
304                 mono_w32handle_set_signal_state (handle, TRUE, TRUE);
305         }
306
307         thr_ret = mono_w32handle_unlock_handle (handle);
308         g_assert (thr_ret == 0);
309
310         return TRUE;
311 }
312
313 gboolean
314 ves_icall_System_Threading_Events_ResetEvent_internal (gpointer handle)
315 {
316         MonoW32HandleType type;
317         MonoW32HandleEvent *event_handle;
318         int thr_ret;
319
320         SetLastError (ERROR_SUCCESS);
321
322         if (handle == NULL) {
323                 SetLastError (ERROR_INVALID_HANDLE);
324                 return(FALSE);
325         }
326
327         switch (type = mono_w32handle_get_type (handle)) {
328         case MONO_W32HANDLE_EVENT:
329         case MONO_W32HANDLE_NAMEDEVENT:
330                 break;
331         default:
332                 SetLastError (ERROR_INVALID_HANDLE);
333                 return FALSE;
334         }
335
336         if (!mono_w32handle_lookup (handle, type, (gpointer *)&event_handle)) {
337                 g_warning ("%s: error looking up %s handle %p",
338                         __func__, mono_w32handle_ops_typename (type), handle);
339                 return FALSE;
340         }
341
342         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: resetting %s handle %p",
343                 __func__, mono_w32handle_ops_typename (type), handle);
344
345         thr_ret = mono_w32handle_lock_handle (handle);
346         g_assert (thr_ret == 0);
347
348         if (!mono_w32handle_issignalled (handle)) {
349                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: no need to reset %s handle %p",
350                         __func__, mono_w32handle_ops_typename (type), handle);
351         } else {
352                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: obtained write lock on %s handle %p",
353                         __func__, mono_w32handle_ops_typename (type), handle);
354
355                 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
356         }
357
358         event_handle->set_count = 0;
359
360         thr_ret = mono_w32handle_unlock_handle (handle);
361         g_assert (thr_ret == 0);
362
363         return TRUE;
364 }
365
366 void
367 ves_icall_System_Threading_Events_CloseEvent_internal (gpointer handle)
368 {
369         CloseHandle (handle);
370 }
371
372 gpointer
373 ves_icall_System_Threading_Events_OpenEvent_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
374 {
375         gpointer handle;
376         gchar *utf8_name;
377
378         *error = ERROR_SUCCESS;
379
380         /* w32 seems to guarantee that opening named objects can't race each other */
381         mono_w32handle_namespace_lock ();
382
383         utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
384
385         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named event [%s]", __func__, utf8_name);
386
387         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDEVENT, utf8_name);
388         if (handle == INVALID_HANDLE_VALUE) {
389                 /* The name has already been used for a different object. */
390                 *error = ERROR_INVALID_HANDLE;
391                 goto cleanup;
392         } else if (!handle) {
393                 /* This name doesn't exist */
394                 *error = ERROR_FILE_NOT_FOUND;
395                 goto cleanup;
396         }
397
398         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named event handle %p", __func__, handle);
399
400 cleanup:
401         g_free (utf8_name);
402
403         mono_w32handle_namespace_unlock ();
404
405         return handle;
406 }
407
408 MonoW32HandleNamespace*
409 mono_w32event_get_namespace (MonoW32HandleNamedEvent *event)
410 {
411         return &event->sharedns;
412 }