unit ps4_mutex;

{$mode objfpc}{$H+}

interface

uses
  Windows,
  sysutils,
  sys_types;

type
 p_pthread_mutex_attr=^pthread_mutex_attr_t;
 pthread_mutex_attr_t=^pthread_mutex_attr;
 pthread_mutex_attr=bitpacked record
  _type:0..7;           //3
  _shared:0..1;         //1
  _protocol:0..3;       //2
  _align:0..67108863;   //26
  _prioceiling:Integer; //32
 end;

 p_pthread_mutex=^pthread_mutex;
 pthread_mutex=^pthread_mutex_t;
 pthread_mutex_t=packed record
  valid:DWORD;
  state:DWORD;
  _type:DWORD;
  rec_lock:DWORD;
  owner:DWORD;
  event:THandle;
  name:array[0..31] of AnsiChar;
 end;

 ScePthreadMutex=pthread_mutex;
 PScePthreadMutex=p_pthread_mutex;

const
 SCE_PTHREAD_MUTEX_ERRORCHECK = 1; // Default POSIX mutex
 SCE_PTHREAD_MUTEX_RECURSIVE  = 2; // Recursive mutex
 SCE_PTHREAD_MUTEX_NORMAL     = 3; // No error checking
 SCE_PTHREAD_MUTEX_ADAPTIVE   = 4; // Adaptive mutex, spins briefly before blocking on lock
 SCE_PTHREAD_MUTEX_DEFAULT    = SCE_PTHREAD_MUTEX_ERRORCHECK;

 PTHREAD_MUTEX_ERRORCHECK = SCE_PTHREAD_MUTEX_ERRORCHECK;
 PTHREAD_MUTEX_RECURSIVE  = SCE_PTHREAD_MUTEX_RECURSIVE;
 PTHREAD_MUTEX_NORMAL     = SCE_PTHREAD_MUTEX_NORMAL;
 PTHREAD_MUTEX_ADAPTIVE   = SCE_PTHREAD_MUTEX_ADAPTIVE;
 PTHREAD_MUTEX_DEFAULT    = SCE_PTHREAD_MUTEX_ERRORCHECK;{SCE_PTHREAD_MUTEX_RECURSIVE;}//PTHREAD_MUTEX_ERRORCHECK;

 PTHREAD_PROCESS_PRIVATE=0;
 PTHREAD_PROCESS_SHARED =1;

 PTHREAD_PRIO_NONE      =0;
 PTHREAD_PRIO_INHERIT   =1;
 PTHREAD_PRIO_PROTECT   =2;

 PTHREAD_MUTEX_INITIALIZER            =0;
 PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP=1;

function ps4_pthread_mutexattr_init(pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutexattr_destroy(pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutexattr_gettype(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutexattr_settype(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutexattr_getpshared(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutexattr_setpshared(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutexattr_getprotocol(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutexattr_setprotocol(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutexattr_getprioceiling(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutexattr_setprioceiling(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;

function ps4_pthread_mutex_lock(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutex_timedlock(pMutex:p_pthread_mutex;ts:Ptimespec):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutex_trylock(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutex_unlock(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutex_init(pMutex:p_pthread_mutex;pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
function ps4_pthread_mutex_destroy(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;

function ps4_pthread_mutex_setname_np(pMutex:p_pthread_mutex;name:Pchar):Integer; SysV_ABI_CDecl;

function ps4_scePthreadMutexattrInit(pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexattrDestroy(pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexattrGettype(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexattrSettype(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexattrGetprotocol(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexattrSetprotocol(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexattrGetprioceiling(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexattrSetprioceiling(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;

function ps4_scePthreadMutexInit(pMutex:PScePthreadMutex;pAttr:p_pthread_mutex_attr;str:PChar):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexLock(pMutex:PScePthreadMutex):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexTimedlock(pMutex:PScePthreadMutex;usec:DWORD):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexTrylock(pMutex:PScePthreadMutex):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexUnlock(pMutex:PScePthreadMutex):Integer; SysV_ABI_CDecl;
function ps4_scePthreadMutexDestroy(pMutex:PScePthreadMutex):Integer; SysV_ABI_CDecl;

//pthread_mutexattr_setkind_np

implementation

Uses
 atomic,
 sys_kernel,
 sys_signal,
 sys_time,
 ps4_sema,
 ps4_time;

function ps4_pthread_mutexattr_init(pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
var
 attr:pthread_mutex_attr_t;
begin
 if (pAttr=nil) then Exit(EINVAL);
 attr:=AllocMem(SizeOf(pthread_mutex_attr));
 if (attr=nil) then Exit(ENOMEM);
 attr^:=Default(pthread_mutex_attr);
 attr^._type:=PTHREAD_MUTEX_DEFAULT;
 pAttr^:=attr;
 Result:=0;
end;

function ps4_pthread_mutexattr_destroy(pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
var
 attr:pthread_mutex_attr_t;
begin
 if (pAttr=nil) then Exit(EINVAL);
 attr:=pAttr^;
 if (attr=nil) then Exit(EINVAL);
 FreeMem(attr);
 Result:=0;
end;

function ps4_pthread_mutexattr_gettype(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) or (t=nil) then Exit(EINVAL);
 if (pAttr^=nil) then Exit(EINVAL);
 t^:=pAttr^^._type;
 Result:=0;
end;

function ps4_pthread_mutexattr_settype(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(EINVAL);
 if (pAttr^=nil) then Exit(EINVAL);
 Case t of
  PTHREAD_MUTEX_ERRORCHECK:;
  PTHREAD_MUTEX_RECURSIVE :;
  PTHREAD_MUTEX_NORMAL    :;
  PTHREAD_MUTEX_ADAPTIVE  :;
  else
   Exit(EINVAL);
 end;
 pAttr^^._type:=t;
 Result:=0;
end;

function ps4_pthread_mutexattr_getpshared(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) or (t=nil) then Exit(EINVAL);
 if (pAttr^=nil) then Exit(EINVAL);
 t^:=pAttr^^._shared;
 Result:=0;
end;

function ps4_pthread_mutexattr_setpshared(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(EINVAL);
 if (pAttr^=nil) then Exit(EINVAL);
 Case t of
  PTHREAD_PROCESS_PRIVATE:;
  PTHREAD_PROCESS_SHARED :;
  else
   Exit(EINVAL);
 end;
 pAttr^^._shared:=t;
 Result:=0;
end;

function ps4_pthread_mutexattr_getprotocol(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) or (t=nil) then Exit(EINVAL);
 if (pAttr^=nil) then Exit(EINVAL);
 t^:=pAttr^^._protocol;
 Result:=0;
end;

function ps4_pthread_mutexattr_setprotocol(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(EINVAL);
 if (pAttr^=nil) then Exit(EINVAL);
 Case t of
  PTHREAD_PRIO_NONE   :;
  PTHREAD_PRIO_INHERIT:;
  PTHREAD_PRIO_PROTECT:;
  else
   Exit(EINVAL);
 end;
 pAttr^^._protocol:=t;
 Result:=0;
end;

function ps4_pthread_mutexattr_getprioceiling(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) or (t=nil) then Exit(EINVAL);
 if (pAttr^=nil) then Exit(EINVAL);
 t^:=pAttr^^._prioceiling;
 Result:=0;
end;

function ps4_pthread_mutexattr_setprioceiling(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(EINVAL);
 if (pAttr^=nil) then Exit(EINVAL);
 pAttr^^._prioceiling:=t;
 Result:=0;
end;

///////////////////////////

Const
 LIFE_MUTEX=$BAB1F00D;
 DEAD_MUTEX=$DEADBEEF;

 MS_Unlocked=0;
 MS_Locked  =1;
 MS_Waiting =2;

const
 PTHREAD_MUTEX_FREE=2;

function STATIC_INITIALIZER(m:pthread_mutex):Boolean; inline;
begin
 Result:=False;
 Case PtrUInt(m) of
  PTHREAD_MUTEX_INITIALIZER,
  PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP:Result:=True;
 end;
end;

function mutex_impl_init(m:p_pthread_mutex;mi:pthread_mutex;_type:Integer):pthread_mutex;
var
 new_mi:pthread_mutex;
begin
 new_mi:=SwAllocMem(SizeOf(pthread_mutex_t));
 if (new_mi=nil) then Exit(new_mi);

 new_mi^.valid:=LIFE_MUTEX;
 new_mi^.state:=MS_Unlocked;
 new_mi^._type:=_type;
 new_mi^.owner:=DWORD(-1);

 if CAS(m^,mi,new_mi) then
 begin
  Result:=new_mi;
 end else
 begin
  SwFreeMem(new_mi);
  Result:=m^;
 end;
end;

function mutex_impl(m:p_pthread_mutex;var mi:pthread_mutex;default:Integer):Integer;
begin
 if (m=nil) then Exit(EINVAL);
 mi:=m^;
 Case PtrUInt(mi) of
  PTHREAD_MUTEX_INITIALIZER            :mi:=mutex_impl_init(m,mi,default);
  PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP:mi:=mutex_impl_init(m,mi,PTHREAD_MUTEX_ADAPTIVE);
  PTHREAD_MUTEX_FREE                   :Exit(EINVAL);
 end;
 if (mi=nil) then Exit(ENOMEM);
 if not safe_test(mi^.valid,LIFE_MUTEX) then Exit(EINVAL);
 Result:=0;
end;

function pthread_mutex_lock_intern(m:p_pthread_mutex;pTimeout:PQWORD;default:Integer):Integer;
var
 mi:pthread_mutex;
 old_state:DWORD;
 ev:THandle;
begin
 Result:=mutex_impl(m,mi,default);
 if (Result<>0) then Exit;

 old_state:=XCHG(mi^.state,MS_Locked);
 if (old_state<>MS_Unlocked) then
 begin
  if (mi^._type<>PTHREAD_MUTEX_NORMAL) then
  if (mi^.owner=GetCurrentThreadId) then
  begin
   CAS(mi^.state,MS_Locked,old_state);
   if (mi^._type=PTHREAD_MUTEX_RECURSIVE) then
   begin
    Inc(mi^.rec_lock);
    Exit(0);
   end else
   begin
    Exit(EDEADLK);
   end;
  end;

  if (mi^.event=0) then
  begin
   ev:=CreateEvent(nil,false,false,nil);
   if (ev=0) then
    Case GetLastError of
     ERROR_ACCESS_DENIED:Exit(EPERM);
     else
                         Exit(EPERM);
    end;

   if not CAS(mi^.event,0,ev) then
   begin
    CloseHandle(ev);
   end;
  end;

  While (XCHG(mi^.state,MS_Waiting)<>MS_Unlocked) do
  begin
   Result:=do_sema_b_wait_intern(mi^.event,pTimeout);
   if (Result<>0) then Exit;
  end;
 end;

 if (mi^._type<>PTHREAD_MUTEX_NORMAL) then
 begin
  mi^.owner:=GetCurrentThreadId;
 end;

 Result:=0;
end;

function ps4_pthread_mutex_lock(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
begin
 _sig_lock;
 Result:=pthread_mutex_lock_intern(pMutex,nil,PTHREAD_MUTEX_DEFAULT);
 _sig_unlock;
end;

function ps4_pthread_mutex_timedlock(pMutex:p_pthread_mutex;ts:Ptimespec):Integer; SysV_ABI_CDecl;
var
 t:QWORD;
begin
 if (ts=nil) then
 begin
  _sig_lock;
  Result:=pthread_mutex_lock_intern(pMutex,nil,PTHREAD_MUTEX_DEFAULT);
  _sig_unlock;
 end else
 begin
  t:=_pthread_rel_time_in_ns(ts^);
  _sig_lock;
  Result:=pthread_mutex_lock_intern(pMutex,@t,PTHREAD_MUTEX_DEFAULT);
  _sig_unlock;
 end;
end;

function pthread_mutex_unlock(m:p_pthread_mutex;default:Integer):Integer;
var
 mi:pthread_mutex;
begin
 Result:=mutex_impl(m,mi,default);
 if (Result<>0) then Exit;

 if (mi^._type<>PTHREAD_MUTEX_NORMAL) then
 begin
  if (mi^.state=MS_Unlocked) then Exit(EPERM);
  if (mi^.owner<>GetCurrentThreadId) then Exit(EPERM);

  if (mi^.rec_lock>0) then
  begin
   Dec(mi^.rec_lock);
   Exit(0);
  end;
  mi^.owner:=DWORD(-1);
 end;

 if XCHG(mi^.state,MS_Unlocked)=MS_Waiting then
 begin
  _sig_lock;
  if not SetEvent(mi^.event) then Result:=EPERM;
  _sig_unlock;
 end;

end;

function ps4_pthread_mutex_unlock(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
begin
 Result:=pthread_mutex_unlock(pMutex,PTHREAD_MUTEX_DEFAULT);
end;

function pthread_mutex_trylock(m:p_pthread_mutex;default:Integer):Integer;
var
 mi:pthread_mutex;
begin
 Result:=mutex_impl(m,mi,default);
 if (Result<>0) then Exit;

 if CAS(mi^.state,MS_Unlocked,MS_Locked) then
 begin
  if (mi^._type<>PTHREAD_MUTEX_NORMAL) then
  begin
   mi^.owner:=GetCurrentThreadId;
  end;
  Exit(0);
 end else
 begin
  if (mi^._type=PTHREAD_MUTEX_RECURSIVE) and (mi^.owner=GetCurrentThreadId) then
  begin
   Inc(mi^.rec_lock);
   Exit(0);
  end;
  Exit(EBUSY);
 end;
end;

function ps4_pthread_mutex_trylock(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
begin
 Result:=pthread_mutex_trylock(pMutex,PTHREAD_MUTEX_DEFAULT);
end;

function pthread_mutex_init(m:p_pthread_mutex;a:p_pthread_mutex_attr;str:PChar;default:Integer):Integer;
var
 mi:pthread_mutex;
 attr:pthread_mutex_attr_t;
begin
 if (m=nil) then Exit(EINVAL);
 mi:=m^;

 attr:=nil;
 if (a<>nil) then
 begin
  attr:=a^;
 end;

 if (attr<>nil) then
 begin
  mi:=mutex_impl_init(m,mi,attr^._type);
 end else
 begin
  mi:=mutex_impl_init(m,mi,default);
 end;
 if (mi=nil) then Exit(ENOMEM);
 if not safe_test(mi^.valid,LIFE_MUTEX) then Exit(EINVAL);

 if (str<>nil) then MoveChar0(str^,mi^.name,32);

 Result:=0;
end;

function ps4_pthread_mutex_init(pMutex:p_pthread_mutex;pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
begin
 Result:=pthread_mutex_init(pMutex,pAttr,nil,PTHREAD_MUTEX_DEFAULT);
end;

function pthread_mutex_destroy(m:p_pthread_mutex):Integer;
var
 mi:pthread_mutex;
begin
 if (m=nil) then Exit(EINVAL);
 mi:=m^;
 if not STATIC_INITIALIZER(mi) then
 begin
  mi:=XCHG(m^,Pointer(PTHREAD_MUTEX_FREE));
  if (mi=Pointer(PTHREAD_MUTEX_FREE)) then Exit(EINVAL);
  if STATIC_INITIALIZER(mi) then Exit(0);
  if not CAS(mi^.valid,LIFE_MUTEX,DEAD_MUTEX) then Exit(EINVAL);
  _sig_lock;
  if (mi^.event<>0) then CloseHandle(mi^.event);
  FreeMem(mi);
  _sig_unlock;
 end;
 Result:=0;
end;

function ps4_pthread_mutex_destroy(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
begin
 Result:=pthread_mutex_destroy(pMutex);
end;

function ps4_pthread_mutex_setname_np(pMutex:p_pthread_mutex;name:Pchar):Integer; SysV_ABI_CDecl;
var
 m:pthread_mutex;
begin
 if (pMutex=nil) then Exit(EINVAL);
 m:=pMutex^;
 if (m=nil) then Exit(EINVAL);
 if (name<>nil) then
 begin
  FillChar(m^.name,32,0);
  MoveChar0(name^,m^.name,32);
 end;
 Result:=0;
end;

//---------------------------------------------------------
//sce

function ps4_scePthreadMutexattrInit(pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_mutexattr_init(pAttr));
end;

function ps4_scePthreadMutexattrDestroy(pAttr:p_pthread_mutex_attr):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_mutexattr_destroy(pAttr));
end;

function ps4_scePthreadMutexattrGettype(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_mutexattr_gettype(pAttr,t));
end;

function ps4_scePthreadMutexattrSettype(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_mutexattr_settype(pAttr,t));
end;

function ps4_scePthreadMutexattrGetprotocol(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_mutexattr_getprotocol(pAttr,t));
end;

function ps4_scePthreadMutexattrSetprotocol(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_mutexattr_setprotocol(pAttr,t));
end;

function ps4_scePthreadMutexattrGetprioceiling(pAttr:p_pthread_mutex_attr;t:PInteger):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_mutexattr_getprioceiling(pAttr,t));

end;

function ps4_scePthreadMutexattrSetprioceiling(pAttr:p_pthread_mutex_attr;t:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_mutexattr_setprioceiling(pAttr,t));
end;

//////////////

function ps4_scePthreadMutexLock(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
begin
 _sig_lock;
 Result:=px2sce(pthread_mutex_lock_intern(pMutex,nil,SCE_PTHREAD_MUTEX_DEFAULT));
 _sig_unlock;
end;

function ps4_scePthreadMutexTimedlock(pMutex:PScePthreadMutex;usec:DWORD):Integer; SysV_ABI_CDecl;
var
 t:QWORD;
begin
 t:=_usec2nsec(usec);
 _sig_lock;
 Result:=px2sce(pthread_mutex_lock_intern(pMutex,@t,SCE_PTHREAD_MUTEX_DEFAULT));
 _sig_unlock;
end;

function ps4_scePthreadMutexUnlock(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(pthread_mutex_unlock(pMutex,SCE_PTHREAD_MUTEX_DEFAULT));
end;

function ps4_scePthreadMutexTrylock(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(pthread_mutex_trylock(pMutex,SCE_PTHREAD_MUTEX_DEFAULT));
end;

function ps4_scePthreadMutexInit(pMutex:PScePthreadMutex;pAttr:p_pthread_mutex_attr;str:PChar):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(pthread_mutex_init(pMutex,pAttr,str,SCE_PTHREAD_MUTEX_DEFAULT));
end;

function ps4_scePthreadMutexDestroy(pMutex:p_pthread_mutex):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(pthread_mutex_destroy(pMutex));
end;

end.

