home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 October / Chip_2000-10_cd1.bin / zkuste / Delphi / navody / multithread / MCHPipe.dpr < prev    next >
Text File  |  2000-06-10  |  25KB  |  814 lines

  1. { 6/10/00 11:34:50 PM > [martin on PERGOLESI] update:  (0.6) /  }
  2. { 27-07-1999 2:03:56 AM > [martin on MARTIN] update: Removing unneeded
  3.    "uses" (0.5) /  }
  4. { 10-05-1999 11:59:56 PM > [martin on MARTIN] update: Reformatting according
  5.    to Delphi guidelines. (0.4) /  }
  6. { 19-04-1999 8:19:01 PM > [martin on MARTIN] update: Inserting proper
  7.    constant for SEMAPHORE_ALL_ACCESS (0.3) /  }
  8. { 08-04-1999 11:29:11 PM > [martin on MARTIN] update: Removed debug checks
  9.    (0.2) /  }
  10. { 08-04-1999 11:10:15 PM > [martin on MARTIN] check in: (0.1) Initial
  11.    Version /  }
  12. library MCHPipe;
  13.  
  14. {Martin Harvey 23/8/98
  15.  A simple pipe library}
  16.  
  17. {Note to the casual reader: There are synchronisation subtleties here,
  18.  particularly with respect to blocking status variables. A good texbook on
  19.  operating system theory is a prerequisite.
  20.  
  21.  The good news is that this code displays a 4 fold symmetry:
  22.  
  23.  1. The treatment of clients and servers is identical
  24.     (with respect to handle checks). (2)
  25.  2. The treatment of reads and writes is identical
  26.     (with respect to blocking). (2*2=4)
  27.  
  28.  As a result, I've folded everything up into a set of "generic" procedures.
  29.  
  30.  The DLL currently only provides one bidirectional pipe. That's because it's all
  31.  I currently need.
  32.  
  33.  It is expected that a typical use of this DLL will result in
  34.  up to 6 threads being present in the DLL at any one time:
  35.  
  36.  1. Client reader and writer threads.
  37.  2. Server reader and writer threads.
  38.  3. Client and server set-up and tear-down threads.
  39.  
  40.  Note that pipes cannot be written to or read from unless both client and server
  41.  are connected.}
  42.  
  43. {Improvement MCH 7/11/1998.
  44.  In order to eliminate polling, a "WaitForPeer" function has been added.
  45.  This function enables a reader thread to wait for the corresponding writer to
  46.  connect, thus ensuring that the creation and startup of the reader thread does
  47.  not have to be polled}
  48.  
  49. uses
  50.   Windows,
  51.   MCHPipeTypes in 'MCHPipeTypes.pas';
  52.  
  53. const
  54.   BufSize = 4096;
  55.   MapName = 'MCHGlobalPipeMap';
  56.   GlobalLockName = 'MCHGlobalPipeLock';
  57.   Cli2ServLockName = 'MCHCli2ServLock';
  58.   Serv2CliLockName = 'MCHServ2CliLock';
  59.   ServPeerWaitSemName = 'MCHServPeerSem';
  60.   CliPeerWaitSemName = 'MCHCliPeerSem';
  61.   Cli2ServReaderSemName = 'MCHCli2ServReaderSem';
  62.   Cli2ServWriterSemName = 'MCHCli2ServWriterSem';
  63.   Serv2CliReaderSemName = 'MCHServ2CliReaderSem';
  64.   Serv2CliWriterSemName = 'MCHServ2CliWriterSem';
  65.   SEMAPHORE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or 3;
  66. { From SRC Modula-3 NT header files. The SEMAPHORE_ALL_ACCESS macro is missing
  67.  from windows.pas}
  68.  
  69. type
  70.   {Misc definitions}
  71.   PByte = ^Byte;
  72.  
  73. (******************************************************************************)
  74.  
  75.   {Definition of global data structures which are shared via a memory mapped file
  76.   without a disk image}
  77.   TCycBuf = array[0..BufSize - 1] of byte;
  78.   TDataBuf = record
  79.     ReadPtr: integer;
  80.     WritePtr: integer;
  81.     CycBuf: TCycBuf;
  82.   end;
  83.  
  84.   TPipe = record
  85.     Buf: TDataBuf;
  86.     ReaderBlocked, WriterBlocked: boolean; {bools to allow for checking before signalling}
  87.   end;
  88.   TBiDirPipe = record
  89.     Cli2ServPipe: TPipe;
  90.     Serv2CliPipe: TPipe;
  91.     ServConnected, CliConnected: boolean; {check connections}
  92.     ServPeerWait, CliPeerWait: boolean; {wait for peer variables}
  93.     ServHandle, CliHandle: TMCHHandle; {handles for reading/writing}
  94.   end;
  95.   PBiDirPipe = ^TBiDirPipe;
  96.  
  97. (******************************************************************************)
  98.  
  99.   {definition of local data structures, which consist of synchronisation
  100.    handles initialised by creating or getting handles to named objects}
  101.  
  102.   TPipeLocks = record
  103.     PipeLock: THandle; {data Mutex}
  104.     ReaderSem, WriterSem: THandle; {semaphores to block operations}
  105.   end;
  106.   PPipeLocks = ^TPipeLocks;
  107.   TBiDirPipeLocks = record
  108.     Cli2ServLocks: TPipeLocks;
  109.     Serv2CliLocks: TPipeLocks;
  110.     ServPeerWaitSem, CliPeerWaitSem: THandle; {wait for peer semaphores}
  111.     BiLock: THandle; {global Mutex}
  112.     MapHandle: THandle;
  113.   end;
  114.   PBiDirPipeLocks = ^TBiDirPipeLocks;
  115.  
  116. var
  117.   BiDirPipe: PBiDirPipe; {pointer to global shared memory}
  118.   BiDirPipeLocks: TBiDirPipeLocks; {local instance of nested record of synchronisation structures}
  119.  
  120. {Note on semaphore / mutex ordering:
  121.  
  122.  1. First global mutex must be aquired.
  123.  2. Then data mutex must be aquired
  124.  3. Releases must occur in the same order
  125.  4. One should not block on the semaphores when holding any mutexes.
  126.  
  127.  If these rules are not respected, deadlock will occur.
  128.  See a decent O/S theory textbook for more explanation of synchronisation primitives}
  129.  
  130.  
  131. {procedures and functions to do with maintaining the cyclic buffers}
  132.  
  133. procedure InitBuf(var DataBuf: TDataBuf);
  134. begin
  135.   with DataBuf do
  136.   begin
  137.     ReadPtr := 0;
  138.     WritePtr := 0;
  139.   end;
  140. end;
  141.  
  142. function EntriesUsed(var DataBuf: TDataBuf): integer;
  143. begin
  144.   with DataBuf do
  145.   begin
  146.     if WritePtr >= ReadPtr then
  147.       result := WritePtr - ReadPtr
  148.     else
  149.       result := BufSize - (ReadPtr - WritePtr);
  150.   end;
  151. end;
  152.  
  153. function EntriesFree(var DataBuf: TDataBuf): integer;
  154.  
  155. {Note that we have introduced an asymmetry here, to ensure
  156.  that we never completely fill up the buffer}
  157.  
  158. begin
  159.   with DataBuf do
  160.   begin
  161.     if WritePtr >= ReadPtr then
  162.       result := BufSize - (WritePtr - ReadPtr)
  163.     else
  164.       result := ReadPtr - WritePtr;
  165.   end;
  166.   dec(result);
  167. end;
  168.  
  169. procedure GetEntries(var DataBuf: TDataBuf; var Dest; Count: integer);
  170.  
  171. var
  172.   Write: PByte;
  173.   Iter: integer;
  174.  
  175. begin
  176.   Write := @Dest;
  177.   with DataBuf do
  178.   begin
  179.     if EntriesUsed(DataBuf) >= Count then
  180.     begin
  181.       for iter := 0 to Count - 1 do
  182.       begin
  183.         Write^ := CycBuf[ReadPtr];
  184.         ReadPtr := (ReadPtr + 1) mod BufSize;
  185.         Inc(Write);
  186.       end;
  187.     end;
  188.   end;
  189. end;
  190.  
  191. procedure AddEntries(var DataBuf: TDataBuf; var Src; Count: integer);
  192.  
  193. var
  194.   Read: PByte;
  195.   Iter: integer;
  196.  
  197. begin
  198.   Read := @Src;
  199.   with DataBuf do
  200.   begin
  201.     if EntriesFree(DataBuf) >= Count then
  202.     begin
  203.       for iter := 0 to Count - 1 do
  204.       begin
  205.         CycBuf[WritePtr] := Read^;
  206.         WritePtr := (WritePtr + 1) mod BufSize;
  207.         Inc(Read);
  208.       end;
  209.     end;
  210.   end;
  211. end;
  212.  
  213. {Connection and disconnection functions. Disconnection should always unblock
  214.  everything}
  215.  
  216. function GenericConnect(var hHandle: TMCHHandle; Server: boolean): TMCHError;
  217. {Returns error if server already connected}
  218. begin
  219.   result := meOK;
  220.   {Get the global mutex}
  221.   WaitForSingleObject(BiDirPipeLocks.BiLock, INFINITE);
  222.   if Server then
  223.   begin
  224.     if BiDirPipe.ServConnected then
  225.       result := meAlreadyConnected
  226.     else
  227.     begin
  228.       hHandle := BiDirPipe.ServHandle;
  229.       BiDirPipe.ServConnected := true;
  230.       {Now think about peer unblocking functions}
  231.       with BiDirPipe^ do
  232.       begin
  233.         if CliConnected and CliPeerWait then
  234.         begin
  235.           {Unblock client}
  236.           CliPeerWait := FALSE;
  237.           ReleaseSemaphore(BiDirPipeLocks.CliPeerWaitSem, 1, nil);
  238.         end;
  239.       end;
  240.     end;
  241.   end
  242.   else
  243.   begin
  244.     if BiDirPipe.CliConnected then
  245.       result := meAlreadyConnected
  246.     else
  247.     begin
  248.       hHandle := BiDirPipe.CliHandle;
  249.       BiDirPipe.CliConnected := true;
  250.       with BiDirPipe^ do
  251.       begin
  252.         if ServConnected and ServPeerWait then
  253.         begin
  254.           {Unblock server}
  255.           ServPeerWait := FALSE;
  256.           ReleaseSemaphore(BiDirPipeLocks.ServPeerWaitSem, 1, nil);
  257.         end;
  258.       end;
  259.     end;
  260.   end;
  261.   ReleaseMutex(BiDirPipeLocks.BiLock);
  262. end;
  263.  
  264. function GenericDisconnect(hHandle: TMCHHandle; Server: boolean): TMCHError;
  265. {Returns error if server not connected, or bad handle}
  266.  
  267. begin
  268.   result := meOK;
  269.   WaitForSingleObject(BiDirPipeLocks.BiLock, INFINITE);
  270.   {Now check handle}
  271.   if Server then
  272.   begin
  273.     if hHandle = BiDirPipe.ServHandle then
  274.       BiDirPipe.ServConnected := false
  275.     else
  276.       result := meServerNotConnected;
  277.   end
  278.   else
  279.   begin
  280.     if hHandle = BiDirPipe.CliHandle then
  281.       BiDirPipe.CliConnected := false
  282.     else
  283.       result := meClientNotConnected;
  284.   end;
  285.   {Now. If quit was successfull, then potentially have to unblock
  286.    all blocked reading and writing threads, so that they can
  287.    return error}
  288.   if Result = meOK then
  289.   begin
  290.     WaitForSingleObject(BiDirPipeLocks.Cli2ServLocks.PipeLock, INFINITE);
  291.     WaitForSingleObject(BiDirPipeLocks.Serv2CliLocks.PipeLock, INFINITE);
  292.     {Now unblock all potentially blocked threads reading/writing on the pipe}
  293.     with BiDirPipeLocks do
  294.     begin
  295.       with Cli2ServLocks do
  296.       begin
  297.         with BiDirPipe.Cli2ServPipe do
  298.         begin
  299.           if ReaderBlocked then
  300.           begin
  301.             ReaderBlocked := false;
  302.             ReleaseSemaphore(ReaderSem, 1, nil);
  303.           end;
  304.           if WriterBlocked then
  305.           begin
  306.             WriterBlocked := false;
  307.             ReleaseSemaphore(WriterSem, 1, nil);
  308.           end;
  309.         end;
  310.       end;
  311.       with Serv2CliLocks do
  312.       begin
  313.         with BiDirPipe.Serv2CliPipe do
  314.         begin
  315.           if ReaderBlocked then
  316.           begin
  317.             ReaderBlocked := false;
  318.             ReleaseSemaphore(ReaderSem, 1, nil);
  319.           end;
  320.           if WriterBlocked then
  321.           begin
  322.             WriterBlocked := false;
  323.             ReleaseSemaphore(WriterSem, 1, nil);
  324.           end;
  325.         end;
  326.       end;
  327.       {Now have to think about functions waiting for peer.}
  328.       {We basically have to unblock all threads blocked waiting for peer on our handle}
  329.       with BiDirPipe^ do
  330.       begin
  331.         if Server then
  332.         begin
  333.           if ServPeerWait then
  334.           begin
  335.             ServPeerWait := false;
  336.             ReleaseSemaphore(ServPeerWaitSem, 1, nil);
  337.           end;
  338.         end
  339.         else
  340.         begin
  341.           if CliPeerWait then
  342.           begin
  343.             CliPeerWait := false;
  344.             ReleaseSemaphore(CLiPeerWaitSem, 1, nil);
  345.           end;
  346.         end;
  347.       end;
  348.     end;
  349.   end;
  350.   {Release mutex before unblocking}
  351.   ReleaseMutex(BiDirPipeLocks.BiLock);
  352.   if Result = meOK then
  353.   begin
  354.     ReleaseMutex(BiDirPipeLocks.Cli2ServLocks.PipeLock);
  355.     ReleaseMutex(BiDirPipeLocks.Serv2CliLocks.PipeLock);
  356.   end;
  357. end;
  358.  
  359.  
  360. function ConnectServer(var hHandle: TMCHHandle): TMCHError stdcall;
  361. {Returns error if server already connected}
  362. begin
  363.   SetLastError(0);
  364.   result := GenericConnect(hHandle, true);
  365. end;
  366.  
  367. function ConnectClient(var hHandle: TMCHHandle): TMCHError stdcall;
  368. {Returns error if client already connected}
  369. begin
  370.   SetLastError(0);
  371.   result := GenericConnect(hHandle, false);
  372. end;
  373.  
  374. function DisconnectServer(hHandle: TMCHHandle): TMCHError stdcall;
  375. begin
  376.   SetLastError(0);
  377.   result := GenericDisconnect(hHandle, true);
  378. end;
  379.  
  380. function DisconnectClient(hHandle: TMCHHandle): TMCHError stdcall;
  381. begin
  382.   SetLastError(0);
  383.   result := GenericDisconnect(hHandle, false);
  384. end;
  385.  
  386. {Generic procedures to prevent duplicity}
  387.  
  388. {This function is *highly* cunning.
  389.  It simply wraps up both reading and writing by both client and server into one procedure.
  390.  
  391.  He that writeth less code, debuggeth less at the end of the day.}
  392.  
  393. function GenericReadWrite(var Buf; Count: integer; var SrcDestPipe: TPipe; var Locks: TPipeLocks; Read: boolean): TMCHError;
  394.  
  395. var
  396.   BlockSelf, UnblockPeer: boolean;
  397.   DoThisTime: integer;
  398.   SrcDestPtr: PByte;
  399.   Avail: integer;
  400.  
  401.  
  402. begin
  403.   {Game plan.
  404.   Check that neither client or server disconnected.
  405.   Read/Write as much as possible and block if required.
  406.   upon unblock, recheck connection status before proceeding.
  407.   Once any data has been read/written, unblock the peer on the buffer.
  408.   Nested mutex aquisition also required here. Respect ordering.}
  409.   result := meOK;
  410.   SrcDestPtr := @Buf;
  411.   repeat
  412.     {connection data critical section}
  413.     WaitForSingleObject(BiDirPipeLocks.BiLock, INFINITE);
  414.     WaitForSingleObject(Locks.PipeLock, INFINITE);
  415.     {Now check connection status}
  416.     if not BiDirPipe.ServConnected then result := meServerNotConnected;
  417.     if not BiDirPipe.CliConnected then result := meClientNotConnected;
  418.     if result <> meOK then
  419.     begin
  420.       {bomb out if not all connected}
  421.       ReleaseMutex(BiDirPipeLocks.BiLock);
  422.       ReleaseMutex(Locks.PipeLock);
  423.       Exit;
  424.     end;
  425.     {So far, it's okay to read/write}
  426.     {Read/write as much as we can this time.}
  427.     if Read then
  428.       Avail := EntriesUsed(SrcDestPipe.Buf)
  429.     else
  430.       Avail := EntriesFree(SrcDestPipe.Buf);
  431.     if Count > Avail then
  432.     begin
  433.       DoThisTime := Avail;
  434.       BlockSelf := true;
  435.     end
  436.     else
  437.     begin
  438.       DoThisTime := Count;
  439.       BlockSelf := false;
  440.     end;
  441.     {work out whether to unblock any peer threads blocked on the converse
  442.      read/write. Local vars are used so we can perform blocking / unblocking
  443.      actions without holding any mutexes}
  444.     if Read then
  445.       UnblockPeer := (DoThisTime > 0) and SrcDestPipe.WriterBlocked
  446.     else
  447.       UnblockPeer := (DoThisTime > 0) and SrcDestPipe.ReaderBlocked;
  448.     {Now do the read/write}
  449.     if Read then
  450.       GetEntries(SrcDestPipe.Buf, SrcDestPtr^, DoThisTime)
  451.     else
  452.       AddEntries(SrcDestPipe.Buf, SrcDestPtr^, DoThisTime);
  453.     {update local vars}
  454.     Count := Count - DoThisTime;
  455.     Inc(SrcDestPtr, DoThisTime);
  456.     {update blocking status variables}
  457.     if Read then
  458.     begin
  459.       SrcDestPipe.WriterBlocked := SrcDestPipe.WriterBlocked and (not UnblockPeer);
  460.       SrcDestPipe.ReaderBlocked := BlockSelf; {it is evident that we currently aren't blocked!}
  461.     end
  462.     else
  463.     begin
  464.       SrcDestPipe.ReaderBlocked := SrcDestPipe.ReaderBlocked and (not UnblockPeer);
  465.       SrcDestPipe.WriterBlocked := BlockSelf; {it is evident that we currently aren't blocked!}
  466.     end;
  467.     {Release data mutexes and perform blocking / unblocking actions}
  468.     ReleaseMutex(BiDirPipeLocks.BiLock);
  469.     ReleaseMutex(Locks.PipeLock);
  470.     if Read then
  471.     begin
  472.       if UnblockPeer then
  473.         ReleaseSemaphore(Locks.WriterSem, 1, nil);
  474.       if BlockSelf then
  475.         WaitForSingleObject(Locks.ReaderSem, INFINITE);
  476.     end
  477.     else
  478.     begin
  479.       if UnblockPeer then
  480.         ReleaseSemaphore(Locks.ReaderSem, 1, nil);
  481.       if BlockSelf then
  482.         WaitForSingleObject(Locks.WriterSem, INFINITE);
  483.     end;
  484.     {All done. If not complete, connection status will be rechecked next iteration}
  485.   until count = 0;
  486. end;
  487.  
  488.  
  489. function GenericPeek(var BytesReady: integer; var SrcPipe: TPipe; var Locks: TPipeLocks): TMCHError;
  490. begin
  491. {Nonblocking peek. Fails if not both server and client connected}
  492.   WaitForSingleObject(BiDirPipeLocks.BiLock, INFINITE);
  493.   WaitForSingleObject(Locks.PipeLock, INFINITE);
  494.   if BiDirPipe.CliConnected then
  495.   begin
  496.     if BiDirPipe.ServConnected then
  497.       result := meOK
  498.     else
  499.       result := meServerNotConnected;
  500.   end
  501.   else
  502.     result := meClientNotConnected;
  503.   if result = meOK then BytesReady := EntriesUsed(SrcPipe.Buf);
  504. {Now release in the same order that we aquired}
  505.   ReleaseMutex(BiDirPipeLocks.BiLock);
  506.   ReleaseMutex(Locks.PipeLock);
  507. end;
  508.  
  509.  
  510. function ReadWriteData(hHandle: TMCHHandle; var Buf; Count: integer; Read: boolean): TMCHError;
  511. {Returns error if client or server not connected (or disconnects during block)
  512.  Blocks if buffer empty}
  513. begin
  514.   WaitForSingleObject(BiDirPipeLocks.BiLock, INFINITE);
  515.   if hHandle = BiDirPipe.ServHandle then
  516.   begin
  517.     if BiDirPipe.ServConnected then
  518.     begin
  519.       ReleaseMutex(BiDirPipeLocks.BiLock);
  520.       if Read then
  521.         {Server is reading, so read from Cli2Serv buffer}
  522.         result := GenericReadWrite(Buf, Count, BiDirPipe.Cli2ServPipe, BiDirPipeLocks.Cli2ServLocks, Read)
  523.       else
  524.         {Server is writing so write to Serv2Cli buffer}
  525.         result := GenericReadWrite(Buf, Count, BiDirPipe.Serv2CliPipe, BiDirPipeLocks.Serv2CliLocks, Read);
  526.     end
  527.     else
  528.     begin
  529.       ReleaseMutex(BiDirPipeLocks.BiLock);
  530.       result := meServerNotConnected;
  531.     end;
  532.   end
  533.   else
  534.   begin
  535.     if hHandle = BiDirPipe.CliHandle then
  536.     begin
  537.       if BiDirPipe.CliConnected then
  538.       begin
  539.         ReleaseMutex(BiDirPipeLocks.BiLock);
  540.         if Read then
  541.           {Client is reading, so read from Serv2Cli buffer}
  542.           result := GenericReadWrite(Buf, Count, BiDirPipe.Serv2CliPipe, BiDirPipeLocks.Serv2CliLocks, Read)
  543.         else
  544.           {Client is writing, so write from Cli2Serv buffer}
  545.           result := GenericReadWrite(Buf, Count, BiDirPipe.Cli2ServPipe, BiDirPipeLocks.Cli2ServLocks, Read);
  546.       end
  547.       else
  548.       begin
  549.         ReleaseMutex(BiDirPipeLocks.BiLock);
  550.         result := meClientNotConnected;
  551.       end;
  552.     end
  553.     else
  554.     begin
  555.       ReleaseMutex(BiDirPipeLocks.BiLock);
  556.       result := meBadHandle;
  557.     end;
  558.   end;
  559. end;
  560.  
  561. {Publicly accesible read, write and peek procedures}
  562.  
  563. function WriteData(hHandle: TMCHHandle; var Buf; Count: integer): TMCHError stdcall;
  564. begin
  565.   SetLastError(0);
  566.   result := ReadWriteData(hHandle, Buf, Count, false);
  567. end;
  568.  
  569. function ReadData(hHandle: TMCHHandle; var Buf; Count: integer): TMCHError stdcall;
  570. begin
  571.   SetLastError(0);
  572.   result := ReadWriteData(hHandle, Buf, Count, true);
  573. end;
  574.  
  575. function PeekData(hHandle: TMCHHandle; var BytesReady: integer): TMCHError stdcall;
  576. {Returns error if client or server not connected, never blocks}
  577. begin
  578.   SetLastError(0);
  579.   WaitForSingleObject(BiDirPipeLocks.BiLock, INFINITE);
  580.   if hHandle = BiDirPipe.ServHandle then
  581.   begin
  582.     if BiDirPipe.ServConnected then
  583.     begin
  584.       ReleaseMutex(BiDirPipeLocks.BiLock);
  585.       {Server is peeking, so peek Cli2Srv buffer}
  586.       result := GenericPeek(BytesReady, BiDirPipe.Cli2ServPipe, BiDirPipeLocks.Cli2ServLocks);
  587.     end
  588.     else
  589.     begin
  590.       ReleaseMutex(BiDirPipeLocks.BiLock);
  591.       result := meServerNotConnected;
  592.     end;
  593.   end
  594.   else
  595.   begin
  596.     if hHandle = BiDirPipe.CliHandle then
  597.     begin
  598.       if BiDirPipe.CliConnected then
  599.       begin
  600.         ReleaseMutex(BiDirPipeLocks.BiLock);
  601.         {Client is peeking, so peek Serv2Cli buffer}
  602.         result := GenericPeek(BytesReady, BiDirPipe.Serv2CliPipe, BiDirPipeLocks.Serv2CliLocks);
  603.       end
  604.       else
  605.       begin
  606.         ReleaseMutex(BiDirPipeLocks.BiLock);
  607.         result := meClientNotConnected;
  608.       end;
  609.     end
  610.     else
  611.     begin
  612.       ReleaseMutex(BiDirPipeLocks.BiLock);
  613.       result := meBadHandle;
  614.     end;
  615.   end;
  616. end;
  617.  
  618. {Wait for peer blocks if self connected and peer not. Returns Okay if both
  619.  connected. Returns error if the state at unblock time is anything other
  620.  than both connected}
  621.  
  622.  {Compiler is a little bit stupid here, and tells me I might have some uninitialised vars.
  623.   What garbage!}
  624.  
  625. {$WARNINGS OFF}
  626.  
  627. function WaitForPeer(hHandle: TMCHHandle): TMCHError; stdcall;
  628.  
  629. {Note: Only one thread can wait for a peer at any one time.}
  630.  
  631. var
  632.   Server, Block: boolean;
  633.  
  634. begin
  635. {Hmmm....
  636.  Game plan.
  637.       1. Get data lock.
  638.       2. Check handles. Determine whether client or server. If error, release lock and quit.
  639.       3. Read connection vars. If self connected and peer disconnected, set var + block (outside crit!)
  640.       4. Upon unblock (or general passthru) determine retcode. If both connected, OK else return appropriate err.
  641.  }
  642.   SetLastError(0);
  643.   WaitForSingleObject(BiDirPipeLocks.BiLock, INFINITE);
  644.  
  645.   {Check handles}
  646.   result := meOK;
  647.   if hHandle = BiDirPipe.ServHandle then
  648.     Server := true
  649.   else if hHandle = BiDirPipe.CliHandle then
  650.     Server := false
  651.   else result := meBadHandle;
  652.   if Result = meOK then
  653.   begin
  654.     with BiDirPipe^ do
  655.     begin
  656.       if Server then
  657.       begin
  658.         Block := ServConnected and not CliConnected;
  659.         if Block then ServPeerWait := true;
  660.       end
  661.       else
  662.       begin
  663.         Block := CliConnected and not ServConnected;
  664.         if Block then CliPeerWait := true;
  665.       end;
  666.     end;
  667.   end;
  668.   ReleaseMutex(BiDirPipeLocks.BiLock);
  669.   if Result = meOK then
  670.   begin
  671.     if Block then
  672.     begin
  673.       if Server then
  674.         WaitForSingleObject(BiDirPipeLocks.ServPeerWaitSem, INFINITE)
  675.       else
  676.         WaitForSingleObject(BiDirPipeLocks.CliPeerWaitSem, INFINITE);
  677.     end;
  678.     {Regardless of whether we have blocked or not, reaquire global mutex and calculate ret code}
  679.     WaitForSingleObject(BiDirPipeLocks.BiLock, INFINITE);
  680.     if BiDirPipe.CliConnected then
  681.     begin
  682.       if BiDirPipe.ServConnected then
  683.         result := meOK
  684.       else
  685.         result := meServerNotConnected;
  686.     end
  687.     else
  688.       result := meClientNotConnected;
  689.     ReleaseMutex(BiDirPipeLocks.BiLock);
  690.   end;
  691. end;
  692.  
  693. {$WARNINGS ON}
  694.  
  695. {
  696.  If we are the process that managed to create the memory mapped file
  697. (rather than opening it), then aquire global and pipe mutexes,
  698. and init the data.
  699.  
  700. This will prevent multiple initialisations from conflicting.
  701. }
  702.  
  703. procedure Initialise()stdcall;
  704.  
  705. var
  706.   SharedMapCreator: boolean;
  707.  
  708. begin
  709.   SetLastError(0);
  710.   {Create mapping should always succeed}
  711.   {IPC Shared memory creation}
  712.   BiDirPipeLocks.MapHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, SizeOf(TBiDirPipe), MapName);
  713.   SharedMapCreator := not (GetLastError = ERROR_ALREADY_EXISTS);
  714.   {Now set up the file mapping...}
  715.   BiDirPipe := MapViewOfFile(BiDirPipeLocks.MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TBiDirPipe));
  716.   {Now create synchronisation objects Open all objects without requesting initial ownership}
  717.   with BiDirPipeLocks do
  718.   begin
  719.     BiLock := CreateMutex(nil, false, GlobalLockName); {should always return valid handle}
  720.     CliPeerWaitSem := CreateSemaphore(nil, 0, High(Integer), CliPeerWaitSemName);
  721.     ServPeerWaitSem := CreateSemaphore(nil, 0, High(integer), ServPeerWaitSemName);
  722.     {Now deal with individual pipe locks}
  723.     with Cli2ServLocks do
  724.     begin
  725.       PipeLock := CreateMutex(nil, false, Cli2ServLockName); {should always return valid handle}
  726.       ReaderSem := CreateSemaphore(nil, 0, High(integer), Cli2ServReaderSemName);
  727.       WriterSem := CreateSemaphore(nil, 0, High(integer), Cli2ServWriterSemName);
  728.     end;
  729.     with Serv2CliLocks do
  730.     begin
  731.       PipeLock := CreateMutex(nil, false, Serv2CliLockName);
  732.       ReaderSem := CreateSemaphore(nil, 0, High(integer), Serv2CliReaderSemName);
  733.       WriterSem := CreateSemaphore(nil, 0, High(integer), Serv2CliWriterSemName);
  734.     end;
  735.   end;
  736.   {Okay. Now if we created the memory map, initialise it. Respect mutex ordering}
  737.   if SharedMapCreator then
  738.   begin
  739.     WaitForSingleObject(BiDirPipeLocks.BiLock, INFINITE);
  740.     WaitForSingleObject(BiDirPipeLocks.Cli2ServLocks.PipeLock, INFINITE);
  741.     WaitForSingleObject(BiDirPipeLocks.Serv2CliLocks.PipeLock, INFINITE);
  742.     {Now initialise data structures}
  743.     Randomize;
  744.     with BiDirPipe^ do
  745.     begin
  746.       ServConnected := false;
  747.       CliConnected := false;
  748.       ServPeerWait := false;
  749.       CliPeerWait := false;
  750.       ServHandle := Random(High(integer) - 1);
  751.       CliHandle := ServHandle + 1;
  752.       with Cli2ServPipe do
  753.       begin
  754.         ReaderBlocked := false;
  755.         WriterBlocked := false;
  756.         InitBuf(Buf);
  757.       end;
  758.       with Serv2CliPipe do
  759.       begin
  760.         ReaderBlocked := false;
  761.         WriterBlocked := false;
  762.         InitBuf(Buf);
  763.       end;
  764.     end;
  765.     {Now release locks in the order we aquired them}
  766.     ReleaseMutex(BiDirPipeLocks.BiLock);
  767.     ReleaseMutex(BiDirPipeLocks.Cli2ServLocks.PipeLock);
  768.     ReleaseMutex(BiDirPipeLocks.Serv2CLiLocks.PipeLock);
  769.   end;
  770.   {Finished!}
  771. end;
  772.  
  773. procedure Finalise()stdcall;
  774. begin
  775.   SetLastError(0);
  776.   {Close just about every handle we have}
  777.   UnMapViewofFile(BiDirPipe);
  778.   with BiDirPipeLocks do
  779.   begin
  780.     CloseHandle(BiLock);
  781.     CloseHandle(ServPeerWaitSem);
  782.     CloseHandle(CliPeerWaitSem);
  783.     CloseHandle(MapHandle);
  784.     with Cli2ServLocks do
  785.     begin
  786.       CloseHandle(PipeLock);
  787.       CloseHandle(ReaderSem);
  788.       CloseHandle(WriterSem);
  789.     end;
  790.     with Serv2CliLocks do
  791.     begin
  792.       CloseHandle(PipeLock);
  793.       CloseHandle(ReaderSem);
  794.       CloseHandle(WriterSem);
  795.     end;
  796.   end;
  797. end;
  798.  
  799. exports
  800.   ConnectServer,
  801.   ConnectClient,
  802.   WriteData,
  803.   ReadData,
  804.   PeekData,
  805.   WaitForPeer,
  806.   DisconnectServer,
  807.   DisconnectClient,
  808.   Initialise,
  809.   Finalise;
  810.  
  811. begin
  812. end.
  813.  
  814.