Advertisement
Guest User

Untitled

a guest
Mar 22nd, 2018
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.53 KB | None | 0 0
  1. #pragma once
  2.  
  3. #include <algorithm>
  4. #include <cassert>
  5. #include <mutex>
  6. #include <stdlib.h>
  7. #include <thread>
  8.  
  9. #include <Windows.h>
  10.  
  11. #include "../Shared/Defer.h"
  12.  
  13. #ifdef max
  14. #undef max
  15. #endif
  16.  
  17. struct IPC_Packet
  18. {
  19.     DWORD to; // recipient
  20.     DWORD from; // sender
  21.     DWORD id; // the id of this packet
  22.     DWORD size; // size of data
  23.     BYTE  data[ 0 ]; // actual data
  24. };
  25.  
  26. inline DWORD SizeOfIpcPacket( IPC_Packet *data )
  27. {
  28.     return data->size + sizeof( IPC_Packet );
  29. }
  30.  
  31. template <typename T>
  32. T *TrailingAllocate( DWORD extraSize )
  33. {
  34.     return (T *)( new BYTE[ sizeof( T ) + extraSize ] );
  35. }
  36.  
  37. // TODO: why is this not in the IPC_Peer class?
  38. struct IPC_Header
  39. {
  40.     HANDLE mutex;
  41.     DWORD  lastClient;
  42.  
  43.     // client --> server packets
  44.     DWORD incomingSize;
  45.  
  46.     // server --> client packets
  47.     DWORD outgoingSize;
  48.  
  49.     DWORD lastId;
  50. };
  51.  
  52. #define IPC_HEADER_SIZE ( sizeof( IPC_Header ) )
  53.  
  54. // TODO: list
  55. // - add an internal method where packets are ACKed when recieved (could only be compiled on debug builds?)
  56. //      - packets shouldnt be dropped but if they are then the user should know about it
  57. // - should the incoming & outgoing buffer sizes be the same??
  58. // - linux implementation
  59. // - track and cleanup packets that havent been collected
  60.  
  61. enum class IPC_Type
  62. {
  63.     Server,
  64.     Client
  65. };
  66.  
  67. template <IPC_Type Type>
  68. class IPC_Peer
  69. {
  70.     // integral peer data
  71.     IPC_Header *header; // represents the entire buffer
  72.     PVOID       incoming; // recieved
  73.     PVOID       outgoing; // sent
  74.     DWORD       totalSize;
  75.  
  76.     HANDLE fileHandle;
  77.     bool   valid = false;
  78.  
  79.     // the id of this peer
  80.     DWORD peerId;
  81.     char  objName[ MAX_PATH ];
  82.  
  83.     std::function<void( IPC_Packet * )> callback;
  84.  
  85.     // debug function
  86.     void Warning( const char *fmt, ... )
  87.     {
  88.         va_list vlist;
  89.  
  90.         char buf[ 1024 ];
  91.  
  92.         va_start( vlist, fmt );
  93.         vsnprintf( buf, 1024, fmt, vlist );
  94.         va_end( vlist );
  95.  
  96.         OutputDebugStringA( buf );
  97.         printf( buf );
  98.     }
  99.  
  100. public:
  101.     IPC_Peer( const char *objectName, DWORD incomingSize, DWORD outgoingSize, std::function<void( IPC_Packet * )> callback );
  102.  
  103.     ~IPC_Peer()
  104.     {
  105.         if ( Type == IPC_Type::Client )
  106.         {
  107.         }
  108.  
  109.         // This is so that we dont cause a deadlock
  110.         if ( hasMutex == true )
  111.             ReleaseMutex();
  112.  
  113.         if ( valid )
  114.         {
  115.             UnmapViewOfFile( header );
  116.  
  117.             CloseHandle( fileHandle );
  118.         }
  119.     }
  120.  
  121.     bool IsValid()
  122.     {
  123.         return valid;
  124.     }
  125.  
  126.     void ProcessIncoming()
  127.     {
  128.         assert( valid );
  129.  
  130.         ObtainMutex();
  131.         Defer( ReleaseMutex() );
  132.  
  133.         DWORD unprocessedOffset = 0;
  134.         BYTE *unprocessed       = new BYTE[ header->incomingSize ];
  135.         Defer( delete[] unprocessed );
  136.  
  137.         memset( unprocessed, 0, header->incomingSize );
  138.  
  139.         DWORD offset = 0;
  140.         while ( true )
  141.         {
  142.             IPC_Packet *dat = (IPC_Packet *)( (DWORD)incoming + offset );
  143.  
  144.             if ( dat->size == 0 )
  145.                 break; // end of data
  146.  
  147.             DWORD packetSize = SizeOfIpcPacket( dat );
  148.             offset += packetSize;
  149.  
  150.             if ( peerId == dat->to )
  151.             {
  152.  
  153.                 if ( Type == IPC_Type::Client )
  154.                     Warning( "Client: Recieve %d --> %d (%d)\n", dat->from, dat->to, dat->id );
  155.                 else if ( Type == IPC_Type::Server )
  156.                     Warning( "Server: Recieve %d --> %d (%d)\n", dat->from, dat->to, dat->id );
  157.  
  158.                 callback( dat );
  159.             }
  160.             else
  161.             {
  162.                 // write to unprocessed buffer
  163.  
  164.                 if ( unprocessedOffset + packetSize > header->incomingSize )
  165.                 {
  166.                     Warning( "Unprocessed Buffer Overflow\n\nPURGING!!!!\n\n" );
  167.                     memset( unprocessed, 0, header->incomingSize );
  168.                     break;
  169.                 }
  170.  
  171.                 memcpy( unprocessed + unprocessedOffset, dat, packetSize );
  172.                 unprocessedOffset += packetSize;
  173.             }
  174.         }
  175.  
  176.         // all incoming packets for us have been dealt with
  177.         // copy unprocessed packets back into buffer
  178.         memcpy( incoming, unprocessed, header->incomingSize );
  179.     }
  180.  
  181.     void SendPacket( IPC_Packet *p )
  182.     {
  183.         assert( valid );
  184.  
  185.         ObtainMutex();
  186.         Defer( ReleaseMutex() );
  187.  
  188.         header->lastId += 1;
  189.  
  190.         p->id = header->lastId;
  191.  
  192.         if ( Type == IPC_Type::Client )
  193.             Warning( "Client: Send %d --> %d (%d)\n", p->from, p->to, p->id );
  194.         else if ( Type == IPC_Type::Server )
  195.             Warning( "Server: Send %d --> %d (%d)\n", p->from, p->to, p->id );
  196.  
  197.         // get the last outgoing packet
  198.         DWORD       offset = 0;
  199.         IPC_Packet *dat    = (IPC_Packet *)outgoing;
  200.         while ( true )
  201.         {
  202.             if ( dat->size == 0 )
  203.                 break; // end of data
  204.  
  205.             offset += SizeOfIpcPacket( dat );
  206.             dat = (IPC_Packet *)( (DWORD)outgoing + offset );
  207.         }
  208.  
  209.         // if this goes off then you overflowed the buffer
  210.         assert( ( offset + p->size ) < header->outgoingSize );
  211.  
  212.         // fill out packet
  213.         memcpy( dat, p, SizeOfIpcPacket( p ) );
  214.     }
  215.  
  216.     // Create and send a packet
  217.     template <typename T>
  218.     void CreateAndSendPacket( T *data );
  219.  
  220.     template <typename T>
  221.     void CreateAndSendPacketTo( T *data, DWORD target );
  222.  
  223.     void SetCallback( std::function<void( IPC_Packet * )> c )
  224.     {
  225.         callback = c;
  226.     }
  227.  
  228.     // TODO: remove
  229.     static void AllocConsole()
  230.     {
  231.         ::AllocConsole();
  232.         freopen( "CONIN$", "r", stdin );
  233.         freopen( "CONOUT$", "w", stdout );
  234.         freopen( "CONOUT$", "w", stderr );
  235.     }
  236.  
  237. private:
  238.     bool hasMutex = false;
  239.     void ObtainMutex()
  240.     {
  241.         // we alrady have the mutex so dont wait for it (which would result in deadlock)
  242.         if ( hasMutex == true )
  243.             return;
  244.  
  245.         while ( WaitForSingleObject( header->mutex, 2 * 1000 ) == WAIT_TIMEOUT )
  246.         {
  247.             Warning( "Timeout waiting for mutex '%s'\n", objName );
  248.         }
  249.         hasMutex = true;
  250.     }
  251.  
  252. public:
  253.     void ReleaseMutex()
  254.     {
  255.         hasMutex = false;
  256.         if (::ReleaseMutex( header->mutex ) == 0 )
  257.         {
  258.             Warning( "Error releasing mutex: %d\n", GetLastError() );
  259.         }
  260.     }
  261. };
  262.  
  263. template <>
  264. inline IPC_Peer<IPC_Type::Server>::IPC_Peer( const char *objectName, DWORD incomingSize, DWORD outgoingSize, std::function<void( IPC_Packet * )> callback )
  265.     : totalSize( incomingSize + outgoingSize + IPC_HEADER_SIZE ), callback( callback )
  266. {
  267.     sprintf( objName, "Local\\%s", objectName );
  268.  
  269.     fileHandle = CreateFileMappingA(
  270.         INVALID_HANDLE_VALUE, // use paging file
  271.         NULL, // default security
  272.         PAGE_READWRITE, // read/write access
  273.         0, // maximum object size (high-order DWORD)
  274.         totalSize + 50, // maximum object size (low-order DWORD)
  275.         objName ); // name of mapping object
  276.  
  277.     if ( fileHandle == NULL || fileHandle == INVALID_HANDLE_VALUE )
  278.     {
  279.         this->Warning( "Could not create file mapping object (%d).\n",
  280.                        GetLastError() );
  281.         return;
  282.     }
  283.  
  284.     header = (IPC_Header *)MapViewOfFile( fileHandle, FILE_MAP_ALL_ACCESS, 0, 0, totalSize );
  285.  
  286.     if ( header == NULL )
  287.     {
  288.         this->Warning( "Could not map header (%d).\n", GetLastError() );
  289.         CloseHandle( fileHandle );
  290.         return;
  291.     }
  292.  
  293.     incoming = ( (BYTE *)header ) + IPC_HEADER_SIZE;
  294.  
  295.     if ( incoming == NULL )
  296.     {
  297.         this->Warning( "Could not map incoming (%d).\n", GetLastError() );
  298.         CloseHandle( fileHandle );
  299.         return;
  300.     }
  301.  
  302.     outgoing = ( (BYTE *)header ) + IPC_HEADER_SIZE + incomingSize;
  303.  
  304.     if ( outgoing == NULL )
  305.     {
  306.         this->Warning( "Could not map outgoing (%d).\n", GetLastError() );
  307.         CloseHandle( fileHandle );
  308.  
  309.         return;
  310.     }
  311.  
  312.     peerId = 0;
  313.  
  314.     // clean out buffers
  315.     memset( incoming, 0, incomingSize );
  316.     memset( outgoing, 0, outgoingSize );
  317.  
  318.     header->mutex        = CreateMutexA( NULL, FALSE, NULL );
  319.     header->lastClient   = 1;
  320.     header->incomingSize = incomingSize;
  321.     header->outgoingSize = outgoingSize;
  322.  
  323.     valid = true;
  324. }
  325.  
  326. template <>
  327. inline IPC_Peer<IPC_Type::Client>::IPC_Peer( const char *objectName, DWORD incomingSize, DWORD outgoingSize, std::function<void( IPC_Packet * )> callback )
  328.     : totalSize( incomingSize + outgoingSize + IPC_HEADER_SIZE ), callback( callback )
  329. {
  330.     sprintf( objName, "Local\\%s", objectName );
  331.  
  332.     fileHandle = OpenFileMappingA(
  333.         FILE_MAP_ALL_ACCESS, // read/write access
  334.         FALSE, // do not inherit the name
  335.         objName ); // name of mapping object
  336.  
  337.     if ( fileHandle == NULL || fileHandle == INVALID_HANDLE_VALUE )
  338.     {
  339.         this->Warning( "Could not create file mapping object (%d).\n",
  340.                        GetLastError() );
  341.         return;
  342.     }
  343.  
  344.     header = (IPC_Header *)MapViewOfFile( fileHandle, FILE_MAP_ALL_ACCESS, 0, 0, totalSize );
  345.  
  346.     if ( header == NULL )
  347.     {
  348.         this->Warning( "Could not map header (%d).\n", GetLastError() );
  349.         CloseHandle( fileHandle );
  350.         return;
  351.     }
  352.  
  353.     // in the client the incoming and outgoing buffers are flipped
  354.  
  355.     incoming = ( (BYTE *)header ) + IPC_HEADER_SIZE + incomingSize;
  356.  
  357.     if ( incoming == NULL )
  358.     {
  359.         this->Warning( "Could not map incoming (%d).\n", GetLastError() );
  360.         CloseHandle( fileHandle );
  361.         return;
  362.     }
  363.  
  364.     outgoing = ( (BYTE *)header ) + IPC_HEADER_SIZE;
  365.  
  366.     if ( outgoing == NULL )
  367.     {
  368.         this->Warning( "Could not map outgoing (%d).\n", GetLastError() );
  369.         CloseHandle( fileHandle );
  370.  
  371.         return;
  372.     }
  373.  
  374.     ObtainMutex();
  375.     {
  376.         peerId = header->lastClient;
  377.         header->lastClient += 1;
  378.     }
  379.     ReleaseMutex();
  380.  
  381.     valid = true;
  382. }
  383.  
  384. // Create and send a packet
  385. template <>
  386. template <typename T>
  387. inline void IPC_Peer<IPC_Type::Client>::CreateAndSendPacket( T *data )
  388. {
  389.     assert( valid );
  390.  
  391.     IPC_Packet *p = TrailingAllocate<IPC_Packet>( sizeof( T ) );
  392.     Defer( delete[] p );
  393.  
  394.     p->from = peerId;
  395.     p->to   = 0;
  396.     p->size = sizeof( T );
  397.  
  398.     memcpy( p->data, data, sizeof( T ) );
  399.  
  400.     SendPacket( p );
  401. }
  402.  
  403. template <>
  404. template <typename T>
  405. inline void IPC_Peer<IPC_Type::Server>::CreateAndSendPacketTo( T *data, DWORD target )
  406. {
  407.     assert( valid );
  408.  
  409.     IPC_Packet *p = TrailingAllocate<IPC_Packet>( sizeof( T ) );
  410.     Defer( delete[] p );
  411.  
  412.     p->from = peerId;
  413.     p->to   = target;
  414.     p->size = sizeof( T );
  415.  
  416.     memcpy( p->data, data, sizeof( T ) );
  417.  
  418.     SendPacket( p );
  419. }
  420.  
  421. using IPC_Server = IPC_Peer<IPC_Type::Server>;
  422. using IPC_Client = IPC_Peer<IPC_Type::Client>;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement