restemplate/indy/SuperCore/IdFiberWeaverInline.pas

362 lines
11 KiB
Plaintext
Raw Permalink Normal View History

{
$Project$
$Workfile$
$Revision$
$DateUTC$
$Id$
This file is part of the Indy (Internet Direct) project, and is offered
under the dual-licensing agreement described on the Indy website.
(http://www.indyproject.org/)
Copyright:
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
$Log$
}
{
Rev 1.2 6/11/2004 8:39:52 AM DSiders
Added "Do not Localize" comments.
Rev 1.1 2004.02.09 9:16:38 PM czhower
Updated to compile and match lib changes.
Rev 1.0 2004.02.03 12:38:52 AM czhower
Move
Rev 1.2 2003.11.04 3:51:20 PM czhower
Update to sync TC
Rev 1.1 2003.10.21 12:19:22 AM czhower
TIdTask support and fiber bug fixes.
Rev 1.0 2003.10.19 2:50:54 PM czhower
Fiber cleanup
Rev 1.4 2003.10.19 1:04:26 PM czhower
Updates
Rev 1.3 2003.10.11 5:43:20 PM czhower
Chained servers now functional.
Rev 1.2 2003.09.19 10:09:40 PM czhower
Next stage of fiber support in servers.
Rev 1.1 2003.08.20 1:46:22 PM czhower
Update to compile.
Rev 1.0 8/16/2003 11:09:12 AM JPMugaas
Moved from Indy Core dir as part of package reorg
Rev 1.8 7/26/2003 12:20:02 PM BGooijen
Small fix to prevent some exceptions
Rev 1.7 2003.06.30 7:33:50 PM czhower
Fix to exception handling.
Rev 1.6 2003.06.25 1:25:58 AM czhower
Small changes.
Rev 1.4 2003.06.03 11:05:02 PM czhower
Modified ProcessInThisFiber to support error flag return.
Rev 1.3 2003.04.17 7:44:58 PM czhower
Rev 1.2 4/11/2003 6:37:38 PM BGooijen
ProcessInThisFiber and WaitForFibers are now overridden here
Rev 1.1 2003.04.10 10:51:06 PM czhower
Rev 1.14 3/27/2003 12:34:02 PM BGooijen
very little clean-up
Rev 1.13 2003.03.27 1:31:18 AM czhower
Removal of hack cast.
Rev 1.12 2003.03.27 1:29:16 AM czhower
Exception frame swapping.
Rev 1.11 2003.03.27 12:45:58 AM czhower
Fixed AV relating to preparation changes for exception frame swapping
Rev 1.10 2003.03.27 12:18:06 AM czhower
Rev 1.9 3/26/2003 8:37:50 PM BGooijen
Added WaitForFibers
Rev 1.8 2003.03.26 12:48:30 AM czhower
Rev 1.7 3/25/2003 01:58:20 PM JPMugaas
Fixed a type-error.
Rev 1.6 3/25/2003 01:27:56 AM JPMugaas
Made a custom exception class that descends from EIdSIlentException so that
the component does not always raise an exception in the server if there's no
client connection.
Rev 1.5 2003.03.16 12:49:32 PM czhower
Rev 1.4 3/13/2003 10:18:14 AM BGooijen
Server side fibers, bug fixes
Rev 1.3 12-15-2002 17:08:00 BGooijen
Removed AssignList, and added a hack-cast to use .Assign
Rev 1.2 2002.12.07 11:10:30 PM czhower
Removed unneeded code.
Rev 1.1 12-6-2002 20:34:10 BGooijen
Now compiles on Delphi 5
Rev 1.0 11/13/2002 08:44:26 AM JPMugaas
}
unit IdFiberWeaverInline;
interface
uses
Classes, IdException,
IdGlobal, IdFiber, IdFiberWeaver, IdThreadSafe,
SyncObjs;
type
TIdFiberWeaverInline = class;
TIdFiberNotifyEvent = procedure(AFiberWeaver: TIdFiberWeaverInline;
AFiber: TIdFiberBase) of object;
TIdFiberWeaverInline = class(TIdFiberWeaver)
protected
// TIdThreadSafeInteger cannot be used for FActiveFiberList because the
// semantics cause the first fiber to be counted more than once during
// finish, and possibly other fibers as well. The only other solution
// involves using TIdFiber itself, and that would cause changes to TIdFiber
// that would be made only for the accomodation of TIdFiberWeaverInline.
//
// As it is TIdFiber itself has no knowledge ot TIdFiberWeaverInline.
//
// FActiveFiberList is used by ProcessInThisThread to detect when all fibers
// have finished.
FActiveFiberList: TIdThreadSafeList;
FAddEvent: TEvent;
// FActiveFiberList contains a list of fibers to schedule. Fibers are
// removed when they are running or are suspened. When a fiber is ready to
// excecuted again it is added to FActiveFiberList and the fiber weaver will
// schedule it.
FFiberList: TIdThreadSafeList;
FFreeFibersOnCompletion: Boolean;
FOnIdle: TNotifyEvent;
FOnSwitch: TIdFiberNotifyEvent;
FSelfFiber: TIdConvertedFiber;
//
procedure DoIdle;
procedure DoSwitch(AFiber: TIdFiberBase); virtual;
procedure InitComponent; override;
procedure Relinquish(
AFiber: TIdFiber;
AReschedule: Boolean
); override;
procedure ScheduleFiber(
ACurrentFiber: TIdFiberBase;
ANextFiber: TIdFiber
);
public
procedure Add(AFiber: TIdFiber); override;
destructor Destroy; override;
function HasFibers: Boolean;
function ProcessInThisThread: Boolean;
function WaitForFibers(
ATimeout: Cardinal = Infinite
): Boolean;
override;
published
property FreeFibersOnCompletion: Boolean read FFreeFibersOnCompletion
write FFreeFibersOnCompletion;
//
property OnIdle: TNotifyEvent read FOnIdle write FOnIdle;
property OnSwitch: TIdFiberNotifyEvent read FOnSwitch write FOnSwitch;
end;
EIdNoFibersToSchedule = class(EIdSilentException);
implementation
uses
SysUtils,
Windows;
{ TIdFiberWeaverInline }
procedure TIdFiberWeaverInline.Add(AFiber: TIdFiber);
begin
inherited;
AFiber.SetRelinquishHandler(Relinquish);
with FFiberList.LockList do try
Add(AFiber);
FAddEvent.SetEvent;
finally FFiberList.UnlockList; end;
end;
destructor TIdFiberWeaverInline.Destroy;
begin
FreeAndNil(FActiveFiberList);
FreeAndNil(FFiberList);
FreeAndNil(FAddEvent);
inherited;
end;
procedure TIdFiberWeaverInline.DoIdle;
begin
if Assigned(FOnIdle) then begin
FOnIdle(Self);
end;
end;
procedure TIdFiberWeaverInline.DoSwitch(AFiber: TIdFiberBase);
begin
if Assigned(FOnSwitch) then begin
FOnSwitch(Self, AFiber);
end;
end;
function TIdFiberWeaverInline.HasFibers: Boolean;
begin
Result := not FFiberList.IsCountLessThan(1);
end;
procedure TIdFiberWeaverInline.InitComponent;
begin
inherited;
FActiveFiberList := TIdThreadSafeList.Create;
FAddEvent := TEvent.Create(nil, False, False, '');
FFiberList := TIdThreadSafeList.Create;
end;
function TIdFiberWeaverInline.ProcessInThisThread: Boolean;
// Returns true if ANY fiber terminated because of an unhandled exception.
// If false, user does not need to loop through the fibers to look.
var
LFiber: TIdFiber;
LFiberList: TList;
begin
Result := False;
LFiberList := FFiberList.LockList; try
if LFiberList.Count = 0 then begin
raise EIdNoFibersToSchedule.Create('No fibers to schedule.'); {do not localize}
end;
FActiveFiberList.Assign(LFiberList);
finally FFiberList.UnlockList; end;
// This loop catches fibers as they finish. Relinquish accomplishes explicit
// switching faster by performing only one switch instead of two.
FSelfFiber := TIdConvertedFiber.Create; try
while True do begin
LFiber := TIdFiber(FFiberList.Pull);
if LFiber = nil then begin
if FActiveFiberList.IsEmpty then begin
// All fibers finished
Break;
end else begin
FAddEvent.WaitFor(Infinite);
end;
end else begin
// So it will switch back here when finished so other fibers can be
// processed.
LFiber.ParentFiber := FSelfFiber;
//
ScheduleFiber(FSelfFiber, LFiber);
// if any fiber terminated with a fatal exception return true
// Dont set it to it, else false would reset it.
if FSelfFiber.PriorFiber is TIdFiber then begin
LFiber := TIdFiber(FSelfFiber.PriorFiber);
if LFiber.FatalExceptionOccurred then begin
Result := True;
end;
// Finished fibers always switch back to parent and will not short
// circuit schedule
if LFiber.Finished then begin
FActiveFiberList.Remove(LFiber);
if FreeFibersOnCompletion then begin
FreeAndNil(LFiber);
end;
end;
end;
end;
end;
finally FreeAndNil(FSelfFiber); end;
end;
procedure TIdFiberWeaverInline.Relinquish(
AFiber: TIdFiber;
AReschedule: Boolean
);
var
LFiber: TIdFiber;
begin
while True do begin
LFiber := nil;
// Get next fiber to schedule
with FFiberList.LockList do try
if Count > 0 then begin
LFiber := TIdFiber(List[0]);
Delete(0);
if AReschedule then begin
Add(AFiber);
end;
// If no fibers to schedule, we will rerun ourself if set to reschedule
end else if AReschedule then begin
// Soft cast as a check that a converted fiber has not been passed
// with AReschedule = True
LFiber := AFiber as TIdFiber;
end;
finally FFiberList.UnlockList; end;
if LFiber = nil then begin
// If there are no fibers to schedule, that means we are waiting on
// ourself, or another relinquished fiber. Wait for one to get readded
// to list.
//
//TODO: Allow a parameter for timeout and call DoIdle
//TODO: Better yet - integrate with AntiFreeze also
DoIdle;
FAddEvent.WaitFor(Infinite);
end else if LFiber = AFiber then begin
// If the next fiber is ourself, simply exit to return to ourself
Break;
end else if LFiber <> nil then begin
// Must set the parent fiber to self so that when it finishes we get
// control again. The main ProcessInThisThread loop does this, but
// only for ones it first starts. Fibers can get added to the list and
// then scheduled here in this short circuit switch. When they finish
// they will have no parent fiber.
LFiber.ParentFiber := FSelfFiber;
ScheduleFiber(AFiber, LFiber);
// If we get switched back to, we have been scheduled so exit
Break;
end;
end;
// For future expansion when can switch between weavers
AFiber.SetRelinquishHandler(Relinquish);
end;
procedure TIdFiberWeaverInline.ScheduleFiber(
ACurrentFiber: TIdFiberBase;
ANextFiber: TIdFiber
);
begin
DoSwitch(ANextFiber);
ACurrentFiber.SwitchTo(ANextFiber);
end;
function TIdFiberWeaverInline.WaitForFibers(
ATimeout: Cardinal = Infinite
): Boolean;
begin
if not FFiberList.IsEmpty then begin
Result := True;
end else begin
Result := (FAddEvent.WaitFor(ATimeout) = wrSignaled) and not FFiberList.IsEmpty;
end;
end;
end.