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"
14 /* event_wait() uses the event-private condition to signal that the
17 * Hold mutex before setting the event, and before the final test
18 * Hold rwlock for reading while testing the event
19 * Hold rwlock for writing before resetting the event
21 struct _WapiHandle_event
24 pthread_mutex_t mutex;
26 pthread_rwlock_t rwlock;
30 /* event_wait_multiple() uses the global condition to signal that an
33 static pthread_mutex_t event_signal_mutex = PTHREAD_MUTEX_INITIALIZER;
34 static pthread_cond_t event_signal_cond = PTHREAD_COND_INITIALIZER;
36 static void event_close(WapiHandle *handle);
37 static gboolean event_wait(WapiHandle *handle, guint32 ms);
38 static guint32 event_wait_multiple(gpointer data);
40 static struct _WapiHandleOps event_ops = {
41 event_close, /* close */
42 NULL, /* getfiletype */
46 NULL, /* setendoffile */
47 NULL, /* getfilesize */
48 event_wait, /* wait */
49 event_wait_multiple, /* wait_multiple */
52 static void event_close(WapiHandle *handle)
54 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
57 g_message(G_GNUC_PRETTY_FUNCTION ": closing event handle %p",
62 static gboolean event_wait(WapiHandle *handle, guint32 ms)
64 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
65 struct timespec timeout;
69 g_message(G_GNUC_PRETTY_FUNCTION
70 ": waiting on event handle %p for %d ms", handle, ms);
73 pthread_mutex_lock(&event_handle->mutex);
75 /* First check if the handle is already signalled */
76 if(handle->signalled==TRUE) {
77 /* If this is an auto-reset event, reset the state to
82 g_message(G_GNUC_PRETTY_FUNCTION
83 ": event handle %p already signalled", handle);
86 if(event_handle->manual==FALSE) {
88 g_message(G_GNUC_PRETTY_FUNCTION
89 ": resetting auto event handle %p", handle);
91 handle->signalled=FALSE;
93 pthread_mutex_unlock(&event_handle->mutex);
98 /* We'll have to wait for it then */
100 _wapi_calc_timeout(&timeout, ms);
104 /* Acquire a read lock so that the signal status can't be
105 * reset without us noticing. (PulseEvent and ResetEvent will
106 * gain a write lock before changing the status to
107 * unsignalled, which will block while one or more threads
110 pthread_rwlock_rdlock(&event_handle->rwlock);
113 g_message(G_GNUC_PRETTY_FUNCTION
114 ": waiting for event handle %p to be signalled", handle);
118 ret=pthread_cond_wait(&event_handle->cond,
119 &event_handle->mutex);
121 ret=pthread_cond_timedwait(&event_handle->cond,
122 &event_handle->mutex, &timeout);
126 /* Condition was signalled, so hopefully event is
127 * signalled now. (It might not be if its an
128 * auto-reset event and someone else got in before
133 g_message(G_GNUC_PRETTY_FUNCTION ": event handle %p signalled",
136 if(handle->signalled==TRUE) {
139 g_message(G_GNUC_PRETTY_FUNCTION
140 ": event handle %p still signalled", handle);
142 /* If this is an auto-reset event, reset the
143 * state to unsignalled
145 if(event_handle->manual==FALSE) {
147 g_message(G_GNUC_PRETTY_FUNCTION
148 ": resetting auto event handle %p",
151 handle->signalled=FALSE;
153 pthread_rwlock_unlock(&event_handle->rwlock);
154 pthread_mutex_unlock(&event_handle->mutex);
160 g_message(G_GNUC_PRETTY_FUNCTION
161 ": event handle %p no longer signalled", handle);
164 /* Better luck next time */
166 /* Drop the rwlock briefly so that another thread has
167 * a chance to reset the event
169 pthread_rwlock_unlock(&event_handle->rwlock);
173 /* Timeout or other error */
176 g_message(G_GNUC_PRETTY_FUNCTION
177 ": wait on event handle %p error: %s", handle,
181 pthread_rwlock_unlock(&event_handle->rwlock);
182 pthread_mutex_unlock(&event_handle->mutex);
187 static gboolean event_count_signalled(GPtrArray *handles, guint32 numhandles,
188 gboolean waitall, guint32 *retcount)
193 /* Lock all the handles, with backoff */
195 for(i=0; i<numhandles; i++) {
196 struct _WapiHandle_event *event_handle;
198 event_handle=g_ptr_array_index(handles, i);
200 ret=pthread_mutex_trylock(&event_handle->mutex);
204 event_handle=g_ptr_array_index(handles, i);
205 pthread_mutex_unlock(&event_handle->mutex);
208 /* It's not possible for two threads calling
209 * WaitForMultipleObjects to both be calling
210 * this function simultaneously, because the
211 * global event_signal_mutex is held.
212 * Therefore any collision is with a single
213 * lock from one of the functions that deal
214 * with single event handles. It's just about
215 * theoretically possible for the other
216 * threads to keep locking an event in a tight
217 * loop but eventually we will get the lock.
226 g_message(G_GNUC_PRETTY_FUNCTION ": Locked all event handles");
229 count=_wapi_handle_count_signalled(handles);
232 g_message(G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
236 if((waitall==TRUE && count==numhandles) ||
237 (waitall==FALSE && count>0)) {
244 for(i=0; i<numhandles; i++) {
245 struct _WapiHandle_event *event_handle;
247 event_handle=g_ptr_array_index(handles, i);
249 pthread_mutex_unlock(&event_handle->mutex);
253 g_message(G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
260 static guint32 event_wait_multiple(gpointer data)
262 WaitQueueItem *item=(WaitQueueItem *)data;
263 struct timespec timeout;
265 guint32 numhandles, count, i;
269 numhandles=item->handles[WAPI_HANDLE_EVENT]->len;
272 g_message(G_GNUC_PRETTY_FUNCTION
273 ": waiting on %d event handles for %d ms", numhandles,
277 /* First, check if any of the handles are already
278 * signalled. If waitall is specified we only return if all
279 * handles have been signalled.
281 done=event_count_signalled(item->handles[WAPI_HANDLE_EVENT],
282 numhandles, item->waitall, &count);
285 g_message(G_GNUC_PRETTY_FUNCTION
286 ": Preliminary check found %d handles signalled", count);
290 item->waited[WAPI_HANDLE_EVENT]=TRUE;
291 item->waitcount[WAPI_HANDLE_EVENT]=count;
296 /* We'll have to wait then */
298 pthread_mutex_lock(&event_signal_mutex);
305 g_message(G_GNUC_PRETTY_FUNCTION ": Wait iteration %d",
309 /* If the timeout isnt INFINITE but greater than 1s,
310 * split the timeout into 1s chunks.
312 * This is so that ResetEvent() wont block forever if
313 * another thread is waiting on multiple events, with
314 * some already signalled, and ResetEvent() wants to
315 * reset one of the signalled ones. (1s is a bit of a
316 * long wait too, this might need to be tuned.)
319 if((item->timeout!=INFINITE) &&
320 (item->timeout < (iterations*1000))) {
322 &timeout, item->timeout-((iterations-1)*1000));
324 _wapi_calc_timeout(&timeout, 1000);
327 /* Acquire a read lock on all handles so that the
328 * signal status can't be reset without us
329 * noticing. (PulseEvent and ResetEvent will gain a
330 * write lock before changing the status to
331 * unsignalled, which will block while one or more
332 * threads hold a read lock.)
334 for(i=0; i<numhandles; i++) {
335 struct _WapiHandle_event *event_handle;
337 event_handle=g_ptr_array_index(
338 item->handles[WAPI_HANDLE_EVENT], i);
340 pthread_rwlock_rdlock(&event_handle->rwlock);
343 ret=pthread_cond_timedwait(&event_signal_cond,
344 &event_signal_mutex, &timeout);
347 /* Condition was signalled, so hopefully an
348 * event is signalled now. (It might not be
349 * if it was an auto-reset event and someone
350 * else got in before us.)
352 done=event_count_signalled(
353 item->handles[WAPI_HANDLE_EVENT], numhandles,
354 item->waitall, &count);
357 g_message(G_GNUC_PRETTY_FUNCTION
358 ": signal check found %d handles signalled",
364 g_message(G_GNUC_PRETTY_FUNCTION
365 ": Returning wait success");
368 for(i=0; i<numhandles; i++) {
369 struct _WapiHandle_event *event_handle;
371 event_handle=g_ptr_array_index(item->handles[WAPI_HANDLE_EVENT], i);
373 pthread_rwlock_unlock(&event_handle->rwlock);
376 item->waited[WAPI_HANDLE_EVENT]=TRUE;
377 item->waitcount[WAPI_HANDLE_EVENT]=count;
383 g_message(G_GNUC_PRETTY_FUNCTION ": Wait error %s",
389 g_message(G_GNUC_PRETTY_FUNCTION
390 ": Still waiting for more event handles");
392 /* Drop the rwlocks briefly so that another thread has
393 * a chance to reset any of the events
395 for(i=0; i<numhandles; i++) {
396 struct _WapiHandle_event *event_handle;
398 event_handle=g_ptr_array_index(
399 item->handles[WAPI_HANDLE_EVENT], i);
401 pthread_rwlock_unlock(&event_handle->rwlock);
403 } while((item->timeout==INFINITE) ||
404 (item->timeout > (iterations * 1000)));
406 /* Timeout or other error */
408 for(i=0; i<numhandles; i++) {
409 struct _WapiHandle_event *event_handle;
411 event_handle=g_ptr_array_index(
412 item->handles[WAPI_HANDLE_EVENT], i);
414 pthread_rwlock_unlock(&event_handle->rwlock);
417 pthread_mutex_unlock(&event_signal_mutex);
420 g_message(G_GNUC_PRETTY_FUNCTION ": Returning wait failed");
423 item->waited[WAPI_HANDLE_EVENT]=TRUE;
424 item->waitcount[WAPI_HANDLE_MUTEX]=0;
431 * @security: Ignored for now.
432 * @manual: Specifies whether the new event handle has manual or auto
434 * @initial: Specifies whether the new event handle is initially
436 * @name:Pointer to a string specifying the name of this name, or
437 * %NULL. Currently ignored.
439 * Creates a new event handle.
441 * An event handle is signalled with SetEvent(). If the new handle is
442 * a manual reset event handle, it remains signalled until it is reset
443 * with ResetEvent(). An auto reset event remains signalled until a
444 * single thread has waited for it, at which time the event handle is
445 * automatically reset to unsignalled.
447 * Return value: A new handle, or %NULL on error.
449 WapiHandle *CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean manual,
450 gboolean initial, const guchar *name G_GNUC_UNUSED)
452 struct _WapiHandle_event *event_handle;
455 event_handle=(struct _WapiHandle_event *)g_new0(struct _WapiHandle_event, 1);
456 handle=(WapiHandle *)event_handle;
457 _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_EVENT, event_ops);
459 pthread_mutex_init(&event_handle->mutex, NULL);
460 pthread_cond_init(&event_handle->cond, NULL);
461 pthread_rwlock_init(&event_handle->rwlock, NULL);
462 event_handle->manual=manual;
465 handle->signalled=TRUE;
469 g_message(G_GNUC_PRETTY_FUNCTION ": created new event handle %p",
478 * @handle: The event handle.
480 * Sets the event handle @handle to the signalled state, and then
481 * resets it to unsignalled after informing any waiting threads.
483 * If @handle is a manual reset event, all waiting threads that can be
484 * released immediately are released. @handle is then reset. If
485 * @handle is an auto reset event, one waiting thread is released even
486 * if multiple threads are waiting.
488 * Return value: %TRUE on success, %FALSE otherwise. (Currently only
489 * ever returns %TRUE).
491 gboolean PulseEvent(WapiHandle *handle)
493 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
495 pthread_mutex_lock(&event_handle->mutex);
498 g_message(G_GNUC_PRETTY_FUNCTION ": Pulsing event handle %p", handle);
501 handle->signalled=TRUE;
503 /* Tell everyone blocking on WaitForSingleObject */
504 if(event_handle->manual==TRUE) {
505 pthread_cond_broadcast(&event_handle->cond);
507 pthread_cond_signal(&event_handle->cond);
509 pthread_mutex_unlock(&event_handle->mutex);
512 g_message(G_GNUC_PRETTY_FUNCTION
513 ": Informed single waits for event handle %p", handle);
516 /* Tell everyone blocking on WaitForMultipleObjects */
517 pthread_mutex_lock(&event_signal_mutex);
518 pthread_cond_broadcast(&event_signal_cond);
519 pthread_mutex_unlock(&event_signal_mutex);
522 g_message(G_GNUC_PRETTY_FUNCTION
523 ": Informed multiple waits for event handles");
526 /* Reset the handle signal state */
528 /* This rwlock blocks until no other thread holds a read lock.
529 * This ensures that we can't reset the event until every
530 * waiting thread has had a chance to examine it
532 pthread_rwlock_wrlock(&event_handle->rwlock);
535 g_message(G_GNUC_PRETTY_FUNCTION
536 ": Obtained write lock on event handle %p", handle);
539 handle->signalled=FALSE;
540 pthread_rwlock_unlock(&event_handle->rwlock);
547 * @handle: The event handle.
549 * Resets the event handle @handle to the unsignalled state.
551 * Return value: %TRUE on success, %FALSE otherwise. (Currently only
552 * ever returns %TRUE).
554 gboolean ResetEvent(WapiHandle *handle)
556 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
559 g_message(G_GNUC_PRETTY_FUNCTION ": Resetting event handle %p",
563 /* Test for the current state, because another thread might be
564 * waiting forever on an unsignalled event with the read lock
565 * held. Theres no point going for the write lock if we dont
568 pthread_mutex_lock(&event_handle->mutex);
569 if(handle->signalled==FALSE) {
572 g_message(G_GNUC_PRETTY_FUNCTION
573 ": No need to reset event handle %p", handle);
576 pthread_mutex_unlock(&event_handle->mutex);
579 pthread_mutex_unlock(&event_handle->mutex);
581 pthread_rwlock_wrlock(&event_handle->rwlock);
584 g_message(G_GNUC_PRETTY_FUNCTION
585 ": Obtained write lock on event handle %p", handle);
588 handle->signalled=FALSE;
589 pthread_rwlock_unlock(&event_handle->rwlock);
596 * @handle: The event handle
598 * Sets the event handle @handle to the signalled state.
600 * If @handle is a manual reset event, it remains signalled until it
601 * is reset with ResetEvent(). An auto reset event remains signalled
602 * until a single thread has waited for it, at which time @handle is
603 * automatically reset to unsignalled.
605 * Return value: %TRUE on success, %FALSE otherwise. (Currently only
606 * ever returns %TRUE).
608 gboolean SetEvent(WapiHandle *handle)
610 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
612 pthread_mutex_lock(&event_handle->mutex);
615 g_message(G_GNUC_PRETTY_FUNCTION ": Setting event handle %p", handle);
618 handle->signalled=TRUE;
620 /* Tell everyone blocking on WaitForSingleObject */
621 if(event_handle->manual==TRUE) {
622 pthread_cond_broadcast(&event_handle->cond);
624 pthread_cond_signal(&event_handle->cond);
626 pthread_mutex_unlock(&event_handle->mutex);
629 g_message(G_GNUC_PRETTY_FUNCTION
630 ": Informed single waits for event handle %p", handle);
633 /* Tell everyone blocking on WaitForMultipleObjects */
634 pthread_mutex_lock(&event_signal_mutex);
635 pthread_cond_broadcast(&event_signal_cond);
636 pthread_mutex_unlock(&event_signal_mutex);
639 g_message(G_GNUC_PRETTY_FUNCTION
640 ": Informed multiple waits for event handles");