예제 #1
0
        /// <summary>
        /// Writes a number of elements to a memory location from the provided buffer starting at the specified index.
        /// </summary>
        /// <typeparam name="T">The structure type</typeparam>
        /// <param name="destination">The destination memory location.</param>
        /// <param name="buffer">The source buffer.</param>
        /// <param name="index">The start index within <paramref name="buffer"/>.</param>
        /// <param name="count">The number of elements to write.</param>
        public static unsafe void WriteArray <T>(IntPtr destination, T[] buffer, int index, int count)
            where T : struct
        {
            uint elementSize = (uint)SizeOf <T>();

            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException("index");
            }
            if (buffer.Length - index < count)
            {
                throw new ArgumentException("Invalid offset into array specified by index and count");
            }

            void *ptr = destination.ToPointer();
            byte *p   = (byte *)FastStructure.GetPtr <T>(ref buffer[0]);

#if NETCORE
            Buffer.MemoryCopy(p + (index * elementSize), ptr, elementSize * count, elementSize * count);
#else
            UnsafeNativeMethods.CopyMemoryPtr(ptr, p + (index * elementSize), (uint)(elementSize * count));
#endif
        }
예제 #2
0
        /// <summary>
        /// Reads a number of elements from a memory location into the provided buffer starting at the specified index.
        /// </summary>
        /// <typeparam name="T">The structure type</typeparam>
        /// <param name="buffer">The destination buffer.</param>
        /// <param name="source">The source memory location.</param>
        /// <param name="index">The start index within <paramref name="buffer"/>.</param>
        /// <param name="count">The number of elements to read.</param>
        public static unsafe void ReadArray <T>(T[] buffer, IntPtr source, int index, int count)
            where T : struct
        {
            uint elementSize = (uint)SizeOf <T>();

            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException("index");
            }
            if (buffer.Length - index < count)
            {
                throw new ArgumentException("Invalid offset into array specified by index and count");
            }

            void *ptr = source.ToPointer();
            byte *p   = (byte *)FastStructure.GetPtr <T>(ref buffer[0]);

            UnsafeNativeMethods.CopyMemoryPtr(p + (index * elementSize), ptr, (uint)(elementSize * count));
        }
예제 #3
0
        /// <summary>
        /// Reads the next available node for reading into the specified struct array
        /// </summary>
        /// <typeparam name="T">The structure type to be read</typeparam>
        /// <param name="buffer">Reference to the buffer</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>
        /// <returns>The number of bytes read</returns>
        /// <remarks>The maximum number of bytes that can be read is the minimum of the length of <paramref name="buffer"/> multiplied by <code>Marshal.SizeOf(typeof(T))</code> and <see cref="NodeBufferSize"/>.</remarks>
        public virtual int Read <T>(T[] buffer, int timeout = 1000)
            where T : struct
        {
            Node *node = GetNodeForReading(timeout);

            if (node == null)
            {
                return(0);
            }

            // Copy the data using the FastStructure class (much faster than the MemoryMappedViewAccessor ReadArray<T> method)
            int amount = Math.Min(buffer.Length, NodeBufferSize / FastStructure.SizeOf <T>());

            base.ReadArray <T>(buffer, node->Offset, 0, amount);

            // Return the node for further writing
            ReturnNode(node);

            return(amount);
        }
예제 #4
0
        /// <summary>
        /// Writes the struct array buffer to the next available node for writing
        /// </summary>
        /// <param name="buffer">Reference to the buffer to write</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>
        /// <returns>The number of bytes written</returns>
        /// <remarks>The maximum number of bytes that can be written is the minimum of the length of <paramref name="buffer"/> multiplied by <code>Marshal.SizeOf(typeof(T))</code> and <see cref="NodeBufferSize"/>.</remarks>
        public virtual int Write <T>(T[] buffer, int timeout = 1000)
            where T : struct
        {
            // Grab a node for writing
            Node *node = GetNodeForWriting(timeout);

            if (node == null)
            {
                return(0);
            }

            // Write the data using the FastStructure class (much faster than the MemoryMappedViewAccessor WriteArray<T> method)
            int amount = Math.Min(buffer.Length, NodeBufferSize / FastStructure.SizeOf <T>());

            base.WriteArray <T>(node->Offset, buffer, 0, amount);

            // Writing is complete, make node readable
            PostNode(node);

            return(amount);
        }
예제 #5
0
        /// <summary>
        /// Writes the structure array buffer to the next available node for writing
        /// </summary>
        /// <param name="source">Reference to the buffer to write</param>
        /// <param name="startIndex">The index within the buffer to start writing from</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>
        /// <returns>The number of elements written</returns>
        /// <remarks>The maximum number of elements that can be written is the minimum of the length of <paramref name="source"/> subtracted by <paramref name="startIndex"/> and <see cref="NodeBufferSize"/> divided by <code>FastStructure.SizeOf&gt;T&lt;()</code>.</remarks>
        public virtual int Write <T>(T[] source, int startIndex = 0)
            where T : struct
        {
            // Grab a node for writing
            Node *node = GetNodeForWriting();

            if (node == null)
            {
                return(0);
            }

            // Write the data using the FastStructure class (much faster than the MemoryMappedViewAccessor WriteArray<T> method)
            int count = Math.Min(source.Length - startIndex, NodeBufferSize / FastStructure.SizeOf <T>());

            base.WriteArray <T>(source, startIndex, count, node->Offset);
            node->AmountWritten = count * FastStructure.SizeOf <T>();

            // Writing is complete, make node readable
            PostNode(node);

            return(count);
        }
예제 #6
0
 /// <summary>
 /// Writes an array of <typeparamref name="T"/> into the buffer
 /// </summary>
 /// <typeparam name="T">A structure type</typeparam>
 /// <param name="source">The source data to be written to the buffer</param>
 /// <param name="index">The start index within <paramref name="source"/>.</param>
 /// <param name="count">The number of elements to write.</param>
 /// <param name="bufferPosition">The offset within the buffer region of the shared memory to write to.</param>
 protected virtual void WriteArray <T>(T[] source, int index, int count, long bufferPosition = 0)
     where T : struct
 {
     FastStructure.WriteArray <T>((IntPtr)(BufferStartPtr + bufferPosition), source, index, count);
 }
예제 #7
0
 /// <summary>
 /// Reads an array of <typeparamref name="T"/> from the buffer
 /// </summary>
 /// <typeparam name="T">A structure type</typeparam>
 /// <param name="buffer">Array that will contain the values read from the buffer. The length of this array controls the number of elements to read.</param>
 /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
 protected virtual void Read <T>(T[] buffer, long bufferPosition = 0)
     where T : struct
 {
     FastStructure.ReadArray <T>(buffer, (IntPtr)(BufferStartPtr + bufferPosition), 0, buffer.Length);
     //View.ReadArray(BufferOffset + bufferPosition, buffer, 0, buffer.Length);
 }
예제 #8
0
 /// <summary>
 /// Writes an array of <typeparamref name="T"/> into the buffer
 /// </summary>
 /// <typeparam name="T">A structure type</typeparam>
 /// <param name="bufferPosition">The destination offset within the buffer region of the shared memory.</param>
 /// <param name="buffer">The source buffer</param>
 /// <param name="index">The start index within <paramref name="buffer"/>.</param>
 /// <param name="count">The number of elements to write.</param>
 protected virtual void WriteArray <T>(long bufferPosition, T[] buffer, int index, int count)
     where T : struct
 {
     FastStructure.WriteArray <T>((IntPtr)(BufferStartPtr + bufferPosition), buffer, 0, buffer.Length);
 }
예제 #9
0
 /// <summary>
 /// Writes the generic value type <typeparamref name="T"/> to the location specified by a pointer. This is achieved by emitting a <see cref="DynamicMethod"/> that copies the value from the referenced structure into the specified memory location.
 /// <para>There is no exact equivalent possible in C#, the closest possible (generates the same IL) is the following code:</para>
 /// <code>
 /// unsafe void WriteToPointer(ref SharedHeader dest, ref SharedHeader src)
 /// {
 ///     dest = src;
 /// }
 /// </code>
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="pointer"></param>
 /// <param name="structure"></param>
 public static unsafe void StructureToPtr <T>(ref T structure, IntPtr pointer)
     where T : struct
 {
     FastStructure <T> .StructureToPtr(ref structure, pointer);
 }
예제 #10
0
 /// <summary>
 /// Loads the generic value type <typeparamref name="T"/> from a pointer. This is achieved by emitting a <see cref="DynamicMethod"/> that returns the value in the memory location as a <typeparamref name="T"/>.
 /// <para>The equivalent non-generic C# code:</para>
 /// <code>
 /// unsafe MyStruct ReadFromPointer(byte* pointer)
 /// {
 ///     return *(MyStruct*)pointer;
 /// }
 /// </code>
 /// </summary>
 /// <typeparam name="T">Any value/structure type</typeparam>
 /// <param name="pointer">Unsafe pointer to memory to load the value from</param>
 /// <returns>The newly loaded value</returns>
 public static unsafe T PtrToStructure <T>(IntPtr pointer)
     where T : struct
 {
     return(FastStructure <T> .PtrToStructure(pointer));
 }
예제 #11
0
        bool WriteProtocolV1(MessageType msgType, ulong msgId, byte[] msg, ulong responseMsgId, int timeout)
        {
            if (Disposed)
            {
                return(false);
            }

            if (WriteBuffer.ShuttingDown)
            {
                return(false);
            }

            // Send the request packets
            lock (lock_sendQ)
            {
                // Split message into correct packet size
                int i    = 0;
                int left = msg?.Length ?? 0;

                byte[] pMsg = null;

                ushort totalPackets  = ((msg?.Length ?? 0) == 0) ? (ushort)1 : Convert.ToUInt16(Math.Ceiling((double)msg.Length / (double)msgBufferLength));
                ushort currentPacket = 1;

                while (true)
                {
                    if (WriteBuffer.ShuttingDown)
                    {
                        return(false);
                    }

                    pMsg = new byte[left > msgBufferLength ? msgBufferLength + protocolLength : left + protocolLength];

                    // Writing protocol header
                    var header = new RpcProtocolHeaderV1
                    {
                        MsgType       = msgType,
                        MsgId         = msgId,
                        CurrentPacket = currentPacket,
                        TotalPackets  = totalPackets,
                        PayloadSize   = msg?.Length ?? 0,
                        ResponseId    = responseMsgId
                    };
                    FastStructure.CopyTo(ref header, pMsg, 0);

                    if (left > msgBufferLength)
                    {
                        // Writing payload
                        if (msg != null && msg.Length > 0)
                        {
                            Buffer.BlockCopy(msg, i, pMsg, protocolLength, msgBufferLength);
                        }

                        left -= msgBufferLength;
                        i    += msgBufferLength;
                    }
                    else
                    {
                        // Writing last packet of payload
                        if (msg != null && msg.Length > 0)
                        {
                            Buffer.BlockCopy(msg, i, pMsg, protocolLength, left);
                        }

                        left = 0;
                    }

                    Statistics.StartWaitWrite();
                    var bytes = WriteBuffer.Write((ptr) =>
                    {
                        FastStructure.WriteBytes(ptr, pMsg, 0, pMsg.Length);
                        return(pMsg.Length);
                    }, 1000);

                    Statistics.WritePacket(bytes - protocolLength);

                    if (left <= 0)
                    {
                        break;
                    }
                    currentPacket++;
                }
            }

            return(true);
        }
예제 #12
0
        /// <summary>
        /// Reads the next available node for reading into the specified structure array
        /// </summary>
        /// <typeparam name="T">The structure type to be read</typeparam>
        /// <param name="destination">Reference to the buffer</param>
        /// <param name="startIndex">The index within the destination to start writing to.</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>
        /// <returns>The number of elements read into destination</returns>
        /// <remarks>The maximum number of elements that can be read is the minimum of the length of <paramref name="destination"/> subtracted by <paramref name="startIndex"/> and <see cref="Node.AmountWritten"/> divided by <code>FastStructure.SizeOf&gt;T&lt;()</code>.</remarks>
        public virtual int Read <T>(T[] destination, int startIndex = 0, int timeout = 1000)
            where T : struct
        {
            Node *node = GetNodeForReading(timeout);

            if (node == null)
            {
                return(0);
            }

            // Copy the data using the FastStructure class (much faster than the MemoryMappedViewAccessor ReadArray<T> method)
            int count = Math.Min(destination.Length - startIndex, node->AmountWritten / FastStructure.SizeOf <T>());

            base.ReadArray <T>(destination, startIndex, count, node->Offset);

            // Return the node for further writing
            ReturnNode(node);

            return(count);
        }
예제 #13
0
        void ReadThreadV1()
        {
            while (true && !ReadBuffer.ShuttingDown)
            {
                if (Interlocked.Read(ref _disposed) == 1)
                {
                    return;
                }

                Statistics.StartWaitRead();

                ReadBuffer.Read((ptr) =>
                {
                    int readLength = 0;
                    var header     = FastStructure <RpcProtocolHeaderV1> .PtrToStructure(ptr);
                    ptr            = ptr + protocolLength;
                    readLength    += protocolLength;

                    RpcRequest request = null;
                    if (header.MsgType == MessageType.RpcResponse || header.MsgType == MessageType.ErrorInRpc)
                    {
                        if (!Requests.TryGetValue(header.ResponseId, out request))
                        {
                            // The response received does not have a  matching message that was sent
                            Statistics.DiscardResponse(header.ResponseId);
                            return(protocolLength);
                        }
                    }
                    else
                    {
                        request = IncomingRequests.GetOrAdd(header.MsgId, new RpcRequest
                        {
                            MsgId = header.MsgId
                        });
                    }

                    int packetSize = header.PayloadSize < msgBufferLength ? header.PayloadSize :
                                     (header.CurrentPacket < header.TotalPackets ? msgBufferLength : header.PayloadSize % msgBufferLength);

                    if (header.PayloadSize > 0)
                    {
                        if (request.Data == null)
                        {
                            request.Data = new byte[header.PayloadSize];
                        }

                        int index = msgBufferLength * (header.CurrentPacket - 1);
                        FastStructure.ReadBytes(request.Data, ptr, index, packetSize);
                        readLength += packetSize;
                    }

                    if (header.CurrentPacket == header.TotalPackets)
                    {
                        if (header.MsgType == MessageType.RpcResponse || header.MsgType == MessageType.ErrorInRpc)
                        {
                            Requests.TryRemove(request.MsgId, out RpcRequest removed);
                        }
                        else
                        {
                            IncomingRequests.TryRemove(request.MsgId, out RpcRequest removed);
                        }

                        // Full message is ready
                        var watching = Stopwatch.StartNew();
                        Task.Run(async() =>
                        {
                            Statistics.MessageReceived(header.MsgType, request.Data?.Length ?? 0);

                            if (header.MsgType == MessageType.RpcResponse)
                            {
                                request.IsSuccess = true;
                                request.ResponseReady.Set();
                            }
                            else if (header.MsgType == MessageType.ErrorInRpc)
                            {
                                request.IsSuccess = false;
                                request.ResponseReady.Set();
                            }
                            else if (header.MsgType == MessageType.RpcRequest)
                            {
                                await ProcessCallHandler(request).ConfigureAwait(false);
                            }
                        });
                    }

                    Statistics.ReadPacket(packetSize);

                    return(protocolLength + packetSize);
                }, 500);
            }
        }
예제 #14
0
 /// <summary>
 /// Reads an array of <typeparamref name="T"/> from the buffer.
 /// </summary>
 /// <typeparam name="T">A structure type</typeparam>
 /// <param name="destination">Array that will contain the values read from the buffer. The length of this array controls the number of elements to read.</param>
 /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
 protected virtual void Read <T>(T[] destination, long bufferPosition = 0)
     where T : struct
 {
     FastStructure.ReadArray <T>(destination, (IntPtr)(BufferStartPtr + bufferPosition), 0, destination.Length);
 }
예제 #15
0
 /// <summary>
 /// Retrieve a pointer to the passed generic structure type. This is achieved by emitting a <see cref="DynamicMethod"/> to retrieve a pointer to the structure.
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="structure"></param>
 /// <returns>A pointer to the provided structure in memory.</returns>
 /// <see cref="FastStructure{T}.GetPtr"/>
 public static unsafe void *GetPtr <T>(ref T structure)
     where T : struct
 {
     return(FastStructure <T> .GetPtr(ref structure));
 }
예제 #16
0
 /// <summary>
 /// Reads a number of elements from a memory location into the provided buffer starting at the specified index.
 /// </summary>
 /// <typeparam name="T">The structure type</typeparam>
 /// <param name="destination">The destination buffer.</param>
 /// <param name="index">The start index within <paramref name="destination"/>.</param>
 /// <param name="count">The number of elements to read.</param>
 /// <param name="bufferPosition">The source offset within the buffer region of the shared memory.</param>
 protected virtual void ReadArray <T>(T[] destination, int index, int count, long bufferPosition)
     where T : struct
 {
     FastStructure.ReadArray <T>(destination, (IntPtr)(BufferStartPtr + bufferPosition), index, count);
 }
예제 #17
0
        /// <summary>
        /// Construct a new RpcBuffer
        /// </summary>
        /// <param name="name">The unique channel name. This is the name to be shared between the master/slave pair. Each pair must have a unique value.</param>
        /// <param name="bufferCapacity">Master only: Maximum buffer capacity. Messages will be split into packets that fit this capacity (including a packet header of 64-bytes). The slave will use the same size as defined by the master</param>
        /// <param name="protocolVersion">ProtocolVersion.V1 = 64-byte header for each packet</param>
        /// <param name="bufferNodeCount">Master only: The number of nodes in the underlying circular buffers, each with a size of <paramref name="bufferCapacity"/></param>
        public RpcBuffer(string name, int bufferCapacity = 50000, RpcProtocol protocolVersion = RpcProtocol.V1, int bufferNodeCount = 10)
        {
            if (bufferCapacity < 256) // min 256 bytes
            {
                throw new ArgumentOutOfRangeException(nameof(bufferCapacity), "cannot be less than 256 bytes");
            }

            if (bufferCapacity > 1024 * 1024) // max 1MB
            {
                throw new ArgumentOutOfRangeException(nameof(bufferCapacity), "cannot be larger than 1MB");
            }

            Statistics = new RpcStatistics();

            masterMutex = new Mutex(true, name + "SharedMemory_MasterMutex", out bool createdNew);

            if (createdNew && masterMutex.WaitOne(500))
            {
                instanceType = InstanceType.Master;
            }
            else
            {
                instanceType = InstanceType.Slave;
                if (masterMutex != null)
                {
                    masterMutex.Close();
                    masterMutex.Dispose();
                    masterMutex = null;
                }
            }

            switch (protocolVersion)
            {
            case RpcProtocol.V1:
                this.protocolVersion = protocolVersion;
                protocolLength       = FastStructure.SizeOf <RpcProtocolHeaderV1>();
                Statistics.ProtocolOverheadPerPacket = protocolLength;
                break;
            }

            this.bufferCapacity  = bufferCapacity;
            this.bufferNodeCount = bufferNodeCount;
            if (instanceType == InstanceType.Master)
            {
                WriteBuffer = new CircularBuffer(name + "_Slave_SharedMemory_MMF", bufferNodeCount, this.bufferCapacity);
                ReadBuffer  = new CircularBuffer(name + "_Master_SharedMemory_MMF", bufferNodeCount, this.bufferCapacity);
            }
            else
            {
                ReadBuffer           = new CircularBuffer(name + "_Slave_SharedMemory_MMF");
                WriteBuffer          = new CircularBuffer(name + "_Master_SharedMemory_MMF");
                this.bufferCapacity  = ReadBuffer.NodeBufferSize;
                this.bufferNodeCount = ReadBuffer.NodeCount;
            }

            this.msgBufferLength = Convert.ToInt32(this.bufferCapacity) - protocolLength;

            Task.Run(() =>
            {
                switch (protocolVersion)
                {
                case RpcProtocol.V1:
                    ReadThreadV1();
                    break;
                }
            });
        }