目录

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线程
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
咔咔咔的7 分钟前
3396. 使数组元素互不相同所需的最少操作次数
c++
小破农1 小时前
手撕 STL 之—— list
开发语言·c++
程序员老茶1 小时前
C++:STL的常用容器(string/vector/deque/stack/queue/list/set/multiset/map/multimap)
开发语言·c++·list
拾忆-eleven1 小时前
C++ 算法(2):STL list 完全解析,从入门到高效使用
开发语言·c++·算法
筱戥芊茹1 小时前
RK3588上Linux系统编译C/C++ Demo时出现BUG:The C/CXX compiler identification is unknown
linux·c语言·c++·嵌入式硬件·bug
黑不溜秋的2 小时前
C++ 编程指南36 - 使用Pimpl模式实现稳定的ABI接口
开发语言·c++
快乐老干妈3 小时前
STL-list链表
c++·链表·list
长沙红胖子Qt4 小时前
GStreamer开发笔记(二):GStreamer在ubnutn平台部署安装,测试gstreamer/cheese/ffmpeg/fmplayer打摄像头延迟
c++·开源·产品
菜树人4 小时前
c/c++ 使用libgeotiff读取全球高程数据ETOPO
c语言·c++
刚入坑的新人编程4 小时前
C++STL——容器-list(含模拟实现,即底层原理)(含迭代器失效问题)(所有你不理解的问题,这里都有解答,最详细)
开发语言·c++·链表·list