IdWorkOpUnit.pas 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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: 56086: IdWorkOpUnit.pas
  11. {
  12. Rev 1.2 6/11/2004 8:40:10 AM DSiders
  13. Added "Do not Localize" comments.
  14. }
  15. {
  16. { Rev 1.1 2004.02.09 9:16:54 PM czhower
  17. { Updated to compile and match lib changes.
  18. }
  19. {
  20. { Rev 1.0 2004.02.03 12:39:08 AM czhower
  21. { Move
  22. }
  23. {
  24. { Rev 1.17 2003.10.19 2:50:42 PM czhower
  25. { Fiber cleanup
  26. }
  27. {
  28. { Rev 1.16 2003.10.11 5:44:02 PM czhower
  29. { Chained servers now functional.
  30. }
  31. {
  32. { Rev 1.15 2003.07.17 4:42:06 PM czhower
  33. { More IOCP improvements.
  34. }
  35. {
  36. { Rev 1.14 2003.07.17 3:55:18 PM czhower
  37. { Removed IdIOChainEngineIOCP and merged it into TIdChaingEngine in
  38. { IdIOHandlerChain.pas.
  39. }
  40. {
  41. { Rev 1.10 2003.07.14 12:54:32 AM czhower
  42. { Fixed graceful close detection if it occurs after connect.
  43. }
  44. {
  45. { Rev 1.9 2003.07.10 7:40:24 PM czhower
  46. { Comments
  47. }
  48. {
  49. Rev 1.8 7/5/2003 11:47:12 PM BGooijen
  50. Added TIdWorkOpUnitCheckForDisconnect and TIdWorkOpUnitWriteFile
  51. }
  52. {
  53. Rev 1.7 4/23/2003 8:22:20 PM BGooijen
  54. }
  55. {
  56. { Rev 1.6 2003.04.22 9:48:50 PM czhower
  57. }
  58. {
  59. { Rev 1.5 2003.04.20 9:12:20 PM czhower
  60. }
  61. {
  62. { Rev 1.5 2003.04.19 3:14:14 PM czhower
  63. }
  64. {
  65. { Rev 1.4 2003.04.17 7:45:02 PM czhower
  66. }
  67. {
  68. Rev 1.2 3/27/2003 2:43:04 PM BGooijen
  69. Added woWriteStream and woWriteBuffer
  70. }
  71. {
  72. Rev 1.1 3/2/2003 12:36:24 AM BGooijen
  73. Added woReadBuffer and TIdWorkOpUnitReadBuffer to read a buffer. Now
  74. ReadBuffer doesn't use ReadStream any more.
  75. TIdIOHandlerChain.ReadLn now supports MaxLineLength (splitting, and
  76. exceptions).
  77. woReadLn doesn't check the intire buffer any more, but continued where it
  78. stopped the last time.
  79. Added basic support for timeouts (probably only on read operations, and maybe
  80. connect), accuratie of timeout is currently 500msec.
  81. }
  82. {
  83. Rev 1.0 2/25/2003 10:45:46 PM BGooijen
  84. Opcode files, some of these were in IdIOHandlerChain.pas
  85. }
  86. unit IdWorkOpUnit;
  87. interface
  88. uses
  89. IdFiber, IdIOHandlerSocket, IdStackConsts, IdWinsock2, IdGlobal
  90. , SysUtils
  91. , Windows;
  92. type
  93. TIdWorkOpUnit = class;
  94. TOnWorkOpUnitCompleted = procedure(ASender: TIdWorkOpUnit) of object;
  95. TIdOverLapped = packed record
  96. // Reqquired parts of structure
  97. Internal: DWORD;
  98. InternalHigh: DWORD;
  99. Offset: DWORD;
  100. OffsetHigh: DWORD;
  101. HEvent: THandle;
  102. // Indy parts
  103. WorkOpUnit: TIdWorkOpUnit;
  104. Buffer: PWSABUF; // Indy part too, we reference it and pass it to IOCP
  105. end;
  106. PIdOverlapped = ^TIdOverlapped;
  107. TIdWorkOpUnit = class(TObject)
  108. protected
  109. FCompleted: Boolean;
  110. FException: Exception;
  111. FFiber: TIdFiber;
  112. FIOHandler: TIdIOHandlerSocket;
  113. FOnCompleted: TOnWorkOpUnitCompleted;
  114. FSocketHandle:TIdStackSocketHandle;
  115. FTimeOutAt: Integer;
  116. FTimedOut: Boolean;
  117. //
  118. procedure DoCompleted;
  119. virtual;
  120. function GetOverlapped(
  121. ABuffer: Pointer;
  122. ABufferSize: Integer
  123. ): PIdOverlapped;
  124. procedure Starting; virtual; abstract;
  125. public
  126. procedure Complete; virtual;
  127. destructor Destroy; override;
  128. procedure MarkComplete; virtual;
  129. // Process is called by the chain engine when data has been processed
  130. procedure Process(
  131. AOverlapped: PIdOverlapped;
  132. AByteCount: Integer
  133. ); virtual; abstract;
  134. procedure RaiseException;
  135. procedure Start;
  136. //
  137. property Completed: Boolean read FCompleted;
  138. property Fiber: TIdFiber read FFiber write FFiber;
  139. property IOHandler: TIdIOHandlerSocket read FIOHandler write FIOHandler;
  140. property OnCompleted: TOnWorkOpUnitCompleted read FOnCompleted
  141. write FOnCompleted;
  142. property SocketHandle:TIdStackSocketHandle read FSocketHandle
  143. write FSocketHandle;
  144. property TimeOutAt:integer read FTimeOutAt write FTimeOutAt;
  145. property TimedOut:boolean read FTimedOut write FTimedOut;
  146. end;
  147. TIdWorkOpUnitRead = class(TIdWorkOpUnit)
  148. protected
  149. // Used when a dynamic buffer is needed
  150. // Since its reference managed, memory is auto cleaned up
  151. FBytes: TIdBytes;
  152. //
  153. procedure Processing(
  154. ABuffer: TIdBytes
  155. ); virtual; abstract;
  156. procedure Starting;
  157. override;
  158. public
  159. procedure Process(
  160. AOverlapped: PIdOverlapped;
  161. AByteCount: Integer
  162. ); override;
  163. procedure Read;
  164. end;
  165. TIdWorkOpUnitWrite = class(TIdWorkOpUnit)
  166. protected
  167. procedure Processing(
  168. ABytes: Integer
  169. ); virtual; abstract;
  170. procedure Write(
  171. ABuffer: Pointer;
  172. ASize: Integer
  173. );
  174. public
  175. procedure Process(
  176. AOverlapped: PIdOverlapped;
  177. AByteCount: Integer
  178. ); override;
  179. end;
  180. const
  181. WOPageSize = 8192;
  182. implementation
  183. uses
  184. IdException, IdIOHandlerChain, IdStack, IdStackWindows;
  185. { TIdWorkOpUnit }
  186. procedure TIdWorkOpUnit.Complete;
  187. begin
  188. DoCompleted;
  189. end;
  190. destructor TIdWorkOpUnit.Destroy;
  191. begin
  192. FreeAndNil(FException);
  193. inherited;
  194. end;
  195. procedure TIdWorkOpUnit.DoCompleted;
  196. begin
  197. if Assigned(OnCompleted) then begin
  198. OnCompleted(Self);
  199. end;
  200. end;
  201. procedure TIdWorkOpUnit.MarkComplete;
  202. begin
  203. FCompleted := True;
  204. end;
  205. procedure TIdWorkOpUnit.RaiseException;
  206. var
  207. LException: Exception;
  208. begin
  209. if FException <> nil then begin
  210. LException := FException;
  211. // We need to set this to nil so it wont be freed. Delphi will free it
  212. // as part of its exception handling mechanism
  213. FException := nil;
  214. raise LException;
  215. end;
  216. end;
  217. function TIdWorkOpUnit.GetOverlapped(
  218. ABuffer: Pointer;
  219. ABufferSize: Integer
  220. ): PIdOverlapped;
  221. begin
  222. Result := TIdIOHandlerChain(IOHandler).Overlapped;
  223. with Result^ do begin
  224. Internal := 0;
  225. InternalHigh := 0;
  226. Offset := 0;
  227. OffsetHigh := 0;
  228. HEvent := 0;
  229. WorkOpUnit := Self;
  230. Buffer.Buf := ABuffer;
  231. Buffer.Len := ABufferSize;
  232. end;
  233. end;
  234. procedure TIdWorkOpUnit.Start;
  235. begin
  236. Starting;
  237. // This can get called after its already been marked complete. This is
  238. // ok and the fiber scheduler handles such a situation.
  239. Fiber.Relinquish;
  240. end;
  241. { TIdWorkOpUnitWrite }
  242. procedure TIdWorkOpUnitWrite.Process(
  243. AOverlapped: PIdOverlapped;
  244. AByteCount: Integer
  245. );
  246. begin
  247. Processing(AByteCount);
  248. end;
  249. procedure TIdWorkOpUnitWrite.Write(ABuffer: Pointer;
  250. ASize: Integer);
  251. var
  252. LFlags: DWORD;
  253. LOverlapped: PIdOverlapped;
  254. LLastError: Integer;
  255. LVoid: DWORD;
  256. begin
  257. LFlags := 0;
  258. LOverlapped := GetOverlapped(ABuffer, ASize);
  259. case WSASend(SocketHandle, LOverlapped.Buffer, 1, LVoid, LFlags, LOverlapped
  260. , nil) of
  261. 0: ; // Do nothing
  262. SOCKET_ERROR: begin
  263. LLastError := GWindowsStack.WSGetLastError;
  264. if LLastError <> WSA_IO_PENDING then begin
  265. GWindowsStack.RaiseSocketError(LLastError);
  266. end;
  267. end;
  268. else Assert(False, 'Unknown result code received from WSARecv'); {do not localize}
  269. end;
  270. end;
  271. { TIdWorkOpUnitRead }
  272. procedure TIdWorkOpUnitRead.Process(
  273. AOverlapped: PIdOverlapped;
  274. AByteCount: Integer
  275. );
  276. begin
  277. SetLength(FBytes, AByteCount);
  278. Processing(FBytes);
  279. end;
  280. procedure TIdWorkOpUnitRead.Read;
  281. var
  282. LBytesReceived: DWORD;
  283. LFlags: DWORD;
  284. LOverlapped: PIdOverlapped;
  285. LLastError: Integer;
  286. begin
  287. LFlags := 0;
  288. // Initialize byte array and pass it to overlapped
  289. SetLength(FBytes, WOPageSize);
  290. LOverlapped := GetOverlapped(@FBytes[0], Length(FBytes));
  291. //TODO: What is this 997? Need to check for it? If changed, do in Write too
  292. // GStack.CheckForSocketError( // can raise a 997
  293. case WSARecv(SocketHandle, LOverlapped.Buffer, 1, LBytesReceived, LFlags
  294. , LOverlapped, nil) of
  295. // , [997] );
  296. // Kudzu
  297. // In this case it completed immediately. The MS docs are not clear, but
  298. // testing shows that it still causes the completion port.
  299. 0: ; // Do nothing
  300. SOCKET_ERROR: begin
  301. LLastError := GWindowsStack.WSGetLastError;
  302. // If its WSA_IO_PENDING this is normal and its been queued
  303. if LLastError <> WSA_IO_PENDING then begin
  304. GWindowsStack.RaiseSocketError(LLastError);
  305. end;
  306. end;
  307. else Assert(False, 'Unknown result code received from WSARecv'); {do not localize}
  308. end;
  309. end;
  310. procedure TIdWorkOpUnitRead.Starting;
  311. begin
  312. Read;
  313. end;
  314. end.