mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
113 lines
4.0 KiB
C#
113 lines
4.0 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using System.Reflection;
|
|
using SharpEmu.Core.Cpu.Native;
|
|
using SharpEmu.HLE;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.Libs.Tests.Cpu;
|
|
|
|
public sealed class GuestThreadBlockWaiterRepresentationTests
|
|
{
|
|
[Fact]
|
|
public void SchedulerStoresOnlyTheWaiterObjectRepresentation()
|
|
{
|
|
var stateType = typeof(DirectExecutionBackend).GetNestedType(
|
|
"GuestThreadState",
|
|
BindingFlags.NonPublic);
|
|
Assert.NotNull(stateType);
|
|
|
|
var waiterProperty = stateType.GetProperty(
|
|
"BlockWaiter",
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
Assert.NotNull(waiterProperty);
|
|
Assert.Equal(typeof(IGuestThreadBlockWaiter), waiterProperty.PropertyType);
|
|
Assert.Null(stateType.GetProperty(
|
|
"BlockResumeHandler",
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
|
|
Assert.Null(stateType.GetProperty(
|
|
"BlockWakeHandler",
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
|
|
|
|
var registerMethods = typeof(DirectExecutionBackend)
|
|
.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
|
|
.Where(method => method.Name == "RegisterBlockedGuestThreadContinuation")
|
|
.ToArray();
|
|
var registerMethod = Assert.Single(registerMethods);
|
|
Assert.Contains(
|
|
registerMethod.GetParameters(),
|
|
parameter => parameter.ParameterType == typeof(IGuestThreadBlockWaiter));
|
|
Assert.DoesNotContain(
|
|
registerMethod.GetParameters(),
|
|
parameter => IsFuncParameter(parameter.ParameterType));
|
|
|
|
var consumeMethods = typeof(GuestThreadExecution)
|
|
.GetMethods(BindingFlags.Static | BindingFlags.Public)
|
|
.Where(method => method.Name == nameof(GuestThreadExecution.TryConsumeCurrentThreadBlock));
|
|
Assert.DoesNotContain(
|
|
consumeMethods.SelectMany(method => method.GetParameters()),
|
|
parameter => IsFuncParameter(parameter.ParameterType));
|
|
}
|
|
|
|
[Fact]
|
|
public void DelegateCompatibilityBridgeIsConsumedAsOneWaiterObject()
|
|
{
|
|
var previousThread = GuestThreadExecution.EnterGuestThread(0x1234);
|
|
try
|
|
{
|
|
var canWake = false;
|
|
var wakeCalls = 0;
|
|
var resumeCalls = 0;
|
|
Assert.True(GuestThreadExecution.RequestCurrentThreadBlock(
|
|
context: null,
|
|
reason: "test_wait",
|
|
wakeKey: "test_waiter:1",
|
|
resumeHandler: () =>
|
|
{
|
|
resumeCalls++;
|
|
return 42;
|
|
},
|
|
wakeHandler: () =>
|
|
{
|
|
wakeCalls++;
|
|
return canWake;
|
|
}));
|
|
|
|
Assert.True(GuestThreadExecution.TryConsumeCurrentThreadBlock(
|
|
out var reason,
|
|
out _,
|
|
out var hasContinuation,
|
|
out var wakeKey,
|
|
out IGuestThreadBlockWaiter? waiter,
|
|
out var deadline));
|
|
Assert.Equal("test_wait", reason);
|
|
Assert.Equal("test_waiter:1", wakeKey);
|
|
Assert.False(hasContinuation);
|
|
Assert.Equal(0, deadline);
|
|
Assert.NotNull(waiter);
|
|
|
|
Assert.False(waiter.TryWake());
|
|
canWake = true;
|
|
Assert.True(waiter.TryWake());
|
|
Assert.Equal(42, waiter.Resume());
|
|
Assert.Equal(2, wakeCalls);
|
|
Assert.Equal(1, resumeCalls);
|
|
}
|
|
finally
|
|
{
|
|
GuestThreadExecution.RestoreGuestThread(previousThread);
|
|
}
|
|
}
|
|
|
|
private static bool IsFuncParameter(Type parameterType)
|
|
{
|
|
var type = parameterType.IsByRef
|
|
? parameterType.GetElementType()
|
|
: parameterType;
|
|
return type is not null &&
|
|
type.IsGenericType &&
|
|
type.GetGenericTypeDefinition() == typeof(Func<>);
|
|
}
|
|
}
|