home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 October / Chip_2000-10_cd1.bin / zkuste / Delphi / navody / multithread / mchpipesocket.pas < prev    next >
Pascal/Delphi Source File  |  1999-05-10  |  11KB  |  340 lines

  1. { 10-05-1999 10:36:51 PM > [martin on MARTIN] checked out /Reformatting
  2.    according to Delphi guidelines. }
  3. { 14-04-1999 11:59:10 PM > [martin on MARTIN] update: Changing dynamic
  4.    methods to virtual. (0.2) /  }
  5. { 14-04-1999 11:53:03 PM > [martin on MARTIN] checked out /Changing dynamic
  6.    methods to virtual. }
  7. { 06-04-1999 7:49:29 PM > [martin on MARTIN] checked out /Modifying Class
  8.    Names }
  9. unit MCHPipeSocket;
  10.  
  11. {$OVERFLOWCHECKS OFF}
  12.  
  13. {Martin Harvey 7/11/1998
  14.  
  15.   This unit does the required interfacing from the socket paradigm to the
  16.   pipe paradigm. It does the required interfacing between the pipe threads, which may
  17.   block, and the pipe transaction manager, which treats all events as aynchronous.
  18.  
  19.   Main points to consider are:
  20.  
  21.   DLL Loading will be handled by the session.
  22.  
  23.   We will regularly have to check the state of the reader and writer threads.
  24.   If either of them terminates, then we need to find out why, and signal that
  25.   as a disconnection event or error event.
  26.  
  27.   We also need to check for success during connection.
  28.  
  29.   Note that writer thread will not terminate unless it fails to write data.
  30.   Reader thread may terminate at any time after it has stopped waiting for the
  31.   peer to connect.
  32.  
  33.   The connection status variable tells us whether we
  34.   are disconnected, fully connected, or waiting for the peer.
  35.   If we are waiting for the peer, ONCP code should not attempt to send anything,
  36.   but signal an error.
  37.  
  38.   Although we signal a connection, this will probably not be used by the ONCP
  39.   Session. It will just check whether we are connected every time something has
  40.   to be sent, and signal an error if we aren't.
  41.  
  42. Design modification: 15/12/98
  43.  
  44.   An additional "issue" has cropped up. It is entirely possible for an immediate
  45.   reconnection attempt after a disconnection to mean that unhandled messages from
  46.   the previous connection get applied to the current connection. *NOT* what we want!
  47.  
  48.   We get around this by having a "Session Number" integer, which starts at 0,
  49.   and always increments. We only allow messages given by the current connection
  50.   any notice.
  51.  
  52.   It is not protected, because it's only read when the reader/writer threads
  53.   do call our callbacks, and written when the threads do not exist.
  54.  
  55. }
  56.  
  57. interface
  58.  
  59. uses Classes,MCHPipeThreads,MCHPipeTypes,MCHTransactions,
  60.   Messages,Windows,Controls;
  61.  
  62. const
  63.   WM_ASYNC_LAZY_READ = WM_USER + 2878;
  64.   WM_ASYNC_TERMINATE = WM_USER + 2879;
  65.   WM_ASYNC_CONNECT = WM_USER + 2880;
  66.  
  67. type
  68.   psServerType = (psServer,psClient,psPeer);
  69.  
  70.   TMCHPipeConnectionStatus = (pcsNotConnected,pcsConnecting,pcsConnected);
  71.  
  72.   TMCHPipeSocket = class(TComponent)
  73.   private
  74.     FSockHandle:TMCHHandle;
  75.     FHWnd:THandle;
  76.     FReaderThread:TMCHPipeReaderThread;
  77.     FWriterThread:TMCHPipeWriterThread;
  78.     FOnDisconnect,FOnConnect:TNotifyEvent;
  79.     FOnSockError:TNotifyEvent;
  80.     FOnRead:TNotifyEvent;
  81.     FConnected:TMCHPipeConnectionStatus;
  82.     FManager:TMCHCustomTransactionManager;
  83.     FServer:psServerType;
  84.     FSessionNumber:Word;
  85.   protected
  86.     procedure HandleDataRecieved(Sender:TObject); {Handle data, Called in separate thread}
  87.     procedure HandleTerminate(Sender:TObject); {Handle termination, Called in Separate Thread}
  88.     procedure HandleConnect(Sender:TObject); {Handle peer connection, Called in separate thread}
  89.     procedure MessageHandler(var Msg:TMessage); {Message handling loop for bridging thread gap}
  90.     procedure DoAsyncHandleTerminate(var Msg:TMessage); {asynchronous handler}
  91.     procedure DoAsyncHandleDataRecieved(var Msg:TMessage); {asynchronous handler}
  92.     procedure DoAsyncHandleConnect(var Msg:TMessage); {asynchronous handler}
  93.     procedure DoDisconnect;virtual; {Event trigger}
  94.     procedure DoSockError;virtual; {Event trigger}
  95.     procedure DoRead;virtual; {Event trigger}
  96.     procedure DoConnect;virtual; {Event trigger}
  97.     procedure StartThreads; {Sets up handles and resumes threads}
  98.   public
  99.     constructor Create(AOwner:TComponent);override;
  100.     destructor Destroy;override;
  101.     function Connect:boolean; {Signals whether connection successful pending remote connection}
  102.     procedure Disconnect; {Closes handles, and Frees threads}
  103.     function ReadData(Stream:TStream):integer; {Appends new data to stream. Returns how many bytes read}
  104.     procedure WriteData(Stream:TStream); {Writes stream data.}
  105.   published
  106.    {On Disconnect is to be treated by the higher layers like a dWinsock
  107.     disconnection was in the original DOP/ONCP stuff.}
  108.     property OnDisconnect:TNotifyEvent read FOnDisconnect write FOnDisconnect;
  109.    {OnPipeError will normally just be handled by the transaction manager,
  110.     which will signal OnFatalError, However, you can assign a handler if you
  111.     want.}
  112.     property OnSockError:TNotifyEvent read FOnSockError write FOnSockError;
  113.     property OnRead:TNotifyEvent read FOnRead write FOnRead;
  114.     property OnConnect:TNotifyEvent read FOnConnect write FOnConnect;
  115.     property Connected:TMCHPipeConnectionStatus read FConnected;
  116.     property Manager:TMCHCustomTransactionManager read FManager write FManager;
  117.     property Server:psServerType read FServer write FServer;
  118.   end;
  119.  
  120. implementation
  121.  
  122. uses MCHPipeInterface2,Forms,MCHPipeTransactions;
  123.  
  124. constructor TMCHPipeSocket.Create(AOwner:TComponent);
  125. begin
  126.   inherited Create(AOwner);
  127.   FHWnd := AllocateHWnd(MessageHandler);
  128.   FManager := TMCHPipeTransactionManager.Create;
  129.   (FManager as TMCHPipeTransactionManager).Socket := Self;
  130.   FSessionNumber := 0;
  131. end;
  132.  
  133. destructor TMCHPipeSocket.Destroy;
  134. begin
  135.   if Assigned(FManager) then
  136.   begin
  137.     FManager.Free;
  138.     FManager := nil;
  139.   end;
  140.   Disconnect;
  141.   DeallocateHWnd(FHWnd);
  142.   inherited Destroy;
  143. end;
  144.  
  145. procedure TMCHPipeSocket.HandleDataRecieved(Sender:TObject);
  146. begin
  147.   PostMessage(FHwnd,WM_ASYNC_LAZY_READ,FSessionNumber,0);
  148. end;
  149.  
  150. procedure TMCHPipeSocket.HandleTerminate(Sender:TObject);
  151. begin
  152.   PostMessage(FHwnd,WM_ASYNC_TERMINATE,FSessionNumber,Longint(Sender));
  153. end;
  154.  
  155. procedure TMCHPipeSocket.HandleConnect(Sender:TObject);
  156. begin
  157.   PostMessage(FHwnd,WM_ASYNC_CONNECT,FSessionNumber,0);
  158. end;
  159.  
  160. procedure TMCHPipeSocket.MessageHandler(var Msg:TMessage);
  161. begin
  162. {The session number check gets rid of a multitude of problems.}
  163. {In particular, it means that we only handle the first termination
  164.  message from the two threads... all later messages are discarded}
  165.   if Msg.WParam = FSessionNumber then
  166.   begin
  167.     case Msg.Msg of
  168.       WM_ASYNC_LAZY_READ:DoAsyncHandleDataRecieved(Msg);
  169.       WM_ASYNC_TERMINATE:DoAsyncHandleTerminate(Msg);
  170.       WM_ASYNC_CONNECT:DoAsyncHandleConnect(Msg);
  171.     end;
  172.   end;
  173. end;
  174.  
  175. procedure TMCHPipeSocket.DoAsyncHandleTerminate(var Msg:TMessage);
  176.  
  177. var
  178.   Sender:TObject;
  179.   Error:TMCHError;
  180.   OrigConnected:TMCHPipeConnectionStatus;
  181.  
  182. begin
  183.   Sender := TObject(Msg.LParam);
  184. {Find out termination reason}
  185.   Error := (Sender as TMCHPipeThread).TermReason;
  186. {Call disconnect, to disconnect everything & free both threads}
  187.   OrigConnected := FConnected;
  188.   Disconnect;
  189.   if not ((Error = meClientNotConnected) or (Error = meServerNotConnected)) then
  190.   begin
  191.     {Serious algorithm failure}
  192.     DoSockError;
  193.   end
  194.   else
  195.   begin
  196.     {Normal disconnection by peer}
  197.     {Don't signal this if it's as a result of us disconnecting}
  198.     if OrigConnected <> pcsNotConnected then DoDisconnect;
  199.   end;
  200. end;
  201.  
  202. procedure TMCHPipeSocket.DoAsyncHandleDataRecieved(var Msg:TMessage);
  203. begin
  204. {Check that we are connected and that we have a thread to read from!}
  205.   if (FConnected = pcsConnected) and Assigned(FReaderThread) then
  206.     DoRead;
  207. end;
  208.  
  209. procedure TMCHPipeSocket.DoAsyncHandleConnect(var Msg:TMessage);
  210. begin
  211.   if FConnected = pcsConnecting then
  212.   begin
  213.     FConnected := pcsConnected;
  214.     DoConnect;
  215.   end
  216.   else
  217.     {Serious algorithm failure}
  218.     DoSockError;
  219. end;
  220.  
  221. procedure TMCHPipeSocket.DoDisconnect;
  222. begin
  223.   (Manager as TMCHPipeTransactionManager).HandleDisconnect;
  224.   if Assigned(FOnDisconnect) then FOnDisconnect(Self);
  225. end;
  226.  
  227. procedure TMCHPipeSocket.DoConnect;
  228. begin
  229.   (Manager as TMCHPipeTransactionManager).HandleConnect;
  230.   if Assigned(FOnConnect) then FOnConnect(Self);
  231. end;
  232.  
  233. procedure TMCHPipeSocket.DoSockError;
  234. begin
  235.   (Manager as TMCHPipeTransactionManager).HandleSockError;
  236.   if Assigned(FOnSockError) then FOnSockError(Self);
  237. end;
  238.  
  239. procedure TMCHPipeSocket.DoRead;
  240. begin
  241.   (Manager as TMCHPipeTransactionManager).HandleSockRead;
  242.   if Assigned(FOnRead) then FOnRead(Self);
  243. end;
  244.  
  245. function TMCHPipeSocket.Connect:boolean;
  246. {Assumes DLL already loaded.}
  247. begin
  248.   if (FConnected = pcsNotConnected) then
  249.   begin
  250.     if Server = psServer then
  251.       result := MCHPipeInterface2.ConnectServer(FSockHandle) = meOK
  252.     else if Server = psClient then
  253.       result := MCHPipeInterface2.ConnectClient(FSockHandle) = meOK
  254.     else {Server=psPeer}
  255.     begin
  256.       result := MCHPipeInterface2.ConnectServer(FSockHandle) = meOK;
  257.       if (not result) then
  258.         result := MCHPipeInterface2.ConnectClient(FSockHandle) = meOK;
  259.     end;
  260.     if result then
  261.     begin
  262.       FConnected := pcsConnecting;
  263.       StartThreads;
  264.     end;
  265.   end
  266.   else
  267.     result := FConnected <> pcsNotConnected;
  268. end;
  269.  
  270. procedure TMCHPipeSocket.StartThreads;
  271. begin
  272. {Sets both reader and writer threads into a known state.
  273.  This state is where all info is flushed from buffers,
  274.  and they have just made their first read/write request
  275.  or are waiting for the peer. }
  276. {OVERFLOWCHECKS ARE OFF}
  277.   Inc(FSessionNumber);
  278. {Set session number to make socket ignore queued messages from all previous
  279.  connections}
  280.   FReaderThread := TMCHPipeReaderThread.Create(true);
  281.   FWriterThread := TMCHPipeWriterThread.Create(true);
  282.   FReaderThread.PipeReadHandle := FSockHandle;
  283.   FWriterThread.PipeWriteHandle := FSockHandle;
  284.   FReaderThread.OnDataRecieved := HandleDataRecieved;
  285.   FReaderThread.OnConnect := HandleConnect;
  286.   FReaderThread.Resume;
  287.   FReaderThread.OnTerminate := HandleTerminate;
  288.   FWriterThread.OnTerminate := HandleTerminate;
  289.   FWriterThread.Resume;
  290. end;
  291.  
  292. procedure TMCHPipeSocket.Disconnect;
  293. begin
  294. {Close handles}
  295.   if MCHPipeInterface2.DisconnectClient(FSockHandle) <> meOK then
  296.     MCHPipeInterface2.DisconnectServer(FSockHandle);
  297. {Free threads}
  298. {Reader thread is already unblocked}
  299. {Writer thread may only unblock when it's destructor is called}
  300.   if Assigned(FReaderThread) then
  301.   begin
  302.     with FReaderThread do
  303.     begin
  304.       Terminate; {Don't need to wait. Destructor calls WaitFor}
  305.       Free;
  306.     end;
  307.     FReaderThread := nil;
  308.   end;
  309.   if Assigned(FWriterThread) then
  310.   begin
  311.     with FWriterThread do
  312.     begin
  313.       Terminate; {Don't need to wait. Destructor calls WaitFor}
  314.       Free;
  315.     end;
  316.     FWriterThread := nil;
  317.   end;
  318.   FConnected := pcsNotConnected;
  319. {OVERFLOWCHECKS ARE OFF}
  320.   Inc(FSessionNumber);
  321. end;
  322.  
  323. function TMCHPipeSocket.ReadData(Stream:TStream):integer;
  324. begin
  325.   if FConnected = pcsConnected then {FReader should be assigned}
  326.     result := FReaderThread.ReadData(Stream)
  327.   else
  328.     result := 0;
  329. end;
  330.  
  331. procedure TMCHPipeSocket.WriteData(Stream:TStream);
  332. begin
  333.   if FConnected = pcsConnected then
  334.     FWriterThread.WriteData(Stream);
  335. end;
  336.  
  337.  
  338. end.
  339.  
  340.