diff --git a/LFQueue.pas b/LFQueue.pas deleted file mode 100644 index 6a453b4..0000000 --- a/LFQueue.pas +++ /dev/null @@ -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=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=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. - - -