diff options
author | Matt Ward <matt.ward@microsoft.com> | 2021-02-24 01:11:58 +0300 |
---|---|---|
committer | Matt Ward <matt.ward@microsoft.com> | 2021-02-24 01:11:58 +0300 |
commit | d0821012bf30ce2771d1bc878a34f6bd555b00ba (patch) | |
tree | fb2ad925057fd507a41e807aeb63039299e3916b | |
parent | 1d3cd2d05a062f41de07c397a187537bcad81370 (diff) |
HACK: Add support for a console main loop
No way to currently run tests that require a UI main loop
with XWT or Xamarin.Mac on .NET 6. Added a temporary hack
that adds a console based main loop, using a queue, and
also registers a SynchronisationContext with the same loop
with MonoDevelop.Core.Runtime. This prevents tests in
MonoDevelop.Core hanging waiting for tasks to complete
on the UI thread.
-rw-r--r-- | src/framework/GuiUnit/TestRunner.cs | 1 | ||||
-rw-r--r-- | src/framework/GuiUnit/XwtMainLoopIntegration.cs | 117 |
2 files changed, 118 insertions, 0 deletions
diff --git a/src/framework/GuiUnit/TestRunner.cs b/src/framework/GuiUnit/TestRunner.cs index 3950cf3..7719fd4 100644 --- a/src/framework/GuiUnit/TestRunner.cs +++ b/src/framework/GuiUnit/TestRunner.cs @@ -59,6 +59,7 @@ namespace GuiUnit try { mainLoop = mainLoop ?? new XwtMainLoopIntegration (); } catch { } try { mainLoop = mainLoop ?? new MonoMacMainLoopIntegration (); } catch { } try { mainLoop = mainLoop ?? new GtkMainLoopIntegration (); } catch { } + mainLoop = mainLoop ?? new ConsoleMainLoop (); return mainLoop; } set { mainLoop = value; diff --git a/src/framework/GuiUnit/XwtMainLoopIntegration.cs b/src/framework/GuiUnit/XwtMainLoopIntegration.cs index 5d20936..7a74842 100644 --- a/src/framework/GuiUnit/XwtMainLoopIntegration.cs +++ b/src/framework/GuiUnit/XwtMainLoopIntegration.cs @@ -73,5 +73,122 @@ namespace GuiUnit Application.GetMethod ("Exit").Invoke (null, null); } } + + class ConsoleMainLoop : System.Threading.SynchronizationContext, IMainLoopIntegration + { + System.Collections.Generic.Queue<InvokerHelper> work = + new System.Collections.Generic.Queue<InvokerHelper>(); + + System.Collections.Generic.Queue<Tuple<System.Threading.SendOrPostCallback, object>> contextWork = + new System.Collections.Generic.Queue<Tuple<System.Threading.SendOrPostCallback, object>>(); + + bool endLoop; + + public void InitializeToolkit() + { + var runtime = Type.GetType("MonoDevelop.Core.Runtime, MonoDevelop.Core"); + if (runtime == null) + return; + + var property = runtime.GetProperty ("MainSynchronizationContext", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (property == null) + return; + + System.Threading.SynchronizationContext.SetSynchronizationContext(this); + property.SetValue (null, System.Threading.SynchronizationContext.Current); + } + + public void InvokeOnMainLoop (InvokerHelper helper) + { + lock (work) + { + work.Enqueue (helper); + System.Threading.Monitor.Pulse (work); + } + } + + public void RunMainLoop () + { + do + { + InvokerHelper next = null; + Tuple<System.Threading.SendOrPostCallback, object> nextContext = null; + lock (work) + { + if (work.Count > 0 && !endLoop) + next = work.Dequeue (); + else if (contextWork.Count > 0 && !endLoop) + nextContext = contextWork.Dequeue (); + else if (!endLoop) + System.Threading.Monitor.Wait (work); + } + if (next != null) + { + try + { + next.Invoke (); + } + catch (Exception ex) + { + Console.WriteLine (ex); + } + } + if (nextContext != null) + { + try + { + nextContext.Item1(nextContext.Item2); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + } + } while (!endLoop); + } + + public void Shutdown () + { + lock (work) + { + endLoop = true; + System.Threading.Monitor.Pulse (work); + } + } + + public override void Post (System.Threading.SendOrPostCallback d, object state) + { + lock (work) + { + contextWork.Enqueue (new Tuple<System.Threading.SendOrPostCallback, object>(d, state)); + System.Threading.Monitor.Pulse (work); + } + } + + public override void Send (System.Threading.SendOrPostCallback d, object state) + { + var evt = new System.Threading.ManualResetEventSlim (false); + Exception exception = null; + Post (s => + { + try + { + d.Invoke (state); + } + catch (Exception ex) + { + exception = ex; + } + finally + { + System.Threading.Thread.MemoryBarrier (); + evt.Set (); + } + }, null); + evt.Wait (); + if (exception != null) + throw exception; + } + } } |