tencent / libco Goto Github PK
View Code? Open in Web Editor NEWlibco is a coroutine library which is widely used in wechat back-end service. It has been running on tens of thousands of machines since 2013.
License: Other
libco is a coroutine library which is widely used in wechat back-end service. It has been running on tens of thousands of machines since 2013.
License: Other
原实现:
void co_free( stCoRoutine_t *co )
{
if (!co->cIsShareStack)
{
free(co->stack_mem->stack_buffer);
free(co->stack_mem);
}
free( co );
}
建议修改为:
void co_free( stCoRoutine_t *co )
{
if (!co->cIsShareStack)
{
free(co->stack_mem->stack_buffer);
free(co->stack_mem);
} else {
if (co->stack_mem->occupy_co == co) {
co->stack_mem->occupy_co = NULL;
}
if (co->save_buffer)
{
free(co->save_buffer), co->save_buffer = NULL;
}
}
free( co );
}
在AddTimeout出错的情况下,应该把前面已经加入到epoll的fd删除掉,否则这里返回了,而epoll调用回来的时候指针已经是野指针了。尽管AddTimeout出错的情况比较罕见,但是我确实是碰到了。
原实现:
if (update_occupy_co && update_pending_co && update_occupy_co != update_pending_co)
{
...
}
当使用共享栈协程,且当有协程回收时,update_occupy_co有可能为NULL,此时进不了条件中执行协程栈恢复,建议改为:
if (update_pending_co && update_occupy_co != update_pending_co)
{
...
}
get compile error on mac osx:
co_hook_sys_call.cpp:865:15: error: use of undeclared identifier 'gethostbyname_r'
while (ret = gethostbyname_r(name, host, __co_hostbuf_wrap->buffer,
I can't find symbol 'gethostbyname_r' on whole proj.
如题
1.在未调用co_init_curr_thread_env函数前co_enable_hook_sys不能设置成功,而co_init_curr_thread_env的声明并未和co_enable_hook_sys的声明放在同一个头文件中,且co_enable_hook_sys没有返回值,不知道是否设置成功;
2.co_enable_hook_sys设置不成功时,调用socket函数未申请fd-info-buffer(alloc_by_fd函数),后面如果co_enable_hook_sys设置成功,但是是无法hook系统函数的(找不到fd-info-buffer,使用默认方法),这样对第三方库十分不友好(有些库必须需要在主线程初始化,且要使用默认函数),因此建议函数socket、fcntl、close不管什么时候都是hook的。
看作者都一样, 会分开维护吗? 还是哪个要被放弃了
现在libco 是一种类lua coroutine 的单线程模式
有些场景用多线程模型还是更方便,考虑支持类 Go/Erlang 的调度器执行模型:
这样引入一些带有block系统调用但耗时可控的第三方库也没啥问题
比如我调用了第三方的库,其中有个write函数是阻塞的,可以被hook吗?
谢谢。
建议对外开放GetPid方法
修改:
将co_hook_sys_call.cpp中的GetPid方法删除,将co_routine.cpp中的GetPid方法的static修饰去掉,并在co_routine.h中加入GetPid方法声明
It seems like co_free/co_release not properly free memory, and cause segment fault.
代码如下
`#include "co_routine.h"
#include "co_routine_inner.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
#include <unistd.h>
stCoRoutine_t* co;
static thread_local int aaa;
void* co_fun(void *)
{
printf("co_fun AAAAAA\n");
printf("aaa 0x%x\n", &aaa);
co_yield(co);
printf("co_fun BBBBBB\n");
printf("aaa 0x%x\n", &aaa);
}
void* thread_fun(void *)
{
printf("thread_fun AAAAAA\n");
co_init_curr_thread_env();
printf("thread_fun BBBBBB\n");
co_resume(co);
printf("thread_fun CCCCCC\n");
return NULL;
}
int main(int argc, char** argvs)
{
co_create(&co, NULL, co_fun, NULL);
co_resume(co);
pthread_t tid;
pthread_create(&tid,NULL,thread_fun,0);
for(;;)
{
sleep(1);
}
}`
aaa这个thread_local变量打印的值是一样的。
一个协程执行到一半yield了,然后由另一个线程继续执行(比如一个同步rpc调用场景),那么里面的thread_local变量会很危险,特别是一些第三方库使用了thread_local后,这个怎么破?
另外CMake中没有设置C++的编译选项,实际上整个项目都是按照O1编译的,上面的例子需要把C++的编译选项加上O2
static inline rpchook_t * alloc_by_fd( int fd )
{
if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) )
{
rpchook_t *lp = (rpchook_t*)calloc( 1,sizeof(rpchook_t) );
lp->read_timeout.tv_sec = 1;
lp->write_timeout.tv_sec = 1;
g_rpchook_socket_fd[ fd ] = lp;
return lp;
}
return NULL;
}
if fd > 102400,return NULL,but caller not handle this,the program will crash?
int socket(int domain, int type, int protocol)
{
HOOK_SYS_FUNC( socket );
if( !co_is_enable_sys_hook() )
{
return g_sys_socket_func( domain,type,protocol );
}
int fd = g_sys_socket_func(domain,type,protocol);
if( fd < 0 )
{
return fd;
}
rpchook_t *lp = alloc_by_fd( fd );
lp->domain = domain;//if lp==NULL,will crash
fcntl( fd, F_SETFL, g_sys_fcntl_func(fd, F_GETFL,0 ) );
return fd;
}
既然 coroutine 是用户态的,那么就有个问题:coroutine 调度是抢占式的吗?
谢谢!
看到coctx下的 ESP = (ss_sp + ss_size - sizeof(coctx_param_t)) & -16L - sizeof(void*)
怎么理解这个计算方法
如题。
协程1 和 协程2使用同一连接(长连接fd)调用其他服务。
收到包时,如何区分切换到协程1还是协程2呢?
因为很多第三方库(比如hiredis或者mysql c driver) 创建连接时使用的是同步connect的函数, 接入libco后,mysql c driver 调用 mysql_real_connect 会返回115错误,调试发现当调用到hook的connect函数后,直接返回ret -1, errno为115。
我觉得这里是不是应该模拟同步connect时的ret, 即发起连接请求后立即调用co_poll yield出去,等待connect成功后再回到这里返回正确的ret。
现在的实现导致mysql c driver无法正确连接成功。
README介绍:
libco 支持gethostbyname、mysqlclient、ssl等常用第三库(New) , 是怎么支持的? 有通过libco修改后mysqlclient的吗?
libco里文档太少了, 接口都不知道怎么用, 有没有详细的API文档呢?
如题,为什么只压栈了其他寄存器
例如:
void fun(void *)
{
co_create(&co, null, fun, null);
co_resume(co);
}
int main(int argc, char** argvs)
{
stCoroutinue* co;
co_create(&co, null, fun, null);
co_resume(co);
}
accept没有被HOOK吧.
在example_echosvr中启动后accept_routine的co_accept返回<0, 会不断调用co_accept.
为什么这么处理呢, 为什么不是accept_routine在co_accept没有连接进入时候挂起呢?
CoRoutineFunc函数原型设计成static int CoRoutineFunc( stCoRoutine_t *co,void * ),为什么要有第2个参数呢?
https://github.com/Tencent/libco/blob/master/coctx.cpp#L96
swap的时候,会把ebp设置成0
fixes/modifications:
Fork: https://github.com/abhinavabcd/libco
https://github.com/Tencent/libco/compare/master...abhinavabcd:master?diff=split&name=master
首先感谢开源这么有价值的库。
在使用过程中我发现通过co_create创建一个协程上下文时,并没有初始化aSpec, 从而导致使用CO_ROUTINE_SPECIFIC 宏时会出现获取到一个未定义指针的异常。
我通过手动清零的方式规避了这个问题。
请问这个问题是bug还是有其他考量的地方?
能给出一些详细点的使用文档吗
该库的协程是否支持C++异常机制?
据我所知,机器通常会在线程栈上构建有异常保护帧;按理如果协程能完整保护/恢复线程栈的话应该没问题,但是这个在该库中是设计特性呢 还是实现的特性,似乎不明确。以及如果某协程抛出未处理的异常,会使整个协程系统中止工作么?
还有假如在异步过程中抛出异常,协程是否会接收该异常呢? 譬如
co_routine code block piece:
...
try
{
co_xxx(); // 如果这里边有异常抛出,协程能接收到么
} catch(XxException& ex)
{
}
有一些业务代码是用libcurl写的,是同步模式,想利用libco改造为异步。
这种场景下,只是利用hook的方式就可以吗?还是需要修改业务代码?
以example_echosvr.cpp代码为例,首先没有hook系统的accept函数,需要调用自命名的co_accept函数。其次在调用co_accept返回小于0的fd的时候,需要自己手动调用co_poll。
为什么这里不对系统的accept函数进行hook,同时在调用accept函数返回小于0fd的时候,库自己内部将这个fd加入poll中呢?因为会出现这种情况,一个服务使用的是另一个二进制库,这个二进制库负责对外接收连接,无法侵入修改它的代码来适配libco的代码。
同样的问题也存在read、write等调用中,还是这个example_echosvr.cpp代码,在处理协程中,首先需要使用者调用co_poll将需要进行read操作的fd加入poll中,仍然对原有的代码造成了侵入。
void save_stack_buffer(stCoRoutine_t* occupy_co)
{
///copy out
stStackMem_t* stack_mem = occupy_co->stack_mem;
int len = stack_mem->stack_bp - occupy_co->stack_sp;
// 为什么直接就释放了,是否可以判断一下,如果当前的空间足够,则直接复用
if (occupy_co->save_buffer)
{
free(occupy_co->save_buffer), occupy_co->save_buffer = NULL;
}
occupy_co->save_buffer = (char*)malloc(len); //malloc buf;
occupy_co->save_size = len;
memcpy(occupy_co->save_buffer, occupy_co->stack_sp, len);
}
想确认一个事,poll回调resume工作协程后,调用原 glibc 内真正的 read() 系统调用是还需要一定是非阻塞调用么,还是没关系。
https://github.com/yuanzhubi/call_in_stack
调用一些不会产生协程切换的函数时,可以选择call_in_stack一个线程局部的内存块作为临时堆栈(可以在线程内的协程间共享使用这个堆栈),避免消耗协程堆栈。
我的理解是stCoRountine_t结构中的env指针指向co_get_curr_thread_env()的,那为什么不直接用co_get_curr_thread_env()呢?
在co_hook_sys_call.cpp的connect方法中,有一段代码如下:
if( pf.revents & POLLOUT ) //connect succ
{
errno = 0;
return 0;
}
在mac os下连接失败也能进入此条件中,建议修改为:
if( pf.revents & POLLOUT ) //connect succ
{
g_sys_connect_func( fd,address,address_len );
if (errno == EISCONN)
{
errno = 0;
return 0;
}
else
{
return -1;
}
}
很多情况下需要对全局变量进行加锁操作,如果原有代码中使用到了pthread_mutex锁机制,这种情况下如何进行协程锁的替换呢?
32 位 和 64 位的实现最后都把 eax 清 0,这是为了符合什么规范吗 ?
syscall() has been deprecated and is not available on macOS 10.12.
.../libco/co_routine.cpp:125:9: warning: 'syscall' is
deprecated: first deprecated in macOS 10.12 - syscall(2) is unsupported;
please switch to a supported interface. For SYS_kdebug_trace use
kdebug_signpost(). [-Wdeprecated-declarations]
tid = syscall( SYS_gettid );
^
/usr/include/unistd.h:745:6: note: 'syscall' has been explicitly marked
deprecated here
int syscall(int, ...);
^
1 warning generated.
如题,
g_arrCoEnvPerThread[ 204800 ] 数组下标为进程pid,当pid数值大于204800 时,将引起数组越界。
虽然概率不大,但可能存在这个问题。
已提交pull requests,请查看下。
不管是通过在协程中返回 或调用co_free,co_release都会泄漏内存,协程退出后并不会释放协程的独享栈,栈拷贝和其他资源
stStackMem_t* co_alloc_stackmem(unsigned int stack_size)
这个函数 malloc了 stStackMem_t结构体和 stack_size,两个 buffer
但是在void co_release( stCoRoutine_t *co ) 函数中,
仅仅free 了 协程 co指针,
co->stack_mem
co->stack_buffer
以上两个指针都没有释放,会导致内存泄漏?
------------------ coctx_swap.s ---------------------------
.CODE
coctx_swap PROC
;;lea rax,[rsp+8]
;;lea rsp,[rdi+112] ;;right
lea rax,[rsp+8]
lea rsp,[rcx+112] ;; rcx存放参数1;rdx存放参数2
push rax
push rbx
push rcx
push rdx
push [rax-8] ;;ret func addr
push rsi
push rdi
push rbp
push r8
push r9
push r12
push r13
push r14
push r15
;;mov rsp,rsi ;;right
mov rsp,rdx ;;test
pop r15
pop r14
pop r13
pop r12
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rax ;;ret func addr
pop rdx
pop rcx
pop rbx
pop rsp
push rax
xor eax,eax
ret
coctx_swap ENDP
--------------------------- coctx.cpp / coctx_make---------------------------
#ifdef WINDOW
ctx->regs[kRSP] = sp - 8;
ctx->regs[kRETAddr] = (char*)pfn;
ctx->regs[kRCX] = (char*)s;
ctx->regs[kRDX] = (char*)s1;
#else
ctx->regs[kRSP] = sp - 8;
ctx->regs[kRETAddr] = (char*)pfn;
ctx->regs[kRDI] = (char*)s;
ctx->regs[kRSI] = (char*)s1;
把汇编改成这样,但运行时还是出现一些堆栈的问题,例如printf报错。
是否window还有什么特殊的汇编规则啊?
example_echosrv的 socket fd设置了非阻塞,但是example_echocli没有,为什么?
void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg )
在这个函数中调用epoll_wait定时1ms查询,进程请求量小时,会出现空转浪费CPU,可能达到1%到2%。
如果进程数量过多的话,这个CPU浪费也较明显。
建议这里考虑用户根据请求量来动态设置epoll_wait定时参数。
比如,可以根据void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg )中的 pfn_co_eventloop_t pfn回调函数的返回值动态设置epoll_wait超时时间。
在某个携程里用了poll(NULL, 0, msleep);来做定时,结果发现定时不准。msleep=10000的情况下有时候会出现12秒的延迟执行。最后定位到co_epoll_wait,好像不是固定阻塞1毫秒,有时候会阻塞1-4秒。整个测试代码就一个协程,内容仅仅是打印时间在屏幕,不会出现执行忙的情况。
看了源码和这篇文章《揭秘:微信是如何用libco支撑8亿用户的》,对共享栈那里有点不能理解啊!
尤其这里“libco也提供了stackless的协程共享栈模式,可以设置若干个协程共享同一个运行栈。”,谁能结合代码讲一下?co_swap就行
pCallStack的数组大小是128,意味着在不yield的情况下,resume 128次就会数组越界。
co_resume的时候,为什么不判断下iCallStackSize,对出现调用链过长的情况进行报错?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.