libhv之hloop源码分析

1int hloop_run(hloop_t* loop)

hloop_run总结

1.loop状态判断和管理

2.loop的flags管理(HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS,HLOOP_FLAG_RUN_ONCE,HLOOP_FLAG_AUTO_FREE

3.创建custom event通信fd,方便loop进行custom event事件传递

4.hloop_process_events真正处理事件的函数

lua 复制代码
int hloop_run(hloop_t* loop) {
	//状态控制已经状态判断,loop基于单线程运行模式,所以状态无锁
    if (loop == NULL) return -1;
    if (loop->status == HLOOP_STATUS_RUNNING) return -2;

    loop->status = HLOOP_STATUS_RUNNING;
    loop->pid = hv_getpid();
    loop->tid = hv_gettid();
    
	//用户custom event通信,即pairsocket
    if (loop->intern_nevents == 0) {
        hmutex_lock(&loop->custom_events_mutex);
        if (loop->eventfds[EVENTFDS_WRITE_INDEX] == -1) {
            hloop_create_eventfds(loop);
        }
        hmutex_unlock(&loop->custom_events_mutex);

#ifdef DEBUG
        htimer_add(loop, hloop_stat_timer_cb, HLOOP_STAT_TIMEOUT, INFINITE);
        ++loop->intern_nevents;
#endif
    }

    while (loop->status != HLOOP_STATUS_STOP) {
        if (loop->status == HLOOP_STATUS_PAUSE) {
            hv_msleep(HLOOP_PAUSE_TIME);
            hloop_update_time(loop);
            continue;
        }
        ++loop->loop_cnt;
        
        //hloop标志,参考epool的flag
        //当没有激活事件的时候,退出loop
        if ((loop->flags & HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS) &&
            loop->nactives <= loop->intern_nevents) {
            break;
        }
		
		//真正处理事件的函数
        hloop_process_events(loop, HLOOP_MAX_BLOCK_TIME);
		
		//只运行一次,然后退出loop
        if (loop->flags & HLOOP_FLAG_RUN_ONCE) {
            break;
        }
    }

    loop->status = HLOOP_STATUS_STOP;
    loop->end_hrtime = gethrtime_us();
	
	//自动释放loop
    if (loop->flags & HLOOP_FLAG_AUTO_FREE) {
        hloop_cleanup(loop);
        HV_FREE(loop);
    }
    return 0;
}
2.int hloop_process_events(hloop_t* loop, int timeout_ms)

hloop_process_events总结

  1. 处理事件优先级:timer > io > idle
  2. 如果有timer事件并且timeout,则加入pendding队列,并且立马处理timer事件,跳过io事件
  3. 如果存在io并且没有触发timer的情况下,则检查io状态,如果io事件触发则加入pendding队列
  4. custom 事件不进入pendding队列,而是直接进入eventfd_read_cb
  5. 当不存在pendding事件,则触发idle
  6. 真正处理事件的是hloop_process_pendings
lua 复制代码
int hloop_process_events(hloop_t* loop, int timeout_ms) {
    // ios -> timers -> idles
    int nios, ntimers, nidles;
    nios = ntimers = nidles = 0;

    // calc blocktime
    int32_t blocktime_ms = timeout_ms;
    //如果存在定时器,则检查定时器是否超时,
    //由于这里是基于小根堆实现的定时器,所以只需要检查根节点是否超时即可
    if (loop->ntimers) {
        hloop_update_time(loop);
        int64_t blocktime_us = blocktime_ms * 1000;
        if (loop->timers.root) {
            int64_t min_timeout = TIMER_ENTRY(loop->timers.root)->next_timeout - loop->cur_hrtime;
            blocktime_us = MIN(blocktime_us, min_timeout);
        }
        if (loop->realtimers.root) {
            int64_t min_timeout = TIMER_ENTRY(loop->realtimers.root)->next_timeout - hloop_now_us(loop);
            blocktime_us = MIN(blocktime_us, min_timeout);
        }
		//如果已经超时,则说明定时器已经触发,直接处理定时器
        if (blocktime_us < 0) goto process_timers;
        blocktime_ms = blocktime_us / 1000 + 1;
        blocktime_ms = MIN(blocktime_ms, timeout_ms);
    }
	
	//如果存在io则处理iowatcher_poll_events,不同的模型,实现不一样
	//如果io事件被触发则加入pendding队列
    if (loop->nios) {
        nios = hloop_process_ios(loop, blocktime_ms);
    } else {
        hv_msleep(blocktime_ms);
    }
    hloop_update_time(loop);
    // wakeup by hloop_stop
    if (loop->status == HLOOP_STATUS_STOP) {
        return 0;
    }

process_timers:
	//处理定时器,并加入pendding队列,真正触发cb的是hloop_process_pendings
    if (loop->ntimers) {
        ntimers = hloop_process_timers(loop);
    }
	
	//如果不存pending事件,则触发idles事件
    int npendings = loop->npendings;
    if (npendings == 0) {
        if (loop->nidles) {
            nidles= hloop_process_idles(loop);
        }
    }
	//处理pendding事件
    int ncbs = hloop_process_pendings(loop);
    // printd("blocktime=%d nios=%d/%u ntimers=%d/%u nidles=%d/%u nactives=%d npendings=%d ncbs=%d\n",
    //         blocktime, nios, loop->nios, ntimers, loop->ntimers, nidles, loop->nidles,
    //         loop->nactives, npendings, ncbs);
    return ncbs;
}
3.int hloop_process_pendings(hloop_t* loop)

hloop_process_pendings总结

  1. 根据优先级遍历队列处理加入的pendding事件,如果存在回调则触发回调
lua 复制代码
int hloop_process_pendings(hloop_t* loop) {
    if (loop->npendings == 0) return 0;

    hevent_t* cur = NULL;
    hevent_t* next = NULL;
    int ncbs = 0;
    // NOTE: invoke event callback from high to low sorted by priority.
    //从高优先级遍历到低优先级,不同优先级的事件放在不同list
    for (int i = HEVENT_PRIORITY_SIZE-1; i >= 0; --i) {
        cur = loop->pendings[i];
        //遍历当前优先级下的所有事件,并且触发回调
        while (cur) {
            next = cur->pending_next;
            if (cur->pending) {
                if (cur->active && cur->cb) {
                    cur->cb(cur);
                    ++ncbs;
                }
                cur->pending = 0;
                // NOTE: Now we can safely delete event marked as destroy.
                if (cur->destroy) {
                    EVENT_DEL(cur);
                }
            }
            cur = next;
        }
        loop->pendings[i] = NULL;
    }
    loop->npendings = 0;
    return ncbs;
}

至此hloop_run的链路分析完毕。

总结

上述代码分析过程中,发现timer,io均是无锁模式(custom event除外)。得益于loop要求一个loop只能运行在一个线程中,避免了多线程竞争的问题。hloop_run基于单线程模式禁止跨线程使用loop,将触发事件加入pendding队列,最后由pendding队列统一处理cb。

问题:如果loop基于单线程模式,那么如果作为服务端是怎么实现高并发的呢?

答:参考libhv的 examples/multi-thread 目录下给出了几种常见的多线程/多进程模式的具体写法:

  • multi-acceptor-processes:多accept进程模式
  • multi-acceptor-threads:多accept线程模式
  • one-acceptor-multi-workers:一个accept线程+多worker线程
相关推荐
Duck Bro4 小时前
MySQL:常用数据类型
java·数据库·c++·mysql·java-ee
z千鑫4 小时前
【C/C++】数据库链接入门教程:从零开始的详细指南!MySQL集成与操作
c语言·数据库·c++
重生之我在VS写bug7 小时前
【C++知识总结2】C++里面的小配角cout和cin
数据结构·c++·算法
pzn25068 小时前
蓝桥杯练习题
c++·算法·蓝桥杯
Zafir20248 小时前
Qt实现窗口内的控件自适应窗口大小
c++·qt·ui
捕鲸叉8 小时前
C++设计模式之组合模式中适用缓存机制提高遍历与查找速度
c++·设计模式·组合模式
奶茶戒断高手9 小时前
【CSP CCF记录】201903-2第16次认证 二十四点
数据结构·c++·算法
scoone10 小时前
C++中的原子操作:原子性、内存顺序、性能优化与原子变量赋值
开发语言·c++
轩情吖10 小时前
模拟实现Bash
linux·c语言·开发语言·c++·后端·bash·环境变量
微尘811 小时前
C++条件编译指令:#if、#elif、#ifdef、#ifndef、#elifdef、#elifndef、#else、#endif
开发语言·c++