libevent源码学习(13):事件主循环event_base_loop

本文详细介绍了libevent的事件主循环event_base_loop的工作原理,包括开启事件主循环、校对时间、处理激活队列中的事件、阻塞/非阻塞模式,以及事件主循环的退出机制event_base_loopexit和event_base_loopbreak。通过对libevent源码的分析,揭示了事件循环如何在不同模式下执行和退出。
摘要由CSDN通过智能技术生成

 

目录

开启事件主循环

执行事件主循环

校对时间 

阻塞/非阻塞

处理激活队列中的event

事件主循环的退出

event_base_loopexit

event_base_loopbreak


开启事件主循环

       在libevent中,事件主循环的作用就是执行一个循环,在循环中监听事件以及超时的事件并且将这些激活的事件进行处理。libevent提供了对用户开放了两种执行事件主循环的函数:

int event_base_dispatch(struct event_base *);

int event_base_loop(struct event_base *, int);

       在event_base_dispatch函数中,实际上调用的是event_base_loop(event_base, 0);也就是如果使用event_base_dispatch函数执行事件主循环,那么会将event_base_loop的第二个参数设置为0去调用它,下面来看看event_base_loop函数的定义:

执行事件主循环

int
event_base_loop(struct event_base *base, int flags)  
{
	//设置为EVLOOP_NONBLOCK,那么event_loop只会处理当前已经激活的event,处理结束后就会退出event_loop
	//设置为EVLOOP_ONCE,那么event_loop就会等待到第一个事件超时,处理在这段时间内激活的event,直到所有激活的事件都处理完就退出event_loop
	//设置为其他值,那么就会一直循环监听,直到被动退出
	const struct eventop *evsel = base->evsel;  //获取使用的后端
	struct timeval tv;
	struct timeval *tv_p;
	int res, done, retval = 0;

	/* Grab the lock.  We will release it inside evsel.dispatch, and again
	 * as we invoke user callbacks. */
	EVBASE_ACQUIRE_LOCK(base, th_base_lock);   //event_base初始化后默认分配的是递归锁

	if (base->running_loop) {
		event_warnx("%s: reentrant invocation.  Only one event_base_loop"
		    " can run on each event_base at once.", __func__);
		EVBASE_RELEASE_LOCK(base, th_base_lock);
		return -1;
	}

	base->running_loop = 1;

	clear_time_cache(base);   //清空缓存的超时

	if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
		evsig_set_base(base);

	done = 0;

#ifndef _EVENT_DISABLE_THREAD_SUPPORT
	base->th_owner_id = EVTHREAD_GET_ID(); //存放执行base主循环的线程id
#endif

	base->event_gotterm = base->event_break = 0;

	while (!done) {
		base->event_continue = 0;

		/* Terminate the loop if we have been asked to */
		if (base->event_gotterm) {
			break;
		}

		if (base->event_break) {
			break;
		}

		timeout_correct(base, &tv);  //用来检查用户是否回调了系统时间,如果改变了就往回调整系统时间

		tv_p = &tv; //回调后的时间

		//如果当前没有事件激活,那么后面的dispatch就应该阻塞,如果不阻塞那么就会多次调用dispatch;
		//而对于flags来说,如果把flags设置为EVLOOP_NONBLOCK,那么说明调用者希望后面的dispatch是非阻塞的
		//因此如果当前没有事件激活的,那么flags就不应该设置为EVLOOP_NONBLOCK;
		//除此之外的其他情况则会将dispatch设置为非阻塞。
		//dispatch是否阻塞取决于传入的超时参数,超时参数描述了dispatch阻塞的时长,如果为0那么dispatch就立即返回,如果为-1就是一直阻塞,直到相应事件发生。
		//因此这里如果需要阻塞,那么就设置dispatch的阻塞时长为从现在开始到第一个超时的event所需的时间
		//否则则设置阻塞时长为0,相当于非阻塞。
		if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {
			timeout_next(base, &tv_p); //计算min_heap中最先超时的event还有多长时间就要超时
		} else {//如果有事件激活或者是event_loop为非阻塞,就不用等待
			/*
			 * if we have active events, we just poll new events
			 * without waiting.
			 */
			evutil_timerclear(&tv);  //清空超时
		}

		/* If we have no events, we just exit */
		if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {//如果base中的所有event都处理完了就退出,如果base中存在永久事件,那么event_loop是不会主动退出的
			event_debug(("%s: no events registered.", __func__));
			retval = 1;
			goto done;
		}

		/* update last old time */
		gettime(base, &base->event_tv);  //更新系统时间

		clear_time_cache(base); //清空缓存的时间

		//根据前面,如果希望dispatch是非阻塞的,那么这里的tv_p就是0,该函数就会立即返回;
		//如果希望dispatch是阻塞的,那么这里的tv_p就是距离第一个超时event的剩余时长
		//这是为了处理在所有事件都没有超时的情况下可能发生的感兴趣事件
		//如果在这段时间内没有感兴趣的事件发生,实际上这里的dispatch也并没有对事件做任何处理
		res = evsel->dispatch(base, tv_p); 
		//返回后,接下来就是处理超时的事件了

		if (res == -1) {
			event_debug(("%s: dispatch returned unsuccessfully.",
				__func__));
			retval = -1;
			goto done;
		}

		update_time_cache(base);

		timeout_process(base);  //将base的min_heap中所有超时的事件以超时激活类型添加到激活队列中

		if (N_ACTIVE_CALLBACKS(base)) {  //如果激活队列中有事件
			int n = event_proce
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值