Browse Source

Merge pull request #4242 from AvaloniaUI/win32-sync-context-fix

Use custom WaitForMultipleObjectsEx to prevent STA/COM reentrancy
pull/4300/head
Nikita Tsukanov 6 years ago
committed by GitHub
parent
commit
5ed130e07d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
  2. 2
      src/Avalonia.FreeDesktop/DBusHelper.cs
  3. 17
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  4. 15
      src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs
  5. 1
      src/Windows/Avalonia.Win32/Win32Platform.cs

27
src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs

@ -1,3 +1,5 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Threading;
namespace Avalonia.Threading
@ -7,6 +9,20 @@ namespace Avalonia.Threading
/// </summary>
public class AvaloniaSynchronizationContext : SynchronizationContext
{
public interface INonPumpingPlatformWaitProvider
{
int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
}
private readonly INonPumpingPlatformWaitProvider _waitProvider;
public AvaloniaSynchronizationContext(INonPumpingPlatformWaitProvider waitProvider)
{
_waitProvider = waitProvider;
if (_waitProvider != null)
SetWaitNotificationRequired();
}
/// <summary>
/// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer.
/// </summary>
@ -22,7 +38,8 @@ namespace Avalonia.Threading
return;
}
SetSynchronizationContext(new AvaloniaSynchronizationContext());
SetSynchronizationContext(new AvaloniaSynchronizationContext(AvaloniaLocator.Current
.GetService<INonPumpingPlatformWaitProvider>()));
}
/// <inheritdoc/>
@ -39,5 +56,13 @@ namespace Avalonia.Threading
else
Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait();
}
[PrePrepareMethod]
public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
{
if (_waitProvider != null)
return _waitProvider.Wait(waitHandles, waitAll, millisecondsTimeout);
return base.Wait(waitHandles, waitAll, millisecondsTimeout);
}
}
}

2
src/Avalonia.FreeDesktop/DBusHelper.cs

@ -43,7 +43,7 @@ namespace Avalonia.FreeDesktop
public void Initialized()
{
lock (_lock)
_ctx = new AvaloniaSynchronizationContext();
_ctx = new AvaloniaSynchronizationContext(null);
}
}
public static Connection Connection { get; private set; }

17
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -1382,6 +1383,22 @@ namespace Avalonia.Win32.Interop
throw new Exception("RtlGetVersion failed!");
}
}
[DllImport("kernel32", EntryPoint="WaitForMultipleObjectsEx", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int IntWaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable);
public const int WAIT_FAILED = unchecked((int)0xFFFFFFFF);
internal static int WaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable)
{
int result = IntWaitForMultipleObjectsEx(nCount, pHandles, bWaitAll, dwMilliseconds, bAlertable);
if(result == WAIT_FAILED)
{
throw new Win32Exception();
}
return result;
}
[DllImport("user32.dll")]
internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);

15
src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs

@ -0,0 +1,15 @@
using System;
using Avalonia.Threading;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32
{
internal class NonPumpingWaitProvider : AvaloniaSynchronizationContext.INonPumpingPlatformWaitProvider
{
public int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
{
return UnmanagedMethods.WaitForMultipleObjectsEx(waitHandles.Length, waitHandles, waitAll,
millisecondsTimeout, false);
}
}
}

1
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -93,6 +93,7 @@ namespace Avalonia.Win32
.Bind<IWindowingPlatform>().ToConstant(s_instance)
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IPlatformIconLoader>().ToConstant(s_instance)
.Bind<AvaloniaSynchronizationContext.INonPumpingPlatformWaitProvider>().ToConstant(new NonPumpingWaitProvider())
.Bind<IMountedVolumeInfoProvider>().ToConstant(new WindowsMountedVolumeInfoProvider());
if (options.AllowEglInitialization)

Loading…
Cancel
Save