/// <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 }
/// <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)); }
/// <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); }
/// <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); }
/// <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>T<()</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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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)); }
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); }
/// <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>T<()</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); }
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); } }
/// <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); }
/// <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)); }
/// <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); }
/// <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; } }); }