Program.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. using System;
  2. using System.Diagnostics;
  3. using System.Reactive.Disposables;
  4. using System.Runtime.CompilerServices;
  5. using System.Threading;
  6. using Avalonia;
  7. using Avalonia.Platform;
  8. using Avalonia.Threading;
  9. using Avalonia.X11;
  10. namespace PlatformSanityChecks
  11. {
  12. public class Program
  13. {
  14. static Thread UiThread;
  15. static void Main(string[] args)
  16. {
  17. UiThread = Thread.CurrentThread;
  18. AppBuilder.Configure<App>().RuntimePlatformServicesInitializer();
  19. var app = new App();
  20. new AvaloniaX11Platform().Initialize();
  21. CheckPlatformThreading();
  22. }
  23. static bool CheckAccess() => UiThread == Thread.CurrentThread;
  24. static void VerifyAccess()
  25. {
  26. if (!CheckAccess())
  27. Die("Call from invalid thread");
  28. }
  29. static Exception Die(string error)
  30. {
  31. Console.Error.WriteLine(error);
  32. Console.Error.WriteLine(Environment.StackTrace);
  33. Process.GetCurrentProcess().Kill();
  34. throw new Exception(error);
  35. }
  36. static IDisposable Enter([CallerMemberName] string caller = null)
  37. {
  38. Console.WriteLine("Entering " + caller);
  39. return Disposable.Create(() => { Console.WriteLine("Leaving " + caller); });
  40. }
  41. static void EnterLoop(Action<CancellationTokenSource> cb, [CallerMemberName] string caller = null)
  42. {
  43. using (Enter(caller))
  44. {
  45. var cts = new CancellationTokenSource();
  46. cb(cts);
  47. Dispatcher.UIThread.MainLoop(cts.Token);
  48. if (!cts.IsCancellationRequested)
  49. Die("Unexpected loop exit");
  50. }
  51. }
  52. static void CheckTimerOrdering() => EnterLoop(cts =>
  53. {
  54. bool firstFired = false, secondFired = false;
  55. DispatcherTimer.Run(() =>
  56. {
  57. Console.WriteLine("Second tick");
  58. VerifyAccess();
  59. if (!firstFired)
  60. throw Die("Invalid timer ordering");
  61. if (secondFired)
  62. throw Die("Invocation of finished timer");
  63. secondFired = true;
  64. cts.Cancel();
  65. return false;
  66. }, TimeSpan.FromSeconds(2));
  67. DispatcherTimer.Run(() =>
  68. {
  69. Console.WriteLine("First tick");
  70. VerifyAccess();
  71. if (secondFired)
  72. throw Die("Invalid timer ordering");
  73. if (firstFired)
  74. throw Die("Invocation of finished timer");
  75. firstFired = true;
  76. return false;
  77. }, TimeSpan.FromSeconds(1));
  78. });
  79. static void CheckTimerTicking() => EnterLoop(cts =>
  80. {
  81. int ticks = 0;
  82. var st = Stopwatch.StartNew();
  83. DispatcherTimer.Run(() =>
  84. {
  85. ticks++;
  86. Console.WriteLine($"Tick {ticks} at {st.Elapsed}");
  87. if (ticks == 5)
  88. {
  89. if (st.Elapsed.TotalSeconds < 4.5)
  90. Die("Timer is too fast");
  91. if (st.Elapsed.TotalSeconds > 6)
  92. Die("Timer is too slow");
  93. cts.Cancel();
  94. return false;
  95. }
  96. return true;
  97. }, TimeSpan.FromSeconds(1));
  98. });
  99. static void CheckSignaling() => EnterLoop(cts =>
  100. {
  101. ThreadPool.QueueUserWorkItem(_ =>
  102. {
  103. Thread.Sleep(100);
  104. Dispatcher.UIThread.Post(() =>
  105. {
  106. VerifyAccess();
  107. cts.Cancel();
  108. });
  109. });
  110. });
  111. static void CheckPlatformThreading()
  112. {
  113. CheckSignaling();
  114. CheckTimerOrdering();
  115. CheckTimerTicking();
  116. }
  117. }
  118. }