Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKatelyn Gadd <kg@luminance.org>2017-12-14 20:14:47 +0300
committerLudovic Henry <luhenry@microsoft.com>2017-12-14 20:14:47 +0300
commit252d61d0558b0bfa3461f3339ce617e2e4c285bb (patch)
tree570839cf1bc4c29f8e07c5e26442929639d05dc9 /mcs/class/corlib
parent5e9081600f0cd4a66e7ff1d274109869b811a5b3 (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.cs99
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)
{