2003-02-25 Dick Porter <dick@ximian.com>
[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 guchar *name G_GNUC_UNUSED)
114 {
115         struct _WapiHandle_event *event_handle;
116         gpointer handle;
117         gboolean ok;
118         
119         mono_once (&event_ops_once, event_ops_init);
120
121         handle=_wapi_handle_new (WAPI_HANDLE_EVENT);
122         if(handle==_WAPI_HANDLE_INVALID) {
123                 g_warning (G_GNUC_PRETTY_FUNCTION
124                            ": error creating event handle");
125                 return(NULL);
126         }
127         
128         _wapi_handle_lock_handle (handle);
129         
130         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
131                                 (gpointer *)&event_handle, NULL);
132         if(ok==FALSE) {
133                 g_warning (G_GNUC_PRETTY_FUNCTION
134                            ": error looking up event handle %p", handle);
135                 _wapi_handle_unlock_handle (handle);
136                 return(NULL);
137         }
138         
139         event_handle->manual=manual;
140
141         if(initial==TRUE) {
142                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
143         }
144         
145 #ifdef DEBUG
146         g_message(G_GNUC_PRETTY_FUNCTION ": created new event handle %p",
147                   handle);
148 #endif
149
150         _wapi_handle_unlock_handle (handle);
151         
152         return(handle);
153 }
154
155 /**
156  * PulseEvent:
157  * @handle: The event handle.
158  *
159  * Sets the event handle @handle to the signalled state, and then
160  * resets it to unsignalled after informing any waiting threads.
161  *
162  * If @handle is a manual reset event, all waiting threads that can be
163  * released immediately are released.  @handle is then reset.  If
164  * @handle is an auto reset event, one waiting thread is released even
165  * if multiple threads are waiting.
166  *
167  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
168  * ever returns %TRUE).
169  */
170 gboolean PulseEvent(gpointer handle)
171 {
172         struct _WapiHandle_event *event_handle;
173         gboolean ok;
174         
175         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
176                                 (gpointer *)&event_handle, NULL);
177         if(ok==FALSE) {
178                 g_warning (G_GNUC_PRETTY_FUNCTION
179                            ": error looking up event handle %p", handle);
180                 return(FALSE);
181         }
182         
183         _wapi_handle_lock_handle (handle);
184
185 #ifdef DEBUG
186         g_message(G_GNUC_PRETTY_FUNCTION ": Pulsing event handle %p", handle);
187 #endif
188
189         if(event_handle->manual==TRUE) {
190                 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
191         } else {
192                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
193         }
194
195         _wapi_handle_unlock_handle (handle);
196         
197         if(event_handle->manual==TRUE) {
198                 /* For a manual-reset event, we're about to try and
199                  * get the handle lock again, so give other threads a
200                  * chance
201                  */
202                 sched_yield ();
203
204                 /* Reset the handle signal state */
205                 /* I'm not sure whether or not we need a barrier here
206                  * to make sure that all threads waiting on the event
207                  * have proceeded.  Currently we rely on broadcasting
208                  * a condition.
209                  */
210 #ifdef DEBUG
211                 g_message(G_GNUC_PRETTY_FUNCTION
212                           ": Obtained write lock on event handle %p", handle);
213 #endif
214
215                 _wapi_handle_lock_handle (handle);
216                 _wapi_handle_set_signal_state (handle, FALSE, FALSE);
217                 _wapi_handle_unlock_handle (handle);
218         }
219
220         return(TRUE);
221 }
222
223 /**
224  * ResetEvent:
225  * @handle: The event handle.
226  *
227  * Resets the event handle @handle to the unsignalled state.
228  *
229  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
230  * ever returns %TRUE).
231  */
232 gboolean ResetEvent(gpointer handle)
233 {
234         struct _WapiHandle_event *event_handle;
235         gboolean ok;
236         
237         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
238                                 (gpointer *)&event_handle, NULL);
239         if(ok==FALSE) {
240                 g_warning (G_GNUC_PRETTY_FUNCTION
241                            ": error looking up event handle %p", handle);
242                 return(FALSE);
243         }
244
245 #ifdef DEBUG
246         g_message(G_GNUC_PRETTY_FUNCTION ": Resetting event handle %p",
247                   handle);
248 #endif
249
250         _wapi_handle_lock_handle (handle);
251         if(_wapi_handle_issignalled (handle)==FALSE) {
252
253 #ifdef DEBUG
254                 g_message(G_GNUC_PRETTY_FUNCTION
255                           ": No need to reset event handle %p", handle);
256 #endif
257
258                 _wapi_handle_unlock_handle (handle);
259                 return(TRUE);
260         }
261         
262 #ifdef DEBUG
263         g_message(G_GNUC_PRETTY_FUNCTION
264                   ": Obtained write lock on event handle %p", handle);
265 #endif
266
267         _wapi_handle_set_signal_state (handle, FALSE, FALSE);
268
269         _wapi_handle_unlock_handle (handle);
270         
271         return(TRUE);
272 }
273
274 /**
275  * SetEvent:
276  * @handle: The event handle
277  *
278  * Sets the event handle @handle to the signalled state.
279  *
280  * If @handle is a manual reset event, it remains signalled until it
281  * is reset with ResetEvent().  An auto reset event remains signalled
282  * until a single thread has waited for it, at which time @handle is
283  * automatically reset to unsignalled.
284  *
285  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
286  * ever returns %TRUE).
287  */
288 gboolean SetEvent(gpointer handle)
289 {
290         struct _WapiHandle_event *event_handle;
291         gboolean ok;
292         
293         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_EVENT,
294                                 (gpointer *)&event_handle, NULL);
295         if(ok==FALSE) {
296                 g_warning (G_GNUC_PRETTY_FUNCTION
297                            ": error looking up event handle %p", handle);
298                 return(FALSE);
299         }
300         
301         _wapi_handle_lock_handle (handle);
302
303 #ifdef DEBUG
304         g_message(G_GNUC_PRETTY_FUNCTION ": Setting event handle %p", handle);
305 #endif
306
307         if(event_handle->manual==TRUE) {
308                 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
309         } else {
310                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
311         }
312
313         _wapi_handle_unlock_handle (handle);
314
315         return(TRUE);
316 }
317