2010-02-06 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / io-layer / wait.c
index 7b583ec6893d8438b3e1ab5216d100ef7aab568d..db008cc1f87a0b910254bd82997650ddc1196bb7 100644 (file)
+/*
+ * wait.c:  wait for handles to become signalled
+ *
+ * Author:
+ *     Dick Porter (dick@ximian.com)
+ *
+ * (C) 2002-2006 Novell, Inc.
+ */
+
 #include <config.h>
 #include <glib.h>
 #include <string.h>
+#include <errno.h>
+
+#include <mono/utils/gc_wrapper.h>
 
-#include "mono/io-layer/wapi.h"
-#include "wait-private.h"
-#include "timed-thread.h"
-#include "handles-private.h"
-#include "wapi-private.h"
+#include <mono/io-layer/wapi.h>
+#include <mono/io-layer/handles-private.h>
+#include <mono/io-layer/wapi-private.h>
+#include <mono/io-layer/mono-mutex.h>
+#include <mono/io-layer/misc-private.h>
 
-#define DEBUG
+#undef DEBUG
 
-static pthread_once_t wait_once=PTHREAD_ONCE_INIT;
+static gboolean own_if_signalled(gpointer handle)
+{
+       gboolean ret = FALSE;
+       
+       if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
+               if (_wapi_handle_trylock_shared_handles () == EBUSY) {
+                       return (FALSE);
+               }
+       }
+       
+       if (_wapi_handle_issignalled (handle)) {
+               _wapi_handle_ops_own (handle);
+               ret = TRUE;
+       }
 
-static GPtrArray *WaitQueue=NULL;
+       if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
+               _wapi_handle_unlock_shared_handles ();
+       }
 
-static pthread_t wait_monitor_thread_id;
-static gboolean wait_monitor_thread_running=FALSE;
-static pthread_mutex_t wait_monitor_mutex=PTHREAD_MUTEX_INITIALIZER;
-static sem_t wait_monitor_sem;
+       return(ret);
+}
 
-static void launch_tidy(guint32 exitcode G_GNUC_UNUSED, gpointer user)
+static gboolean own_if_owned(gpointer handle)
 {
-       WaitQueueItem *item=(WaitQueueItem *)user;
+       gboolean ret = FALSE;
        
-#ifdef DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": Informing monitor thread");
-#endif
-
-       /* Update queue item */
-       pthread_mutex_lock(&item->mutex);
-       item->update++;
-       pthread_mutex_unlock(&item->mutex);
+       if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
+               if (_wapi_handle_trylock_shared_handles () == EBUSY) {
+                       return (FALSE);
+               }
+       }
        
-       /* Signal monitor */
-       sem_post(&wait_monitor_sem);
+       if (_wapi_handle_ops_isowned (handle)) {
+               _wapi_handle_ops_own (handle);
+               ret = TRUE;
+       }
+
+       if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
+               _wapi_handle_unlock_shared_handles ();
+       }
+
+       return(ret);
 }
 
-/* This function is called by the monitor thread to launch handle-type
- * specific threads to block in particular ways.
+/**
+ * WaitForSingleObjectEx:
+ * @handle: an object to wait for
+ * @timeout: the maximum time in milliseconds to wait for
+ * @alertable: if TRUE, the wait can be interrupted by an APC call
  *
- * The item mutex is held by the monitor thread when this function is
- * called.
+ * This function returns when either @handle is signalled, or @timeout
+ * ms elapses.  If @timeout is zero, the object's state is tested and
+ * the function returns immediately.  If @timeout is %INFINITE, the
+ * function waits forever.
+ *
+ * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
+ * released by the owning thread when it exited.  Ownership of the
+ * mutex object is granted to the calling thread and the mutex is set
+ * to nonsignalled.  %WAIT_OBJECT_0 - The state of @handle is
+ * signalled.  %WAIT_TIMEOUT - The @timeout interval elapsed and
+ * @handle's state is still not signalled.  %WAIT_FAILED - an error
+ * occurred. %WAIT_IO_COMPLETION - the wait was ended by an APC.
  */
-static void launch_blocker_threads(WaitQueueItem *item)
+guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
+                             gboolean alertable)
 {
-       int i, ret;
+       guint32 ret, waited;
+       struct timespec abstime;
+       int thr_ret;
+       gboolean apc_pending = FALSE;
+       gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
+       
+       if (current_thread == NULL) {
+               SetLastError (ERROR_INVALID_HANDLE);
+               return(WAIT_FAILED);
+       }
+
+       if (handle == _WAPI_THREAD_CURRENT) {
+               handle = _wapi_thread_handle_from_id (pthread_self ());
+               if (handle == NULL) {
+                       SetLastError (ERROR_INVALID_HANDLE);
+                       return(WAIT_FAILED);
+               }
+       }
+
+       if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               SetLastError (ERROR_INVALID_HANDLE);
+               return(WAIT_FAILED);
+       }
        
+       if (_wapi_handle_test_capabilities (handle,
+                                           WAPI_HANDLE_CAP_WAIT) == FALSE) {
 #ifdef DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": Launching blocker threads");
+               g_message ("%s: handle %p can't be waited for", __func__,
+                          handle);
 #endif
 
-       for(i=0; i<WAPI_HANDLE_COUNT; i++) {
-               if(item->handles[i]->len>0) {
-                       WapiHandle *handle;
+               return(WAIT_FAILED);
+       }
 
-                       handle=g_ptr_array_index(item->handles[i], 0);
-                       g_assert(handle!=NULL);
-                       g_assert(handle->ops->wait_multiple!=NULL);
-                       
+       _wapi_handle_ops_prewait (handle);
+       
+       if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
 #ifdef DEBUG
-                       g_message("Handle type %d active", i);
+               g_message ("%s: handle %p has special wait", __func__, handle);
 #endif
-                       item->waited[i]=FALSE;
-                       
-                       ret=_wapi_timed_thread_create(
-                               &item->thread[i], NULL,
-                               handle->ops->wait_multiple, launch_tidy, item,
-                               item);
-                       if(ret!=0) {
-                               g_warning(G_GNUC_PRETTY_FUNCTION
-                                         ": Thread create error: %s",
-                                         strerror(ret));
-                               return;
-                       }
-               } else {
-                       /* Pretend to have already waited for the
-                        * thread; it makes life easier for the
-                        * monitor thread.
-                        */
-                       item->waited[i]=TRUE;
-               }
-       }
-}
 
-static gboolean launch_threads_done(WaitQueueItem *item)
-{
-       int i;
+               ret = _wapi_handle_ops_special_wait (handle, timeout);
        
-       for(i=0; i<WAPI_HANDLE_COUNT; i++) {
-               if(item->waited[i]==FALSE) {
-                       return(FALSE);
+               if (alertable && _wapi_thread_apc_pending (current_thread)) {
+                       apc_pending = TRUE;
+                       ret = WAIT_IO_COMPLETION;
                }
+
+               goto check_pending;
        }
+       
+       
+#ifdef DEBUG
+       g_message ("%s: locking handle %p", __func__, handle);
+#endif
 
-       return(TRUE);
-}
+       pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
+                             handle);
+       thr_ret = _wapi_handle_lock_handle (handle);
+       g_assert (thr_ret == 0);
 
-/* This is the main loop for the monitor thread.  It waits for a
- * signal to check the wait queue, then takes any action necessary on
- * any queue items that have indicated readiness.
- */
-static void *wait_monitor_thread(void *unused G_GNUC_UNUSED)
-{
-       guint i;
+       if (_wapi_handle_test_capabilities (handle,
+                                           WAPI_HANDLE_CAP_OWN) == TRUE) {
+               if (own_if_owned (handle) == TRUE) {
+#ifdef DEBUG
+                       g_message ("%s: handle %p already owned", __func__,
+                                  handle);
+#endif
+                       ret = WAIT_OBJECT_0;
+                       goto done;
+               }
+       }
        
-       /* Signal that the monitor thread is ready */
-       wait_monitor_thread_running=TRUE;
+       if (alertable && _wapi_thread_apc_pending (current_thread)) {
+               apc_pending = TRUE;
+               ret = WAIT_IO_COMPLETION;
+               goto done;
+       }
        
-       while(TRUE) {
-               /* Use a semaphore rather than a cond so we dont miss
-                * any signals
-                */
-               sem_wait(&wait_monitor_sem);
-               
+       if (own_if_signalled (handle) == TRUE) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION
-                         ": Blocking thread doing stuff");
+               g_message ("%s: handle %p already signalled", __func__,
+                          handle);
 #endif
-               
-               /* We've been signalled, so scan the wait queue for
-                * activity.
+
+               ret=WAIT_OBJECT_0;
+               goto done;
+       }
+
+       if (timeout == 0) {
+               ret = WAIT_TIMEOUT;
+               goto done;
+       }
+       /* Have to wait for it */
+       if (timeout != INFINITE) {
+               _wapi_calc_timeout (&abstime, timeout);
+       }
+       
+       do {
+               /* Check before waiting on the condition, just in case
                 */
-               pthread_mutex_lock(&wait_monitor_mutex);
-               for(i=0; i<WaitQueue->len; i++) {
-                       WaitQueueItem *item=g_ptr_array_index(WaitQueue, i);
+               _wapi_handle_ops_prewait (handle);
+
+               if (own_if_signalled (handle)) {
+#ifdef DEBUG
+                       g_message ("%s: handle %p signalled", __func__,
+                                  handle);
+#endif
+
+                       ret = WAIT_OBJECT_0;
+                       goto done;
+               }
                        
-                       if(item->update > item->ack) {
-                               /* Something changed */
-                               pthread_mutex_lock(&item->mutex);
-                               item->ack=item->update;
-                               
-                               switch(item->state) {
-                               case WQ_NEW:
-                                       /* Launch a new thread for each type of
-                                        * handle to be waited for here.
-                                        */
-                                       launch_blocker_threads(item);
-                                       
-                                       item->state=WQ_WAITING;
-                                       break;
-                                       
-                               case WQ_WAITING:
-                                       /* See if we have waited for
-                                        * the last blocker thread.
-                                        */
-                                       if(launch_threads_done(item)) {
-                                               /* All handles have
-                                                * been signalled, so
-                                                * signal the waiting
-                                                * thread.  Let the
-                                                * waiting thread
-                                                * remove this item
-                                                * from the queue,
-                                                * because it makes
-                                                * the logic a lot
-                                                * easier here.
-                                                */
-                                               item->state=WQ_SIGNALLED;
-                                               sem_post(&item->wait_sem);
-                                       }
-                                       break;
-                                       
-                               case WQ_SIGNALLED:
-                                       /* This shouldn't happen. Prod
-                                        * the blocking thread again
-                                        * just to make sure.
-                                        */
-                                       g_warning(G_GNUC_PRETTY_FUNCTION
-                                                 ": Prodding blocker again");
-                                       sem_post(&item->wait_sem);
-                                       break;
-                               }
-                               
-                               pthread_mutex_unlock(&item->mutex);
+               if (timeout == INFINITE) {
+                       waited = _wapi_handle_wait_signal_handle (handle, alertable);
+               } else {
+                       waited = _wapi_handle_timedwait_signal_handle (handle, &abstime, alertable, FALSE);
+               }
+       
+               if (alertable)
+                       apc_pending = _wapi_thread_apc_pending (current_thread);
+
+               if(waited==0 && !apc_pending) {
+                       /* Condition was signalled, so hopefully
+                        * handle is signalled now.  (It might not be
+                        * if someone else got in before us.)
+                        */
+                       if (own_if_signalled (handle)) {
+#ifdef DEBUG
+                               g_message ("%s: handle %p signalled", __func__,
+                                          handle);
+#endif
+
+                               ret=WAIT_OBJECT_0;
+                               goto done;
                        }
+               
+                       /* Better luck next time */
                }
+       } while(waited == 0 && !apc_pending);
 
-               pthread_mutex_unlock(&wait_monitor_mutex);
-       }
-       
-       return(NULL);
-}
+       /* Timeout or other error */
+#ifdef DEBUG
+       g_message ("%s: wait on handle %p error: %s", __func__, handle,
+                  strerror (waited));
+#endif
 
-static void wait_init(void)
-{
-       int ret;
+       ret = WAIT_TIMEOUT;
        
+done:
+
 #ifdef DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION ": Starting monitor thread");
+       g_message ("%s: unlocking handle %p", __func__, handle);
 #endif
        
-       WaitQueue=g_ptr_array_new();
+       thr_ret = _wapi_handle_unlock_handle (handle);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
        
-       sem_init(&wait_monitor_sem, 0, 0);
-       
-       /* Launch a thread which manages the wait queue, and deals
-        * with waiting for handles of various types.
-        */
-       ret=pthread_create(&wait_monitor_thread_id, NULL,
-                          wait_monitor_thread, NULL);
-       if(ret!=0) {
-               g_warning(G_GNUC_PRETTY_FUNCTION
-                         ": Couldn't start handle monitor thread: %s",
-                         strerror(ret));
-       }
-       
-       /* Wait for the monitor thread to get going */
-       while(wait_monitor_thread_running==FALSE) {
-               pthread_yield();
+check_pending:
+       if (apc_pending) {
+               _wapi_thread_dispatch_apc_queue (current_thread);
+               ret = WAIT_IO_COMPLETION;
        }
+               
+       return(ret);
 }
 
-static WaitQueueItem *wait_item_new(guint32 timeout, gboolean waitall)
+guint32 WaitForSingleObject(gpointer handle, guint32 timeout)
 {
-       WaitQueueItem *new;
-       int i;
+       return WaitForSingleObjectEx (handle, timeout, FALSE);
+}
+
+
+/**
+ * SignalObjectAndWait:
+ * @signal_handle: An object to signal
+ * @wait: An object to wait for
+ * @timeout: The maximum time in milliseconds to wait for
+ * @alertable: Specifies whether the function returnes when the system
+ * queues an I/O completion routine or an APC for the calling thread.
+ *
+ * Atomically signals @signal and waits for @wait to become signalled,
+ * or @timeout ms elapses.  If @timeout is zero, the object's state is
+ * tested and the function returns immediately.  If @timeout is
+ * %INFINITE, the function waits forever.
+ *
+ * @signal can be a semaphore, mutex or event object.
+ *
+ * If @alertable is %TRUE and the system queues an I/O completion
+ * routine or an APC for the calling thread, the function returns and
+ * the thread calls the completion routine or APC function.  If
+ * %FALSE, the function does not return, and the thread does not call
+ * the completion routine or APC function.  A completion routine is
+ * queued when the ReadFileEx() or WriteFileEx() function in which it
+ * was specified has completed.  The calling thread is the thread that
+ * initiated the read or write operation.  An APC is queued when
+ * QueueUserAPC() is called.  Currently completion routines and APC
+ * functions are not supported.
+ *
+ * Return value: %WAIT_ABANDONED - @wait is a mutex that was not
+ * released by the owning thread when it exited.  Ownershop of the
+ * mutex object is granted to the calling thread and the mutex is set
+ * to nonsignalled.  %WAIT_IO_COMPLETION - the wait was ended by one
+ * or more user-mode asynchronous procedure calls queued to the
+ * thread.  %WAIT_OBJECT_0 - The state of @wait is signalled.
+ * %WAIT_TIMEOUT - The @timeout interval elapsed and @wait's state is
+ * still not signalled.  %WAIT_FAILED - an error occurred.
+ */
+guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
+                           guint32 timeout, gboolean alertable)
+{
+       guint32 ret, waited;
+       struct timespec abstime;
+       int thr_ret;
+       gboolean apc_pending = FALSE;
+       gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
+       
+       if (current_thread == NULL) {
+               SetLastError (ERROR_INVALID_HANDLE);
+               return(WAIT_FAILED);
+       }
+
+       if (signal_handle == _WAPI_THREAD_CURRENT) {
+               signal_handle = _wapi_thread_handle_from_id (pthread_self ());
+               if (signal_handle == NULL) {
+                       SetLastError (ERROR_INVALID_HANDLE);
+                       return(WAIT_FAILED);
+               }
+       }
+
+       if (wait == _WAPI_THREAD_CURRENT) {
+               wait = _wapi_thread_handle_from_id (pthread_self ());
+               if (wait == NULL) {
+                       SetLastError (ERROR_INVALID_HANDLE);
+                       return(WAIT_FAILED);
+               }
+       }
+
+       if ((GPOINTER_TO_UINT (signal_handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               SetLastError (ERROR_INVALID_HANDLE);
+               return(WAIT_FAILED);
+       }
+
+       if ((GPOINTER_TO_UINT (wait) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               SetLastError (ERROR_INVALID_HANDLE);
+               return(WAIT_FAILED);
+       }
        
-       new=g_new0(WaitQueueItem, 1);
+       if (_wapi_handle_test_capabilities (signal_handle,
+                                           WAPI_HANDLE_CAP_SIGNAL)==FALSE) {
+               return(WAIT_FAILED);
+       }
        
-       pthread_mutex_init(&new->mutex, NULL);
-       sem_init(&new->wait_sem, 0, 0);
+       if (_wapi_handle_test_capabilities (wait,
+                                           WAPI_HANDLE_CAP_WAIT)==FALSE) {
+               return(WAIT_FAILED);
+       }
 
-       new->update=1;          /* As soon as this item is queued it
-                                * will need attention.
-                                */
-       new->state=WQ_NEW;
-       new->timeout=timeout;
-       new->waitall=waitall;
-       new->lowest_signal=MAXIMUM_WAIT_OBJECTS;
+       _wapi_handle_ops_prewait (wait);
        
-       for(i=0; i<WAPI_HANDLE_COUNT; i++) {
-               new->handles[i]=g_ptr_array_new();
-               new->waitindex[i]=g_array_new(FALSE, FALSE, sizeof(guint32));
+       if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) {
+               g_warning ("%s: handle %p has special wait, implement me!!",
+                          __func__, wait);
+
+               return (WAIT_FAILED);
        }
-       
-       return(new);
-}
 
-/* Adds our queue item to the work queue, and blocks until the monitor
- * thread thinks it's done the work.  Returns TRUE for done, FALSE for
- * timed out.  Sets lowest to the index of the first signalled handle
- * in the list.
- */
-static gboolean wait_for_item(WaitQueueItem *item, guint32 *lowest)
-{
-       gboolean ret;
-       int i;
+#ifdef DEBUG
+       g_message ("%s: locking handle %p", __func__, wait);
+#endif
+
+       pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
+                             wait);
+       thr_ret = _wapi_handle_lock_handle (wait);
+       g_assert (thr_ret == 0);
+
+       _wapi_handle_ops_signal (signal_handle);
+
+       if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_OWN)==TRUE) {
+               if (own_if_owned (wait)) {
+#ifdef DEBUG
+                       g_message ("%s: handle %p already owned", __func__,
+                                  wait);
+#endif
+                       ret = WAIT_OBJECT_0;
+                       goto done;
+               }
+       }
        
-       /* Add the wait item to the monitor queue, and signal the
-        * monitor thread */
-       pthread_mutex_lock(&wait_monitor_mutex);
-       g_ptr_array_add(WaitQueue, item);
-       sem_post(&wait_monitor_sem);
-       pthread_mutex_unlock(&wait_monitor_mutex);
+       if (alertable && _wapi_thread_apc_pending (current_thread)) {
+               apc_pending = TRUE;
+               ret = WAIT_IO_COMPLETION;
+               goto done;
+       }
        
-       /* Wait for the item to become ready */
-       sem_wait(&item->wait_sem);
+       if (own_if_signalled (wait)) {
+#ifdef DEBUG
+               g_message ("%s: handle %p already signalled", __func__, wait);
+#endif
+
+               ret = WAIT_OBJECT_0;
+               goto done;
+       }
+
+       /* Have to wait for it */
+       if (timeout != INFINITE) {
+               _wapi_calc_timeout (&abstime, timeout);
+       }
        
-       pthread_mutex_lock(&item->mutex);
+       do {
+               /* Check before waiting on the condition, just in case
+                */
+               _wapi_handle_ops_prewait (wait);
        
-       /* If waitall is TRUE, then the number signalled in each handle type
-        * must be the length of the handle type array for the wait to be
-        * successful.  Otherwise, any signalled handle is good enough
-        */
-       if(item->waitall==TRUE) {
-               ret=TRUE;
-               for(i=0; i<WAPI_HANDLE_COUNT; i++) {
-                       if(item->waitcount[i]!=item->handles[i]->len) {
-                               ret=FALSE;
-                               break;
-                       }
+               if (own_if_signalled (wait)) {
+#ifdef DEBUG
+                       g_message ("%s: handle %p signalled", __func__, wait);
+#endif
+
+                       ret = WAIT_OBJECT_0;
+                       goto done;
                }
-       } else {
-               ret=FALSE;
-               for(i=0; i<WAPI_HANDLE_COUNT; i++) {
-                       if(item->waitcount[i]>0) {
-                               ret=TRUE;
-                               break;
+               
+               if (timeout == INFINITE) {
+                       waited = _wapi_handle_wait_signal_handle (wait, alertable);
+               } else {
+                       waited = _wapi_handle_timedwait_signal_handle (wait, &abstime, alertable, FALSE);
+               }
+
+               if (alertable) {
+                       apc_pending = _wapi_thread_apc_pending (current_thread);
+               }
+
+               if (waited==0 && !apc_pending) {
+                       /* Condition was signalled, so hopefully
+                        * handle is signalled now.  (It might not be
+                        * if someone else got in before us.)
+                        */
+                       if (own_if_signalled (wait)) {
+#ifdef DEBUG
+                               g_message ("%s: handle %p signalled", __func__,
+                                          wait);
+#endif
+
+                               ret = WAIT_OBJECT_0;
+                               goto done;
                        }
+               
+                       /* Better luck next time */
                }
-       }
+       } while(waited == 0 && !apc_pending);
 
-       *lowest=item->lowest_signal;
+       /* Timeout or other error */
+#ifdef DEBUG
+       g_message ("%s: wait on handle %p error: %s", __func__, wait,
+                  strerror (ret));
+#endif
+
+       ret = WAIT_TIMEOUT;
        
-       pthread_mutex_unlock(&item->mutex);
+done:
 
-       return(ret);
-}
+#ifdef DEBUG
+       g_message ("%s: unlocking handle %p", __func__, wait);
+#endif
 
-static gboolean wait_dequeue_item(WaitQueueItem *item)
-{
-       gboolean ret;
-       
-       g_assert(WaitQueue!=NULL);
-       
-       pthread_mutex_lock(&wait_monitor_mutex);
-       ret=g_ptr_array_remove_fast(WaitQueue, item);
-       pthread_mutex_unlock(&wait_monitor_mutex);
+       thr_ret = _wapi_handle_unlock_handle (wait);
+       g_assert (thr_ret == 0);
+       pthread_cleanup_pop (0);
+
+       if (apc_pending) {
+               _wapi_thread_dispatch_apc_queue (current_thread);
+               ret = WAIT_IO_COMPLETION;
+       }
        
        return(ret);
 }
 
-static void wait_item_destroy(WaitQueueItem *item)
+struct handle_cleanup_data
 {
-       int i;
-       
-       pthread_mutex_destroy(&item->mutex);
-       sem_destroy(&item->wait_sem);
-       
-       for(i=0; i<WAPI_HANDLE_COUNT; i++) {
-               if(item->thread[i]!=NULL) {
-                       g_free(item->thread[i]);
-               }
-               g_ptr_array_free(item->handles[i], FALSE);
-               g_array_free(item->waitindex[i], FALSE);
-       }
-}
+       guint32 numobjects;
+       gpointer *handles;
+};
 
+static void handle_cleanup (void *data)
+{
+       struct handle_cleanup_data *handles = (struct handle_cleanup_data *)data;
 
-/**
- * WaitForSingleObject:
- * @handle: an object to wait for
- * @timeout: the maximum time in milliseconds to wait for
- *
- * This function returns when either @handle is signalled, or @timeout
- * ms elapses.  If @timeout is zero, the object's state is tested and
- * the function returns immediately.  If @timeout is %INFINITE, the
- * function waits forever.
- *
- * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
- * released by the owning thread when it exited.  Ownership of the
- * mutex object is granted to the calling thread and the mutex is set
- * to nonsignalled.  %WAIT_OBJECT_0 - The state of @handle is
- * signalled.  %WAIT_TIMEOUT - The @timeout interval elapsed and
- * @handle's state is still not signalled.  %WAIT_FAILED - an error
- * occurred.
- */
-guint32 WaitForSingleObject(WapiHandle *handle, guint32 timeout)
+       _wapi_handle_unlock_handles (handles->numobjects, handles->handles);
+}
+
+static gboolean test_and_own (guint32 numobjects, gpointer *handles,
+                             gboolean waitall, guint32 *count,
+                             guint32 *lowest)
 {
-       gboolean wait;
+       struct handle_cleanup_data cleanup_data;
+       gboolean done;
+       int i;
        
-       if(handle->ops->wait==NULL) {
-               return(WAIT_FAILED);
-       }
-
-       wait=handle->ops->wait(handle, NULL, timeout);
-       if(wait==TRUE) {
-               /* Object signalled before timeout expired */
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": Object %p signalled",
-                         handle);
+       g_message ("%s: locking handles", __func__);
 #endif
-               return(WAIT_OBJECT_0);
-       } else {
+       cleanup_data.numobjects = numobjects;
+       cleanup_data.handles = handles;
+       
+       pthread_cleanup_push (handle_cleanup, (void *)&cleanup_data);
+       done = _wapi_handle_count_signalled_handles (numobjects, handles,
+                                                    waitall, count, lowest);
+       if (done == TRUE) {
+               if (waitall == TRUE) {
+                       for (i = 0; i < numobjects; i++) {
+                               own_if_signalled (handles[i]);
+                       }
+               } else {
+                       own_if_signalled (handles[*lowest]);
+               }
+       }
+       
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": Object %p wait timed out",
-                         handle);
+       g_message ("%s: unlocking handles", __func__);
 #endif
-               return(WAIT_TIMEOUT);
-       }
+
+       /* calls the unlock function */
+       pthread_cleanup_pop (1);
+
+       return(done);
 }
 
+
+
 /**
- * WaitForMultipleObjects:
+ * WaitForMultipleObjectsEx:
  * @numobjects: The number of objects in @handles. The maximum allowed
  * is %MAXIMUM_WAIT_OBJECTS.
  * @handles: An array of object handles.  Duplicates are not allowed.
@@ -370,6 +528,7 @@ guint32 WaitForSingleObject(WapiHandle *handle, guint32 timeout)
  * are signalled.  If %FALSE, this function returns when any object is
  * signalled.
  * @timeout: The maximum time in milliseconds to wait for.
+ * @alertable: if TRUE, the wait can be interrupted by an APC call
  * 
  * This function returns when either one or more of @handles is
  * signalled, or @timeout ms elapses.  If @timeout is zero, the state
@@ -388,90 +547,262 @@ guint32 WaitForSingleObject(WapiHandle *handle, guint32 timeout)
  * indicates the first index into @handles of an abandoned mutex.
  * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
  * @handles are signalled.  %WAIT_FAILED - an error occurred.
+ * %WAIT_IO_COMPLETION - the wait was ended by an APC.
  */
-guint32 WaitForMultipleObjects(guint32 numobjects, WapiHandle **handles,
-                              gboolean waitall, guint32 timeout)
+guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
+                                gboolean waitall, guint32 timeout,
+                                gboolean alertable)
 {
-       WaitQueueItem *item;
        GHashTable *dups;
-       gboolean duplicate=FALSE, bogustype=FALSE;
-       gboolean wait;
+       gboolean duplicate = FALSE, bogustype = FALSE, done;
+       guint32 count, lowest;
+       struct timespec abstime;
        guint i;
-       guint32 lowest;
-       
-       pthread_once(&wait_once, wait_init);
+       guint32 ret;
+       int thr_ret;
+       gpointer current_thread = _wapi_thread_handle_from_id (pthread_self ());
+       guint32 retval;
+       gboolean poll;
+       
+       if (current_thread == NULL) {
+               SetLastError (ERROR_INVALID_HANDLE);
+               return(WAIT_FAILED);
+       }
        
-       if(numobjects>MAXIMUM_WAIT_OBJECTS) {
+       if (numobjects > MAXIMUM_WAIT_OBJECTS) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION ": Too many handles: %d",
-                         numobjects);
+               g_message ("%s: Too many handles: %d", __func__, numobjects);
 #endif
 
                return(WAIT_FAILED);
        }
        
+       if (numobjects == 1) {
+               return WaitForSingleObjectEx (handles [0], timeout, alertable);
+       }
+
        /* Check for duplicates */
-       dups=g_hash_table_new(g_direct_hash, g_direct_equal);
-       for(i=0; i<numobjects; i++) {
-               gpointer exists=g_hash_table_lookup(dups, handles[i]);
-               if(exists!=NULL) {
+       dups = g_hash_table_new (g_direct_hash, g_direct_equal);
+       for (i = 0; i < numobjects; i++) {
+               gpointer exists;
+
+               if (handles[i] == _WAPI_THREAD_CURRENT) {
+                       handles[i] = _wapi_thread_handle_from_id (pthread_self ());
+                       
+                       if (handles[i] == NULL) {
+#ifdef DEBUG
+                               g_message ("%s: Handle %d bogus", __func__, i);
+#endif
+
+                               bogustype = TRUE;
+                               break;
+                       }
+               }
+
+               if ((GPOINTER_TO_UINT (handles[i]) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+#ifdef DEBUG
+                       g_message ("%s: Handle %d pseudo process", __func__,
+                                  i);
+#endif
+
+                       bogustype = TRUE;
+                       break;
+               }
+               
+               exists = g_hash_table_lookup (dups, handles[i]);
+               if (exists != NULL) {
 #ifdef DEBUG
-                       g_message(G_GNUC_PRETTY_FUNCTION
-                                 ": Handle %p duplicated", handles[i]);
+                       g_message ("%s: Handle %p duplicated", __func__,
+                                  handles[i]);
 #endif
 
-                       duplicate=TRUE;
+                       duplicate = TRUE;
                        break;
                }
 
-               if(handles[i]->ops->wait_multiple==NULL) {
+               if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_WAIT) == FALSE) {
 #ifdef DEBUG
-                       g_message(G_GNUC_PRETTY_FUNCTION
-                                 ": Handle %p can't be waited for (type %d)",
-                                 handles[i], handles[i]->type);
+                       g_message ("%s: Handle %p can't be waited for",
+                                  __func__, handles[i]);
 #endif
 
-                       bogustype=TRUE;
+                       bogustype = TRUE;
+                       break;
                }
 
-               g_hash_table_insert(dups, handles[i], handles[i]);
+               g_hash_table_insert (dups, handles[i], handles[i]);
+               _wapi_handle_ops_prewait (handles[i]);
        }
-       g_hash_table_destroy(dups);
+       g_hash_table_destroy (dups);
 
-       if(duplicate==TRUE) {
+       if (duplicate == TRUE) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION
-                         ": Returning due to duplicates");
+               g_message ("%s: Returning due to duplicates", __func__);
 #endif
 
                return(WAIT_FAILED);
        }
 
-       if(bogustype==TRUE) {
+       if (bogustype == TRUE) {
 #ifdef DEBUG
-               g_message(G_GNUC_PRETTY_FUNCTION
-                         ": Returning due to bogus type");
+               g_message ("%s: Returning due to bogus type", __func__);
 #endif
 
                return(WAIT_FAILED);
        }
+
+       poll = FALSE;
+       for (i = 0; i < numobjects; ++i)
+               if (_wapi_handle_type (handles [i]) == WAPI_HANDLE_PROCESS)
+                       /* Can't wait for a process handle + another handle without polling */
+                       poll = TRUE;
+
+       done = test_and_own (numobjects, handles, waitall, &count, &lowest);
+       if (done == TRUE) {
+               return(WAIT_OBJECT_0+lowest);
+       }
        
-       item=wait_item_new(timeout, waitall);
+       if (timeout == 0) {
+               return WAIT_TIMEOUT;
+       }
+       /* Have to wait for some or all handles to become signalled
+        */
+
+       if(timeout!=INFINITE) {
+               _wapi_calc_timeout (&abstime, timeout);
+       }
 
-       /* Sort the handles by type */
-       for(i=0; i<numobjects; i++) {
-               g_ptr_array_add(item->handles[handles[i]->type], handles[i]);
-               g_array_append_val(item->waitindex[handles[i]->type], i);
+       if (alertable && _wapi_thread_apc_pending (current_thread)) {
+               _wapi_thread_dispatch_apc_queue (current_thread);
+               return WAIT_IO_COMPLETION;
        }
        
-       wait=wait_for_item(item, &lowest);
-       wait_dequeue_item(item);
-       wait_item_destroy(item);
+       for (i = 0; i < numobjects; i++) {
+               /* Add a reference, as we need to ensure the handle wont
+                * disappear from under us while we're waiting in the loop
+                * (not lock, as we don't want exclusive access here)
+                */
+               _wapi_handle_ref (handles[i]);
+       }
+
+       while(1) {
+               /* Prod all handles with prewait methods and
+                * special-wait handles that aren't already signalled
+                */
+               for (i = 0; i < numobjects; i++) {
+                       _wapi_handle_ops_prewait (handles[i]);
+               
+                       if (_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE && _wapi_handle_issignalled (handles[i]) == FALSE) {
+                               _wapi_handle_ops_special_wait (handles[i], 0);
+                       }
+               }
+               
+#ifdef DEBUG
+               g_message ("%s: locking signal mutex", __func__);
+#endif
+
+               pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_signal_mutex, NULL);
+               thr_ret = _wapi_handle_lock_signal_mutex ();
+               g_assert (thr_ret == 0);
 
-       if(wait==FALSE) {
-               /* Wait timed out */
-               return(WAIT_TIMEOUT);
+               /* Check the signalled state of handles inside the critical section */
+               if (waitall) {
+                       done = TRUE;
+                       for (i = 0; i < numobjects; i++)
+                               if (!_wapi_handle_issignalled (handles [i]))
+                                       done = FALSE;
+               } else {
+                       done = FALSE;
+                       for (i = 0; i < numobjects; i++)
+                               if (_wapi_handle_issignalled (handles [i]))
+                                       done = TRUE;
+               }
+               
+               if (!done) {
+                       /* Enter the wait */
+                       if (timeout == INFINITE) {
+                               ret = _wapi_handle_wait_signal (poll);
+                       } else {
+                               ret = _wapi_handle_timedwait_signal (&abstime, poll);
+                       }
+               } else {
+                       /* No need to wait */
+                       ret = 0;
+               }
+
+#ifdef DEBUG
+               g_message ("%s: unlocking signal mutex", __func__);
+#endif
+
+               thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
+               g_assert (thr_ret == 0);
+               pthread_cleanup_pop (0);
+               
+               if (alertable && _wapi_thread_apc_pending (current_thread)) {
+                       _wapi_thread_dispatch_apc_queue (current_thread);
+                       retval = WAIT_IO_COMPLETION;
+                       break;
+               }
+       
+               /* Check if everything is signalled, as we can't
+                * guarantee to notice a shared signal even if the
+                * wait timed out
+                */
+               done = test_and_own (numobjects, handles, waitall,
+                                    &count, &lowest);
+               if (done == TRUE) {
+                       retval = WAIT_OBJECT_0+lowest;
+                       break;
+               } else if (ret != 0) {
+                       /* Didn't get all handles, and there was a
+                        * timeout or other error
+                        */
+#ifdef DEBUG
+                       g_message ("%s: wait returned error: %s", __func__,
+                                  strerror (ret));
+#endif
+
+                       if(ret==ETIMEDOUT) {
+                               retval = WAIT_TIMEOUT;
+                       } else {
+                               retval = WAIT_FAILED;
+                       }
+                       break;
+               }
+       }
+
+       for (i = 0; i < numobjects; i++) {
+               /* Unref everything we reffed above */
+               _wapi_handle_unref (handles[i]);
        }
 
-       return(WAIT_OBJECT_0+lowest);
+       return retval;
+}
+
+guint32 WaitForMultipleObjects(guint32 numobjects, gpointer *handles,
+                              gboolean waitall, guint32 timeout)
+{
+       return WaitForMultipleObjectsEx(numobjects, handles, waitall, timeout, FALSE);
 }
+
+/**
+ * WaitForInputIdle:
+ * @handle: a handle to the process to wait for
+ * @timeout: the maximum time in milliseconds to wait for
+ *
+ * This function returns when either @handle process is waiting
+ * for input, or @timeout ms elapses.  If @timeout is zero, the
+ * process state is tested and the function returns immediately.
+ * If @timeout is %INFINITE, the function waits forever.
+ *
+ * Return value: 0 - @handle process is waiting for input.
+ * %WAIT_TIMEOUT - The @timeout interval elapsed and
+ * @handle process is not waiting for input.  %WAIT_FAILED - an error
+ * occurred. 
+ */
+guint32 WaitForInputIdle(gpointer handle, guint32 timeout)
+{
+       /*TODO: Not implemented*/
+       return WAIT_TIMEOUT;
+}
+