IdWorkOpUnit.pas 8.4 KB

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