├── LICENSE.txt ├── Makefile ├── README.md ├── co.mk ├── co_hook_sys_call.cpp ├── co_routine.cpp ├── co_routine.h ├── co_routine_inner.h ├── coctx.cpp ├── coctx.h ├── coctx_swap.S ├── example_echocli.cpp ├── example_echosvr.cpp ├── example_poll.cpp └── example_thread.cpp /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hymanyx/libco/bfc7b71bf92453afed397ca7c069cc2da21b44dc/LICENSE.txt -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Tencent is pleased to support the open source community by making Libco available. 3 | # 4 | # Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | 20 | COMM_MAKE = 1 21 | COMM_ECHO = 1 22 | version=0.5 23 | v=release 24 | include co.mk 25 | 26 | ########## options ########## 27 | CFLAGS += -g -fno-strict-aliasing -O2 -Wall -export-dynamic \ 28 | -Wall -pipe -D_GNU_SOURCE -D_REENTRANT -fPIC -Wno-deprecated 29 | 30 | LINKS += -g -L./lib -lcolib -lpthread -ldl 31 | 32 | COLIB_OBJS=co_routine.o co_hook_sys_call.o coctx_swap.o coctx.o 33 | #co_swapcontext.o 34 | 35 | PROGS = colib example_poll example_echosvr example_echocli example_thread 36 | 37 | all:$(PROGS) 38 | 39 | colib:libcolib.a libcolib.so 40 | 41 | libcolib.a: $(COLIB_OBJS) 42 | $(ARSTATICLIB) 43 | libcolib.so: $(COLIB_OBJS) 44 | $(BUILDSHARELIB) 45 | 46 | example_echosvr:example_echosvr.o 47 | $(BUILDEXE) 48 | 49 | example_echocli:example_echocli.o 50 | $(BUILDEXE) 51 | 52 | example_thread:example_thread.o 53 | $(BUILDEXE) 54 | 55 | example_poll:example_poll.o 56 | $(BUILDEXE) 57 | 58 | 59 | dist: clean libco-$(version).src.tar.gz 60 | 61 | libco-$(version).src.tar.gz: 62 | @find . -type f | grep -v CVS | grep -v .svn | sed s:^./:libco-$(version)/: > MANIFEST 63 | @(cd ..; ln -s libco_pub libco-$(version)) 64 | (cd ..; tar cvf - `cat libco_pub/MANIFEST` | gzip > libco_pub/libco-$(version).src.tar.gz) 65 | @(cd ..; rm libco-$(version)) 66 | 67 | clean: 68 | $(CLEAN) *.o $(PROGS) 69 | rm -fr MANIFEST lib solib libco-$(version).src.tar.gz libco-$(version) 70 | 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hello libco -------------------------------------------------------------------------------- /co.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Tencent is pleased to support the open source community by making Libco available. 3 | # 4 | # Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | 20 | 21 | ##### Makefile Rules ########## 22 | MAIL_ROOT=. 23 | SRCROOT=. 24 | 25 | ##define the compliers 26 | CPP = g++ 27 | CC = gcc 28 | AR = ar -rc 29 | RANLIB = ranlib 30 | 31 | CPPSHARE = $(CPP) -fPIC -shared -O2 -pipe -L$(SRCROOT)/solib/ -o 32 | CSHARE = $(CC) -fPIC -shared -O2 -pipe -L$(SRCROOT)/solib/ -o 33 | 34 | ifeq ($v,release) 35 | CFLAGS= -O2 $(INCLS) -fPIC -DLINUX -pipe -Wno-deprecated -c 36 | else 37 | CFLAGS= -g $(INCLS) -fPIC -DLINUX -pipe -c -fno-inline 38 | endif 39 | 40 | ifneq ($v,release) 41 | BFLAGS= -g 42 | endif 43 | 44 | STATICLIBPATH=$(SRCROOT)/lib 45 | DYNAMICLIBPATH=$(SRCROOT)/solib 46 | 47 | INCLS += -I$(SRCROOT) 48 | 49 | ## default links 50 | ifeq ($(LINKS_DYNAMIC), 1) 51 | LINKS += -L$(DYNAMICLIBPATH) -L$(STATICLIBPATH) 52 | else 53 | LINKS += -L$(STATICLIBPATH) 54 | endif 55 | 56 | CPPSRCS = $(wildcard *.cpp) 57 | CSRCS = $(wildcard *.c) 58 | CPPOBJS = $(patsubst %.cpp,%.o,$(CPPSRCS)) 59 | COBJS = $(patsubst %.c,%.o,$(CSRCS)) 60 | 61 | SRCS = $(CPPSRCS) $(CSRCS) 62 | OBJS = $(CPPOBJS) $(COBJS) 63 | 64 | CPPCOMPI=$(CPP) $(CFLAGS) -Wno-deprecated 65 | CCCOMPI=$(CC) $(CFLAGS) 66 | 67 | BUILDEXE = $(CPP) $(BFLAGS) -o $@ $^ $(LINKS) 68 | CLEAN = rm -f *.o 69 | 70 | CPPCOMPILE = $(CPPCOMPI) $< $(FLAGS) $(INCLS) $(MTOOL_INCL) -o $@ 71 | CCCOMPILE = $(CCCOMPI) $< $(FLAGS) $(INCLS) $(MTOOL_INCL) -o $@ 72 | 73 | ARSTATICLIB = $(AR) $@.tmp $^ $(AR_FLAGS); \ 74 | if [ $$? -ne 0 ]; then exit 1; fi; \ 75 | test -d $(STATICLIBPATH) || mkdir -p $(STATICLIBPATH); \ 76 | mv -f $@.tmp $(STATICLIBPATH)/$@; 77 | 78 | BUILDSHARELIB = $(CPPSHARE) $@.tmp $^ $(BS_FLAGS); \ 79 | if [ $$? -ne 0 ]; then exit 1; fi; \ 80 | test -d $(DYNAMICLIBPATH) || mkdir -p $(DYNAMICLIBPATH); \ 81 | mv -f $@.tmp $(DYNAMICLIBPATH)/$@; 82 | 83 | .cpp.o: 84 | $(CPPCOMPILE) 85 | .c.o: 86 | $(CCCOMPILE) 87 | -------------------------------------------------------------------------------- /co_hook_sys_call.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include "co_routine.h" 42 | #include "co_routine_inner.h" 43 | 44 | typedef long long ll64_t; 45 | 46 | /* 套接字hook信息结构 - 存储hook函数中跟套接字相关的信息 47 | * 48 | * 为什么需要该结构呢? - 举例说明, 套接字的O_NONBLOCK属性决定了hook后的read函数是直接调用系统的read函数, 49 | * 还是先向内核注册事件(向内核注册事件时又需要读超时时间), 然后切换协程并等待事件发生时切换回该协程, 最后调用 50 | * 系统的read函数. 如果不把这些信息(是否O_NONBLOCK, 读写超时时间等)传递到被hook函数中, 我们就无法实现hook 51 | * 函数的逻辑. 这就是需要该结构的原因! 52 | * 53 | * 说句题外话, 其实我觉得该结构是非必需的, O_NONBLOCK属性可通过未hook的fcntl函数获得, 超时时间采用全局设置也未尝不可. 54 | * */ 55 | struct rpchook_t 56 | { 57 | int user_flag; // 套接字的阻塞/非阻塞属性 58 | struct sockaddr_in dest; //maybe sockaddr_un; // 套接字目的主机地址 59 | int domain; //AF_LOCAL , AF_INET // 套接字类型 60 | 61 | struct timeval read_timeout; // 套接字读超时时间 62 | struct timeval write_timeout; // 该套接写超时时间 63 | }; 64 | 65 | /* 获取线程id */ 66 | static inline pid_t GetPid() 67 | { 68 | char **p = (char**)pthread_self(); 69 | return p ? *(pid_t*)(p + 18) : getpid(); 70 | } 71 | 72 | /* 套接字hook信息数组 - 存储(该线程内)所有协程中的套接字hook信息, 便于套接字hook信息在被hook的系统调用之间传递, 73 | * 部分被hook函数用这些信息来控制函数逻辑(详见下面被hook的系统调用). 74 | * 75 | * 理解这个数组是重点, 一部分被hook的系统调用初始化这些数组中的元素, 另一部分被hook的系统调用获取数组元素来控制函数逻辑. 76 | * */ 77 | static rpchook_t *g_rpchook_socket_fd[ 102400 ] = { 0 }; 78 | 79 | /* 对每个被hook的系统调用声明一种函数指针类型 */ 80 | typedef int (*socket_pfn_t)(int domain, int type, int protocol); 81 | typedef int (*connect_pfn_t)(int socket, const struct sockaddr *address, socklen_t address_len); 82 | typedef int (*close_pfn_t)(int fd); 83 | typedef ssize_t (*read_pfn_t)(int fildes, void *buf, size_t nbyte); 84 | typedef ssize_t (*write_pfn_t)(int fildes, const void *buf, size_t nbyte); 85 | typedef ssize_t (*sendto_pfn_t)(int socket, const void *message, size_t length, 86 | int flags, const struct sockaddr *dest_addr, 87 | socklen_t dest_len); 88 | typedef ssize_t (*recvfrom_pfn_t)(int socket, void *buffer, size_t length, 89 | int flags, struct sockaddr *address, 90 | socklen_t *address_len); 91 | typedef size_t (*send_pfn_t)(int socket, const void *buffer, size_t length, int flags); 92 | typedef ssize_t (*recv_pfn_t)(int socket, void *buffer, size_t length, int flags); 93 | typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); 94 | typedef int (*setsockopt_pfn_t)(int socket, int level, int option_name, 95 | const void *option_value, socklen_t option_len); 96 | typedef int (*fcntl_pfn_t)(int fildes, int cmd, ...); 97 | typedef struct tm *(*localtime_r_pfn_t)( const time_t *timep, struct tm *result ); 98 | typedef void *(*pthread_getspecific_pfn_t)(pthread_key_t key); 99 | typedef int (*pthread_setspecific_pfn_t)(pthread_key_t key, const void *value); 100 | typedef int (*pthread_rwlock_rdlock_pfn_t)(pthread_rwlock_t *rwlock); 101 | typedef int (*pthread_rwlock_wrlock_pfn_t)(pthread_rwlock_t *rwlock); 102 | typedef int (*pthread_rwlock_unlock_pfn_t)(pthread_rwlock_t *rwlock); 103 | 104 | /* 将动态库中被hook的系统调用的地址(即函数指针)绑定到以g_sys_##name##__func命名的函数指针 105 | * 106 | * 为什么要这么做呢? - 这样做的目的是在链接阶段让系统调用在动态库中找不到对应的实现, 而来链接到我们代码中的同名函数(即被hook的函数) 107 | * */ 108 | static socket_pfn_t g_sys_socket_func = (socket_pfn_t)dlsym(RTLD_NEXT,"socket"); 109 | static connect_pfn_t g_sys_connect_func = (connect_pfn_t)dlsym(RTLD_NEXT,"connect"); 110 | static close_pfn_t g_sys_close_func = (close_pfn_t)dlsym(RTLD_NEXT,"close"); 111 | static read_pfn_t g_sys_read_func = (read_pfn_t)dlsym(RTLD_NEXT,"read"); 112 | static write_pfn_t g_sys_write_func = (write_pfn_t)dlsym(RTLD_NEXT,"write"); 113 | static sendto_pfn_t g_sys_sendto_func = (sendto_pfn_t)dlsym(RTLD_NEXT,"sendto"); 114 | static recvfrom_pfn_t g_sys_recvfrom_func = (recvfrom_pfn_t)dlsym(RTLD_NEXT,"recvfrom"); 115 | static send_pfn_t g_sys_send_func = (send_pfn_t)dlsym(RTLD_NEXT,"send"); 116 | static recv_pfn_t g_sys_recv_func = (recv_pfn_t)dlsym(RTLD_NEXT,"recv"); 117 | static poll_pfn_t g_sys_poll_func = (poll_pfn_t)dlsym(RTLD_NEXT,"poll"); 118 | static setsockopt_pfn_t g_sys_setsockopt_func = (setsockopt_pfn_t)dlsym(RTLD_NEXT,"setsockopt"); 119 | static fcntl_pfn_t g_sys_fcntl_func = (fcntl_pfn_t)dlsym(RTLD_NEXT,"fcntl"); 120 | 121 | 122 | /* 未使用该函数 */ 123 | static inline unsigned long long get_tick_count() 124 | { 125 | uint32_t lo, hi; 126 | __asm__ __volatile__ ( 127 | "rdtscp" : "=a"(lo), "=d"(hi) 128 | ); 129 | return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); 130 | } 131 | 132 | /* 未使用该结构 */ 133 | struct rpchook_connagent_head_t 134 | { 135 | unsigned char bVersion; 136 | struct in_addr iIP; 137 | unsigned short hPort; 138 | unsigned int iBodyLen; 139 | unsigned int iOssAttrID; 140 | unsigned char bIsRespNotExist; 141 | unsigned char sReserved[6]; 142 | }__attribute__((packed)); 143 | 144 | /* hook系统调用 - 将动态库中名为name的系统调用地址(即函数指针)绑定到以g_sys_##name##__func命名的函数指针 */ 145 | #define HOOK_SYS_FUNC(name) if( !g_sys_##name##_func ) { g_sys_##name##_func = (name##_pfn_t)dlsym(RTLD_NEXT,#name); } 146 | 147 | /* 148 | * diff_ms - 计算以毫秒为单位的时间差 149 | * @param begin - (input) 开始时间 150 | * @param end - (input) 结束时间 151 | * @return 毫秒为单位的时间差 152 | * */ 153 | static inline ll64_t diff_ms(struct timeval &begin,struct timeval &end) 154 | { 155 | ll64_t u = (end.tv_sec - begin.tv_sec) ; 156 | u *= 1000 * 10; 157 | u += ( end.tv_usec - begin.tv_usec ) / ( 100 ); 158 | return u; 159 | } 160 | 161 | /* 162 | * get_by_fd - 在套接字hook信息数组(g_rpchook_socket_fd)中获取套接字fd对应的rpchook_t类型变量的指针 163 | * @param fd - (input) 套接字文件描述符 164 | * @return 成功返回rpchook_t类型变量的指针, 失败返回NULL 165 | * */ 166 | static inline rpchook_t * get_by_fd( int fd ) 167 | { 168 | if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) 169 | { 170 | return g_rpchook_socket_fd[ fd ]; 171 | } 172 | return NULL; 173 | } 174 | 175 | /* 176 | * alloc_by_fd - 为套接字fd分配对应的rpchook_t类型类型的存储空间, 并将存储空间的地址加入到套接字hook信息数组(g_rpchook_socket_fd)中 177 | * @param fd - (input) 套接字文件描述符 178 | * @return 成功返回rpchook_t类型变量的指针, 失败返回NULL 179 | * */ 180 | static inline rpchook_t * alloc_by_fd( int fd ) 181 | { 182 | if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) 183 | { 184 | rpchook_t *lp = (rpchook_t*)calloc( 1,sizeof(rpchook_t) ); 185 | lp->read_timeout.tv_sec = 1; 186 | lp->write_timeout.tv_sec = 1; 187 | g_rpchook_socket_fd[ fd ] = lp; 188 | return lp; 189 | } 190 | return NULL; 191 | } 192 | 193 | /* 194 | * free_by_fd - 在套接字hook信息数组(g_rpchook_socket_fd)中释放套接字fd对应rpchook_t类型变量的存储空间 195 | * @param fd - (input) 套接字文件描述符 196 | * @return 197 | * */ 198 | static inline void free_by_fd( int fd ) 199 | { 200 | if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) 201 | { 202 | rpchook_t *lp = g_rpchook_socket_fd[ fd ]; 203 | if( lp ) 204 | { 205 | g_rpchook_socket_fd[ fd ] = NULL; 206 | free(lp); 207 | } 208 | } 209 | return; 210 | 211 | } 212 | 213 | 214 | /* socket - 被hook后的socket函数, 主要是为套接字fd分配对应的rpchook_t类型的内存空间, 并往g_rpchook_socket_fd中添加该内存空间的地址(指针指向的变量未全部初始化) */ 215 | int socket(int domain, int type, int protocol) 216 | { 217 | // 重命名动态库中的socket系统调用 218 | HOOK_SYS_FUNC( socket ); 219 | 220 | // 协程禁止hook系统调用, 则直接调用socket系统调用返回socket文件描述符fd 221 | if( !co_is_enable_sys_hook() ) 222 | { 223 | return g_sys_socket_func( domain,type,protocol ); 224 | } 225 | 226 | // 协程使用了hook, 则直接调用socket系统调用获取socket文件描述符fd 227 | int fd = g_sys_socket_func(domain,type,protocol); 228 | if( fd < 0 ) 229 | { 230 | return fd; 231 | } 232 | 233 | // 为fd分配rpchook_t类型的内存空间, 其中存储套接字hook信息, 并将其加入套接字hook信息数组g_rpchook_socket_fd中 234 | rpchook_t *lp = alloc_by_fd( fd ); 235 | lp->domain = domain; 236 | 237 | // 设置套接字fd属性 238 | fcntl( fd, F_SETFL, g_sys_fcntl_func(fd, F_GETFL,0 ) ); 239 | 240 | return fd; 241 | } 242 | 243 | /* co_accpet - accpet系统调用的封装而已 */ 244 | int co_accept( int fd, struct sockaddr *addr, socklen_t *len ) 245 | { 246 | int cli = accept( fd,addr,len ); 247 | if( cli < 0 ) 248 | { 249 | return cli; 250 | } 251 | //rpchook_t *lp = alloc_by_fd( cli ); 252 | return cli; 253 | } 254 | 255 | /* connect - 被hook后的connect函数, 主要是初始化(g_rpchook_socket_fd中)套接字fd对应的rpchook_t类型变量的dest成员 */ 256 | int connect(int fd, const struct sockaddr *address, socklen_t address_len) 257 | { 258 | HOOK_SYS_FUNC( connect ); 259 | 260 | if( !co_is_enable_sys_hook() ) 261 | { 262 | return g_sys_connect_func(fd,address,address_len); 263 | } 264 | 265 | int ret = g_sys_connect_func(fd,address,address_len); 266 | 267 | 268 | if( address_len == sizeof(sockaddr_un)) 269 | { 270 | const struct sockaddr_un *p = (const struct sockaddr_un *)address; 271 | if( strstr( p->sun_path,"connagent_unix_domain_socket") ) ///tmp/connagent_unix_domain_socket 272 | { 273 | } 274 | } 275 | rpchook_t *lp = get_by_fd( fd ); 276 | if( lp ) 277 | { 278 | if( sizeof(lp->dest) >= address_len ) 279 | { 280 | memcpy( &(lp->dest),address,(int)address_len ); 281 | } 282 | } 283 | return ret; 284 | } 285 | 286 | /* close - 被hook后的close函数, 主要是释放(g_rpchook_socket_fd中)套接字fd对应的rpchook_t类型存储空间 */ 287 | int close(int fd) 288 | { 289 | HOOK_SYS_FUNC( close ); 290 | 291 | // 协程禁止hook系统调用, 则直接调用系统调用 292 | if( !co_is_enable_sys_hook() ) 293 | { 294 | return g_sys_close_func( fd ); 295 | } 296 | 297 | // 协程hook系统调用, 则释放(g_rpchook_socket_fd中)套接字fd对应的rpchook_t类型的存储空间 298 | free_by_fd( fd ); 299 | int ret = g_sys_close_func(fd); 300 | 301 | return ret; 302 | } 303 | 304 | /* read - 被hook后的read函数, 主要是向内核注册套接字fd上的事件 */ 305 | ssize_t read( int fd, void *buf, size_t nbyte ) 306 | { 307 | HOOK_SYS_FUNC( read ); 308 | 309 | // 协程禁止hook系统调用, 则直接调用系统调用 310 | if( !co_is_enable_sys_hook() ) 311 | { 312 | return g_sys_read_func( fd,buf,nbyte ); 313 | } 314 | 315 | // 协程hook系统调用, 根据套接字是否为非阻塞选择不同的处理方式 316 | rpchook_t *lp = get_by_fd( fd ); 317 | 318 | // 非阻塞, 直接调用系统调用 319 | if( !lp || ( O_NONBLOCK & lp->user_flag ) ) 320 | { 321 | ssize_t ret = g_sys_read_func( fd,buf,nbyte ); 322 | return ret; 323 | } 324 | 325 | // 阻塞, 向内核注册套接字fd的事件 326 | // poll如果未hook,则直接调用poll系统调用; 327 | // poll如果被hook,则调用co_poll向内核注册, co_poll中会切换协程, 协程被恢复时将会从co_poll中的挂起点继续运行 328 | int timeout = ( lp->read_timeout.tv_sec * 1000 ) + ( lp->read_timeout.tv_usec / 1000 ); 329 | struct pollfd pf = { 0 }; 330 | pf.fd = fd; 331 | pf.events = ( POLLIN | POLLERR | POLLHUP ); 332 | int pollret = poll( &pf,1,timeout ); 333 | 334 | ssize_t readret = g_sys_read_func( fd,(char*)buf ,nbyte ); 335 | if( readret < 0 ) 336 | { 337 | co_log_err("CO_ERR: read fd %d ret %ld errno %d poll ret %d timeout %d", 338 | fd,readret,errno,pollret,timeout); 339 | } 340 | 341 | return readret; 342 | 343 | } 344 | 345 | /* write - 被hook后的write函数, 主要是向内核注册套接字fd上的事件 */ 346 | ssize_t write( int fd, const void *buf, size_t nbyte ) 347 | { 348 | HOOK_SYS_FUNC( write ); 349 | 350 | // 协程禁止hook系统调用, 则直接调用系统调用 351 | if( !co_is_enable_sys_hook() ) 352 | { 353 | return g_sys_write_func( fd,buf,nbyte ); 354 | } 355 | 356 | // 协程hook系统调用, 根据套接字是否为非阻塞选择不同的处理方式 357 | rpchook_t *lp = get_by_fd( fd ); 358 | 359 | // 非阻塞, 直接调用系统调用 360 | if( !lp || ( O_NONBLOCK & lp->user_flag ) ) 361 | { 362 | ssize_t ret = g_sys_write_func( fd,buf,nbyte ); 363 | return ret; 364 | } 365 | 366 | // 阻塞, 向内核注册套接字fd的事件 367 | // poll如果未hook,则直接调用poll系统调用; 368 | // poll如果被hook,则调用co_poll向内核注册, co_poll中会切换协程, 协程被恢复时将会从co_poll中的挂起点继续运行 369 | size_t wrotelen = 0; 370 | int timeout = ( lp->write_timeout.tv_sec * 1000 ) + ( lp->write_timeout.tv_usec / 1000 ); 371 | 372 | ssize_t writeret = g_sys_write_func( fd,(const char*)buf + wrotelen,nbyte - wrotelen ); 373 | 374 | if( writeret > 0 ) 375 | { 376 | wrotelen += writeret; 377 | } 378 | while( wrotelen < nbyte ) 379 | { 380 | // buf中的数据未全部写到fd上, 则向内核注册套接字fd的事件 381 | struct pollfd pf = { 0 }; 382 | pf.fd = fd; 383 | pf.events = ( POLLOUT | POLLERR | POLLHUP ); 384 | poll( &pf,1,timeout ); 385 | 386 | writeret = g_sys_write_func( fd,(const char*)buf + wrotelen,nbyte - wrotelen ); 387 | 388 | if( writeret <= 0 ) 389 | { 390 | break; 391 | } 392 | wrotelen += writeret ; 393 | } 394 | return wrotelen; 395 | } 396 | 397 | /* sendto - 被hook后的sendto函数, 主要是向内核注册套接字fd上的事件 */ 398 | ssize_t sendto(int socket, const void *message, size_t length, 399 | int flags, const struct sockaddr *dest_addr, 400 | socklen_t dest_len) 401 | { 402 | /* 403 | 1.no enable sys call ? sys 404 | 2.( !lp || lp is non block ) ? sys 405 | 3.try 406 | 4.wait 407 | 5.try 408 | */ 409 | HOOK_SYS_FUNC( sendto ); 410 | if( !co_is_enable_sys_hook() ) 411 | { 412 | return g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); 413 | } 414 | 415 | rpchook_t *lp = get_by_fd( socket ); 416 | if( !lp || ( O_NONBLOCK & lp->user_flag ) ) 417 | { 418 | return g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); 419 | } 420 | 421 | ssize_t ret = g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); 422 | if( ret < 0 && EAGAIN == errno ) 423 | { 424 | int timeout = ( lp->write_timeout.tv_sec * 1000 ) 425 | + ( lp->write_timeout.tv_usec / 1000 ); 426 | 427 | 428 | struct pollfd pf = { 0 }; 429 | pf.fd = socket; 430 | pf.events = ( POLLOUT | POLLERR | POLLHUP ); 431 | poll( &pf,1,timeout ); 432 | 433 | ret = g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); 434 | 435 | } 436 | return ret; 437 | } 438 | 439 | /* recvfrom - 被hook后的recvfrom函数, 主要是向内核注册套接字fd上的事件 */ 440 | ssize_t recvfrom(int socket, void *buffer, size_t length, 441 | int flags, struct sockaddr *address, 442 | socklen_t *address_len) 443 | { 444 | HOOK_SYS_FUNC( recvfrom ); 445 | if( !co_is_enable_sys_hook() ) 446 | { 447 | return g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); 448 | } 449 | 450 | rpchook_t *lp = get_by_fd( socket ); 451 | if( !lp || ( O_NONBLOCK & lp->user_flag ) ) 452 | { 453 | return g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); 454 | } 455 | 456 | int timeout = ( lp->read_timeout.tv_sec * 1000 ) 457 | + ( lp->read_timeout.tv_usec / 1000 ); 458 | 459 | 460 | struct pollfd pf = { 0 }; 461 | pf.fd = socket; 462 | pf.events = ( POLLIN | POLLERR | POLLHUP ); 463 | poll( &pf,1,timeout ); 464 | 465 | ssize_t ret = g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); 466 | return ret; 467 | } 468 | 469 | /* send - 被hook后的send函数, 主要是向内核注册套接字fd上的事件 */ 470 | ssize_t send(int socket, const void *buffer, size_t length, int flags) 471 | { 472 | HOOK_SYS_FUNC( send ); 473 | 474 | if( !co_is_enable_sys_hook() ) 475 | { 476 | return g_sys_send_func( socket,buffer,length,flags ); 477 | } 478 | rpchook_t *lp = get_by_fd( socket ); 479 | 480 | if( !lp || ( O_NONBLOCK & lp->user_flag ) ) 481 | { 482 | return g_sys_send_func( socket,buffer,length,flags ); 483 | } 484 | size_t wrotelen = 0; 485 | int timeout = ( lp->write_timeout.tv_sec * 1000 ) 486 | + ( lp->write_timeout.tv_usec / 1000 ); 487 | 488 | ssize_t writeret = g_sys_send_func( socket,buffer,length,flags ); 489 | 490 | if( writeret > 0 ) 491 | { 492 | wrotelen += writeret; 493 | } 494 | while( wrotelen < length ) 495 | { 496 | 497 | struct pollfd pf = { 0 }; 498 | pf.fd = socket; 499 | pf.events = ( POLLOUT | POLLERR | POLLHUP ); 500 | poll( &pf,1,timeout ); 501 | 502 | writeret = g_sys_send_func( socket,(const char*)buffer + wrotelen,length - wrotelen,flags ); 503 | 504 | if( writeret <= 0 ) 505 | { 506 | break; 507 | } 508 | wrotelen += writeret ; 509 | } 510 | 511 | return wrotelen; 512 | } 513 | 514 | /* recv - 被hook后的recv函数, 主要是向内核注册套接字fd上的事件 */ 515 | ssize_t recv( int socket, void *buffer, size_t length, int flags ) 516 | { 517 | HOOK_SYS_FUNC( recv ); 518 | 519 | if( !co_is_enable_sys_hook() ) 520 | { 521 | return g_sys_recv_func( socket,buffer,length,flags ); 522 | } 523 | rpchook_t *lp = get_by_fd( socket ); 524 | 525 | if( !lp || ( O_NONBLOCK & lp->user_flag ) ) 526 | { 527 | return g_sys_recv_func( socket,buffer,length,flags ); 528 | } 529 | int timeout = ( lp->read_timeout.tv_sec * 1000 ) 530 | + ( lp->read_timeout.tv_usec / 1000 ); 531 | 532 | struct pollfd pf = { 0 }; 533 | pf.fd = socket; 534 | pf.events = ( POLLIN | POLLERR | POLLHUP ); 535 | int pollret = poll( &pf,1,timeout ); 536 | 537 | ssize_t readret = g_sys_recv_func( socket,buffer,length,flags ); 538 | if( readret < 0 ) 539 | { 540 | co_log_err("CO_ERR: read fd %d ret %ld errno %d poll ret %d timeout %d", 541 | socket,readret,errno,pollret,timeout); 542 | } 543 | 544 | return readret; 545 | 546 | } 547 | 548 | int poll(struct pollfd fds[], nfds_t nfds, int timeout) 549 | { 550 | 551 | HOOK_SYS_FUNC( poll ); 552 | 553 | if( !co_is_enable_sys_hook() ) 554 | { 555 | return g_sys_poll_func( fds,nfds,timeout ); 556 | } 557 | 558 | return co_poll( co_get_epoll_ct(),fds,nfds,timeout ); 559 | 560 | } 561 | 562 | /* setsockopt - 被hook后的setsockopt函数, 主要是初始化(g_rpchook_socket_fd中)套接字fd对应的rpchook_t类型变量的read_timeout和write_timeout成员 */ 563 | int setsockopt(int fd, int level, int option_name, 564 | const void *option_value, socklen_t option_len) 565 | { 566 | HOOK_SYS_FUNC( setsockopt ); 567 | 568 | if( !co_is_enable_sys_hook() ) 569 | { 570 | return g_sys_setsockopt_func( fd,level,option_name,option_value,option_len ); 571 | } 572 | rpchook_t *lp = get_by_fd( fd ); 573 | 574 | if( lp && SOL_SOCKET == level ) 575 | { 576 | struct timeval *val = (struct timeval*)option_value; 577 | if( SO_RCVTIMEO == option_name ) 578 | { 579 | memcpy( &lp->read_timeout,val,sizeof(*val) ); 580 | } 581 | else if( SO_SNDTIMEO == option_name ) 582 | { 583 | memcpy( &lp->write_timeout,val,sizeof(*val) ); 584 | } 585 | } 586 | return g_sys_setsockopt_func( fd,level,option_name,option_value,option_len ); 587 | } 588 | 589 | /* fcntl - 被hook后的fcntl函数, 主要是初始化(g_rpchook_socket_fd中)套接字fd对应的rpchook_t类型变量的user_flag成员 */ 590 | int fcntl(int fildes, int cmd, ...) 591 | { 592 | HOOK_SYS_FUNC( fcntl ); 593 | 594 | if( fildes < 0 ) 595 | { 596 | return __LINE__; 597 | } 598 | 599 | va_list arg_list; 600 | va_start( arg_list,cmd ); 601 | 602 | int ret = -1; 603 | rpchook_t *lp = get_by_fd( fildes ); 604 | switch( cmd ) 605 | { 606 | case F_DUPFD: 607 | { 608 | int param = va_arg(arg_list,int); 609 | ret = g_sys_fcntl_func( fildes,cmd,param ); 610 | break; 611 | } 612 | case F_GETFD: 613 | { 614 | ret = g_sys_fcntl_func( fildes,cmd ); 615 | break; 616 | } 617 | case F_SETFD: 618 | { 619 | int param = va_arg(arg_list,int); 620 | ret = g_sys_fcntl_func( fildes,cmd,param ); 621 | break; 622 | } 623 | case F_GETFL: 624 | { 625 | ret = g_sys_fcntl_func( fildes,cmd ); 626 | break; 627 | } 628 | case F_SETFL: 629 | { 630 | int param = va_arg(arg_list,int); 631 | int flag = param; 632 | if( co_is_enable_sys_hook() && lp ) 633 | { 634 | flag |= O_NONBLOCK; 635 | } 636 | ret = g_sys_fcntl_func( fildes,cmd,flag ); 637 | if( 0 == ret && lp ) 638 | { 639 | lp->user_flag = param; 640 | } 641 | break; 642 | } 643 | case F_GETOWN: 644 | { 645 | ret = g_sys_fcntl_func( fildes,cmd ); 646 | break; 647 | } 648 | case F_SETOWN: 649 | { 650 | int param = va_arg(arg_list,int); 651 | ret = g_sys_fcntl_func( fildes,cmd,param ); 652 | break; 653 | } 654 | case F_GETLK: 655 | { 656 | struct flock *param = va_arg(arg_list,struct flock *); 657 | ret = g_sys_fcntl_func( fildes,cmd,param ); 658 | break; 659 | } 660 | case F_SETLK: 661 | { 662 | struct flock *param = va_arg(arg_list,struct flock *); 663 | ret = g_sys_fcntl_func( fildes,cmd,param ); 664 | break; 665 | } 666 | case F_SETLKW: 667 | { 668 | struct flock *param = va_arg(arg_list,struct flock *); 669 | ret = g_sys_fcntl_func( fildes,cmd,param ); 670 | break; 671 | } 672 | } 673 | 674 | va_end( arg_list ); 675 | 676 | return ret; 677 | } 678 | 679 | /* co_enable_hook_sys - 设置当前线程中正在运行的协程中使用hook系统调用*/ 680 | void co_enable_hook_sys() //这函数必须写在这里,否则本文件会被忽略!!! 681 | { 682 | stCoRoutine_t *co = GetCurrThreadCo(); 683 | if( co ) 684 | { 685 | co->cEnableSysHook = 1; 686 | } 687 | } 688 | 689 | -------------------------------------------------------------------------------- /co_routine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "co_routine.h" 20 | #include "co_routine_inner.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | extern "C" 43 | { 44 | extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap"); 45 | }; 46 | using namespace std; 47 | stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env ); 48 | struct stCoEpoll_t; 49 | 50 | /* 协程环境类型 - 每个线程有且仅有一个该类型的变量 51 | * 52 | * 该结构的作用是什么呢? - 我们知道, 非对称协程允许嵌套创建子协程, 为了记录这种嵌套创建的协程, 以便子协程退出 53 | * 时正确恢复到挂起点(挂起点位于父协程中), 我们就需要记录这种嵌套调用过程; 另外, 协程中的套接字向内核注册了事件, 54 | * 我们必须保存套接字和协程的对应关系, 以便该线程的eventloop中检测到套接字上事件发生时, 能够恢复该套接字对应的 55 | * 协程来处理事件. 56 | * */ 57 | struct stCoRoutineEnv_t 58 | { 59 | stCoRoutine_t *pCallStack[ 128 ]; // 该线程内允许嵌套创建128个协程(即协程1内创建协程2, 协程2内创建协程3... 协程127内创建协程128. 该结构虽然是数组, 但将其作为栈来使用, 满足后进先出的特点) 60 | int iCallStackSize; // 该线程内嵌套创建的协程数量, 即pCallStack数组中元素的数量 61 | stCoEpoll_t *pEpoll; // 该线程内的epoll实例(套接字通过该结构内的epoll句柄向内核注册事件), 也用于该线程的事件循环eventloop中 62 | }; 63 | 64 | /* co_log_err - 协程日志输出 */ 65 | void co_log_err( const char *fmt,... ) 66 | { 67 | } 68 | 69 | // 辅助功能函数, 就不详细说明了 70 | static unsigned long long counter(void); 71 | static unsigned long long getCpuKhz() 72 | { 73 | FILE *fp = fopen("/proc/cpuinfo","r"); 74 | if(!fp) return 1; 75 | char buf[4096] = {0}; 76 | fread(buf,1,sizeof(buf),fp); 77 | fclose(fp); 78 | 79 | char *lp = strstr(buf,"cpu MHz"); 80 | if(!lp) return 1; 81 | lp += strlen("cpu MHz"); 82 | while(*lp == ' ' || *lp == '\t' || *lp == ':') 83 | { 84 | ++lp; 85 | } 86 | 87 | double mhz = atof(lp); 88 | unsigned long long u = (unsigned long long)(mhz * 1000); 89 | return u; 90 | } 91 | 92 | static unsigned long long GetTickMS() 93 | { 94 | static uint32_t khz = getCpuKhz(); 95 | return counter() / khz; 96 | } 97 | 98 | static unsigned long long counter(void) 99 | { 100 | register uint32_t lo, hi; 101 | register unsigned long long o; 102 | __asm__ __volatile__ ( 103 | "rdtscp" : "=a"(lo), "=d"(hi) 104 | ); 105 | o = hi; 106 | o <<= 32; 107 | return (o | lo); 108 | } 109 | 110 | /* 111 | * GetPid - 获取当前线程id 112 | * @return void 113 | * */ 114 | static pid_t GetPid() 115 | { 116 | static __thread pid_t pid = 0; 117 | static __thread pid_t tid = 0; 118 | if( !pid || !tid || pid != getpid() ) 119 | { 120 | pid = getpid(); // 获取当前进程id 121 | tid = syscall( __NR_gettid ); // 获取当前线程id 122 | } 123 | return tid; 124 | 125 | } 126 | /* 127 | static pid_t GetPid() 128 | { 129 | char **p = (char**)pthread_self(); 130 | return p ? *(pid_t*)(p + 18) : getpid(); 131 | } 132 | */ 133 | template 134 | void RemoveFromLink(T *ap) 135 | { 136 | TLink *lst = ap->pLink; 137 | if(!lst) return ; 138 | assert( lst->head && lst->tail ); 139 | 140 | if( ap == lst->head ) 141 | { 142 | lst->head = ap->pNext; 143 | if(lst->head) 144 | { 145 | lst->head->pPrev = NULL; 146 | } 147 | } 148 | else 149 | { 150 | if(ap->pPrev) 151 | { 152 | ap->pPrev->pNext = ap->pNext; 153 | } 154 | } 155 | 156 | if( ap == lst->tail ) 157 | { 158 | lst->tail = ap->pPrev; 159 | if(lst->tail) 160 | { 161 | lst->tail->pNext = NULL; 162 | } 163 | } 164 | else 165 | { 166 | ap->pNext->pPrev = ap->pPrev; 167 | } 168 | 169 | ap->pPrev = ap->pNext = NULL; 170 | ap->pLink = NULL; 171 | } 172 | 173 | template 174 | void inline AddTail(TLink*apLink,TNode *ap) 175 | { 176 | if( ap->pLink ) 177 | { 178 | return ; 179 | } 180 | if(apLink->tail) 181 | { 182 | apLink->tail->pNext = (TNode*)ap; 183 | ap->pNext = NULL; 184 | ap->pPrev = apLink->tail; 185 | apLink->tail = ap; 186 | } 187 | else 188 | { 189 | apLink->head = apLink->tail = ap; 190 | ap->pNext = ap->pPrev = NULL; 191 | } 192 | ap->pLink = apLink; 193 | } 194 | template 195 | void inline PopHead( TLink*apLink ) 196 | { 197 | if( !apLink->head ) 198 | { 199 | return ; 200 | } 201 | TNode *lp = apLink->head; 202 | if( apLink->head == apLink->tail ) 203 | { 204 | apLink->head = apLink->tail = NULL; 205 | } 206 | else 207 | { 208 | apLink->head = apLink->head->pNext; 209 | } 210 | 211 | lp->pPrev = lp->pNext = NULL; 212 | lp->pLink = NULL; 213 | 214 | if( apLink->head ) 215 | { 216 | apLink->head->pPrev = NULL; 217 | } 218 | } 219 | 220 | template 221 | void inline Join( TLink*apLink,TLink *apOther ) 222 | { 223 | //printf("apOther %p\n",apOther); 224 | if( !apOther->head ) 225 | { 226 | return ; 227 | } 228 | TNode *lp = apOther->head; 229 | while( lp ) 230 | { 231 | lp->pLink = apLink; 232 | lp = lp->pNext; 233 | } 234 | lp = apOther->head; 235 | if(apLink->tail) 236 | { 237 | apLink->tail->pNext = (TNode*)lp; 238 | lp->pPrev = apLink->tail; 239 | apLink->tail = apOther->tail; 240 | } 241 | else 242 | { 243 | apLink->head = apOther->head; 244 | apLink->tail = apOther->tail; 245 | } 246 | 247 | apOther->head = apOther->tail = NULL; 248 | } 249 | 250 | 251 | // ---------------------------------------------------------------------------- 252 | struct stTimeoutItemLink_t; 253 | struct stTimeoutItem_t; 254 | 255 | /* 线程epoll实例 - 该结构存在于stCoRoutineEnv_t结构中 256 | * 257 | * 同一线程内所有的套接字都通过iEpollFd文件描述符向内核注册事件 258 | * */ 259 | struct stCoEpoll_t 260 | { 261 | int iEpollFd; // 由epoll_create函数创建的epoll句柄 262 | static const int _EPOLL_SIZE = 1024 * 10; 263 | struct stTimeout_t *pTimeout; 264 | struct stTimeoutItemLink_t *pstTimeoutList; 265 | struct stTimeoutItemLink_t *pstActiveList; 266 | 267 | }; 268 | typedef void (*OnPreparePfn_t)( stTimeoutItem_t *,struct epoll_event &ev, stTimeoutItemLink_t *active ); 269 | typedef void (*OnProcessPfn_t)( stTimeoutItem_t *); 270 | 271 | /* 详见stPoll_t结构说明 */ 272 | struct stTimeoutItem_t 273 | { 274 | 275 | enum 276 | { 277 | eMaxTimeout = 20 * 1000 //20s 278 | }; 279 | stTimeoutItem_t *pPrev; 280 | stTimeoutItem_t *pNext; 281 | stTimeoutItemLink_t *pLink; 282 | 283 | unsigned long long ullExpireTime; 284 | 285 | OnPreparePfn_t pfnPrepare; 286 | OnProcessPfn_t pfnProcess; 287 | 288 | void *pArg; // routine 289 | bool bTimeout; 290 | }; 291 | struct stTimeoutItemLink_t 292 | { 293 | stTimeoutItem_t *head; 294 | stTimeoutItem_t *tail; 295 | 296 | }; 297 | struct stTimeout_t 298 | { 299 | stTimeoutItemLink_t *pItems; 300 | int iItemSize; 301 | 302 | unsigned long long ullStart; 303 | long long llStartIdx; 304 | }; 305 | stTimeout_t *AllocTimeout( int iSize ) 306 | { 307 | stTimeout_t *lp = (stTimeout_t*)calloc( 1,sizeof(stTimeout_t) ); 308 | 309 | lp->iItemSize = iSize; 310 | lp->pItems = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) * lp->iItemSize ); 311 | 312 | lp->ullStart = GetTickMS(); 313 | lp->llStartIdx = 0; 314 | 315 | return lp; 316 | } 317 | void FreeTimeout( stTimeout_t *apTimeout ) 318 | { 319 | free( apTimeout->pItems ); 320 | free ( apTimeout ); 321 | } 322 | int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,unsigned long long allNow ) 323 | { 324 | if( apTimeout->ullStart == 0 ) 325 | { 326 | apTimeout->ullStart = allNow; 327 | apTimeout->llStartIdx = 0; 328 | } 329 | if( allNow < apTimeout->ullStart ) 330 | { 331 | co_log_err("CO_ERR: AddTimeout line %d allNow %llu apTimeout->ullStart %llu", 332 | __LINE__,allNow,apTimeout->ullStart); 333 | 334 | return __LINE__; 335 | } 336 | if( apItem->ullExpireTime < allNow ) 337 | { 338 | co_log_err("CO_ERR: AddTimeout line %d apItem->ullExpireTime %llu allNow %llu apTimeout->ullStart %llu", 339 | __LINE__,apItem->ullExpireTime,allNow,apTimeout->ullStart); 340 | 341 | return __LINE__; 342 | } 343 | int diff = apItem->ullExpireTime - apTimeout->ullStart; 344 | 345 | if( diff >= apTimeout->iItemSize ) 346 | { 347 | co_log_err("CO_ERR: AddTimeout line %d diff %d", 348 | __LINE__,diff); 349 | 350 | return __LINE__; 351 | } 352 | AddTail( apTimeout->pItems + ( apTimeout->llStartIdx + diff ) % apTimeout->iItemSize , apItem ); 353 | 354 | return 0; 355 | } 356 | inline void TakeAllTimeout( stTimeout_t *apTimeout,unsigned long long allNow,stTimeoutItemLink_t *apResult ) 357 | { 358 | if( apTimeout->ullStart == 0 ) 359 | { 360 | apTimeout->ullStart = allNow; 361 | apTimeout->llStartIdx = 0; 362 | } 363 | 364 | if( allNow < apTimeout->ullStart ) 365 | { 366 | return ; 367 | } 368 | int cnt = allNow - apTimeout->ullStart + 1; 369 | if( cnt > apTimeout->iItemSize ) 370 | { 371 | cnt = apTimeout->iItemSize; 372 | } 373 | if( cnt < 0 ) 374 | { 375 | return; 376 | } 377 | for( int i = 0;illStartIdx + i) % apTimeout->iItemSize; 380 | Join( apResult,apTimeout->pItems + idx ); 381 | } 382 | apTimeout->ullStart = allNow; 383 | apTimeout->llStartIdx += cnt - 1; 384 | 385 | 386 | } 387 | 388 | /* 389 | * CoRoutineFunc - 所有新协程第一次被调度执行时的入口函数, 新协程在该入口函数中被执行 390 | * @param co - (input) 第一次被调度的协程 391 | * @param 未命名指针 - (input) 用于兼容函数类型 392 | * */ 393 | static int CoRoutineFunc( stCoRoutine_t *co,void * ) 394 | { 395 | if( co->pfn ) // 执行协程函数 396 | { 397 | co->pfn( co->arg ); 398 | } 399 | co->cEnd = 1; //执行结束将cEnd置1 400 | 401 | stCoRoutineEnv_t *env = co->env; // 获取当前线程的调度器 402 | 403 | co_yield_env( env ); // 删除调度器的协程数组中最后一个协程 404 | 405 | return 0; 406 | } 407 | 408 | /* 409 | * co_create_env - 分配协程存储空间(stCoRoutine_t)并初始化其中的部分成员变量 410 | * @param env - (input) 当前线程环境,用于初始化协程存储结构stCoRoutine_t 411 | * @param pfn - (input) 协程函数,用于初始化协程存储结构stCoRoutine_t 412 | * @param arg - (input) 协程函数的参数,用于初始化协程存储结构stCoRoutine_t 413 | * @return stCoRoutine_t类型的指针 414 | * */ 415 | struct stCoRoutine_t *co_create_env( stCoRoutineEnv_t * env,pfn_co_routine_t pfn,void *arg ) 416 | { 417 | stCoRoutine_t *lp = (stCoRoutine_t*)malloc( sizeof(stCoRoutine_t) ); // 分配写成存储空间 418 | 419 | memset( lp,0,(long)((stCoRoutine_t*)0)->sRunStack ); // 初始化除sRunStack外的其他内存空间 420 | 421 | lp->env = env; // 初始化协程中的线程环境 422 | lp->pfn = pfn; // 初始化协程函数 423 | lp->arg = arg; // 初始化协程函数的参数 424 | 425 | lp->ctx.ss_sp = lp->sRunStack ; // 初始化协程栈 426 | lp->ctx.ss_size = sizeof(lp->sRunStack) ; // 初始化协程栈大小 427 | 428 | return lp; 429 | } 430 | 431 | /* 432 | * co_create - 创建协程 433 | * @param ppco - (output) 协程指针的地址(传入前未分配内存空间,即未初始化),在函数体中将为协程申请内存空间, 且该内存空间的地址将为ppco赋值 434 | * @param attr - (input) 协程属性 435 | * @param pfn - (input) 协程函数 436 | * @param arg - (input) 协程函数的参数 437 | * @return 成功返回0. 438 | * */ 439 | int co_create( stCoRoutine_t **ppco,const stCoRoutineAttr_t *attr,pfn_co_routine_t pfn,void *arg ) 440 | { 441 | if( !co_get_curr_thread_env() ) 442 | { 443 | co_init_curr_thread_env(); // 初始化协程环境(协程环境其实就是调度器) 444 | } 445 | stCoRoutine_t *co = co_create_env( co_get_curr_thread_env(),pfn,arg ); 446 | *ppco = co; 447 | return 0; 448 | } 449 | 450 | /* 451 | * co_free - 无论协程处于什么状态, 释放协程co占用的内存空间 452 | * @param co - (input) 待释放空间的协程 453 | * @return void 454 | * */ 455 | void co_free( stCoRoutine_t *co ) 456 | { 457 | free( co ); 458 | } 459 | 460 | /* 461 | * co_release - 协程处于执行结束状态, 释放协程co占用的内存空间 462 | * @param co - (input) 待释放空间的协程 463 | * @return void 464 | * */ 465 | void co_release( stCoRoutine_t *co ) 466 | { 467 | if( co->cEnd ) 468 | { 469 | free( co ); 470 | } 471 | } 472 | 473 | /* 474 | * co_resume - 执行协程 475 | * @param co - (input) 待切换的协程 476 | * @return void 477 | * */ 478 | void co_resume( stCoRoutine_t *co ) 479 | { 480 | stCoRoutineEnv_t *env = co->env; // 获取协程co的调度器 481 | stCoRoutine_t *lpCurrRoutine = env->pCallStack[ env->iCallStackSize - 1 ]; // 在协程co的协程环境的协程数组末尾获取当前正在执行的协程lpCurrRoutine 482 | if( !co->cStart ) 483 | { 484 | // 如果当前协程是第一次被调度,则通过入口函数CoRoutineFunc来为其构造上下文 485 | coctx_make( &co->ctx,(coctx_pfn_t)CoRoutineFunc,co,0 ); 486 | co->cStart = 1; 487 | } 488 | env->pCallStack[ env->iCallStackSize++ ] = co; // 将协程co加入到协程环境的协程数组末尾 489 | coctx_swap( &(lpCurrRoutine->ctx),&(co->ctx) ); // 保存当前上下文到lpCurrRoutine->ctx, 并切换到新的上下文co->ctx 490 | } 491 | 492 | /* 493 | * co_yield_env - 删除协程环境的协程数组中最后一个协程(即当前正在执行的协程) 494 | * @param env - (input) 当前线程的调度器 495 | * @return void 496 | * */ 497 | void co_yield_env( stCoRoutineEnv_t *env ) 498 | { 499 | 500 | stCoRoutine_t *last = env->pCallStack[ env->iCallStackSize - 2 ]; // 上次切换协程时, 被当前协程切换出去的协程 501 | stCoRoutine_t *curr = env->pCallStack[ env->iCallStackSize - 1 ]; // 当前协程 502 | 503 | env->iCallStackSize--; // 删除当前协程 504 | 505 | coctx_swap( &curr->ctx, &last->ctx ); // 切换到上次被切换出去的协程last 506 | } 507 | 508 | /* 509 | * co_yield_ct - 删除协程环境的协程数组中最后一个协程(即当前正在执行的协程) 510 | * @return void 511 | * */ 512 | void co_yield_ct() 513 | { 514 | 515 | co_yield_env( co_get_curr_thread_env() ); 516 | } 517 | 518 | /* 519 | * co_yield - 删除协程环境的协程数组中最后一个协程(即当前正在执行的协程) 520 | * @param co - (input) 用于获取调度器 521 | * @return void 522 | * */ 523 | void co_yield( stCoRoutine_t *co ) 524 | { 525 | co_yield_env( co->env ); 526 | } 527 | 528 | 529 | 530 | //int poll(struct pollfd fds[], nfds_t nfds, int timeout); 531 | // { fd,events,revents } 532 | struct stPollItem_t ; 533 | struct stPoll_t : public stTimeoutItem_t 534 | { 535 | struct pollfd *fds; // 待检测的套接字描述符集合 536 | nfds_t nfds; // 待检测的套接字描述符个数 537 | stPollItem_t *pPollItems; // (重点)存储了待检测的每个文件描述符的信息(详见下面注释) 538 | // struct stPollItem_t : public stTimeoutItem_t 539 | // { 540 | // struct pollfd *pSelf; // 待检测的套接字描述符集合 541 | // stPoll_t *pPoll; // 指向存储该stPollItem_t结构的stPoll_t类型变量地址 542 | // struct epoll_event stEvent; // 待检测的套接字描述符的事件 543 | // }; 544 | 545 | int iAllEventDetach; 546 | int iEpollFd; // 由epoll_create函数创建的epoll句柄, 检测事件通过该句柄向内核通知 547 | int iRaiseCnt; // 发生事件的套接字数量 548 | 549 | // struct stTimeoutItem_t 550 | // { 551 | // enum 552 | // { 553 | // eMaxTimeout = 20 * 1000 //20s 554 | // }; 555 | // stTimeoutItem_t *pPrev; 556 | // stTimeoutItem_t *pNext; 557 | // stTimeoutItemLink_t *pLink; 558 | // 559 | // unsigned long long ullExpireTime; // 超时时的系统时间 560 | // 561 | // OnPreparePfn_t pfnPrepare; // 562 | // OnProcessPfn_t pfnProcess; // 事件发生时的回调函数, 其主要功能是恢复pArg指向的协程 563 | // 564 | // void *pArg; // routine // 值为协程结构stCoRoutine_t的指针, 指针指向的协程为该待检测套接字所属的协程, 在事件发生时从该值中获得并恢复协程 565 | // bool bTimeout; // 是否超时标志, True表示超时时间内套接字上没有事件发生, False表示超时时间内套接字上有事件发生 566 | // } 567 | }; 568 | /* 详见stPoll_t说明 */ 569 | struct stPollItem_t : public stTimeoutItem_t 570 | { 571 | struct pollfd *pSelf; 572 | stPoll_t *pPoll; 573 | 574 | struct epoll_event stEvent; 575 | }; 576 | /* 577 | * EPOLLPRI POLLPRI // There is urgent data to read. 578 | * EPOLLMSG POLLMSG 579 | * 580 | * POLLREMOVE 581 | * POLLRDHUP 582 | * POLLNVAL 583 | * 584 | * */ 585 | static uint32_t PollEvent2Epoll( short events ) 586 | { 587 | uint32_t e = 0; 588 | if( events & POLLIN ) e |= EPOLLIN; 589 | if( events & POLLOUT ) e |= EPOLLOUT; 590 | if( events & POLLHUP ) e |= EPOLLHUP; 591 | if( events & POLLERR ) e |= EPOLLERR; 592 | return e; 593 | } 594 | static short EpollEvent2Poll( uint32_t events ) 595 | { 596 | short e = 0; 597 | if( events & EPOLLIN ) e |= POLLIN; 598 | if( events & EPOLLOUT ) e |= POLLOUT; 599 | if( events & EPOLLHUP ) e |= POLLHUP; 600 | if( events & EPOLLERR ) e |= POLLERR; 601 | return e; 602 | } 603 | 604 | /* 协程环境数组, 数组中元素类型为stCoRoutineEnv_t的指针 */ 605 | static stCoRoutineEnv_t* g_arrCoEnvPerThread[ 102400 ] = { 0 }; 606 | 607 | /* 608 | * co_init_curr_thread_env - 为当前线程分配协程环境存储空间(stCoRoutineEnv_t)并初始化其中的部分成员变量 609 | * @return void 610 | * */ 611 | void co_init_curr_thread_env() 612 | { 613 | // (为当前线程)分配调度器存储空间(stCoRoutineEnv_t) 614 | pid_t pid = GetPid(); // 获取当前线程id 615 | g_arrCoEnvPerThread[ pid ] = (stCoRoutineEnv_t*)calloc( 1,sizeof(stCoRoutineEnv_t) ); // 为当前线程分配线程环境的存储空间 616 | stCoRoutineEnv_t *env = g_arrCoEnvPerThread[ pid ]; 617 | printf("init pid %ld env %p\n",(long)pid,env); // Always use %p for pointers in printf 618 | 619 | // 初始化协程环境(stCoRoutineEnv_t)中的部分成员变量 620 | env->iCallStackSize = 0; // 初始化(当前线程)调度器中协程栈大小为0 621 | struct stCoRoutine_t *self = co_create_env( env,NULL,NULL ); // 将当前线程中的上下文包装成主协程 622 | self->cIsMain = 1; 623 | 624 | coctx_init( &self->ctx ); // 将包装好的主协程中的上下文置零 625 | 626 | env->pCallStack[ env->iCallStackSize++ ] = self; // 将包装好的主协程加入调度器的协程数组中 627 | 628 | stCoEpoll_t *ev = AllocEpoll(); // 为调度器创建epoll文件描述符并分配超时链表的存储空间 629 | SetEpoll( env,ev ); // 将ev加入到调度器中 630 | } 631 | 632 | /* 633 | * co_get_curr_thread_env - 获取当前线程的协程环境 634 | * @return 返回当前线程的调度器指针 635 | * */ 636 | stCoRoutineEnv_t *co_get_curr_thread_env() 637 | { 638 | return g_arrCoEnvPerThread[ GetPid() ]; 639 | } 640 | 641 | /* 642 | * OnPollProcessEvent - 事件发生时的回调函数, 其主要功能是恢复pArg指向的协程 643 | * */ 644 | void OnPollProcessEvent( stTimeoutItem_t * ap ) 645 | { 646 | stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; 647 | co_resume( co ); 648 | } 649 | 650 | void OnPollPreparePfn( stTimeoutItem_t * ap,struct epoll_event &e,stTimeoutItemLink_t *active ) 651 | { 652 | stPollItem_t *lp = (stPollItem_t *)ap; 653 | lp->pSelf->revents = EpollEvent2Poll( e.events ); 654 | 655 | 656 | stPoll_t *pPoll = lp->pPoll; 657 | pPoll->iRaiseCnt++; 658 | 659 | if( !pPoll->iAllEventDetach ) 660 | { 661 | pPoll->iAllEventDetach = 1; 662 | 663 | RemoveFromLink( pPoll ); 664 | 665 | AddTail( active,pPoll ); 666 | 667 | } 668 | } 669 | 670 | /* co_eventloop - 事件循环, 作用是检测套接字上的事件并恢复相关协程来处理事件 */ 671 | void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg ) 672 | { 673 | epoll_event *result = (epoll_event*)calloc(1, sizeof(epoll_event) * stCoEpoll_t::_EPOLL_SIZE ); 674 | 675 | for(;;) 676 | { 677 | int ret = epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 ); 678 | 679 | stTimeoutItemLink_t *active = (ctx->pstActiveList); 680 | stTimeoutItemLink_t *timeout = (ctx->pstTimeoutList); 681 | 682 | memset( active,0,sizeof(stTimeoutItemLink_t) ); 683 | memset( timeout,0,sizeof(stTimeoutItemLink_t) ); 684 | 685 | for(int i=0;ipfnPrepare ) 689 | { 690 | item->pfnPrepare( item,result[i],active ); 691 | } 692 | else 693 | { 694 | AddTail( active,item ); 695 | } 696 | } 697 | 698 | 699 | unsigned long long now = GetTickMS(); 700 | TakeAllTimeout( ctx->pTimeout,now,timeout ); 701 | 702 | stTimeoutItem_t *lp = timeout->head; 703 | while( lp ) 704 | { 705 | //printf("raise timeout %p\n",lp); 706 | lp->bTimeout = true; 707 | lp = lp->pNext; 708 | } 709 | 710 | Join( active,timeout ); 711 | 712 | lp = active->head; 713 | while( lp ) 714 | { 715 | 716 | PopHead( active ); 717 | if( lp->pfnProcess ) 718 | { 719 | lp->pfnProcess( lp ); // 恢复协程处理事件 720 | } 721 | 722 | lp = active->head; 723 | } 724 | if( pfn ) 725 | { 726 | if( -1 == pfn( arg ) ) 727 | { 728 | break; 729 | } 730 | } 731 | 732 | } 733 | free( result ); 734 | result = 0; 735 | } 736 | 737 | void OnCoroutineEvent( stTimeoutItem_t * ap ) 738 | { 739 | stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; 740 | co_resume( co ); 741 | } 742 | 743 | /* AllocEpoll - 为当前线程分配stCoEpoll_t类型的存储空间, 并初始化 744 | * @return 函数中分配的stCoEpoll_t类型空间的地址 745 | * */ 746 | stCoEpoll_t *AllocEpoll() 747 | { 748 | stCoEpoll_t *ctx = (stCoEpoll_t*)calloc( 1,sizeof(stCoEpoll_t) ); 749 | 750 | ctx->iEpollFd = epoll_create( stCoEpoll_t::_EPOLL_SIZE ); 751 | ctx->pTimeout = AllocTimeout( 60 * 1000 ); 752 | 753 | ctx->pstActiveList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) ); 754 | ctx->pstTimeoutList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) ); 755 | 756 | 757 | return ctx; 758 | } 759 | 760 | /* FreeEpoll - 释放当前线程中的stCoEpoll_t类型的存储空间 761 | * @param ctx (input) 待释放的stCoEpoll_t类型存储空间的地址 762 | * @return void 763 | * */ 764 | void FreeEpoll( stCoEpoll_t *ctx ) 765 | { 766 | if( ctx ) 767 | { 768 | free( ctx->pstActiveList ); 769 | free( ctx->pstTimeoutList ); 770 | FreeTimeout( ctx->pTimeout ); 771 | } 772 | free( ctx ); 773 | } 774 | 775 | /* GetCurrCo - 获取某一协程环境中正在执行的协程 776 | * @param env (input) 协程环境 777 | * return 正在执行的协程的地址 778 | * */ 779 | stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env ) 780 | { 781 | return env->pCallStack[ env->iCallStackSize - 1 ]; 782 | } 783 | 784 | /* GetCurrThreadCo - 获取当前线程中正在执行的协程 785 | * @param env (input) 协程环境 786 | * return 正在执行的协程的地址 787 | * */ 788 | stCoRoutine_t *GetCurrThreadCo( ) 789 | { 790 | stCoRoutineEnv_t *env = co_get_curr_thread_env(); 791 | if( !env ) return 0; 792 | return GetCurrCo(env); 793 | } 794 | 795 | 796 | /* co_poll - 该函数主要向内核注册套接字上待监听的事件, 然后切换协程, 当该协程被恢复时即说明程序结束, 然后处理善后工作 */ 797 | int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout ) 798 | { 799 | 800 | if( timeout > stTimeoutItem_t::eMaxTimeout ) 801 | { 802 | timeout = stTimeoutItem_t::eMaxTimeout; 803 | } 804 | int epfd = ctx->iEpollFd; 805 | 806 | //1.struct change 807 | stPoll_t arg; 808 | memset( &arg,0,sizeof(arg) ); 809 | 810 | arg.iEpollFd = epfd; 811 | arg.fds = fds; 812 | arg.nfds = nfds; 813 | 814 | stPollItem_t arr[2]; 815 | if( nfds < sizeof(arr) / sizeof(arr[0]) ) 816 | { 817 | arg.pPollItems = arr; 818 | } 819 | else 820 | { 821 | arg.pPollItems = (stPollItem_t*)malloc( nfds * sizeof( stPollItem_t ) ); 822 | } 823 | memset( arg.pPollItems,0,nfds * sizeof(stPollItem_t) ); 824 | 825 | arg.pfnProcess = OnPollProcessEvent; 826 | arg.pArg = GetCurrCo( co_get_curr_thread_env() ); 827 | 828 | //2.add timeout 829 | 830 | unsigned long long now = GetTickMS(); 831 | arg.ullExpireTime = now + timeout; 832 | int ret = AddTimeout( ctx->pTimeout,&arg,now ); 833 | if( ret != 0 ) 834 | { 835 | co_log_err("CO_ERR: AddTimeout ret %d now %lld timeout %d arg.ullExpireTime %lld", 836 | ret,now,timeout,arg.ullExpireTime); 837 | errno = EINVAL; 838 | return -__LINE__; 839 | } 840 | //3. add epoll 841 | 842 | for(nfds_t i=0;i -1 ) 851 | { 852 | ev.data.ptr = arg.pPollItems + i; 853 | ev.events = PollEvent2Epoll( fds[i].events ); 854 | 855 | epoll_ctl( epfd,EPOLL_CTL_ADD, fds[i].fd, &ev ); 856 | } 857 | //if fail,the timeout would work 858 | 859 | } 860 | 861 | co_yield_env( co_get_curr_thread_env() ); 862 | 863 | RemoveFromLink( &arg ); 864 | for(nfds_t i = 0;i < nfds;i++) 865 | { 866 | int fd = fds[i].fd; 867 | if( fd > -1 ) 868 | { 869 | epoll_ctl( epfd,EPOLL_CTL_DEL,fd,&arg.pPollItems[i].stEvent ); 870 | } 871 | } 872 | 873 | 874 | if( arg.pPollItems != arr ) 875 | { 876 | free( arg.pPollItems ); 877 | arg.pPollItems = NULL; 878 | } 879 | return arg.iRaiseCnt; 880 | } 881 | 882 | void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev ) 883 | { 884 | env->pEpoll = ev; 885 | } 886 | 887 | /* co_get_epoll_ct - 获取(当前线程中)协程环境中的epoll实例 */ 888 | stCoEpoll_t *co_get_epoll_ct() 889 | { 890 | if( !co_get_curr_thread_env() ) 891 | { 892 | co_init_curr_thread_env(); 893 | } 894 | return co_get_curr_thread_env()->pEpoll; 895 | } 896 | struct stHookPThreadSpec_t 897 | { 898 | stCoRoutine_t *co; 899 | void *value; 900 | 901 | enum 902 | { 903 | size = 1024 904 | }; 905 | }; 906 | void *co_getspecific(pthread_key_t key) 907 | { 908 | stCoRoutine_t *co = GetCurrThreadCo(); 909 | if( !co || co->cIsMain ) 910 | { 911 | return pthread_getspecific( key ); 912 | } 913 | return co->aSpec[ key ].value; 914 | } 915 | int co_setspecific(pthread_key_t key, const void *value) 916 | { 917 | stCoRoutine_t *co = GetCurrThreadCo(); 918 | if( !co || co->cIsMain ) 919 | { 920 | return pthread_setspecific( key,value ); 921 | } 922 | co->aSpec[ key ].value = (void*)value; 923 | return 0; 924 | } 925 | 926 | 927 | /* 928 | * co_disable_hook_sys - 禁止hook系统调用 929 | * return void 930 | * */ 931 | void co_disable_hook_sys() 932 | { 933 | stCoRoutine_t *co = GetCurrThreadCo(); 934 | if( co ) 935 | { 936 | co->cEnableSysHook = 0; 937 | } 938 | } 939 | 940 | /* 941 | * co_is_enable_sys_hook - 判断协程中的系统调用是否被hook 942 | * @return hook了系统调用返回true, 否则返回false 943 | * */ 944 | bool co_is_enable_sys_hook() 945 | { 946 | stCoRoutine_t *co = GetCurrThreadCo(); 947 | return ( co && co->cEnableSysHook ); 948 | } 949 | 950 | 951 | stCoRoutine_t *co_self() 952 | { 953 | return GetCurrThreadCo(); 954 | } 955 | -------------------------------------------------------------------------------- /co_routine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef __CO_ROUTINE_H__ 20 | #define __CO_ROUTINE_H__ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | //1.struct 27 | 28 | struct stCoRoutine_t; 29 | struct stCoRoutineAttr_t 30 | { 31 | int stack_size; 32 | char reserve[ 60 ]; 33 | 34 | }__attribute__ ((packed)); 35 | 36 | struct stCoEpoll_t; 37 | typedef int (*pfn_co_eventloop_t)(void *); 38 | typedef void *(*pfn_co_routine_t)( void * ); 39 | 40 | //2.co_routine 41 | 42 | int co_create( stCoRoutine_t **co,const stCoRoutineAttr_t *attr,void *(*routine)(void*),void *arg ); 43 | void co_resume( stCoRoutine_t *co ); 44 | void co_yield( stCoRoutine_t *co ); 45 | void co_yield_ct(); //ct = current thread 46 | void co_release( stCoRoutine_t *co ); 47 | 48 | stCoRoutine_t *co_self(); 49 | 50 | int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms ); 51 | void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg ); 52 | 53 | //3.specific 54 | 55 | int co_setspecific( pthread_key_t key, const void *value ); 56 | void * co_getspecific( pthread_key_t key ); 57 | 58 | //4.event 59 | 60 | stCoEpoll_t * co_get_epoll_ct(); //ct = current thread 61 | 62 | //5.hook syscall ( poll/read/write/recv/send/recvfrom/sendto ) 63 | 64 | void co_enable_hook_sys(); 65 | void co_disable_hook_sys(); 66 | bool co_is_enable_sys_hook(); 67 | 68 | void co_log_err( const char *fmt,... ); 69 | #endif 70 | 71 | -------------------------------------------------------------------------------- /co_routine_inner.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | #ifndef __CO_ROUTINE_INNER_H__ 21 | 22 | #include "co_routine.h" 23 | #include "coctx.h" 24 | struct stCoRoutineEnv_t; 25 | struct stCoSpec_t 26 | { 27 | void *value; 28 | }; 29 | struct stCoRoutine_t 30 | { 31 | stCoRoutineEnv_t *env; 32 | pfn_co_routine_t pfn; 33 | void *arg; 34 | //ucontext_t ctx; 35 | coctx_t ctx; 36 | char cStart; 37 | char cEnd; 38 | stCoSpec_t aSpec[1024]; 39 | char cIsMain; 40 | char cEnableSysHook; 41 | 42 | char sRunStack[ 1024 * 128 ]; 43 | 44 | }; 45 | 46 | 47 | 48 | //1.env 49 | void co_init_curr_thread_env(); 50 | stCoRoutineEnv_t * co_get_curr_thread_env(); 51 | 52 | //2.coroutine 53 | void co_free( stCoRoutine_t * co ); 54 | void co_yield_env( stCoRoutineEnv_t *env ); 55 | 56 | //3.func 57 | 58 | 59 | 60 | //----------------------------------------------------------------------------------------------- 61 | 62 | struct stTimeout_t; 63 | struct stTimeoutItem_t ; 64 | 65 | stTimeout_t *AllocTimeout( int iSize ); 66 | void FreeTimeout( stTimeout_t *apTimeout ); 67 | int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,uint64_t allNow ); 68 | 69 | struct stCoEpoll_t; 70 | stCoEpoll_t * AllocEpoll(); 71 | void FreeEpoll( stCoEpoll_t *ctx ); 72 | 73 | stCoRoutine_t * GetCurrThreadCo(); 74 | void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev ); 75 | 76 | typedef void (*pfnCoRoutineFunc_t)(); 77 | 78 | #endif 79 | 80 | #define __CO_ROUTINE_INNER_H__ 81 | -------------------------------------------------------------------------------- /coctx.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "coctx.h" 20 | #include 21 | 22 | 23 | #define ESP 0 24 | #define EIP 1 25 | // ----------- 26 | #define RSP 0 27 | #define RIP 1 28 | #define RBX 2 29 | #define RDI 3 30 | #define RSI 4 31 | 32 | extern "C" 33 | { 34 | extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap"); 35 | }; 36 | #if defined(__i386__) 37 | int coctx_init( coctx_t *ctx ) 38 | { 39 | memset( ctx,0,sizeof(*ctx)); 40 | return 0; 41 | } 42 | int coctx_make( coctx_t *ctx,coctx_pfn_t pfn,const void *s,const void *s1 ) 43 | { 44 | 45 | char *sp = ctx->ss_sp + ctx->ss_size ; 46 | sp = (char*)((unsigned long)sp & -16L); 47 | 48 | int len = sizeof(coctx_param_t) + 64; 49 | memset( sp - len,0,len ); 50 | ctx->routine = pfn; 51 | ctx->s1 = s; 52 | ctx->s2 = s1; 53 | 54 | ctx->param = (coctx_param_t*)sp ; 55 | ctx->param->f = pfn; 56 | ctx->param->f_link = 0; 57 | ctx->param->s1 = s; 58 | ctx->param->s2 = s1; 59 | 60 | ctx->regs[ ESP ] = (char*)(ctx->param) + sizeof(void*); 61 | ctx->regs[ EIP ] = (char*)pfn; 62 | 63 | 64 | return 0; 65 | } 66 | #elif defined(__x86_64__) 67 | int coctx_make( coctx_t *ctx,coctx_pfn_t pfn,const void *s,const void *s1 ) 68 | { 69 | char *stack = ctx->ss_sp; 70 | *stack = 0; 71 | 72 | char *sp = stack + ctx->ss_size - 1; 73 | sp = (char*)( ( (unsigned long)sp & -16LL ) - 8); 74 | 75 | int len = sizeof(coctx_param_t) + 64; 76 | memset( sp - len,0,len ); 77 | 78 | ctx->routine = pfn; 79 | ctx->s1 = s; 80 | ctx->s2 = s1; 81 | 82 | ctx->param = (coctx_param_t*)sp; 83 | ctx->param->f = pfn; 84 | ctx->param->f_link = 0; 85 | ctx->param->s1 = s; 86 | ctx->param->s2 = s1; 87 | 88 | ctx->regs[ RBX ] = stack + ctx->ss_size - 1; 89 | ctx->regs[ RSP ] = (char*)(ctx->param) + 8; 90 | ctx->regs[ RIP ] = (char*)pfn; 91 | 92 | ctx->regs[ RDI ] = (char*)s; 93 | ctx->regs[ RSI ] = (char*)s1; 94 | 95 | return 0; 96 | } 97 | int coctx_init( coctx_t *ctx ) 98 | { 99 | memset( ctx,0,sizeof(*ctx)); 100 | return 0; 101 | } 102 | 103 | #endif 104 | 105 | -------------------------------------------------------------------------------- /coctx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef __CO_CTX_H__ 20 | #define __CO_CTX_H__ 21 | #include 22 | typedef int (*coctx_pfn_t)( const char *s,const char *s2 ); 23 | struct coctx_param_t 24 | { 25 | coctx_pfn_t f; 26 | coctx_pfn_t f_link; 27 | const void *s1; 28 | const void *s2; 29 | }; 30 | struct coctx_t 31 | { 32 | void *regs[ 5 ]; 33 | 34 | coctx_param_t *param; 35 | 36 | coctx_pfn_t routine; 37 | const void *s1; 38 | const void *s2; 39 | size_t ss_size; 40 | char *ss_sp; 41 | 42 | }; 43 | int coctx_init( coctx_t *ctx ); 44 | int coctx_make( coctx_t *ctx,coctx_pfn_t pfn,const void *s,const void *s1 ); 45 | #endif 46 | -------------------------------------------------------------------------------- /coctx_swap.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #define _esp 0 20 | #define _eip 4 21 | /* ------ */ 22 | #define _rsp 0 23 | #define _rip 8 24 | #define _rbx 16 25 | #define _rdi 24 26 | #define _rsi 32 27 | 28 | .globl coctx_swap 29 | .type coctx_swap, @function 30 | coctx_swap: 31 | 32 | #if defined(__i386__) 33 | 34 | leal -4(%esp),%esp 35 | pushl %ebx 36 | pushl %ecx 37 | pushl %edx 38 | 39 | pushl %edi 40 | pushl %esi 41 | pushl %ebp 42 | leal 28(%esp),%esp 43 | 44 | /* sp,ip */ 45 | /* param old ctx */ 46 | movl 4(%esp), %eax 47 | 48 | movl (%esp), %ecx 49 | movl %ecx, _eip(%eax) 50 | 51 | leal 4(%esp), %ecx 52 | movl %ecx, _esp(%eax) 53 | 54 | /* param new ctx */ 55 | movl 8(%esp), %eax 56 | 57 | /* push ret ( ip,sp ) */ 58 | movl _eip(%eax), %ecx 59 | movl _esp(%eax), %esp 60 | pushl %ecx 61 | 62 | leal -28(%esp),%esp 63 | popl %ebp 64 | popl %esi 65 | popl %edi 66 | 67 | popl %edx 68 | popl %ecx 69 | popl %ebx 70 | leal 4(%esp),%esp 71 | 72 | movl $0, %eax 73 | ret 74 | 75 | #elif defined(__x86_64__) 76 | 77 | leaq -8(%rsp),%rsp 78 | pushq %rbp 79 | pushq %r12 80 | pushq %r13 81 | pushq %r14 82 | pushq %r15 83 | pushq %rdx 84 | pushq %rcx 85 | pushq %r8 86 | pushq %r9 87 | leaq 80(%rsp),%rsp 88 | 89 | movq %rbx,_rbx(%rdi) 90 | movq %rdi,_rdi(%rdi) 91 | movq %rsi,_rsi(%rdi) 92 | /* sp */ 93 | movq (%rsp), %rcx 94 | movq %rcx, _rip(%rdi) 95 | leaq 8(%rsp), %rcx 96 | movq %rcx, _rsp(%rdi) 97 | 98 | /* sp */ 99 | movq _rip(%rsi), %rcx 100 | movq _rsp(%rsi), %rsp 101 | pushq %rcx 102 | 103 | movq _rbx(%rsi),%rbx 104 | movq _rdi(%rsi),%rdi 105 | movq _rsi(%rsi),%rsi 106 | 107 | leaq -80(%rsp),%rsp 108 | popq %r9 109 | popq %r8 110 | popq %rcx 111 | popq %rdx 112 | popq %r15 113 | popq %r14 114 | popq %r13 115 | popq %r12 116 | popq %rbp 117 | leaq 8(%rsp),%rsp 118 | 119 | xorl %eax, %eax 120 | ret 121 | #endif 122 | -------------------------------------------------------------------------------- /example_echocli.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "co_routine.h" 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | using namespace std; 38 | struct stEndPoint 39 | { 40 | char *ip; 41 | unsigned short int port; 42 | }; 43 | 44 | static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) 45 | { 46 | bzero(&addr,sizeof(addr)); 47 | addr.sin_family = AF_INET; 48 | addr.sin_port = htons(shPort); 49 | int nIP = 0; 50 | if( !pszIP || '\0' == *pszIP 51 | || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") 52 | || 0 == strcmp(pszIP,"*") 53 | ) 54 | { 55 | nIP = htonl(INADDR_ANY); 56 | } 57 | else 58 | { 59 | nIP = inet_addr(pszIP); 60 | } 61 | addr.sin_addr.s_addr = nIP; 62 | 63 | } 64 | 65 | static int iSuccCnt = 0; 66 | static int iFailCnt = 0; 67 | static int iTime = 0; 68 | 69 | void AddSuccCnt() 70 | { 71 | int now = time(NULL); 72 | if (now >iTime) 73 | { 74 | printf("time %d Succ Cnt %d Fail Cnt %d\n", iTime, iSuccCnt, iFailCnt); 75 | iTime = now; 76 | iSuccCnt = 0; 77 | iFailCnt = 0; 78 | } 79 | else 80 | { 81 | iSuccCnt++; 82 | } 83 | } 84 | void AddFailCnt() 85 | { 86 | int now = time(NULL); 87 | if (now >iTime) 88 | { 89 | printf("time %d Succ Cnt %d Fail Cnt %d\n", iTime, iSuccCnt, iFailCnt); 90 | iTime = now; 91 | iSuccCnt = 0; 92 | iFailCnt = 0; 93 | } 94 | else 95 | { 96 | iFailCnt++; 97 | } 98 | } 99 | 100 | static void *readwrite_routine( void *arg ) 101 | { 102 | 103 | co_enable_hook_sys(); 104 | 105 | stEndPoint *endpoint = (stEndPoint *)arg; 106 | char str[8]="sarlmol"; 107 | char buf[ 1024 * 16 ]; 108 | int fd = -1; 109 | int ret = 0; 110 | for(;;) 111 | { 112 | if ( fd < 0 ) 113 | { 114 | fd = socket(PF_INET, SOCK_STREAM, 0); 115 | struct sockaddr_in addr; 116 | SetAddr(endpoint->ip, endpoint->port, addr); 117 | ret = connect(fd,(struct sockaddr*)&addr,sizeof(addr)); 118 | 119 | if ( errno == EALREADY || errno == EINPROGRESS ) 120 | { 121 | struct pollfd pf = { 0 }; 122 | pf.fd = fd; 123 | pf.events = (POLLOUT|POLLERR|POLLHUP); 124 | co_poll( co_get_epoll_ct(),&pf,1,200); 125 | //check connect 126 | int error = 0; 127 | uint32_t socklen = sizeof(error); 128 | errno = 0; 129 | ret = getsockopt(fd, SOL_SOCKET, SO_ERROR,(void *)&error, &socklen); 130 | if ( ret == -1 ) 131 | { 132 | //printf("getsockopt ERROR ret %d %d:%s\n", ret, errno, strerror(errno)); 133 | close(fd); 134 | fd = -1; 135 | AddFailCnt(); 136 | continue; 137 | } 138 | if ( error ) 139 | { 140 | errno = error; 141 | //printf("connect ERROR ret %d %d:%s\n", error, errno, strerror(errno)); 142 | close(fd); 143 | fd = -1; 144 | AddFailCnt(); 145 | continue; 146 | } 147 | } 148 | 149 | } 150 | 151 | ret = write( fd,str, 8); 152 | if ( ret > 0 ) 153 | { 154 | ret = read( fd,buf, sizeof(buf) ); 155 | if ( ret <= 0 ) 156 | { 157 | //printf("co %p read ret %d errno %d (%s)\n", 158 | // co_self(), ret,errno,strerror(errno)); 159 | close(fd); 160 | fd = -1; 161 | AddFailCnt(); 162 | } 163 | else 164 | { 165 | //printf("echo %s fd %d\n", buf,fd); 166 | AddSuccCnt(); 167 | } 168 | } 169 | else 170 | { 171 | //printf("co %p write ret %d errno %d (%s)\n", 172 | // co_self(), ret,errno,strerror(errno)); 173 | close(fd); 174 | fd = -1; 175 | AddFailCnt(); 176 | } 177 | } 178 | return 0; 179 | } 180 | 181 | int main(int argc,char *argv[]) 182 | { 183 | stEndPoint endpoint; 184 | endpoint.ip = argv[1]; 185 | endpoint.port = atoi(argv[2]); 186 | int cnt = atoi( argv[3] ); 187 | int proccnt = atoi( argv[4] ); 188 | 189 | struct sigaction sa; 190 | sa.sa_handler = SIG_IGN; 191 | sigaction( SIGPIPE, &sa, NULL ); 192 | 193 | for(int k=0;k 0 ) 198 | { 199 | continue; 200 | } 201 | else if( pid < 0 ) 202 | { 203 | break; 204 | } 205 | for(int i=0;i 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | struct task_t 38 | { 39 | stCoRoutine_t *co; 40 | int fd; 41 | }; 42 | 43 | static stack g_readwrite; 44 | static int g_listen_fd = -1; 45 | static int SetNonBlock(int iSock) 46 | { 47 | int iFlags; 48 | 49 | iFlags = fcntl(iSock, F_GETFL, 0); 50 | iFlags |= O_NONBLOCK; 51 | iFlags |= O_NDELAY; 52 | int ret = fcntl(iSock, F_SETFL, iFlags); 53 | return ret; 54 | } 55 | 56 | static void *readwrite_routine( void *arg ) 57 | { 58 | 59 | co_enable_hook_sys(); 60 | 61 | task_t *co = (task_t*)arg; 62 | char buf[ 1024 * 16 ]; 63 | for(;;) 64 | { 65 | if( -1 == co->fd ) 66 | { 67 | g_readwrite.push( co ); 68 | co_yield_ct(); 69 | continue; 70 | } 71 | 72 | int fd = co->fd; 73 | co->fd = -1; 74 | 75 | for(;;) 76 | { 77 | struct pollfd pf = { 0 }; 78 | pf.fd = fd; 79 | pf.events = (POLLIN|POLLERR|POLLHUP); 80 | co_poll( co_get_epoll_ct(),&pf,1,1000); 81 | 82 | int ret = read( fd,buf,sizeof(buf) ); 83 | if( ret > 0 ) 84 | { 85 | ret = write( fd,buf,ret ); 86 | } 87 | if( ret <= 0 ) 88 | { 89 | close( fd ); 90 | break; 91 | } 92 | } 93 | 94 | } 95 | return 0; 96 | } 97 | int co_accept(int fd, struct sockaddr *addr, socklen_t *len ); 98 | static void *accept_routine( void * ) 99 | { 100 | co_enable_hook_sys(); 101 | printf("accept_routine\n"); 102 | fflush(stdout); 103 | for(;;) 104 | { 105 | //printf("pid %ld g_readwrite.size %ld\n",getpid(),g_readwrite.size()); 106 | if( g_readwrite.empty() ) 107 | { 108 | printf("empty\n"); //sleep 109 | struct pollfd pf = { 0 }; 110 | pf.fd = -1; 111 | poll( &pf,1,1000); 112 | 113 | continue; 114 | 115 | } 116 | struct sockaddr_in addr; //maybe sockaddr_un; 117 | memset( &addr,0,sizeof(addr) ); 118 | socklen_t len = sizeof(addr); 119 | 120 | int fd = co_accept(g_listen_fd, (struct sockaddr *)&addr, &len); 121 | if( fd < 0 ) 122 | { 123 | struct pollfd pf = { 0 }; 124 | pf.fd = g_listen_fd; 125 | pf.events = (POLLIN|POLLERR|POLLHUP); 126 | co_poll( co_get_epoll_ct(),&pf,1,1000 ); 127 | continue; 128 | } 129 | if( g_readwrite.empty() ) 130 | { 131 | close( fd ); 132 | continue; 133 | } 134 | SetNonBlock( fd ); 135 | task_t *co = g_readwrite.top(); 136 | co->fd = fd; 137 | g_readwrite.pop(); 138 | co_resume( co->co ); 139 | } 140 | return 0; 141 | } 142 | 143 | static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) 144 | { 145 | bzero(&addr,sizeof(addr)); 146 | addr.sin_family = AF_INET; 147 | addr.sin_port = htons(shPort); 148 | int nIP = 0; 149 | if( !pszIP || '\0' == *pszIP 150 | || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") 151 | || 0 == strcmp(pszIP,"*") 152 | ) 153 | { 154 | nIP = htonl(INADDR_ANY); 155 | } 156 | else 157 | { 158 | nIP = inet_addr(pszIP); 159 | } 160 | addr.sin_addr.s_addr = nIP; 161 | 162 | } 163 | 164 | static int CreateTcpSocket(const unsigned short shPort /* = 0 */,const char *pszIP /* = "*" */,bool bReuse /* = false */) 165 | { 166 | int fd = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP); 167 | if( fd >= 0 ) 168 | { 169 | if(shPort != 0) 170 | { 171 | if(bReuse) 172 | { 173 | int nReuseAddr = 1; 174 | setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&nReuseAddr,sizeof(nReuseAddr)); 175 | } 176 | struct sockaddr_in addr ; 177 | SetAddr(pszIP,shPort,addr); 178 | int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); 179 | if( ret != 0) 180 | { 181 | close(fd); 182 | return -1; 183 | } 184 | } 185 | } 186 | return fd; 187 | } 188 | 189 | 190 | int main(int argc,char *argv[]) 191 | { 192 | const char *ip = argv[1]; 193 | int port = atoi( argv[2] ); 194 | int cnt = atoi( argv[3] ); 195 | int proccnt = atoi( argv[4] ); 196 | 197 | g_listen_fd = CreateTcpSocket( port,ip,true ); 198 | listen( g_listen_fd,1024 ); 199 | printf("listen %d %s:%d\n",g_listen_fd,ip,port); 200 | 201 | SetNonBlock( g_listen_fd ); 202 | 203 | for(int k=0;k 0 ) 208 | { 209 | continue; 210 | } 211 | else if( pid < 0 ) 212 | { 213 | break; 214 | } 215 | for(int i=0;ifd = -1; 219 | 220 | co_create( &(task->co),NULL,readwrite_routine,task ); 221 | co_resume( task->co ); 222 | 223 | } 224 | stCoRoutine_t *accept_co = NULL; 225 | co_create( &accept_co,NULL,accept_routine,0 ); 226 | co_resume( accept_co ); 227 | 228 | co_eventloop( co_get_epoll_ct(),0,0 ); 229 | 230 | exit(0); 231 | } 232 | return 0; 233 | } 234 | 235 | -------------------------------------------------------------------------------- /example_poll.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include "co_routine.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | using namespace std; 37 | 38 | struct task_t 39 | { 40 | stCoRoutine_t *co; 41 | int fd; 42 | struct sockaddr_in addr; 43 | }; 44 | 45 | static int SetNonBlock(int iSock) 46 | { 47 | int iFlags; 48 | 49 | iFlags = fcntl(iSock, F_GETFL, 0); 50 | iFlags |= O_NONBLOCK; 51 | iFlags |= O_NDELAY; 52 | int ret = fcntl(iSock, F_SETFL, iFlags); 53 | return ret; 54 | } 55 | 56 | 57 | 58 | static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) 59 | { 60 | bzero(&addr,sizeof(addr)); 61 | addr.sin_family = AF_INET; 62 | addr.sin_port = htons(shPort); 63 | int nIP = 0; 64 | if( !pszIP || '\0' == *pszIP 65 | || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") 66 | || 0 == strcmp(pszIP,"*") 67 | ) 68 | { 69 | nIP = htonl(INADDR_ANY); 70 | } 71 | else 72 | { 73 | nIP = inet_addr(pszIP); 74 | } 75 | addr.sin_addr.s_addr = nIP; 76 | 77 | } 78 | 79 | static int CreateTcpSocket(const unsigned short shPort = 0 ,const char *pszIP = "*" ,bool bReuse = false ) 80 | { 81 | int fd = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP); 82 | if( fd >= 0 ) 83 | { 84 | if(shPort != 0) 85 | { 86 | if(bReuse) 87 | { 88 | int nReuseAddr = 1; 89 | setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&nReuseAddr,sizeof(nReuseAddr)); 90 | } 91 | struct sockaddr_in addr ; 92 | SetAddr(pszIP,shPort,addr); 93 | int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); 94 | if( ret != 0) 95 | { 96 | close(fd); 97 | return -1; 98 | } 99 | } 100 | } 101 | return fd; 102 | } 103 | 104 | static void *poll_routine( void *arg ) 105 | { 106 | co_enable_hook_sys(); 107 | 108 | vector &v = *(vector*)arg; 109 | for(size_t i=0;i setRaiseFds; 127 | size_t iWaitCnt = v.size(); 128 | for(;;) 129 | { 130 | int ret = poll( pf,iWaitCnt,1000 ); 131 | printf("co %p poll wait %ld ret %d\n", 132 | co_self(),iWaitCnt,ret); 133 | for(int i=0;i v; 178 | for(int i=1;i v2 = v; 188 | poll_routine( &v2 ); 189 | printf("--------------------- routine -------------------\n"); 190 | 191 | for(int i=0;i<10;i++) 192 | { 193 | stCoRoutine_t *co = 0; 194 | vector *v2 = new vector(); 195 | *v2 = v; 196 | co_create( &co,NULL,poll_routine,v2 ); 197 | printf("routine i %d\n",i); 198 | co_resume( co ); 199 | } 200 | 201 | co_eventloop( co_get_epoll_ct(),0,0 ); 202 | 203 | return 0; 204 | } 205 | //./example_poll 127.0.0.1 12365 127.0.0.1 12222 192.168.1.1 1000 192.168.1.2 1111 206 | 207 | -------------------------------------------------------------------------------- /example_thread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | 21 | #include "co_routine.h" 22 | #include "co_routine_inner.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | int loop(void *) 31 | { 32 | return 0; 33 | } 34 | static void *routine_func( void * ) 35 | { 36 | stCoEpoll_t * ev = co_get_epoll_ct(); //ct = current thread 37 | co_eventloop( ev,loop,0 ); 38 | return 0; 39 | } 40 | int main(int argc,char *argv[]) 41 | { 42 | int cnt = atoi( argv[1] ); 43 | 44 | pthread_t tid[ cnt ]; 45 | for(int i=0;i