Files
sharpemu/tests/SharpEmu.Libs.Tests/Pthread/PthreadSemaphoreSemanticsTests.cs
T

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));
}
}