Browse Source

Win32 dispatcher fixes

pull/10691/head
Nikita Tsukanov 3 years ago
parent
commit
fe4945d395
  1. 35
      src/Avalonia.Base/Threading/Dispatcher.Timers.cs
  2. 2
      src/Avalonia.Base/Threading/DispatcherTimer.cs
  3. 2
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  4. 30
      src/Windows/Avalonia.Win32/Win32DispatcherImpl.cs
  5. 29
      src/Windows/Avalonia.Win32/Win32Platform.cs

35
src/Avalonia.Base/Threading/Dispatcher.Timers.cs

@ -17,23 +17,21 @@ public partial class Dispatcher
private void UpdateOSTimer()
{
lock (InstanceLock)
{
var nextDueTime =
(_dueTimeForTimers.HasValue && _dueTimeForBackgroundProcessing.HasValue)
? Math.Min(_dueTimeForTimers.Value, _dueTimeForBackgroundProcessing.Value)
: _dueTimeForTimers ?? _dueTimeForBackgroundProcessing;
if(_osTimerSetTo == nextDueTime)
return;
_impl.UpdateTimer(_osTimerSetTo = nextDueTime);
}
VerifyAccess();
var nextDueTime =
(_dueTimeForTimers.HasValue && _dueTimeForBackgroundProcessing.HasValue) ?
Math.Min(_dueTimeForTimers.Value, _dueTimeForBackgroundProcessing.Value) :
_dueTimeForTimers ?? _dueTimeForBackgroundProcessing;
if (_osTimerSetTo == nextDueTime)
return;
_impl.UpdateTimer(_osTimerSetTo = nextDueTime);
}
internal void UpdateOSTimerForTimers()
internal void RescheduleTimers()
{
if (!CheckAccess())
{
Post(UpdateOSTimerForTimers, DispatcherPriority.Send);
Post(RescheduleTimers, DispatcherPriority.Send);
return;
}
@ -89,7 +87,7 @@ public partial class Dispatcher
}
}
UpdateOSTimerForTimers();
RescheduleTimers();
}
internal void RemoveTimer(DispatcherTimer timer)
@ -103,15 +101,18 @@ public partial class Dispatcher
}
}
UpdateOSTimerForTimers();
RescheduleTimers();
}
private void OnOSTimer()
{
_impl.UpdateTimer(null);
_osTimerSetTo = null;
bool needToPromoteTimers = false;
bool needToProcessQueue = false;
lock (InstanceLock)
{
_impl.UpdateTimer(_osTimerSetTo = null);
needToPromoteTimers = _dueTimeForTimers.HasValue && _dueTimeForTimers.Value <= Clock.TickCount;
if (needToPromoteTimers)
_dueTimeForTimers = null;
@ -119,13 +120,13 @@ public partial class Dispatcher
_dueTimeForBackgroundProcessing.Value <= Clock.TickCount;
if (needToProcessQueue)
_dueTimeForBackgroundProcessing = null;
UpdateOSTimer();
}
if (needToPromoteTimers)
PromoteTimers();
if (needToProcessQueue)
ExecuteJobsCore();
UpdateOSTimer();
}
internal void PromoteTimers()
@ -195,10 +196,10 @@ public partial class Dispatcher
}
finally
{
UpdateOSTimerForTimers();
RescheduleTimers();
}
}
internal static List<DispatcherTimer> SnapshotTimersForUnitTests() =>
s_uiThread!._timers.ToList();
}
}

2
src/Avalonia.Base/Threading/DispatcherTimer.cs

@ -132,7 +132,7 @@ public partial class DispatcherTimer
if (updateOSTimer)
{
_dispatcher.UpdateOSTimerForTimers();
_dispatcher.RescheduleTimers();
}
}
}

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

@ -1796,7 +1796,7 @@ namespace Avalonia.Win32.Interop
QS_SENDMESSAGE = 0x0040,
QS_HOTKEY = 0x0080,
QS_ALLPOSTMESSAGE = 0x0100,
QS_EVENT = 0x0200,
QS_EVENT = 0x02000,
QS_MOUSE = QS_MOUSEMOVE | QS_MOUSEBUTTON,
QS_INPUT = QS_MOUSE | QS_KEY,
QS_ALLEVENTS = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY,

30
src/Windows/Avalonia.Win32/Win32DispatcherImpl.cs

@ -10,13 +10,10 @@ internal class Win32DispatcherImpl : IControlledDispatcherImpl, IDispatcherClock
{
private readonly IntPtr _messageWindow;
private static Thread? s_uiThread;
private IntPtr? _timerHandle;
private readonly TimerProc _timerDelegate;
public Win32DispatcherImpl(IntPtr messageWindow)
{
_messageWindow = messageWindow;
s_uiThread = Thread.CurrentThread;
_timerDelegate = TimerProc;
}
public bool CurrentThreadIsLoopThread => s_uiThread == Thread.CurrentThread;
@ -37,26 +34,27 @@ internal class Win32DispatcherImpl : IControlledDispatcherImpl, IDispatcherClock
public event Action? Signaled;
public event Action? Timer;
void TimerProc(IntPtr hWnd, uint uMsg, IntPtr nIdEvent, uint dwTime) => Timer?.Invoke();
public void FireTimer() => Timer?.Invoke();
public void UpdateTimer(int? dueTimeInMs)
{
if (_timerHandle.HasValue)
KillTimer(IntPtr.Zero, _timerHandle.Value);
if (dueTimeInMs == null)
return;
var interval = (uint)Math.Max(1, TickCount - dueTimeInMs.Value);
_timerHandle = SetTimer(
IntPtr.Zero,
IntPtr.Zero,
interval,
_timerDelegate);
{
KillTimer(_messageWindow, (IntPtr)Win32Platform.TIMERID_DISPATCHER);
}
else
{
var interval = (uint)Math.Max(1, TickCount - dueTimeInMs.Value);
SetTimer(
_messageWindow,
(IntPtr)Win32Platform.TIMERID_DISPATCHER,
interval,
null!);
}
}
public bool CanQueryPendingInput => true;
public bool HasPendingInput
{
get

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

@ -112,6 +112,7 @@ namespace Avalonia.Win32
private static readonly Win32Platform s_instance = new();
private static Win32PlatformOptions? s_options;
private static Compositor? s_compositor;
internal const int TIMERID_DISPATCHER = 1;
private WndProc? _wndProcDelegate;
private IntPtr _hwnd;
@ -182,27 +183,7 @@ namespace Avalonia.Win32
s_compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), platformGraphics);
}
public bool HasMessages()
{
return PeekMessage(out _, IntPtr.Zero, 0, 0, 0);
}
public void ProcessMessage()
{
if (GetMessage(out var msg, IntPtr.Zero, 0, 0) > -1)
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
else
{
Logging.Logger.TryGet(Logging.LogEventLevel.Error, Logging.LogArea.Win32Platform)
?.Log(this, "Unmanaged error in {0}. Error Code: {1}", nameof(ProcessMessage), Marshal.GetLastWin32Error());
}
}
public event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested;
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]
@ -238,6 +219,12 @@ namespace Avalonia.Win32
win32PlatformSettings.OnColorValuesChanged();
}
}
if (msg == (uint)WindowsMessage.WM_TIMER)
{
if (wParam == (IntPtr)TIMERID_DISPATCHER)
_dispatcher?.FireTimer();
}
TrayIconImpl.ProcWnd(hWnd, msg, wParam, lParam);

Loading…
Cancel
Save