async(3)

handle.h

Handle是一个纯虚类(run函数),当CoroHandle父类。

CoroHandle也是一个抽象类,run函数还没实现。

Task::promise_type会继承CoroHandle

namespace ASYNCIO_NS {
// for cancelled
using HandleId = uint64_t;

/*
句柄类,回调类
*/
struct Handle { // type erase for EventLoop
    enum State: uint8_t { // 句柄状态
        UNSCHEDULED,
        SUSPEND,
        SCHEDULED,
    };

    Handle() noexcept: handle_id_(handle_id_generation_++) {}
    virtual void run() = 0;
    void set_state(State state) { state_ = state; }
    HandleId get_handle_id() { return handle_id_; }
    virtual ~Handle() = default;
private:
    HandleId handle_id_;
    static HandleId handle_id_generation_;
protected:
    State state_ {Handle::UNSCHEDULED};
};

// handle maybe destroyed, using the increasing id to track the lifetime of handle.
// don't directly using a raw pointer to track coroutine lifetime,
// because a destroyed coroutine may has the same address as a new ready coroutine has created.

/*
通过id来追踪句柄
*/
struct HandleInfo {
    HandleId id { }; 	// id(递增)
    Handle* handle { }; // 处理句柄(回调函数)
};

/*
协程句柄类
实现在event_loop.cpp
*/
struct CoroHandle: Handle {
    std::string frame_name() const {
        const auto& frame_info = get_frame_info();
        return fmt::format("{} at {}:{}", frame_info.function_name(),
                           frame_info.file_name(), frame_info.line());
    }
    
    virtual void dump_backtrace(size_t depth = 0) const {};
    
    /*
    调度: call_soon
    */
    void CoroHandle::schedule() {
        if (state_ == Handle::UNSCHEDULED){
            get_event_loop().call_soon(*this);
        }
    }
    
    /*
    取消调度:cancel_handle
    */
    void CoroHandle::cancel() {
        if (state_ != Handle::UNSCHEDULED){
            get_event_loop().cancel_handle(*this);
        }
    }
private:
    virtual const std::source_location& get_frame_info() const;
};

selector 封装epoll

d1ad1

  • selector.h

using Selector = EpollSelector;

  • event.h
namespace ASYNCIO_NS {
/*
事件类:包括事件类型以及对应的回调函数(事件处理句柄)
*/
struct Event {
    enum Flags: Flags_t {
        EVENT_READ = EPOLLIN,
        EVENT_WRITE = EPOLLOUT
    };

    int fd;					// fd
    Flags flags;    		// 事件类型
    HandleInfo handle_info; // 处理句柄(回调函数)
};
}
  • epoll_selector.h:封装epoll
namespace ASYNCIO_NS {
struct EpollSelector {
    EpollSelector(): epfd_(epoll_create1(0)) {
        if (epfd_ < 0) {
            perror("epoll_create1");
            throw;
        }
    }
    
    /*
    epoll_wait将就绪事件的句柄返回
    */
    std::vector<Event> select(int timeout /* ms */) {
        errno = 0;
        std::vector<epoll_event> events;
        events.resize(register_event_count_);
        int ndfs = epoll_wait(epfd_, events.data(), register_event_count_, timeout);
        std::vector<Event> result;
        for (size_t i = 0; i < ndfs; ++i) {
            auto handle_info = reinterpret_cast<HandleInfo*>(events[i].data.ptr);
            // 有处理句柄
            if (handle_info->handle != nullptr && handle_info->handle != (Handle*)&handle_info->handle) {
                // 不要fd?
                result.emplace_back(Event {
                    .handle_info = *handle_info
                });
            } else { // 没有处理句柄(或者说处理句柄已经调用过了),就作个标志(不用这么复杂把)
                // mark event ready, but has no response callback
                handle_info->handle = (Handle*)&handle_info->handle;
            }
        }
        return result;
    }
    
    ~EpollSelector() {
        if (epfd_ > 0) { close(epfd_); }
    }

    bool is_stop() { return register_event_count_ == 1; }

    /*
    添加监听事件
    */
    void register_event(const Event& event) {
        // ev.data.ptr置为handle_info
        epoll_event ev{ .events = event.flags, .data {.ptr = const_cast<HandleInfo*>(&event.handle_info) } };
        if (epoll_ctl(epfd_, EPOLL_CTL_ADD, event.fd, &ev) == 0) {
            ++register_event_count_;
        }
    }

    /*
    移除监听事件
    */
    void remove_event(const Event& event) {
        epoll_event ev{ .events = event.flags };
        if (epoll_ctl(epfd_, EPOLL_CTL_DEL, event.fd, &ev) == 0) {
            --register_event_count_;
        }
    }
private:
    int epfd_;
    int register_event_count_ {1};
};
}

EventLoop 事件循环

主要功能:增加事件监听(wait_event) 、 定时器(call_later) 、事件调度(call_soon)

EventLoop::WaitEventAwaiter

顾名思义,当需要等待事件发生时,创建一个WaitEventAwaiter,然后co_await

在EventLoop的wait_event()用到:

selector是EventLoop所属的Selector

event需要外部传入

auto wait_event(const Event& event) {
	return WaitEventAwaiter{selector_, event};

}
struct WaitEventAwaiter {
    bool await_ready() noexcept {
        // 检查传入的Event是否已经发生,是否需要挂起
        bool ready = (event_.handle_info.handle == (const Handle*)&event_.handle_info.handle); // 这个地方看EpollSelector::select
        event_.handle_info.handle = nullptr;
        return ready;
    }
    
    template<typename Promise>
    constexpr void await_suspend(std::coroutine_handle<Promise> handle) noexcept {
        handle.promise().set_state(Handle::SUSPEND); // 标志当前协程状态为suspend
        event_.handle_info = {
            .id = handle.promise().get_handle_id(), // 记录id???? // 调用的是Handle::get_handle_id(),这里就是Task::promise_type::get_handle_id()
            .handle = &handle.promise() //< set callback 注意这里的回调函数设置为&handle.promise()
        };
        if (! registered_) { // 注册到Selecotr监听, 当事件发生时,会被唤醒
            selector_.register_event(event_); 
            registered_ = true;
        }
    }
    
    void await_resume() noexcept {
        event_.handle_info = { }; //< reset callback  // co_await返回时,清空event的回调函数,后续不会再调用
    }

    void destroy() noexcept { // 取消监听
        if (registered_) {
            selector_.remove_event(event_);
            registered_ = false;
        }
    }

    ~WaitEventAwaiter() {
        destroy();
    }

    Selector& selector_;		// 对应的Selector
    Event event_ {};			// 对应的Event
    bool registered_ { false }; // 是否注册到Selector等待监听
};

EventLoop事件循环

对外暴露的有:

  • call_later(delay, callback)调用call_at(),call_at()将callback加入schedule_(相当于一个定时器),通过Selector等待调度,时间到了,会将callback放到ready _
  • call_soon(Handle& handle): 将handle加入到ready _, ready _会在每次epoll时都执行清空一次
  • run_until_complete()相当于muduo中的EventLoop::loop()
  • wait_event(const Event& event): 返回一个WaitEventAwaiter
  • cancel_handle(Handle& handle):取消监听
namespace ASYNCIO_NS {
class EventLoop: private NonCopyable {
    using MSDuration = std::chrono::milliseconds;
public:
    EventLoop() { // 记录开始时间
        auto now = std::chrono::steady_clock::now();
        start_time_ = duration_cast<MSDuration>(now.time_since_epoch());
    }

    MSDuration time() { // 当前时间(相对于start_time_)
        auto now = std::chrono::steady_clock::now();
        return duration_cast<MSDuration>(now.time_since_epoch()) - start_time_;
    }

    // delay(单位:milliseconds)后调用callback
    template<typename Rep, typename Period>
    void call_later(std::chrono::duration<Rep, Period> delay, Handle& callback) {
        call_at(time() + duration_cast<MSDuration>(delay), callback);
    }

    // 取消句柄调度(执行)
    void cancel_handle(Handle& handle) {
        handle.set_state(Handle::UNSCHEDULED);
        cancelled_.insert(handle.get_handle_id());
    }

    // 调用句柄(直接加入队列等待执行)
    void call_soon(Handle& handle) {
        handle.set_state(Handle::SCHEDULED);
        ready_.push({handle.get_handle_id(), &handle});
    }

    struct WaitEventAwaiter {
    	...
    };

    [[nodiscard]]
    auto wait_event(const Event& event) {
        return WaitEventAwaiter{selector_, event};
    }

    void run_until_complete() {
    	while (! is_stop()) { run_once(); } // 死循环,每次就调用run_once()
	}

private:
    bool is_stop() {
        return schedule_.empty() && ready_.empty() && selector_.is_stop();
    }

    /*
    清除被cancel的句柄,只清理堆顶部分,不然清理时间太长,也没有必要全清理
    */
    void EventLoop::cleanup_delayed_call() {
        // Remove delayed calls that were cancelled from head of queue.
        while (! schedule_.empty()) {
            auto&& [when, handle_info] = schedule_[0];
            if (auto iter = cancelled_.find(handle_info.id); iter != cancelled_.end()) {
                ranges::pop_heap(schedule_, std::ranges::greater{}, &TimerHandle::first);
                schedule_.pop_back();
                cancelled_.erase(iter);
            } else {
                break;
            }
        }
    }

    /*
    std::chrono::duration 是一个表示时间间隔的模板类,它有两个模板参数:Rep 和 Period。
    这两个参数分别表示时间间隔的数量(如秒、毫秒等)和时间单位(如秒、毫秒等)。
    */
    /*
    插入回调,在when时调用
    */
    template<typename Rep, typename Period>
    void call_at(std::chrono::duration<Rep, Period> when, Handle& callback) {
        callback.set_state(Handle::SCHEDULED);
        schedule_.emplace_back(duration_cast<MSDuration>(when),
                               HandleInfo{callback.get_handle_id(), &callback});
        /*
        这段代码使用了cpp标准库中的std::ranges::push_heap函数,该函数用于将指定范围内的元素调整为堆。
        具体来说,std::ranges::push_heap函数接受三个参数:
        schedule_:表示要调整为堆的范围。
        std::ranges::greater{}:表示使用大顶堆的比较方式,即父节点的值大于或等于其子节点的值。
        &TimerHandle::first:表示以TimerHandle类的first成员作为排序依据。
        */
        std::ranges::push_heap(schedule_, std::ranges::greater{}, &TimerHandle::first);
    }
	
    /*
    0:确定epoll_wait超时时间(0(ready_不为空)、堆顶的超时时间、-1(一直阻塞))
    1:调用Selecotr.select(epoll)来获取已就绪Event,添加到ready_
    2:将已超时句柄从schedule_插入到ready_
    3:执行ready_句柄
    4:清除被cancel的句柄
    */
    void run_once() {
        std::optional<MSDuration> timeout;
        if (!ready_.empty()) { // 如果已有等待执行的句柄,则不执行select,先调用已经入队的
            timeout.emplace(0);
        } else if (! schedule_.empty()) {// 如果schedule_非空,则以第一个超时时间作为select超时间
            auto&& [when, _] = schedule_[0];
            timeout = std::max(when - time(), MSDuration(0));
        }
		
        // 将已就绪句柄从schedule_插入到ready_
        // 调用select(epoll_wait)获取已就绪事件的句柄
        auto event_lists = selector_.select(timeout.has_value() ? timeout->count() : -1);
        for (auto&& event: event_lists) {
            ready_.push(event.handle_info);
        }

        // 将已超时句柄从schedule_插入到ready_
        auto end_time = time();
        while (! schedule_.empty()) {
            auto&& [when, handle_info] = schedule_[0];
            if (when >= end_time) break;
            ready_.push(handle_info);
            ranges::pop_heap(schedule_, ranges::greater{}, &TimerHandle::first);
            schedule_.pop_back();
        }

        // 执行ready_句柄
        for (size_t ntodo = ready_.size(), i = 0; i < ntodo; ++i) {
            auto [handle_id, handle] = ready_.front(); ready_.pop();
            if (auto iter = cancelled_.find(handle_id); iter != cancelled_.end()) {
                cancelled_.erase(iter);
            } else {
                handle->set_state(Handle::UNSCHEDULED);
                handle->run();
            }
        }

        // 清除被cancel的句柄
        cleanup_delayed_call();
    }

private:
    MSDuration start_time_; 					// 记录eventloop开始的时间
    Selector selector_;     					// EpollSelector
    std::queue<HandleInfo> ready_;  			// 等待执行的句柄
    using TimerHandle = std::pair<MSDuration, HandleInfo>;
    std::vector<TimerHandle> schedule_; 		// min time heap(定时器)
    std::unordered_set<HandleId> cancelled_;    // 已取消的句柄
};

// 单例EventLoop
EventLoop& get_event_loop() {
    static EventLoop loop;
    return loop;
}
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值