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"
12 #include "mono-mutex.h"
16 /* event_wait() uses the event-private condition to signal that the
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
23 struct _WapiHandle_event
28 pthread_rwlock_t rwlock;
32 /* event_wait_multiple() uses the global condition to signal that an
35 static mono_mutex_t event_signal_mutex = MONO_MUTEX_INITIALIZER;
36 static pthread_cond_t event_signal_cond = PTHREAD_COND_INITIALIZER;
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);
43 static struct _WapiHandleOps event_ops = {
44 event_close, /* close */
45 NULL, /* getfiletype */
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 */
59 static void event_close(WapiHandle *handle)
61 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
64 g_message(G_GNUC_PRETTY_FUNCTION ": closing event handle %p",
68 mono_mutex_destroy(&event_handle->mutex);
69 pthread_cond_destroy(&event_handle->cond);
70 pthread_rwlock_destroy(&event_handle->rwlock);
73 static gboolean event_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms)
75 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
76 struct timespec timeout;
80 g_message(G_GNUC_PRETTY_FUNCTION
81 ": waiting on event handle %p for %d ms", handle, ms);
84 mono_mutex_lock(&event_handle->mutex);
86 /* Signal this handle after we have obtained the event lock */
88 signal->ops->signal(signal);
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
98 g_message(G_GNUC_PRETTY_FUNCTION
99 ": event handle %p already signalled", handle);
102 if(event_handle->manual==FALSE) {
104 g_message(G_GNUC_PRETTY_FUNCTION
105 ": resetting auto event handle %p", handle);
107 handle->signalled=FALSE;
109 mono_mutex_unlock(&event_handle->mutex);
114 /* We'll have to wait for it then */
116 _wapi_calc_timeout(&timeout, ms);
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
126 pthread_rwlock_rdlock(&event_handle->rwlock);
129 g_message(G_GNUC_PRETTY_FUNCTION
130 ": waiting for event handle %p to be signalled", handle);
134 ret=mono_cond_wait(&event_handle->cond,
135 &event_handle->mutex);
137 ret=mono_cond_timedwait(&event_handle->cond,
138 &event_handle->mutex, &timeout);
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
149 g_message(G_GNUC_PRETTY_FUNCTION ": event handle %p signalled",
152 if(handle->signalled==TRUE) {
155 g_message(G_GNUC_PRETTY_FUNCTION
156 ": event handle %p still signalled", handle);
158 /* If this is an auto-reset event, reset the
159 * state to unsignalled
161 if(event_handle->manual==FALSE) {
163 g_message(G_GNUC_PRETTY_FUNCTION
164 ": resetting auto event handle %p",
167 handle->signalled=FALSE;
169 pthread_rwlock_unlock(&event_handle->rwlock);
170 mono_mutex_unlock(&event_handle->mutex);
176 g_message(G_GNUC_PRETTY_FUNCTION
177 ": event handle %p no longer signalled", handle);
180 /* Better luck next time */
182 /* Drop the rwlock briefly so that another thread has
183 * a chance to reset the event
185 pthread_rwlock_unlock(&event_handle->rwlock);
189 /* Timeout or other error */
192 g_message(G_GNUC_PRETTY_FUNCTION
193 ": wait on event handle %p error: %s", handle,
197 pthread_rwlock_unlock(&event_handle->rwlock);
198 mono_mutex_unlock(&event_handle->mutex);
203 static gboolean event_count_signalled(WaitQueueItem *item, guint32 numhandles,
204 gboolean waitall, guint32 *retcount)
209 /* Lock all the handles, with backoff */
211 for(i=0; i<numhandles; i++) {
212 struct _WapiHandle_event *event_handle;
214 event_handle=g_ptr_array_index(
215 item->handles[WAPI_HANDLE_EVENT], i);
217 ret=mono_mutex_trylock(&event_handle->mutex);
221 event_handle=g_ptr_array_index(
222 item->handles[WAPI_HANDLE_EVENT], i);
223 mono_mutex_unlock(&event_handle->mutex);
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.
244 g_message(G_GNUC_PRETTY_FUNCTION ": Locked all event handles");
247 count=_wapi_handle_count_signalled(item, WAPI_HANDLE_EVENT);
250 g_message(G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
254 if((waitall==TRUE && count==numhandles) ||
255 (waitall==FALSE && count>0)) {
262 for(i=0; i<numhandles; i++) {
263 struct _WapiHandle_event *event_handle;
265 event_handle=g_ptr_array_index(
266 item->handles[WAPI_HANDLE_EVENT], i);
268 mono_mutex_unlock(&event_handle->mutex);
272 g_message(G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
279 static guint32 event_wait_multiple(gpointer data)
281 WaitQueueItem *item=(WaitQueueItem *)data;
282 struct timespec timeout;
284 guint32 numhandles, count, i;
288 numhandles=item->handles[WAPI_HANDLE_EVENT]->len;
291 g_message(G_GNUC_PRETTY_FUNCTION
292 ": waiting on %d event handles for %d ms", numhandles,
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.
300 done=event_count_signalled(item, numhandles, item->waitall, &count);
303 g_message(G_GNUC_PRETTY_FUNCTION
304 ": Preliminary check found %d handles signalled", count);
308 item->waited[WAPI_HANDLE_EVENT]=TRUE;
309 item->waitcount[WAPI_HANDLE_EVENT]=count;
314 /* We'll have to wait then */
316 mono_mutex_lock(&event_signal_mutex);
323 g_message(G_GNUC_PRETTY_FUNCTION ": Wait iteration %d",
327 /* If the timeout isnt INFINITE but greater than 1s,
328 * split the timeout into 1s chunks.
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.)
337 if((item->timeout!=INFINITE) &&
338 (item->timeout < (iterations*1000))) {
340 &timeout, item->timeout-((iterations-1)*1000));
342 _wapi_calc_timeout(&timeout, 1000);
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.)
352 for(i=0; i<numhandles; i++) {
353 struct _WapiHandle_event *event_handle;
355 event_handle=g_ptr_array_index(
356 item->handles[WAPI_HANDLE_EVENT], i);
358 pthread_rwlock_rdlock(&event_handle->rwlock);
361 ret=mono_cond_timedwait(&event_signal_cond,
362 &event_signal_mutex, &timeout);
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.)
370 done=event_count_signalled(item, numhandles,
371 item->waitall, &count);
374 g_message(G_GNUC_PRETTY_FUNCTION
375 ": signal check found %d handles signalled",
381 g_message(G_GNUC_PRETTY_FUNCTION
382 ": Returning wait success");
385 for(i=0; i<numhandles; i++) {
386 struct _WapiHandle_event *event_handle;
388 event_handle=g_ptr_array_index(item->handles[WAPI_HANDLE_EVENT], i);
390 pthread_rwlock_unlock(&event_handle->rwlock);
393 item->waited[WAPI_HANDLE_EVENT]=TRUE;
394 item->waitcount[WAPI_HANDLE_EVENT]=count;
400 g_message(G_GNUC_PRETTY_FUNCTION ": Wait error %s",
406 g_message(G_GNUC_PRETTY_FUNCTION
407 ": Still waiting for more event handles");
409 /* Drop the rwlocks briefly so that another thread has
410 * a chance to reset any of the events
412 for(i=0; i<numhandles; i++) {
413 struct _WapiHandle_event *event_handle;
415 event_handle=g_ptr_array_index(
416 item->handles[WAPI_HANDLE_EVENT], i);
418 pthread_rwlock_unlock(&event_handle->rwlock);
420 } while((item->timeout==INFINITE) ||
421 (item->timeout > (iterations * 1000)));
423 /* Timeout or other error */
425 for(i=0; i<numhandles; i++) {
426 struct _WapiHandle_event *event_handle;
428 event_handle=g_ptr_array_index(
429 item->handles[WAPI_HANDLE_EVENT], i);
431 pthread_rwlock_unlock(&event_handle->rwlock);
434 mono_mutex_unlock(&event_signal_mutex);
437 g_message(G_GNUC_PRETTY_FUNCTION ": Returning wait failed");
440 item->waited[WAPI_HANDLE_EVENT]=TRUE;
441 item->waitcount[WAPI_HANDLE_MUTEX]=0;
446 static void event_signal(WapiHandle *handle)
453 * @security: Ignored for now.
454 * @manual: Specifies whether the new event handle has manual or auto
456 * @initial: Specifies whether the new event handle is initially
458 * @name:Pointer to a string specifying the name of this name, or
459 * %NULL. Currently ignored.
461 * Creates a new event handle.
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.
469 * Return value: A new handle, or %NULL on error.
471 WapiHandle *CreateEvent(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean manual,
472 gboolean initial, const guchar *name G_GNUC_UNUSED)
474 struct _WapiHandle_event *event_handle;
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);
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;
487 handle->signalled=TRUE;
491 g_message(G_GNUC_PRETTY_FUNCTION ": created new event handle %p",
500 * @handle: The event handle.
502 * Sets the event handle @handle to the signalled state, and then
503 * resets it to unsignalled after informing any waiting threads.
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.
510 * Return value: %TRUE on success, %FALSE otherwise. (Currently only
511 * ever returns %TRUE).
513 gboolean PulseEvent(WapiHandle *handle)
515 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
517 mono_mutex_lock(&event_handle->mutex);
520 g_message(G_GNUC_PRETTY_FUNCTION ": Pulsing event handle %p", handle);
523 handle->signalled=TRUE;
525 /* Tell everyone blocking on WaitForSingleObject */
526 if(event_handle->manual==TRUE) {
527 pthread_cond_broadcast(&event_handle->cond);
529 pthread_cond_signal(&event_handle->cond);
531 mono_mutex_unlock(&event_handle->mutex);
534 g_message(G_GNUC_PRETTY_FUNCTION
535 ": Informed single waits for event handle %p", handle);
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);
544 g_message(G_GNUC_PRETTY_FUNCTION
545 ": Informed multiple waits for event handles");
548 /* Reset the handle signal state */
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
554 pthread_rwlock_wrlock(&event_handle->rwlock);
557 g_message(G_GNUC_PRETTY_FUNCTION
558 ": Obtained write lock on event handle %p", handle);
561 handle->signalled=FALSE;
562 pthread_rwlock_unlock(&event_handle->rwlock);
569 * @handle: The event handle.
571 * Resets the event handle @handle to the unsignalled state.
573 * Return value: %TRUE on success, %FALSE otherwise. (Currently only
574 * ever returns %TRUE).
576 gboolean ResetEvent(WapiHandle *handle)
578 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
581 g_message(G_GNUC_PRETTY_FUNCTION ": Resetting event handle %p",
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
590 mono_mutex_lock(&event_handle->mutex);
591 if(handle->signalled==FALSE) {
594 g_message(G_GNUC_PRETTY_FUNCTION
595 ": No need to reset event handle %p", handle);
598 mono_mutex_unlock(&event_handle->mutex);
601 mono_mutex_unlock(&event_handle->mutex);
603 pthread_rwlock_wrlock(&event_handle->rwlock);
606 g_message(G_GNUC_PRETTY_FUNCTION
607 ": Obtained write lock on event handle %p", handle);
610 handle->signalled=FALSE;
611 pthread_rwlock_unlock(&event_handle->rwlock);
618 * @handle: The event handle
620 * Sets the event handle @handle to the signalled state.
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.
627 * Return value: %TRUE on success, %FALSE otherwise. (Currently only
628 * ever returns %TRUE).
630 gboolean SetEvent(WapiHandle *handle)
632 struct _WapiHandle_event *event_handle=(struct _WapiHandle_event *)handle;
634 mono_mutex_lock(&event_handle->mutex);
637 g_message(G_GNUC_PRETTY_FUNCTION ": Setting event handle %p", handle);
640 handle->signalled=TRUE;
642 /* Tell everyone blocking on WaitForSingleObject */
643 if(event_handle->manual==TRUE) {
644 pthread_cond_broadcast(&event_handle->cond);
646 pthread_cond_signal(&event_handle->cond);
648 mono_mutex_unlock(&event_handle->mutex);
651 g_message(G_GNUC_PRETTY_FUNCTION
652 ": Informed single waits for event handle %p", handle);
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);
661 g_message(G_GNUC_PRETTY_FUNCTION
662 ": Informed multiple waits for event handles");