using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Avalonia.Platform; using Avalonia.Rendering; namespace Avalonia.Controls.Platform { public class InternalPlatformThreadingInterface : IPlatformThreadingInterface, IRenderLoop { public InternalPlatformThreadingInterface() { TlsCurrentThreadIsLoopThread = true; StartTimer(new TimeSpan(0, 0, 0, 0, 66), () => Tick?.Invoke(this, new EventArgs())); } private readonly AutoResetEvent _signaled = new AutoResetEvent(false); private readonly AutoResetEvent _queued = new AutoResetEvent(false); private readonly Queue _actions = new Queue(); public void RunLoop(CancellationToken cancellationToken) { var handles = new[] {_signaled, _queued}; while (true) { if (0 == WaitHandle.WaitAny(handles)) Signaled?.Invoke(); else { while (true) { Action item; lock (_actions) if (_actions.Count == 0) break; else item = _actions.Dequeue(); item(); } } } } public void Send(Action cb) { lock (_actions) { _actions.Enqueue(cb); _queued.Set(); } } class WatTimer : IDisposable { private readonly IDisposable _timer; private GCHandle _handle; public WatTimer(IDisposable timer) { _timer = timer; _handle = GCHandle.Alloc(_timer); } public void Dispose() { _handle.Free(); _timer.Dispose(); } } public IDisposable StartTimer(TimeSpan interval, Action tick) { return new WatTimer(new System.Threading.Timer(delegate { var tcs = new TaskCompletionSource(); Send(() => { try { tick(); } finally { tcs.SetResult(0); } }); tcs.Task.Wait(); }, null, TimeSpan.Zero, interval)); } public void Signal() { _signaled.Set(); } [ThreadStatic] private static bool TlsCurrentThreadIsLoopThread; public bool CurrentThreadIsLoopThread => TlsCurrentThreadIsLoopThread; public event Action Signaled; public event EventHandler Tick; } }