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