unit ps4_rwlock;

{$mode objfpc}{$H+}

interface

uses
 RWLock;

const
 PTHREAD_RWLOCK_INITIALIZER=nil;

 SCE_PTHREAD_RWLOCK_NORMAL        = 1; // Default POSIX rwlock
 SCE_PTHREAD_RWLOCK_PREFER_READER = 2; // Reader preferred rwlock

type
 p_pthread_rwlock=^pthread_rwlock;
 pthread_rwlock=^pthread_rwlock_t;
 pthread_rwlock_t=record
  valid:DWORD;
  Lock:TRWLock;
  name:array[0..31] of AnsiChar;
 end;

 p_pthread_rwlockattr=^pthread_rwlockattr_t;
 pthread_rwlockattr_t=bitpacked record
  _type:0..3;           //2
  _shared:0..1;         //1
  _align:0..536870911;  //29
  _align2:Integer;      //32
 end;

function ps4_pthread_rwlockattr_init(pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlockattr_destroy(pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlockattr_gettype_np(pAttr:p_pthread_rwlockattr;t:PInteger):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlockattr_settype_np(pAttr:p_pthread_rwlockattr;t:Integer):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlockattr_getpshared(pAttr:p_pthread_rwlockattr;t:PInteger):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlockattr_setpshared(pAttr:p_pthread_rwlockattr;t:Integer):Integer; SysV_ABI_CDecl;

function ps4_pthread_rwlock_init(pRwlock:p_pthread_rwlock;pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlock_destroy(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlock_rdlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlock_wrlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlock_tryrdlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlock_trywrlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_pthread_rwlock_unlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;

function ps4_scePthreadRwlockattrInit(pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
function ps4_scePthreadRwlockattrDestroy(pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
function ps4_scePthreadRwlockattrGettype(pAttr:p_pthread_rwlockattr;t:Pinteger):Integer; SysV_ABI_CDecl;
function ps4_scePthreadRwlockattrSettype(pAttr:p_pthread_rwlockattr;t:integer):Integer; SysV_ABI_CDecl;

function ps4_scePthreadRwlockInit(pRwlock:p_pthread_rwlock;pAttr:p_pthread_rwlockattr;str:PChar):Integer; SysV_ABI_CDecl;
function ps4_scePthreadRwlockDestroy(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_scePthreadRwlockRdlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_scePthreadRwlockWrlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_scePthreadRwlockTryrdlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_scePthreadRwlockTrywrlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
function ps4_scePthreadRwlockUnlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;

//function ps4_scePthreadRwlockTimedrdlock(pRwlock:p_pthread_rwlock;usec:DWORD):Integer; SysV_ABI_CDecl;
//function ps4_scePthreadRwlockTimedwrlock(pRwlock:p_pthread_rwlock;usec:DWORD):Integer; SysV_ABI_CDecl;

implementation

Uses
 atomic,
 sys_kernel,
 sys_signal,
 ps4_mutex;

//int pthread_rwlock_timedrdlock(pthread_rwlock_t *,const struct timespec *);
//int pthread_rwlock_timedwrlock(pthread_rwlock_t *,const struct timespec *);

//int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *,int *);
//int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *, int);

function ps4_pthread_rwlockattr_init(pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(EINVAL);
 pAttr^:=Default(pthread_rwlockattr_t);
 pAttr^._type:=SCE_PTHREAD_RWLOCK_NORMAL;
 Result:=0;
end;

function ps4_pthread_rwlockattr_destroy(pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(EINVAL);
 Result:=0;
end;

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

function ps4_pthread_rwlockattr_settype_np(pAttr:p_pthread_rwlockattr;t:Integer):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(EINVAL);
 Case t of
  SCE_PTHREAD_RWLOCK_NORMAL       :;
  SCE_PTHREAD_RWLOCK_PREFER_READER:;
  else
   Exit(EINVAL);
 end;
 pAttr^._type:=t;
 Result:=0;
end;

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

function ps4_pthread_rwlockattr_setpshared(pAttr:p_pthread_rwlockattr;t:Integer):Integer; SysV_ABI_CDecl;
begin
 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;

Const
 LIFE_RWLOCK=$BAB1F0ED;
 DEAD_RWLOCK=$DEADB0EF;

function STATIC_RWL_INITIALIZER(x:pthread_rwlock):Boolean; inline;
begin
 Result:=(x=PTHREAD_RWLOCK_INITIALIZER);
end;

function rwlock_impl_init(m:p_pthread_rwlock;mi:pthread_rwlock):pthread_rwlock;
var
 new_mi:pthread_rwlock;
begin
 new_mi:=SwAllocMem(SizeOf(pthread_rwlock_t));

 if (new_mi=nil) then Exit(new_mi);

 new_mi^.valid:=LIFE_RWLOCK;

 _sig_lock;
 rwlock_init(new_mi^.Lock);
 _sig_unlock;

 if CAS(m^,mi,new_mi) then
 begin
  Result:=new_mi;
 end else
 begin
  _sig_lock;
  rwlock_destroy(new_mi^.Lock);
  FreeMem(new_mi);
  _sig_unlock;

  Result:=m^;
 end;
end;

function rwlock_impl(m:p_pthread_rwlock;var mi:pthread_rwlock):Integer;
begin
 if (m=nil) then Exit(EINVAL);
 mi:=m^;
 if STATIC_RWL_INITIALIZER(mi) then
 begin
  mi:=rwlock_impl_init(m,mi);
  if (mi=nil) then Exit(ENOMEM);
 end;
 if (mi^.valid<>LIFE_RWLOCK) then Exit(EINVAL);
 Result:=0;
end;

function pthread_rwlock_init(m:p_pthread_rwlock;a:p_pthread_rwlockattr;str:PChar):Integer; SysV_ABI_CDecl;
var
 mi:pthread_rwlock;
begin
 Result:=0;

 mi:=m^;
 mi:=rwlock_impl_init(m,mi);
 if (mi=nil) then Exit(ENOMEM);

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

function ps4_pthread_rwlock_init(pRwlock:p_pthread_rwlock;pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
begin
 Result:=pthread_rwlock_init(pRwlock,pAttr,nil);
end;

function ps4_pthread_rwlock_destroy(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
var
 mi:pthread_rwlock;
begin
 if (m=nil) then Exit(EINVAL);
 mi:=m^;
 if not STATIC_RWL_INITIALIZER(mi) then
 begin
  mi:=XCHG(m^,nil);
  if STATIC_RWL_INITIALIZER(mi) then Exit(0);
  if not CAS(mi^.valid,LIFE_RWLOCK,DEAD_RWLOCK) then Exit(EINVAL);
  _sig_lock;
  rwlock_destroy(mi^.Lock);
  FreeMem(mi);
  _sig_unlock;
 end;
 Result:=0;
end;

function ps4_pthread_rwlock_rdlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
var
 mi:pthread_rwlock;
begin
 Result:=rwlock_impl(m,mi);
 if (Result<>0) then Exit;
 _sig_lock;
 rwlock_rdlock(mi^.Lock);
 _sig_unlock;
end;

function ps4_pthread_rwlock_wrlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
var
 mi:pthread_rwlock;
begin
 Result:=rwlock_impl(m,mi);
 if (Result<>0) then Exit;
 _sig_lock;
 rwlock_wrlock(mi^.Lock);
 _sig_unlock;
end;

function ps4_pthread_rwlock_tryrdlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
var
 mi:pthread_rwlock;
begin
 Result:=rwlock_impl(m,mi);
 if (Result<>0) then Exit;
 _sig_lock;
 if not rwlock_tryrdlock(mi^.Lock) then Result:=EBUSY;
 _sig_unlock;
end;

function ps4_pthread_rwlock_trywrlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
var
 mi:pthread_rwlock;
begin
 Result:=rwlock_impl(m,mi);
 if (Result<>0) then Exit;
 _sig_lock;
 if not rwlock_trywrlock(mi^.Lock) then Result:=EBUSY;
 _sig_unlock;
end;

function ps4_pthread_rwlock_unlock(m:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
var
 mi:pthread_rwlock;
begin
 Result:=rwlock_impl(m,mi);
 if (Result<>0) then Exit;
 _sig_lock;
 rwlock_unlock(mi^.Lock);
 _sig_unlock;
end;

///////////

function ps4_scePthreadRwlockattrInit(pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(SCE_KERNEL_ERROR_EINVAL);
 pAttr^:=Default(pthread_rwlockattr_t);
 pAttr^._type:=SCE_PTHREAD_RWLOCK_NORMAL;
 Result:=0;
end;

function ps4_scePthreadRwlockattrDestroy(pAttr:p_pthread_rwlockattr):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(SCE_KERNEL_ERROR_EINVAL);
 Result:=0;
end;

function ps4_scePthreadRwlockattrGettype(pAttr:p_pthread_rwlockattr;t:Pinteger):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) or (t=nil) then Exit(SCE_KERNEL_ERROR_EINVAL);
 t^:=pAttr^._type;
 Result:=0;
end;

function ps4_scePthreadRwlockattrSettype(pAttr:p_pthread_rwlockattr;t:integer):Integer; SysV_ABI_CDecl;
begin
 if (pAttr=nil) then Exit(SCE_KERNEL_ERROR_EINVAL);
 Case t of
  SCE_PTHREAD_RWLOCK_NORMAL       :;
  SCE_PTHREAD_RWLOCK_PREFER_READER:;
  else
   Exit(SCE_KERNEL_ERROR_EINVAL);
 end;
 pAttr^._type:=t;
 Result:=0;
end;

//

function ps4_scePthreadRwlockInit(pRwlock:p_pthread_rwlock;pAttr:p_pthread_rwlockattr;str:PChar):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(pthread_rwlock_init(pRwlock,pAttr,str));
end;

function ps4_scePthreadRwlockDestroy(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_rwlock_destroy(pRwlock));
end;

function ps4_scePthreadRwlockRdlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_rwlock_rdlock(pRwlock));
end;

function ps4_scePthreadRwlockWrlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_rwlock_wrlock(pRwlock));
end;

function ps4_scePthreadRwlockTryrdlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_rwlock_tryrdlock(pRwlock));
end;

function ps4_scePthreadRwlockTrywrlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_rwlock_trywrlock(pRwlock));
end;

function ps4_scePthreadRwlockUnlock(pRwlock:p_pthread_rwlock):Integer; SysV_ABI_CDecl;
begin
 Result:=px2sce(ps4_pthread_rwlock_unlock(pRwlock));
end;

end.

