Example #1
0
            // private unsafe iovec* IoVectorTable => (iovec*)Align(_ioVectorTableMemory); // TODO

            public unsafe LinuxAio() :
                base(supportsPolling: false, isThreadSafe: false)
            {
                _memoryHandles = new List <MemoryHandle>();
                try
                {
                    // Memory
                    _aioEventsMemory   = AllocMemory(sizeof(io_event) * IocbLength);
                    _aioCbsMemory      = AllocMemory(sizeof(iocb) * IocbLength);
                    _aioCbsTableMemory = AllocMemory(IntPtr.Size * IocbLength);
                    // _ioVectorTableMemory = AllocMemory(SizeOf.iovec * IovsPerIocb * _ioCbLength);
                    _scheduledOperations = new List <Operation>();
                    for (int i = 0; i < IocbLength; i++)
                    {
                        AioCbsTable[i] = &AioCbs[i];
                    }

                    // Aio
                    aio_context_t ctx;
                    int           rv = io_setup(NrEvents, &ctx);
                    if (rv == -1)
                    {
                        PlatformException.Throw();
                    }
                    _ctx = ctx;
                }
                catch
                {
                    FreeResources();
                }
            }
 void IAsyncExecutionResultHandler.HandleAsyncResult(AsyncOperationResult result)
 {
     if (result.IsError && result.Errno != EAGAIN)
     {
         PlatformException.Throw();
     }
 }
            private unsafe void CreateResources(bool useLinuxAio)
            {
                try
                {
                    if (useLinuxAio)
                    {
                        _asyncExecutionQueue = new LinuxAio();
                    }

                    _epollFd = epoll_create1(EPOLL_CLOEXEC);
                    if (_epollFd == -1)
                    {
                        PlatformException.Throw();
                    }

                    _pipeReadEnd  = new CloseSafeHandle();
                    _pipeWriteEnd = new CloseSafeHandle();
                    int *pipeFds = stackalloc int[2];
                    int  rv      = pipe2(pipeFds, O_CLOEXEC | O_NONBLOCK);
                    if (rv == -1)
                    {
                        PlatformException.Throw();
                    }
                    _pipeReadEnd.SetHandle(pipeFds[0]);
                    _pipeWriteEnd.SetHandle(pipeFds[1]);

                    Control(EPOLL_CTL_ADD, pipeFds[0], EPOLLIN, PipeKey);
                }
                catch
                {
                    FreeResources();

                    throw;
                }
            }
 private unsafe void ReadFromPipe(AsyncExecutionQueue?executionEngine)
 {
     if (executionEngine == null)
     {
         Span <byte> buffer = stackalloc byte[128];
         int         rv     = IoPal.Read(_pipeReadEnd !, buffer);
         if (rv == -1)
         {
             if (errno != EAGAIN)
             {
                 PlatformException.Throw();
             }
         }
     }
     else
     {
         if (_dummyReadBuffer == null)
         {
             _dummyReadBuffer = new byte[128];
         }
         executionEngine.AddRead(_pipeReadEnd !, _dummyReadBuffer,
                                 (AsyncOperationResult result, object?state, int data) =>
         {
             if (result.IsError && result.Errno != EAGAIN)
             {
                 PlatformException.Throw();
             }
         }, state: null, data: 0);
     }
 }
            public unsafe void Control(int op, int fd, int events, int key)
            {
                epoll_event ev = default;

                ev.events  = events;
                ev.data.fd = key;
                int rv = epoll_ctl(_epollFd, op, fd, &ev);

                if (rv != 0)
                {
                    PlatformException.Throw();
                }
            }
            private unsafe void WriteToPipe()
            {
                Span <byte> buffer = stackalloc byte[1];
                int         rv     = IoPal.Write(_pipeWriteEnd !, buffer);

                if (rv == -1)
                {
                    if (errno != EAGAIN)
                    {
                        PlatformException.Throw();
                    }
                }
            }
            private unsafe void WriteToEventFd()
            {
                Span <byte> buffer = stackalloc byte[8];

                buffer[7] = 1;
                int rv = IoPal.Write(_eventFd !, buffer);

                if (rv == -1)
                {
                    if (errno != EAGAIN)
                    {
                        PlatformException.Throw();
                    }
                }
            }
 void IAsyncExecutionResultHandler.HandleAsyncResult(AsyncOperationResult asyncResult)
 {
     // TODO: do we need to do a volatile read of _disposed?
     if (asyncResult.Errno == EAGAIN || (!asyncResult.IsError && asyncResult.Value == 8))
     {
         AddReadFromEventFd();
     }
     else if (asyncResult.IsError)
     {
         PlatformException.Throw(asyncResult.Errno);
     }
     else
     {
         ThrowHelper.ThrowIndexOutOfRange(asyncResult.Value);
     }
 }
Example #9
0
        public static void SetNonBlocking(SafeHandle handle)
        {
            int fd    = handle.DangerousGetHandle().ToInt32(); // TODO: make safe
            int flags = fcntl(fd, F_GETFL, 0);

            if (flags == -1)
            {
                PlatformException.Throw();
            }

            flags |= O_NONBLOCK;

            int rv = fcntl(fd, F_SETFL, flags);

            if (rv == -1)
            {
                PlatformException.Throw();
            }
        }
 private void AddReadFromEventFd()
 {
     _iouring !.AddRead(_eventFd !, _dummyReadBuffer,
                        (AsyncOperationResult asyncResult, object?state, int data) =>
     {
         // TODO: do we need to do a volatile read of _disposed?
         if (asyncResult.Errno == EAGAIN || (!asyncResult.IsError && asyncResult.Value == 8))
         {
             ((IOUringThread)state !).AddReadFromEventFd();
         }
         else if (asyncResult.IsError)
         {
             PlatformException.Throw(asyncResult.Errno);
         }
         else
         {
             ThrowHelper.ThrowIndexOutOfRange(asyncResult.Value);
         }
     }
                        , this, 0);
 }
 private unsafe void ReadFromPipe(AsyncExecutionQueue?executionEngine)
 {
     if (executionEngine == null)
     {
         Span <byte> buffer = stackalloc byte[128];
         int         rv     = IoPal.Read(_pipeReadEnd !, buffer);
         if (rv == -1)
         {
             if (errno != EAGAIN)
             {
                 PlatformException.Throw();
             }
         }
     }
     else
     {
         if (_dummyReadBuffer == null)
         {
             _dummyReadBuffer = new byte[128];
         }
         executionEngine.AddRead(_pipeReadEnd !, _dummyReadBuffer, this, data: 0);
     }
 }
            private unsafe void CreateResources()
            {
                try
                {
                    _iouring = new IOUringExecutionQueue();

                    _eventFd = new CloseSafeHandle();
                    int rv = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
                    if (rv == -1)
                    {
                        PlatformException.Throw();
                    }
                    _eventFd.SetHandle(rv);

                    AddReadFromEventFd();
                }
                catch
                {
                    FreeResources();

                    throw;
                }
            }
Example #13
0
            public unsafe bool ExecuteOperations()
            {
                List <Operation>?scheduled = _scheduledOperations;

                if (scheduled == null)
                {
                    return(false);
                }
                _scheduledOperations  = _cachedOperationsList;
                _cachedOperationsList = null;

                if (_results == null || _results.Length < scheduled.Count)
                {
                    _results = new AsyncOperationResult[Math.Max(scheduled.Count, 2 * (_results?.Length ?? 0))];
                }
                AsyncOperationResult[] results = _results;

                int queueLength = scheduled.Count;
                int queueOffset = 0;

                while (queueOffset < queueLength)
                {
                    int nr = Math.Min((queueLength - queueOffset), IocbLength);
                    try
                    {
                        iocb *aioCbs = AioCbs;
                        for (int i = queueOffset; i < (queueOffset + nr); i++)
                        {
                            Operation op = scheduled[i];
                            int       fd = op.Handle.DangerousGetHandle().ToInt32(); // TODO: make safe

                            MemoryHandle handle = op.Memory.Pin();
                            _memoryHandles.Add(handle);

                            aioCbs->aio_fildes     = fd;
                            aioCbs->aio_data       = (ulong)i;
                            aioCbs->aio_lio_opcode = op.IsReadNotWrite ? IOCB_CMD_PREAD : IOCB_CMD_PWRITE;
                            aioCbs->aio_buf        = (ulong)handle.Pointer;
                            aioCbs->aio_nbytes     = (ulong)op.Memory.Length;
                            aioCbs++;
                        }
                        iocb **iocbpp   = AioCbsTable;
                        int    toSubmit = nr;
                        while (toSubmit > 0)
                        {
                            int rv = io_submit(_ctx, toSubmit, iocbpp);
                            if (rv == -1)
                            {
                                PlatformException.Throw();
                            }
                            int toReceive = rv;
                            toSubmit -= rv;
                            iocbpp   += rv;
                            while (toReceive > 0)
                            {
                                do
                                {
                                    rv = IoGetEvents(_ctx, toReceive, AioEvents);
                                } while (rv == -1 && errno == EINTR);
                                if (rv == -1)
                                {
                                    PlatformException.Throw();
                                }
                                io_event *events = AioEvents;
                                for (int i = 0; i < rv; i++)
                                {
                                    results[(int)events->data] = new AsyncOperationResult(events->res);
                                    events++;
                                }
                                toReceive -= rv;
                            }
                        }
                    }
                    finally
                    {
                        foreach (var handle in _memoryHandles)
                        {
                            handle.Dispose();
                        }
                        _memoryHandles.Clear();
                    }

                    // Callbacks
                    for (int i = queueOffset; i < (queueOffset + nr); i++)
                    {
                        Operation op = scheduled[i];
                        op.Callback(results[i], op.State, op.Data);
                    }

                    queueOffset += nr;
                }

                scheduled.Clear();
                if (_scheduledOperations == null)
                {
                    _scheduledOperations = scheduled;
                    return(false);
                }
                else
                {
                    _cachedOperationsList = scheduled;
                    return(_scheduledOperations.Count > 0);
                }
            }
            private unsafe void EventLoop()
            {
                try
                {
                    var eventBuffer = stackalloc epoll_event[EventBufferLength];
                    List <EPollAsyncContext?> asyncContextsForEvents = new List <EPollAsyncContext?>();

                    bool running      = true;
                    int  epollTimeout = -1;
                    while (running)
                    {
                        int rv = epoll_wait(_epollFd, eventBuffer, EventBufferLength, epollTimeout);
                        Volatile.Write(ref _blockedState, StateNotBlocked);

                        if (rv == -1)
                        {
                            if (LibC.errno == EINTR)
                            {
                                continue;
                            }

                            PlatformException.Throw();
                        }

                        lock (_asyncContexts)
                        {
                            for (int i = 0; i < rv; i++)
                            {
                                int key = eventBuffer[i].data.fd;
                                if (_asyncContexts.TryGetValue(key, out EPollAsyncContext? eventContext))
                                {
                                    asyncContextsForEvents.Add(eventContext);
                                }
                                else
                                {
                                    if (key == PipeKey)
                                    {
                                        running = !_disposed;
                                        ReadFromPipe(_asyncExecutionQueue);
                                    }
                                    asyncContextsForEvents.Add(null);
                                }
                            }
                        }

                        for (int i = 0; i < rv; i++)
                        {
                            EPollAsyncContext?context = asyncContextsForEvents[i];
                            if (context != null)
                            {
                                context.HandleEvents(eventBuffer[i].events);
                            }
                        }
                        asyncContextsForEvents.Clear();

                        bool actionsRemaining = false;

                        // Run this twice, executions cause more executions.
                        for (int i = 0; i < 2; i++)
                        {
                            // First execute scheduled actions, they can add to the exection queue.
                            ExecuteScheduledActions();
                            if (_asyncExecutionQueue != null)
                            {
                                actionsRemaining = _asyncExecutionQueue.ExecuteOperations();
                            }
                        }

                        if (!actionsRemaining)
                        {
                            // Check if there are scheduled actions remaining.
                            lock (_actionQueueGate)
                            {
                                actionsRemaining = _scheduledActions.Count > 0;
                                if (!actionsRemaining)
                                {
                                    Volatile.Write(ref _blockedState, StateBlocked);
                                }
                            }
                        }
                        epollTimeout = actionsRemaining ? 0 : -1;
                    }

                    // Execute actions that got posted before we were disposed.
                    ExecuteScheduledActions();

                    // Complete pending async operations.
                    _asyncExecutionQueue?.Dispose();

                    EPollAsyncContext[] contexts;
                    lock (_asyncContexts)
                    {
                        contexts = new EPollAsyncContext[_asyncContexts.Count];
                        _asyncContexts.Values.CopyTo(contexts, 0);
                        _asyncContexts.Clear();
                    }
                    foreach (var context in contexts)
                    {
                        context.Dispose();
                    }

                    FreeResources();
                }
                catch (Exception e)
                {
                    Environment.FailFast(e.ToString());
                }
            }