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