+/*
+ * 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;
+ _wapi_handle_ops_prewait (wait);
- for(i=0; i<WAPI_HANDLE_COUNT; i++) {
- new->handles[i]=g_ptr_array_new();
+ 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.
- */
-static gboolean wait_for_item(WaitQueueItem *item)
-{
- 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 */
}
- }
- pthread_mutex_unlock(&item->mutex);
+ } while(waited == 0 && !apc_pending);
- return(ret);
-}
+ /* Timeout or other error */
+#ifdef DEBUG
+ g_message ("%s: wait on handle %p error: %s", __func__, wait,
+ strerror (ret));
+#endif
-static gboolean wait_dequeue_item(WaitQueueItem *item)
-{
- gboolean ret;
+ ret = WAIT_TIMEOUT;
- g_assert(WaitQueue!=NULL);
-
- pthread_mutex_lock(&wait_monitor_mutex);
- ret=g_ptr_array_remove_fast(WaitQueue, item);
- pthread_mutex_unlock(&wait_monitor_mutex);
+done:
+
+#ifdef DEBUG
+ g_message ("%s: unlocking handle %p", __func__, wait);
+#endif
+
+ 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);
- }
-}
+ 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);
- }
-
- if(timeout==0) {
- /* Just poll the object */
#ifdef DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": Polling");
+ g_message ("%s: locking handles", __func__);
#endif
-
- if(handle->signalled==TRUE) {
- return(WAIT_OBJECT_0);
+ 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 {
- return(WAIT_TIMEOUT);
+ own_if_signalled (handles[*lowest]);
}
}
- wait=handle->ops->wait(handle, timeout);
- if(wait==TRUE) {
- /* Object signalled before timeout expired */
#ifdef DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": Object %p signalled",
- handle);
+ g_message ("%s: unlocking handles", __func__);
#endif
- return(WAIT_OBJECT_0);
- } else {
-#ifdef DEBUG
- g_message(G_GNUC_PRETTY_FUNCTION ": Object %p wait timed out",
- handle);
-#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.
* 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
* 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 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);
+ }
- pthread_once(&wait_once, wait_init);
-
- 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);
}
-
- item=wait_item_new(timeout, waitall);
- /* Sort the handles by type */
- for(i=0; i<numobjects; i++) {
- g_ptr_array_add(item->handles[handles[i]->type], handles[i]);
+ 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);
}
- wait=wait_for_item(item);
- wait_dequeue_item(item);
- wait_item_destroy(item);
+ if (timeout == 0) {
+ return WAIT_TIMEOUT;
+ }
+ /* Have to wait for some or all handles to become signalled
+ */
- if(wait==FALSE) {
- /* Wait timed out */
- return(WAIT_TIMEOUT);
+ if(timeout!=INFINITE) {
+ _wapi_calc_timeout (&abstime, timeout);
}
- for(i=0; i<numobjects; i++) {
- if(handles[i]->signalled==TRUE) {
- return(WAIT_OBJECT_0+i);
- }
+ if (alertable && _wapi_thread_apc_pending (current_thread)) {
+ _wapi_thread_dispatch_apc_queue (current_thread);
+ return WAIT_IO_COMPLETION;
}
- /* Oh dear. Something returned from the wait, but nothing
- * appears to be signalled.
- */
- return(WAIT_FAILED);
+ 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);
+
+ /* 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 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;
+}
+