fpPS4/sys/spinlock.pas
2022-10-24 14:17:28 +03:00

308 lines
5.5 KiB
ObjectPascal

unit spinlock;
{$mode objfpc}{$H+}
interface
type
backoff_exp=object
private
Const
lower_bound = 16; ///< Minimum spinning limit
upper_bound = 16*1024; ///< Maximum spinning limit
Var
m_nExpCur:SizeUInt; //=lower_bound
public
Procedure Wait;
Procedure Reset;
end;
r_spin_lock=record
_lock:DWORD;
count:DWORD;
owner:DWORD;
end;
function spin_trylock(Var P:Pointer):Boolean;
function spin_trylock(Var P:SizeUint):Boolean;
{$IF defined(CPUX86_64)}
function spin_trylock(Var P:DWORD):Boolean;
{$ENDIF}
function spin_tryunlock(Var P:Pointer):Boolean;
function spin_tryunlock(Var P:SizeUint):Boolean;
{$IF defined(CPUX86_64)}
function spin_tryunlock(Var P:DWORD):Boolean;
{$ENDIF}
procedure spin_lock(Var P:Pointer);
procedure spin_lock(Var P:SizeUint);
{$IF defined(CPUX86_64)}
procedure spin_lock(Var P:DWORD);
{$ENDIF}
procedure spin_unlock(Var P:Pointer);
procedure spin_unlock(Var P:SizeUint);
{$IF defined(CPUX86_64)}
procedure spin_unlock(Var P:DWORD);
{$ENDIF}
function spin_trylock(var t:r_spin_lock):Boolean;
procedure spin_lock(var t:r_spin_lock);
procedure spin_unlock(var t:r_spin_lock);
Procedure wait_until_equal(Var P:Pointer;V:Pointer);
Procedure wait_until_equal(Var P:SizeUint;V:SizeUint);
{$IF defined(CPUX86_64)}
Procedure wait_until_equal(Var P:DWORD;V:DWORD);
Procedure wait_until_equal(Var P:Integer;V:Integer);
{$ENDIF}
const
EVL_DIS=0; //disable
EVL_NEW=1; //new
EVL_ENB=2; //enable
function event_try_enable(Var P:Pointer):Boolean;
function event_try_enable(Var P:DWORD):Boolean;
function event_try_disable(Var P:Pointer):Boolean;
function event_try_disable(Var P:DWORD):Boolean;
procedure event_disable(Var P:Pointer);
procedure event_disable(Var P:DWORD);
implementation
Uses
atomic,
sys_kernel,
sys_signal;
Procedure backoff_exp.Wait;
Var
n:SizeUInt;
begin
if (m_nExpCur<=upper_bound) then
begin
For n:=0 to m_nExpCur-1 do
begin
SwYieldExecution;
end;
m_nExpCur:=m_nExpCur*2;
end else
begin
n:=-1000;
SwDelayExecution(False,@n);
end;
end;
Procedure backoff_exp.Reset;
begin
m_nExpCur:=lower_bound;
end;
function spin_trylock(Var P:Pointer):Boolean;
begin
Result:=XCHG(P,Pointer(1))=nil;
end;
function spin_trylock(Var P:SizeUint):Boolean;
begin
Result:=XCHG(P,1)=0;
end;
{$IF defined(CPUX86_64)}
function spin_trylock(Var P:DWORD):Boolean;
begin
Result:=XCHG(P,1)=0;
end;
{$ENDIF}
function spin_tryunlock(Var P:Pointer):Boolean;
begin
Result:=XCHG(P,nil)=Pointer(1);
end;
function spin_tryunlock(Var P:SizeUint):Boolean;
begin
Result:=XCHG(P,0)=1;
end;
{$IF defined(CPUX86_64)}
function spin_tryunlock(Var P:DWORD):Boolean;
begin
Result:=XCHG(P,0)=1;
end;
{$ENDIF}
procedure spin_lock(Var P:Pointer);
Var
bkoff:backoff_exp;
begin
bkoff.Reset;
While (XCHG(P,Pointer(1))<>nil) do bkoff.Wait;
end;
procedure spin_lock(Var P:SizeUint);
Var
bkoff:backoff_exp;
begin
bkoff.Reset;
While (XCHG(P,1)<>0) do bkoff.Wait;
end;
{$IF defined(CPUX86_64)}
procedure spin_lock(Var P:DWORD);
Var
bkoff:backoff_exp;
begin
bkoff.Reset;
While (XCHG(P,1)<>0) do bkoff.Wait;
end;
{$ENDIF}
procedure spin_unlock(Var P:Pointer);
begin
store_release(P,nil);
end;
procedure spin_unlock(Var P:SizeUint);
begin
store_release(P,0);
end;
{$IF defined(CPUX86_64)}
procedure spin_unlock(Var P:DWORD);
begin
store_release(P,0);
end;
{$ENDIF}
//recrusive spin lock
function spin_trylock(var t:r_spin_lock):Boolean;
begin
Result:=True;
_sig_lock;
if spin_trylock(t._lock) then
begin
t.count:=0;
t.owner:=GetCurrentThreadId;
end else
if (t.owner=GetCurrentThreadId) then
begin
Inc(t.count);
end else
begin
Result:=False;
end;
_sig_unlock;
end;
procedure spin_lock(var t:r_spin_lock);
Var
bkoff:backoff_exp;
begin
bkoff.Reset;
While (not spin_trylock(t)) do bkoff.Wait;
end;
procedure spin_unlock(var t:r_spin_lock);
begin
_sig_lock;
if (t.count=0) then
begin
t.owner:=DWORD(-1);
spin_unlock(t._lock);
end else
begin
Dec(t.count);
end;
_sig_unlock;
end;
//
Procedure wait_until_equal(Var P:Pointer;V:Pointer);
Var
bkoff:backoff_exp;
begin
bkoff.Reset;
While (load_acq_rel(P)=V) do bkoff.Wait;
end;
Procedure wait_until_equal(Var P:SizeUint;V:SizeUint);
Var
bkoff:backoff_exp;
begin
bkoff.Reset;
While (load_acq_rel(P)=V) do bkoff.Wait;
end;
{$IF defined(CPUX86_64)}
Procedure wait_until_equal(Var P:DWORD;V:DWORD);
Var
bkoff:backoff_exp;
begin
bkoff.Reset;
While (load_acq_rel(P)=V) do bkoff.Wait;
end;
Procedure wait_until_equal(Var P:Integer;V:Integer);
Var
bkoff:backoff_exp;
begin
bkoff.Reset;
While (load_acq_rel(P)=V) do bkoff.Wait;
end;
{$ENDIF}
function event_try_enable(Var P:Pointer):Boolean;
begin
Result:=(XCHG(P,Pointer(EVL_NEW))=Pointer(EVL_DIS));
if Result then
begin
store_release(P,Pointer(EVL_ENB));
end;
end;
function event_try_enable(Var P:DWORD):Boolean;
begin
Result:=(XCHG(P,EVL_NEW)=EVL_DIS);
if Result then
begin
store_release(P,EVL_ENB);
end;
end;
function event_try_disable(Var P:Pointer):Boolean;
begin
Result:=False;
Case SizeUint(_CAS(P,Pointer(EVL_ENB),Pointer(EVL_DIS))) of
EVL_DIS,
EVL_ENB:Result:=True;
EVL_NEW:store_release(P,Pointer(EVL_ENB));
else;
end;
end;
function event_try_disable(Var P:DWORD):Boolean;
begin
Result:=False;
Case _CAS(P,EVL_ENB,EVL_DIS) of
EVL_DIS,
EVL_ENB:Result:=True;
EVL_NEW:store_release(P,EVL_ENB);
else;
end;
end;
procedure event_disable(Var P:Pointer);
begin
store_release(P,Pointer(EVL_DIS));
end;
procedure event_disable(Var P:DWORD);
begin
store_release(P,EVL_DIS);
end;
end.