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