Merge pull request #228 from QuickJack/3e163743eda89cc8c239779a75dd245be12aee3c
[mono.git] / mono / io-layer / events.c
1 /*
2  * events.c:  Event 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 #include <string.h>
14
15 #include <mono/io-layer/wapi.h>
16 #include <mono/io-layer/wapi-private.h>
17 #include <mono/io-layer/handles-private.h>
18 #include <mono/io-layer/misc-private.h>
19
20 #include <mono/io-layer/mono-mutex.h>
21
22 #include <mono/io-layer/event-private.h>
23
24 #if 0
25 #define DEBUG(...) g_message(__VA_ARGS__)
26 #else
27 #define DEBUG(...)
28 #endif
29
30 static void event_signal(gpointer handle);
31 static gboolean event_own (gpointer handle);
32
33 static void namedevent_signal (gpointer handle);
34 static gboolean namedevent_own (gpointer handle);
35
36 struct _WapiHandleOps _wapi_event_ops = {
37         NULL,                   /* close */
38         event_signal,           /* signal */
39         event_own,              /* own */
40         NULL,                   /* is_owned */
41         NULL,                   /* special_wait */
42         NULL                    /* prewait */
43 };
44
45 struct _WapiHandleOps _wapi_namedevent_ops = {
46         NULL,                   /* close */
47         namedevent_signal,      /* signal */
48         namedevent_own,         /* own */
49         NULL,                   /* is_owned */
50 };
51
52 static gboolean event_pulse (gpointer handle);
53 static gboolean event_reset (gpointer handle);
54 static gboolean event_set (gpointer handle);
55
56 static gboolean namedevent_pulse (gpointer handle);
57 static gboolean namedevent_reset (gpointer handle);
58 static gboolean namedevent_set (gpointer handle);
59
60 static struct
61 {
62         gboolean (*pulse)(gpointer handle);
63         gboolean (*reset)(gpointer handle);
64         gboolean (*set)(gpointer handle);
65 } event_ops[WAPI_HANDLE_COUNT] = {
66                 {NULL},
67                 {NULL},
68                 {NULL},
69                 {NULL},
70                 {NULL},
71                 {NULL},
72                 {event_pulse, event_reset, event_set},
73                 {NULL},
74                 {NULL},
75                 {NULL},
76                 {NULL},
77                 {NULL},
78                 {NULL},
79                 {namedevent_pulse, namedevent_reset, namedevent_set},
80 };
81
82 void _wapi_event_details (gpointer handle_info)
83 {
84         struct _WapiHandle_event *event = (struct _WapiHandle_event *)handle_info;
85         
86         g_print ("manual: %s", event->manual?"TRUE":"FALSE");
87 }
88
89 static mono_once_t event_ops_once=MONO_ONCE_INIT;
90
91 static void event_ops_init (void)
92 {
93         _wapi_handle_register_capabilities (WAPI_HANDLE_EVENT,
94                                             WAPI_HANDLE_CAP_WAIT |
95                                             WAPI_HANDLE_CAP_SIGNAL);
96         _wapi_handle_register_capabilities (WAPI_HANDLE_NAMEDEVENT,
97                                             WAPI_HANDLE_CAP_WAIT |
98                                             WAPI_HANDLE_CAP_SIGNAL);
99 }
100
101 static void event_signal(gpointer handle)
102 {
103         SetEvent(handle);
104 }
105
106 static gboolean event_own (gpointer handle)
107 {
108         struct _WapiHandle_event *event_handle;
109         gboolean ok;
110         
111         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
112                                 (gpointer *)&event_handle);
113         if(ok==FALSE) {
114                 g_warning ("%s: error looking up event handle %p", __func__,
115                            handle);
116                 return (FALSE);
117         }
118         
119         DEBUG("%s: owning event handle %p", __func__, handle);
120
121         if(event_handle->manual==FALSE) {
122                 g_assert (event_handle->set_count > 0);
123                 
124                 if (--event_handle->set_count == 0) {
125                         _wapi_handle_set_signal_state (handle, FALSE, FALSE);
126                 }
127         }
128
129         return(TRUE);
130 }
131
132 static void namedevent_signal (gpointer handle)
133 {
134         SetEvent (handle);
135 }
136
137 /* NB, always called with the shared handle lock held */
138 static gboolean namedevent_own (gpointer handle)
139 {
140         struct _WapiHandle_namedevent *namedevent_handle;
141         gboolean ok;
142         
143         DEBUG ("%s: owning named event handle %p", __func__, handle);
144
145         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDEVENT,
146                                   (gpointer *)&namedevent_handle);
147         if (ok == FALSE) {
148                 g_warning ("%s: error looking up named event handle %p",
149                            __func__, handle);
150                 return(FALSE);
151         }
152         
153         if (namedevent_handle->manual == FALSE) {
154                 g_assert (namedevent_handle->set_count > 0);
155                 
156                 if (--namedevent_handle->set_count == 0) {
157                         _wapi_shared_handle_set_signal_state (handle, FALSE);
158                 }
159         }
160         
161         return (TRUE);
162 }
163 static gpointer event_create (WapiSecurityAttributes *security G_GNUC_UNUSED,
164                               gboolean manual, gboolean initial)
165 {
166         struct _WapiHandle_event event_handle = {0};
167         gpointer handle;
168         int thr_ret;
169         
170         /* Need to blow away any old errors here, because code tests
171          * for ERROR_ALREADY_EXISTS on success (!) to see if an event
172          * was freshly created
173          */
174         SetLastError (ERROR_SUCCESS);
175
176         DEBUG ("%s: Creating unnamed event", __func__);
177         
178         event_handle.manual = manual;
179         event_handle.set_count = 0;
180
181         if (initial == TRUE) {
182                 if (manual == FALSE) {
183                         event_handle.set_count = 1;
184                 }
185         }
186         
187         handle = _wapi_handle_new (WAPI_HANDLE_EVENT, &event_handle);
188         if (handle == _WAPI_HANDLE_INVALID) {
189                 g_warning ("%s: error creating event handle", __func__);
190                 SetLastError (ERROR_GEN_FAILURE);
191                 return(NULL);
192         }
193
194         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
195                               handle);
196         thr_ret = _wapi_handle_lock_handle (handle);
197         g_assert (thr_ret == 0);
198         
199         if (initial == TRUE) {
200                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
201         }
202         
203         DEBUG("%s: created new event handle %p", __func__, handle);
204
205         thr_ret = _wapi_handle_unlock_handle (handle);
206         g_assert (thr_ret == 0);
207         pthread_cleanup_pop (0);
208
209         return(handle);
210 }
211
212 static gpointer namedevent_create (WapiSecurityAttributes *security G_GNUC_UNUSED,
213                                    gboolean manual, gboolean initial,
214                                    const gunichar2 *name G_GNUC_UNUSED)
215 {
216         struct _WapiHandle_namedevent namedevent_handle = {{{0}}, 0};
217         gpointer handle;
218         gchar *utf8_name;
219         int thr_ret;
220         gpointer ret = NULL;
221         guint32 namelen;
222         gint32 offset;
223         
224         /* w32 seems to guarantee that opening named objects can't
225          * race each other
226          */
227         thr_ret = _wapi_namespace_lock ();
228         g_assert (thr_ret == 0);
229
230         /* Need to blow away any old errors here, because code tests
231          * for ERROR_ALREADY_EXISTS on success (!) to see if an event
232          * was freshly created
233          */
234         SetLastError (ERROR_SUCCESS);
235         
236         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
237         
238         DEBUG ("%s: Creating named event [%s]", __func__, utf8_name);
239         
240         offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDEVENT,
241                                                 utf8_name);
242         if (offset == -1) {
243                 /* The name has already been used for a different
244                  * object.
245                  */
246                 SetLastError (ERROR_INVALID_HANDLE);
247                 goto cleanup;
248         } else if (offset != 0) {
249                 /* Not an error, but this is how the caller is
250                  * informed that the event wasn't freshly created
251                  */
252                 SetLastError (ERROR_ALREADY_EXISTS);
253         }
254         /* Fall through to create the event handle. */
255
256         if (offset == 0) {
257                 /* A new named event, so create both the private and
258                  * shared parts
259                  */
260         
261                 if (strlen (utf8_name) < MAX_PATH) {
262                         namelen = strlen (utf8_name);
263                 } else {
264                         namelen = MAX_PATH;
265                 }
266         
267                 memcpy (&namedevent_handle.sharedns.name, utf8_name, namelen);
268
269                 namedevent_handle.manual = manual;
270                 namedevent_handle.set_count = 0;
271                 
272                 if (initial == TRUE) {
273                         if (manual == FALSE) {
274                                 namedevent_handle.set_count = 1;
275                         }
276                 }
277                 
278                 handle = _wapi_handle_new (WAPI_HANDLE_NAMEDEVENT,
279                                            &namedevent_handle);
280         } else {
281                 /* A new reference to an existing named event, so just
282                  * create the private part
283                  */
284                 handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDEVENT,
285                                                        offset, TRUE);
286         }
287         
288         if (handle == _WAPI_HANDLE_INVALID) {
289                 g_warning ("%s: error creating event handle", __func__);
290                 SetLastError (ERROR_GEN_FAILURE);
291                 goto cleanup;
292         }
293         ret = handle;
294
295         if (offset == 0) {
296                 /* Set the initial state, as this is a completely new
297                  * handle
298                  */
299                 thr_ret = _wapi_handle_lock_shared_handles ();
300                 g_assert (thr_ret == 0);
301         
302                 if (initial == TRUE) {
303                         _wapi_shared_handle_set_signal_state (handle, TRUE);
304                 }
305
306                 _wapi_handle_unlock_shared_handles ();
307         }
308         
309         DEBUG ("%s: returning event handle %p", __func__, handle);
310
311 cleanup:
312         g_free (utf8_name);
313
314         _wapi_namespace_unlock (NULL);
315         
316         return(ret);
317
318 }
319
320
321 /**
322  * CreateEvent:
323  * @security: Ignored for now.
324  * @manual: Specifies whether the new event handle has manual or auto
325  * reset behaviour.
326  * @initial: Specifies whether the new event handle is initially
327  * signalled or not.
328  * @name:Pointer to a string specifying the name of this name, or
329  * %NULL.  Currently ignored.
330  *
331  * Creates a new event handle.
332  *
333  * An event handle is signalled with SetEvent().  If the new handle is
334  * a manual reset event handle, it remains signalled until it is reset
335  * with ResetEvent().  An auto reset event remains signalled until a
336  * single thread has waited for it, at which time the event handle is
337  * automatically reset to unsignalled.
338  *
339  * Return value: A new handle, or %NULL on error.
340  */
341 gpointer CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED,
342                      gboolean manual, gboolean initial,
343                      const gunichar2 *name G_GNUC_UNUSED)
344 {
345         mono_once (&event_ops_once, event_ops_init);
346
347         if (name == NULL) {
348                 return(event_create (security, manual, initial));
349         } else {
350                 return(namedevent_create (security, manual, initial, name));
351         }
352 }
353
354 static gboolean event_pulse (gpointer handle)
355 {
356         struct _WapiHandle_event *event_handle;
357         gboolean ok;
358         int thr_ret;
359         
360         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
361                                   (gpointer *)&event_handle);
362         if (ok == FALSE) {
363                 g_warning ("%s: error looking up event handle %p", __func__,
364                            handle);
365                 return(FALSE);
366         }
367         
368         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
369                               handle);
370         thr_ret = _wapi_handle_lock_handle (handle);
371         g_assert (thr_ret == 0);
372
373         DEBUG ("%s: Pulsing event handle %p", __func__, handle);
374
375         if (event_handle->manual == TRUE) {
376                 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
377         } else {
378                 event_handle->set_count = 1;
379                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
380         }
381
382         thr_ret = _wapi_handle_unlock_handle (handle);
383         g_assert (thr_ret == 0);
384         
385         pthread_cleanup_pop (0);
386         
387         if (event_handle->manual == TRUE) {
388                 /* For a manual-reset event, we're about to try and
389                  * get the handle lock again, so give other threads a
390                  * chance
391                  */
392                 sched_yield ();
393
394                 /* Reset the handle signal state */
395                 /* I'm not sure whether or not we need a barrier here
396                  * to make sure that all threads waiting on the event
397                  * have proceeded.  Currently we rely on broadcasting
398                  * a condition.
399                  */
400                 DEBUG ("%s: Obtained write lock on event handle %p",
401                            __func__, handle);
402
403                 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle, handle);
404                 thr_ret = _wapi_handle_lock_handle (handle);
405                 g_assert (thr_ret == 0);
406                 
407                 _wapi_handle_set_signal_state (handle, FALSE, FALSE);
408
409                 thr_ret = _wapi_handle_unlock_handle (handle);
410                 g_assert (thr_ret == 0);
411                 pthread_cleanup_pop (0);
412         }
413
414         return(TRUE);
415 }
416
417 static gboolean namedevent_pulse (gpointer handle)
418 {
419         struct _WapiHandle_namedevent *namedevent_handle;
420         gboolean ok;
421         int thr_ret;
422         
423         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDEVENT,
424                                   (gpointer *)&namedevent_handle);
425         if (ok == FALSE) {
426                 g_warning ("%s: error looking up named event handle %p",
427                            __func__, handle);
428                 return(FALSE);
429         }
430         
431         thr_ret = _wapi_handle_lock_shared_handles ();
432         g_assert (thr_ret == 0);
433
434         DEBUG ("%s: Pulsing named event handle %p", __func__, handle);
435
436         if (namedevent_handle->manual == TRUE) {
437                 _wapi_shared_handle_set_signal_state (handle, TRUE);
438         } else {
439                 namedevent_handle->set_count = 1;
440                 _wapi_shared_handle_set_signal_state (handle, TRUE);
441         }
442
443         _wapi_handle_unlock_shared_handles ();
444         
445         if (namedevent_handle->manual == TRUE) {
446                 /* For a manual-reset event, we're about to try and
447                  * get the handle lock again, so give other processes
448                  * a chance
449                  */
450                 _wapi_handle_spin (200);
451
452                 /* Reset the handle signal state */
453                 /* I'm not sure whether or not we need a barrier here
454                  * to make sure that all threads waiting on the event
455                  * have proceeded.  Currently we rely on waiting for
456                  * twice the shared handle poll interval.
457                  */
458                 DEBUG ("%s: Obtained write lock on event handle %p",
459                            __func__, handle);
460
461                 thr_ret = _wapi_handle_lock_shared_handles ();
462                 g_assert (thr_ret == 0);
463                 
464                 _wapi_shared_handle_set_signal_state (handle, FALSE);
465
466                 _wapi_handle_unlock_shared_handles ();
467         }
468
469         return(TRUE);
470 }
471
472 /**
473  * PulseEvent:
474  * @handle: The event handle.
475  *
476  * Sets the event handle @handle to the signalled state, and then
477  * resets it to unsignalled after informing any waiting threads.
478  *
479  * If @handle is a manual reset event, all waiting threads that can be
480  * released immediately are released.  @handle is then reset.  If
481  * @handle is an auto reset event, one waiting thread is released even
482  * if multiple threads are waiting.
483  *
484  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
485  * ever returns %TRUE).
486  */
487 gboolean PulseEvent(gpointer handle)
488 {
489         WapiHandleType type;
490         
491         if (handle == NULL) {
492                 SetLastError (ERROR_INVALID_HANDLE);
493                 return(FALSE);
494         }
495         
496         type = _wapi_handle_type (handle);
497         
498         if (event_ops[type].pulse == NULL) {
499                 SetLastError (ERROR_INVALID_HANDLE);
500                 return(FALSE);
501         }
502         
503         return(event_ops[type].pulse (handle));
504 }
505
506 static gboolean event_reset (gpointer handle)
507 {
508         struct _WapiHandle_event *event_handle;
509         gboolean ok;
510         int thr_ret;
511         
512         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
513                                   (gpointer *)&event_handle);
514         if (ok == FALSE) {
515                 g_warning ("%s: error looking up event handle %p",
516                            __func__, handle);
517                 return(FALSE);
518         }
519
520         DEBUG ("%s: Resetting event handle %p", __func__, handle);
521
522         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
523                               handle);
524         thr_ret = _wapi_handle_lock_handle (handle);
525         g_assert (thr_ret == 0);
526         
527         if (_wapi_handle_issignalled (handle) == FALSE) {
528                 DEBUG ("%s: No need to reset event handle %p", __func__,
529                            handle);
530         } else {
531                 DEBUG ("%s: Obtained write lock on event handle %p",
532                            __func__, handle);
533
534                 _wapi_handle_set_signal_state (handle, FALSE, FALSE);
535         }
536         
537         event_handle->set_count = 0;
538         
539         thr_ret = _wapi_handle_unlock_handle (handle);
540         g_assert (thr_ret == 0);
541         
542         pthread_cleanup_pop (0);
543         
544         return(TRUE);
545 }
546
547 static gboolean namedevent_reset (gpointer handle)
548 {
549         struct _WapiHandle_namedevent *namedevent_handle;
550         gboolean ok;
551         int thr_ret;
552         
553         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDEVENT,
554                                   (gpointer *)&namedevent_handle);
555         if (ok == FALSE) {
556                 g_warning ("%s: error looking up named event handle %p",
557                            __func__, handle);
558                 return(FALSE);
559         }
560
561         DEBUG ("%s: Resetting named event handle %p", __func__, handle);
562
563         thr_ret = _wapi_handle_lock_shared_handles ();
564         g_assert (thr_ret == 0);
565         
566         if (_wapi_handle_issignalled (handle) == FALSE) {
567                 DEBUG ("%s: No need to reset named event handle %p",
568                            __func__, handle);
569         } else {
570                 DEBUG ("%s: Obtained write lock on named event handle %p",
571                            __func__, handle);
572
573                 _wapi_shared_handle_set_signal_state (handle, FALSE);
574         }
575         
576         namedevent_handle->set_count = 0;
577         
578         _wapi_handle_unlock_shared_handles ();
579         
580         return(TRUE);
581 }
582
583 /**
584  * ResetEvent:
585  * @handle: The event handle.
586  *
587  * Resets the event handle @handle to the unsignalled state.
588  *
589  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
590  * ever returns %TRUE).
591  */
592 gboolean ResetEvent(gpointer handle)
593 {
594         WapiHandleType type;
595         
596         if (handle == NULL) {
597                 SetLastError (ERROR_INVALID_HANDLE);
598                 return(FALSE);
599         }
600         
601         type = _wapi_handle_type (handle);
602         
603         if (event_ops[type].reset == NULL) {
604                 SetLastError (ERROR_INVALID_HANDLE);
605                 return(FALSE);
606         }
607         
608         return(event_ops[type].reset (handle));
609 }
610
611 static gboolean event_set (gpointer handle)
612 {
613         struct _WapiHandle_event *event_handle;
614         gboolean ok;
615         int thr_ret;
616         
617         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
618                                   (gpointer *)&event_handle);
619         if (ok == FALSE) {
620                 g_warning ("%s: error looking up event handle %p", __func__,
621                            handle);
622                 return(FALSE);
623         }
624         
625         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
626                               handle);
627         thr_ret = _wapi_handle_lock_handle (handle);
628         g_assert (thr_ret == 0);
629
630         DEBUG ("%s: Setting event handle %p", __func__, handle);
631
632         if (event_handle->manual == TRUE) {
633                 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
634         } else {
635                 event_handle->set_count = 1;
636                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
637         }
638
639         thr_ret = _wapi_handle_unlock_handle (handle);
640         g_assert (thr_ret == 0);
641         
642         pthread_cleanup_pop (0);
643
644         return(TRUE);
645 }
646
647 static gboolean namedevent_set (gpointer handle)
648 {
649         struct _WapiHandle_namedevent *namedevent_handle;
650         gboolean ok;
651         int thr_ret;
652         
653         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDEVENT,
654                                   (gpointer *)&namedevent_handle);
655         if (ok == FALSE) {
656                 g_warning ("%s: error looking up named event handle %p",
657                            __func__, handle);
658                 return(FALSE);
659         }
660         
661         thr_ret = _wapi_handle_lock_shared_handles ();
662         g_assert (thr_ret == 0);
663
664         DEBUG ("%s: Setting named event handle %p", __func__, handle);
665
666         if (namedevent_handle->manual == TRUE) {
667                 _wapi_shared_handle_set_signal_state (handle, TRUE);
668         } else {
669                 namedevent_handle->set_count = 1;
670                 _wapi_shared_handle_set_signal_state (handle, TRUE);
671         }
672
673         _wapi_handle_unlock_shared_handles ();
674
675         return(TRUE);
676 }
677
678 /**
679  * SetEvent:
680  * @handle: The event handle
681  *
682  * Sets the event handle @handle to the signalled state.
683  *
684  * If @handle is a manual reset event, it remains signalled until it
685  * is reset with ResetEvent().  An auto reset event remains signalled
686  * until a single thread has waited for it, at which time @handle is
687  * automatically reset to unsignalled.
688  *
689  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
690  * ever returns %TRUE).
691  */
692 gboolean SetEvent(gpointer handle)
693 {
694         WapiHandleType type;
695         
696         if (handle == NULL) {
697                 SetLastError (ERROR_INVALID_HANDLE);
698                 return(FALSE);
699         }
700         
701         type = _wapi_handle_type (handle);
702         
703         if (event_ops[type].set == NULL) {
704                 SetLastError (ERROR_INVALID_HANDLE);
705                 return(FALSE);
706         }
707         
708         return(event_ops[type].set (handle));
709 }
710
711 gpointer OpenEvent (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, const gunichar2 *name)
712 {
713         gpointer handle;
714         gchar *utf8_name;
715         int thr_ret;
716         gpointer ret = NULL;
717         gint32 offset;
718         
719         mono_once (&event_ops_once, event_ops_init);
720
721         /* w32 seems to guarantee that opening named objects can't
722          * race each other
723          */
724         thr_ret = _wapi_namespace_lock ();
725         g_assert (thr_ret == 0);
726
727         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
728         
729         DEBUG ("%s: Opening named event [%s]", __func__, utf8_name);
730         
731         offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDEVENT,
732                                                 utf8_name);
733         if (offset == -1) {
734                 /* The name has already been used for a different
735                  * object.
736                  */
737                 SetLastError (ERROR_INVALID_HANDLE);
738                 goto cleanup;
739         } else if (offset == 0) {
740                 /* This name doesn't exist */
741                 SetLastError (ERROR_FILE_NOT_FOUND);    /* yes, really */
742                 goto cleanup;
743         }
744
745         /* A new reference to an existing named event, so just create
746          * the private part
747          */
748         handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDEVENT, offset,
749                                                TRUE);
750         
751         if (handle == _WAPI_HANDLE_INVALID) {
752                 g_warning ("%s: error opening named event handle", __func__);
753                 SetLastError (ERROR_GEN_FAILURE);
754                 goto cleanup;
755         }
756         ret = handle;
757
758         DEBUG ("%s: returning named event handle %p", __func__, handle);
759
760 cleanup:
761         g_free (utf8_name);
762
763         _wapi_namespace_unlock (NULL);
764         
765         return(ret);
766
767 }