2004-11-30 Zoltan Varga <vargaz@freemail.hu>
[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 #undef DEBUG
25
26 static void event_close_shared (gpointer handle);
27 static void event_signal(gpointer handle);
28 static void event_own (gpointer handle);
29
30 struct _WapiHandleOps _wapi_event_ops = {
31         event_close_shared,     /* close_shared */
32         NULL,                   /* close_private */
33         event_signal,           /* signal */
34         event_own,              /* own */
35         NULL,                   /* is_owned */
36 };
37
38 static mono_once_t event_ops_once=MONO_ONCE_INIT;
39
40 static void event_ops_init (void)
41 {
42         _wapi_handle_register_capabilities (WAPI_HANDLE_EVENT,
43                                             WAPI_HANDLE_CAP_WAIT |
44                                             WAPI_HANDLE_CAP_SIGNAL);
45 }
46
47 static void event_close_shared(gpointer handle)
48 {
49         struct _WapiHandle_event *event_handle;
50         gboolean ok;
51         
52         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
53                                 (gpointer *)&event_handle, NULL);
54         if(ok==FALSE) {
55                 g_warning (G_GNUC_PRETTY_FUNCTION
56                            ": error looking up event handle %p", handle);
57                 return;
58         }
59         
60 #ifdef DEBUG
61         g_message(G_GNUC_PRETTY_FUNCTION ": closing event handle %p", handle);
62 #endif
63 }
64
65 static void event_signal(gpointer handle)
66 {
67         ResetEvent(handle);
68 }
69
70 static void event_own (gpointer handle)
71 {
72         struct _WapiHandle_event *event_handle;
73         gboolean ok;
74         
75         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
76                                 (gpointer *)&event_handle, NULL);
77         if(ok==FALSE) {
78                 g_warning (G_GNUC_PRETTY_FUNCTION
79                            ": error looking up event handle %p", handle);
80                 return;
81         }
82         
83 #ifdef DEBUG
84         g_message(G_GNUC_PRETTY_FUNCTION ": owning event handle %p", handle);
85 #endif
86
87         if(event_handle->manual==FALSE) {
88                 g_assert (event_handle->set_count > 0);
89                 
90                 if (--event_handle->set_count == 0) {
91                         _wapi_handle_set_signal_state (handle, FALSE, FALSE);
92                 }
93         }
94 }
95
96 /**
97  * CreateEvent:
98  * @security: Ignored for now.
99  * @manual: Specifies whether the new event handle has manual or auto
100  * reset behaviour.
101  * @initial: Specifies whether the new event handle is initially
102  * signalled or not.
103  * @name:Pointer to a string specifying the name of this name, or
104  * %NULL.  Currently ignored.
105  *
106  * Creates a new event handle.
107  *
108  * An event handle is signalled with SetEvent().  If the new handle is
109  * a manual reset event handle, it remains signalled until it is reset
110  * with ResetEvent().  An auto reset event remains signalled until a
111  * single thread has waited for it, at which time the event handle is
112  * automatically reset to unsignalled.
113  *
114  * Return value: A new handle, or %NULL on error.
115  */
116 gpointer CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean manual,
117                      gboolean initial, const gunichar2 *name G_GNUC_UNUSED)
118 {
119         struct _WapiHandle_event *event_handle;
120         gpointer handle;
121         gboolean ok;
122         gpointer ret = NULL;
123         int thr_ret;
124         
125         mono_once (&event_ops_once, event_ops_init);
126
127         handle=_wapi_handle_new (WAPI_HANDLE_EVENT);
128         if(handle==_WAPI_HANDLE_INVALID) {
129                 g_warning (G_GNUC_PRETTY_FUNCTION
130                            ": error creating event handle");
131                 return(NULL);
132         }
133         
134         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
135                               handle);
136         thr_ret = _wapi_handle_lock_handle (handle);
137         g_assert (thr_ret == 0);
138         
139         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
140                                 (gpointer *)&event_handle, NULL);
141         if(ok==FALSE) {
142                 g_warning (G_GNUC_PRETTY_FUNCTION
143                            ": error looking up event handle %p", handle);
144                 goto cleanup;
145         }
146         ret = handle;
147         
148         event_handle->manual=manual;
149         event_handle->set_count = 0;
150
151         if(initial==TRUE) {
152                 if (manual == FALSE) {
153                         event_handle->set_count = 1;
154                 }
155                 
156                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
157         }
158         
159 #ifdef DEBUG
160         g_message(G_GNUC_PRETTY_FUNCTION ": created new event handle %p",
161                   handle);
162 #endif
163
164 cleanup:
165         thr_ret = _wapi_handle_unlock_handle (handle);
166         g_assert (thr_ret == 0);
167         
168         pthread_cleanup_pop (0);
169
170         return(ret);
171 }
172
173 /**
174  * PulseEvent:
175  * @handle: The event handle.
176  *
177  * Sets the event handle @handle to the signalled state, and then
178  * resets it to unsignalled after informing any waiting threads.
179  *
180  * If @handle is a manual reset event, all waiting threads that can be
181  * released immediately are released.  @handle is then reset.  If
182  * @handle is an auto reset event, one waiting thread is released even
183  * if multiple threads are waiting.
184  *
185  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
186  * ever returns %TRUE).
187  */
188 gboolean PulseEvent(gpointer handle)
189 {
190         struct _WapiHandle_event *event_handle;
191         gboolean ok;
192         int thr_ret;
193         
194         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
195                                 (gpointer *)&event_handle, NULL);
196         if(ok==FALSE) {
197                 g_warning (G_GNUC_PRETTY_FUNCTION
198                            ": error looking up event handle %p", handle);
199                 return(FALSE);
200         }
201         
202         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
203                               handle);
204         thr_ret = _wapi_handle_lock_handle (handle);
205         g_assert (thr_ret == 0);
206
207 #ifdef DEBUG
208         g_message(G_GNUC_PRETTY_FUNCTION ": Pulsing event handle %p", handle);
209 #endif
210
211         if(event_handle->manual==TRUE) {
212                 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
213         } else {
214                 event_handle->set_count++;
215                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
216         }
217
218         thr_ret = _wapi_handle_unlock_handle (handle);
219         g_assert (thr_ret == 0);
220         
221         pthread_cleanup_pop (0);
222         
223         if(event_handle->manual==TRUE) {
224                 /* For a manual-reset event, we're about to try and
225                  * get the handle lock again, so give other threads a
226                  * chance
227                  */
228                 sched_yield ();
229
230                 /* Reset the handle signal state */
231                 /* I'm not sure whether or not we need a barrier here
232                  * to make sure that all threads waiting on the event
233                  * have proceeded.  Currently we rely on broadcasting
234                  * a condition.
235                  */
236 #ifdef DEBUG
237                 g_message(G_GNUC_PRETTY_FUNCTION
238                           ": Obtained write lock on event handle %p", handle);
239 #endif
240
241                 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle, handle);
242                 thr_ret = _wapi_handle_lock_handle (handle);
243                 g_assert (thr_ret == 0);
244                 
245                 _wapi_handle_set_signal_state (handle, FALSE, FALSE);
246
247                 thr_ret = _wapi_handle_unlock_handle (handle);
248                 g_assert (thr_ret == 0);
249                 pthread_cleanup_pop (0);
250         }
251
252         return(TRUE);
253 }
254
255 /**
256  * ResetEvent:
257  * @handle: The event handle.
258  *
259  * Resets the event handle @handle to the unsignalled state.
260  *
261  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
262  * ever returns %TRUE).
263  */
264 gboolean ResetEvent(gpointer handle)
265 {
266         struct _WapiHandle_event *event_handle;
267         gboolean ok;
268         int thr_ret;
269         
270         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
271                                 (gpointer *)&event_handle, NULL);
272         if(ok==FALSE) {
273                 g_warning (G_GNUC_PRETTY_FUNCTION
274                            ": error looking up event handle %p", handle);
275                 return(FALSE);
276         }
277
278 #ifdef DEBUG
279         g_message(G_GNUC_PRETTY_FUNCTION ": Resetting event handle %p",
280                   handle);
281 #endif
282
283         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
284                               handle);
285         thr_ret = _wapi_handle_lock_handle (handle);
286         g_assert (thr_ret == 0);
287         
288         if(_wapi_handle_issignalled (handle)==FALSE) {
289 #ifdef DEBUG
290                 g_message(G_GNUC_PRETTY_FUNCTION
291                           ": No need to reset event handle %p", handle);
292 #endif
293         } else {
294 #ifdef DEBUG
295                 g_message(G_GNUC_PRETTY_FUNCTION
296                           ": Obtained write lock on event handle %p", handle);
297 #endif
298
299                 _wapi_handle_set_signal_state (handle, FALSE, FALSE);
300         }
301         
302         event_handle->set_count = 0;
303         
304         thr_ret = _wapi_handle_unlock_handle (handle);
305         g_assert (thr_ret == 0);
306         
307         pthread_cleanup_pop (0);
308         
309         return(TRUE);
310 }
311
312 /**
313  * SetEvent:
314  * @handle: The event handle
315  *
316  * Sets the event handle @handle to the signalled state.
317  *
318  * If @handle is a manual reset event, it remains signalled until it
319  * is reset with ResetEvent().  An auto reset event remains signalled
320  * until a single thread has waited for it, at which time @handle is
321  * automatically reset to unsignalled.
322  *
323  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
324  * ever returns %TRUE).
325  */
326 gboolean SetEvent(gpointer handle)
327 {
328         struct _WapiHandle_event *event_handle;
329         gboolean ok;
330         int thr_ret;
331         
332         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
333                                 (gpointer *)&event_handle, NULL);
334         if(ok==FALSE) {
335                 g_warning (G_GNUC_PRETTY_FUNCTION
336                            ": error looking up event handle %p", handle);
337                 return(FALSE);
338         }
339         
340         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
341                               handle);
342         thr_ret = _wapi_handle_lock_handle (handle);
343         g_assert (thr_ret == 0);
344
345 #ifdef DEBUG
346         g_message(G_GNUC_PRETTY_FUNCTION ": Setting event handle %p", handle);
347 #endif
348
349         if(event_handle->manual==TRUE) {
350                 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
351         } else {
352                 event_handle->set_count++;
353                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
354         }
355
356         thr_ret = _wapi_handle_unlock_handle (handle);
357         g_assert (thr_ret == 0);
358         
359         pthread_cleanup_pop (0);
360
361         return(TRUE);
362 }
363