private int AllocateRequest() { var newRqUsed = rqUsed + 1; if (newRqUsed > rqReservedSize) { var newRqReservedSize = rqReservedSize + rioRqGrowthFactor; if (!RioNative.AllocateRIOCompletion((uint)rioRqGrowthFactor)) { var errorCode = Marshal.GetLastWin32Error(); logger.LogError( "Failed to allocate completions to this socket. AllocateRIOCompletion() returns error {0}", errorCode); return(errorCode); } // Resize the RQ. if (!RioNative.ResizeRIORequestQueue(rioRqHandle, (uint)newRqReservedSize >> 1, (uint)newRqReservedSize >> 1)) { var errorCode = Marshal.GetLastWin32Error(); logger.LogError("Failed to resize the request queue. ResizeRIORequestQueue() returns error {0}", errorCode); RioNative.ReleaseRIOCompletion((uint)rioRqGrowthFactor); return(errorCode); } // since it succeeded, update reserved with the new size rqReservedSize = (uint)newRqReservedSize; } // everything succeeded - update rqUsed with the new slots being used for this next IO rqUsed = newRqUsed; return(0); }
/// <summary> /// Creates a request queue for this socket operation /// </summary> private void CreateRequestQueue() { // Allocate completion from completion queue if (!RioNative.AllocateRIOCompletion((uint)InitialCqRoom)) { var socketException = new SocketException(); logger.LogError( "AllocateRIOCompletion() failed to allocate completions to this socket. Returns error {0}", socketException.ErrorCode); throw socketException; } // Create the RQ for this socket. connectionId = GenerateUniqueKey(); while (!RioNative.ConnectionTable.TryAdd(connectionId, this)) { connectionId = GenerateUniqueKey(); } rioRqHandle = RioNative.CreateRIORequestQueue(SockHandle, rqReservedSize >> 1, rqReservedSize >> 1, connectionId); if (rioRqHandle != IntPtr.Zero) { return; } // Error Handling var sockException = new SocketException(); RioNative.ReleaseRIOCompletion((uint)InitialCqRoom); RioSocketWrapper sock; RioNative.ConnectionTable.TryRemove(connectionId, out sock); logger.LogError("CreateRIORequestQueue() returns error {0}", sockException.ErrorCode); throw sockException; }
/// <summary> /// Accepts a incoming connection request. /// </summary> /// <returns>A ISocket instance used to send and receive data</returns> public unsafe ISocketWrapper Accept() { EnsureAccessible(); if (!isListening) { throw new InvalidOperationException("You must call the Listen method before performing this operation."); } var sockaddrlen = Ipv4AddressSize; var addrbuf = stackalloc byte[(sockaddrlen / IntPtr.Size + 2) * IntPtr.Size]; //sizeof DWORD var sockaddr = (IntPtr)addrbuf; var acceptedSockHandle = RioNative.accept(SockHandle, sockaddr, ref sockaddrlen); if (acceptedSockHandle == new IntPtr(-1)) { // if the native call fails we'll throw a SocketException var socketException = new SocketException(); logger.LogError("Native accept() failed with error {0}", socketException.NativeErrorCode); throw socketException; } var remoteEp = CreateIpEndPoint(sockaddr); var socket = new RioSocketWrapper(acceptedSockHandle, LocalEndPoint, remoteEp); logger.LogDebug("Accepted connection from {0} to {1}", socket.RemoteEndPoint, socket.LocalEndPoint); return(socket); }
/// <summary> /// Implementation of the Dispose pattern. /// </summary> private void Dispose(bool disposing) { if (disposed) { return; } logger.LogDebug("Disposing ByteBufChunk [{0}].", ToString()); if (!IsUnsafe && memory != null) { memory = null; segmentQueue.Clear(); } if (BufId != IntPtr.Zero) { RioNative.DeregisterRIOBuffer(BufId); } // If the unsafedMemory is still valid, free it. if (unsafeMemory != IntPtr.Zero) { var heapBlock = unsafeMemory; unsafeMemory = IntPtr.Zero; FreeToProcessHeap(heapBlock); } if (disposing) { GC.SuppressFinalize(this); } disposed = true; }
/// <summary> /// Posts a receive operation to this socket /// </summary> private unsafe void DoReceive() { // Make a room from Request Queue of this socket for operation. var errorStatus = AllocateRequest(); if (errorStatus != 0) { logger.LogError("Cannot post receive operation due to no room in Request Queue."); receivedDataQueue.Add(ByteBuf.NewErrorStatusByteBuf(errorStatus)); return; } // Allocate buffer to receive incoming network data. var dataBuffer = ByteBufPool.UnsafeDefault.Allocate(); if (dataBuffer == null) { logger.LogError("Failed to allocate ByteBuf at DoReceive()."); receivedDataQueue.Add(ByteBuf.NewErrorStatusByteBuf((int)SocketError.NoBufferSpaceAvailable)); return; } var context = new RequestContext(SocketOperation.Receive, dataBuffer); var recvId = GenerateUniqueKey(); // Add the operation context to request table for completion callback. while (!requestContexts.TryAdd(recvId, context)) { // Generate another key, if the key is duplicated. recvId = GenerateUniqueKey(); } // Post a receive operation via native method. var rioBuf = dataBuffer.GetInputRioBuf(); if (RioNative.PostRIOReceive(rioRqHandle, &rioBuf, 1, 0, recvId)) { return; } requestContexts.TryRemove(recvId, out context); context.Data.Release(); if (isCleanedUp) { logger.LogDebug("Socket is already disposed. DoReceive() do nothing."); receivedDataQueue.Add(ByteBuf.NewErrorStatusByteBuf((int)SocketError.NetworkDown)); return; } // Log exception, if post receive operation failed. var socketException = new SocketException(); logger.LogError("Failed to call DoReceive() with error code [{0}], error message: {1}", socketException.ErrorCode, socketException.Message); context.Data.Status = socketException.ErrorCode; receivedDataQueue.Add(context.Data); }
private void Dispose(bool disposing) { // Mark this as disposed before changing anything else. var cleanedUp = isCleanedUp; isCleanedUp = true; if (!cleanedUp && disposing) { try { // Release room back to Completion Queue RioNative.ReleaseRIOCompletion((uint)InitialCqRoom); // Remove this socket from the connected socket table. RioSocketWrapper socket; RioNative.ConnectionTable.TryRemove(connectionId, out socket); } catch (Exception) { logger.LogDebug("RioNative default instance already disposed."); } // Remove all pending socket operations if (!requestContexts.IsEmpty) { foreach (var keyValuePair in requestContexts) { // Release the data buffer of the pending operation keyValuePair.Value.Data.Release(); } requestContexts.Clear(); } // Remove received Data from the queue, and release buffer back to byte pool. while (receivedDataQueue.Count > 0) { ByteBuf data; receivedDataQueue.TryTake(out data); data.Release(); } // Close the socket handle. No need to release Request Queue handle that // will be gone once the socket handle be closed. if (SockHandle != IntPtr.Zero) { RioNative.closesocket(SockHandle); SockHandle = IntPtr.Zero; } GC.SuppressFinalize(this); } isConnected = false; isListening = false; }
public static ByteBufChunk NewChunk(ByteBufPool pool, int segmentSize, int chunkSize, bool isUnsafe) { ByteBufChunk chunk = null; if (!isUnsafe) { chunk = new ByteBufChunk(pool, new byte[chunkSize], segmentSize, chunkSize); return(chunk); } // allocate buffers from process heap var token = HeapAlloc(GetProcessHeap(), 0, chunkSize); if (token == IntPtr.Zero) { throw new OutOfMemoryException("Failed to allocate memory by calling HeapAlloc()"); } // register this heap buffer to RIO buffer var bufferId = RioNative.RegisterRIOBuffer(token, (uint)chunkSize); if (bufferId == IntPtr.Zero) { FreeToProcessHeap(token); throw new Exception(string.Format("Failed to register RIO buffer with error code {0}", Marshal.GetLastWin32Error())); } try { chunk = new ByteBufChunk(pool, token, bufferId, segmentSize, chunkSize); token = IntPtr.Zero; bufferId = IntPtr.Zero; return(chunk); } finally { if (chunk == null && token != IntPtr.Zero) { if (bufferId != IntPtr.Zero) { RioNative.DeregisterRIOBuffer(bufferId); } FreeToProcessHeap(token); } } }
/// <summary> /// Starts listening for incoming connections requests /// </summary> /// <param name="backlog">The maximum length of the pending connections queue. </param> public void Listen(int backlog = 16) { EnsureAccessible(); if (isListening) { return; } var errorCode = RioNative.listen(SockHandle, backlog); if (errorCode != 0) { var socketException = new SocketException(); logger.LogError("Native listen() failed with error {0}", socketException.ErrorCode); throw socketException; } isListening = true; }
/// <summary> /// Posts a send operation to this socket. /// </summary> private unsafe void DoSend(long sendId, RequestContext context) { // Make a room from Request Queue of this socket for operation. var errorStatus = AllocateRequest(); if (errorStatus != 0) { logger.LogError("Cannot post send operation due to no room in Request Queue."); sendStatusQueue.Add(errorStatus); return; } // Add the operation context to request table for completion callback. while (!requestContexts.TryAdd(sendId, context)) { // Generate another key, if the key is duplicated. sendId = GenerateUniqueKey(); } // Post a send operation via native method. var rioBuf = context.Data.GetOutputRioBuf(); if (RioNative.PostRIOSend(rioRqHandle, &rioBuf, 1, 0, sendId)) { return; } requestContexts.TryRemove(sendId, out context); context.Data.Release(); if (isCleanedUp) { logger.LogDebug("Socket is already disposed. PostSend() do nothing."); receivedDataQueue.Add(ByteBuf.NewErrorStatusByteBuf((int)SocketError.NetworkDown)); return; } // Log exception, if post send operation failed. var socketException = new SocketException(); logger.LogError("Failed to call PostRIOSend() with error code [{0}]. Error message: {1}", socketException.ErrorCode, socketException.Message); sendStatusQueue.Add(socketException.ErrorCode); }
/// <summary> /// Default ctor that creates a new instance of RioSocketWrapper class. /// The instance binds to loop-back address with port 0. /// </summary> public unsafe RioSocketWrapper() { RioNative.EnsureRioLoaded(); // Creates a socket handle by calling native method. var sockaddlen = Ipv4AddressSize; var addrbuf = stackalloc byte[(sockaddlen / IntPtr.Size + 2) * IntPtr.Size]; var sockaddr = (IntPtr)addrbuf; SockHandle = RioNative.CreateRIOSocket(sockaddr, ref sockaddlen); if (SockHandle == new IntPtr(-1)) { // if the native call fails we'll throw a SocketException var socketException = new SocketException(); logger.LogError("Native CreateRIOSocket() failed with error {0}", socketException.ErrorCode); throw socketException; } // Generate the local IP endpoint from the returned raw socket address data. LocalEndPoint = CreateIpEndPoint(sockaddr); }
/// <summary> /// Establishes a connection to a remote host that is specified by an IP address and a port number /// </summary> /// <param name="remoteaddr">The IP address of the remote host</param> /// <param name="port">The port number of the remote host</param> public void Connect(IPAddress remoteaddr, int port) { EnsureAccessible(); var remoteEp = new IPEndPoint(remoteaddr, port); int sockaddrlen; var sockaddr = GetNativeSocketAddress(remoteEp, out sockaddrlen); var errorCode = RioNative.connect(SockHandle, sockaddr, sockaddrlen); if (errorCode != 0) { // if the native call fails we'll throw a SocketException var socketException = new SocketException(); logger.LogError("Native connect() failed with error {0}", socketException.ErrorCode); throw socketException; } CreateRequestQueue(); isConnected = true; RemoteEndPoint = remoteEp; // Post a receive operation from the connected RIO socket. DoReceive(); }