mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-23 03:16:22 +08:00
145 lines
5.3 KiB
C#
145 lines
5.3 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.HLE;
|
|
using SharpEmu.Libs.Kernel;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.Libs.Tests.Pthread;
|
|
|
|
public sealed class PthreadSemaphoreSemanticsTests
|
|
{
|
|
private const ulong MemoryBase = 0x1_0000_0000;
|
|
private const ulong SemaphoreAddress = MemoryBase + 0x100;
|
|
private const ulong ValueAddress = MemoryBase + 0x200;
|
|
|
|
[Theory]
|
|
[InlineData("C36iRE0F5sE", "scePthreadSemWait")]
|
|
[InlineData("aishVAiFaYM", "scePthreadSemPost")]
|
|
[InlineData("H2a+IN9TP0E", "scePthreadSemTrywait")]
|
|
[InlineData("GEnUkDZoUwY", "scePthreadSemInit")]
|
|
[InlineData("Vwc+L05e6oE", "scePthreadSemDestroy")]
|
|
public void RegistryResolvesPthreadSemaphoreExports(string nid, string exportName)
|
|
{
|
|
var manager = new ModuleManager();
|
|
manager.RegisterExports(SharpEmu.Generated.SysAbiExportRegistry.CreateExports(
|
|
Generation.Gen4 | Generation.Gen5));
|
|
|
|
Assert.True(manager.TryGetExport(nid, out var export));
|
|
Assert.Equal(exportName, export.Name);
|
|
Assert.Equal("libKernel", export.LibraryName);
|
|
}
|
|
|
|
[Fact]
|
|
public void PthreadSemPostAndWaitShareSemaphoreState()
|
|
{
|
|
var context = CreateContext();
|
|
InitializeSemaphore(context, initialCount: 0);
|
|
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
Assert.Equal(0, KernelSemaphoreCompatExports.PthreadSemPost(context));
|
|
Assert.Equal(0UL, context[CpuRegister.Rax]);
|
|
Assert.Equal(1, ReadSemaphoreValue(context));
|
|
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
Assert.Equal(0, KernelSemaphoreCompatExports.PthreadSemWait(context));
|
|
Assert.Equal(0UL, context[CpuRegister.Rax]);
|
|
Assert.Equal(0, ReadSemaphoreValue(context));
|
|
|
|
DestroySemaphore(context);
|
|
}
|
|
|
|
[Fact]
|
|
public void PthreadSemTryWaitConsumesAvailableTokenAndReturnsTryAgainWhenEmpty()
|
|
{
|
|
var context = CreateContext();
|
|
InitializeSemaphore(context, initialCount: 1);
|
|
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
Assert.Equal(0, KernelSemaphoreCompatExports.PthreadSemTryWait(context));
|
|
Assert.Equal(0, ReadSemaphoreValue(context));
|
|
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN,
|
|
KernelSemaphoreCompatExports.PthreadSemTryWait(context));
|
|
Assert.Equal(unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN),
|
|
context[CpuRegister.Rax]);
|
|
|
|
DestroySemaphore(context);
|
|
}
|
|
|
|
[Fact]
|
|
public void PthreadSemInitRejectsNonPrivateFlag()
|
|
{
|
|
var context = CreateContext();
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
context[CpuRegister.Rsi] = 1;
|
|
context[CpuRegister.Rdx] = 0;
|
|
context[CpuRegister.Rcx] = 0;
|
|
|
|
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT,
|
|
KernelSemaphoreCompatExports.PthreadSemInit(context));
|
|
Assert.Equal(unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT),
|
|
context[CpuRegister.Rax]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PthreadSemDestroyClearsSemaphoreAndRejectsSecondDestroy()
|
|
{
|
|
var context = CreateContext();
|
|
InitializeSemaphore(context, initialCount: 0);
|
|
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
Assert.Equal(0, KernelSemaphoreCompatExports.PthreadSemDestroy(context));
|
|
Assert.True(context.TryReadUInt32(SemaphoreAddress, out var handle));
|
|
Assert.Equal(0U, handle);
|
|
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT,
|
|
KernelSemaphoreCompatExports.PthreadSemDestroy(context));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(true)]
|
|
[InlineData(false)]
|
|
public void PthreadSemOperationRejectsInvalidSemaphore(bool post)
|
|
{
|
|
var context = CreateContext();
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
|
|
var result = post
|
|
? KernelSemaphoreCompatExports.PthreadSemPost(context)
|
|
: KernelSemaphoreCompatExports.PthreadSemWait(context);
|
|
|
|
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT, result);
|
|
Assert.Equal(unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT),
|
|
context[CpuRegister.Rax]);
|
|
}
|
|
|
|
private static CpuContext CreateContext() => new(new FakeCpuMemory(MemoryBase, 0x1000), Generation.Gen5);
|
|
|
|
private static void InitializeSemaphore(CpuContext context, uint initialCount)
|
|
{
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
context[CpuRegister.Rsi] = 0;
|
|
context[CpuRegister.Rdx] = initialCount;
|
|
context[CpuRegister.Rcx] = 0;
|
|
Assert.Equal(0, KernelSemaphoreCompatExports.PthreadSemInit(context));
|
|
}
|
|
|
|
private static int ReadSemaphoreValue(CpuContext context)
|
|
{
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
context[CpuRegister.Rsi] = ValueAddress;
|
|
Assert.Equal(0, KernelSemaphoreCompatExports.PosixSemGetValue(context));
|
|
Assert.True(context.TryReadUInt32(ValueAddress, out var value));
|
|
return unchecked((int)value);
|
|
}
|
|
|
|
private static void DestroySemaphore(CpuContext context)
|
|
{
|
|
context[CpuRegister.Rdi] = SemaphoreAddress;
|
|
Assert.Equal(0, KernelSemaphoreCompatExports.PthreadSemDestroy(context));
|
|
}
|
|
}
|