static gboolean test_and_own (guint32 numobjects, gpointer *handles, gboolean waitall, guint32 *count, guint32 *lowest) { gboolean done; int i; MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handles", __func__); 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]); } } MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handles", __func__); _wapi_handle_unlock_handles (numobjects, handles); return(done); }
static gboolean test_and_own (guint32 numobjects, gpointer *handles, gboolean waitall, guint32 *count, guint32 *lowest) { struct handle_cleanup_data cleanup_data; gboolean done; int i; #ifdef DEBUG g_message ("%s: locking handles", __func__); #endif 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 ("%s: unlocking handles", __func__); #endif /* calls the unlock function */ pthread_cleanup_pop (1); return(done); }
/** * 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 * * 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. */ guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout, gboolean alertable) { guint32 ret, waited; int thr_ret; gboolean apc_pending = FALSE; gpointer current_thread = wapi_get_current_thread_handle (); gint64 now, end; if (current_thread == NULL) { SetLastError (ERROR_INVALID_HANDLE); return(WAIT_FAILED); } if (handle == _WAPI_THREAD_CURRENT) { handle = wapi_get_current_thread_handle (); 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) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p can't be waited for", __func__, handle); return(WAIT_FAILED); } _wapi_handle_ops_prewait (handle); if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p has special wait", __func__, handle); ret = _wapi_handle_ops_special_wait (handle, timeout, alertable); if (alertable && _wapi_thread_cur_apc_pending ()) ret = WAIT_IO_COMPLETION; return ret; } MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, handle); thr_ret = _wapi_handle_lock_handle (handle); g_assert (thr_ret == 0); if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN) == TRUE) { if (own_if_owned (handle) == TRUE) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__, handle); ret = WAIT_OBJECT_0; goto done; } } if (own_if_signalled (handle) == TRUE) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__, handle); ret=WAIT_OBJECT_0; goto done; } if (timeout == 0) { ret = WAIT_TIMEOUT; goto done; } if (timeout != INFINITE) end = mono_100ns_ticks () + timeout * 1000 * 10; do { /* Check before waiting on the condition, just in case */ _wapi_handle_ops_prewait (handle); if (own_if_signalled (handle)) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__, handle); ret = WAIT_OBJECT_0; goto done; } if (timeout == INFINITE) { waited = _wapi_handle_timedwait_signal_handle (handle, INFINITE, alertable, FALSE, &apc_pending); } else { now = mono_100ns_ticks (); if (end < now) { ret = WAIT_TIMEOUT; goto done; } waited = _wapi_handle_timedwait_signal_handle (handle, (end - now) / 10 / 1000, alertable, FALSE, &apc_pending); } 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)) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__, handle); ret=WAIT_OBJECT_0; goto done; } /* Better luck next time */ } } while(waited == 0 && !apc_pending); /* Timeout or other error */ MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, handle, strerror (waited)); ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT; done: MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, handle); thr_ret = _wapi_handle_unlock_handle (handle); g_assert (thr_ret == 0); return(ret); }
/** * 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 * * 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. */ guint32 WaitForSingleObjectEx(gpointer handle, 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 (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 ("%s: handle %p can't be waited for", __func__, handle); #endif return(WAIT_FAILED); } _wapi_handle_ops_prewait (handle); if (_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_SPECIAL_WAIT) == TRUE) { #ifdef DEBUG g_message ("%s: handle %p has special wait", __func__, handle); #endif ret = _wapi_handle_ops_special_wait (handle, timeout); 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 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle, handle); thr_ret = _wapi_handle_lock_handle (handle); g_assert (thr_ret == 0); 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; } } if (alertable && _wapi_thread_apc_pending (current_thread)) { apc_pending = TRUE; ret = WAIT_IO_COMPLETION; goto done; } if (own_if_signalled (handle) == TRUE) { #ifdef DEBUG g_message ("%s: handle %p already signalled", __func__, handle); #endif 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 */ _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 (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); /* Timeout or other error */ #ifdef DEBUG g_message ("%s: wait on handle %p error: %s", __func__, handle, strerror (waited)); #endif ret = WAIT_TIMEOUT; done: #ifdef DEBUG g_message ("%s: unlocking handle %p", __func__, handle); #endif thr_ret = _wapi_handle_unlock_handle (handle); g_assert (thr_ret == 0); pthread_cleanup_pop (0); check_pending: if (apc_pending) { _wapi_thread_dispatch_apc_queue (current_thread); ret = WAIT_IO_COMPLETION; } return(ret); }
/** * 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 = GetCurrentThread (); if (_wapi_handle_test_capabilities (signal_handle, WAPI_HANDLE_CAP_SIGNAL)==FALSE) { return(WAIT_FAILED); } if (_wapi_handle_test_capabilities (wait, WAPI_HANDLE_CAP_WAIT)==FALSE) { return(WAIT_FAILED); } 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); } #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; } } if (alertable && _wapi_thread_apc_pending (current_thread)) { apc_pending = TRUE; ret = WAIT_IO_COMPLETION; goto done; } 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); } do { /* Check before waiting on the condition, just in case */ if (own_if_signalled (wait)) { #ifdef DEBUG g_message ("%s: handle %p signalled", __func__, wait); #endif ret = WAIT_OBJECT_0; goto done; } if (timeout == INFINITE) { waited = _wapi_handle_wait_signal_handle (wait); } else { waited = _wapi_handle_timedwait_signal_handle (wait, &abstime); } 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); /* Timeout or other error */ #ifdef DEBUG g_message ("%s: wait on handle %p error: %s", __func__, wait, strerror (ret)); #endif ret = WAIT_TIMEOUT; 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); }
/** * 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 = 0, waited; int thr_ret; gboolean apc_pending = FALSE; gpointer current_thread = wapi_get_current_thread_handle (); gint64 wait_start, timeout_in_ticks; if (current_thread == NULL) { SetLastError (ERROR_INVALID_HANDLE); return(WAIT_FAILED); } if (signal_handle == _WAPI_THREAD_CURRENT) { signal_handle = wapi_get_current_thread_handle (); if (signal_handle == NULL) { SetLastError (ERROR_INVALID_HANDLE); return(WAIT_FAILED); } } if (wait == _WAPI_THREAD_CURRENT) { wait = wapi_get_current_thread_handle (); 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); } if (mono_w32handle_test_capabilities (signal_handle, MONO_W32HANDLE_CAP_SIGNAL)==FALSE) { return(WAIT_FAILED); } if (mono_w32handle_test_capabilities (wait, MONO_W32HANDLE_CAP_WAIT)==FALSE) { return(WAIT_FAILED); } mono_w32handle_ops_prewait (wait); if (mono_w32handle_test_capabilities (wait, MONO_W32HANDLE_CAP_SPECIAL_WAIT) == TRUE) { g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait); return (WAIT_FAILED); } MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: locking handle %p", __func__, wait); thr_ret = mono_w32handle_lock_handle (wait); g_assert (thr_ret == 0); mono_w32handle_ops_signal (signal_handle); if (mono_w32handle_test_capabilities (wait, MONO_W32HANDLE_CAP_OWN)==TRUE) { if (own_if_owned (wait)) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already owned", __func__, wait); ret = WAIT_OBJECT_0; goto done; } } if (own_if_signalled (wait)) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p already signalled", __func__, wait); ret = WAIT_OBJECT_0; goto done; } if (timeout != INFINITE) { wait_start = mono_100ns_ticks (); timeout_in_ticks = (gint64)timeout * 10 * 1000; //can't overflow as timeout is 32bits } do { /* Check before waiting on the condition, just in case */ mono_w32handle_ops_prewait (wait); if (own_if_signalled (wait)) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__, wait); ret = WAIT_OBJECT_0; goto done; } if (timeout == INFINITE) { waited = mono_w32handle_timedwait_signal_handle (wait, INFINITE, FALSE, alertable ? &apc_pending : NULL); } else { gint64 elapsed = mono_100ns_ticks () - wait_start; if (elapsed >= timeout_in_ticks) { ret = WAIT_TIMEOUT; goto done; } waited = mono_w32handle_timedwait_signal_handle (wait, (timeout_in_ticks - elapsed) / 10 / 1000, FALSE, alertable ? &apc_pending : NULL); } 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)) { MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle %p signalled", __func__, wait); ret = WAIT_OBJECT_0; goto done; } /* Better luck next time */ } } while(waited == 0 && !apc_pending); /* Timeout or other error */ MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: wait on handle %p error: %s", __func__, wait, strerror (ret)); ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT; done: MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, wait); thr_ret = mono_w32handle_unlock_handle (wait); g_assert (thr_ret == 0); return(ret); }