IdFiberWeaverInline.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.2 6/11/2004 8:39:52 AM DSiders
  18. Added "Do not Localize" comments.
  19. Rev 1.1 2004.02.09 9:16:38 PM czhower
  20. Updated to compile and match lib changes.
  21. Rev 1.0 2004.02.03 12:38:52 AM czhower
  22. Move
  23. Rev 1.2 2003.11.04 3:51:20 PM czhower
  24. Update to sync TC
  25. Rev 1.1 2003.10.21 12:19:22 AM czhower
  26. TIdTask support and fiber bug fixes.
  27. Rev 1.0 2003.10.19 2:50:54 PM czhower
  28. Fiber cleanup
  29. Rev 1.4 2003.10.19 1:04:26 PM czhower
  30. Updates
  31. Rev 1.3 2003.10.11 5:43:20 PM czhower
  32. Chained servers now functional.
  33. Rev 1.2 2003.09.19 10:09:40 PM czhower
  34. Next stage of fiber support in servers.
  35. Rev 1.1 2003.08.20 1:46:22 PM czhower
  36. Update to compile.
  37. Rev 1.0 8/16/2003 11:09:12 AM JPMugaas
  38. Moved from Indy Core dir as part of package reorg
  39. Rev 1.8 7/26/2003 12:20:02 PM BGooijen
  40. Small fix to prevent some exceptions
  41. Rev 1.7 2003.06.30 7:33:50 PM czhower
  42. Fix to exception handling.
  43. Rev 1.6 2003.06.25 1:25:58 AM czhower
  44. Small changes.
  45. Rev 1.4 2003.06.03 11:05:02 PM czhower
  46. Modified ProcessInThisFiber to support error flag return.
  47. Rev 1.3 2003.04.17 7:44:58 PM czhower
  48. Rev 1.2 4/11/2003 6:37:38 PM BGooijen
  49. ProcessInThisFiber and WaitForFibers are now overridden here
  50. Rev 1.1 2003.04.10 10:51:06 PM czhower
  51. Rev 1.14 3/27/2003 12:34:02 PM BGooijen
  52. very little clean-up
  53. Rev 1.13 2003.03.27 1:31:18 AM czhower
  54. Removal of hack cast.
  55. Rev 1.12 2003.03.27 1:29:16 AM czhower
  56. Exception frame swapping.
  57. Rev 1.11 2003.03.27 12:45:58 AM czhower
  58. Fixed AV relating to preparation changes for exception frame swapping
  59. Rev 1.10 2003.03.27 12:18:06 AM czhower
  60. Rev 1.9 3/26/2003 8:37:50 PM BGooijen
  61. Added WaitForFibers
  62. Rev 1.8 2003.03.26 12:48:30 AM czhower
  63. Rev 1.7 3/25/2003 01:58:20 PM JPMugaas
  64. Fixed a type-error.
  65. Rev 1.6 3/25/2003 01:27:56 AM JPMugaas
  66. Made a custom exception class that descends from EIdSIlentException so that
  67. the component does not always raise an exception in the server if there's no
  68. client connection.
  69. Rev 1.5 2003.03.16 12:49:32 PM czhower
  70. Rev 1.4 3/13/2003 10:18:14 AM BGooijen
  71. Server side fibers, bug fixes
  72. Rev 1.3 12-15-2002 17:08:00 BGooijen
  73. Removed AssignList, and added a hack-cast to use .Assign
  74. Rev 1.2 2002.12.07 11:10:30 PM czhower
  75. Removed unneeded code.
  76. Rev 1.1 12-6-2002 20:34:10 BGooijen
  77. Now compiles on Delphi 5
  78. Rev 1.0 11/13/2002 08:44:26 AM JPMugaas
  79. }
  80. unit IdFiberWeaverInline;
  81. interface
  82. uses
  83. Classes, IdException,
  84. IdGlobal, IdFiber, IdFiberWeaver, IdThreadSafe,
  85. SyncObjs;
  86. type
  87. TIdFiberWeaverInline = class;
  88. TIdFiberNotifyEvent = procedure(AFiberWeaver: TIdFiberWeaverInline;
  89. AFiber: TIdFiberBase) of object;
  90. TIdFiberWeaverInline = class(TIdFiberWeaver)
  91. protected
  92. // TIdThreadSafeInteger cannot be used for FActiveFiberList because the
  93. // semantics cause the first fiber to be counted more than once during
  94. // finish, and possibly other fibers as well. The only other solution
  95. // involves using TIdFiber itself, and that would cause changes to TIdFiber
  96. // that would be made only for the accomodation of TIdFiberWeaverInline.
  97. //
  98. // As it is TIdFiber itself has no knowledge ot TIdFiberWeaverInline.
  99. //
  100. // FActiveFiberList is used by ProcessInThisThread to detect when all fibers
  101. // have finished.
  102. FActiveFiberList: TIdThreadSafeList;
  103. FAddEvent: TEvent;
  104. // FActiveFiberList contains a list of fibers to schedule. Fibers are
  105. // removed when they are running or are suspened. When a fiber is ready to
  106. // excecuted again it is added to FActiveFiberList and the fiber weaver will
  107. // schedule it.
  108. FFiberList: TIdThreadSafeList;
  109. FFreeFibersOnCompletion: Boolean;
  110. FOnIdle: TNotifyEvent;
  111. FOnSwitch: TIdFiberNotifyEvent;
  112. FSelfFiber: TIdConvertedFiber;
  113. //
  114. procedure DoIdle;
  115. procedure DoSwitch(AFiber: TIdFiberBase); virtual;
  116. procedure InitComponent; override;
  117. procedure Relinquish(
  118. AFiber: TIdFiber;
  119. AReschedule: Boolean
  120. ); override;
  121. procedure ScheduleFiber(
  122. ACurrentFiber: TIdFiberBase;
  123. ANextFiber: TIdFiber
  124. );
  125. public
  126. procedure Add(AFiber: TIdFiber); override;
  127. destructor Destroy; override;
  128. function HasFibers: Boolean;
  129. function ProcessInThisThread: Boolean;
  130. function WaitForFibers(
  131. ATimeout: Cardinal = Infinite
  132. ): Boolean;
  133. override;
  134. published
  135. property FreeFibersOnCompletion: Boolean read FFreeFibersOnCompletion
  136. write FFreeFibersOnCompletion;
  137. //
  138. property OnIdle: TNotifyEvent read FOnIdle write FOnIdle;
  139. property OnSwitch: TIdFiberNotifyEvent read FOnSwitch write FOnSwitch;
  140. end;
  141. EIdNoFibersToSchedule = class(EIdSilentException);
  142. implementation
  143. uses
  144. SysUtils,
  145. Windows;
  146. { TIdFiberWeaverInline }
  147. procedure TIdFiberWeaverInline.Add(AFiber: TIdFiber);
  148. begin
  149. inherited;
  150. AFiber.SetRelinquishHandler(Relinquish);
  151. with FFiberList.LockList do try
  152. Add(AFiber);
  153. FAddEvent.SetEvent;
  154. finally FFiberList.UnlockList; end;
  155. end;
  156. destructor TIdFiberWeaverInline.Destroy;
  157. begin
  158. FreeAndNil(FActiveFiberList);
  159. FreeAndNil(FFiberList);
  160. FreeAndNil(FAddEvent);
  161. inherited;
  162. end;
  163. procedure TIdFiberWeaverInline.DoIdle;
  164. begin
  165. if Assigned(FOnIdle) then begin
  166. FOnIdle(Self);
  167. end;
  168. end;
  169. procedure TIdFiberWeaverInline.DoSwitch(AFiber: TIdFiberBase);
  170. begin
  171. if Assigned(FOnSwitch) then begin
  172. FOnSwitch(Self, AFiber);
  173. end;
  174. end;
  175. function TIdFiberWeaverInline.HasFibers: Boolean;
  176. begin
  177. Result := not FFiberList.IsCountLessThan(1);
  178. end;
  179. procedure TIdFiberWeaverInline.InitComponent;
  180. begin
  181. inherited;
  182. FActiveFiberList := TIdThreadSafeList.Create;
  183. FAddEvent := TEvent.Create(nil, False, False, '');
  184. FFiberList := TIdThreadSafeList.Create;
  185. end;
  186. function TIdFiberWeaverInline.ProcessInThisThread: Boolean;
  187. // Returns true if ANY fiber terminated because of an unhandled exception.
  188. // If false, user does not need to loop through the fibers to look.
  189. var
  190. LFiber: TIdFiber;
  191. LFiberList: TList;
  192. begin
  193. Result := False;
  194. LFiberList := FFiberList.LockList; try
  195. if LFiberList.Count = 0 then begin
  196. raise EIdNoFibersToSchedule.Create('No fibers to schedule.'); {do not localize}
  197. end;
  198. FActiveFiberList.Assign(LFiberList);
  199. finally FFiberList.UnlockList; end;
  200. // This loop catches fibers as they finish. Relinquish accomplishes explicit
  201. // switching faster by performing only one switch instead of two.
  202. FSelfFiber := TIdConvertedFiber.Create; try
  203. while True do begin
  204. LFiber := TIdFiber(FFiberList.Pull);
  205. if LFiber = nil then begin
  206. if FActiveFiberList.IsEmpty then begin
  207. // All fibers finished
  208. Break;
  209. end else begin
  210. FAddEvent.WaitFor(Infinite);
  211. end;
  212. end else begin
  213. // So it will switch back here when finished so other fibers can be
  214. // processed.
  215. LFiber.ParentFiber := FSelfFiber;
  216. //
  217. ScheduleFiber(FSelfFiber, LFiber);
  218. // if any fiber terminated with a fatal exception return true
  219. // Dont set it to it, else false would reset it.
  220. if FSelfFiber.PriorFiber is TIdFiber then begin
  221. LFiber := TIdFiber(FSelfFiber.PriorFiber);
  222. if LFiber.FatalExceptionOccurred then begin
  223. Result := True;
  224. end;
  225. // Finished fibers always switch back to parent and will not short
  226. // circuit schedule
  227. if LFiber.Finished then begin
  228. FActiveFiberList.Remove(LFiber);
  229. if FreeFibersOnCompletion then begin
  230. FreeAndNil(LFiber);
  231. end;
  232. end;
  233. end;
  234. end;
  235. end;
  236. finally FreeAndNil(FSelfFiber); end;
  237. end;
  238. procedure TIdFiberWeaverInline.Relinquish(
  239. AFiber: TIdFiber;
  240. AReschedule: Boolean
  241. );
  242. var
  243. LFiber: TIdFiber;
  244. begin
  245. while True do begin
  246. LFiber := nil;
  247. // Get next fiber to schedule
  248. with FFiberList.LockList do try
  249. if Count > 0 then begin
  250. LFiber := TIdFiber(List[0]);
  251. Delete(0);
  252. if AReschedule then begin
  253. Add(AFiber);
  254. end;
  255. // If no fibers to schedule, we will rerun ourself if set to reschedule
  256. end else if AReschedule then begin
  257. // Soft cast as a check that a converted fiber has not been passed
  258. // with AReschedule = True
  259. LFiber := AFiber as TIdFiber;
  260. end;
  261. finally FFiberList.UnlockList; end;
  262. if LFiber = nil then begin
  263. // If there are no fibers to schedule, that means we are waiting on
  264. // ourself, or another relinquished fiber. Wait for one to get readded
  265. // to list.
  266. //
  267. //TODO: Allow a parameter for timeout and call DoIdle
  268. //TODO: Better yet - integrate with AntiFreeze also
  269. DoIdle;
  270. FAddEvent.WaitFor(Infinite);
  271. end else if LFiber = AFiber then begin
  272. // If the next fiber is ourself, simply exit to return to ourself
  273. Break;
  274. end else if LFiber <> nil then begin
  275. // Must set the parent fiber to self so that when it finishes we get
  276. // control again. The main ProcessInThisThread loop does this, but
  277. // only for ones it first starts. Fibers can get added to the list and
  278. // then scheduled here in this short circuit switch. When they finish
  279. // they will have no parent fiber.
  280. LFiber.ParentFiber := FSelfFiber;
  281. ScheduleFiber(AFiber, LFiber);
  282. // If we get switched back to, we have been scheduled so exit
  283. Break;
  284. end;
  285. end;
  286. // For future expansion when can switch between weavers
  287. AFiber.SetRelinquishHandler(Relinquish);
  288. end;
  289. procedure TIdFiberWeaverInline.ScheduleFiber(
  290. ACurrentFiber: TIdFiberBase;
  291. ANextFiber: TIdFiber
  292. );
  293. begin
  294. DoSwitch(ANextFiber);
  295. ACurrentFiber.SwitchTo(ANextFiber);
  296. end;
  297. function TIdFiberWeaverInline.WaitForFibers(
  298. ATimeout: Cardinal = Infinite
  299. ): Boolean;
  300. begin
  301. if not FFiberList.IsEmpty then begin
  302. Result := True;
  303. end else begin
  304. Result := (FAddEvent.WaitFor(ATimeout) = wrSignaled) and not FFiberList.IsEmpty;
  305. end;
  306. end;
  307. end.