diff options
author | Katelyn Gadd <kg@luminance.org> | 2017-12-14 20:14:47 +0300 |
---|---|---|
committer | Ludovic Henry <luhenry@microsoft.com> | 2017-12-14 20:14:47 +0300 |
commit | 252d61d0558b0bfa3461f3339ce617e2e4c285bb (patch) | |
tree | 570839cf1bc4c29f8e07c5e26442929639d05dc9 /mcs/class/corlib | |
parent | 5e9081600f0cd4a66e7ff1d274109869b811a5b3 (diff) |
Fix bug 60568: SynchronizationContext.Wait not implemented or invoked by runtime (#6062)
* Checkpoint: Implement SynchronizationContext.Wait
* Add missing check for null synchronization context
* Update bug 60568 fix to cover WaitAll and add test cases
* Add missing file
Diffstat (limited to 'mcs/class/corlib')
-rw-r--r-- | mcs/class/corlib/System.Threading/WaitHandle.cs | 99 |
1 files changed, 68 insertions, 31 deletions
diff --git a/mcs/class/corlib/System.Threading/WaitHandle.cs b/mcs/class/corlib/System.Threading/WaitHandle.cs index 0e16f046e80..4708dbd3cb7 100644 --- a/mcs/class/corlib/System.Threading/WaitHandle.cs +++ b/mcs/class/corlib/System.Threading/WaitHandle.cs @@ -47,12 +47,63 @@ namespace System.Threading internal const int MaxWaitHandles = 64; + // We rely on the reference source implementation of WaitHandle, and it delegates to a function named + // WaitOneNative to perform the actual operation of waiting on a handle. + // This native operation actually has to call back into managed code and invoke .Wait + // on the current SynchronizationContext. As such, our implementation of this "native" method + // is actually managed code, and the real native icall being used is Wait_internal. + static int WaitOneNative (SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext) + { + bool release = false; + var context = SynchronizationContext.Current; + try { + waitableSafeHandle.DangerousAddRef (ref release); + +#if !DISABLE_REMOTING + if (exitContext) + SynchronizationAttribute.ExitContext (); +#endif + + // HACK: Documentation (and public posts by experts like Joe Duffy) suggests that + // users must first call SetWaitNotificationRequired to flag that a given synchronization + // context overrides .Wait. Because invoking the Wait method is somewhat expensive, we use + // the notification-required flag to determine whether or not we should invoke the managed + // wait method. + // Another option would be to check whether this context uses the default Wait implementation, + // but I don't know of a cheap way to do this that handles derived types correctly. + // If the thread does not have a synchronization context set at all, we can safely just + // jump directly to invoking Wait_internal. + if ((context != null) && context.IsWaitNotificationRequired ()) { + return context.Wait ( + new IntPtr[] { waitableSafeHandle.DangerousGetHandle () }, + false, + (int)millisecondsTimeout + ); + } else { + unsafe { + IntPtr handle = waitableSafeHandle.DangerousGetHandle (); + return Wait_internal (&handle, 1, false, (int)millisecondsTimeout); + } + } + } finally { + if (release) + waitableSafeHandle.DangerousRelease (); + +#if !DISABLE_REMOTING + if (exitContext) + SynchronizationAttribute.EnterContext (); +#endif + } + + } + static int WaitMultiple(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext, bool WaitAll) { if (waitHandles.Length > MaxWaitHandles) return WAIT_FAILED; int release_last = -1; + var context = SynchronizationContext.Current; try { #if !DISABLE_REMOTING @@ -70,13 +121,25 @@ namespace System.Threading } } - unsafe { - IntPtr* handles = stackalloc IntPtr[waitHandles.Length]; - + if ((context != null) && context.IsWaitNotificationRequired ()) { + IntPtr[] handles = new IntPtr[waitHandles.Length]; for (int i = 0; i < waitHandles.Length; ++i) handles[i] = waitHandles[i].SafeWaitHandle.DangerousGetHandle (); - return Wait_internal(handles, waitHandles.Length, WaitAll, millisecondsTimeout); + return context.Wait ( + handles, + false, + (int)millisecondsTimeout + ); + } else { + unsafe { + IntPtr* handles = stackalloc IntPtr[waitHandles.Length]; + + for (int i = 0; i < waitHandles.Length; ++i) + handles[i] = waitHandles[i].SafeWaitHandle.DangerousGetHandle (); + + return Wait_internal (handles, waitHandles.Length, WaitAll, millisecondsTimeout); + } } } finally { for (int i = release_last; i >= 0; --i) { @@ -90,34 +153,8 @@ namespace System.Threading } } - static int WaitOneNative (SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext) - { - bool release = false; - try { -#if !DISABLE_REMOTING - if (exitContext) - SynchronizationAttribute.ExitContext (); -#endif - - waitableSafeHandle.DangerousAddRef (ref release); - - unsafe { - IntPtr handle = waitableSafeHandle.DangerousGetHandle(); - return Wait_internal(&handle, 1, false, (int)millisecondsTimeout); - } - } finally { - if (release) - waitableSafeHandle.DangerousRelease (); - -#if !DISABLE_REMOTING - if (exitContext) - SynchronizationAttribute.EnterContext (); -#endif - } - } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - unsafe static extern int Wait_internal(IntPtr* handles, int numHandles, bool waitAll, int ms); + internal unsafe static extern int Wait_internal(IntPtr* handles, int numHandles, bool waitAll, int ms); static int SignalAndWaitOne (SafeWaitHandle waitHandleToSignal,SafeWaitHandle waitHandleToWaitOn, int millisecondsTimeout, bool hasThreadAffinity, bool exitContext) { |