www.pudn.com > indy10.0.52_source.rar > IdSync.pas


{ $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:  11996: IdSync.pas  
{ 
{   Rev 1.12    2004.04.13 10:22:52 PM  czhower 
{ Changed procedure to class method. 
} 
{ 
{   Rev 1.11    4/12/2004 11:44:36 AM  BGooijen 
{ fix 
} 
{ 
{   Rev 1.10    4/12/2004 11:36:56 AM  BGooijen 
{ NotifyThread can be cleaned up with procedure now 
} 
{ 
{   Rev 1.9    2004.03.11 10:14:46 AM  czhower 
{ Improper cast fixed. 
} 
{ 
{   Rev 1.8    2004.02.29 8:23:16 PM  czhower 
{ Fixed visibility mismatch. 
} 
{ 
{   Rev 1.7    2004.02.25 10:11:42 AM  czhower 
{ Fixed visibility in notify 
} 
{ 
{   Rev 1.6    2004.02.03 4:16:54 PM  czhower 
{ For unit name changes. 
} 
{ 
{   Rev 1.5    1/1/2004 11:56:10 PM  PIonescu 
{ Fix for TIdNotifyMethod's constructor 
} 
{ 
{   Rev 1.4    2003.12.31 7:33:20 PM  czhower 
{ Constructor bug fix. 
} 
{ 
{   Rev 1.3    5/12/2003 9:17:42 AM  GGrieve 
{ compile fix 
} 
{ 
{   Rev 1.2    2003.09.18 5:42:14 PM  czhower 
{ Removed TIdThreadBase 
} 
{ 
{   Rev 1.1    05.6.2003 ã. 11:30:12  DBondzhev 
{ Mem leak fix for notifiers created in main thread. Also WaitFor for waiting 
{ notification to be executed. 
} 
{ 
{   Rev 1.0    11/13/2002 09:00:10 AM  JPMugaas 
} 
unit IdSync; 
 
interface 
 
// Author: Chad Z. Hower - a.k.a. Kudzu 
 
uses 
  Classes, 
  IdGlobal, IdThread; 
 
type 
  TIdSync = class(TObject) 
  protected 
    FThread: TIdThread; 
    // 
    procedure DoSynchronize; virtual; abstract; 
  public 
    constructor Create; overload; virtual; 
    constructor Create(AThread: TIdThread); overload; virtual; 
    procedure Synchronize; 
    class procedure SynchronizeMethod(AMethod: TThreadMethod); 
    // 
    property Thread: TIdThread read FThread; 
  end; 
 
  TIdNotify = class(TObject) 
  protected 
    FMainThreadUsesNotify: Boolean; 
    // 
    procedure DoNotify; virtual; abstract; 
  public 
    constructor Create; virtual; // here to make virtual 
    procedure Notify; 
    class procedure FreeThread; 
    procedure WaitFor; 
    class procedure NotifyMethod(AMethod: TThreadMethod); 
    // 
    property MainThreadUsesNotify: Boolean read FMainThreadUsesNotify write FMainThreadUsesNotify; 
  end; 
 
  TIdNotifyMethod = class(TIdNotify) 
  protected 
    FMethod: TThreadMethod; 
    // 
    procedure DoNotify; override; 
  public 
    constructor Create(AMethod: TThreadMethod); reintroduce; virtual; 
  end; 
   
implementation 
 
uses 
  SysUtils; 
 
type 
  // This is done with a NotifyThread instead of PostMessage because starting 
  // with D6/Kylix Borland radically modified the mecanisms for .Synchronize. 
  // This is a bit more code in the end, but its source compatible and does not 
  // rely on Indy directly accessing any OS APIs and performance is still more 
  // than acceptable, especially considering Notifications are low priority. 
  TIdNotifyThread = class(TIdThread) 
  protected 
    FEvent: TIdLocalEvent; 
    FNotifications: TThreadList; 
  public 
    procedure AddNotification(ASync: TIdNotify); 
    constructor Create; reintroduce; 
    destructor Destroy; override; 
    procedure Run; override; 
  end; 
 
var 
  GNotifyThread: TIdNotifyThread = nil; 
 
procedure CreateNotifyThread; 
begin 
  if GNotifyThread = nil then begin 
    GNotifyThread := TIdNotifyThread.Create; 
  end; 
end; 
 
{ TIdSync } 
 
constructor TIdSync.Create(AThread: TIdThread); 
begin 
  inherited Create; 
  FThread := AThread; 
end; 
 
constructor TIdNotify.Create; 
begin 
  inherited Create; 
end; 
 
class procedure TIdNotify.FreeThread; 
begin 
  if GNotifyThread <> nil then begin 
    GNotifyThread.Stop; 
    GNotifyThread.FEvent.SetEvent; 
    GNotifyThread.WaitFor; 
    // Instead of FreeOnTerminate so we can set the reference to nil 
    FreeAndNil(GNotifyThread); 
  end; 
end; 
 
procedure TIdNotify.Notify; 
begin 
  if InMainThread and (MainThreadUsesNotify = False) then begin 
    DoNotify; 
    Free; 
  end else begin 
    CreateNotifyThread; 
    GNotifyThread.AddNotification(Self); 
  end; 
end; 
 
class procedure TIdNotify.NotifyMethod(AMethod: TThreadMethod); 
begin 
  TIdNotifyMethod.Create(AMethod).Notify; 
end; 
 
constructor TIdSync.Create; 
begin 
  CreateNotifyThread; 
  Create(GNotifyThread); 
end; 
 
procedure TIdSync.Synchronize; 
begin 
  FThread.Synchronize(DoSynchronize); 
end; 
 
class procedure TIdSync.SynchronizeMethod(AMethod: TThreadMethod); 
begin 
  with Create do try 
    FThread.Synchronize(AMethod); 
  finally Free; end; 
end; 
 
{ TIdNotifyThread } 
 
procedure TIdNotifyThread.AddNotification(ASync: TIdNotify); 
begin 
  FNotifications.Add(ASync); 
  FEvent.SetEvent; 
end; 
 
constructor TIdNotifyThread.Create; 
begin 
  FEvent := TIdLocalEvent.Create; 
  FNotifications := TThreadList.Create; 
  // Must be before - Thread starts running when we call inherited 
  inherited Create(False, False); 
end; 
 
destructor TIdNotifyThread.Destroy; 
begin 
  // Free remaining Notifications if thre is somthing that is still in 
  // the queue after thread was terminated 
  with FNotifications.LockList do try 
    while Count > 0 do begin 
      TIdNotify(Items[0]).Free; 
      Delete(0); 
    end; 
  finally FNotifications.UnlockList; end; 
  FreeAndNil(FNotifications); 
  FreeAndNil(FEvent); 
  inherited Destroy; 
end; 
 
procedure TIdNotifyThread.Run; 
// NOTE: Be VERY careful with making changes to this proc. It is VERY delicate and the order 
// of execution is very important. Small changes can have drastic effects 
var 
  LNotifications: TList; 
  LNotify: TIdNotify; 
begin 
  FEvent.WaitForEver; 
  // If terminated while waiting on the event or during the loop 
  while not Stopped do begin 
    try 
      LNotifications := FNotifications.LockList; try 
        if LNotifications.Count = 0 then begin 
          Break; 
        end; 
        LNotify := TIdNotify(LNotifications.Items[0]); 
      finally FNotifications.UnlockList; end; 
      Synchronize(LNotify.DoNotify); 
      FreeAndNil(LNotify); 
      with FNotifications.LockList do try 
        Delete(0); 
      finally FNotifications.UnlockList; end; 
    except // Catch all exceptions especially these which are raised during the application close 
    end; 
  end; 
end; 
 
{ TIdNotifyMethod } 
 
constructor TIdNotifyMethod.Create(AMethod: TThreadMethod); 
begin 
  inherited Create; 
  FMethod := AMethod; 
end; 
 
procedure TIdNotifyMethod.DoNotify; 
begin 
  FMethod; 
end; 
 
procedure TIdNotify.WaitFor; 
Var 
  LNotifyIndex: Integer; 
begin 
  LNotifyIndex := 0; 
  while LNotifyIndex <> -1 do begin 
    with GNotifyThread.FNotifications.LockList do try 
      LNotifyIndex := IndexOf(Self); 
    finally GNotifyThread.FNotifications.UnlockList; end; 
    Sleep(10); 
  end; 
end; 
 
initialization 
finalization 
  TIdNotify.FreeThread 
end.