【sylar-webserver】7 定时器模块

34 阅读4分钟

设计

classDiagram
    class Timer {
        <<enable_shared_from_this>>
        -uint64_t m_ms
        -uint64_t m_next
        -std::function<void()> m_cb
        -bool m_recurring
        -TimerManager* m_manager
        +bool cancel()
        +bool refresh()
        +bool reset(uint64_t ms, bool from_now)
        -Timer(uint64_t ms, std::function<void()> cb, bool recurring, TimerManager* manager)
        -Timer(uint64_t next)
        +struct Comparator
    }

    class TimerManager {
        -RWMutexType m_mutex
        -std::set<Timer::ptr, Timer::Comparator> m_timers
        -bool m_tickled
        -uint64_t m_previouseTime
        +TimerManager()
        +~TimerManager()
        +Timer::ptr addTimer(uint64_t ms, std::function<void()> cb, bool recurring = false)
        +Timer::ptr addConditionTimer(uint64_t ms, std::function<void()> cb, std::weak_ptr<void> weak_cond, bool recurring = false)
        +uint64_t getNextTimer()
        +void listExpiredCb(std::vector<std::function<void()>>& cbs)
        +bool hasTimer()
        +bool detectClockRollover(uint64_t now_ms)
        #virtual void onTimerInsertedAtFront() = 0
        #void addTimer(Timer::ptr val, RWMutexType::WriteLock& lock)
    }

    Timer --|> enable_shared_from_this
    Timer "1" -- "1" TimerManager : belongs to
    TimerManager o-- "many" Timer : manages

Timer 和 TimerManger 互为 友元类,Timer 持有 TimerManager* 成员变量⭐⭐⭐⭐⭐ 好处: Timer可以自己管理自身,通过Timer实例 cancel,refresh,reset。(无需通过TimerManager操作)

红黑树 m_timers

std::set<Timer::ptr, Timer::Comparator> m_timers;

calss Timer{
	/**
	 * 定时器比较仿函数,最小堆
	 */
	struct Comparator{
	   bool operator()(const Timer::ptr& lhs, const Timer::ptr& rhs) const{
			if(!lhs && !rhs){
		        return false;
		    }
		    // 空的放前面???
		    if(!lhs){
		        return true;
		    }
		
		    if(!rhs){
		        return false;
		    }
		
		    if(lhs->m_next < rhs->m_next){
		        return true;
		    }
		
		    if(lhs->m_next > rhs->m_next){
		        return false;
		    }
		
		    return lhs.get() < rhs.get();
		}
	};
};

bool Timer::refresh(); // 刷新设置定时器的执行时间,按照当前时间,定时周期不变 bool Timer::reset(uint64_t ms, bool from_now); // 重置定时器时间

添加定时器 ⭐⭐⭐⭐⭐

Timer::ptr TimerManager::addTimer(uint64_t ms, std::function<void()> cb, bool recurring){
    Timer::ptr timer(new Timer(ms, cb, recurring, this));
    RWMutexType::WriteLock lock(m_mutex);
    addTimer(timer, lock);
    return timer;
}

// 增加代码复用,让addConditionTimer 可以调用 addTiemr ⭐

// 若直接使用std::shared_ptr参数,
// 当定时器回调持有该shared_ptr时,会强制延长关联对象的生命周期,即使外部已不再需要该对象。⭐⭐⭐

// 用weak_ptr则不会增加引用计数,允许关联对象在外部引用归零时正常析构。⭐

// 这种模式常用于需要对象关联生命周期的定时任务,例如:
// 网络连接超时检测(当连接已关闭时无需触发超时回调)
// 资源释放校验(当资源持有者已销毁时取消清理操作)
static void OnTimer(std::weak_ptr<void> weak_ptr, std::function<void()> cb){
    std::shared_ptr<void> it = weak_ptr.lock();
    if(it){
        cb();
    }
}

Timer::ptr TimerManager::addConditionTimer(uint64_t ms, std::function<void()> cb, std::weak_ptr<void> weak_cond, bool recurring){
    return addTimer(ms, std::bind(&OnTimer, weak_cond, cb), recurring);
}

uint64_t TimerManager::getNextTimer(){
    RWMutexType::ReadLock lock(m_mutex);
    // 获取下一次最近执行事件的 相对事件,重置 m_tickle
    m_tickled = false;
    if(m_timers.empty()){
        return ~0ull;
    }

    const Timer::ptr& next = *m_timers.begin();
    uint64_t now_ms = GetCurrentTimeMS();
    if(now_ms >= next->m_next){
        return 0;
    } else {
        return next->m_next - now_ms;
    }
}

// 获取所有超时事件
void TimerManager::listExpiredCb(std::vector<std::function<void()> >& cbs){
    uint64_t now_ms = GetCurrentTimeMS();
    std::vector<Timer::ptr> expired;
    {
        RWMutexType::ReadLock lock(m_mutex);
        if(m_timers.empty()){
            return;
        }
    }

    RWMutexType::WriteLock lock(m_mutex);
    bool rollover = detectClockRollover(now_ms);
    if(!rollover && ((*m_timers.begin())->m_next > now_ms)){
        return;
    }

    Timer::ptr now_timer(new Timer(now_ms));
    // 获取到第一个 发生时间 < 现在。即需要执行的事件
    // 如果系统时间被往前调整了 1个小时,就把全部定时器的事件 返回。
    // 这个就比较粗暴了~ 
    auto it = rollover ? m_timers.end() : m_timers.lower_bound(now_timer);
    while(it != m_timers.end() && (*it)->m_next == now_ms){
        ++it;
    }

    expired.insert(expired.begin(), m_timers.begin(), it);
    m_timers.erase(m_timers.begin(), it);
    cbs.reserve(expired.size());


    for(auto& timer : expired){
        cbs.push_back(timer->m_cb);
        // 如果事件需要重复执行,再次写回 m_timers ⭐
        if(timer->m_recurring){
            timer->m_next = now_ms + timer->m_ms;
            m_timers.insert(timer);
        }else{
            timer->m_cb = nullptr;
        }
    }
}

// 添加 Timer,需要判断是否在堆顶。
// 如果在堆顶,此时需要onTimerInsertedAtFront(),唤醒 epoll_wait
void TimerManager::addTimer(Timer::ptr val, RWMutexType::WriteLock& lock){
    auto it = m_timers.insert(val).first;
    bool at_front = (it == m_timers.begin()) && !m_tickled;
    if(at_front){
        m_tickled = true;
    }
    lock.unlock();

    if(at_front){
        onTimerInsertedAtFront();
    }
}

bool TimerManager::hasTimer(){
    RWMutexType::ReadLock lock(m_mutex);
    return !m_timers.empty();
}

bool TimerManager::detectClockRollover(uint64_t now_ms){
    bool rollover = false;
    if(now_ms < m_previouseTime && now_ms < (m_previouseTime - 60 * 60 * 1000)){
        rollover = true;
    }
    m_previouseTime = now_ms;
    return rollover;
}

IOManager的调整

void IOManager::onTimerInsertedAtFront(){
    SYLAR_LOG_DEBUG(g_logger) << "onTimerInsertedAtFront() called";
    tickle();
}

bool IOManager::stopping(){
    uint64_t timeout = 0;
    return stopping(timeout);
}

bool IOManager::stopping(uint64_t& next_timeout){
    next_timeout = getNextTimer();
    return next_timeout== ~0ull && m_pendingEventCount == 0 && Scheduler::stopping();
}

void IOManager::idle(){
    SYLAR_LOG_DEBUG(g_logger) << "IOManager::idle() started";

    // 一次epoll_wait最多检测 256 个就绪事件
    const uint64_t MAX_EVENTS = 256;

    epoll_event* events = new epoll_event[MAX_EVENTS]();
    
    // 通过shared_ptr管理,避免内存泄漏 ⭐
    std::shared_ptr<epoll_event> shared_events(events, [](epoll_event* ev){
        delete[] ev;
    });

    while(true){
        // 获取下一个定时器的超时事件,顺便判断调度器是否停止。
        uint64_t next_timeout = 0;
        if(stopping(next_timeout)){
            SYLAR_LOG_DEBUG(g_logger) << "IOManager::idle() stopping, name=" << getName();
            break;
        }
        int rt = 0;
        do{
            static const int MAX_TIMEOUT = 5000;
            if(next_timeout != ~0ull){
                next_timeout = std::min((int)next_timeout, MAX_TIMEOUT); ⭐⭐⭐
            }else{
                next_timeout = MAX_TIMEOUT; 
            }
            SYLAR_LOG_DEBUG(g_logger) << "epoll_wait with timeout=" << next_timeout;
            rt = epoll_wait(m_epfd, events, MAX_EVENTS, (int)next_timeout);

            if(rt < 0){
                if(errno == EINTR){ //在任何请求的事件发生或超时到期之前,信号处理程序中断了该调用
                    SYLAR_LOG_WARN(g_logger) << "epoll_wait interrupted by signal, retrying";
                    continue;
                }
                SYLAR_LOG_ERROR(g_logger) << "epoll_wait error, errno=" << errno << ", errstr=" << strerror(errno);
                break;
            }else{
                break;
            }
        }while(true);

        // 退出epoll_wait
        std::vector<std::function<void()>> cbs;
        listExpiredCb(cbs); ⭐
        if(!cbs.empty()){
            for(const auto& cb: cbs){
                schedule(cb); // ⭐重新添加到调度队列,不直接调度
            }
            cbs.clear();
        }
	
		// 处理 pipe
		// 处理 事件,将对应事件的任务添加到调度队列
        ...
        
    } // end while(true)

    SYLAR_LOG_DEBUG(g_logger) << "IOManager::idle() exited";
}

知识点

定时器实现方式

  • 当前项目使用 epoll_wait 实现等待,查询 set 红黑树,将任务添加到调度器
  • libgo 通过 std::condition_variable 定时唤醒,查询跳表,将任务添加到调度器
  • 时间轮