libevent源码学习(4):线程锁、条件变量(二)(调试锁)

 目录

EVUTIL_ASSERT宏

开启调试锁

调试锁结构

调试锁函数

debug_lock_alloc

debug_lock_free

debug_lock_lock

加锁检测

debug_lock_unlock

解锁检测

调试锁下的条件变量函数

总结


以下源码均基于libevent-2.0.21-stable。

       在上一章中,分析了线程锁函数以及条件变量函数,其中遗留了一些问题,需要分析调试锁才能得到解决,因此本文就主要分析调试锁。 调试锁是libevent中用户可选的一种模式,它不仅可以调用前面设置的锁函数和条件变量函数,还可以捕获使用锁时的典型错误:重新锁定一个已锁定的非递归锁解锁一个并未持有的锁

EVUTIL_ASSERT宏

        在正式分析之前,先来说一个libevent中经常用的宏EVUTIL_ASSERT。

        该宏定义如下:

#define EVUTIL_ASSERT(cond)						\
	do {								\
		if (EVUTIL_UNLIKELY(!(cond))) {				\
			event_errx(_EVENT_ERR_ABORT,			\
			    "%s:%d: Assertion %s failed in %s",		\
			    __FILE__,__LINE__,#cond,__func__);		\
			/* In case a user-supplied handler tries to */	\
			/* return control to us, log and abort here. */	\
			(void)fprintf(stderr,				\
			    "%s:%d: Assertion %s failed in %s",		\
			    __FILE__,__LINE__,#cond,__func__);		\
			abort();					\
		}							\
	} while (0)

        在if中调用了一个event_errx函数进行错误提示(可参考libevent日志及错误处理),然后终止进程,因此EVUTIL_ASSERT宏的作用实际上就是进行错误判断。那什么情况认为是发生了错误呢?

        if的判断条件是EVUTIL_UNLIKELY(!(cond)),这又是一个宏,该宏的定义如下:

#define EVUTIL_UNLIKELY(p) __builtin_expect(!!(p),0)

        __builtin_expect其实就是做一个分支预测优化,括号内的第一个参数是一个表达式EXP,第二个参数是一个值N,__builtin_expect的作用就是告诉编译器,EXP==N的概率更大,这样编译器就会进行优化,将更有可能的分支指令放在前面,从而降低跳转指令带来的效率影响。对于这里来说,就相当于告诉编译器,!!(p)这个表达式(第一个参数)的值更可能是0(第二个参数)。这里使用!!(p)的作用是将p转换为布尔值0或1。

       因此,EVUTIL_UNLIKELY宏实际上并不会改变参数的值,它只是根据参数来让编译器进行一种优化,如果不管这种优化,完全就可以无视这个宏,if的判断条件就是!(cond)。

       因此,EVUTIL_ASSERT(p)实际上是当p为假时报错并终止进程,当p为真时则继续执行。

       宏定义中还用到了do{}while(0);这也是一种比较巧妙的用法,可以参考do{}while(0)的好处。       

开启调试锁

        开启调试锁的函数evthread_enable_lock_debuging,用户可直接调用,其定义如下:

void
evthread_enable_lock_debuging(void)//开启调试锁后,evthread_lock_callbacks里的锁函数指针以及_evthread_cond_fns全部变为调试锁相关的了。
{
	struct evthread_lock_callbacks cbs = {
		EVTHREAD_LOCK_API_VERSION,
		EVTHREAD_LOCKTYPE_RECURSIVE,
		debug_lock_alloc,
		debug_lock_free,
		debug_lock_lock,
		debug_lock_unlock
	};
	if (_evthread_lock_debugging_enabled)  //如果已经开启了调试锁,就直接返回
		return;
	memcpy(&_original_lock_fns, &_evthread_lock_fns,
	    sizeof(struct evthread_lock_callbacks));   //_original_lock_fns = _evthread_lock_fns    保存原本的锁函数结构
	memcpy(&_evthread_lock_fns, &cbs,
	    sizeof(struct evthread_lock_callbacks));   //_evthread_lock_fns = cbs   将调试锁函数赋值给_evthread_lock_fns

	memcpy(&_original_cond_fns, &_evthread_cond_fns,   //_original_cond_fns = _evthread_cond_fns  保存原本的条件变量函数结构
	    sizeof(struct evthread_condition_callbacks));
	_evthread_cond_fns.wait_condition = debug_cond_wait;  //将调试锁的条件变量函数赋值给_evthread_lock_fns
	_evthread_lock_debugging_enabled = 1;

	/* XXX return value should get checked. */
	event_global_setup_locks_(0);
}

       在该函数中,首先定义了一个锁函数结构体cbs,其中已经设定好了一系列的调试锁相关的锁函数。

       接着会判断_evthread_lock_debugging_enabled是否为真,_evthread_lock_debugging_enabled的定义如下:

GLOBAL int _evthread_lock_debugging_enabled = 0;    //如果调用了evthread_enable_lock_debuging,那么该变量置为1

        posix下,GLOBAL是一个空宏,而在windows下GLOBAL宏定义为static,因此这里的_evthread_lock_debugging_enabled就是一个全局变量,初始为0。可以看到,在evthread_enable_lock_debuging函数的末尾将_evthread_lock_debugging_enabled置1,而evthread_enable_lock_debuging函数用于开启调试锁。因此,_evthread_lock_debugging_enabled实际上就是一个标志位,标识是否调用了evthread_enable_lock_debuging函数,也就是说,_evthread_lock_debugging_enabled为1时表示开启调试锁,为0时表示没有开启调试锁。

        这样第一个if语句就表示如果多次开启调试锁,函数就会直接返回。

        接下来是三句memcpy:将_evthread_lock_fns的值赋值给_original_lock_fns;将cbs的值保存到_evthread_lock_fns;将_evthread_cond_fns的值赋值给_original_cond_fns。其中第二句memcpy,将调试锁相关的锁函数保存到了_evthread_lock_fns中,也就是说,在开启调试锁之后,_evthread_lock_fns中存放的是调试锁相关的锁函数(debug_lock_alloc...)。同样的&

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值