mirror of https://github.com/red-prig/fpPS4.git
371 lines
5.9 KiB
Plaintext
371 lines
5.9 KiB
Plaintext
unit kern_hazard_pointer;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
|
|
interface
|
|
|
|
type
|
|
TGuard=object
|
|
private
|
|
type
|
|
PGuardHandle=^TGuardHandle;
|
|
TGuardHandle=packed record
|
|
Item:Pointer;
|
|
end;
|
|
Var
|
|
Handle:PGuardHandle;
|
|
public
|
|
type
|
|
TFuncFree=Function(P:Pointer):SizeUInt;
|
|
TFuncGet =function(P:Pointer):Pointer;
|
|
function New:TGuard; static;
|
|
procedure Free;
|
|
procedure Clear;
|
|
function Assign(P:Pointer):Pointer;
|
|
function Get:Pointer;
|
|
function Protect(Var P:Pointer;Func:TFuncGet=nil):Pointer;
|
|
Procedure Retire (P:Pointer;FuncFree:TFuncFree); static;
|
|
Procedure WaitFor(P:Pointer); static;
|
|
Procedure Flush; static;
|
|
Procedure FLazy; static;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
atomic,
|
|
mqueue,
|
|
LFQueue,
|
|
g_node_splay,
|
|
kern_thr,
|
|
time,
|
|
md_sleep;
|
|
|
|
var
|
|
rlist_bs:LIST_HEAD=(lh_first:nil);
|
|
rlist_lf:TIntrusiveMPSCQueue=(tail_:@rlist_lf.stub_;stub_:(next_:nil);head_:@rlist_lf.stub_);
|
|
rcount :Integer=0;
|
|
|
|
function AllocGuard:Pointer;
|
|
var
|
|
td:p_kthread;
|
|
i:Byte;
|
|
begin
|
|
Result:=nil;
|
|
td:=curkthread;
|
|
Assert(td<>nil,'AllocGuard#1');
|
|
|
|
For i:=0 to High(kthread.td_guards) do
|
|
if (td^.td_guards[i]=nil) then
|
|
begin
|
|
td^.td_guards[i]:=Pointer(1);
|
|
Exit(@td^.td_guards[i]);
|
|
end;
|
|
|
|
Assert(false,'AllocGuard#2');
|
|
end;
|
|
|
|
Procedure FreeGuard(P:Pointer); inline;
|
|
begin
|
|
PPointer(P)^:=nil;
|
|
end;
|
|
|
|
////////
|
|
|
|
type
|
|
p_r_node=^t_r_node;
|
|
t_r_node=record
|
|
entry:LIST_ENTRY;
|
|
//
|
|
P:Pointer;
|
|
F:TGuard.TFuncFree;
|
|
end;
|
|
|
|
p_pointer_node=^t_pointer_node;
|
|
t_pointer_node=object
|
|
//key should be first
|
|
P:Pointer;
|
|
//
|
|
pLeft :p_pointer_node;
|
|
pRight:p_pointer_node;
|
|
//
|
|
function c(n1,n2:p_pointer_node):Integer; static;
|
|
end;
|
|
TPointerSet=specialize TNodeSplay<t_pointer_node>;
|
|
|
|
function t_pointer_node.c(n1,n2:p_pointer_node):Integer;
|
|
begin
|
|
Result:=Integer(n1^.P>n2^.P)-Integer(n1^.P<n2^.P);
|
|
end;
|
|
|
|
procedure WaitForRetire(P:Pointer);
|
|
label
|
|
_again;
|
|
var
|
|
p_data:Pointer;
|
|
ttd :p_kthread;
|
|
i :Byte;
|
|
begin
|
|
if (P=nil) or (P=Pointer(1)) then Exit;
|
|
|
|
_again:
|
|
|
|
threads_lock;
|
|
|
|
ttd:=TAILQ_FIRST(get_p_threads);
|
|
while (ttd<>nil) do
|
|
begin
|
|
|
|
For i:=0 to High(kthread.td_guards) do
|
|
begin
|
|
p_data:=load_acq_rel(ttd^.td_guards[i]);
|
|
|
|
if (p_data=P) then
|
|
begin
|
|
threads_unlock;
|
|
msleep_td(hz div 10000);
|
|
goto _again;
|
|
end;
|
|
|
|
end;
|
|
|
|
ttd:=TAILQ_NEXT(ttd,@ttd^.td_plist)
|
|
end;
|
|
|
|
threads_unlock;
|
|
end;
|
|
|
|
type
|
|
t_scan_mode=(smLazy,smForce);
|
|
|
|
Procedure Scan(mode:t_scan_mode);
|
|
label
|
|
_again;
|
|
var
|
|
p_set :TPointerSet;
|
|
p_data:Pointer;
|
|
p_node:p_pointer_node;
|
|
r_node:p_r_node;
|
|
r_next:p_r_node;
|
|
ttd :p_kthread;
|
|
f_list:LIST_HEAD;
|
|
i :Byte;
|
|
begin
|
|
|
|
_again:
|
|
|
|
p_set :=Default(TPointerSet);
|
|
r_node:=nil;
|
|
f_list:=Default(LIST_HEAD);
|
|
|
|
if (mode=smForce) then
|
|
begin
|
|
threads_lock;
|
|
end else
|
|
begin
|
|
if not threads_trylock then Exit;
|
|
end;
|
|
|
|
//flush to base list
|
|
while rlist_lf.Pop(r_node) do
|
|
begin
|
|
LIST_INSERT_HEAD(@rlist_bs,r_node,@r_node^.entry);
|
|
end;
|
|
|
|
r_node:=LIST_FIRST(@rlist_bs);
|
|
if (r_node=nil) then
|
|
begin
|
|
//zero list
|
|
threads_unlock;
|
|
Exit;
|
|
end;
|
|
|
|
ttd:=TAILQ_FIRST(get_p_threads);
|
|
while (ttd<>nil) do
|
|
begin
|
|
|
|
For i:=0 to High(kthread.td_guards) do
|
|
begin
|
|
p_data:=load_acq_rel(ttd^.td_guards[i]);
|
|
if (p_data<>nil) and
|
|
(p_data<>Pointer(1)) then
|
|
begin
|
|
p_node:=AllocMem(SizeOf(t_pointer_node));
|
|
p_node^.P:=p_data;
|
|
p_set.Insert(p_node);
|
|
end;
|
|
end;
|
|
|
|
ttd:=TAILQ_NEXT(ttd,@ttd^.td_plist)
|
|
end;
|
|
|
|
while (r_node<>nil) do
|
|
begin
|
|
r_next:=LIST_NEXT(r_node,@r_node^.entry);
|
|
//
|
|
p_node:=p_set.Find(@r_node^.P);
|
|
//
|
|
if (p_node=nil) then
|
|
begin
|
|
//delete node
|
|
LIST_REMOVE(r_node,@r_node^.entry);
|
|
//add to free list
|
|
LIST_INSERT_HEAD(@f_list,r_node,@r_node^.entry);
|
|
end;
|
|
//
|
|
r_node:=r_next;
|
|
end;
|
|
|
|
threads_unlock;
|
|
|
|
//free set
|
|
p_node:=p_set.Min;
|
|
while (p_node<>nil) do
|
|
begin
|
|
p_set.Delete(p_node);
|
|
//
|
|
FreeMem(p_node);
|
|
//
|
|
p_node:=p_set.Min;
|
|
end;
|
|
|
|
//free elements
|
|
r_node:=LIST_FIRST(@f_list);
|
|
while (r_node<>nil) do
|
|
begin
|
|
LIST_REMOVE(r_node,@r_node^.entry);
|
|
//free element
|
|
if (r_node^.F<>nil) then
|
|
begin
|
|
r_node^.F(r_node^.P);
|
|
end;
|
|
//free node
|
|
System.InterlockedDecrement(rcount);
|
|
FreeMem(r_node);
|
|
//
|
|
r_node:=LIST_FIRST(@f_list);
|
|
end;
|
|
|
|
if (mode=smForce) and
|
|
(LIST_FIRST(@rlist_bs)<>nil) then
|
|
begin
|
|
msleep_td(hz div 10000);
|
|
goto _again;
|
|
end;
|
|
|
|
end;
|
|
|
|
Procedure Retire(P:Pointer;FuncFree:TGuard.TFuncFree);
|
|
var
|
|
node:p_r_node;
|
|
begin
|
|
node:=AllocMem(SizeOf(t_r_node));
|
|
node^.P:=P;
|
|
node^.F:=FuncFree;
|
|
//
|
|
rlist_lf.Push(node);
|
|
System.InterlockedIncrement(rcount);
|
|
//
|
|
if rcount>(4*256) then
|
|
begin
|
|
Scan(smLazy);
|
|
end;
|
|
end;
|
|
|
|
////////
|
|
|
|
function TGuard.New:TGuard;
|
|
begin
|
|
Result.Handle:=AllocGuard;
|
|
Result.Clear;
|
|
end;
|
|
|
|
procedure TGuard.Free;
|
|
begin
|
|
if Assigned(Handle) then
|
|
begin
|
|
Clear;
|
|
FreeGuard(Handle);
|
|
Handle:=nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TGuard.Clear;
|
|
begin
|
|
if Assigned(Handle) then
|
|
begin
|
|
PGuardHandle(Handle)^.Item:=Pointer(1);
|
|
end;
|
|
end;
|
|
|
|
function TGuard.Assign(P:Pointer):Pointer;
|
|
begin
|
|
Result:=nil;
|
|
if Assigned(Handle) then
|
|
begin
|
|
store_seq_cst(PGuardHandle(Handle)^.Item,P);
|
|
Result:=P;
|
|
end;
|
|
end;
|
|
|
|
function TGuard.Get:Pointer;
|
|
begin
|
|
Result:=nil;
|
|
if Assigned(Handle) then
|
|
begin
|
|
Result:=PGuardHandle(Handle)^.Item;
|
|
end;
|
|
end;
|
|
|
|
function TGuard.Protect(Var P:Pointer;Func:TFuncGet=nil):Pointer;
|
|
Var
|
|
pCur,pRet:Pointer;
|
|
begin
|
|
Assert(Handle<>nil);
|
|
Result:=nil;
|
|
if Assigned(Handle) then
|
|
begin
|
|
pCur:=load_acq_rel(P);
|
|
repeat
|
|
if (Func=nil) then
|
|
begin
|
|
store_seq_cst(PGuardHandle(Handle)^.Item,pCur);
|
|
end else
|
|
begin
|
|
store_seq_cst(PGuardHandle(Handle)^.Item,Func(pCur));
|
|
end;
|
|
pRet:=load_acquire(pCur);
|
|
pCur:=load_acq_rel(P);
|
|
until (pRet=pCur);
|
|
Result:=pCur;
|
|
end;
|
|
end;
|
|
|
|
Procedure TGuard.Retire(P:Pointer;FuncFree:TFuncFree);
|
|
begin
|
|
if Assigned(P) and Assigned(FuncFree) then
|
|
begin
|
|
Retire(P,FuncFree);
|
|
end;
|
|
end;
|
|
|
|
Procedure TGuard.WaitFor(P:Pointer);
|
|
begin
|
|
WaitForRetire(P);
|
|
end;
|
|
|
|
Procedure TGuard.Flush;
|
|
begin
|
|
Scan(smForce);
|
|
end;
|
|
|
|
Procedure TGuard.FLazy;
|
|
begin
|
|
Scan(smLazy);
|
|
end;
|
|
|
|
/////////
|
|
|
|
end.
|
|
|