Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / utils / mono-os-wait-win32.c
index 5d18a717588d8ee49348fe1b188bbd7a8cdcebe9..a302d973295d80d5b3cd95cfbc81b8daced08a8a 100644 (file)
@@ -1,46 +1,53 @@
+/**
+* \file
+* Win32 OS wait wrappers and interrupt/abort APC handling.
+*
+* Author:
+*   Johan Lorensson (lateralusx.github@gmail.com)
+*
+* Licensed under the MIT license. See LICENSE file in the project root for full license information.
+*/
+
 #include <mono/utils/mono-os-wait.h>
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/mono-threads-debug.h>
 
-#define THREAD_WAIT_INFO_CLEARED 0
-#define THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT 1
-#define THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT 2
-#define THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT 4
+enum ThreadWaitInfo {
+       THREAD_WAIT_INFO_CLEARED = 0,
+       THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT = 1 << 0,
+       THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT = 1 << 1,
+       THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT = 1 << 2
+};
 
 static inline void
-reqeust_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid)
+request_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid)
 {
        /*
-       * On Windows platforms the async interrupt/abort requests might need to process
-       * APC on target thread before the thread can return back from OS wait calls
-       * and complete the mono interrupt/abort request. In such cases, just keep on
-       * queuing APC over again again will flood the APC queue preventing the target thread
-       * to return from it's alertable OS wait call. This check makes sure not to issue an APC
-       * as long as there is a pending requests in requested APC slot for the targeted thread.
-       * NOTE, this code will executed regardless if thread is currently in an alertable wait or not.
-       * This is done to prevent races between interrupt/abort occurring just before thread enters an
-       * alertable wait. Threads entering waits already need to handle WAIT_IO_COMPLETION scenarios and
-       * if that happens due to a previous pending interrupt/abort APC, the calling thread should already
-       * have logic to restart the alertable wait operation.
+       * On Windows platforms, an async interrupt/abort request queues an APC
+       * that needs to be processed by target thread before it can return from an
+       * alertable OS wait call and complete the mono interrupt/abort request.
+       * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread
+       * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete
+       * This check makes sure that only one APC per type gets queued, preventing potential flooding
+       * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in
+       * an alertable wait or not. This is done to prevent races between interrupt/abort requests and
+       * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION
+       * return scenarios and restart the alertable wait operation if needed or take other actions
+       * (like service the interrupt/abort request).
        */
        MonoThreadInfo *info = (MonoThreadInfo *)thread_info;
-       gboolean queue_apc = FALSE;
+       gint32 old_wait_info, new_wait_info;
 
-       while (!queue_apc) {
-               gint32 old_wait_info = InterlockedRead (&info->thread_wait_info);
+       do {
+               old_wait_info = InterlockedRead (&info->thread_wait_info);
                if (old_wait_info & pending_apc_slot)
-                       break;
+                       return;
 
-               gint32 new_wait_info = old_wait_info | pending_apc_slot;
-               if (InterlockedCompareExchange (&info->thread_wait_info, new_wait_info, old_wait_info) == old_wait_info) {
-                       queue_apc = TRUE;
-               }
-       }
+               new_wait_info = old_wait_info | pending_apc_slot;
+       } while (InterlockedCompareExchange (&info->thread_wait_info, new_wait_info, old_wait_info) != old_wait_info);
 
-       if (queue_apc == TRUE) {
-               THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid);
-               QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL);
-       }
+       THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid);
+       QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL);
 }
 
 static void CALLBACK
@@ -52,7 +59,7 @@ interrupt_apc (ULONG_PTR param)
 void
 mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
 {
-       reqeust_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT, interrupt_apc, tid);
+       request_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT, interrupt_apc, tid);
 }
 
 static void CALLBACK
@@ -64,7 +71,7 @@ abort_apc (ULONG_PTR param)
 void
 mono_win32_abort_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
 {
-       reqeust_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT, abort_apc, tid);
+       request_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT, abort_apc, tid);
 }
 
 static inline void
@@ -95,7 +102,7 @@ mono_win32_sleep_ex (DWORD timeout, BOOL alertable)
        result = SleepEx (timeout, alertable);
 
        // NOTE, leave_alertable_wait should not affect GetLastError but
-       // if changed, last error need to be preserved and reset before returning.
+       // if changed, GetLastError needs to be preserved and reset before returning.
        if (alertable && info) {
                leave_alertable_wait (info);
        }
@@ -116,7 +123,7 @@ mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertab
        result = WaitForSingleObjectEx (handle, timeout, alertable);
 
        // NOTE, leave_alertable_wait should not affect GetLastError but
-       // if changed, last error need to be preserved and reset before returning.
+       // if changed, GetLastError needs to be preserved and reset before returning.
        if (alertable && info) {
                leave_alertable_wait (info);
        }
@@ -137,7 +144,7 @@ mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOO
        result = WaitForMultipleObjectsEx (count, handles, waitAll, timeout, alertable);
 
        // NOTE, leave_alertable_wait should not affect GetLastError but
-       // if changed, last error need to be preserved and reset before returning.
+       // if changed, GetLastError needs to be preserved and reset before returning.
        if (alertable && info) {
                leave_alertable_wait (info);
        }
@@ -155,10 +162,10 @@ mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout
                enter_alertable_wait (info);
        }
 
-       result = SignalObjectAndWait (toSignal, towlower, timeout, alertable);
+       result = SignalObjectAndWait (toSignal, toWait, timeout, alertable);
 
        // NOTE, leave_alertable_wait should not affect GetLastError but
-       // if changed, last error need to be preserved and reset before returning.
+       // if changed, GetLastError needs to be preserved and reset before returning.
        if (alertable && info) {
                leave_alertable_wait (info);
        }
@@ -180,7 +187,7 @@ mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles,
        result = MsgWaitForMultipleObjectsEx (count, handles, timeout, wakeMask, flags);
 
        // NOTE, leave_alertable_wait should not affect GetLastError but
-       // if changed, last error need to be preserved and reset before returning.
+       // if changed, GetLastError needs to be preserved and reset before returning.
        if (alertable && info) {
                leave_alertable_wait (info);
        }
@@ -201,7 +208,7 @@ mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handle
        result = WSAWaitForMultipleEvents (count, handles, waitAll, timeout, alertable);
 
        // NOTE, leave_alertable_wait should not affect GetLastError but
-       // if changed, last error need to be preserved and reset before returning.
+       // if changed, GetLastError needs to be preserved and reset before returning.
        if (alertable && info) {
                leave_alertable_wait (info);
        }