2021-06-25 12:02:19 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System ;
using osu.Framework.Allocation ;
using osu.Framework.Bindables ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
Fix test deadlock due to TPL threadpool saturation
As found in aggressive CI runs:
```csharp
00007F6F527F32B0 00007f74f937c72d (MethodDesc 00007f74f5545a78 + 0x22d System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken)), calling (MethodDesc 00007f74f51c0838 + 0 System.Threading.Monitor.Wait(System.Object, Int32))
00007F6F527F3330 00007f74f93861ca (MethodDesc 00007f74f5259100 + 0x10a System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken)), calling (MethodDesc 00007f74f5545a78 + 0 System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken))
00007F6F527F3390 00007f74f9385d11 (MethodDesc 00007f74f52590e8 + 0x81 System.Threading.Tasks.Task.InternalWaitCore(Int32, System.Threading.CancellationToken)), calling (MethodDesc 00007f74f5259100 + 0 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken))
00007F6F527F33E0 00007f74fc3823af (MethodDesc 00007f74f5934c08 + 0x4f System.Threading.Tasks.Task`1[[System.Boolean, System.Private.CoreLib]].GetResultCore(Boolean)), calling (MethodDesc 00007f74f52590e8 + 0 System.Threading.Tasks.Task.InternalWaitCore(Int32, System.Threading.CancellationToken))
00007F6F527F3400 00007f74fb0aad92 (MethodDesc 00007f74fb28b000 + 0x122 osu.Game.Tests.Visual.OnlinePlay.OnlinePlayTestScene+<>c__DisplayClass21_0.<Setup>b__1(osu.Game.Online.API.APIRequest)), calling (MethodDesc 00007f74f5934be8 + 0 System.Threading.Tasks.Task`1[[System.Boolean, System.Private.CoreLib]].get_Result())
00007F6F527F3470 00007f74fa9c6dd7 (MethodDesc 00007f74f921f6c8 + 0x77 osu.Game.Online.API.DummyAPIAccess.PerformAsync(osu.Game.Online.API.APIRequest))
00007F6F527F34C0 00007f74fb13c718 (MethodDesc 00007f74fb4e0c50 + 0x538 osu.Game.Database.OnlineLookupCache`3+<performLookup>d__13[[System.Int32, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]].MoveNext())
```
Going with the simplest solution, as what we had was pretty ugly to start with and I don't want to overthink this, just push a fix and hope for the best.
2022-06-26 15:42:28 +08:00
using osu.Framework.Logging ;
2022-02-15 21:02:33 +08:00
using osu.Game.Beatmaps ;
Fix test deadlock due to TPL threadpool saturation
As found in aggressive CI runs:
```csharp
00007F6F527F32B0 00007f74f937c72d (MethodDesc 00007f74f5545a78 + 0x22d System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken)), calling (MethodDesc 00007f74f51c0838 + 0 System.Threading.Monitor.Wait(System.Object, Int32))
00007F6F527F3330 00007f74f93861ca (MethodDesc 00007f74f5259100 + 0x10a System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken)), calling (MethodDesc 00007f74f5545a78 + 0 System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken))
00007F6F527F3390 00007f74f9385d11 (MethodDesc 00007f74f52590e8 + 0x81 System.Threading.Tasks.Task.InternalWaitCore(Int32, System.Threading.CancellationToken)), calling (MethodDesc 00007f74f5259100 + 0 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken))
00007F6F527F33E0 00007f74fc3823af (MethodDesc 00007f74f5934c08 + 0x4f System.Threading.Tasks.Task`1[[System.Boolean, System.Private.CoreLib]].GetResultCore(Boolean)), calling (MethodDesc 00007f74f52590e8 + 0 System.Threading.Tasks.Task.InternalWaitCore(Int32, System.Threading.CancellationToken))
00007F6F527F3400 00007f74fb0aad92 (MethodDesc 00007f74fb28b000 + 0x122 osu.Game.Tests.Visual.OnlinePlay.OnlinePlayTestScene+<>c__DisplayClass21_0.<Setup>b__1(osu.Game.Online.API.APIRequest)), calling (MethodDesc 00007f74f5934be8 + 0 System.Threading.Tasks.Task`1[[System.Boolean, System.Private.CoreLib]].get_Result())
00007F6F527F3470 00007f74fa9c6dd7 (MethodDesc 00007f74f921f6c8 + 0x77 osu.Game.Online.API.DummyAPIAccess.PerformAsync(osu.Game.Online.API.APIRequest))
00007F6F527F34C0 00007f74fb13c718 (MethodDesc 00007f74fb4e0c50 + 0x538 osu.Game.Database.OnlineLookupCache`3+<performLookup>d__13[[System.Int32, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]].MoveNext())
```
Going with the simplest solution, as what we had was pretty ugly to start with and I don't want to overthink this, just push a fix and hope for the best.
2022-06-26 15:42:28 +08:00
using osu.Game.Database ;
2021-10-27 13:49:50 +08:00
using osu.Game.Online.API ;
2021-06-25 12:02:19 +08:00
using osu.Game.Online.Rooms ;
using osu.Game.Screens.OnlinePlay ;
namespace osu.Game.Tests.Visual.OnlinePlay
{
/// <summary>
2021-06-25 17:07:47 +08:00
/// A base test scene for all online play components and screens.
2021-06-25 12:02:19 +08:00
/// </summary>
2022-11-24 13:32:20 +08:00
public abstract partial class OnlinePlayTestScene : ScreenTestScene , IOnlinePlayTestSceneDependencies
2021-06-25 12:02:19 +08:00
{
2024-02-02 18:48:13 +08:00
public Bindable < Room > SelectedRoom = > OnlinePlayDependencies . SelectedRoom ;
public IRoomManager RoomManager = > OnlinePlayDependencies . RoomManager ;
public OngoingOperationTracker OngoingOperationTracker = > OnlinePlayDependencies . OngoingOperationTracker ;
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker = > OnlinePlayDependencies . AvailabilityTracker ;
public TestUserLookupCache UserLookupCache = > OnlinePlayDependencies . UserLookupCache ;
public BeatmapLookupCache BeatmapLookupCache = > OnlinePlayDependencies . BeatmapLookupCache ;
2021-06-25 12:02:19 +08:00
2021-06-25 17:07:47 +08:00
/// <summary>
/// All dependencies required for online play components and screens.
/// </summary>
2024-02-02 21:34:48 +08:00
protected OnlinePlayTestSceneDependencies OnlinePlayDependencies = > dependencies . OnlinePlayDependencies ! ;
2021-06-25 17:07:47 +08:00
2021-06-25 12:02:19 +08:00
protected override Container < Drawable > Content = > content ;
2021-10-27 13:49:50 +08:00
2021-06-25 12:02:19 +08:00
private readonly Container content ;
private readonly Container drawableDependenciesContainer ;
2024-02-02 18:48:13 +08:00
private DelegatedDependencyContainer dependencies = null ! ;
2021-06-25 12:02:19 +08:00
protected OnlinePlayTestScene ( )
{
base . Content . AddRange ( new Drawable [ ]
{
drawableDependenciesContainer = new Container { RelativeSizeAxes = Axes . Both } ,
content = new Container { RelativeSizeAxes = Axes . Both } ,
} ) ;
}
2022-02-15 19:55:56 +08:00
protected sealed override IReadOnlyDependencyContainer CreateChildDependencies ( IReadOnlyDependencyContainer parent )
2024-02-02 21:34:48 +08:00
= > dependencies = new DelegatedDependencyContainer ( base . CreateChildDependencies ( parent ) ) ;
2021-06-25 12:02:19 +08:00
2022-07-29 14:27:39 +08:00
public override void SetUpSteps ( )
2021-06-25 12:02:19 +08:00
{
2022-07-29 14:27:39 +08:00
base . SetUpSteps ( ) ;
2021-10-28 15:48:17 +08:00
2022-07-29 14:27:39 +08:00
AddStep ( "setup dependencies" , ( ) = >
{
// Reset the room dependencies to a fresh state.
2024-02-02 21:34:48 +08:00
dependencies . OnlinePlayDependencies = CreateOnlinePlayDependencies ( ) ;
drawableDependenciesContainer . Clear ( ) ;
drawableDependenciesContainer . AddRange ( dependencies . OnlinePlayDependencies . DrawableComponents ) ;
2022-02-15 21:02:33 +08:00
2022-07-29 14:27:39 +08:00
var handler = OnlinePlayDependencies . RequestsHandler ;
2022-02-15 21:02:33 +08:00
2022-07-29 14:27:39 +08:00
// Resolving the BeatmapManager in the test scene will inject the game-wide BeatmapManager, while many test scenes cache their own BeatmapManager instead.
// To get around this, the BeatmapManager is looked up from the dependencies provided to the children of the test scene instead.
var beatmapManager = dependencies . Get < BeatmapManager > ( ) ;
( ( DummyAPIAccess ) API ) . HandleRequest = request = >
Fix test deadlock due to TPL threadpool saturation
As found in aggressive CI runs:
```csharp
00007F6F527F32B0 00007f74f937c72d (MethodDesc 00007f74f5545a78 + 0x22d System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken)), calling (MethodDesc 00007f74f51c0838 + 0 System.Threading.Monitor.Wait(System.Object, Int32))
00007F6F527F3330 00007f74f93861ca (MethodDesc 00007f74f5259100 + 0x10a System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken)), calling (MethodDesc 00007f74f5545a78 + 0 System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken))
00007F6F527F3390 00007f74f9385d11 (MethodDesc 00007f74f52590e8 + 0x81 System.Threading.Tasks.Task.InternalWaitCore(Int32, System.Threading.CancellationToken)), calling (MethodDesc 00007f74f5259100 + 0 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken))
00007F6F527F33E0 00007f74fc3823af (MethodDesc 00007f74f5934c08 + 0x4f System.Threading.Tasks.Task`1[[System.Boolean, System.Private.CoreLib]].GetResultCore(Boolean)), calling (MethodDesc 00007f74f52590e8 + 0 System.Threading.Tasks.Task.InternalWaitCore(Int32, System.Threading.CancellationToken))
00007F6F527F3400 00007f74fb0aad92 (MethodDesc 00007f74fb28b000 + 0x122 osu.Game.Tests.Visual.OnlinePlay.OnlinePlayTestScene+<>c__DisplayClass21_0.<Setup>b__1(osu.Game.Online.API.APIRequest)), calling (MethodDesc 00007f74f5934be8 + 0 System.Threading.Tasks.Task`1[[System.Boolean, System.Private.CoreLib]].get_Result())
00007F6F527F3470 00007f74fa9c6dd7 (MethodDesc 00007f74f921f6c8 + 0x77 osu.Game.Online.API.DummyAPIAccess.PerformAsync(osu.Game.Online.API.APIRequest))
00007F6F527F34C0 00007f74fb13c718 (MethodDesc 00007f74fb4e0c50 + 0x538 osu.Game.Database.OnlineLookupCache`3+<performLookup>d__13[[System.Int32, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]].MoveNext())
```
Going with the simplest solution, as what we had was pretty ugly to start with and I don't want to overthink this, just push a fix and hope for the best.
2022-06-26 15:42:28 +08:00
{
2022-07-29 14:27:39 +08:00
try
{
return handler . HandleRequest ( request , API . LocalUser . Value , beatmapManager ) ;
}
catch ( ObjectDisposedException )
{
// These requests can be fired asynchronously, but potentially arrive after game components
// have been disposed (ie. realm in BeatmapManager).
// This only happens in tests and it's easiest to ignore them for now.
Logger . Log ( $"Handled {nameof(ObjectDisposedException)} in test request handling" ) ;
return true ;
}
} ;
} ) ;
}
2021-10-27 13:49:50 +08:00
2021-06-25 12:02:19 +08:00
/// <summary>
2022-07-29 14:27:39 +08:00
/// Creates the room dependencies. Called every <see cref="SetUpSteps"/>.
2021-06-25 12:02:19 +08:00
/// </summary>
/// <remarks>
2021-06-25 17:07:47 +08:00
/// Any custom dependencies required for online play sub-classes should be added here.
2021-06-25 12:02:19 +08:00
/// </remarks>
2021-06-25 19:11:38 +08:00
protected virtual OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies ( ) = > new OnlinePlayTestSceneDependencies ( ) ;
2021-06-25 12:02:19 +08:00
/// <summary>
2021-06-25 17:07:47 +08:00
/// A <see cref="IReadOnlyDependencyContainer"/> providing a mutable lookup source for online play dependencies.
2021-06-25 12:02:19 +08:00
/// </summary>
2021-06-25 17:07:47 +08:00
private class DelegatedDependencyContainer : IReadOnlyDependencyContainer
2021-06-25 12:02:19 +08:00
{
/// <summary>
2021-06-25 17:07:47 +08:00
/// The online play dependencies.
2021-06-25 12:02:19 +08:00
/// </summary>
2024-02-02 21:34:48 +08:00
public OnlinePlayTestSceneDependencies ? OnlinePlayDependencies { get ; set ; }
2021-06-25 12:02:19 +08:00
private readonly IReadOnlyDependencyContainer parent ;
private readonly DependencyContainer injectableDependencies ;
/// <summary>
2021-06-25 17:07:47 +08:00
/// Creates a new <see cref="DelegatedDependencyContainer"/>.
2021-06-25 12:02:19 +08:00
/// </summary>
2021-06-25 17:07:47 +08:00
/// <param name="parent">The fallback <see cref="IReadOnlyDependencyContainer"/> to use when <see cref="OnlinePlayDependencies"/> cannot satisfy a dependency.</param>
public DelegatedDependencyContainer ( IReadOnlyDependencyContainer parent )
2021-06-25 12:02:19 +08:00
{
this . parent = parent ;
injectableDependencies = new DependencyContainer ( this ) ;
}
public object Get ( Type type )
2024-02-02 21:34:48 +08:00
= > OnlinePlayDependencies ? . Get ( type ) ? ? parent . Get ( type ) ;
2021-06-25 12:02:19 +08:00
public object Get ( Type type , CacheInfo info )
2024-02-02 21:34:48 +08:00
= > OnlinePlayDependencies ? . Get ( type , info ) ? ? parent . Get ( type , info ) ;
2021-06-25 12:02:19 +08:00
public void Inject < T > ( T instance )
2022-11-29 13:45:26 +08:00
where T : class , IDependencyInjectionCandidate
2021-06-25 12:02:19 +08:00
= > injectableDependencies . Inject ( instance ) ;
}
}
}