int poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
    DebugPrint(dbg_hook, "hook poll. %s coroutine.", g_Scheduler.IsCoroutine() ? "In" : "Not in");

    if (!g_Scheduler.IsCoroutine())
        return poll_f(fds, nfds, timeout);

    if (timeout == 0)
        return poll_f(fds, nfds, timeout);

    Task* tk = g_Scheduler.GetCurrentTask();
    uint32_t timer_id = 0;
    if (timeout != -1) {
        std::chrono::milliseconds duration{timeout};
        tk->IncrementRef(); // timer use ref.
        timer_id = g_Scheduler.ExpireAt(duration, [=]{
                g_Scheduler.IOBlockCancel(tk);
                tk->DecrementRef(); // timer use ref.
                });
    }

    std::vector<FdStruct> fdsts;
    for (nfds_t i = 0; i < nfds; ++i) {
        fdsts.emplace_back();
        fdsts.back().fd = fds[i].fd;
        fdsts.back().event = PollEvent2Epoll(fds[i].events);
    }

    // add into epoll, and switch other context.
    g_Scheduler.IOBlockSwitch(fdsts);
    bool is_timeout = false; // 是否超时
    if (timer_id) {
        is_timeout = true;
        if (g_Scheduler.CancelTimer(timer_id)) {
            tk->DecrementRef(); // timer use ref.
            is_timeout = false;
        }
    }

    if (tk->wait_successful_ == 0) {
        if (is_timeout)
            return 0;
        else
            return poll_f(fds, nfds, 0);
    }

    int n = 0;
    for (int i = 0; i < (int)tk->wait_fds_.size(); ++i)
    {
        fds[i].revents = EpollEvent2Poll(tk->wait_fds_[i].epoll_ptr.revent);
        if (fds[i].revents) ++n;
    }

    assert(n == (int)tk->wait_successful_);

    return n;
}
ssize_t read_write_mode(int fd, OriginF fn, const char* hook_fn_name, uint32_t event, int timeout_so, Args && ... args)
{
    DebugPrint(dbg_hook, "hook %s. %s coroutine.", hook_fn_name, g_Scheduler.IsCoroutine() ? "In" : "Not in");

    if (!g_Scheduler.IsCoroutine()) {
        return fn(fd, std::forward<Args>(args)...);
    } else {
        int flags = fcntl(fd, F_GETFL, 0);
        if (flags & O_NONBLOCK)
            return fn(fd, std::forward<Args>(args)...);

        if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK))
            return fn(fd, std::forward<Args>(args)...);

        ssize_t n = fn(fd, std::forward<Args>(args)...);
        if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
            // get timeout option.
            Task* tk = g_Scheduler.GetCurrentTask();
            uint64_t timer_id = 0;
            struct timeval timeout;
            socklen_t timeout_blen = sizeof(timeout);
            if (0 == getsockopt(fd, SOL_SOCKET, timeout_so, &timeout, &timeout_blen)) {
                if (timeout.tv_sec > 0 || timeout.tv_usec > 0) {
                    std::chrono::milliseconds duration{timeout.tv_sec * 1000 +
                        timeout.tv_usec / 1000};
                    DebugPrint(dbg_hook, "hook task(%s) %s timeout=%dms. fd=%d",
                            g_Scheduler.GetCurrentTaskDebugInfo(), hook_fn_name, (int)duration.count(), fd);
                    tk->IncrementRef(); // timer use ref.
                    timer_id = g_Scheduler.ExpireAt(duration, [=]{
                            g_Scheduler.IOBlockCancel(tk);
                            tk->DecrementRef(); // timer use ref.
                            });
                }
            }

            // add into epoll, and switch other context.
            g_Scheduler.IOBlockSwitch(fd, event);
            bool is_timeout = false;
            if (timer_id) {
                is_timeout = true;
                if (g_Scheduler.CancelTimer(timer_id)) {
                    is_timeout = false;
                    tk->DecrementRef(); // timer use ref.
                }
            }

            if (tk->wait_successful_ == 0) {
                if (is_timeout) {
                    fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
                    errno = EAGAIN;
                    return -1;
                } else {
                    fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
                    return fn(fd, std::forward<Args>(args)...);
                }
            }

            DebugPrint(dbg_hook, "continue task(%s) %s. fd=%d", g_Scheduler.GetCurrentTaskDebugInfo(), hook_fn_name, fd);
            n = fn(fd, std::forward<Args>(args)...);
        } else {
            DebugPrint(dbg_hook, "task(%s) syscall(%s) completed immediately. fd=%d",
                    g_Scheduler.GetCurrentTaskDebugInfo(), hook_fn_name, fd);
        }

        int e = errno;
        fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
        errno = e;
        return n;
    }
}