13 changed files with 149 additions and 210 deletions
@ -1,92 +0,0 @@ |
|||||
using System; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Threading; |
|
||||
using Avalonia.Metadata; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Threading; |
|
||||
|
|
||||
namespace Avalonia.Controls.Platform |
|
||||
{ |
|
||||
[Unstable] |
|
||||
public class InternalPlatformThreadingInterface : IPlatformThreadingInterface |
|
||||
{ |
|
||||
public InternalPlatformThreadingInterface() |
|
||||
{ |
|
||||
TlsCurrentThreadIsLoopThread = true; |
|
||||
} |
|
||||
|
|
||||
private readonly AutoResetEvent _signaled = new AutoResetEvent(false); |
|
||||
|
|
||||
|
|
||||
public void RunLoop(CancellationToken cancellationToken) |
|
||||
{ |
|
||||
var handles = new[] { _signaled, cancellationToken.WaitHandle }; |
|
||||
|
|
||||
while (!cancellationToken.IsCancellationRequested) |
|
||||
{ |
|
||||
Signaled?.Invoke(null); |
|
||||
WaitHandle.WaitAny(handles); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
class TimerImpl : IDisposable |
|
||||
{ |
|
||||
private readonly DispatcherPriority _priority; |
|
||||
private readonly TimeSpan _interval; |
|
||||
private readonly Action _tick; |
|
||||
private Timer? _timer; |
|
||||
private GCHandle _handle; |
|
||||
|
|
||||
public TimerImpl(DispatcherPriority priority, TimeSpan interval, Action tick) |
|
||||
{ |
|
||||
_priority = priority; |
|
||||
_interval = interval; |
|
||||
_tick = tick; |
|
||||
_timer = new Timer(OnTimer, null, interval, Timeout.InfiniteTimeSpan); |
|
||||
_handle = GCHandle.Alloc(_timer); |
|
||||
} |
|
||||
|
|
||||
private void OnTimer(object? state) |
|
||||
{ |
|
||||
if (_timer == null) |
|
||||
return; |
|
||||
Dispatcher.UIThread.Post(() => |
|
||||
{ |
|
||||
|
|
||||
if (_timer == null) |
|
||||
return; |
|
||||
_tick(); |
|
||||
_timer?.Change(_interval, Timeout.InfiniteTimeSpan); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
_handle.Free(); |
|
||||
_timer?.Dispose(); |
|
||||
_timer = null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) |
|
||||
{ |
|
||||
return new TimerImpl(priority, interval, tick); |
|
||||
} |
|
||||
|
|
||||
public void Signal(DispatcherPriority prio) |
|
||||
{ |
|
||||
_signaled.Set(); |
|
||||
} |
|
||||
|
|
||||
[ThreadStatic] private static bool TlsCurrentThreadIsLoopThread; |
|
||||
|
|
||||
public bool CurrentThreadIsLoopThread => TlsCurrentThreadIsLoopThread; |
|
||||
public event Action<DispatcherPriority?>? Signaled; |
|
||||
#pragma warning disable CS0067
|
|
||||
public event Action<TimeSpan>? Tick; |
|
||||
#pragma warning restore CS0067
|
|
||||
|
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,108 @@ |
|||||
|
using System; |
||||
|
using System.Diagnostics; |
||||
|
using System.Threading; |
||||
|
using Avalonia.Metadata; |
||||
|
using Avalonia.Threading; |
||||
|
|
||||
|
namespace Avalonia.Controls.Platform; |
||||
|
|
||||
|
[Unstable] |
||||
|
public class ManagedDispatcherImpl : IControlledDispatcherImpl |
||||
|
{ |
||||
|
private readonly IManagedDispatcherInputProvider? _inputProvider; |
||||
|
private readonly AutoResetEvent _wakeup = new(false); |
||||
|
private bool _signaled; |
||||
|
private readonly object _lock = new(); |
||||
|
private readonly Stopwatch _clock = Stopwatch.StartNew(); |
||||
|
private TimeSpan? _nextTimer; |
||||
|
private readonly Thread _loopThread = Thread.CurrentThread; |
||||
|
|
||||
|
public interface IManagedDispatcherInputProvider |
||||
|
{ |
||||
|
bool HasInput { get; } |
||||
|
void DispatchNextInputEvent(); |
||||
|
} |
||||
|
|
||||
|
public ManagedDispatcherImpl(IManagedDispatcherInputProvider? inputProvider) |
||||
|
{ |
||||
|
_inputProvider = inputProvider; |
||||
|
} |
||||
|
|
||||
|
public bool CurrentThreadIsLoopThread => _loopThread == Thread.CurrentThread; |
||||
|
public void Signal() |
||||
|
{ |
||||
|
lock (_lock) |
||||
|
{ |
||||
|
_signaled = true; |
||||
|
_wakeup.Set(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public event Action? Signaled; |
||||
|
public event Action? Timer; |
||||
|
public void UpdateTimer(int? dueTimeInTicks) |
||||
|
{ |
||||
|
lock (_lock) |
||||
|
{ |
||||
|
_nextTimer = dueTimeInTicks == null |
||||
|
? null |
||||
|
: _clock.Elapsed + TimeSpan.FromMilliseconds(dueTimeInTicks.Value); |
||||
|
if (!CurrentThreadIsLoopThread) |
||||
|
_wakeup.Set(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool CanQueryPendingInput => _inputProvider != null; |
||||
|
public bool HasPendingInput => _inputProvider?.HasInput ?? false; |
||||
|
|
||||
|
public void RunLoop(CancellationToken token) |
||||
|
{ |
||||
|
while (!token.IsCancellationRequested) |
||||
|
{ |
||||
|
bool signaled; |
||||
|
lock (_lock) |
||||
|
{ |
||||
|
signaled = _signaled; |
||||
|
_signaled = false; |
||||
|
} |
||||
|
|
||||
|
if (signaled) |
||||
|
{ |
||||
|
Signaled?.Invoke(); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
bool fireTimer = false; |
||||
|
lock (_lock) |
||||
|
{ |
||||
|
if (_nextTimer < _clock.Elapsed) |
||||
|
{ |
||||
|
fireTimer = true; |
||||
|
_nextTimer = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (fireTimer) |
||||
|
{ |
||||
|
Timer?.Invoke(); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (_inputProvider?.HasInput == true) |
||||
|
{ |
||||
|
_inputProvider.DispatchNextInputEvent(); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (_nextTimer != null) |
||||
|
{ |
||||
|
var waitFor = _clock.Elapsed - _nextTimer.Value; |
||||
|
if (waitFor.TotalMilliseconds < 1) |
||||
|
continue; |
||||
|
_wakeup.WaitOne(waitFor); |
||||
|
} |
||||
|
else |
||||
|
_wakeup.WaitOne(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,86 +0,0 @@ |
|||||
using System; |
|
||||
using Avalonia.Reactive; |
|
||||
using System.Threading; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Threading; |
|
||||
|
|
||||
namespace Avalonia.Headless |
|
||||
{ |
|
||||
class HeadlessPlatformThreadingInterface : IPlatformThreadingInterface |
|
||||
{ |
|
||||
public HeadlessPlatformThreadingInterface() |
|
||||
{ |
|
||||
_thread = Thread.CurrentThread; |
|
||||
} |
|
||||
|
|
||||
private AutoResetEvent _event = new AutoResetEvent(false); |
|
||||
private Thread _thread; |
|
||||
private object _lock = new object(); |
|
||||
private DispatcherPriority? _signaledPriority; |
|
||||
|
|
||||
public void RunLoop(CancellationToken cancellationToken) |
|
||||
{ |
|
||||
while (!cancellationToken.IsCancellationRequested) |
|
||||
{ |
|
||||
DispatcherPriority? signaled = null; |
|
||||
lock (_lock) |
|
||||
{ |
|
||||
signaled = _signaledPriority; |
|
||||
_signaledPriority = null; |
|
||||
} |
|
||||
if(signaled.HasValue) |
|
||||
Signaled?.Invoke(signaled); |
|
||||
WaitHandle.WaitAny(new[] {cancellationToken.WaitHandle, _event}, TimeSpan.FromMilliseconds(20)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) |
|
||||
{ |
|
||||
if (interval.TotalMilliseconds < 10) |
|
||||
interval = TimeSpan.FromMilliseconds(10); |
|
||||
|
|
||||
var stopped = false; |
|
||||
Timer timer = null; |
|
||||
timer = new Timer(_ => |
|
||||
{ |
|
||||
if (stopped) |
|
||||
return; |
|
||||
|
|
||||
Dispatcher.UIThread.Post(() => |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
tick(); |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
if (!stopped) |
|
||||
timer.Change(interval, Timeout.InfiniteTimeSpan); |
|
||||
} |
|
||||
}); |
|
||||
}, |
|
||||
null, interval, Timeout.InfiniteTimeSpan); |
|
||||
|
|
||||
return Disposable.Create(() => |
|
||||
{ |
|
||||
stopped = true; |
|
||||
timer.Dispose(); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public void Signal(DispatcherPriority priority) |
|
||||
{ |
|
||||
lock (_lock) |
|
||||
{ |
|
||||
if (_signaledPriority == null || _signaledPriority.Value > priority) |
|
||||
{ |
|
||||
_signaledPriority = priority; |
|
||||
} |
|
||||
_event.Set(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public bool CurrentThreadIsLoopThread => _thread == Thread.CurrentThread; |
|
||||
public event Action<DispatcherPriority?> Signaled; |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue