libco是腾讯微信开源的C/C++实现的协程库,在微信后台有大规模应用。
在早期微信后台业务逻辑实现中,大量采用了多进程或多线程同步模型。随着业务量不断增大,同步模型的弊端日益显现,在这种场景下,业务系统数据处理能力及整理吞吐量往往非常低。
为了解决此类问题,后台业务系统必须进行大规模改造,改造的方式有两种:
前一种方式往往要求将现有系统中所有同步模型全部替换为异步模型,无异于将整个系统从框架层开始全部重写,风险显然比较高。而后一种方式,由于hook了socket API,因此将对老系统代码的侵入性降到了最低。
libco的项目背景及整体设计思想,可以参考《C/C++协程库libco:微信怎样漂亮地完成异步化改造》一文。
libco中提供了一份简单的闭包实现,接下去会分段进行分析。
代码片段一:
#ifndef __CO_CLOSURE_H__
#define __CO_CLOSURE_H__
struct stCoClosure_t
{
public:
virtual void exec() = 0;
};
这段代码本质上定义了Closure的基类,闭包在调用时,最终会调用exec()函数。
代码片段二:
//1.base
//-- 1.1 comac_argc
#define comac_get_args_cnt( ... ) comac_arg_n( __VA_ARGS__ )
#define comac_arg_n( _0,_1,_2,_3,_4,_5,_6,_7,N,...) N
#define comac_args_seqs() 7,6,5,4,3,2,1,0
#define comac_join_1( x,y ) x##y
#define comac_argc( ... ) comac_get_args_cnt( 0,##__VA_ARGS__,comac_args_seqs() )
#define comac_join( x,y) comac_join_1( x,y )
上面定义的6个宏主要分为以下两类:
comac_argc宏展开举例:
comac_argc( A, B ) -> comac_get_args_cnt( 0, A, B, 7,6,5,4,3,2,1,0 ) -> comac_arg_n( 0, A, B, 7,6,5,4,3,2,1,0 ) -> 2
__VA_ARGS__代表宏参数是可变的,此处为什么需要添加前缀##呢?
补充说明:
为什么需要在__VA_ARGS__前添加##?
在gcc中,前缀##有一个特殊约定,即当##arg前面是逗号(,)时,如果arg为空,则##之前的逗号(,)将会被自动省去。
因此,当comac_argc()不填写任何参数时,宏将会被按照以下方式展开:
comac_argc( ) -> comac_get_args_cnt( 0, 7,6,5,4,3,2,1,0 ) -> comac_arg_n( 0, 7,6,5,4,3,2,1,0 ) -> 0
但是,对于C++11(-std=c++11),如果##arg中的arg为空,则##arg将会被默认转换为空字符串(""),此时,宏将会按照下面的方式展开:
comac_argc( ) -> comac_get_args_cnt( 0, "", 7,6,5,4,3,2,1,0 ) -> comac_arg_n( 0, "", 7,6,5,4,3,2,1,0 ) -> 1
代码片段三:
//-- 1.2 repeat
#define repeat_0( fun,a,... )
#define repeat_1( fun,a,... ) fun( 1,a,__VA_ARGS__ ) repeat_0( fun,__VA_ARGS__ )
#define repeat_2( fun,a,... ) fun( 2,a,__VA_ARGS__ ) repeat_1( fun,__VA_ARGS__ )
#define repeat_3( fun,a,... ) fun( 3,a,__VA_ARGS__ ) repeat_2( fun,__VA_ARGS__ )
#define repeat_4( fun,a,... ) fun( 4,a,__VA_ARGS__ ) repeat_3( fun,__VA_ARGS__ )
#define repeat_5( fun,a,... ) fun( 5,a,__VA_ARGS__ ) repeat_4( fun,__VA_ARGS__ )
#define repeat_6( fun,a,... ) fun( 6,a,__VA_ARGS__ ) repeat_5( fun,__VA_ARGS__ )
#define repeat( n,fun,... ) comac_join( repeat_,n )( fun,__VA_ARGS__)
上面这些宏主要用于定义重复操作。在后文中有语句举例。
代码片段四:
//2.implement
#if __cplusplus <= 199711L
#define decl_typeof( i,a,... ) typedef typeof( a ) typeof_##a;
#else
#define decl_typeof( i,a,... ) typedef decltype( a ) typeof_##a;
#endif
#define impl_typeof( i,a,... ) typeof_##a & a;
#define impl_typeof_cpy( i,a,... ) typeof_##a a;
#define con_param_typeof( i,a,... ) typeof_##a & a##r,
#define param_init_typeof( i,a,... ) a(a##r),
1. decl_typeof:主要用于获取变量a的类型。例如:
int a = 100;
decl_typeof( 1,a);
==>
int a = 100;
typedef typeof(a) typeof_a;
2. impl_typeof:主要用于创建一个和变量a的类型相同的引用。
3. impl_typeof_copy:主要用于创建一个和变量a类型相同的变量。
4. con_param_typeof:用于生成类构造函数入参。
5. param_init_typeof:用于生成类构造函数初始化列表。
代码片段五:
//2.1 reference
#define co_ref( name,... )\
repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )\
class type_##name\
{\
public:\
repeat( comac_argc(__VA_ARGS__) ,impl_typeof,__VA_ARGS__ )\
int _member_cnt;\
type_##name( \
repeat( comac_argc(__VA_ARGS__),con_param_typeof,__VA_ARGS__ ) ... ): \
repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__)) \
{}\
} name( __VA_ARGS__ ) ;
上面这段宏定义,主要用于产生协程入参。例如,有以下一段代码:
int a = 100;
co_ref(ref,a);
经过宏展开后,代码如下:
int a = 100;
typedef typeof(a) typeof_a;
class type_ref
{
public:
typeof_a & a;
int _member_cnt;
type_ref(typeof_a & ar, ...)
: a(ar), _member_cnt(1)
{
}
} ref(a);
本质上,就是构造了一个type_ref类,持有了对栈上变量a的引用。
_member_cnt(1)中,成员个数"1"实际上是由宏comac_argc自动推导出来的。
代码片段六:
//2.2 function
#define co_func(name,...)\
repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )\
class name:public stCoClosure_t\
{\
public:\
repeat( comac_argc(__VA_ARGS__) ,impl_typeof_cpy,__VA_ARGS__ )\
int _member_cnt;\
public:\
name( repeat( comac_argc(__VA_ARGS__),con_param_typeof,__VA_ARGS__ ) ... ): \
repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__))\
{}\
void exec()
#define co_func_end }
上述宏创建一个协程,代码举例如下:
#include "co_closure.h"
#include
int main()
{
int a = 100;
co_ref(ref, a);
co_func(f, ref)
{
printf("hello, world!\n");
}
co_func_end;
}
宏展开之后,代码生成如下。可以看到,co_func定义的协程,引用了co_ref定义的参数。
int main()
{
int a = 100;
typedef typeof(a) typeof_a;
class type_ref
{
public:
typeof_a & a;
int _member_cnt;
type_ref(typeof_a & ar, ...) : a(ar), _member_cnt(1)
{
}
} ref(a);;
typedef typeof(ref) typeof_ref;
class f : public stCoClosure_t
{
public:
typeof_ref ref;
int _member_cnt;
public:
f(typeof_ref & refr, ...) : ref(refr), _member_cnt(1)
{
}
void exec()
{
printf("hello, world!\n");
}
};
}
co_func经过宏展开后,生成了一个名称为f的类。只要创建这个类的实例,然后调用exec()方法,即可运行协程。