mirror of https://github.com/red-prig/fpPS4.git
moved to rtl
This commit is contained in:
parent
d12be00a29
commit
aa0d0347b0
461
LFQueue.pas
461
LFQueue.pas
|
@ -1,461 +0,0 @@
|
|||
{ Implimentation of Dmitry Vyukov Intrusive MPSC node-based queue on free pascal
|
||||
|
||||
Copyright (C) 2018-2020 Red_prig
|
||||
|
||||
This library is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Library General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version with the following modification:
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent modules,and
|
||||
to copy and distribute the resulting executable under terms of your choice,
|
||||
provided that you also meet, for each linked independent module, the terms
|
||||
and conditions of the license of that module. An independent module is a
|
||||
module which is not derived from or based on this library. If you modify
|
||||
this library, you may extend this exception to your version of the library,
|
||||
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
|
||||
for more details.
|
||||
}
|
||||
|
||||
unit LFQueue;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
Const
|
||||
CacheLineSize=64;
|
||||
|
||||
Type
|
||||
TIntrusiveMPSCQueue=object
|
||||
protected
|
||||
type
|
||||
PQNode=^TQNode;
|
||||
TQNode=record
|
||||
next_:PQNode;
|
||||
//some data
|
||||
end;
|
||||
Var
|
||||
tail_:PQNode;
|
||||
stub_:TQNode;
|
||||
head_:PQNode;
|
||||
public
|
||||
Procedure Create;
|
||||
Function Push(Node:Pointer):Boolean;
|
||||
Function Pop(Var Node:Pointer):Boolean;
|
||||
Function IsEmpty:Boolean; inline;
|
||||
end;
|
||||
|
||||
TIntrusiveMPSCQueueA=object
|
||||
protected
|
||||
type
|
||||
PQNode=^TQNode;
|
||||
TQNode=record
|
||||
next_:PQNode;
|
||||
//some data
|
||||
end;
|
||||
Var
|
||||
tail_:record
|
||||
Case Byte of
|
||||
0:(Pad:Array[0..CacheLineSize-1] of Byte);
|
||||
1:(VAL:PQNode;stub:TQNode);
|
||||
end;
|
||||
head_:record
|
||||
Case Byte of
|
||||
0:(Pad:Array[0..CacheLineSize-1] of Byte);
|
||||
1:(VAL:PQNode);
|
||||
end;
|
||||
public
|
||||
Procedure Create;
|
||||
Function Push(Node:Pointer):Boolean;
|
||||
Function Pop(Var Node:Pointer):Boolean;
|
||||
Function IsEmpty:Boolean; inline;
|
||||
end;
|
||||
|
||||
generic TLFQueue<TItem,Allocator,back_off>=object(TIntrusiveMPSCQueue)
|
||||
type
|
||||
PNode=^TNode;
|
||||
TNode=record
|
||||
next_:PNode;
|
||||
Item:TItem;
|
||||
end;
|
||||
Function push_front(Const val:TItem):Boolean;
|
||||
Function pop_back(Var val:TItem):Boolean;
|
||||
end;
|
||||
|
||||
generic TLFQueueA<TItem,Allocator,back_off>=object(TIntrusiveMPSCQueueA)
|
||||
type
|
||||
PNode=^TNode;
|
||||
TNode=record
|
||||
next_:PNode;
|
||||
Item:TItem;
|
||||
end;
|
||||
Function push_front(Const val:TItem):Boolean;
|
||||
Function pop_back(Var val:TItem):Boolean;
|
||||
end;
|
||||
|
||||
function load_consume(Var addr:Pointer):Pointer; inline;
|
||||
function load_consume(Var addr:PtrUInt):PtrUInt; inline;
|
||||
Procedure store_release(Var addr:Pointer;v:Pointer); inline;
|
||||
function XCHG(Var addr:Pointer;New:Pointer):Pointer; inline;
|
||||
|
||||
implementation
|
||||
|
||||
function load_consume(Var addr:Pointer):Pointer; inline;
|
||||
begin
|
||||
ReadDependencyBarrier;
|
||||
Result:=addr;
|
||||
end;
|
||||
|
||||
function load_consume(Var addr:PtrUInt):PtrUInt; inline;
|
||||
begin
|
||||
ReadDependencyBarrier;
|
||||
Result:=addr;
|
||||
end;
|
||||
|
||||
Procedure store_release(Var addr:Pointer;v:Pointer); inline;
|
||||
begin
|
||||
WriteBarrier;
|
||||
addr:=v;
|
||||
end;
|
||||
|
||||
function XCHG(Var addr:Pointer;New:Pointer):Pointer; inline;
|
||||
begin
|
||||
Result:=System.InterLockedExchange(addr,New);
|
||||
end;
|
||||
|
||||
//
|
||||
|
||||
Procedure TIntrusiveMPSCQueue.Create;
|
||||
begin
|
||||
FillChar(Self,SizeOf(Self),0);
|
||||
head_:=@stub_;
|
||||
tail_:=@stub_;
|
||||
ReadWriteBarrier;
|
||||
end;
|
||||
|
||||
Function TIntrusiveMPSCQueue.Push(Node:Pointer):Boolean;
|
||||
Var
|
||||
prev:PQNode;
|
||||
begin
|
||||
if not Assigned(Node) then Exit(False);
|
||||
store_release(PQNode(Node)^.next_,nil);
|
||||
prev:=XCHG(head_,Node);
|
||||
store_release(prev^.next_,Node);
|
||||
Result:=True;
|
||||
end;
|
||||
|
||||
Function TIntrusiveMPSCQueue.Pop(Var Node:Pointer):Boolean;
|
||||
Var
|
||||
tail,n,head:PQNode;
|
||||
begin
|
||||
Node:=nil;
|
||||
Result:=False;
|
||||
|
||||
tail:=tail_;
|
||||
n:=load_consume(tail^.next_);
|
||||
|
||||
if tail=@stub_ then
|
||||
begin
|
||||
if n=nil then Exit;
|
||||
store_release(tail_,n);
|
||||
tail:=n;
|
||||
n:=load_consume(n^.next_);
|
||||
end;
|
||||
|
||||
if n<>nil then
|
||||
begin
|
||||
store_release(tail_,n);
|
||||
Node:=tail;
|
||||
store_release(tail^.next_,nil);
|
||||
Exit(True);
|
||||
end;
|
||||
|
||||
head:=head_;
|
||||
if tail<>head then Exit;
|
||||
|
||||
stub_.next_:=nil;
|
||||
n:=XCHG(head_,@stub_);
|
||||
store_release(n^.next_,@stub_);
|
||||
|
||||
n:=load_consume(tail^.next_);
|
||||
|
||||
if n<>nil then
|
||||
begin
|
||||
store_release(tail_,n);
|
||||
Node:=tail;
|
||||
store_release(tail^.next_,nil);
|
||||
Exit(True);
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
Function TIntrusiveMPSCQueue.IsEmpty:Boolean; inline;
|
||||
begin
|
||||
Result:=head_=@stub_;
|
||||
end;
|
||||
|
||||
//
|
||||
|
||||
Procedure TIntrusiveMPSCQueueA.Create;
|
||||
begin
|
||||
FillChar(Self,SizeOf(Self),0);
|
||||
head_.VAL:=@tail_.stub;
|
||||
tail_.VAL:=@tail_.stub;
|
||||
ReadWriteBarrier;
|
||||
end;
|
||||
|
||||
Function TIntrusiveMPSCQueueA.Push(Node:Pointer):Boolean;
|
||||
Var
|
||||
prev:PQNode;
|
||||
begin
|
||||
if not Assigned(Node) then Exit(False);
|
||||
store_release(PQNode(Node)^.next_,nil);
|
||||
prev:=XCHG(head_.VAL,Node);
|
||||
store_release(prev^.next_,Node);
|
||||
Result:=True;
|
||||
end;
|
||||
|
||||
Function TIntrusiveMPSCQueueA.Pop(Var Node:Pointer):Boolean;
|
||||
Var
|
||||
tail,n,head:PQNode;
|
||||
begin
|
||||
Node:=nil;
|
||||
Result:=False;
|
||||
|
||||
tail:=tail_.VAL;
|
||||
n:=load_consume(tail^.next_);
|
||||
|
||||
if tail=@tail_.stub then
|
||||
begin
|
||||
if n=nil then Exit;
|
||||
store_release(tail_.VAL,n);
|
||||
tail:=n;
|
||||
n:=load_consume(n^.next_);
|
||||
end;
|
||||
|
||||
if n<>nil then
|
||||
begin
|
||||
store_release(tail_.VAL,n);
|
||||
Node:=tail;
|
||||
store_release(tail^.next_,nil);
|
||||
Exit(True);
|
||||
end;
|
||||
|
||||
head:=head_.VAL;
|
||||
if tail<>head then Exit;
|
||||
|
||||
tail_.stub.next_:=nil;
|
||||
n:=XCHG(head_.VAL,@tail_.stub);
|
||||
store_release(n^.next_,@tail_.stub);
|
||||
|
||||
n:=load_consume(tail^.next_);
|
||||
|
||||
if n<>nil then
|
||||
begin
|
||||
store_release(tail_.VAL,n);
|
||||
Node:=tail;
|
||||
store_release(tail^.next_,nil);
|
||||
Exit(True);
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
Function TIntrusiveMPSCQueueA.IsEmpty:Boolean; inline;
|
||||
begin
|
||||
Result:=head_.VAL=@tail_.stub;
|
||||
end;
|
||||
|
||||
//
|
||||
|
||||
Function TLFQueue.push_front(Const val:TItem):Boolean;
|
||||
Var
|
||||
Node:PNode;
|
||||
begin
|
||||
Node:=Allocator.AllocMem(SizeOf(TNode));
|
||||
Result:=Push(Node);
|
||||
end;
|
||||
|
||||
Function TLFQueue.pop_back(Var val:TItem):Boolean;
|
||||
Var
|
||||
tail,n,head:PQNode;
|
||||
bkoff:back_off;
|
||||
begin
|
||||
Result:=False;
|
||||
|
||||
bkoff.Reset;
|
||||
repeat
|
||||
tail:=XCHG(tail_,nil);
|
||||
|
||||
if (tail<>nil) then
|
||||
begin
|
||||
Break;
|
||||
end else
|
||||
begin
|
||||
bkoff.Wait;
|
||||
end;
|
||||
|
||||
until false;
|
||||
|
||||
n:=load_consume(tail^.next_);
|
||||
|
||||
if tail=@stub_ then
|
||||
begin
|
||||
if n=nil then
|
||||
begin
|
||||
|
||||
if tail=nil then tail:=@stub_;
|
||||
store_release(tail_,tail); //unlock
|
||||
|
||||
Exit;
|
||||
end;
|
||||
tail:=n;
|
||||
n:=load_consume(n^.next_);
|
||||
end;
|
||||
|
||||
if n<>nil then
|
||||
begin
|
||||
|
||||
val:=PNode(tail)^.Item;
|
||||
FreeMem(tail);
|
||||
|
||||
if n=nil then n:=@stub_;
|
||||
store_release(tail_,n); //unlock
|
||||
|
||||
Exit(True);
|
||||
end;
|
||||
|
||||
head:=head_;
|
||||
if tail<>head then
|
||||
begin
|
||||
|
||||
if tail=nil then tail:=@stub_;
|
||||
store_release(tail_,tail); //unlock
|
||||
|
||||
Exit;
|
||||
end;
|
||||
|
||||
stub_.next_:=nil;
|
||||
n:=XCHG(head_,@stub_);
|
||||
store_release(n^.next_,@stub_);
|
||||
|
||||
n:=load_consume(tail^.next_);
|
||||
|
||||
if n<>nil then
|
||||
begin
|
||||
|
||||
val:=PNode(tail)^.Item;
|
||||
FreeMem(tail);
|
||||
|
||||
if n=nil then n:=@stub_;
|
||||
store_release(tail_,n); //unlock
|
||||
|
||||
Exit(True);
|
||||
end;
|
||||
|
||||
if tail=nil then tail:=@stub_;
|
||||
store_release(tail_,tail); //unlock
|
||||
end;
|
||||
|
||||
//
|
||||
|
||||
Function TLFQueueA.push_front(Const val:TItem):Boolean;
|
||||
Var
|
||||
Node:PNode;
|
||||
begin
|
||||
Node:=Allocator.AllocMem(SizeOf(TNode));
|
||||
Result:=Push(Node);
|
||||
end;
|
||||
|
||||
Function TLFQueueA.pop_back(Var val:TItem):Boolean;
|
||||
Var
|
||||
tail,n,head:PQNode;
|
||||
bkoff:back_off;
|
||||
begin
|
||||
Result:=False;
|
||||
|
||||
bkoff.Reset;
|
||||
repeat
|
||||
tail:=XCHG(tail_.VAL,nil);
|
||||
|
||||
if (tail<>nil) then
|
||||
begin
|
||||
Break;
|
||||
end else
|
||||
begin
|
||||
bkoff.Wait;
|
||||
end;
|
||||
|
||||
until false;
|
||||
|
||||
n:=load_consume(tail^.next_);
|
||||
|
||||
if tail=@tail_.stub then
|
||||
begin
|
||||
if n=nil then
|
||||
begin
|
||||
|
||||
if tail=nil then tail:=@tail_.stub;
|
||||
store_release(tail_.VAL,tail); //unlock
|
||||
|
||||
Exit;
|
||||
end;
|
||||
tail:=n;
|
||||
n:=load_consume(n^.next_);
|
||||
end;
|
||||
|
||||
if n<>nil then
|
||||
begin
|
||||
|
||||
val:=PNode(tail)^.Item;
|
||||
FreeMem(tail);
|
||||
|
||||
if n=nil then n:=@tail_.stub;
|
||||
store_release(tail_.VAL,n); //unlock
|
||||
|
||||
Exit(True);
|
||||
end;
|
||||
|
||||
head:=head_.VAL;
|
||||
if tail<>head then
|
||||
begin
|
||||
|
||||
if tail=nil then tail:=@tail_.stub;
|
||||
store_release(tail_.VAL,tail); //unlock
|
||||
|
||||
Exit;
|
||||
end;
|
||||
|
||||
tail_.stub.next_:=nil;
|
||||
n:=XCHG(head_.VAL,@tail_.stub);
|
||||
store_release(n^.next_,@tail_.stub);
|
||||
|
||||
n:=load_consume(tail^.next_);
|
||||
|
||||
if n<>nil then
|
||||
begin
|
||||
|
||||
val:=PNode(tail)^.Item;
|
||||
FreeMem(tail);
|
||||
|
||||
if n=nil then n:=@tail_.stub;
|
||||
store_release(tail_.VAL,n); //unlock
|
||||
|
||||
Exit(True);
|
||||
end;
|
||||
|
||||
if tail=nil then tail:=@tail_.stub;
|
||||
store_release(tail_.VAL,tail); //unlock
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue