libSceFiber + libSceUlt (#92)

* libSceFiber

* Minor

* Minor

* Minor

* Minor

* Struct align

* Minor

* Minor

* Minor

* Minor

* Minor

* sceDepthInitialize + sceDepthQueryMemory

* Minor

* ult

* More ult funcs

* Minor

* Minor

* Minor

* sceUlt mutex

* Minor

* wait instead of yield

* sceUltUlthreadExit

* Minor

* Minor

* Minor

* Adds assertion

* Minor

* sceUltSemaphoreXxx

* sceNpTssCreateNpTitleCtx

* Adjust SceUltUlthreadRuntime size

* Adds reversing info of SceFiber structs

* Minor

* Minor
This commit is contained in:
Kagamma 2023-02-26 22:48:22 +07:00 committed by GitHub
parent 23947a2662
commit 224254aee9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1253 additions and 7 deletions

View File

@ -31,7 +31,7 @@
<PackageName Value="LCL"/>
</Item1>
</RequiredPackages>
<Units Count="135">
<Units Count="138">
<Unit0>
<Filename Value="fpPS4.lpr"/>
<IsPartOfProject Value="True"/>
@ -620,6 +620,21 @@
<Filename Value="src\audiodec\ps4_libsceaudiodeccpu.pas"/>
<IsPartOfProject Value="True"/>
</Unit134>
<Unit135>
<Filename Value="src\ps4_libscefiber.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="ps4_libSceFiber"/>
</Unit135>
<Unit136>
<Filename Value="src\ps4_libscedepth.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="ps4_libSceDepth"/>
</Unit136>
<Unit137>
<Filename Value="src\np\ps4_libscenptus.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="ps4_libSceNpTus"/>
</Unit137>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -14,6 +14,7 @@ uses
sys_types,
sys_pthread,
sys_path,
sys_kernel,
ps4libdoc,
ps4_libkernel,
ps4_libSceLibcInternal,
@ -53,6 +54,7 @@ uses
ps4_libSceComposite,
ps4_libSceSysCore,
ps4_libSceSsl,
ps4_libSceFiber,
ps4_libSceUlt,
ps4_libSceGameLiveStreaming,
ps4_libSceSharePlay,
@ -60,6 +62,8 @@ uses
ps4_libSceContentExport,
ps4_libSceUsbd,
ps4_libSceAudiodecCpu,
ps4_libSceDepth,
ps4_libSceNpTus,
ps4_elf,
ps4_pthread,
ps4_program,
@ -224,7 +228,7 @@ end;
procedure print_stub(nid:QWORD;lib:PLIBRARY); MS_ABI_Default;
begin
Writeln(StdErr,'nop nid:',lib^.strName,':',HexStr(nid,16),':',ps4libdoc.GetFunctName(nid));
Writeln(StdErr,SysLogPrefix,'nop nid:',lib^.strName,':',HexStr(nid,16),':',ps4libdoc.GetFunctName(nid));
//DebugBreak;
Sleep(INFINITE);
//readln;

View File

@ -0,0 +1,32 @@
unit ps4_libSceNpTus;
{$mode ObjFPC}{$H+}
interface
uses
ps4_program;
implementation
function ps4_sceNpTssCreateNpTitleCtx(serviceLabel:DWord;id:Integer):Integer; SysV_ABI_CDecl;
begin
Result:=-1;
end;
function Load_libSceNpTus(Const name:RawByteString):TElf_node;
var
lib:PLIBRARY;
begin
Result:=TElf_node.Create;
Result.pFileName:=name;
lib:=Result._add_lib('libSceNpTus');
lib^.set_proc($B1155BD827F41878,@ps4_sceNpTssCreateNpTitleCtx);
end;
initialization
ps4_app.RegistredPreLoad('libSceNpTus.prx',@Load_libSceNpTus);
end.

46
src/ps4_libscedepth.pas Normal file
View File

@ -0,0 +1,46 @@
unit ps4_libSceDepth;
{$mode ObjFPC}{$H+}
interface
uses
ps4_program;
implementation
type
PSceDepthInitializeParameter=Pointer;
PSceDepthQueryMemoryResult=Pointer;
function ps4_sceDepthQueryMemory(initParam:PSceDepthInitializeParameter;queryMem:PSceDepthQueryMemoryResult):Integer; SysV_ABI_CDecl;
begin
Writeln('sceDepthQueryMemory');
Result:=0;
end;
function ps4_sceDepthInitialize(initParam:PSceDepthInitializeParameter;queryMem:PSceDepthQueryMemoryResult):Integer; SysV_ABI_CDecl;
begin
Writeln('sceDepthInitialize');
Result:=0;
end;
function Load_libSceDepth(Const name:RawByteString):TElf_node;
var
lib:PLIBRARY;
begin
Result:=TElf_node.Create;
Result.pFileName:=name;
lib:=Result._add_lib('libSceDepth');
lib^.set_proc($E4CC31224DFF80BA,@ps4_sceDepthQueryMemory);
lib^.set_proc($3DA6BD7601E71E94,@ps4_sceDepthInitialize);
end;
initialization
ps4_app.RegistredPreLoad('libSceDepth.prx',@Load_libSceDepth);
end.

403
src/ps4_libscefiber.pas Normal file
View File

@ -0,0 +1,403 @@
unit ps4_libSceFiber;
{$mode ObjFPC}{$H+}
interface
uses
Windows,
SysUtils,
ps4_program,
sys_kernel;
const
SCE_FIBER_ERROR_NULL =$80590001;
SCE_FIBER_ERROR_RANGE =$80590003;
SCE_FIBER_ERROR_INVALID =$80590004;
SCE_FIBER_ERROR_PERMISSION=$80590005;
SCE_FIBER_ERROR_STATE =$80590006;
SCE_FIBER_CONTEXT_MINIMUM_SIZE=512;
SCE_FIBER_MAX_NAME_LENGTH =31;
// Our own defined state constants. Not match with the lib
FIBER_STATE_INIT =0;
FIBER_STATE_RUN =1;
FIBER_STATE_SUSPEND=2;
type
SceFiberEntry=procedure(argInit,argRun:QWord); SysV_ABI_CDecl;
// Original struct layout from sce_module v1.75. Different sce_module version may have different struct layout.
{
SceFiberContext=packed record // Internal struct storing context
rsp :QWord; // 8
rbp :QWord; // 16
rip :QWord; // 24
rbx :QWord; // 32
r12 :QWord; // 40
r13 :QWord; // 48
r14 :QWord; // 56
r15 :QWord; // 64
fnstcw :Word; // 66
_unknown1 :array[0..1] of Byte; // 68
mxcsr :DWord; // 72
end;
SceFiber=packed record
sig1 :DWord; // 4 - Signature = $DEF1649C
state :DWord; // 8
entry :Pointer; // 16 - Entry function pointer
initValue :QWord; // 24
addrContext :Pointer; // 32
sizeContext :QWord; // 40
name :array[0..31] of Byte; // 72
context :SceFiberContext; // 144
_unknown1 :array[0..87] of Byte; // 232
sizeContextMirror:QWord; // 240
addrContextEnd :Pointer; // 248 - Point to the start of "stack", probably?
_unknown2 :DWord; // 252
sig2 :DWord; // 256 - Signature = $B37592A0
end;
SceFiberContext2=packed record // The struct that will be passed to set_context function. It is represented as array of 6 uint64s in ghidra
entry :Pointer; // 8
initValue :QWord; // 16
runValue :QWord; // 24
stackAddr :Pointer; // 32 - addrContextEnd
returnToMainThreadEntry:Pointer; // 40
_unknown1 :Pointer; // 48
end;
}
// While we keep the size correct, the content is not the same as the one in original lib
SceFiber=packed record
handle :Pointer; // 8 - Pointer to Windows's fiber
name :array[0..SCE_FIBER_MAX_NAME_LENGTH] of Char; // 40
entry :SceFiberEntry; // 48
argInit :QWord; // 56
pArgRun :PQWord; // 64
pArgReturn :PQWord; // 72
argRun :QWord; // 80
argReturn :QWord; // 88
state :QWord; // 96 - 0 = init, 1 = running, 2 = suspend
addrContext :Pointer; // 104
sizeContext :QWord; // 112
sizeContextMargin:QWord; // 120
_unknown :array[0..255-120] of Byte; // 256
end;
PSceFiber =^SceFiber;
PPSceFiber=^PSceFiber;
// While we keep the size correct, the content is not the same as the one in original lib
SceFiberOptParam=packed record
_unknown:array[0..127] of Byte;
end;
PSceFiberOptParam=^SceFiberOptParam;
SceFiberInfo=packed record
size :QWord; // 8
entry :SceFiberEntry; // 16
argInit :QWord; // 24
addrContext :Pointer; // 32
sizeContext :QWord; // 40
name :array[0..SCE_FIBER_MAX_NAME_LENGTH] of Char; // 72
sizeContextMargin:QWord; // 80
_unkown :array[0..127-80] of Byte; // 128
end;
PSceFiberInfo=^SceFiberInfo;
function GetFiberString:RawByteString;
function GetFiberStringParam(fiber:PSceFiber):RawByteString;
function ps4_sceFiberInitialize(fiber :PSceFiber;
name :PChar;
entry :SceFiberEntry;
argInit :QWord;
addrContext:Pointer;
sizeContext:QWord;
option :PSceFiberOptParam):Integer; SysV_ABI_CDecl;
function ps4_sceFiberFinalize(fiber:PSceFiber):Integer; SysV_ABI_CDecl;
function ps4_sceFiberSwitch(fiber:PSceFiber;argRunTo:QWord;argRun:PQWord):Integer; SysV_ABI_CDecl;
function ps4_sceFiberRun(fiber:PSceFiber;argRun:QWord;argReturn:PQWord):Integer; SysV_ABI_CDecl;
function ps4_sceFiberReturnToThread(argReturn:QWord;argRun:PQWord):Integer; SysV_ABI_CDecl;
implementation
threadvar
_threadFiber :Pointer; // TODO: Memory leak if thread is destroyed
_currentFiber:PSceFiber;
// Imports
function ConvertThreadToFiber(lpParameter:LPVOID):LPVOID; external 'kernel32' name 'ConvertThreadToFiber';
function CreateFiber(dwStackSize:size_t;lpStartAddress:LPVOID;lpParameter:LPVOID):LPVOID; external 'kernel32' name 'CreateFiber';
procedure SwitchToFiber(lpFiber:LPVOID); external 'kernel32' name 'SwitchToFiber';
procedure DeleteFiber(lpFiber:LPVOID); external 'kernel32' name 'DeleteFiber';
// Wrappers
procedure _CheckFail(P:Pointer);
begin
if P=nil then
raise Exception.Create(Format('Error with code %d', [GetLastError]));
end;
function GetFiberStringParam(fiber:PSceFiber):RawByteString;
begin
Result:='F:['+PChar(@fiber^.name[0])+':'+IntToStr(QWord(fiber^.handle))+'] ';
end;
function GetFiberString:RawByteString;
begin
if _currentFiber<>nil then
Result:=GetFiberStringParam(_currentFiber);
end;
procedure _CreateThreadFiber;
begin
// Create a main fiber
if _threadFiber=nil then
begin
_threadFiber:=ConvertThreadToFiber(nil);
_CheckFail(_threadFiber);
end;
end;
procedure _FiberEntry(fiber:PSceFiber);
var
argRun :QWord=0;
argReturn:QWord=0;
begin
_currentFiber:=fiber;
Writeln(SysLogPrefix,'_FiberEntry Start');
if fiber^.pArgRun<>nil then
argRun:=fiber^.pArgRun^;
fiber^.entry(fiber^.argInit,argRun);
Writeln(SysLogPrefix,'_FiberEntry End');
if fiber^.pArgReturn<>nil then
argReturn:=fiber^.pArgReturn^;
ps4_sceFiberReturnToThread(fiber^.argReturn,fiber^.pArgRun);
end;
function _CreateFiber(fiber :PSceFiber;
name :PChar;
entry :SceFiberEntry;
argInit :QWord;
addrContext:Pointer;
sizeContext:QWord):Integer;
begin
fiber^.argInit :=argInit;
fiber^.argRun :=0;
fiber^.argReturn :=0;
fiber^.pArgRun :=@fiber^.argRun;
fiber^.pArgReturn :=@fiber^.argReturn;
fiber^.entry :=entry;
fiber^.state :=FIBER_STATE_INIT;
fiber^.addrContext :=addrContext;
fiber^.sizeContext :=sizeContext;
fiber^.sizeContextMargin:=sizeContext;
fiber^.handle :=CreateFiber(sizeContext,@_FiberEntry,fiber);
StrLCopy(@fiber^.name[0],name,SCE_FIBER_MAX_NAME_LENGTH);
Result:=0;
end;
function _RunFiber(fiber:PSceFiber;argRun:QWord;argReturn:PQWord):Integer;
begin
if _currentFiber<>nil then
Exit(SCE_FIBER_ERROR_PERMISSION);
_CreateThreadFiber;
_currentFiber :=fiber;
if fiber^.pArgRun<>nil then
fiber^.pArgRun^ :=argRun;
fiber^.pArgReturn:=argReturn;
fiber^.state :=FIBER_STATE_RUN;
SwitchToFiber(fiber^.handle);
Result:=0;
end;
function _DeleteFiber(fiber:PSceFiber):Integer;
begin
if (fiber^.state=FIBER_STATE_RUN) or (fiber^.state=FIBER_STATE_SUSPEND) then
DeleteFiber(fiber^.handle);
Result:=0;
end;
function _ReCreateFiber(fiber:PSceFiber):Integer;
begin
_DeleteFiber(fiber);
fiber^.handle:=CreateFiber(fiber^.sizeContext,@_FiberEntry,fiber);
fiber^.state :=FIBER_STATE_INIT;
Result:=0;
end;
function _SwitchFiber(fiber:PSceFiber;argRunTo:QWord;argRun:PQWord):Integer; SysV_ABI_CDecl;
begin
if _currentFiber^.addrContext<>nil then
begin
_currentFiber^.state:=FIBER_STATE_SUSPEND;
end else
begin
_ReCreateFiber(_currentFiber);
end;
if _currentFiber^.pArgRun<>nil then
_currentFiber^.pArgRun^:=argRunTo;
fiber^.pArgRun :=argRun;
fiber^.state :=FIBER_STATE_RUN;
_currentFiber :=fiber;
SwitchToFiber(fiber^.handle);
Result:=0;
end;
// APIs
function ps4_sceFiberSwitch(fiber:PSceFiber;argRunTo:QWord;argRun:PQWord):Integer; SysV_ABI_CDecl;
begin
if _currentFiber=nil then
Exit(SCE_FIBER_ERROR_PERMISSION);
if fiber=nil then
Exit(SCE_FIBER_ERROR_NULL);
Writeln(SysLogPrefix,'sceFiberSwitch,to=',GetFiberStringParam(fiber));
Result:=_SwitchFiber(fiber,argRunTo,argRun);
end;
function ps4_sceFiberInitialize(fiber :PSceFiber;
name :PChar;
entry :SceFiberEntry;
argInit :QWord;
addrContext:Pointer;
sizeContext:QWord;
option :PSceFiberOptParam):Integer; SysV_ABI_CDecl;
begin
if fiber=nil then
Exit(SCE_FIBER_ERROR_NULL);
if (sizeContext mod 16<>0) or
((addrContext=nil) and (sizeContext>0)) or
((addrContext<>nil) and (sizeContext=0)) then
Exit(SCE_FIBER_ERROR_INVALID);
if sizeContext<SCE_FIBER_CONTEXT_MINIMUM_SIZE then
Exit(SCE_FIBER_ERROR_RANGE);
Result:=_CreateFiber(fiber,name,entry,argInit,addrContext,sizeContext);
Writeln(SysLogPrefix,'sceFiberInitialize,fiber=',GetFiberStringParam(fiber),',sizeContext=',sizeContext,',argInit=',argInit);
end;
function ps4_sceFiberFinalize(fiber:PSceFiber):Integer; SysV_ABI_CDecl;
begin
if fiber=nil then
Exit(SCE_FIBER_ERROR_NULL);
if fiber=_currentFiber then
Exit(SCE_FIBER_ERROR_STATE);
Writeln(SysLogPrefix,'sceFiberFinalize,fiber=',GetFiberStringParam(fiber));
Result:=_DeleteFiber(fiber);
end;
function ps4_sceFiberRun(fiber:PSceFiber;argRun:QWord;argReturn:PQWord):Integer; SysV_ABI_CDecl;
begin
if fiber=nil then
Exit(SCE_FIBER_ERROR_NULL);
if fiber^.state=1 then
Exit(SCE_FIBER_ERROR_STATE);
Writeln(SysLogPrefix,'sceFiberRun,fiber=',GetFiberStringParam(fiber));
fiber^.state:=1;
Result:=_RunFiber(fiber,argRun,argReturn);
end;
function ps4_sceFiberReturnToThread(argReturn:QWord;argRun:PQWord):Integer; SysV_ABI_CDecl;
begin
if _currentFiber=nil then
Exit(SCE_FIBER_ERROR_PERMISSION);
Writeln(SysLogPrefix,'sceFiberReturnToThread');
if _currentFiber^.addrContext<>nil then
begin
_currentFiber^.state:=FIBER_STATE_SUSPEND;
end else
begin
_ReCreateFiber(_currentFiber); // TODO: Proper reset fiber
end;
_currentFiber^.pArgRun :=argRun;
if _currentFiber^.pArgReturn<>nil then
_currentFiber^.pArgReturn^:=argReturn;
_currentFiber:=nil;
SwitchToFiber(_threadFiber);
Result:=0;
end;
function ps4_sceFiberOptParamInitialize(param:PSceFiberOptParam):Integer; SysV_ABI_CDecl;
begin
if param=nil then
Exit(SCE_FIBER_ERROR_NULL);
Writeln(SysLogPrefix,'sceFiberOptParamInitialize');
Result:=0;
end;
function ps4_sceFiberStartContextSizeCheck(flags:DWord):Integer; SysV_ABI_CDecl;
begin
Writeln(SysLogPrefix,'sceFiberStartContextSizeCheck');
Result:=0;
end;
function ps4_sceFiberStopContextSizeCheck(flags:DWord):Integer; SysV_ABI_CDecl;
begin
Writeln(SysLogPrefix,'sceFiberStopContextSizeCheck');
Result:=0;
end;
function ps4_sceFiberRename(fiber:PSceFiber;name:PChar):Integer; SysV_ABI_CDecl;
begin
//Writeln(SysLogPrefix,'sceFiberRename,fiber=',GetFiberStringParam(fiber),',newName=',name);
if fiber=nil then
Exit(SCE_FIBER_ERROR_NULL);
StrLCopy(@fiber^.name[0],name,SCE_FIBER_MAX_NAME_LENGTH);
Result:=0;
end;
function ps4_sceFiberGetSelf(pfiber:PPSceFiber):Integer; SysV_ABI_CDecl;
begin
if _currentFiber=nil then
Exit(SCE_FIBER_ERROR_PERMISSION);
if pfiber=nil then
Exit(SCE_FIBER_ERROR_NULL);
//Writeln(SysLogPrefix,'sceFiberGetSelf');
pfiber^:=_currentFiber;
Result:=0;
end;
function ps4_sceFiberGetInfo(fiber:PSceFiber;fiberInfo:PSceFiberInfo):Integer; SysV_ABI_CDecl;
begin
if (fiber=nil) or (fiberInfo=nil) then
Exit(SCE_FIBER_ERROR_NULL);
fiberInfo^.size :=128;
fiberInfo^.entry :=fiber^.entry;
fiberInfo^.argInit :=fiber^.argInit;
fiberInfo^.addrContext :=fiber^.addrContext;
fiberInfo^.sizeContext :=fiber^.sizeContext;
fiberInfo^.sizeContextMargin:=fiber^.sizeContextMargin;
Result:=0;
end;
function Load_libSceFiber(Const name:RawByteString):TElf_node;
var
lib:PLIBRARY;
begin
Result:=TElf_node.Create;
Result.pFileName:=name;
lib:=Result._add_lib('libSceFiber');
lib^.set_proc($855603ECEBB6A424,@ps4_sceFiberInitialize);
lib^.set_proc($25E357E45FCDCD05,@ps4_sceFiberFinalize);
lib^.set_proc($6B42CBAD959A7343,@ps4_sceFiberRun);
lib^.set_proc($3C54F64BFB49ED49,@ps4_sceFiberSwitch);
lib^.set_proc($074657DA1C7D0CCC,@ps4_sceFiberReturnToThread);
lib^.set_proc($6AC8D4249F9A6BCB,@ps4_sceFiberOptParamInitialize);
lib^.set_proc($2DCAADCBE40D5857,@ps4_sceFiberStartContextSizeCheck);
lib^.set_proc($2A3E275CCA6733C6,@ps4_sceFiberStopContextSizeCheck);
lib^.set_proc($273C93F75B9C1837,@ps4_sceFiberRename);
lib^.set_proc($A7ECCB20E836EF35,@ps4_sceFiberGetSelf);
lib^.set_proc($BAAD98E41173D0F1,@ps4_sceFiberGetInfo);
end;
initialization
ps4_app.RegistredPreLoad('libSceFiber.prx',@Load_libSceFiber);
end.

View File

@ -1,20 +1,739 @@
unit ps4_libSceUlt;
{$mode ObjFPC}{$H+}
{$modeswitch ADVANCEDRECORDS}
interface
uses
ps4_program;
SysUtils,
ps4_program,
sys_pthread,
sys_kernel,
ps4_pthread,
ps4_mutex,
ps4_sema,
Generics.Collections;
implementation
uses
ps4_libSceFiber;
{$I sce_errno.inc}
const
SCE_ULT_ERROR_NULL =$80810001;
SCE_ULT_ERROR_ALIGNMENT =$80810002;
SCE_ULT_ERROR_RANGE =$80810003;
SCE_ULT_ERROR_INVALID =$80810004;
SCE_ULT_ERROR_PERMISSION =$80810005;
SCE_ULT_ERROR_STATE =$80810006;
SCE_ULT_ERROR_BUSY =$80810007;
SCE_ULT_ERROR_AGAIN =$80810008;
SCE_ULT_ERROR_NOT_INITIALIZE=$8081000A;
SCE_ULT_MAX_NAME_LENGTH =31;
ULT_STATE_INIT =0;
ULT_STATE_RUN =1;
ULT_STATE_SUSPEND =2;
ULT_STATE_PREPARE_JOIN=3;
ULT_STATE_DESTROYED =4;
ULT_STATE_WAIT =5;
ULT_EVENT_TIMEOUT =5000;
type
SceUltUlthreadEntry=function(arg:QWord):Integer; SysV_ABI_CDecl;
PWorkerThread =^TWorkerThread;
PSceUltUlthreadRuntime =^SceUltUlthreadRuntime;
PSceUltUlthreadRuntimeOptParam =^SceUltUlthreadRuntimeOptParam;
PSceUltWaitingQueueResourcePoolOptParam=^SceUltWaitingQueueResourcePoolOptParam;
PSceUltUlthreadOptParam =^SceUltUlthreadOptParam;
PSceUltWaitingQueueResourcePool =^SceUltWaitingQueueResourcePool;
PSceUltUlthread =^SceUltUlthread;
PSceUltQueueDataResourcePoolOptParam =^SceUltQueueDataResourcePoolOptParam;
PSceUltQueueDataResourcePool =^SceUltQueueDataResourcePool;
PSceUltQueueOptParam =^SceUltQueueOptParam;
PSceUltQueue =^SceUltQueue;
PSceUltMutexOptParam =^SceUltMutexOptParam;
PSceUltMutex =^SceUltMutex;
PSceUltSemaphoreOptParam =^SceUltSemaphoreOptParam;
PSceUltSemaphore =^SceUltSemaphore;
SceUltUlthreadRuntimeOptParam=packed record
oneShotThreadStackSize :QWord; // 8
workerThreadCpuAffinityMask:QWord; // 16
workerThreadPriority :Integer; // 20
inheritSched :Integer; // 24
_unknown :array[0..127-24] of Byte; // 128
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltUlthread=packed record
fiber :PSceFiber; // 8
entry :SceUltUlthreadEntry; // 16
arg :QWord; // 24
state :QWord; // 32
runtime :PSceUltUlthreadRuntime; // 40
returnStatus:QWord; // 48
_unknown:array[0..255-48] of Byte; // 256
procedure init(const aRuntime:PSceUltUlthreadRuntime;
const aName:PChar;
const aEntry:SceUltUlthreadEntry;
const aArg:QWord;
const aContext:Pointer;
const aSizeContext:QWord);
function getState:Integer;
procedure destroy;
end;
TUlThreadList=specialize TList<PSceUltUlthread>;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltUlthreadOptParam=packed record
attr :DWord; // 4
_unknown:array[0..127-4] of Byte; // 128
end;
TWorkerThread=record
thread :pthread;
runtime :PSceUltUlthreadRuntime;
ulThreadList:TUlThreadList;
current :QWord; // Current ulThread index
indx :QWord; // This worker thread index in runtime
wakeUpEvent :PRTLEvent;
end;
TWorkerThreadList=specialize TList<PWorkerThread>;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltUlthreadRuntime=packed record
param :SceUltUlthreadRuntimeOptParam; // 128
name :array[0..SCE_ULT_MAX_NAME_LENGTH] of Char; // 160
maxUlThread :Integer; // 164
maxWorkerThread :Integer; // 168
workerThreadList:TWorkerThreadList; // 176
ulThreadCount :QWord; // 184
balancer :QWord; // 192
cs :TRTLCriticalSection; // 232
_unknown:array[0..4095-232] of Byte; // 4096
procedure enter;
procedure leave;
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltWaitingQueueResourcePoolOptParam=packed record
_unknown:array[0..127] of Byte; // 128
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltWaitingQueueResourcePool=packed record
param :SceUltWaitingQueueResourcePoolOptParam; // 128
name :array[0..SCE_ULT_MAX_NAME_LENGTH] of Char; // 160
numThreads :DWord; // 168
numSyncObjects:DWord; // 176
workArea :Pointer; // 184
_unknown:array[0..255-184] of Byte; // 256
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltQueueDataResourcePoolOptParam=packed record
_unknown:array[0..127] of Byte; // 128
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltQueueDataResourcePool=packed record
param :SceUltQueueDataResourcePoolOptParam; // 128
numData :DWord; // 132
numQueueObjects:DWord; // 136
dataSize :QWord; // 144
waitingQueue :PSceUltWaitingQueueResourcePool; // 152
workArea :Pointer; // 160
name :array[0..SCE_ULT_MAX_NAME_LENGTH] of Char; // 192
cs :TRTLCriticalSection; // 200
queuePtr :Pointer; // 208
pushEvent :PRTLEvent; // 216
popEvent :PRTLEvent; // 224
_unknown:array[0..511-224] of Byte; // 512
procedure enter;
procedure leave;
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltQueueOptParam=packed record
_unknown:array[0..127] of Byte; // 128
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltQueue=packed record
param :SceUltQueueOptParam; // 128
name :array[0..SCE_ULT_MAX_NAME_LENGTH] of Char; // 160
queueData :PSceUltQueueDataResourcePool; // 168
waitingQueue:PSceUltWaitingQueueResourcePool; // 176
_unknown:array[0..511-152] of Byte; // 512
function push(const aData:Pointer):Integer;
function pop(const aData:Pointer):Integer;
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltMutexOptParam=packed record
_unknown:array[0..127] of Byte; // 128
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltMutex=packed record
param :SceUltMutexOptParam; // 128
name :array[0..SCE_ULT_MAX_NAME_LENGTH] of Char; // 160
waitingQueue:PSceUltWaitingQueueResourcePool; // 168
handle :p_pthread_mutex; // 176
_unknown:array[0..255-176] of Byte; // 256
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltSemaphoreOptParam=packed record
_unknown:array[0..127] of Byte; // 128
end;
// While we keep the size correct, the content is not the same as the one in original lib
SceUltSemaphore=packed record
param :SceUltSemaphoreOptParam; // 128
name :array[0..SCE_ULT_MAX_NAME_LENGTH] of Char; // 160
waitingQueue:PSceUltWaitingQueueResourcePool; // 168
handle :PSceKernelSema; // 176
wakeUpEvent :PRTLEvent; // 186
_unknown:array[0..255-184] of Byte; // 256
end;
threadvar
_currentUlThread:PSceUltUlthread;
// Wrappers
procedure _workerThreadEntry(const workerThread:PWorkerThread); SysV_ABI_CDecl;
var
ulThread:PSceUltUlthread;
runtime :PSceUltUlthreadRuntime;
begin
assert(workerThread<>nil,'workerThread cannot be null');
Writeln(SysLogPrefix,'_workerThreadEntry Start');
runtime:=workerThread^.runtime;
while True do
begin
while workerThread^.ulThreadList.Count=0 do
begin
RTLEventWaitFor(workerThread^.wakeUpEvent,ULT_EVENT_TIMEOUT);
end;
if workerThread^.ulThreadList.Count=0 then
continue;
// Select next ulThread to be executed
runtime^.enter;
Inc(workerThread^.current);
if workerThread^.current>=workerThread^.ulThreadList.Count then
workerThread^.current:=0;
ulThread:=workerThread^.ulThreadList[workerThread^.current];
_currentUlThread:=ulThread;
runtime^.leave;
// Execute ulThread
if (ulThread^.state<>ULT_STATE_PREPARE_JOIN) and (ulThread^.state<>ULT_STATE_WAIT) then
begin
if ulThread^.fiber^.state=FIBER_STATE_INIT then
begin
ulThread^.state:=ULT_STATE_RUN;
ps4_sceFiberRun(ulThread^.fiber,0,@ulThread^.returnStatus);
end else
if ulThread^.fiber^.state=FIBER_STATE_SUSPEND then
ps4_sceFiberSwitch(ulThread^.fiber,0,nil);
end;
end;
Writeln(SysLogPrefix,'_workerThreadEntry End');
ps4_scePthreadExit(nil);
end;
procedure _ulThreadEntry(argInit,argRun:QWord); SysV_ABI_CDecl;
var
ulThread:PSceUltUlthread;
begin
ulThread:=PSceUltUlthread(argInit);
assert(ulThread<>nil,'ulThread cannot be null');
Writeln(SysLogPrefix,'_ulThreadEntry Start');
ulThread^.entry(ulThread^.arg);
Writeln(SysLogPrefix,'_ulThreadEntry End');
ulThread^.state:=ULT_STATE_PREPARE_JOIN;
end;
function _workerThreadCreate(const runtime:PSceUltUlthreadRuntime):PWorkerThread;
begin
New(Result);
Result^.current :=0;
Result^.ulThreadList:=TUlThreadList.Create;
Result^.ulThreadList.Capacity:=runtime^.maxUlThread;
Result^.runtime :=runtime;
Result^.indx :=runtime^.workerThreadList.Count;
Result^.wakeUpEvent :=RTLEventCreate;
runtime^.workerThreadList.Add(Result);
ps4_scePthreadCreate(@Result^.thread,nil,@_workerThreadEntry,Result,PChar(runtime^.name+'_ultWorker_'+IntToStr(Result^.indx)));
end;
procedure _currentUlThreadSetState(const state:QWord); inline;
begin
if _currentUlThread<>nil then
_currentUlThread^.state:=state;
end;
procedure SceUltUlthreadRuntime.enter;
begin
EnterCriticalSection(cs);
end;
procedure SceUltUlthreadRuntime.leave;
begin
LeaveCriticalSection(cs);
end;
procedure SceUltUlthread.init(const aRuntime:PSceUltUlthreadRuntime;
const aName:PChar;
const aEntry:SceUltUlthreadEntry;
const aArg:QWord;
const aContext:Pointer;
const aSizeContext:QWord);
var
workerThread:PWorkerThread;
begin
aRuntime^.enter;
fiber :=AllocMem(SizeOf(SceFiber));
runtime:=aRuntime;
entry :=aEntry;
arg :=aArg;
ps4_sceFiberInitialize(fiber,aName,@_ulThreadEntry,QWord(@Self),aContext,aSizeContext,nil);
workerThread:=runtime^.workerThreadList[runtime^.balancer mod runtime^.maxWorkerThread];
workerThread^.ulThreadList.Add(@Self);
Inc(runtime^.balancer);
RTLEventSetEvent(workerThread^.wakeUpEvent);
aRuntime^.leave;
end;
function SceUltUlthread.getState:Integer;
var
i,j :Integer;
workerThread:PWorkerThread;
begin
runtime^.enter;
for j:=0 to runtime^.workerThreadList.Count-1 do
begin
workerThread:=runtime^.workerThreadList[j];
for i:=0 to workerThread^.ulThreadList.Count-1 do
if workerThread^.ulThreadList[i]=@Self then
Exit(workerThread^.ulThreadList[i]^.state);
end;
Result:=ULT_STATE_DESTROYED;
runtime^.leave;
end;
procedure SceUltUlthread.destroy;
var
workerThread:PWorkerThread;
ulThread :PSceUltUlthread;
i,j :Integer;
begin
ulThread:=@Self;
runtime^.enter;
for j:=0 to runtime^.workerThreadList.Count-1 do
begin
workerThread:=runtime^.workerThreadList[j];
for i:=0 to workerThread^.ulThreadList.Count-1 do
if workerThread^.ulThreadList[i]=ulThread then
begin
ps4_sceFiberFinalize(ulThread^.fiber);
workerThread^.ulThreadList.Delete(i);
Exit;
end;
end;
runtime^.leave;
end;
procedure SceUltQueueDataResourcePool.enter;
begin
EnterCriticalSection(cs);
end;
procedure SceUltQueueDataResourcePool.leave;
begin
LeaveCriticalSection(cs);
end;
function SceUltQueue.push(const aData:Pointer):Integer;
begin
queueData^.enter;
while ((QWord(queueData^.queuePtr) - QWord(queueData^.workArea)) div queueData^.dataSize) >= queueData^.numData do
begin
assert(_currentUlThread=nil,'TODO: SceUltQueue.push currently not working with ulthreads');
_currentUlThreadSetState(ULT_STATE_WAIT);
queueData^.leave;
RTLEventWaitFor(queueData^.popEvent,ULT_EVENT_TIMEOUT);
queueData^.enter;
end;
_currentUlThreadSetState(ULT_STATE_RUN);
Move(aData^,queueData^.queuePtr^,queueData^.dataSize);
Inc(queueData^.queuePtr,queueData^.dataSize);
Result:=0;
RTLEventSetEvent(queueData^.pushEvent);
queueData^.leave;
end;
function SceUltQueue.pop(const aData:Pointer):Integer;
begin
queueData^.enter;
while QWord(queueData^.queuePtr) <= QWord(queueData^.workArea) do
begin
assert(_currentUlThread=nil,'TODO: SceUltQueue.pop currently not working with ulthreads');
_currentUlThreadSetState(ULT_STATE_WAIT);
queueData^.leave;
RTLEventWaitFor(queueData^.pushEvent,ULT_EVENT_TIMEOUT);
queueData^.enter;
end;
_currentUlThreadSetState(ULT_STATE_RUN);
Move(queueData^.workArea^,aData^,queueData^.dataSize);
Move((queueData^.workArea+queueData^.dataSize)^,queueData^.workArea^,QWord(queueData^.queuePtr)-QWord(queueData^.workArea)-queueData^.dataSize);
Dec(queueData^.queuePtr,queueData^.dataSize);
Result:=0;
RTLEventSetEvent(queueData^.popEvent);
queueData^.leave;
end;
// APIs
function ps4_sceUltInitialize():Integer; SysV_ABI_CDecl;
begin
Writeln('sceUltInitialize');
Writeln(SysLogPrefix,'sceUltInitialize');
Result:=0;
end;
function ps4_sceUltUlthreadRuntimeGetWorkAreaSize(maxUlThread,maxWorkerThread:DWord):QWord; SysV_ABI_CDecl;
begin
Result:=8; // TODO: Fake size. Not important for current implementation of this lib.
end;
function ps4_sceUltUlthreadRuntimeCreate(runtime :PSceUltUlthreadRuntime;
name :PChar;
maxUlThread :Integer;
maxWorkerThread:Integer;
workArea :Pointer;
param :PSceUltUlthreadRuntimeOptParam):Integer; SysV_ABI_CDecl;
var
i:Integer;
begin
if (runtime=nil) or (name=nil) or (workArea=nil) then
Exit(SCE_ULT_ERROR_NULL);
if (maxUlThread=0) or (maxWorkerThread=0) then
Exit(SCE_ULT_ERROR_INVALID);
Writeln(SysLogPrefix,'sceUltUlthreadRuntimeCreate,name=',name,',maxUltThread=',maxUlThread,',maxWorkerThread=',maxWorkerThread);
StrLCopy(@runtime^.name[0],name,SCE_ULT_MAX_NAME_LENGTH);
runtime^.maxUlThread :=maxUlThread;
runtime^.maxWorkerThread:=maxWorkerThread;
runtime^.balancer :=0;
InitCriticalSection(runtime^.cs);
if param<>nil then
runtime^.param:=param^
else
begin
runtime^.param.oneShotThreadStackSize :=16*1024;
runtime^.param.workerThreadCpuAffinityMask:=SCE_KERNEL_CPUMASK_6CPU_ALL;
runtime^.param.workerThreadPriority :=SCE_KERNEL_PRIO_FIFO_DEFAULT;
runtime^.param.inheritSched :=SCE_PTHREAD_INHERIT_SCHED;
end;
runtime^.workerThreadList:=TWorkerThreadList.Create;
runtime^.workerThreadList.Capacity:=maxWorkerThread;
for i:=0 to maxWorkerThread-1 do
_workerThreadCreate(runtime);
end;
function ps4_sceUltUlthreadRuntimeOptParamInitialize(optParam:PSceUltUlthreadOptParam):Integer; SysV_ABI_CDecl;
begin
Result:=0;
end;
function ps4_sceUltUlthreadCreate(ulThread :PSceUltUlthread;
name :PChar;
entry :SceUltUlthreadEntry;
arg :QWord;
context :Pointer;
sizeContext:QWord;
runtime :PSceUltUlthreadRuntime;
optParam :PSceUltUlthreadOptParam):Integer; SysV_ABI_CDecl;
begin
if (ulThread=nil) or (name=nil) or (entry=nil) or (runtime=nil) then
Exit(SCE_ULT_ERROR_NULL);
if (sizeContext>0) and (sizeContext<512) then
Exit(SCE_ULT_ERROR_RANGE);
if (context<>nil) and ((sizeContext=0) or (sizeContext mod 16<>0)) then
Exit(SCE_ULT_ERROR_INVALID);
if runtime^.workerThreadList=nil then
Exit(SCE_ULT_ERROR_STATE);
if runtime^.ulThreadCount>=runtime^.maxUlThread then
Exit(SCE_ULT_ERROR_AGAIN);
Writeln(SysLogPrefix,'sceUltUlthreadCreate,name=',name,'runtime=',runtime^.name);
ulThread^.init(runtime,name,entry,arg,context,sizeContext);
end;
function ps4_sceUltUlthreadJoin(ulThread:PSceUltUlthread;status:PInteger):Integer; SysV_ABI_CDecl;
begin
if ulThread=nil then
Exit(SCE_ULT_ERROR_NULL);
if ulThread=_currentUlThread then
Exit(SCE_ULT_ERROR_PERMISSION);
if ulThread^.getState=ULT_STATE_DESTROYED then
Exit(SCE_ULT_ERROR_STATE);
Writeln(SysLogPrefix,'sceUltUlthreadJoin,fiber=',GetFiberStringParam(ulThread^.fiber));
_currentUlThreadSetState(ULT_STATE_WAIT);
repeat
SwYieldExecution;
until ulThread^.getState=ULT_STATE_PREPARE_JOIN;
_currentUlThreadSetState(ULT_STATE_RUN);
//
if (status<>nil) and (ulThread^.fiber^.pArgReturn<>nil) then
begin
status^:=ulThread^.fiber^.pArgReturn^;
Writeln(SysLogPrefix,'Return code:',status^);
end;
ulThread^.destroy;
end;
function ps4_sceUltUlthreadExit(status:Integer):Integer; SysV_ABI_CDecl;
var
ulThread:PSceUltUlthread;
begin
ulThread:=_currentUlThread;
if ulThread=nil then
Exit(SCE_ULT_ERROR_PERMISSION);
Writeln(SysLogPrefix,'sceUltUlthreadExit,fiber=',GetFiberStringParam(ulThread^.fiber));
ulThread^.state:=ULT_STATE_PREPARE_JOIN;
ps4_sceFiberReturnToThread(status,nil);
end;
//
function ps4_sceUltWaitingQueueResourcePoolGetWorkAreaSize(numThreads,numSyncObjects:DWord):QWord; SysV_ABI_CDecl;
begin
Result:=8; // TODO: Fake size. Not important for current implementation of this lib.
end;
function ps4_sceUltWaitingQueueResourcePoolCreate(pool :PSceUltWaitingQueueResourcePool;
name :PChar;
numThreads :DWord;
numSyncObjects:DWord;
workArea :Pointer;
param :PSceUltWaitingQueueResourcePoolOptParam):Integer; SysV_ABI_CDecl;
begin
if (pool=nil) or (name=nil) or (workArea=nil) then
Exit(SCE_ULT_ERROR_NULL);
if (numThreads=0) or (numSyncObjects=0) then
Exit(SCE_ULT_ERROR_INVALID);
Writeln(SysLogPrefix,'sceUltWaitingQueueResourcePoolCreate,name=',name,',numThreads=',numThreads,',numSyncObjects=',numSyncObjects);
StrLCopy(@pool^.name[0],name,SCE_ULT_MAX_NAME_LENGTH);
pool^.numThreads :=numThreads;
pool^.numSyncObjects:=numSyncObjects;
pool^.workArea :=workArea;
pool^.workArea :=workArea;
if param<>nil then
pool^.param:=param^;
Result:=0; // TODO: Not used by current implementation of this lib.
end;
//
function ps4_sceUltQueueDataResourcePoolGetWorkAreaSize(numData:DWord;dataSize:QWord;numQueueObjects:DWord):Integer; SysV_ABI_CDecl;
begin
Result:=numData*dataSize*numQueueObjects;
end;
function ps4_sceUltQueueDataResourcePoolCreate(pool :PSceUltQueueDataResourcePool;
name :PChar;
numData :DWord;
dataSize :QWord;
numQueueObjects:DWord;
waitingQueue :PSceUltWaitingQueueResourcePool;
workArea :Pointer;
param :PSceUltQueueDataResourcePoolOptParam):Integer; SysV_ABI_CDecl;
begin
if (pool=nil) or (name=nil) or (workArea=nil) then
Exit(SCE_ULT_ERROR_NULL);
if (numData=0) or (dataSize=0) or (numQueueObjects=0) then
Exit(SCE_ULT_ERROR_INVALID);
assert(numQueueObjects=1,'TODO: numQueueObjects higher than 1');
Writeln(SysLogPrefix,'sceUltQueueDataResourcePoolCreate,name=',name,',numData=',numData,',dataSize=',dataSize,',numQueueObjects=',numQueueObjects);
StrLCopy(@pool^.name[0],name,SCE_ULT_MAX_NAME_LENGTH);
pool^.numData :=numData;
pool^.dataSize :=dataSize;
pool^.numQueueObjects:=numQueueObjects;
pool^.waitingQueue :=waitingQueue;
pool^.workArea :=workArea;
pool^.queuePtr :=workArea;
InitCriticalSection(pool^.cs);
pool^.pushEvent :=RTLEventCreate;
pool^.popEvent :=RTLEventCreate;
if param<>nil then
pool^.param:=param^;
Result:=0;
end;
//
function ps4_sceUltQueueCreate(queue :PSceUltQueue;
name :PChar;
dataSize :QWord;
waitingQueue:PSceUltWaitingQueueResourcePool;
queueData :PSceUltQueueDataResourcePool;
param :PSceUltQueueOptParam):Integer; SysV_ABI_CDecl;
begin
if (queue=nil) or (name=nil) or (queueData=nil) then
Exit(SCE_ULT_ERROR_NULL);
if (dataSize=0) or (dataSize>queueData^.dataSize) then
Exit(SCE_ULT_ERROR_INVALID);
Writeln(SysLogPrefix,'sceUltQueueCreate,name=',name,',dataSize=',dataSize);
StrLCopy(@queue^.name[0],name,SCE_ULT_MAX_NAME_LENGTH);
queue^.queueData :=queueData;
queue^.waitingQueue:=waitingQueue;
if param<>nil then
queue^.param:=param^;
Result:=0;
end;
function ps4_sceUltQueuePush(queue:PSceUltQueue;data:Pointer):Integer; SysV_ABI_CDecl;
begin
if (queue=nil) or (data=nil) then
Exit(SCE_ULT_ERROR_NULL);
Writeln(SysLogPrefix,'sceUltQueuePush,queue=',queue^.name);
Result:=queue^.push(data);
end;
function ps4_sceUltQueuePop(queue:PSceUltQueue;data:Pointer):Integer; SysV_ABI_CDecl;
begin
if (queue=nil) or (data=nil) then
Exit(SCE_ULT_ERROR_NULL);
Writeln(SysLogPrefix,'sceUltQueuePop,queue=',queue^.name);
Result:=queue^.pop(data);
end;
//
function ps4_sceUltMutexCreate(mutex :PSceUltMutex;
name :PChar;
waitingQueue:PSceUltWaitingQueueResourcePool;
param :PSceUltMutexOptParam):Integer; SysV_ABI_CDecl;
begin
if (mutex=nil) or (name=nil) or (waitingQueue=nil) then
Exit(SCE_ULT_ERROR_NULL);
Writeln(SysLogPrefix,'sceUltMutexCreate,name=',name);
mutex^.handle :=AllocMem(SizeOf(pthread_mutex));
ps4_pthread_mutex_init(mutex^.handle,nil);
StrLCopy(@mutex^.name[0],name,SCE_ULT_MAX_NAME_LENGTH);
mutex^.waitingQueue:=waitingQueue;
if param<>nil then
mutex^.param:=param^;
Result:=0;
end;
function ps4_sceUltMutexLock(mutex:PSceUltMutex):Integer; SysV_ABI_CDecl;
begin
if (mutex=nil) then
Exit(SCE_ULT_ERROR_NULL);
//Writeln(SysLogPrefix,'sceUltMutexLock,mutex=',mutex^.name);
ps4_pthread_mutex_lock(mutex^.handle);
assert(_currentUlThread=nil,'TODO: ps4_sceUltMutexLock currently not working with ulthreads');
end;
function ps4_sceUltMutexUnlock(mutex:PSceUltMutex):Integer; SysV_ABI_CDecl;
begin
if (mutex=nil) then
Exit(SCE_ULT_ERROR_NULL);
//Writeln(SysLogPrefix,'sceUltMutexUnlock,mutex=',mutex^.name);
ps4_pthread_mutex_unlock(mutex^.handle);
assert(_currentUlThread=nil,'TODO: ps4_sceUltMutexUnlock currently not working with ulthreads');
end;
function ps4_sceUltMutexOptParamInitialize(param:PSceUltMutexOptParam):Integer; SysV_ABI_CDecl;
begin
if param=nil then
Exit(SCE_ULT_ERROR_NULL);
//Writeln(SysLogPrefix,'sceUltMutexOptParamInitialize');
Result:=0;
end;
//
function ps4_sceUltSemaphoreCreate(semaphore :PSceUltSemaphore;
name :PChar;
numResource :Integer;
waitingQueue:PSceUltWaitingQueueResourcePool;
param :PSceUltSemaphoreOptParam):Integer; SysV_ABI_CDecl;
begin
if (semaphore=nil) or (name=nil) then
Exit(SCE_ULT_ERROR_NULL);
Writeln(SysLogPrefix,'sceUltSemaphoreCreate,name=',name,',numResource=',numResource);
semaphore^.handle :=AllocMem(SizeOf(pthread_mutex));
semaphore^.waitingQueue:=waitingQueue;
semaphore^.wakeUpEvent :=RTLEventCreate;
ps4_sceKernelCreateSema(semaphore^.handle,name,SCE_KERNEL_SEMA_ATTR_TH_FIFO,numResource,$7FFFFFFF,nil);
StrLCopy(@semaphore^.name[0],name,SCE_ULT_MAX_NAME_LENGTH);
if param<>nil then
semaphore^.param:=param^;
Result:=0;
end;
function ps4_sceUltSemaphoreTryAcquire(semaphore :PSceUltSemaphore;
numResource:DWord):Integer; SysV_ABI_CDecl;
var
r:Integer;
begin
if semaphore=nil then
Exit(SCE_ULT_ERROR_NULL);
if (numResource<=0) or (numResource>=$80000000) then
Exit(SCE_ULT_ERROR_RANGE);
//Writeln(SysLogPrefix,'sceUltSemaphoreTryAcquire,name=',semaphore^.name,',numResource=',numResource);
assert(_currentUlThread=nil,'TODO: ps4_sceUltSemaphoreTryAcquire currently not working with ulthreads');
r:=ps4_sceKernelPollSema(semaphore^.handle^,numResource);
if r=SCE_KERNEL_ERROR_EBUSY then
Exit(SCE_ULT_ERROR_AGAIN);
Result:=0;
end;
function ps4_sceUltSemaphoreAcquire(semaphore :PSceUltSemaphore;
numResource:DWord):Integer; SysV_ABI_CDecl;
begin
if semaphore=nil then
Exit(SCE_ULT_ERROR_NULL);
if (numResource<=0) or (numResource>=$80000000) then
Exit(SCE_ULT_ERROR_RANGE);
//Writeln(SysLogPrefix,'sceUltSemaphoreAcquire,name=',semaphore^.name,',numResource=',numResource);
assert(_currentUlThread=nil,'TODO: ps4_sceUltSemaphoreAcquire currently not working with ulthreads');
while ps4_sceKernelPollSema(semaphore^.handle^,numResource)<>0 do
begin
RTLEventWaitFor(semaphore^.wakeUpEvent,ULT_EVENT_TIMEOUT);
end;
Result:=0;
end;
function ps4_sceUltSemaphoreRelease(semaphore :PSceUltSemaphore;
numResource:DWord):Integer; SysV_ABI_CDecl;
begin
if semaphore=nil then
Exit(SCE_ULT_ERROR_NULL);
if (numResource<=0) or (numResource>=$80000000) then
Exit(SCE_ULT_ERROR_RANGE);
//Writeln(SysLogPrefix,'sceUltSemaphoreRelease,name=',semaphore^.name,',numResource=',numResource);
assert(_currentUlThread=nil,'TODO: ps4_sceUltSemaphoreRelease currently not working with ulthreads');
if ps4_sceKernelSignalSema(semaphore^.handle^,numResource)=SCE_KERNEL_ERROR_EINVAL then
Exit(SCE_ULT_ERROR_STATE);
Result:=0;
RTLEventSetEvent(semaphore^.wakeUpEvent);
end;
//
function Load_libSceUlt(Const name:RawByteString):TElf_node;
var
lib:PLIBRARY;
@ -25,6 +744,32 @@ begin
lib:=Result._add_lib('libSceUlt');
lib^.set_proc($859220D44586B073,@ps4_sceUltInitialize);
lib^.set_proc($82BB36A5B7366B03,@ps4_sceUltUlthreadRuntimeGetWorkAreaSize);
lib^.set_proc($576BB758BAF087AE,@ps4_sceUltUlthreadRuntimeOptParamInitialize);
lib^.set_proc($8F0F45919057A3F8,@ps4_sceUltUlthreadRuntimeCreate);
lib^.set_proc($CE7237ABC4BB290E,@ps4_sceUltUlthreadCreate);
lib^.set_proc($802780239ECB1A02,@ps4_sceUltUlthreadJoin);
lib^.set_proc($905FFFB37C598DCA,@ps4_sceUltUlthreadExit);
lib^.set_proc($588595D5077B3C55,@ps4_sceUltWaitingQueueResourcePoolGetWorkAreaSize);
lib^.set_proc($6221EE8CE1BDBD76,@ps4_sceUltWaitingQueueResourcePoolCreate);
lib^.set_proc($7AF8FD60F912F2CE,@ps4_sceUltQueueDataResourcePoolGetWorkAreaSize);
lib^.set_proc($4C51E6EBF37ABE4B,@ps4_sceUltQueueDataResourcePoolCreate);
lib^.set_proc($F58E6478EBDBEA89,@ps4_sceUltQueueCreate);
lib^.set_proc($754C295F77B93431,@ps4_sceUltQueuePush);
lib^.set_proc($4554AADADB26DB2C,@ps4_sceUltQueuePop);
lib^.set_proc($9A6B7C49AEAD2FA7,@ps4_sceUltMutexCreate);
lib^.set_proc($F21106911D697EBF,@ps4_sceUltMutexLock);
lib^.set_proc($8745DE6CA88C06D9,@ps4_sceUltMutexUnlock);
lib^.set_proc($D7EF2DF5A1CB8B3F,@ps4_sceUltMutexOptParamInitialize);
lib^.set_proc($8794252188FE468F,@ps4_sceUltSemaphoreCreate);
lib^.set_proc($1C0D4B75B8B794F6,@ps4_sceUltSemaphoreTryAcquire);
lib^.set_proc($4001F5A1F23DEEF5,@ps4_sceUltSemaphoreAcquire);
lib^.set_proc($95BB64E57D6679CC,@ps4_sceUltSemaphoreRelease);
end;
initialization

View File

@ -122,17 +122,18 @@ implementation
uses
ntapi,
ps4_libscefiber,
sys_pthread,
sys_signal,
sys_time;
function SysLogPrefix : string;
begin
// Add thread name and id as prefix to log messages
begin
// Add thread+fiber name and id as prefix to log messages
Result := '';
if _get_curthread <> nil then
begin
Result:='['+_get_curthread^.name + ':'+ IntToStr(_get_curthread^.ThreadId) + '] ';
Result:='['+_get_curthread^.name + ':'+ IntToStr(_get_curthread^.ThreadId) + '] ' + GetFiberString;
end;
end;