__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*/中的临时变量声明周期的提供了一个的区块。让它在这个地方出在后再死去,因为加锁调用而出生,死去时它会自己销毁自己。
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);