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