Example #1
0
        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);
        }
Example #2
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;
        }
Example #3
0
        /// <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);
        }
Example #4
0
        /// <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;
        }
Example #5
0
        /// <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);
        }
Example #6
0
        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;
        }
Example #7
0
        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);
                }
            }
        }
Example #8
0
        /// <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;
        }
Example #9
0
        /// <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);
        }
Example #10
0
        /// <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);
        }
Example #11
0
        /// <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();
        }