Repository: Tencent/libco Branch: master Commit: dc6aafcc5e64 Files: 27 Total size: 104.0 KB Directory structure: gitextract_h3r4eeci/ ├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── Makefile ├── co.mk ├── co_closure.h ├── co_comm.cpp ├── co_comm.h ├── co_epoll.cpp ├── co_epoll.h ├── co_hook_sys_call.cpp ├── co_routine.cpp ├── co_routine.h ├── co_routine_inner.h ├── co_routine_specific.h ├── coctx.cpp ├── coctx.h ├── coctx_swap.S ├── example_closure.cpp ├── example_cond.cpp ├── example_copystack.cpp ├── example_echocli.cpp ├── example_echosvr.cpp ├── example_poll.cpp ├── example_setenv.cpp ├── example_specific.cpp └── example_thread.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ README.md # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 2.8) project(libco) # This for mac osx only set(CMAKE_MACOSX_RPATH 0) # Set lib version set(LIBCO_VERSION 0.5) # Set cflags set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -g -fno-strict-aliasing -O2 -Wall -export-dynamic -Wall -pipe -D_GNU_SOURCE -D_REENTRANT -fPIC -Wno-deprecated -m64) # Use c and asm enable_language(C ASM) # Add source files set(SOURCE_FILES co_epoll.cpp co_hook_sys_call.cpp co_routine.cpp coctx.cpp coctx_swap.S) # Add static and shared library target add_library(colib_static STATIC ${SOURCE_FILES}) add_library(colib_shared SHARED ${SOURCE_FILES}) # Set library output name set_target_properties(colib_static PROPERTIES OUTPUT_NAME colib) set_target_properties(colib_shared PROPERTIES OUTPUT_NAME colib) set_target_properties(colib_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) set_target_properties(colib_shared PROPERTIES CLEAN_DIRECT_OUTPUT 1) # Set shared library version, will generate libcolib.${LIBCO_VERSION}.so and a symbol link named libcolib.so # For mac osx, the extension name will be .dylib set_target_properties(colib_shared PROPERTIES VERSION ${LIBCO_VERSION} SOVERSION ${LIBCO_VERSION}) # Macro for add example target macro(add_example_target EXAMPLE_TARGET) add_executable("example_${EXAMPLE_TARGET}" "example_${EXAMPLE_TARGET}.cpp") target_link_libraries("example_${EXAMPLE_TARGET}" colib_static pthread dl) endmacro(add_example_target) add_example_target(closure) add_example_target(cond) add_example_target(copystack) add_example_target(echocli) add_example_target(echosvr) add_example_target(poll) add_example_target(setenv) add_example_target(specific) add_example_target(thread) ================================================ FILE: Makefile ================================================ # # Tencent is pleased to support the open source community by making Libco available. # # Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # COMM_MAKE = 1 COMM_ECHO = 1 version=0.5 v=debug include co.mk ########## options ########## CFLAGS += -g -fno-strict-aliasing -O2 -Wall -export-dynamic \ -Wall -pipe -D_GNU_SOURCE -D_REENTRANT -fPIC -Wno-deprecated -m64 UNAME := $(shell uname -s) ifeq ($(UNAME), FreeBSD) LINKS += -g -L./lib -lcolib -lpthread else LINKS += -g -L./lib -lcolib -lpthread -ldl endif COLIB_OBJS=co_epoll.o co_routine.o co_hook_sys_call.o coctx_swap.o coctx.o co_comm.o #co_swapcontext.o PROGS = colib example_poll example_echosvr example_echocli example_thread example_cond example_specific example_copystack example_closure example_setenv all:$(PROGS) colib:libcolib.a libcolib.so libcolib.a: $(COLIB_OBJS) $(ARSTATICLIB) libcolib.so: $(COLIB_OBJS) $(BUILDSHARELIB) example_echosvr:example_echosvr.o $(BUILDEXE) example_echocli:example_echocli.o $(BUILDEXE) example_thread:example_thread.o $(BUILDEXE) example_poll:example_poll.o $(BUILDEXE) example_exit:example_exit.o $(BUILDEXE) example_cond:example_cond.o $(BUILDEXE) example_specific:example_specific.o $(BUILDEXE) example_copystack:example_copystack.o $(BUILDEXE) example_setenv:example_setenv.o $(BUILDEXE) example_closure:example_closure.o $(BUILDEXE) dist: clean libco-$(version).src.tar.gz libco-$(version).src.tar.gz: @find . -type f | grep -v CVS | grep -v .svn | sed s:^./:libco-$(version)/: > MANIFEST @(cd ..; ln -s libco_pub libco-$(version)) (cd ..; tar cvf - `cat libco_pub/MANIFEST` | gzip > libco_pub/libco-$(version).src.tar.gz) @(cd ..; rm libco-$(version)) clean: $(CLEAN) *.o $(PROGS) rm -fr MANIFEST lib solib libco-$(version).src.tar.gz libco-$(version) ================================================ FILE: co.mk ================================================ # # Tencent is pleased to support the open source community by making Libco available. # # Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ##### Makefile Rules ########## MAIL_ROOT=. SRCROOT=. ##define the compliers CPP = $(CXX) AR = ar -rc RANLIB = ranlib CPPSHARE = $(CPP) -fPIC -shared -O2 -pipe -L$(SRCROOT)/solib/ -o CSHARE = $(CC) -fPIC -shared -O2 -pipe -L$(SRCROOT)/solib/ -o ifeq ($v,release) CFLAGS= -O2 $(INCLS) -fPIC -DLINUX -pipe -Wno-deprecated -c else CFLAGS= -g $(INCLS) -fPIC -DLINUX -pipe -c -fno-inline endif ifneq ($v,release) BFLAGS= -g endif STATICLIBPATH=$(SRCROOT)/lib DYNAMICLIBPATH=$(SRCROOT)/solib INCLS += -I$(SRCROOT) ## default links ifeq ($(LINKS_DYNAMIC), 1) LINKS += -L$(DYNAMICLIBPATH) -L$(STATICLIBPATH) else LINKS += -L$(STATICLIBPATH) endif CPPSRCS = $(wildcard *.cpp) CSRCS = $(wildcard *.c) CPPOBJS = $(patsubst %.cpp,%.o,$(CPPSRCS)) COBJS = $(patsubst %.c,%.o,$(CSRCS)) SRCS = $(CPPSRCS) $(CSRCS) OBJS = $(CPPOBJS) $(COBJS) CPPCOMPI=$(CPP) $(CFLAGS) -Wno-deprecated CCCOMPI=$(CC) $(CFLAGS) BUILDEXE = $(CPP) $(BFLAGS) -o $@ $^ $(LINKS) CLEAN = rm -f *.o CPPCOMPILE = $(CPPCOMPI) $< $(FLAGS) $(INCLS) $(MTOOL_INCL) -o $@ CCCOMPILE = $(CCCOMPI) $< $(FLAGS) $(INCLS) $(MTOOL_INCL) -o $@ ARSTATICLIB = $(AR) $@.tmp $^ $(AR_FLAGS); \ if [ $$? -ne 0 ]; then exit 1; fi; \ test -d $(STATICLIBPATH) || mkdir -p $(STATICLIBPATH); \ mv -f $@.tmp $(STATICLIBPATH)/$@; BUILDSHARELIB = $(CPPSHARE) $@.tmp $^ $(BS_FLAGS); \ if [ $$? -ne 0 ]; then exit 1; fi; \ test -d $(DYNAMICLIBPATH) || mkdir -p $(DYNAMICLIBPATH); \ mv -f $@.tmp $(DYNAMICLIBPATH)/$@; .cpp.o: $(CPPCOMPILE) .c.o: $(CCCOMPILE) ================================================ FILE: co_closure.h ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CO_CLOSURE_H__ #define __CO_CLOSURE_H__ struct stCoClosure_t { public: virtual void exec() = 0; virtual ~stCoClosure_t(){} }; //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 ) //-- 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), //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__ ) ; //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 } #endif ================================================ FILE: co_comm.cpp ================================================ #include "co_comm.h" clsCoMutex::clsCoMutex() { m_ptCondSignal = co_cond_alloc(); m_iWaitItemCnt = 0; } clsCoMutex::~clsCoMutex() { co_cond_free(m_ptCondSignal); } void clsCoMutex::CoLock() { if (m_iWaitItemCnt > 0) { m_iWaitItemCnt++; co_cond_timedwait(m_ptCondSignal, -1); } else { m_iWaitItemCnt++; } } void clsCoMutex::CoUnLock() { m_iWaitItemCnt--; co_cond_signal(m_ptCondSignal); } ================================================ FILE: co_comm.h ================================================ #pragma once #include "co_routine.h" class clsCoMutex { public: clsCoMutex(); ~clsCoMutex(); void CoLock(); void CoUnLock(); private: stCoCond_t* m_ptCondSignal; int m_iWaitItemCnt; }; class clsSmartLock { public: clsSmartLock(clsCoMutex* m) { m_ptMutex = m; m_ptMutex->CoLock(); } ~clsSmartLock() { m_ptMutex->CoUnLock(); } private: clsCoMutex* m_ptMutex; }; ================================================ FILE: co_epoll.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "co_epoll.h" #include #include #include #include #if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ) { return epoll_wait( epfd,events->events,maxevents,timeout ); } int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ev ) { return epoll_ctl( epfd,op,fd,ev ); } int co_epoll_create( int size ) { return epoll_create( size ); } struct co_epoll_res *co_epoll_res_alloc( int n ) { struct co_epoll_res * ptr = (struct co_epoll_res *)malloc( sizeof( struct co_epoll_res ) ); ptr->size = n; ptr->events = (struct epoll_event*)calloc( 1,n * sizeof( struct epoll_event ) ); return ptr; } void co_epoll_res_free( struct co_epoll_res * ptr ) { if( !ptr ) return; if( ptr->events ) free( ptr->events ); free( ptr ); } #else class clsFdMap // million of fd , 1024 * 1024 { private: static const int row_size = 1024; static const int col_size = 1024; void **m_pp[ 1024 ]; public: clsFdMap() { memset( m_pp,0,sizeof(m_pp) ); } ~clsFdMap() { for(int i=0;i= sizeof(m_pp)/sizeof(m_pp[0]) ) { assert( __LINE__ == 0 ); return -__LINE__; } if( !m_pp[ idx ] ) { m_pp[ idx ] = (void**)calloc( 1,sizeof(void*) * col_size ); } m_pp[ idx ][ fd % col_size ] = (void*)ptr; return 0; } inline void *get( int fd ) { int idx = fd / row_size; if( idx < 0 || idx >= sizeof(m_pp)/sizeof(m_pp[0]) ) { return NULL; } void **lp = m_pp[ idx ]; if( !lp ) return NULL; return lp[ fd % col_size ]; } }; __thread clsFdMap *s_fd_map = NULL; static inline clsFdMap *get_fd_map() { if( !s_fd_map ) { s_fd_map = new clsFdMap(); } return s_fd_map; } struct kevent_pair_t { int fire_idx; int events; uint64_t u64; }; int co_epoll_create( int size ) { return kqueue(); } int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ) { struct timespec t = { 0 }; if( timeout > 0 ) { t.tv_sec = timeout; } int ret = kevent( epfd, NULL, 0, //register null events->eventlist, maxevents,//just retrival ( -1 == timeout ) ? NULL : &t ); int j = 0; for(int i=0;ieventlist[i]; struct kevent_pair_t *ptr = (struct kevent_pair_t*)kev.udata; struct epoll_event *ev = events->events + i; if( 0 == ptr->fire_idx ) { ptr->fire_idx = i + 1; memset( ev,0,sizeof(*ev) ); ++j; } else { ev = events->events + ptr->fire_idx - 1; } if( EVFILT_READ == kev.filter ) { ev->events |= EPOLLIN; } else if( EVFILT_WRITE == kev.filter ) { ev->events |= EPOLLOUT; } ev->data.u64 = ptr->u64; } for(int i=0;ieventlist[i].udata) )->fire_idx = 0; } return j; } int co_epoll_del( int epfd,int fd ) { struct timespec t = { 0 }; struct kevent_pair_t *ptr = ( struct kevent_pair_t* )get_fd_map()->get( fd ); if( !ptr ) return 0; if( EPOLLIN & ptr->events ) { struct kevent kev = { 0 }; kev.ident = fd; kev.filter = EVFILT_READ; kev.flags = EV_DELETE; kevent( epfd,&kev,1, NULL,0,&t ); } if( EPOLLOUT & ptr->events ) { struct kevent kev = { 0 }; kev.ident = fd; kev.filter = EVFILT_WRITE; kev.flags = EV_DELETE; kevent( epfd,&kev,1, NULL,0,&t ); } get_fd_map()->clear( fd ); free( ptr ); return 0; } int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ev ) { if( EPOLL_CTL_DEL == op ) { return co_epoll_del( epfd,fd ); } const int flags = ( EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP ); if( ev->events & ~flags ) { return -1; } if( EPOLL_CTL_ADD == op && get_fd_map()->get( fd ) ) { errno = EEXIST; return -1; } else if( EPOLL_CTL_MOD == op && !get_fd_map()->get( fd ) ) { errno = ENOENT; return -1; } struct kevent_pair_t *ptr = (struct kevent_pair_t*)get_fd_map()->get( fd ); if( !ptr ) { ptr = (kevent_pair_t*)calloc(1,sizeof(kevent_pair_t)); get_fd_map()->set( fd,ptr ); } int ret = 0; struct timespec t = { 0 }; // printf("ptr->events 0x%X\n",ptr->events); if( EPOLL_CTL_MOD == op ) { //1.delete if exists if( ptr->events & EPOLLIN ) { struct kevent kev = { 0 }; EV_SET( &kev,fd,EVFILT_READ,EV_DELETE,0,0,NULL ); kevent( epfd, &kev,1, NULL,0, &t ); } //1.delete if exists if( ptr->events & EPOLLOUT ) { struct kevent kev = { 0 }; EV_SET( &kev,fd,EVFILT_WRITE,EV_DELETE,0,0,NULL ); ret = kevent( epfd, &kev,1, NULL,0, &t ); // printf("delete write ret %d\n",ret ); } } do { if( ev->events & EPOLLIN ) { //2.add struct kevent kev = { 0 }; EV_SET( &kev,fd,EVFILT_READ,EV_ADD,0,0,ptr ); ret = kevent( epfd, &kev,1, NULL,0, &t ); if( ret ) break; } if( ev->events & EPOLLOUT ) { //2.add struct kevent kev = { 0 }; EV_SET( &kev,fd,EVFILT_WRITE,EV_ADD,0,0,ptr ); ret = kevent( epfd, &kev,1, NULL,0, &t ); if( ret ) break; } } while( 0 ); if( ret ) { get_fd_map()->clear( fd ); free( ptr ); return ret; } ptr->events = ev->events; ptr->u64 = ev->data.u64; return ret; } struct co_epoll_res *co_epoll_res_alloc( int n ) { struct co_epoll_res * ptr = (struct co_epoll_res *)malloc( sizeof( struct co_epoll_res ) ); ptr->size = n; ptr->events = (struct epoll_event*)calloc( 1,n * sizeof( struct epoll_event ) ); ptr->eventlist = (struct kevent*)calloc( 1,n * sizeof( struct kevent) ); return ptr; } void co_epoll_res_free( struct co_epoll_res * ptr ) { if( !ptr ) return; if( ptr->events ) free( ptr->events ); if( ptr->eventlist ) free( ptr->eventlist ); free( ptr ); } #endif ================================================ FILE: co_epoll.h ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CO_EPOLL_H__ #define __CO_EPOLL_H__ #include #include #include #include #include #include #include #if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) #include struct co_epoll_res { int size; struct epoll_event *events; struct kevent *eventlist; }; int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ); int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ); int co_epoll_create( int size ); struct co_epoll_res *co_epoll_res_alloc( int n ); void co_epoll_res_free( struct co_epoll_res * ); #else #include enum EPOLL_EVENTS { EPOLLIN = 0X001, EPOLLPRI = 0X002, EPOLLOUT = 0X004, EPOLLERR = 0X008, EPOLLHUP = 0X010, EPOLLRDNORM = 0x40, EPOLLWRNORM = 0x004, }; #define EPOLL_CTL_ADD 1 #define EPOLL_CTL_DEL 2 #define EPOLL_CTL_MOD 3 typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; epoll_data_t data; }; struct co_epoll_res { int size; struct epoll_event *events; struct kevent *eventlist; }; int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ); int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ); int co_epoll_create( int size ); struct co_epoll_res *co_epoll_res_alloc( int n ); void co_epoll_res_free( struct co_epoll_res * ); #endif #endif ================================================ FILE: co_hook_sys_call.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "co_routine.h" #include "co_routine_inner.h" #include "co_routine_specific.h" #include "co_comm.h" typedef long long ll64_t; struct rpchook_t { int user_flag; struct sockaddr_in dest; //maybe sockaddr_un; int domain; //AF_LOCAL , AF_INET struct timeval read_timeout; struct timeval write_timeout; }; static inline pid_t GetPid() { char **p = (char**)pthread_self(); return p ? *(pid_t*)(p + 18) : getpid(); } static rpchook_t *g_rpchook_socket_fd[ 102400 ] = { 0 }; typedef int (*socket_pfn_t)(int domain, int type, int protocol); typedef int (*connect_pfn_t)(int socket, const struct sockaddr *address, socklen_t address_len); typedef int (*close_pfn_t)(int fd); typedef ssize_t (*read_pfn_t)(int fildes, void *buf, size_t nbyte); typedef ssize_t (*write_pfn_t)(int fildes, const void *buf, size_t nbyte); typedef ssize_t (*sendto_pfn_t)(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len); typedef ssize_t (*recvfrom_pfn_t)(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len); typedef ssize_t (*send_pfn_t)(int socket, const void *buffer, size_t length, int flags); typedef ssize_t (*recv_pfn_t)(int socket, void *buffer, size_t length, int flags); typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); typedef int (*setsockopt_pfn_t)(int socket, int level, int option_name, const void *option_value, socklen_t option_len); typedef int (*fcntl_pfn_t)(int fildes, int cmd, ...); typedef struct tm *(*localtime_r_pfn_t)( const time_t *timep, struct tm *result ); typedef void *(*pthread_getspecific_pfn_t)(pthread_key_t key); typedef int (*pthread_setspecific_pfn_t)(pthread_key_t key, const void *value); typedef int (*setenv_pfn_t)(const char *name, const char *value, int overwrite); typedef int (*unsetenv_pfn_t)(const char *name); typedef char *(*getenv_pfn_t)(const char *name); typedef hostent* (*gethostbyname_pfn_t)(const char *name); typedef res_state (*__res_state_pfn_t)(); typedef int (*__poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); typedef int (*gethostbyname_r_pfn_t)(const char* __restrict name, struct hostent* __restrict __result_buf, char* __restrict __buf, size_t __buflen, struct hostent** __restrict __result, int* __restrict __h_errnop); static socket_pfn_t g_sys_socket_func = (socket_pfn_t)dlsym(RTLD_NEXT,"socket"); static connect_pfn_t g_sys_connect_func = (connect_pfn_t)dlsym(RTLD_NEXT,"connect"); static close_pfn_t g_sys_close_func = (close_pfn_t)dlsym(RTLD_NEXT,"close"); static read_pfn_t g_sys_read_func = (read_pfn_t)dlsym(RTLD_NEXT,"read"); static write_pfn_t g_sys_write_func = (write_pfn_t)dlsym(RTLD_NEXT,"write"); static sendto_pfn_t g_sys_sendto_func = (sendto_pfn_t)dlsym(RTLD_NEXT,"sendto"); static recvfrom_pfn_t g_sys_recvfrom_func = (recvfrom_pfn_t)dlsym(RTLD_NEXT,"recvfrom"); static send_pfn_t g_sys_send_func = (send_pfn_t)dlsym(RTLD_NEXT,"send"); static recv_pfn_t g_sys_recv_func = (recv_pfn_t)dlsym(RTLD_NEXT,"recv"); static poll_pfn_t g_sys_poll_func = (poll_pfn_t)dlsym(RTLD_NEXT,"poll"); static setsockopt_pfn_t g_sys_setsockopt_func = (setsockopt_pfn_t)dlsym(RTLD_NEXT,"setsockopt"); static fcntl_pfn_t g_sys_fcntl_func = (fcntl_pfn_t)dlsym(RTLD_NEXT,"fcntl"); static setenv_pfn_t g_sys_setenv_func = (setenv_pfn_t)dlsym(RTLD_NEXT,"setenv"); static unsetenv_pfn_t g_sys_unsetenv_func = (unsetenv_pfn_t)dlsym(RTLD_NEXT,"unsetenv"); static getenv_pfn_t g_sys_getenv_func = (getenv_pfn_t)dlsym(RTLD_NEXT,"getenv"); static __res_state_pfn_t g_sys___res_state_func = (__res_state_pfn_t)dlsym(RTLD_NEXT,"__res_state"); static gethostbyname_pfn_t g_sys_gethostbyname_func = (gethostbyname_pfn_t)dlsym(RTLD_NEXT, "gethostbyname"); static gethostbyname_r_pfn_t g_sys_gethostbyname_r_func = (gethostbyname_r_pfn_t)dlsym(RTLD_NEXT, "gethostbyname_r"); static __poll_pfn_t g_sys___poll_func = (__poll_pfn_t)dlsym(RTLD_NEXT, "__poll"); /* static pthread_getspecific_pfn_t g_sys_pthread_getspecific_func = (pthread_getspecific_pfn_t)dlsym(RTLD_NEXT,"pthread_getspecific"); static pthread_setspecific_pfn_t g_sys_pthread_setspecific_func = (pthread_setspecific_pfn_t)dlsym(RTLD_NEXT,"pthread_setspecific"); static pthread_rwlock_rdlock_pfn_t g_sys_pthread_rwlock_rdlock_func = (pthread_rwlock_rdlock_pfn_t)dlsym(RTLD_NEXT,"pthread_rwlock_rdlock"); static pthread_rwlock_wrlock_pfn_t g_sys_pthread_rwlock_wrlock_func = (pthread_rwlock_wrlock_pfn_t)dlsym(RTLD_NEXT,"pthread_rwlock_wrlock"); static pthread_rwlock_unlock_pfn_t g_sys_pthread_rwlock_unlock_func = (pthread_rwlock_unlock_pfn_t)dlsym(RTLD_NEXT,"pthread_rwlock_unlock"); */ static inline unsigned long long get_tick_count() { uint32_t lo, hi; __asm__ __volatile__ ( "rdtscp" : "=a"(lo), "=d"(hi) ); return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); } struct rpchook_connagent_head_t { unsigned char bVersion; struct in_addr iIP; unsigned short hPort; unsigned int iBodyLen; unsigned int iOssAttrID; unsigned char bIsRespNotExist; unsigned char sReserved[6]; }__attribute__((packed)); #define HOOK_SYS_FUNC(name) if( !g_sys_##name##_func ) { g_sys_##name##_func = (name##_pfn_t)dlsym(RTLD_NEXT,#name); } static inline ll64_t diff_ms(struct timeval &begin,struct timeval &end) { ll64_t u = (end.tv_sec - begin.tv_sec) ; u *= 1000 * 10; u += ( end.tv_usec - begin.tv_usec ) / ( 100 ); return u; } static inline rpchook_t * get_by_fd( int fd ) { if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) { return g_rpchook_socket_fd[ fd ]; } return NULL; } 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; } static inline void free_by_fd( int fd ) { if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) { rpchook_t *lp = g_rpchook_socket_fd[ fd ]; if( lp ) { g_rpchook_socket_fd[ fd ] = NULL; free(lp); } } return; } 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; fcntl( fd, F_SETFL, g_sys_fcntl_func(fd, F_GETFL,0 ) ); return fd; } int co_accept( int fd, struct sockaddr *addr, socklen_t *len ) { int cli = accept( fd,addr,len ); if( cli < 0 ) { return cli; } alloc_by_fd( cli ); return cli; } int connect(int fd, const struct sockaddr *address, socklen_t address_len) { HOOK_SYS_FUNC( connect ); if( !co_is_enable_sys_hook() ) { return g_sys_connect_func(fd,address,address_len); } //1.sys call int ret = g_sys_connect_func( fd,address,address_len ); rpchook_t *lp = get_by_fd( fd ); if( !lp ) return ret; if( sizeof(lp->dest) >= address_len ) { memcpy( &(lp->dest),address,(int)address_len ); } if( O_NONBLOCK & lp->user_flag ) { return ret; } if (!(ret < 0 && errno == EINPROGRESS)) { return ret; } //2.wait int pollret = 0; struct pollfd pf = { 0 }; for(int i=0;i<3;i++) //25s * 3 = 75s { memset( &pf,0,sizeof(pf) ); pf.fd = fd; pf.events = ( POLLOUT | POLLERR | POLLHUP ); pollret = poll( &pf,1,25000 ); if( 1 == pollret ) { break; } } if( pf.revents & POLLOUT ) //connect succ { // 3.check getsockopt ret int err = 0; socklen_t errlen = sizeof(err); ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen); if (ret < 0) { return ret; } else if (err != 0) { errno = err; return -1; } errno = 0; return 0; } errno = ETIMEDOUT; return ret; } int close(int fd) { HOOK_SYS_FUNC( close ); if( !co_is_enable_sys_hook() ) { return g_sys_close_func( fd ); } free_by_fd( fd ); int ret = g_sys_close_func(fd); return ret; } ssize_t read( int fd, void *buf, size_t nbyte ) { HOOK_SYS_FUNC( read ); if( !co_is_enable_sys_hook() ) { return g_sys_read_func( fd,buf,nbyte ); } rpchook_t *lp = get_by_fd( fd ); if( !lp || ( O_NONBLOCK & lp->user_flag ) ) { ssize_t ret = g_sys_read_func( fd,buf,nbyte ); return ret; } int timeout = ( lp->read_timeout.tv_sec * 1000 ) + ( lp->read_timeout.tv_usec / 1000 ); struct pollfd pf = { 0 }; pf.fd = fd; pf.events = ( POLLIN | POLLERR | POLLHUP ); int pollret = poll( &pf,1,timeout ); ssize_t readret = g_sys_read_func( fd,(char*)buf ,nbyte ); if( readret < 0 ) { co_log_err("CO_ERR: read fd %d ret %ld errno %d poll ret %d timeout %d", fd,readret,errno,pollret,timeout); } return readret; } ssize_t write( int fd, const void *buf, size_t nbyte ) { HOOK_SYS_FUNC( write ); if( !co_is_enable_sys_hook() ) { return g_sys_write_func( fd,buf,nbyte ); } rpchook_t *lp = get_by_fd( fd ); if( !lp || ( O_NONBLOCK & lp->user_flag ) ) { ssize_t ret = g_sys_write_func( fd,buf,nbyte ); return ret; } size_t wrotelen = 0; int timeout = ( lp->write_timeout.tv_sec * 1000 ) + ( lp->write_timeout.tv_usec / 1000 ); ssize_t writeret = g_sys_write_func( fd,(const char*)buf + wrotelen,nbyte - wrotelen ); if (writeret == 0) { return writeret; } if( writeret > 0 ) { wrotelen += writeret; } while( wrotelen < nbyte ) { struct pollfd pf = { 0 }; pf.fd = fd; pf.events = ( POLLOUT | POLLERR | POLLHUP ); poll( &pf,1,timeout ); writeret = g_sys_write_func( fd,(const char*)buf + wrotelen,nbyte - wrotelen ); if( writeret <= 0 ) { break; } wrotelen += writeret ; } if (writeret <= 0 && wrotelen == 0) { return writeret; } return wrotelen; } ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len) { /* 1.no enable sys call ? sys 2.( !lp || lp is non block ) ? sys 3.try 4.wait 5.try */ HOOK_SYS_FUNC( sendto ); if( !co_is_enable_sys_hook() ) { return g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); } rpchook_t *lp = get_by_fd( socket ); if( !lp || ( O_NONBLOCK & lp->user_flag ) ) { return g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); } ssize_t ret = g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); if( ret < 0 && EAGAIN == errno ) { int timeout = ( lp->write_timeout.tv_sec * 1000 ) + ( lp->write_timeout.tv_usec / 1000 ); struct pollfd pf = { 0 }; pf.fd = socket; pf.events = ( POLLOUT | POLLERR | POLLHUP ); poll( &pf,1,timeout ); ret = g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); } return ret; } ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len) { HOOK_SYS_FUNC( recvfrom ); if( !co_is_enable_sys_hook() ) { return g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); } rpchook_t *lp = get_by_fd( socket ); if( !lp || ( O_NONBLOCK & lp->user_flag ) ) { return g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); } int timeout = ( lp->read_timeout.tv_sec * 1000 ) + ( lp->read_timeout.tv_usec / 1000 ); struct pollfd pf = { 0 }; pf.fd = socket; pf.events = ( POLLIN | POLLERR | POLLHUP ); poll( &pf,1,timeout ); ssize_t ret = g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); return ret; } ssize_t send(int socket, const void *buffer, size_t length, int flags) { HOOK_SYS_FUNC( send ); if( !co_is_enable_sys_hook() ) { return g_sys_send_func( socket,buffer,length,flags ); } rpchook_t *lp = get_by_fd( socket ); if( !lp || ( O_NONBLOCK & lp->user_flag ) ) { return g_sys_send_func( socket,buffer,length,flags ); } size_t wrotelen = 0; int timeout = ( lp->write_timeout.tv_sec * 1000 ) + ( lp->write_timeout.tv_usec / 1000 ); ssize_t writeret = g_sys_send_func( socket,buffer,length,flags ); if (writeret == 0) { return writeret; } if( writeret > 0 ) { wrotelen += writeret; } while( wrotelen < length ) { struct pollfd pf = { 0 }; pf.fd = socket; pf.events = ( POLLOUT | POLLERR | POLLHUP ); poll( &pf,1,timeout ); writeret = g_sys_send_func( socket,(const char*)buffer + wrotelen,length - wrotelen,flags ); if( writeret <= 0 ) { break; } wrotelen += writeret ; } if (writeret <= 0 && wrotelen == 0) { return writeret; } return wrotelen; } ssize_t recv( int socket, void *buffer, size_t length, int flags ) { HOOK_SYS_FUNC( recv ); if( !co_is_enable_sys_hook() ) { return g_sys_recv_func( socket,buffer,length,flags ); } rpchook_t *lp = get_by_fd( socket ); if( !lp || ( O_NONBLOCK & lp->user_flag ) ) { return g_sys_recv_func( socket,buffer,length,flags ); } int timeout = ( lp->read_timeout.tv_sec * 1000 ) + ( lp->read_timeout.tv_usec / 1000 ); struct pollfd pf = { 0 }; pf.fd = socket; pf.events = ( POLLIN | POLLERR | POLLHUP ); int pollret = poll( &pf,1,timeout ); ssize_t readret = g_sys_recv_func( socket,buffer,length,flags ); if( readret < 0 ) { co_log_err("CO_ERR: read fd %d ret %ld errno %d poll ret %d timeout %d", socket,readret,errno,pollret,timeout); } return readret; } extern int co_poll_inner( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout, poll_pfn_t pollfunc); int poll(struct pollfd fds[], nfds_t nfds, int timeout) { HOOK_SYS_FUNC( poll ); if (!co_is_enable_sys_hook() || timeout == 0) { return g_sys_poll_func(fds, nfds, timeout); } pollfd *fds_merge = NULL; nfds_t nfds_merge = 0; std::map m; // fd --> idx std::map::iterator it; if (nfds > 1) { fds_merge = (pollfd *)malloc(sizeof(pollfd) * nfds); for (size_t i = 0; i < nfds; i++) { if ((it = m.find(fds[i].fd)) == m.end()) { fds_merge[nfds_merge] = fds[i]; m[fds[i].fd] = nfds_merge; nfds_merge++; } else { int j = it->second; fds_merge[j].events |= fds[i].events; // merge in j slot } } } int ret = 0; if (nfds_merge == nfds || nfds == 1) { ret = co_poll_inner(co_get_epoll_ct(), fds, nfds, timeout, g_sys_poll_func); } else { ret = co_poll_inner(co_get_epoll_ct(), fds_merge, nfds_merge, timeout, g_sys_poll_func); if (ret > 0) { for (size_t i = 0; i < nfds; i++) { it = m.find(fds[i].fd); if (it != m.end()) { int j = it->second; fds[i].revents = fds_merge[j].revents & fds[i].events; } } } } free(fds_merge); return ret; } int setsockopt(int fd, int level, int option_name, const void *option_value, socklen_t option_len) { HOOK_SYS_FUNC( setsockopt ); if( !co_is_enable_sys_hook() ) { return g_sys_setsockopt_func( fd,level,option_name,option_value,option_len ); } rpchook_t *lp = get_by_fd( fd ); if( lp && SOL_SOCKET == level ) { struct timeval *val = (struct timeval*)option_value; if( SO_RCVTIMEO == option_name ) { memcpy( &lp->read_timeout,val,sizeof(*val) ); } else if( SO_SNDTIMEO == option_name ) { memcpy( &lp->write_timeout,val,sizeof(*val) ); } } return g_sys_setsockopt_func( fd,level,option_name,option_value,option_len ); } int fcntl(int fildes, int cmd, ...) { HOOK_SYS_FUNC( fcntl ); if( fildes < 0 ) { return __LINE__; } va_list arg_list; va_start( arg_list,cmd ); int ret = -1; rpchook_t *lp = get_by_fd( fildes ); switch( cmd ) { case F_DUPFD: { int param = va_arg(arg_list,int); ret = g_sys_fcntl_func( fildes,cmd,param ); break; } case F_GETFD: { ret = g_sys_fcntl_func( fildes,cmd ); break; } case F_SETFD: { int param = va_arg(arg_list,int); ret = g_sys_fcntl_func( fildes,cmd,param ); break; } case F_GETFL: { ret = g_sys_fcntl_func( fildes,cmd ); if (lp && !(lp->user_flag & O_NONBLOCK)) { ret = ret & (~O_NONBLOCK); } break; } case F_SETFL: { int param = va_arg(arg_list,int); int flag = param; if( co_is_enable_sys_hook() && lp ) { flag |= O_NONBLOCK; } ret = g_sys_fcntl_func( fildes,cmd,flag ); if( 0 == ret && lp ) { lp->user_flag = param; } break; } case F_GETOWN: { ret = g_sys_fcntl_func( fildes,cmd ); break; } case F_SETOWN: { int param = va_arg(arg_list,int); ret = g_sys_fcntl_func( fildes,cmd,param ); break; } case F_GETLK: { struct flock *param = va_arg(arg_list,struct flock *); ret = g_sys_fcntl_func( fildes,cmd,param ); break; } case F_SETLK: { struct flock *param = va_arg(arg_list,struct flock *); ret = g_sys_fcntl_func( fildes,cmd,param ); break; } case F_SETLKW: { struct flock *param = va_arg(arg_list,struct flock *); ret = g_sys_fcntl_func( fildes,cmd,param ); break; } } va_end( arg_list ); return ret; } struct stCoSysEnv_t { char *name; char *value; }; struct stCoSysEnvArr_t { stCoSysEnv_t *data; size_t cnt; }; static stCoSysEnvArr_t *dup_co_sysenv_arr( stCoSysEnvArr_t * arr ) { stCoSysEnvArr_t *lp = (stCoSysEnvArr_t*)calloc( sizeof(stCoSysEnvArr_t),1 ); if( arr->cnt ) { lp->data = (stCoSysEnv_t*)calloc( sizeof(stCoSysEnv_t) * arr->cnt,1 ); lp->cnt = arr->cnt; memcpy( lp->data,arr->data,sizeof( stCoSysEnv_t ) * arr->cnt ); } return lp; } static int co_sysenv_comp(const void *a, const void *b) { return strcmp(((stCoSysEnv_t*)a)->name, ((stCoSysEnv_t*)b)->name); } static stCoSysEnvArr_t g_co_sysenv = { 0 }; void co_set_env_list( const char *name[],size_t cnt) { if( g_co_sysenv.data ) { return ; } g_co_sysenv.data = (stCoSysEnv_t*)calloc( 1,sizeof(stCoSysEnv_t) * cnt ); for(size_t i=0;i 1 ) { qsort( g_co_sysenv.data,g_co_sysenv.cnt,sizeof(stCoSysEnv_t),co_sysenv_comp ); stCoSysEnv_t *lp = g_co_sysenv.data; stCoSysEnv_t *lq = g_co_sysenv.data + 1; for(size_t i=1;iname,lq->name ) ) { ++lp; if( lq != lp ) { *lp = *lq; } } ++lq; } g_co_sysenv.cnt = lp - g_co_sysenv.data + 1; } } int setenv(const char *n, const char *value, int overwrite) { HOOK_SYS_FUNC( setenv ) if( co_is_enable_sys_hook() && g_co_sysenv.data ) { stCoRoutine_t *self = co_self(); if( self ) { if( !self->pvEnv ) { self->pvEnv = dup_co_sysenv_arr( &g_co_sysenv ); } stCoSysEnvArr_t *arr = (stCoSysEnvArr_t*)(self->pvEnv); stCoSysEnv_t name = { (char*)n,0 }; stCoSysEnv_t *e = (stCoSysEnv_t*)bsearch( &name,arr->data,arr->cnt,sizeof(name),co_sysenv_comp ); if( e ) { if( overwrite || !e->value ) { if( e->value ) free( e->value ); e->value = ( value ? strdup( value ) : 0 ); } return 0; } } } return g_sys_setenv_func( n,value,overwrite ); } int unsetenv(const char *n) { HOOK_SYS_FUNC( unsetenv ) if( co_is_enable_sys_hook() && g_co_sysenv.data ) { stCoRoutine_t *self = co_self(); if( self ) { if( !self->pvEnv ) { self->pvEnv = dup_co_sysenv_arr( &g_co_sysenv ); } stCoSysEnvArr_t *arr = (stCoSysEnvArr_t*)(self->pvEnv); stCoSysEnv_t name = { (char*)n,0 }; stCoSysEnv_t *e = (stCoSysEnv_t*)bsearch( &name,arr->data,arr->cnt,sizeof(name),co_sysenv_comp ); if( e ) { if( e->value ) { free( e->value ); e->value = 0; } return 0; } } } return g_sys_unsetenv_func( n ); } char *getenv( const char *n ) { HOOK_SYS_FUNC( getenv ) if( co_is_enable_sys_hook() && g_co_sysenv.data ) { stCoRoutine_t *self = co_self(); stCoSysEnv_t name = { (char*)n,0 }; if( !self->pvEnv ) { self->pvEnv = dup_co_sysenv_arr( &g_co_sysenv ); } stCoSysEnvArr_t *arr = (stCoSysEnvArr_t*)(self->pvEnv); stCoSysEnv_t *e = (stCoSysEnv_t*)bsearch( &name,arr->data,arr->cnt,sizeof(name),co_sysenv_comp ); if( e ) { return e->value; } } return g_sys_getenv_func( n ); } struct hostent* co_gethostbyname(const char *name); struct hostent *gethostbyname(const char *name) { HOOK_SYS_FUNC( gethostbyname ); #if defined( __APPLE__ ) || defined( __FreeBSD__ ) return g_sys_gethostbyname_func( name ); #else if (!co_is_enable_sys_hook()) { return g_sys_gethostbyname_func(name); } return co_gethostbyname(name); #endif } int co_gethostbyname_r(const char* __restrict name, struct hostent* __restrict __result_buf, char* __restrict __buf, size_t __buflen, struct hostent** __restrict __result, int* __restrict __h_errnop) { static __thread clsCoMutex* tls_leaky_dns_lock = NULL; if(tls_leaky_dns_lock == NULL) { tls_leaky_dns_lock = new clsCoMutex(); } clsSmartLock auto_lock(tls_leaky_dns_lock); return g_sys_gethostbyname_r_func(name, __result_buf, __buf, __buflen, __result, __h_errnop); } int gethostbyname_r(const char* __restrict name, struct hostent* __restrict __result_buf, char* __restrict __buf, size_t __buflen, struct hostent** __restrict __result, int* __restrict __h_errnop) { HOOK_SYS_FUNC(gethostbyname_r); #if defined( __APPLE__ ) || defined( __FreeBSD__ ) return g_sys_gethostbyname_r_func( name ); #else if (!co_is_enable_sys_hook()) { return g_sys_gethostbyname_r_func(name, __result_buf, __buf, __buflen, __result, __h_errnop); } return co_gethostbyname_r(name, __result_buf, __buf, __buflen, __result, __h_errnop); #endif } struct res_state_wrap { struct __res_state state; }; CO_ROUTINE_SPECIFIC(res_state_wrap, __co_state_wrap); extern "C" { res_state __res_state() { HOOK_SYS_FUNC(__res_state); if (!co_is_enable_sys_hook()) { return g_sys___res_state_func(); } return &(__co_state_wrap->state); } int __poll(struct pollfd fds[], nfds_t nfds, int timeout) { return poll(fds, nfds, timeout); } } struct hostbuf_wrap { struct hostent host; char* buffer; size_t iBufferSize; int host_errno; }; CO_ROUTINE_SPECIFIC(hostbuf_wrap, __co_hostbuf_wrap); #if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) struct hostent *co_gethostbyname(const char *name) { if (!name) { return NULL; } if (__co_hostbuf_wrap->buffer && __co_hostbuf_wrap->iBufferSize > 1024) { free(__co_hostbuf_wrap->buffer); __co_hostbuf_wrap->buffer = NULL; } if (!__co_hostbuf_wrap->buffer) { __co_hostbuf_wrap->buffer = (char*)malloc(1024); __co_hostbuf_wrap->iBufferSize = 1024; } struct hostent *host = &__co_hostbuf_wrap->host; struct hostent *result = NULL; int *h_errnop = &(__co_hostbuf_wrap->host_errno); int ret = -1; while (ret = gethostbyname_r(name, host, __co_hostbuf_wrap->buffer, __co_hostbuf_wrap->iBufferSize, &result, h_errnop) == ERANGE && *h_errnop == NETDB_INTERNAL ) { free(__co_hostbuf_wrap->buffer); __co_hostbuf_wrap->iBufferSize = __co_hostbuf_wrap->iBufferSize * 2; __co_hostbuf_wrap->buffer = (char*)malloc(__co_hostbuf_wrap->iBufferSize); } if (ret == 0 && (host == result)) { return host; } return NULL; } #endif void co_enable_hook_sys() //这函数必须在这里,否则本文件会被忽略!!! { stCoRoutine_t *co = GetCurrThreadCo(); if( co ) { co->cEnableSysHook = 1; } } ================================================ FILE: co_routine.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "co_routine.h" #include "co_routine_inner.h" #include "co_epoll.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap"); }; using namespace std; stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env ); struct stCoEpoll_t; struct stCoRoutineEnv_t { stCoRoutine_t *pCallStack[ 128 ]; int iCallStackSize; stCoEpoll_t *pEpoll; //for copy stack log lastco and nextco stCoRoutine_t* pending_co; stCoRoutine_t* occupy_co; }; //int socket(int domain, int type, int protocol); void co_log_err( const char *fmt,... ) { } #if defined( __LIBCO_RDTSCP__) static unsigned long long counter(void) { register uint32_t lo, hi; register unsigned long long o; __asm__ __volatile__ ( "rdtscp" : "=a"(lo), "=d"(hi)::"%rcx" ); o = hi; o <<= 32; return (o | lo); } static unsigned long long getCpuKhz() { FILE *fp = fopen("/proc/cpuinfo","r"); if(!fp) return 1; char buf[4096] = {0}; fread(buf,1,sizeof(buf),fp); fclose(fp); char *lp = strstr(buf,"cpu MHz"); if(!lp) return 1; lp += strlen("cpu MHz"); while(*lp == ' ' || *lp == '\t' || *lp == ':') { ++lp; } double mhz = atof(lp); unsigned long long u = (unsigned long long)(mhz * 1000); return u; } #endif static unsigned long long GetTickMS() { #if defined( __LIBCO_RDTSCP__) static uint32_t khz = getCpuKhz(); return counter() / khz; #else struct timeval now = { 0 }; gettimeofday( &now,NULL ); unsigned long long u = now.tv_sec; u *= 1000; u += now.tv_usec / 1000; return u; #endif } /* no longer use static pid_t GetPid() { static __thread pid_t pid = 0; static __thread pid_t tid = 0; if( !pid || !tid || pid != getpid() ) { pid = getpid(); #if defined( __APPLE__ ) tid = syscall( SYS_gettid ); if( -1 == (long)tid ) { tid = pid; } #elif defined( __FreeBSD__ ) syscall(SYS_thr_self, &tid); if( tid < 0 ) { tid = pid; } #else tid = syscall( __NR_gettid ); #endif } return tid; } static pid_t GetPid() { char **p = (char**)pthread_self(); return p ? *(pid_t*)(p + 18) : getpid(); } */ template void RemoveFromLink(T *ap) { TLink *lst = ap->pLink; if(!lst) return ; assert( lst->head && lst->tail ); if( ap == lst->head ) { lst->head = ap->pNext; if(lst->head) { lst->head->pPrev = NULL; } } else { if(ap->pPrev) { ap->pPrev->pNext = ap->pNext; } } if( ap == lst->tail ) { lst->tail = ap->pPrev; if(lst->tail) { lst->tail->pNext = NULL; } } else { ap->pNext->pPrev = ap->pPrev; } ap->pPrev = ap->pNext = NULL; ap->pLink = NULL; } template void inline AddTail(TLink*apLink,TNode *ap) { if( ap->pLink ) { return ; } if(apLink->tail) { apLink->tail->pNext = (TNode*)ap; ap->pNext = NULL; ap->pPrev = apLink->tail; apLink->tail = ap; } else { apLink->head = apLink->tail = ap; ap->pNext = ap->pPrev = NULL; } ap->pLink = apLink; } template void inline PopHead( TLink*apLink ) { if( !apLink->head ) { return ; } TNode *lp = apLink->head; if( apLink->head == apLink->tail ) { apLink->head = apLink->tail = NULL; } else { apLink->head = apLink->head->pNext; } lp->pPrev = lp->pNext = NULL; lp->pLink = NULL; if( apLink->head ) { apLink->head->pPrev = NULL; } } template void inline Join( TLink*apLink,TLink *apOther ) { //printf("apOther %p\n",apOther); if( !apOther->head ) { return ; } TNode *lp = apOther->head; while( lp ) { lp->pLink = apLink; lp = lp->pNext; } lp = apOther->head; if(apLink->tail) { apLink->tail->pNext = (TNode*)lp; lp->pPrev = apLink->tail; apLink->tail = apOther->tail; } else { apLink->head = apOther->head; apLink->tail = apOther->tail; } apOther->head = apOther->tail = NULL; } /////////////////for copy stack ////////////////////////// stStackMem_t* co_alloc_stackmem(unsigned int stack_size) { stStackMem_t* stack_mem = (stStackMem_t*)malloc(sizeof(stStackMem_t)); stack_mem->occupy_co= NULL; stack_mem->stack_size = stack_size; stack_mem->stack_buffer = (char*)malloc(stack_size); stack_mem->stack_bp = stack_mem->stack_buffer + stack_size; return stack_mem; } stShareStack_t* co_alloc_sharestack(int count, int stack_size) { stShareStack_t* share_stack = (stShareStack_t*)malloc(sizeof(stShareStack_t)); share_stack->alloc_idx = 0; share_stack->stack_size = stack_size; //alloc stack array share_stack->count = count; stStackMem_t** stack_array = (stStackMem_t**)calloc(count, sizeof(stStackMem_t*)); for (int i = 0; i < count; i++) { stack_array[i] = co_alloc_stackmem(stack_size); } share_stack->stack_array = stack_array; return share_stack; } static stStackMem_t* co_get_stackmem(stShareStack_t* share_stack) { if (!share_stack) { return NULL; } int idx = share_stack->alloc_idx % share_stack->count; share_stack->alloc_idx++; return share_stack->stack_array[idx]; } // ---------------------------------------------------------------------------- struct stTimeoutItemLink_t; struct stTimeoutItem_t; struct stCoEpoll_t { int iEpollFd; static const int _EPOLL_SIZE = 1024 * 10; struct stTimeout_t *pTimeout; struct stTimeoutItemLink_t *pstTimeoutList; struct stTimeoutItemLink_t *pstActiveList; co_epoll_res *result; }; typedef void (*OnPreparePfn_t)( stTimeoutItem_t *,struct epoll_event &ev, stTimeoutItemLink_t *active ); typedef void (*OnProcessPfn_t)( stTimeoutItem_t *); struct stTimeoutItem_t { enum { eMaxTimeout = 40 * 1000 //40s }; stTimeoutItem_t *pPrev; stTimeoutItem_t *pNext; stTimeoutItemLink_t *pLink; unsigned long long ullExpireTime; OnPreparePfn_t pfnPrepare; OnProcessPfn_t pfnProcess; void *pArg; // routine bool bTimeout; }; struct stTimeoutItemLink_t { stTimeoutItem_t *head; stTimeoutItem_t *tail; }; struct stTimeout_t { stTimeoutItemLink_t *pItems; int iItemSize; unsigned long long ullStart; long long llStartIdx; }; stTimeout_t *AllocTimeout( int iSize ) { stTimeout_t *lp = (stTimeout_t*)calloc( 1,sizeof(stTimeout_t) ); lp->iItemSize = iSize; lp->pItems = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) * lp->iItemSize ); lp->ullStart = GetTickMS(); lp->llStartIdx = 0; return lp; } void FreeTimeout( stTimeout_t *apTimeout ) { free( apTimeout->pItems ); free ( apTimeout ); } int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,unsigned long long allNow ) { if( apTimeout->ullStart == 0 ) { apTimeout->ullStart = allNow; apTimeout->llStartIdx = 0; } if( allNow < apTimeout->ullStart ) { co_log_err("CO_ERR: AddTimeout line %d allNow %llu apTimeout->ullStart %llu", __LINE__,allNow,apTimeout->ullStart); return __LINE__; } if( apItem->ullExpireTime < allNow ) { co_log_err("CO_ERR: AddTimeout line %d apItem->ullExpireTime %llu allNow %llu apTimeout->ullStart %llu", __LINE__,apItem->ullExpireTime,allNow,apTimeout->ullStart); return __LINE__; } unsigned long long diff = apItem->ullExpireTime - apTimeout->ullStart; if( diff >= (unsigned long long)apTimeout->iItemSize ) { diff = apTimeout->iItemSize - 1; co_log_err("CO_ERR: AddTimeout line %d diff %d", __LINE__,diff); //return __LINE__; } AddTail( apTimeout->pItems + ( apTimeout->llStartIdx + diff ) % apTimeout->iItemSize , apItem ); return 0; } inline void TakeAllTimeout( stTimeout_t *apTimeout,unsigned long long allNow,stTimeoutItemLink_t *apResult ) { if( apTimeout->ullStart == 0 ) { apTimeout->ullStart = allNow; apTimeout->llStartIdx = 0; } if( allNow < apTimeout->ullStart ) { return ; } int cnt = allNow - apTimeout->ullStart + 1; if( cnt > apTimeout->iItemSize ) { cnt = apTimeout->iItemSize; } if( cnt < 0 ) { return; } for( int i = 0;illStartIdx + i) % apTimeout->iItemSize; Join( apResult,apTimeout->pItems + idx ); } apTimeout->ullStart = allNow; apTimeout->llStartIdx += cnt - 1; } static int CoRoutineFunc( stCoRoutine_t *co,void * ) { if( co->pfn ) { co->pfn( co->arg ); } co->cEnd = 1; stCoRoutineEnv_t *env = co->env; co_yield_env( env ); return 0; } struct stCoRoutine_t *co_create_env( stCoRoutineEnv_t * env, const stCoRoutineAttr_t* attr, pfn_co_routine_t pfn,void *arg ) { stCoRoutineAttr_t at; if( attr ) { memcpy( &at,attr,sizeof(at) ); } if( at.stack_size <= 0 ) { at.stack_size = 128 * 1024; } else if( at.stack_size > 1024 * 1024 * 8 ) { at.stack_size = 1024 * 1024 * 8; } if( at.stack_size & 0xFFF ) { at.stack_size &= ~0xFFF; at.stack_size += 0x1000; } stCoRoutine_t *lp = (stCoRoutine_t*)malloc( sizeof(stCoRoutine_t) ); memset( lp,0,(long)(sizeof(stCoRoutine_t))); lp->env = env; lp->pfn = pfn; lp->arg = arg; stStackMem_t* stack_mem = NULL; if( at.share_stack ) { stack_mem = co_get_stackmem( at.share_stack); at.stack_size = at.share_stack->stack_size; } else { stack_mem = co_alloc_stackmem(at.stack_size); } lp->stack_mem = stack_mem; lp->ctx.ss_sp = stack_mem->stack_buffer; lp->ctx.ss_size = at.stack_size; lp->cStart = 0; lp->cEnd = 0; lp->cIsMain = 0; lp->cEnableSysHook = 0; lp->cIsShareStack = at.share_stack != NULL; lp->save_size = 0; lp->save_buffer = NULL; return lp; } int co_create( stCoRoutine_t **ppco,const stCoRoutineAttr_t *attr,pfn_co_routine_t pfn,void *arg ) { if( !co_get_curr_thread_env() ) { co_init_curr_thread_env(); } stCoRoutine_t *co = co_create_env( co_get_curr_thread_env(), attr, pfn,arg ); *ppco = co; return 0; } void co_free( stCoRoutine_t *co ) { if (!co->cIsShareStack) { free(co->stack_mem->stack_buffer); free(co->stack_mem); } //walkerdu fix at 2018-01-20 //存在内存泄漏 else { if(co->save_buffer) free(co->save_buffer); if(co->stack_mem->occupy_co == co) co->stack_mem->occupy_co = NULL; } free( co ); } void co_release( stCoRoutine_t *co ) { co_free( co ); } void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co); void co_resume( stCoRoutine_t *co ) { stCoRoutineEnv_t *env = co->env; stCoRoutine_t *lpCurrRoutine = env->pCallStack[ env->iCallStackSize - 1 ]; if( !co->cStart ) { coctx_make( &co->ctx,(coctx_pfn_t)CoRoutineFunc,co,0 ); co->cStart = 1; } env->pCallStack[ env->iCallStackSize++ ] = co; co_swap( lpCurrRoutine, co ); } // walkerdu 2018-01-14 // 用于reset超时无法重复使用的协程 void co_reset(stCoRoutine_t * co) { if(!co->cStart || co->cIsMain) return; co->cStart = 0; co->cEnd = 0; // 如果当前协程有共享栈被切出的buff,要进行释放 if(co->save_buffer) { free(co->save_buffer); co->save_buffer = NULL; co->save_size = 0; } // 如果共享栈被当前协程占用,要释放占用标志,否则被切换,会执行save_stack_buffer() if(co->stack_mem->occupy_co == co) co->stack_mem->occupy_co = NULL; } void co_yield_env( stCoRoutineEnv_t *env ) { stCoRoutine_t *last = env->pCallStack[ env->iCallStackSize - 2 ]; stCoRoutine_t *curr = env->pCallStack[ env->iCallStackSize - 1 ]; env->iCallStackSize--; co_swap( curr, last); } void co_yield_ct() { co_yield_env( co_get_curr_thread_env() ); } void co_yield( stCoRoutine_t *co ) { co_yield_env( co->env ); } 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); } void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co) { stCoRoutineEnv_t* env = co_get_curr_thread_env(); //get curr stack sp char c; curr->stack_sp= &c; if (!pending_co->cIsShareStack) { env->pending_co = NULL; env->occupy_co = NULL; } else { env->pending_co = pending_co; //get last occupy co on the same stack mem stCoRoutine_t* occupy_co = pending_co->stack_mem->occupy_co; //set pending co to occupy thest stack mem; pending_co->stack_mem->occupy_co = pending_co; env->occupy_co = occupy_co; if (occupy_co && occupy_co != pending_co) { save_stack_buffer(occupy_co); } } //swap context coctx_swap(&(curr->ctx),&(pending_co->ctx) ); //stack buffer may be overwrite, so get again; stCoRoutineEnv_t* curr_env = co_get_curr_thread_env(); stCoRoutine_t* update_occupy_co = curr_env->occupy_co; stCoRoutine_t* update_pending_co = curr_env->pending_co; if (update_occupy_co && update_pending_co && update_occupy_co != update_pending_co) { //resume stack buffer if (update_pending_co->save_buffer && update_pending_co->save_size > 0) { memcpy(update_pending_co->stack_sp, update_pending_co->save_buffer, update_pending_co->save_size); } } } //int poll(struct pollfd fds[], nfds_t nfds, int timeout); // { fd,events,revents } struct stPollItem_t ; struct stPoll_t : public stTimeoutItem_t { struct pollfd *fds; nfds_t nfds; // typedef unsigned long int nfds_t; stPollItem_t *pPollItems; int iAllEventDetach; int iEpollFd; int iRaiseCnt; }; struct stPollItem_t : public stTimeoutItem_t { struct pollfd *pSelf; stPoll_t *pPoll; struct epoll_event stEvent; }; /* * EPOLLPRI POLLPRI // There is urgent data to read. * EPOLLMSG POLLMSG * * POLLREMOVE * POLLRDHUP * POLLNVAL * * */ static uint32_t PollEvent2Epoll( short events ) { uint32_t e = 0; if( events & POLLIN ) e |= EPOLLIN; if( events & POLLOUT ) e |= EPOLLOUT; if( events & POLLHUP ) e |= EPOLLHUP; if( events & POLLERR ) e |= EPOLLERR; if( events & POLLRDNORM ) e |= EPOLLRDNORM; if( events & POLLWRNORM ) e |= EPOLLWRNORM; return e; } static short EpollEvent2Poll( uint32_t events ) { short e = 0; if( events & EPOLLIN ) e |= POLLIN; if( events & EPOLLOUT ) e |= POLLOUT; if( events & EPOLLHUP ) e |= POLLHUP; if( events & EPOLLERR ) e |= POLLERR; if( events & EPOLLRDNORM ) e |= POLLRDNORM; if( events & EPOLLWRNORM ) e |= POLLWRNORM; return e; } static __thread stCoRoutineEnv_t* gCoEnvPerThread = NULL; void co_init_curr_thread_env() { gCoEnvPerThread = (stCoRoutineEnv_t*)calloc( 1, sizeof(stCoRoutineEnv_t) ); stCoRoutineEnv_t *env = gCoEnvPerThread; env->iCallStackSize = 0; struct stCoRoutine_t *self = co_create_env( env, NULL, NULL,NULL ); self->cIsMain = 1; env->pending_co = NULL; env->occupy_co = NULL; coctx_init( &self->ctx ); env->pCallStack[ env->iCallStackSize++ ] = self; stCoEpoll_t *ev = AllocEpoll(); SetEpoll( env,ev ); } stCoRoutineEnv_t *co_get_curr_thread_env() { return gCoEnvPerThread; } void OnPollProcessEvent( stTimeoutItem_t * ap ) { stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; co_resume( co ); } void OnPollPreparePfn( stTimeoutItem_t * ap,struct epoll_event &e,stTimeoutItemLink_t *active ) { stPollItem_t *lp = (stPollItem_t *)ap; lp->pSelf->revents = EpollEvent2Poll( e.events ); stPoll_t *pPoll = lp->pPoll; pPoll->iRaiseCnt++; if( !pPoll->iAllEventDetach ) { pPoll->iAllEventDetach = 1; RemoveFromLink( pPoll ); AddTail( active,pPoll ); } } void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg ) { if( !ctx->result ) { ctx->result = co_epoll_res_alloc( stCoEpoll_t::_EPOLL_SIZE ); } co_epoll_res *result = ctx->result; for(;;) { int ret = co_epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 ); stTimeoutItemLink_t *active = (ctx->pstActiveList); stTimeoutItemLink_t *timeout = (ctx->pstTimeoutList); memset( timeout,0,sizeof(stTimeoutItemLink_t) ); for(int i=0;ievents[i].data.ptr; if( item->pfnPrepare ) { item->pfnPrepare( item,result->events[i],active ); } else { AddTail( active,item ); } } unsigned long long now = GetTickMS(); TakeAllTimeout( ctx->pTimeout,now,timeout ); stTimeoutItem_t *lp = timeout->head; while( lp ) { //printf("raise timeout %p\n",lp); lp->bTimeout = true; lp = lp->pNext; } Join( active,timeout ); lp = active->head; while( lp ) { PopHead( active ); if (lp->bTimeout && now < lp->ullExpireTime) { int ret = AddTimeout(ctx->pTimeout, lp, now); if (!ret) { lp->bTimeout = false; lp = active->head; continue; } } if( lp->pfnProcess ) { lp->pfnProcess( lp ); } lp = active->head; } if( pfn ) { if( -1 == pfn( arg ) ) { break; } } } } void OnCoroutineEvent( stTimeoutItem_t * ap ) { stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; co_resume( co ); } stCoEpoll_t *AllocEpoll() { stCoEpoll_t *ctx = (stCoEpoll_t*)calloc( 1,sizeof(stCoEpoll_t) ); ctx->iEpollFd = co_epoll_create( stCoEpoll_t::_EPOLL_SIZE ); ctx->pTimeout = AllocTimeout( 60 * 1000 ); ctx->pstActiveList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) ); ctx->pstTimeoutList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) ); return ctx; } void FreeEpoll( stCoEpoll_t *ctx ) { if( ctx ) { free( ctx->pstActiveList ); free( ctx->pstTimeoutList ); FreeTimeout( ctx->pTimeout ); co_epoll_res_free( ctx->result ); } free( ctx ); } stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env ) { return env->pCallStack[ env->iCallStackSize - 1 ]; } stCoRoutine_t *GetCurrThreadCo( ) { stCoRoutineEnv_t *env = co_get_curr_thread_env(); if( !env ) return 0; return GetCurrCo(env); } typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); int co_poll_inner( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout, poll_pfn_t pollfunc) { if (timeout == 0) { return pollfunc(fds, nfds, timeout); } if (timeout < 0) { timeout = INT_MAX; } int epfd = ctx->iEpollFd; stCoRoutine_t* self = co_self(); //1.struct change stPoll_t& arg = *((stPoll_t*)malloc(sizeof(stPoll_t))); memset( &arg,0,sizeof(arg) ); arg.iEpollFd = epfd; arg.fds = (pollfd*)calloc(nfds, sizeof(pollfd)); arg.nfds = nfds; stPollItem_t arr[2]; if( nfds < sizeof(arr) / sizeof(arr[0]) && !self->cIsShareStack) { arg.pPollItems = arr; } else { arg.pPollItems = (stPollItem_t*)malloc( nfds * sizeof( stPollItem_t ) ); } memset( arg.pPollItems,0,nfds * sizeof(stPollItem_t) ); arg.pfnProcess = OnPollProcessEvent; arg.pArg = GetCurrCo( co_get_curr_thread_env() ); //2. add epoll for(nfds_t i=0;i -1 ) { ev.data.ptr = arg.pPollItems + i; ev.events = PollEvent2Epoll( fds[i].events ); int ret = co_epoll_ctl( epfd,EPOLL_CTL_ADD, fds[i].fd, &ev ); if (ret < 0 && errno == EPERM && nfds == 1 && pollfunc != NULL) { if( arg.pPollItems != arr ) { free( arg.pPollItems ); arg.pPollItems = NULL; } free(arg.fds); free(&arg); return pollfunc(fds, nfds, timeout); } } //if fail,the timeout would work } //3.add timeout unsigned long long now = GetTickMS(); arg.ullExpireTime = now + timeout; int ret = AddTimeout( ctx->pTimeout,&arg,now ); int iRaiseCnt = 0; if( ret != 0 ) { co_log_err("CO_ERR: AddTimeout ret %d now %lld timeout %d arg.ullExpireTime %lld", ret,now,timeout,arg.ullExpireTime); errno = EINVAL; iRaiseCnt = -1; } else { co_yield_env( co_get_curr_thread_env() ); iRaiseCnt = arg.iRaiseCnt; } { //clear epoll status and memory RemoveFromLink( &arg ); for(nfds_t i = 0;i < nfds;i++) { int fd = fds[i].fd; if( fd > -1 ) { co_epoll_ctl( epfd,EPOLL_CTL_DEL,fd,&arg.pPollItems[i].stEvent ); } fds[i].revents = arg.fds[i].revents; } if( arg.pPollItems != arr ) { free( arg.pPollItems ); arg.pPollItems = NULL; } free(arg.fds); free(&arg); } return iRaiseCnt; } int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms ) { return co_poll_inner(ctx, fds, nfds, timeout_ms, NULL); } void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev ) { env->pEpoll = ev; } stCoEpoll_t *co_get_epoll_ct() { if( !co_get_curr_thread_env() ) { co_init_curr_thread_env(); } return co_get_curr_thread_env()->pEpoll; } struct stHookPThreadSpec_t { stCoRoutine_t *co; void *value; enum { size = 1024 }; }; void *co_getspecific(pthread_key_t key) { stCoRoutine_t *co = GetCurrThreadCo(); if( !co || co->cIsMain ) { return pthread_getspecific( key ); } return co->aSpec[ key ].value; } int co_setspecific(pthread_key_t key, const void *value) { stCoRoutine_t *co = GetCurrThreadCo(); if( !co || co->cIsMain ) { return pthread_setspecific( key,value ); } co->aSpec[ key ].value = (void*)value; return 0; } void co_disable_hook_sys() { stCoRoutine_t *co = GetCurrThreadCo(); if( co ) { co->cEnableSysHook = 0; } } bool co_is_enable_sys_hook() { stCoRoutine_t *co = GetCurrThreadCo(); return ( co && co->cEnableSysHook ); } stCoRoutine_t *co_self() { return GetCurrThreadCo(); } //co cond struct stCoCond_t; struct stCoCondItem_t { stCoCondItem_t *pPrev; stCoCondItem_t *pNext; stCoCond_t *pLink; stTimeoutItem_t timeout; }; struct stCoCond_t { stCoCondItem_t *head; stCoCondItem_t *tail; }; static void OnSignalProcessEvent( stTimeoutItem_t * ap ) { stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; co_resume( co ); } stCoCondItem_t *co_cond_pop( stCoCond_t *link ); int co_cond_signal( stCoCond_t *si ) { stCoCondItem_t * sp = co_cond_pop( si ); if( !sp ) { return 0; } RemoveFromLink( &sp->timeout ); AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout ); return 0; } int co_cond_broadcast( stCoCond_t *si ) { for(;;) { stCoCondItem_t * sp = co_cond_pop( si ); if( !sp ) return 0; RemoveFromLink( &sp->timeout ); AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout ); } return 0; } int co_cond_timedwait( stCoCond_t *link,int ms ) { stCoCondItem_t* psi = (stCoCondItem_t*)calloc(1, sizeof(stCoCondItem_t)); psi->timeout.pArg = GetCurrThreadCo(); psi->timeout.pfnProcess = OnSignalProcessEvent; if( ms > 0 ) { unsigned long long now = GetTickMS(); psi->timeout.ullExpireTime = now + ms; int ret = AddTimeout( co_get_curr_thread_env()->pEpoll->pTimeout,&psi->timeout,now ); if( ret != 0 ) { free(psi); return ret; } } AddTail( link, psi); co_yield_ct(); RemoveFromLink( psi ); free(psi); return 0; } stCoCond_t *co_cond_alloc() { return (stCoCond_t*)calloc( 1,sizeof(stCoCond_t) ); } int co_cond_free( stCoCond_t * cc ) { free( cc ); return 0; } stCoCondItem_t *co_cond_pop( stCoCond_t *link ) { stCoCondItem_t *p = link->head; if( p ) { PopHead( link ); } return p; } ================================================ FILE: co_routine.h ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CO_ROUTINE_H__ #define __CO_ROUTINE_H__ #include #include #include //1.struct struct stCoRoutine_t; struct stShareStack_t; struct stCoRoutineAttr_t { int stack_size; stShareStack_t* share_stack; stCoRoutineAttr_t() { stack_size = 128 * 1024; share_stack = NULL; } }__attribute__ ((packed)); struct stCoEpoll_t; typedef int (*pfn_co_eventloop_t)(void *); typedef void *(*pfn_co_routine_t)( void * ); //2.co_routine int co_create( stCoRoutine_t **co,const stCoRoutineAttr_t *attr,void *(*routine)(void*),void *arg ); void co_resume( stCoRoutine_t *co ); void co_yield( stCoRoutine_t *co ); void co_yield_ct(); //ct = current thread void co_release( stCoRoutine_t *co ); void co_reset(stCoRoutine_t * co); stCoRoutine_t *co_self(); int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms ); void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg ); //3.specific int co_setspecific( pthread_key_t key, const void *value ); void * co_getspecific( pthread_key_t key ); //4.event stCoEpoll_t * co_get_epoll_ct(); //ct = current thread //5.hook syscall ( poll/read/write/recv/send/recvfrom/sendto ) void co_enable_hook_sys(); void co_disable_hook_sys(); bool co_is_enable_sys_hook(); //6.sync struct stCoCond_t; stCoCond_t *co_cond_alloc(); int co_cond_free( stCoCond_t * cc ); int co_cond_signal( stCoCond_t * ); int co_cond_broadcast( stCoCond_t * ); int co_cond_timedwait( stCoCond_t *,int timeout_ms ); //7.share stack stShareStack_t* co_alloc_sharestack(int iCount, int iStackSize); //8.init envlist for hook get/set env void co_set_env_list( const char *name[],size_t cnt); void co_log_err( const char *fmt,... ); #endif ================================================ FILE: co_routine_inner.h ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CO_ROUTINE_INNER_H__ #include "co_routine.h" #include "coctx.h" struct stCoRoutineEnv_t; struct stCoSpec_t { void *value; }; struct stStackMem_t { stCoRoutine_t* occupy_co; int stack_size; char* stack_bp; //stack_buffer + stack_size char* stack_buffer; }; struct stShareStack_t { unsigned int alloc_idx; int stack_size; int count; stStackMem_t** stack_array; }; struct stCoRoutine_t { stCoRoutineEnv_t *env; pfn_co_routine_t pfn; void *arg; coctx_t ctx; char cStart; char cEnd; char cIsMain; char cEnableSysHook; char cIsShareStack; void *pvEnv; //char sRunStack[ 1024 * 128 ]; stStackMem_t* stack_mem; //save satck buffer while confilct on same stack_buffer; char* stack_sp; unsigned int save_size; char* save_buffer; stCoSpec_t aSpec[1024]; }; //1.env void co_init_curr_thread_env(); stCoRoutineEnv_t * co_get_curr_thread_env(); //2.coroutine void co_free( stCoRoutine_t * co ); void co_yield_env( stCoRoutineEnv_t *env ); //3.func //----------------------------------------------------------------------------------------------- struct stTimeout_t; struct stTimeoutItem_t ; stTimeout_t *AllocTimeout( int iSize ); void FreeTimeout( stTimeout_t *apTimeout ); int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,uint64_t allNow ); struct stCoEpoll_t; stCoEpoll_t * AllocEpoll(); void FreeEpoll( stCoEpoll_t *ctx ); stCoRoutine_t * GetCurrThreadCo(); void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev ); typedef void (*pfnCoRoutineFunc_t)(); #endif #define __CO_ROUTINE_INNER_H__ ================================================ FILE: co_routine_specific.h ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include /* invoke only once in the whole program CoRoutineSetSpecificCallBack(CoRoutineGetSpecificFunc_t pfnGet,CoRoutineSetSpecificFunc_t pfnSet) struct MyData_t { int iValue; char szValue[100]; }; CO_ROUTINE_SPECIFIC( MyData_t,__routine ); int main() { CoRoutineSetSpecificCallBack( co_getspecific,co_setspecific ); __routine->iValue = 10; strcpy( __routine->szValue,"hello world" ); return 0; } */ extern int co_setspecific( pthread_key_t key, const void *value ); extern void * co_getspecific( pthread_key_t key ); #define CO_ROUTINE_SPECIFIC( name,y ) \ \ static pthread_once_t _routine_once_##name = PTHREAD_ONCE_INIT; \ static pthread_key_t _routine_key_##name;\ static int _routine_init_##name = 0;\ static void _routine_make_key_##name() \ {\ (void) pthread_key_create(&_routine_key_##name, NULL); \ }\ template \ class clsRoutineData_routine_##name\ {\ public:\ inline T *operator->()\ {\ if( !_routine_init_##name ) \ {\ pthread_once( &_routine_once_##name,_routine_make_key_##name );\ _routine_init_##name = 1;\ }\ T* p = (T*)co_getspecific( _routine_key_##name );\ if( !p )\ {\ p = (T*)calloc(1,sizeof( T ));\ int ret = co_setspecific( _routine_key_##name,p) ;\ if ( ret )\ {\ if ( p )\ {\ free(p);\ p = NULL;\ }\ }\ }\ return p;\ }\ };\ \ static clsRoutineData_routine_##name y; ================================================ FILE: coctx.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "coctx.h" #include #include #define ESP 0 #define EIP 1 #define EAX 2 #define ECX 3 // ----------- #define RSP 0 #define RIP 1 #define RBX 2 #define RDI 3 #define RSI 4 #define RBP 5 #define R12 6 #define R13 7 #define R14 8 #define R15 9 #define RDX 10 #define RCX 11 #define R8 12 #define R9 13 //----- -------- // 32 bit // | regs[0]: ret | // | regs[1]: ebx | // | regs[2]: ecx | // | regs[3]: edx | // | regs[4]: edi | // | regs[5]: esi | // | regs[6]: ebp | // | regs[7]: eax | = esp enum { kEIP = 0, kEBP = 6, kESP = 7, }; //------------- // 64 bit // low | regs[0]: r15 | // | regs[1]: r14 | // | regs[2]: r13 | // | regs[3]: r12 | // | regs[4]: r9 | // | regs[5]: r8 | // | regs[6]: rbp | // | regs[7]: rdi | // | regs[8]: rsi | // | regs[9]: ret | //ret func addr // | regs[10]: rdx | // | regs[11]: rcx | // | regs[12]: rbx | // hig | regs[13]: rsp | enum { kRDI = 7, kRSI = 8, kRETAddr = 9, kRSP = 13, }; // 64 bit extern "C" { extern void coctx_swap(coctx_t*, coctx_t*) asm("coctx_swap"); }; #if defined(__i386__) int coctx_init(coctx_t* ctx) { memset(ctx, 0, sizeof(*ctx)); return 0; } int coctx_make(coctx_t* ctx, coctx_pfn_t pfn, const void* s, const void* s1) { // make room for coctx_param char* sp = ctx->ss_sp + ctx->ss_size - sizeof(coctx_param_t); sp = (char*)((unsigned long)sp & -16L); coctx_param_t* param = (coctx_param_t*)sp; void** ret_addr = (void**)(sp - sizeof(void*) * 2); *ret_addr = (void*)pfn; param->s1 = s; param->s2 = s1; memset(ctx->regs, 0, sizeof(ctx->regs)); ctx->regs[kESP] = (char*)(sp) - sizeof(void*) * 2; return 0; } #elif defined(__x86_64__) int coctx_make(coctx_t* ctx, coctx_pfn_t pfn, const void* s, const void* s1) { char* sp = ctx->ss_sp + ctx->ss_size - sizeof(void*); sp = (char*)((unsigned long)sp & -16LL); memset(ctx->regs, 0, sizeof(ctx->regs)); void** ret_addr = (void**)(sp); *ret_addr = (void*)pfn; ctx->regs[kRSP] = sp; ctx->regs[kRETAddr] = (char*)pfn; ctx->regs[kRDI] = (char*)s; ctx->regs[kRSI] = (char*)s1; return 0; } int coctx_init(coctx_t* ctx) { memset(ctx, 0, sizeof(*ctx)); return 0; } #endif ================================================ FILE: coctx.h ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CO_CTX_H__ #define __CO_CTX_H__ #include typedef void* (*coctx_pfn_t)( void* s, void* s2 ); struct coctx_param_t { const void *s1; const void *s2; }; struct coctx_t { #if defined(__i386__) void *regs[ 8 ]; #else void *regs[ 14 ]; #endif size_t ss_size; char *ss_sp; }; int coctx_init( coctx_t *ctx ); int coctx_make( coctx_t *ctx,coctx_pfn_t pfn,const void *s,const void *s1 ); #endif ================================================ FILE: coctx_swap.S ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ .globl coctx_swap #if !defined( __APPLE__ ) .type coctx_swap, @function #endif coctx_swap: #if defined(__i386__) movl 4(%esp), %eax movl %esp, 28(%eax) movl %ebp, 24(%eax) movl %esi, 20(%eax) movl %edi, 16(%eax) movl %edx, 12(%eax) movl %ecx, 8(%eax) movl %ebx, 4(%eax) movl 8(%esp), %eax movl 4(%eax), %ebx movl 8(%eax), %ecx movl 12(%eax), %edx movl 16(%eax), %edi movl 20(%eax), %esi movl 24(%eax), %ebp movl 28(%eax), %esp ret #elif defined(__x86_64__) leaq (%rsp),%rax movq %rax, 104(%rdi) movq %rbx, 96(%rdi) movq %rcx, 88(%rdi) movq %rdx, 80(%rdi) movq 0(%rax), %rax movq %rax, 72(%rdi) movq %rsi, 64(%rdi) movq %rdi, 56(%rdi) movq %rbp, 48(%rdi) movq %r8, 40(%rdi) movq %r9, 32(%rdi) movq %r12, 24(%rdi) movq %r13, 16(%rdi) movq %r14, 8(%rdi) movq %r15, (%rdi) xorq %rax, %rax movq 48(%rsi), %rbp movq 104(%rsi), %rsp movq (%rsi), %r15 movq 8(%rsi), %r14 movq 16(%rsi), %r13 movq 24(%rsi), %r12 movq 32(%rsi), %r9 movq 40(%rsi), %r8 movq 56(%rsi), %rdi movq 80(%rsi), %rdx movq 88(%rsi), %rcx movq 96(%rsi), %rbx leaq 8(%rsp), %rsp pushq 72(%rsi) movq 64(%rsi), %rsi ret #endif ================================================ FILE: example_closure.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "co_closure.h" #include #include #include #include #include using namespace std; static void *thread_func( void * arg ) { stCoClosure_t *p = (stCoClosure_t*) arg; p->exec(); return 0; } static void batch_exec( vector &v ) { vector ths; for( size_t i=0;i v; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int total = 100; vector v2; co_ref( ref,total,v2,m); for(int i=0;i<10;i++) { co_func( f,ref,i ) { printf("ref.total %d i %d\n",ref.total,i ); //lock pthread_mutex_lock(&ref.m); ref.v2.push_back( i ); pthread_mutex_unlock(&ref.m); //unlock } co_func_end; v.push_back( new f( ref,i ) ); } for(int i=0;i<2;i++) { co_func( f2,i ) { printf("i: %d\n",i); for(int j=0;j<2;j++) { usleep( 1000 ); printf("i %d j %d\n",i,j); } } co_func_end; v.push_back( new f2( i ) ); } batch_exec( v ); printf("done\n"); return 0; } ================================================ FILE: example_cond.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "co_routine.h" using namespace std; struct stTask_t { int id; }; struct stEnv_t { stCoCond_t* cond; queue task_queue; }; void* Producer(void* args) { co_enable_hook_sys(); stEnv_t* env= (stEnv_t*)args; int id = 0; while (true) { stTask_t* task = (stTask_t*)calloc(1, sizeof(stTask_t)); task->id = id++; env->task_queue.push(task); printf("%s:%d produce task %d\n", __func__, __LINE__, task->id); co_cond_signal(env->cond); poll(NULL, 0, 1000); } return NULL; } void* Consumer(void* args) { co_enable_hook_sys(); stEnv_t* env = (stEnv_t*)args; while (true) { if (env->task_queue.empty()) { co_cond_timedwait(env->cond, -1); continue; } stTask_t* task = env->task_queue.front(); env->task_queue.pop(); printf("%s:%d consume task %d\n", __func__, __LINE__, task->id); free(task); } return NULL; } int main() { stEnv_t* env = new stEnv_t; env->cond = co_cond_alloc(); stCoRoutine_t* consumer_routine; co_create(&consumer_routine, NULL, Consumer, env); co_resume(consumer_routine); stCoRoutine_t* producer_routine; co_create(&producer_routine, NULL, Producer, env); co_resume(producer_routine); co_eventloop(co_get_epoll_ct(), NULL, NULL); return 0; } ================================================ FILE: example_copystack.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "coctx.h" #include "co_routine.h" #include "co_routine_inner.h" void* RoutineFunc(void* args) { co_enable_hook_sys(); int* routineid = (int*)args; while (true) { char sBuff[128]; sprintf(sBuff, "from routineid %d stack addr %p\n", *routineid, sBuff); printf("%s", sBuff); poll(NULL, 0, 1000); //sleep 1s } return NULL; } int main() { stShareStack_t* share_stack= co_alloc_sharestack(1, 1024 * 128); stCoRoutineAttr_t attr; attr.stack_size = 0; attr.share_stack = share_stack; stCoRoutine_t* co[2]; int routineid[2]; for (int i = 0; i < 2; i++) { routineid[i] = i; co_create(&co[i], &attr, RoutineFunc, routineid + i); co_resume(co[i]); } co_eventloop(co_get_epoll_ct(), NULL, NULL); return 0; } ================================================ FILE: example_echocli.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "co_routine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; struct stEndPoint { char *ip; unsigned short int port; }; static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) { bzero(&addr,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(shPort); int nIP = 0; if( !pszIP || '\0' == *pszIP || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") || 0 == strcmp(pszIP,"*") ) { nIP = htonl(INADDR_ANY); } else { nIP = inet_addr(pszIP); } addr.sin_addr.s_addr = nIP; } static int iSuccCnt = 0; static int iFailCnt = 0; static int iTime = 0; void AddSuccCnt() { int now = time(NULL); if (now >iTime) { printf("time %d Succ Cnt %d Fail Cnt %d\n", iTime, iSuccCnt, iFailCnt); iTime = now; iSuccCnt = 0; iFailCnt = 0; } else { iSuccCnt++; } } void AddFailCnt() { int now = time(NULL); if (now >iTime) { printf("time %d Succ Cnt %d Fail Cnt %d\n", iTime, iSuccCnt, iFailCnt); iTime = now; iSuccCnt = 0; iFailCnt = 0; } else { iFailCnt++; } } static void *readwrite_routine( void *arg ) { co_enable_hook_sys(); stEndPoint *endpoint = (stEndPoint *)arg; char str[8]="sarlmol"; char buf[ 1024 * 16 ]; int fd = -1; int ret = 0; for(;;) { if ( fd < 0 ) { fd = socket(PF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; SetAddr(endpoint->ip, endpoint->port, addr); ret = connect(fd,(struct sockaddr*)&addr,sizeof(addr)); if ( errno == EALREADY || errno == EINPROGRESS ) { struct pollfd pf = { 0 }; pf.fd = fd; pf.events = (POLLOUT|POLLERR|POLLHUP); co_poll( co_get_epoll_ct(),&pf,1,200); //check connect int error = 0; uint32_t socklen = sizeof(error); errno = 0; ret = getsockopt(fd, SOL_SOCKET, SO_ERROR,(void *)&error, &socklen); if ( ret == -1 ) { //printf("getsockopt ERROR ret %d %d:%s\n", ret, errno, strerror(errno)); close(fd); fd = -1; AddFailCnt(); continue; } if ( error ) { errno = error; //printf("connect ERROR ret %d %d:%s\n", error, errno, strerror(errno)); close(fd); fd = -1; AddFailCnt(); continue; } } } ret = write( fd,str, 8); if ( ret > 0 ) { ret = read( fd,buf, sizeof(buf) ); if ( ret <= 0 ) { //printf("co %p read ret %d errno %d (%s)\n", // co_self(), ret,errno,strerror(errno)); close(fd); fd = -1; AddFailCnt(); } else { //printf("echo %s fd %d\n", buf,fd); AddSuccCnt(); } } else { //printf("co %p write ret %d errno %d (%s)\n", // co_self(), ret,errno,strerror(errno)); close(fd); fd = -1; AddFailCnt(); } } return 0; } int main(int argc,char *argv[]) { stEndPoint endpoint; endpoint.ip = argv[1]; endpoint.port = atoi(argv[2]); int cnt = atoi( argv[3] ); int proccnt = atoi( argv[4] ); struct sigaction sa; sa.sa_handler = SIG_IGN; sigaction( SIGPIPE, &sa, NULL ); for(int k=0;k 0 ) { continue; } else if( pid < 0 ) { break; } for(int i=0;i #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #include #include #endif using namespace std; struct task_t { stCoRoutine_t *co; int fd; }; static stack g_readwrite; static int g_listen_fd = -1; static int SetNonBlock(int iSock) { int iFlags; iFlags = fcntl(iSock, F_GETFL, 0); iFlags |= O_NONBLOCK; iFlags |= O_NDELAY; int ret = fcntl(iSock, F_SETFL, iFlags); return ret; } static void *readwrite_routine( void *arg ) { co_enable_hook_sys(); task_t *co = (task_t*)arg; char buf[ 1024 * 16 ]; for(;;) { if( -1 == co->fd ) { g_readwrite.push( co ); co_yield_ct(); continue; } int fd = co->fd; co->fd = -1; for(;;) { struct pollfd pf = { 0 }; pf.fd = fd; pf.events = (POLLIN|POLLERR|POLLHUP); co_poll( co_get_epoll_ct(),&pf,1,1000); int ret = read( fd,buf,sizeof(buf) ); if( ret > 0 ) { ret = write( fd,buf,ret ); } if( ret > 0 || ( -1 == ret && EAGAIN == errno ) ) { continue; } close( fd ); break; } } return 0; } int co_accept(int fd, struct sockaddr *addr, socklen_t *len ); static void *accept_routine( void * ) { co_enable_hook_sys(); printf("accept_routine\n"); fflush(stdout); for(;;) { //printf("pid %ld g_readwrite.size %ld\n",getpid(),g_readwrite.size()); if( g_readwrite.empty() ) { printf("empty\n"); //sleep struct pollfd pf = { 0 }; pf.fd = -1; poll( &pf,1,1000); continue; } struct sockaddr_in addr; //maybe sockaddr_un; memset( &addr,0,sizeof(addr) ); socklen_t len = sizeof(addr); int fd = co_accept(g_listen_fd, (struct sockaddr *)&addr, &len); if( fd < 0 ) { struct pollfd pf = { 0 }; pf.fd = g_listen_fd; pf.events = (POLLIN|POLLERR|POLLHUP); co_poll( co_get_epoll_ct(),&pf,1,1000 ); continue; } if( g_readwrite.empty() ) { close( fd ); continue; } SetNonBlock( fd ); task_t *co = g_readwrite.top(); co->fd = fd; g_readwrite.pop(); co_resume( co->co ); } return 0; } static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) { bzero(&addr,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(shPort); int nIP = 0; if( !pszIP || '\0' == *pszIP || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") || 0 == strcmp(pszIP,"*") ) { nIP = htonl(INADDR_ANY); } else { nIP = inet_addr(pszIP); } addr.sin_addr.s_addr = nIP; } static int CreateTcpSocket(const unsigned short shPort /* = 0 */,const char *pszIP /* = "*" */,bool bReuse /* = false */) { int fd = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP); if( fd >= 0 ) { if(shPort != 0) { if(bReuse) { int nReuseAddr = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&nReuseAddr,sizeof(nReuseAddr)); } struct sockaddr_in addr ; SetAddr(pszIP,shPort,addr); int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); if( ret != 0) { close(fd); return -1; } } } return fd; } int main(int argc,char *argv[]) { if(argc<5){ printf("Usage:\n" "example_echosvr [IP] [PORT] [TASK_COUNT] [PROCESS_COUNT]\n" "example_echosvr [IP] [PORT] [TASK_COUNT] [PROCESS_COUNT] -d # daemonize mode\n"); return -1; } const char *ip = argv[1]; int port = atoi( argv[2] ); int cnt = atoi( argv[3] ); int proccnt = atoi( argv[4] ); bool deamonize = argc >= 6 && strcmp(argv[5], "-d") == 0; g_listen_fd = CreateTcpSocket( port,ip,true ); listen( g_listen_fd,1024 ); if(g_listen_fd==-1){ printf("Port %d is in use\n", port); return -1; } printf("listen %d %s:%d\n",g_listen_fd,ip,port); SetNonBlock( g_listen_fd ); for(int k=0;k 0 ) { continue; } else if( pid < 0 ) { break; } for(int i=0;ifd = -1; co_create( &(task->co),NULL,readwrite_routine,task ); co_resume( task->co ); } stCoRoutine_t *accept_co = NULL; co_create( &accept_co,NULL,accept_routine,0 ); co_resume( accept_co ); co_eventloop( co_get_epoll_ct(),0,0 ); exit(0); } if(!deamonize) wait(NULL); return 0; } ================================================ FILE: example_poll.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "co_routine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #endif using namespace std; struct task_t { stCoRoutine_t *co; int fd; struct sockaddr_in addr; }; static int SetNonBlock(int iSock) { int iFlags; iFlags = fcntl(iSock, F_GETFL, 0); iFlags |= O_NONBLOCK; iFlags |= O_NDELAY; int ret = fcntl(iSock, F_SETFL, iFlags); return ret; } static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) { bzero(&addr,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(shPort); int nIP = 0; if( !pszIP || '\0' == *pszIP || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") || 0 == strcmp(pszIP,"*") ) { nIP = htonl(INADDR_ANY); } else { nIP = inet_addr(pszIP); } addr.sin_addr.s_addr = nIP; } static int CreateTcpSocket(const unsigned short shPort = 0 ,const char *pszIP = "*" ,bool bReuse = false ) { int fd = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP); if( fd >= 0 ) { if(shPort != 0) { if(bReuse) { int nReuseAddr = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&nReuseAddr,sizeof(nReuseAddr)); } struct sockaddr_in addr ; SetAddr(pszIP,shPort,addr); int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); if( ret != 0) { close(fd); return -1; } } } return fd; } static void *poll_routine( void *arg ) { co_enable_hook_sys(); vector &v = *(vector*)arg; for(size_t i=0;i setRaiseFds; size_t iWaitCnt = v.size(); for(;;) { int ret = poll( pf,iWaitCnt,1000 ); printf("co %p poll wait %ld ret %d\n", co_self(),iWaitCnt,ret); for(int i=0;i<(int)iWaitCnt;i++) { printf("co %p fire fd %d revents 0x%X POLLOUT 0x%X POLLERR 0x%X POLLHUP 0x%X\n", co_self(), pf[i].fd, pf[i].revents, POLLOUT, POLLERR, POLLHUP ); setRaiseFds.insert( pf[i].fd ); } if( setRaiseFds.size() == v.size()) { break; } if( ret <= 0 ) { break; } iWaitCnt = 0; for(size_t i=0;i v; for(int i=1;i v2 = v; poll_routine( &v2 ); printf("--------------------- routine -------------------\n"); for(int i=0;i<10;i++) { stCoRoutine_t *co = 0; vector *v2 = new vector(); *v2 = v; co_create( &co,NULL,poll_routine,v2 ); printf("routine i %d\n",i); co_resume( co ); } co_eventloop( co_get_epoll_ct(),0,0 ); return 0; } //./example_poll 127.0.0.1 12365 127.0.0.1 12222 192.168.1.1 1000 192.168.1.2 1111 ================================================ FILE: example_setenv.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "co_routine.h" const char* CGI_ENV_HOOK_LIST [] = { "CGINAME", }; struct stRoutineArgs_t { int iRoutineID; }; void SetAndGetEnv(int iRoutineID) { printf("routineid %d begin\n", iRoutineID); //use poll as sleep poll(NULL, 0, 500); char sBuf[128]; sprintf(sBuf, "cgi_routine_%d", iRoutineID); int ret = setenv("CGINAME", sBuf, 1); if (ret) { printf("%s:%d set env err ret %d errno %d %s\n", __func__, __LINE__, ret, errno, strerror(errno)); return; } printf("routineid %d set env CGINAME %s\n", iRoutineID, sBuf); poll(NULL, 0, 500); char* env = getenv("CGINAME"); if (!env) { printf("%s:%d get env err errno %d %s\n", __func__, __LINE__, errno, strerror(errno)); return; } printf("routineid %d get env CGINAME %s\n", iRoutineID, env); } void* RoutineFunc(void* args) { co_enable_hook_sys(); stRoutineArgs_t* g = (stRoutineArgs_t*)args; SetAndGetEnv(g->iRoutineID); return NULL; } int main(int argc, char* argv[]) { co_set_env_list(CGI_ENV_HOOK_LIST, sizeof(CGI_ENV_HOOK_LIST) / sizeof(char*)); stRoutineArgs_t args[3]; for (int i = 0; i < 3; i++) { stCoRoutine_t* co = NULL; args[i].iRoutineID = i; co_create(&co, NULL, RoutineFunc, &args[i]); co_resume(co); } co_eventloop(co_get_epoll_ct(), NULL, NULL); return 0; } ================================================ FILE: example_specific.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "co_routine_specific.h" #include "co_routine.h" #include #include #include #include using namespace std; struct stRoutineArgs_t { stCoRoutine_t* co; int routine_id; }; struct stRoutineSpecificData_t { int idx; }; CO_ROUTINE_SPECIFIC(stRoutineSpecificData_t, __routine); void* RoutineFunc(void* args) { co_enable_hook_sys(); stRoutineArgs_t* routine_args = (stRoutineArgs_t*)args; __routine->idx = routine_args->routine_id; while (true) { printf("%s:%d routine specific data idx %d\n", __func__, __LINE__, __routine->idx); poll(NULL, 0, 1000); } return NULL; } int main() { stRoutineArgs_t args[10]; for (int i = 0; i < 10; i++) { args[i].routine_id = i; co_create(&args[i].co, NULL, RoutineFunc, (void*)&args[i]); co_resume(args[i].co); } co_eventloop(co_get_epoll_ct(), NULL, NULL); return 0; } ================================================ FILE: example_thread.cpp ================================================ /* * Tencent is pleased to support the open source community by making Libco available. * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "co_routine.h" #include "co_routine_inner.h" #include #include #include #include #include int loop(void *) { return 0; } static void *routine_func( void * ) { stCoEpoll_t * ev = co_get_epoll_ct(); //ct = current thread co_eventloop( ev,loop,0 ); return 0; } int main(int argc,char *argv[]) { int cnt = atoi( argv[1] ); pthread_t tid[ cnt ]; for(int i=0;i