diff --git a/osu.Framework/Platform/HeadlessGameHost.cs b/osu.Framework/Platform/HeadlessGameHost.cs index 2c7d591d09..96bf762f97 100644 --- a/osu.Framework/Platform/HeadlessGameHost.cs +++ b/osu.Framework/Platform/HeadlessGameHost.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Rendering.Dummy; using osu.Framework.Input.Handlers; using osu.Framework.Logging; +using osu.Framework.Testing; using osu.Framework.Threading; using osu.Framework.Timing; @@ -106,6 +107,8 @@ protected override void UpdateFrame() protected override IEnumerable CreateAvailableInputHandlers() => Array.Empty(); + protected override ThreadRunner CreateThreadRunner(InputThread mainThread) => new HeadlessThreadRunner(mainThread); + private class FastClock : IClock { private readonly double increment; diff --git a/osu.Framework/Testing/HeadlessThreadRunner.cs b/osu.Framework/Testing/HeadlessThreadRunner.cs new file mode 100644 index 0000000000..2be30ea076 --- /dev/null +++ b/osu.Framework/Testing/HeadlessThreadRunner.cs @@ -0,0 +1,51 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Development; +using osu.Framework.Platform; +using osu.Framework.Threading; +using osu.Framework.Utils; + +namespace osu.Framework.Testing +{ + public class HeadlessThreadRunner : ThreadRunner + { + public HeadlessThreadRunner(InputThread mainThread) + : base(mainThread) + { + } + + public override void RunMainLoop() + { + // Run true single-threaded mode. + if (ThreadSafety.ExecutionMode == ExecutionMode.SingleThread) + { + base.RunMainLoop(); + return; + } + + // Run a simulated multi-threaded mode. + // This is because the CI test runners are usually CPU-limited and may be preferring one thread (audio) over another (update). + + ThreadSafety.ExecutionMode = ExecutionMode.SingleThread; + + for (int i = 0; i < Threads.Count; i++) + { + int from; + int to; + + do + { + from = RNG.Next(Threads.Count); + to = RNG.Next(Threads.Count); + } while (from == to); + + (Threads[from], Threads[to]) = (Threads[to], Threads[from]); + } + + base.RunMainLoop(); + + ThreadSafety.ExecutionMode = ExecutionMode.MultiThreaded; + } + } +}