mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
[Kernel] Implement pthread semaphore exports (#424)
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user