{ $HDR$}
{**********************************************************************}
{ Unit archived using Team Coherence                                   }
{ Team Coherence is Copyright 2002 by Quality Software Components      }
{                                                                      }
{ For further information / comments, visit our WEB site at            }
{ http://www.TeamCoherence.com                                         }
{**********************************************************************}
{
  $Log:  10385: IdThreadSafe.pas

    Rev 1.0    2002.11.12 10:56:28 PM  czhower
}
unit IdThreadSafe;

interface
{$i idcompilerdefines.inc}
uses
  Classes,
  SyncObjs;

type
  TIdThreadSafe = class
  protected
    FCriticalSection: TCriticalSection;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    procedure Lock;
    procedure Unlock;
  end;

  // Yes we know that integer operations are "atomic". However we do not like to rely on
  // internal compiler implementation. This is a safe and proper way to keep our code independent
  TIdThreadSafeInteger = class(TIdThreadSafe)
  protected
    FValue: Integer;
    //
    function GetValue: Integer;
    procedure SetValue(const AValue: Integer);
  public
    function Decrement: Integer;
    function Increment: Integer;
    //
    property Value: Integer read GetValue write SetValue;
  end;

  TIdThreadSafeCardinal = class(TIdThreadSafe)
  protected
    FValue: Cardinal;
    //
    function GetValue: Cardinal;
    procedure SetValue(const AValue: Cardinal);
  public
    function Decrement: Cardinal;
    function Increment: Cardinal;
    //
    property Value: Cardinal read GetValue write SetValue;
  end;

  TIdThreadSafeString = class(TIdThreadSafe)
  protected
    FValue: string;
    //
    function GetValue: string;
    procedure SetValue(const AValue: string);
  public
    procedure Append(const AValue: string);
    procedure Prepend(const AValue: string);
    //
    property Value: string read GetValue write SetValue;
  end;

  TIdThreadSafeStringList = class(TIdThreadSafe)
  protected
    FValue: TStringList;
  public
    constructor Create(const ASorted: Boolean = False); reintroduce;
    destructor Destroy; override;
    procedure Add(const AItem: string);
    procedure AddObject(const AItem: string; AObject: TObject);
    procedure Clear;
    function Lock: TStringList; reintroduce;
    function ObjectByItem(const AItem: string): TObject;
    procedure Remove(const AItem: string);
    procedure Unlock; reintroduce;
  end;

  TIdThreadSafeList = class(TThreadList)
  public
    function IsCountLessThan(const AValue: Cardinal): Boolean;
  End;

implementation

uses
  IdGlobal, // For FreeAndNil
  SysUtils;

{ TIdThreadSafe }

constructor TIdThreadSafe.Create;
begin
  inherited;
  FCriticalSection := TCriticalSection.Create;
end;

destructor TIdThreadSafe.Destroy;
begin
  FreeAndNil(FCriticalSection);
  inherited;
end;

procedure TIdThreadSafe.Lock;
begin
  FCriticalSection.Enter;
end;

procedure TIdThreadSafe.Unlock;
begin
  FCriticalSection.Leave;
end;

{ TIdThreadSafeInteger }

function TIdThreadSafeInteger.Decrement: Integer;
begin
  Lock; try
    Result := FValue;
    Dec(FValue);
  finally Unlock; end;
end;

function TIdThreadSafeInteger.GetValue: Integer;
begin
  Lock; try
    Result := FValue;
  finally Unlock; end;
end;

function TIdThreadSafeInteger.Increment: Integer;
begin
  Lock; try
    Result := FValue;
    Inc(FValue);
  finally Unlock; end;
end;

procedure TIdThreadSafeInteger.SetValue(const AValue: Integer);
begin
  Lock; try
    FValue := AValue;
  finally Unlock; end;
end;

{ TIdThreadSafeString }

procedure TIdThreadSafeString.Append(const AValue: string);
begin
  Lock; try
    FValue := FValue + AValue;
  finally Unlock; end;
end;

function TIdThreadSafeString.GetValue: string;
begin
  Lock; try
    Result := FValue;
  finally Unlock; end;
end;

procedure TIdThreadSafeString.Prepend(const AValue: string);
begin
  Lock; try
    FValue := AValue + FValue;
  finally Unlock; end;
end;

procedure TIdThreadSafeString.SetValue(const AValue: string);
begin
  Lock; try
    FValue := AValue;
  finally Unlock; end;
end;

{ TIdThreadSafeStringList }

procedure TIdThreadSafeStringList.Add(const AItem: string);
begin
  with Lock do try
    Add(AItem);
  finally Unlock; end;
end;

procedure TIdThreadSafeStringList.AddObject(const AItem: string; AObject: TObject);
begin
  with Lock do try
    AddObject(AItem, AObject);
  finally Unlock; end;
end;

procedure TIdThreadSafeStringList.Clear;
begin
  with Lock do try
    Clear;
  finally Unlock; end;
end;

constructor TIdThreadSafeStringList.Create(const ASorted: Boolean = False);
begin
  inherited Create;
  FValue := TStringList.Create;
  FValue.Sorted := ASorted;
end;

destructor TIdThreadSafeStringList.Destroy;
begin
  inherited Lock; try
    FreeAndNil(FValue);
  finally inherited Unlock; end;
  inherited;
end;

function TIdThreadSafeStringList.Lock: TStringList;
begin
  inherited Lock;
  Result := FValue;
end;

function TIdThreadSafeStringList.ObjectByItem(const AItem: string): TObject;
var
  i: Integer;
begin
  Result := nil;
  with Lock do try
    i := IndexOf(AItem);
    if i > -1 then begin
      Result := Objects[i];
    end;
  finally Unlock; end;
end;

procedure TIdThreadSafeStringList.Remove(const AItem: string);
var
  i: Integer;
begin
  with Lock do try
    i := IndexOf(AItem);
    if i > -1 then begin
      Delete(i);
    end;
  finally Unlock; end;
end;

procedure TIdThreadSafeStringList.Unlock;
begin
  inherited Unlock;
end;

{ TIdThreadSafeCardinal }

function TIdThreadSafeCardinal.Decrement: Cardinal;
begin
  Lock; try
    Result := FValue;
    Dec(FValue);
  finally Unlock; end;
end;

function TIdThreadSafeCardinal.GetValue: Cardinal;
begin
  Lock; try
    Result := FValue;
  finally Unlock; end;
end;

function TIdThreadSafeCardinal.Increment: Cardinal;
begin
  Lock; try
    Result := FValue;
    Inc(FValue);
  finally Unlock; end;
end;

procedure TIdThreadSafeCardinal.SetValue(const AValue: Cardinal);
begin
  Lock; try
    FValue := AValue;
  finally Unlock; end;
end;

{ TIdThreadSafeList }

function TIdThreadSafeList.IsCountLessThan(const AValue: Cardinal): Boolean;
Begin
  if Assigned(SELF) then begin
    try
      Result := Cardinal(LockList.Count) < AValue;
    finally
      UnlockList;
    end;
  end else begin
    Result := TRUE; // none always <
  end;
End;

end.
