| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- { $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: 56070: IdFiberWeaverInline.pas
- {
- 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.
|