LazUtils: add thread-save map iterator (lock map)

git-svn-id: trunk@64127 -
This commit is contained in:
martin 2020-11-12 12:02:31 +00:00
parent 0a7abce1cf
commit 689df5211a

View File

@ -82,6 +82,13 @@ type
{ TBaseMap } { TBaseMap }
{ TLockedMapModifyException }
TLockedMapModifyException = class(Exception)
public
constructor Create;
end;
TBaseMapIterator = class; TBaseMapIterator = class;
TBaseMap = class(TPersistent) TBaseMap = class(TPersistent)
@ -92,6 +99,7 @@ type
FFirst: PMapItem; // First element of our linkedlist FFirst: PMapItem; // First element of our linkedlist
FLast: PMapItem; // Last element of our linkedlist FLast: PMapItem; // Last element of our linkedlist
FIterators: TList; // A List of iterators iterating us FIterators: TList; // A List of iterators iterating us
FLocked: integer;
function FindNode(const AId): TAvlTreeNode; function FindNode(const AId): TAvlTreeNode;
function FindItem(const AId): PMapItem; function FindItem(const AId): PMapItem;
procedure FreeData(ANode: TAvlTreeNode); procedure FreeData(ANode: TAvlTreeNode);
@ -100,6 +108,8 @@ type
procedure IteratorAdd(AIterator: TBaseMapIterator); procedure IteratorAdd(AIterator: TBaseMapIterator);
procedure IteratorRemove(AIterator: TBaseMapIterator); procedure IteratorRemove(AIterator: TBaseMapIterator);
protected protected
procedure LockMap;
procedure UnLockMap;
procedure InternalAdd(const AId, AData); procedure InternalAdd(const AId, AData);
function InternalGetData(AItem: PMapItem; out AData): Boolean; function InternalGetData(AItem: PMapItem; out AData): Boolean;
function InternalGetDataPtr(AItem: PMapItem): Pointer; function InternalGetDataPtr(AItem: PMapItem): Pointer;
@ -128,6 +138,8 @@ type
procedure MapCleared; // Called when our map is cleared procedure MapCleared; // Called when our map is cleared
procedure ItemRemove(AData: Pointer); // Called when an Item is removed from the map procedure ItemRemove(AData: Pointer); // Called when an Item is removed from the map
protected protected
procedure AddToMap; virtual;
procedure RemoveFromMap; virtual;
procedure InternalCreate(AMap: TBaseMap); procedure InternalCreate(AMap: TBaseMap);
function InternalLocate(const AId): Boolean; //True if match found. If not found, current is next and Invalid is set function InternalLocate(const AId): Boolean; //True if match found. If not found, current is next and Invalid is set
procedure Validate; procedure Validate;
@ -172,6 +184,17 @@ type
procedure SetData(const AData); procedure SetData(const AData);
end; end;
{ TLockedMapIterator
Allow iteration of a map in multiple threads.
The map will be locked againts adding/removing entries.
}
TLockedMapIterator = class(TMapIterator)
protected
procedure AddToMap; override;
procedure RemoveFromMap; override;
end;
{ TTypedMap } { TTypedMap }
TTypedMap = class(TBaseMap) TTypedMap = class(TBaseMap)
@ -222,6 +245,12 @@ begin
else Result := AMap.FTree.ReportAsString; else Result := AMap.FTree.ReportAsString;
end; end;
{ TLockedMapModifyException }
constructor TLockedMapModifyException.Create;
begin
inherited Create('Map modification not allowed');
end;
{ TBaseMap } { TBaseMap }
@ -229,6 +258,8 @@ procedure TBaseMap.Clear;
var var
n: Integer; n: Integer;
begin begin
if FLocked > 0 then
raise TLockedMapModifyException.Create;
FreeData(FTree.Root); FreeData(FTree.Root);
FTree.Clear; FTree.Clear;
FFirst := nil; FFirst := nil;
@ -260,6 +291,9 @@ var
Node: TAvlTreeNode; Node: TAvlTreeNode;
n: integer; n: integer;
begin begin
if FLocked > 0 then
raise TLockedMapModifyException.Create;
Node := FindNode(AId); Node := FindNode(AId);
Result := Node <> nil; Result := Node <> nil;
if not result then Exit; if not result then Exit;
@ -288,6 +322,9 @@ destructor TBaseMap.Destroy;
var var
n: Integer; n: Integer;
begin begin
if FLocked > 0 then
raise TLockedMapModifyException.Create;
// notify our iterators // notify our iterators
if FIterators <> nil if FIterators <> nil
then begin then begin
@ -361,6 +398,9 @@ var
p: Pointer; p: Pointer;
Node, NewNode: TAvlTreeNode; Node, NewNode: TAvlTreeNode;
begin begin
if FLocked > 0 then
raise TLockedMapModifyException.Create;
if FindNode(AId) <> nil if FindNode(AId) <> nil
then begin then begin
Error; Error;
@ -461,6 +501,16 @@ begin
if FIterators.Count = 0 then FreeAndNil(FIterators); if FIterators.Count = 0 then FreeAndNil(FIterators);
end; end;
procedure TBaseMap.LockMap;
begin
InterLockedIncrement(FLocked);
end;
procedure TBaseMap.UnLockMap;
begin
InterLockedDecrement(FLocked);
end;
procedure TBaseMap.ReleaseData(ADataPtr: Pointer); procedure TBaseMap.ReleaseData(ADataPtr: Pointer);
begin begin
end; end;
@ -515,7 +565,7 @@ end;
destructor TBaseMapIterator.Destroy; destructor TBaseMapIterator.Destroy;
begin begin
if FMap <> nil then FMap.IteratorRemove(Self); if FMap <> nil then RemoveFromMap;
FMap := nil; FMap := nil;
inherited Destroy; inherited Destroy;
end; end;
@ -532,7 +582,7 @@ procedure TBaseMapIterator.InternalCreate(AMap: TBaseMap);
begin begin
inherited Create; inherited Create;
FMap := AMap; FMap := AMap;
FMap.IteratorAdd(Self); AddToMap;
FCurrent := FMap.FFirst; FCurrent := FMap.FFirst;
FBOM := FCurrent = nil; FBOM := FCurrent = nil;
FEOM := FCurrent = nil; FEOM := FCurrent = nil;
@ -600,6 +650,16 @@ begin
FEOM := FCurrent = nil; FEOM := FCurrent = nil;
end; end;
procedure TBaseMapIterator.AddToMap;
begin
FMap.IteratorAdd(Self);
end;
procedure TBaseMapIterator.RemoveFromMap;
begin
FMap.IteratorRemove(Self);
end;
procedure TBaseMapIterator.Last; procedure TBaseMapIterator.Last;
begin begin
if FMap = nil then Exit; if FMap = nil then Exit;
@ -878,6 +938,18 @@ begin
FMap.InternalSetData(FCurrent, AData); FMap.InternalSetData(FCurrent, AData);
end; end;
{ TLockedMapIterator }
procedure TLockedMapIterator.AddToMap;
begin
FMap.LockMap;
end;
procedure TLockedMapIterator.RemoveFromMap;
begin
FMap.UnLockMap;
end;
{ TTypedMapIterator } { TTypedMapIterator }
constructor TTypedMapIterator.Create(AMap: TTypedMap); constructor TTypedMapIterator.Create(AMap: TTypedMap);