IdFiberWeaverInline.pas 11 KB

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