__attribute__ (( unused,__cleanup__)),FRR的互斥锁

        今天看了下FRR中常见的一个锁实现,当然离开不了其中强大的宏frr_with_mutex。以一个示例慢慢揭开它的面纱吧。

    frr_with_mutex(&m->mtx) {
		if (XXX)
			break;

		/*
        *对m的操作
        */

		//AWAKEN(m);
	}

       为了知道frr_with_mutex是什么,接下来是一层层扒开宏,接下来的宏可能比较枯燥,不愿细看推倒可以直接看后面宏结果。

/* mutex auto-lock/unlock */

/* variant 1:
 * (for short blocks, multiple mutexes supported)
 * break & return can be used for aborting the block
 *
 * frr_with_mutex(&mtx, &mtx2) {
 *    if (error)
 *       break;
 *    ...
 * }
 */
#define _frr_with_mutex(mutex)                                                 \
	*NAMECTR(_mtx_) __attribute__((                                        \
		unused, cleanup(_frr_mtx_unlock))) = _frr_mtx_lock(mutex),     \
	/* end */

#define frr_with_mutex(...)                                                    \
	for (pthread_mutex_t MACRO_REPEAT(_frr_with_mutex, ##__VA_ARGS__)      \
	     *_once = NULL; _once == NULL; _once = (void *)1)                  \
	/* end */



/* variadic macros, use like:
 * #define V_0()  ...
 * #define V_1(x) ...
 * #define V(...) MACRO_VARIANT(V, ##__VA_ARGS__)(__VA_ARGS__)
 */
#define _MACRO_VARIANT(A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10, N, ...) N

#define _CONCAT2(a, b) a ## b
#define _CONCAT(a, b) _CONCAT2(a,b)

#define MACRO_VARIANT(NAME, ...) \
	_CONCAT(NAME, _MACRO_VARIANT(0, ##__VA_ARGS__, \
			_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _0))

#define NAMECTR(name) _CONCAT(name, __COUNTER__)

/* per-arg repeat macros, use like:
 * #define PERARG(n) ...n...
 * #define FOO(...) MACRO_REPEAT(PERARG, ##__VA_ARGS__)
 */

#define _MACRO_REPEAT_0(NAME)
#define _MACRO_REPEAT_1(NAME, A1) \
	NAME(A1)
#define _MACRO_REPEAT_2(NAME, A1, A2) \
	NAME(A1) NAME(A2)
#define _MACRO_REPEAT_3(NAME, A1, A2, A3) \
	NAME(A1) NAME(A2) NAME(A3)
#define _MACRO_REPEAT_4(NAME, A1, A2, A3, A4) \
	NAME(A1) NAME(A2) NAME(A3) NAME(A4)
#define _MACRO_REPEAT_5(NAME, A1, A2, A3, A4, A5) \
	NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5)
#define _MACRO_REPEAT_6(NAME, A1, A2, A3, A4, A5, A6) \
	NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6)
#define _MACRO_REPEAT_7(NAME, A1, A2, A3, A4, A5, A6, A7) \
	NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) NAME(A7)
#define _MACRO_REPEAT_8(NAME, A1, A2, A3, A4, A5, A6, A7, A8) \
	NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) NAME(A7) NAME(A8)

#define MACRO_REPEAT(NAME, ...) \
	MACRO_VARIANT(_MACRO_REPEAT, ##__VA_ARGS__)(NAME, ##__VA_ARGS__)

相关函数有:

static inline pthread_mutex_t *_frr_mtx_lock(pthread_mutex_t *mutex)
{
	pthread_mutex_lock(mutex);
	return mutex;
}

static inline void _frr_mtx_unlock(pthread_mutex_t **mutex)
{
	if (!*mutex)
		return;
	pthread_mutex_unlock(*mutex);
	*mutex = NULL;
}

    其中__COUNTER__是一个从 0 开始计数,然后每次调用加 1的计数器。

    经过一番推敲,得到了以下代码:

//frr_with_mutex(mutex)即
for (pthread_mutex_t *NAMECTR(_mtx_)                                           /* 1 */
    __attribute__((unused, cleanup(_frr_mtx_unlock))) = _frr_mtx_lock(mutex),  /* 2 */
    *_once = NULL;_once == NULL; _once = (void *)1)                            /* 3 */

1:NAMECTR(_mtx_) 是一个名字变的变量名,__COUNTER__每次调用则加1,所以每次用这个宏,这个临时指针变量名就是_mtx_0,_mtx_1,_mtx_2...,后记_mtx_X

2:编译器属性__attribute__用于向编译器描述特殊的标识、检查或优化,这里用到了unused和cleanup可选属性值。

  • 如果定义了一个变量而不使用,有的编译器会有告警。__attribute__((unused))可以告诉编译器忽略此告警。
  • __attribute__ ((cleanup(function))) 的作用是  当其修饰的变量离开了其生命周期,那么会自动调用所指定的销毁函数function。这里是先调用_frr_mtx_lock(mutex),将其返回值mutex传给了临时指针变量_mtx_X,然后在for循环结束时,调用_frr_mtx_unlock销毁这个锁。

    __attribute__((unused, cleanup(_frr_mtx_unlock)))在这里是一个整体,放在NAMECTR(_mtx_) 变量后面用于修饰该变量,pthread_mutex_t *NAMECTR(_mtx_) = _frr_mtx_lock(mutex)是一个函数调用。

     注意一个细节,临时变量(一级指针)再传给销毁函数时,传的是二级指针。

3:这个_once的定义、判断和赋值,没有实际的意义,就是给/*2*/中的临时变量_mtx_X声明周期的提供了一个的区块。让_mtx_X在这个地方出生后再死去,因为加锁调用而生,死去时_mtx_X会自己销毁自己。

     frr_with_mutex宏实际控制的区块就是这个for循环的循环体,也就是这个锁的整个生命周期。不得不说,这里选中for而不是while的一个巧妙之处,可以自行控制锁的影响范围。


    综上,不得不说,这些个复杂的宏背后干了太多事,最大的意图就是自动干程序员可能会忘的一件事:unlock锁。同时还可以自由控制锁影响的区块大小。学习啦。

    此外提一下最开始示例代码中的AWAKEN(m),后期学习用:

#define AWAKEN(m)                                                              \
	do {                                                                   \
		static unsigned char wakebyte = 0x01;                          \
		write(m->io_pipe[1], &wakebyte, 1);                            \
	} while (0);

猜你喜欢

转载自blog.csdn.net/sy_123a/article/details/108624073
今日推荐