csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
4.0 KiB
140 lines
4.0 KiB
using System;
|
|
using System.Diagnostics;
|
|
using System.Reactive.Disposables;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
using Avalonia;
|
|
using Avalonia.Platform;
|
|
using Avalonia.Threading;
|
|
using Avalonia.X11;
|
|
|
|
namespace PlatformSanityChecks
|
|
{
|
|
public class Program
|
|
{
|
|
static Thread UiThread;
|
|
|
|
static void Main(string[] args)
|
|
{
|
|
UiThread = Thread.CurrentThread;
|
|
AppBuilder.Configure<App>().RuntimePlatformServicesInitializer();
|
|
var app = new App();
|
|
|
|
new AvaloniaX11Platform().Initialize();
|
|
|
|
CheckPlatformThreading();
|
|
|
|
|
|
|
|
}
|
|
|
|
static bool CheckAccess() => UiThread == Thread.CurrentThread;
|
|
|
|
static void VerifyAccess()
|
|
{
|
|
if (!CheckAccess())
|
|
Die("Call from invalid thread");
|
|
}
|
|
|
|
static Exception Die(string error)
|
|
{
|
|
Console.Error.WriteLine(error);
|
|
Console.Error.WriteLine(Environment.StackTrace);
|
|
Process.GetCurrentProcess().Kill();
|
|
throw new Exception(error);
|
|
}
|
|
|
|
static IDisposable Enter([CallerMemberName] string caller = null)
|
|
{
|
|
Console.WriteLine("Entering " + caller);
|
|
return Disposable.Create(() => { Console.WriteLine("Leaving " + caller); });
|
|
}
|
|
|
|
static void EnterLoop(Action<CancellationTokenSource> cb, [CallerMemberName] string caller = null)
|
|
{
|
|
using (Enter(caller))
|
|
{
|
|
var cts = new CancellationTokenSource();
|
|
cb(cts);
|
|
Dispatcher.UIThread.MainLoop(cts.Token);
|
|
if (!cts.IsCancellationRequested)
|
|
Die("Unexpected loop exit");
|
|
}
|
|
}
|
|
|
|
static void CheckTimerOrdering() => EnterLoop(cts =>
|
|
{
|
|
bool firstFired = false, secondFired = false;
|
|
DispatcherTimer.Run(() =>
|
|
{
|
|
Console.WriteLine("Second tick");
|
|
VerifyAccess();
|
|
if (!firstFired)
|
|
throw Die("Invalid timer ordering");
|
|
if (secondFired)
|
|
throw Die("Invocation of finished timer");
|
|
secondFired = true;
|
|
cts.Cancel();
|
|
return false;
|
|
}, TimeSpan.FromSeconds(2));
|
|
DispatcherTimer.Run(() =>
|
|
{
|
|
Console.WriteLine("First tick");
|
|
VerifyAccess();
|
|
if (secondFired)
|
|
throw Die("Invalid timer ordering");
|
|
if (firstFired)
|
|
throw Die("Invocation of finished timer");
|
|
firstFired = true;
|
|
return false;
|
|
}, TimeSpan.FromSeconds(1));
|
|
});
|
|
|
|
static void CheckTimerTicking() => EnterLoop(cts =>
|
|
{
|
|
int ticks = 0;
|
|
var st = Stopwatch.StartNew();
|
|
DispatcherTimer.Run(() =>
|
|
{
|
|
ticks++;
|
|
Console.WriteLine($"Tick {ticks} at {st.Elapsed}");
|
|
if (ticks == 5)
|
|
{
|
|
if (st.Elapsed.TotalSeconds < 4.5)
|
|
Die("Timer is too fast");
|
|
if (st.Elapsed.TotalSeconds > 6)
|
|
Die("Timer is too slow");
|
|
cts.Cancel();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}, TimeSpan.FromSeconds(1));
|
|
|
|
|
|
});
|
|
|
|
|
|
static void CheckSignaling() => EnterLoop(cts =>
|
|
{
|
|
ThreadPool.QueueUserWorkItem(_ =>
|
|
{
|
|
Thread.Sleep(100);
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
VerifyAccess();
|
|
cts.Cancel();
|
|
});
|
|
});
|
|
});
|
|
|
|
static void CheckPlatformThreading()
|
|
{
|
|
CheckSignaling();
|
|
CheckTimerOrdering();
|
|
CheckTimerTicking();
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|