2002-04-04 Dan Lewis <dihlewis@yahoo.co.uk>
[mono.git] / mono / io-layer / events.c
1 #include <config.h>
2 #include <glib.h>
3 #include <pthread.h>
4 #include <string.h>
5
6 #include "mono/io-layer/wapi.h"
7 #include "wapi-private.h"
8 #include "wait-private.h"
9 #include "handles-private.h"
10 #include "misc-private.h"
11
12 #include "mono-mutex.h"
13
14 #undef DEBUG
15
16 /* event_wait() uses the event-private condition to signal that the
17  * event has been set
18  *
19  * Hold mutex before setting the event, and before the final test
20  * Hold rwlock for reading while testing the event
21  * Hold rwlock for writing before resetting the event
22  */
23 struct _WapiHandle_event
24 {
25         WapiHandle handle;
26         mono_mutex_t mutex;
27         pthread_cond_t cond;
28         pthread_rwlock_t rwlock;
29         gboolean manual;
30 };
31
32 /* event_wait_multiple() uses the global condition to signal that an
33  * event has been set
34  */
35 static mono_mutex_t event_signal_mutex = MONO_MUTEX_INITIALIZER;
36 static pthread_cond_t event_signal_cond = PTHREAD_COND_INITIALIZER;
37
38 static void event_close(WapiHandle *handle);
39 static gboolean event_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms);
40 static guint32 event_wait_multiple(gpointer data);
41 static void event_signal(WapiHandle *handle);
42
43 static struct _WapiHandleOps event_ops = {
44         event_close,            /* close */
45         NULL,                   /* getfiletype */
46         NULL,                   /* readfile */
47         NULL,                   /* writefile */
48         NULL,                   /* flushfile */
49         NULL,                   /* seek */
50         NULL,                   /* setendoffile */
51         NULL,                   /* getfilesize */
52         NULL,                   /* getfiletime */
53         NULL,                   /* setfiletime */
54         event_wait,             /* wait */
55         event_wait_multiple,    /* wait_multiple */
56         event_signal,           /* signal */
57 };
58
59 static void event_close(WapiHandle *handle)
60 {
61         struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
62         
63 #ifdef DEBUG
64         g_message(G_GNUC_PRETTY_FUNCTION ": closing event handle %p",
65                   event_handle);
66 #endif
67
68         mono_mutex_destroy(&event_handle->mutex);
69         pthread_cond_destroy(&event_handle->cond);
70         pthread_rwlock_destroy(&event_handle->rwlock);
71 }
72
73 static gboolean event_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms)
74 {
75         struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
76         struct timespec timeout;
77         int ret;
78
79 #ifdef DEBUG
80         g_message(G_GNUC_PRETTY_FUNCTION
81                   ": waiting on event handle %p for %d ms", handle, ms);
82 #endif
83
84         mono_mutex_lock(&event_handle->mutex);
85
86         /* Signal this handle after we have obtained the event lock */
87         if(signal!=NULL) {
88                 signal->ops->signal(signal);
89         }
90         
91         /* First check if the handle is already signalled */
92         if(handle->signalled==TRUE) {
93                 /* If this is an auto-reset event, reset the state to
94                  * unsignalled
95                  */
96         
97 #ifdef DEBUG
98                 g_message(G_GNUC_PRETTY_FUNCTION
99                           ": event handle %p already signalled", handle);
100 #endif
101
102                 if(event_handle->manual==FALSE) {
103 #ifdef DEBUG
104                         g_message(G_GNUC_PRETTY_FUNCTION
105                                   ": resetting auto event handle %p", handle);
106 #endif
107                         handle->signalled=FALSE;
108                 }
109                 mono_mutex_unlock(&event_handle->mutex);
110                 
111                 return(TRUE);
112         }
113
114         /* We'll have to wait for it then */
115         if(ms!=INFINITE) {
116                 _wapi_calc_timeout(&timeout, ms);
117         }
118         
119 again:
120         /* Acquire a read lock so that the signal status can't be
121          * reset without us noticing. (PulseEvent and ResetEvent will
122          * gain a write lock before changing the status to
123          * unsignalled, which will block while one or more threads
124          * hold a read lock.)
125          */
126         pthread_rwlock_rdlock(&event_handle->rwlock);
127                 
128 #ifdef DEBUG
129         g_message(G_GNUC_PRETTY_FUNCTION
130                   ": waiting for event handle %p to be signalled", handle);
131 #endif
132
133         if(ms==INFINITE) {
134                 ret=mono_cond_wait(&event_handle->cond,
135                                    &event_handle->mutex);
136         } else {
137                 ret=mono_cond_timedwait(&event_handle->cond,
138                                         &event_handle->mutex, &timeout);
139         }
140
141         if(ret==0) {
142                 /* Condition was signalled, so hopefully event is
143                  * signalled now.  (It might not be if its an
144                  * auto-reset event and someone else got in before
145                  * us.)
146                  */
147         
148 #ifdef DEBUG
149                 g_message(G_GNUC_PRETTY_FUNCTION ": event handle %p signalled",
150                           handle);
151 #endif
152                 if(handle->signalled==TRUE) {
153         
154 #ifdef DEBUG
155                         g_message(G_GNUC_PRETTY_FUNCTION
156                                   ": event handle %p still signalled", handle);
157 #endif
158                         /* If this is an auto-reset event, reset the
159                          * state to unsignalled
160                          */
161                         if(event_handle->manual==FALSE) {
162 #ifdef DEBUG
163                                 g_message(G_GNUC_PRETTY_FUNCTION
164                                           ": resetting auto event handle %p",
165                                           handle);
166 #endif
167                                 handle->signalled=FALSE;
168                         }
169                         pthread_rwlock_unlock(&event_handle->rwlock);
170                         mono_mutex_unlock(&event_handle->mutex);
171                         
172                         return(TRUE);
173                 }
174         
175 #ifdef DEBUG
176                 g_message(G_GNUC_PRETTY_FUNCTION
177                           ": event handle %p no longer signalled", handle);
178 #endif
179
180                 /* Better luck next time */
181                 
182                 /* Drop the rwlock briefly so that another thread has
183                  * a chance to reset the event
184                  */
185                 pthread_rwlock_unlock(&event_handle->rwlock);
186                 goto again;
187         }
188
189         /* Timeout or other error */
190         
191 #ifdef DEBUG
192         g_message(G_GNUC_PRETTY_FUNCTION
193                   ": wait on event handle %p error: %s", handle,
194                   strerror(ret));
195 #endif
196         
197         pthread_rwlock_unlock(&event_handle->rwlock);
198         mono_mutex_unlock(&event_handle->mutex);
199         
200         return(FALSE);
201 }
202
203 static gboolean event_count_signalled(WaitQueueItem *item, guint32 numhandles,
204                                       gboolean waitall, guint32 *retcount)
205 {
206         guint32 count, i;
207         gboolean ret;
208         
209         /* Lock all the handles, with backoff */
210 again:
211         for(i=0; i<numhandles; i++) {
212                 struct _WapiHandle_event *event_handle;
213                 
214                 event_handle=g_ptr_array_index(
215                         item->handles[WAPI_HANDLE_EVENT], i);
216                 
217                 ret=mono_mutex_trylock(&event_handle->mutex);
218                 if(ret!=0) {
219                         /* Bummer */
220                         while(i--) {
221                                 event_handle=g_ptr_array_index(
222                                         item->handles[WAPI_HANDLE_EVENT], i);
223                                 mono_mutex_unlock(&event_handle->mutex);
224                         }
225
226                         /* It's not possible for two threads calling
227                          * WaitForMultipleObjects to both be calling
228                          * this function simultaneously, because the
229                          * global event_signal_mutex is held.
230                          * Therefore any collision is with a single
231                          * lock from one of the functions that deal
232                          * with single event handles.  It's just about
233                          * theoretically possible for the other
234                          * threads to keep locking an event in a tight
235                          * loop but eventually we will get the lock.
236                          */
237                         sched_yield();
238                         
239                         goto again;
240                 }
241         }
242         
243 #ifdef DEBUG
244         g_message(G_GNUC_PRETTY_FUNCTION ": Locked all event handles");
245 #endif
246         
247         count=_wapi_handle_count_signalled(item, WAPI_HANDLE_EVENT);
248         
249 #ifdef DEBUG
250         g_message(G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
251                   count);
252 #endif
253         
254         if((waitall==TRUE && count==numhandles) ||
255            (waitall==FALSE && count>0)) {
256                 /* done */
257                 ret=TRUE;
258         } else {
259                 ret=FALSE;
260         }
261         
262         for(i=0; i<numhandles; i++) {
263                 struct _WapiHandle_event *event_handle;
264                 
265                 event_handle=g_ptr_array_index(
266                         item->handles[WAPI_HANDLE_EVENT], i);
267                 
268                 mono_mutex_unlock(&event_handle->mutex);
269         }
270         
271 #ifdef DEBUG
272         g_message(G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
273 #endif
274
275         *retcount=count;
276         return(ret);
277 }
278
279 static guint32 event_wait_multiple(gpointer data)
280 {
281         WaitQueueItem *item=(WaitQueueItem *)data;
282         struct timespec timeout;
283         guint32 iterations;
284         guint32 numhandles, count, i;
285         gboolean done;
286         int ret;
287
288         numhandles=item->handles[WAPI_HANDLE_EVENT]->len;
289         
290 #ifdef DEBUG
291         g_message(G_GNUC_PRETTY_FUNCTION
292                   ": waiting on %d event handles for %d ms", numhandles,
293                   item->timeout);
294 #endif
295
296         /* First, check if any of the handles are already
297          * signalled. If waitall is specified we only return if all
298          * handles have been signalled.
299          */
300         done=event_count_signalled(item, numhandles, item->waitall, &count);
301         
302 #ifdef DEBUG
303         g_message(G_GNUC_PRETTY_FUNCTION
304                   ": Preliminary check found %d handles signalled", count);
305 #endif
306
307         if(done==TRUE) {
308                 item->waited[WAPI_HANDLE_EVENT]=TRUE;
309                 item->waitcount[WAPI_HANDLE_EVENT]=count;
310                 
311                 return(count);
312         }
313         
314         /* We'll have to wait then */
315         
316         mono_mutex_lock(&event_signal_mutex);
317         
318         iterations=0;
319         do {
320                 iterations++;
321                 
322 #ifdef DEBUG
323                 g_message(G_GNUC_PRETTY_FUNCTION ": Wait iteration %d",
324                           iterations);
325 #endif
326
327                 /* If the timeout isnt INFINITE but greater than 1s,
328                  * split the timeout into 1s chunks.
329                  *
330                  * This is so that ResetEvent() wont block forever if
331                  * another thread is waiting on multiple events, with
332                  * some already signalled, and ResetEvent() wants to
333                  * reset one of the signalled ones.  (1s is a bit of a
334                  * long wait too, this might need to be tuned.)
335                  */
336
337                 if((item->timeout!=INFINITE) &&
338                    (item->timeout < (iterations*1000))) {
339                         _wapi_calc_timeout(
340                                 &timeout, item->timeout-((iterations-1)*1000));
341                 } else {
342                         _wapi_calc_timeout(&timeout, 1000);
343                 }
344                 
345                 /* Acquire a read lock on all handles so that the
346                  * signal status can't be reset without us
347                  * noticing. (PulseEvent and ResetEvent will gain a
348                  * write lock before changing the status to
349                  * unsignalled, which will block while one or more
350                  * threads hold a read lock.)
351                  */
352                 for(i=0; i<numhandles; i++) {
353                         struct _WapiHandle_event *event_handle;
354                         
355                         event_handle=g_ptr_array_index(
356                                 item->handles[WAPI_HANDLE_EVENT], i);
357                         
358                         pthread_rwlock_rdlock(&event_handle->rwlock);
359                 }
360                 
361                 ret=mono_cond_timedwait(&event_signal_cond,
362                                            &event_signal_mutex, &timeout);
363
364                 if(ret==0) {
365                         /* Condition was signalled, so hopefully an
366                          * event is signalled now.  (It might not be
367                          * if it was an auto-reset event and someone
368                          * else got in before us.)
369                          */
370                         done=event_count_signalled(item, numhandles,
371                                                    item->waitall, &count);
372         
373 #ifdef DEBUG
374                         g_message(G_GNUC_PRETTY_FUNCTION
375                                   ": signal check found %d handles signalled",
376                                   count);
377 #endif
378                         
379                         if(done==TRUE) {
380 #ifdef DEBUG
381                                 g_message(G_GNUC_PRETTY_FUNCTION
382                                           ": Returning wait success");
383 #endif
384
385                                 for(i=0; i<numhandles; i++) {
386                                         struct _WapiHandle_event *event_handle;
387                                 
388                                         event_handle=g_ptr_array_index(item->handles[WAPI_HANDLE_EVENT], i);
389                         
390                                         pthread_rwlock_unlock(&event_handle->rwlock);
391                                 }
392
393                                 item->waited[WAPI_HANDLE_EVENT]=TRUE;
394                                 item->waitcount[WAPI_HANDLE_EVENT]=count;
395                                 
396                                 return(count);
397                         }
398                 } else {
399 #ifdef DEBUG
400                         g_message(G_GNUC_PRETTY_FUNCTION ": Wait error %s",
401                                   strerror(ret));
402 #endif
403                 }
404
405 #ifdef DEBUG
406                 g_message(G_GNUC_PRETTY_FUNCTION
407                           ": Still waiting for more event handles");
408 #endif
409                 /* Drop the rwlocks briefly so that another thread has
410                  * a chance to reset any of the events
411                  */
412                 for(i=0; i<numhandles; i++) {
413                         struct _WapiHandle_event *event_handle;
414                                 
415                         event_handle=g_ptr_array_index(
416                                 item->handles[WAPI_HANDLE_EVENT], i);
417                         
418                         pthread_rwlock_unlock(&event_handle->rwlock);
419                 }
420         } while((item->timeout==INFINITE) ||
421                 (item->timeout > (iterations * 1000)));
422
423         /* Timeout or other error */
424         
425         for(i=0; i<numhandles; i++) {
426                 struct _WapiHandle_event *event_handle;
427                 
428                 event_handle=g_ptr_array_index(
429                         item->handles[WAPI_HANDLE_EVENT], i);
430                 
431                 pthread_rwlock_unlock(&event_handle->rwlock);
432         }
433
434         mono_mutex_unlock(&event_signal_mutex);
435
436 #ifdef DEBUG
437         g_message(G_GNUC_PRETTY_FUNCTION ": Returning wait failed");
438 #endif
439         
440         item->waited[WAPI_HANDLE_EVENT]=TRUE;
441         item->waitcount[WAPI_HANDLE_MUTEX]=0;
442         
443         return(0);
444 }
445
446 static void event_signal(WapiHandle *handle)
447 {
448         ResetEvent(handle);
449 }
450
451 /**
452  * CreateEvent:
453  * @security: Ignored for now.
454  * @manual: Specifies whether the new event handle has manual or auto
455  * reset behaviour.
456  * @initial: Specifies whether the new event handle is initially
457  * signalled or not.
458  * @name:Pointer to a string specifying the name of this name, or
459  * %NULL.  Currently ignored.
460  *
461  * Creates a new event handle.
462  *
463  * An event handle is signalled with SetEvent().  If the new handle is
464  * a manual reset event handle, it remains signalled until it is reset
465  * with ResetEvent().  An auto reset event remains signalled until a
466  * single thread has waited for it, at which time the event handle is
467  * automatically reset to unsignalled.
468  *
469  * Return value: A new handle, or %NULL on error.
470  */
471 WapiHandle *CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean manual,
472                         gboolean initial, const guchar *name G_GNUC_UNUSED)
473 {
474         struct _WapiHandle_event *event_handle;
475         WapiHandle *handle;
476         
477         event_handle=(struct _WapiHandle_event *)g_new0(struct _WapiHandle_event, 1);
478         handle=(WapiHandle *)event_handle;
479         _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_EVENT, event_ops);
480         
481         mono_mutex_init(&event_handle->mutex, NULL);
482         pthread_cond_init(&event_handle->cond, NULL);
483         pthread_rwlock_init(&event_handle->rwlock, NULL);
484         event_handle->manual=manual;
485
486         if(initial==TRUE) {
487                 handle->signalled=TRUE;
488         }
489         
490 #ifdef DEBUG
491         g_message(G_GNUC_PRETTY_FUNCTION ": created new event handle %p",
492                   handle);
493 #endif
494
495         return(handle);
496 }
497
498 /**
499  * PulseEvent:
500  * @handle: The event handle.
501  *
502  * Sets the event handle @handle to the signalled state, and then
503  * resets it to unsignalled after informing any waiting threads.
504  *
505  * If @handle is a manual reset event, all waiting threads that can be
506  * released immediately are released.  @handle is then reset.  If
507  * @handle is an auto reset event, one waiting thread is released even
508  * if multiple threads are waiting.
509  *
510  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
511  * ever returns %TRUE).
512  */
513 gboolean PulseEvent(WapiHandle *handle)
514 {
515         struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
516         
517         mono_mutex_lock(&event_handle->mutex);
518
519 #ifdef DEBUG
520         g_message(G_GNUC_PRETTY_FUNCTION ": Pulsing event handle %p", handle);
521 #endif
522
523         handle->signalled=TRUE;
524         
525         /* Tell everyone blocking on WaitForSingleObject */
526         if(event_handle->manual==TRUE) {
527                 pthread_cond_broadcast(&event_handle->cond);
528         } else {
529                 pthread_cond_signal(&event_handle->cond);
530         }
531         mono_mutex_unlock(&event_handle->mutex);
532
533 #ifdef DEBUG
534         g_message(G_GNUC_PRETTY_FUNCTION
535                   ": Informed single waits for event handle %p", handle);
536 #endif
537         
538         /* Tell everyone blocking on WaitForMultipleObjects */
539         mono_mutex_lock(&event_signal_mutex);
540         pthread_cond_broadcast(&event_signal_cond);
541         mono_mutex_unlock(&event_signal_mutex);
542
543 #ifdef DEBUG
544         g_message(G_GNUC_PRETTY_FUNCTION
545                   ": Informed multiple waits for event handles");
546 #endif
547         
548         /* Reset the handle signal state */
549
550         /* This rwlock blocks until no other thread holds a read lock.
551          * This ensures that we can't reset the event until every
552          * waiting thread has had a chance to examine it
553          */
554         pthread_rwlock_wrlock(&event_handle->rwlock);
555
556 #ifdef DEBUG
557         g_message(G_GNUC_PRETTY_FUNCTION
558                   ": Obtained write lock on event handle %p", handle);
559 #endif
560
561         handle->signalled=FALSE;
562         pthread_rwlock_unlock(&event_handle->rwlock);
563
564         return(TRUE);
565 }
566
567 /**
568  * ResetEvent:
569  * @handle: The event handle.
570  *
571  * Resets the event handle @handle to the unsignalled state.
572  *
573  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
574  * ever returns %TRUE).
575  */
576 gboolean ResetEvent(WapiHandle *handle)
577 {
578         struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
579
580 #ifdef DEBUG
581         g_message(G_GNUC_PRETTY_FUNCTION ": Resetting event handle %p",
582                   handle);
583 #endif
584
585         /* Test for the current state, because another thread might be
586          * waiting forever on an unsignalled event with the read lock
587          * held.  Theres no point going for the write lock if we dont
588          * need it.
589          */
590         mono_mutex_lock(&event_handle->mutex);
591         if(handle->signalled==FALSE) {
592
593 #ifdef DEBUG
594                 g_message(G_GNUC_PRETTY_FUNCTION
595                           ": No need to reset event handle %p", handle);
596 #endif
597
598                 mono_mutex_unlock(&event_handle->mutex);
599                 return(TRUE);
600         }
601         mono_mutex_unlock(&event_handle->mutex);
602         
603         pthread_rwlock_wrlock(&event_handle->rwlock);
604
605 #ifdef DEBUG
606         g_message(G_GNUC_PRETTY_FUNCTION
607                   ": Obtained write lock on event handle %p", handle);
608 #endif
609
610         handle->signalled=FALSE;
611         pthread_rwlock_unlock(&event_handle->rwlock);
612         
613         return(TRUE);
614 }
615
616 /**
617  * SetEvent:
618  * @handle: The event handle
619  *
620  * Sets the event handle @handle to the signalled state.
621  *
622  * If @handle is a manual reset event, it remains signalled until it
623  * is reset with ResetEvent().  An auto reset event remains signalled
624  * until a single thread has waited for it, at which time @handle is
625  * automatically reset to unsignalled.
626  *
627  * Return value: %TRUE on success, %FALSE otherwise.  (Currently only
628  * ever returns %TRUE).
629  */
630 gboolean SetEvent(WapiHandle *handle)
631 {
632         struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
633         
634         mono_mutex_lock(&event_handle->mutex);
635
636 #ifdef DEBUG
637         g_message(G_GNUC_PRETTY_FUNCTION ": Setting event handle %p", handle);
638 #endif
639
640         handle->signalled=TRUE;
641         
642         /* Tell everyone blocking on WaitForSingleObject */
643         if(event_handle->manual==TRUE) {
644                 pthread_cond_broadcast(&event_handle->cond);
645         } else {
646                 pthread_cond_signal(&event_handle->cond);
647         }
648         mono_mutex_unlock(&event_handle->mutex);
649
650 #ifdef DEBUG
651         g_message(G_GNUC_PRETTY_FUNCTION
652                   ": Informed single waits for event handle %p", handle);
653 #endif
654         
655         /* Tell everyone blocking on WaitForMultipleObjects */
656         mono_mutex_lock(&event_signal_mutex);
657         pthread_cond_broadcast(&event_signal_cond);
658         mono_mutex_unlock(&event_signal_mutex);
659
660 #ifdef DEBUG
661         g_message(G_GNUC_PRETTY_FUNCTION
662                   ": Informed multiple waits for event handles");
663 #endif
664         
665         return(TRUE);
666 }
667