File linux.h

File List > coroutine > linux.h

Go to the documentation of this file.

#ifndef COROUTINE_SYSTEM_WRAPPER_H
#define COROUTINE_SYSTEM_WRAPPER_H
#if !(defined(__linux__))
#error "expect Linux platform for this file"
#endif
#include <sys/epoll.h> // for Linux epoll

#include <coroutine/return.h>
#include <gsl/gsl>

namespace coro {

class epoll_owner final {
    int64_t epfd;

  public:
    epoll_owner() noexcept(false);
    ~epoll_owner() noexcept;
    epoll_owner(const epoll_owner&) = delete;
    epoll_owner(epoll_owner&&) = delete;
    epoll_owner& operator=(const epoll_owner&) = delete;
    epoll_owner& operator=(epoll_owner&&) = delete;

  public:
    void try_add(uint64_t fd, epoll_event& req) noexcept(false);

    void remove(uint64_t fd);

    ptrdiff_t wait(uint32_t wait_ms,
                   gsl::span<epoll_event> list) noexcept(false);

  public:
    [[nodiscard]] auto submit(int64_t fd, epoll_event& req) noexcept {
        class awaiter final : public suspend_always {
            epoll_owner& ep;
            int64_t fd;
            epoll_event& req;

          public:
            constexpr awaiter(epoll_owner& _ep, int64_t _fd, epoll_event& _req)
                : ep{_ep}, fd{_fd}, req{_req} {
            }

          public:
            void await_suspend(coroutine_handle<void> coro) noexcept(false) {
                if (req.data.ptr == nullptr)
                    req.data.ptr = coro.address();
                return ep.try_add(fd, req);
            }
        };
        return awaiter{*this, fd, req};
    }
};

class event final {
    uint64_t state;

  public:
    event() noexcept(false);
    ~event() noexcept;
    event(const event&) = delete;
    event(event&&) = delete;
    event& operator=(const event&) = delete;
    event& operator=(event&&) = delete;

    uint64_t fd() const noexcept;
    bool is_set() const noexcept;
    void set() noexcept(false);
    void reset() noexcept(false);
};

auto wait_in(epoll_owner& ep, event& efd) {
    class awaiter : epoll_event {
        epoll_owner& ep;
        event& efd;

      public:
        awaiter(epoll_owner& _ep, event& _efd) noexcept
            : epoll_event{}, ep{_ep}, efd{_efd} {
            this->events = EPOLLET | EPOLLIN | EPOLLONESHOT;
        }

        bool await_ready() const noexcept {
            return efd.is_set();
        }
        void await_suspend(coroutine_handle<void> coro) noexcept(false) {
            this->data.ptr = coro.address();
            return ep.try_add(efd.fd(), *this);
        }
        void await_resume() noexcept {
            return efd.reset();
        }
    };
    return awaiter{ep, efd};
}

} // namespace coro

#endif // COROUTINE_SYSTEM_WRAPPER_H