├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── example ├── echoserver.c ├── sleep_coroutine.c └── test.cc ├── lib └── .gitignore ├── obj └── .gitignore ├── oldsrc ├── coroutine.cc ├── coroutine.h ├── hooks.cc ├── idmap.cc └── idmap.h └── src ├── assert.h ├── context.c ├── context.h ├── context_swap.S ├── coroutine.h ├── coroutine_impl.c ├── coroutine_impl.h ├── coroutine_specific.c ├── coroutine_specific.h ├── coroutine_task.c ├── coroutine_task.h ├── epoll.c ├── epoll.h ├── hook_sys_call.c ├── misc.c ├── misc.h └── typedef.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | cscope* 6 | tag* 7 | *swp 8 | 9 | # Libraries 10 | *.lib 11 | *.a 12 | 13 | # Shared objects (inc. Windows DLLs) 14 | *.dll 15 | *.so 16 | *.so.* 17 | *.dylib 18 | 19 | # Executables 20 | *.exe 21 | *.out 22 | *.app 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, lichuang 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DIR=. 2 | BIN_DIR=$(DIR)/bin 3 | SRC_DIR=$(DIR)/src 4 | INCLUDE_DIR=$(DIR)/ 5 | OBJ_DIR=$(DIR)/obj 6 | LIB_DIR=$(DIR)/lib 7 | LIB=libcr.a 8 | 9 | EXTENSION=c 10 | OBJS=$(patsubst $(SRC_DIR)/%.$(EXTENSION), $(OBJ_DIR)/%.o,$(wildcard $(SRC_DIR)/*.$(EXTENSION))) 11 | OBJS+=$(OBJ_DIR)/context_swap.o 12 | DEPS=$(patsubst $(OBJ_DIR)/%.o, $(DEPS_DIR)/%.d, $(OBJS)) 13 | 14 | INCLUDE= 15 | 16 | CC=gcc 17 | AR= ar rcu 18 | #CFLAGS=-std=c99 -Wall -Werror -g 19 | CFLAGS=-std=c99 -Wall -g -fno-strict-aliasing -O0 -export-dynamic -Wall -pipe -D_GNU_SOURCE -D_REENTRANT -fPIC -Wno-deprecated -m64 20 | #LDFLAGS= -L ./lib -lcr -pthread 21 | LDFLAGS= -L ./lib -lcr -ldl -lpthread 22 | 23 | all:$(OBJS) 24 | $(AR) $(LIB_DIR)/$(LIB) $(OBJS) 25 | $(CC) example/echoserver.c -I./src $(CFLAGS) $(INCLUDE) -o example/echoserver $(LDFLAGS) 26 | $(CC) example/sleep_coroutine.c -I./src $(CFLAGS) $(INCLUDE) -o example/sleep_coroutine $(LDFLAGS) 27 | 28 | ex:$(LIB_DIR)/$(LIB) example/*.cc 29 | $(CC) example/test.cc -I./src $(CFLAGS) $(INCLUDE) -o example/test $(LDFLAGS) 30 | 31 | test:$(LIB_DIR)/$(LIB) 32 | $(CC) test/main.c -I./src $(CFLAGS) $(INCLUDE) -o test/test $(LDFLAGS) 33 | 34 | $(OBJ_DIR)/context_swap.o:$(SRC_DIR)/context_swap.S 35 | $(CC) $< -o $@ -c $(CFLAGS) $(INCLUDE) 36 | 37 | $(OBJ_DIR)/%.o:$(SRC_DIR)/%.$(EXTENSION) 38 | $(CC) $< -o $@ -c $(CFLAGS) $(INCLUDE) 39 | 40 | rebuild: 41 | make clean 42 | make 43 | 44 | clean: 45 | rm -rf $(OBJS) $(BIN_DIR)/* $(LIB_DIR)/* 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libcr 2 | ===== 3 | 4 | a coroutine lib for C users,in non-intrusive mode. 5 | -------------------------------------------------------------------------------- /example/echoserver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include //inet_addr 6 | #include 7 | #include 8 | #include 9 | #include "coroutine.h" 10 | 11 | int count = 0; 12 | 13 | void *timeout(void *arg) { 14 | printf("timeout\n"); 15 | return NULL; 16 | } 17 | 18 | void *echo(void *arg) { 19 | int fd = *((int*)arg); 20 | char buf[1024]; 21 | 22 | printf("in echo\n"); 23 | while (1) { 24 | ssize_t n = recv(fd, buf, sizeof(buf), 0); 25 | if (n <= 0) { 26 | close(fd); 27 | break; 28 | } 29 | buf[n] = '\0'; 30 | printf("echo recv: %s, n: %ld\n", buf, n); 31 | 32 | n = send(fd, buf, n, 0); 33 | if (n <= 0) { 34 | close(fd); 35 | break; 36 | } 37 | } 38 | 39 | printf("end:%d\n", ++count); 40 | return NULL; 41 | } 42 | 43 | void *listener(void *arg) { 44 | printf("in listener\n"); 45 | struct sockaddr_in server; 46 | int socket_desc = socket(AF_INET , SOCK_STREAM , 0); 47 | 48 | server.sin_family = AF_INET; 49 | server.sin_addr.s_addr = INADDR_ANY; 50 | server.sin_port = htons(28888); 51 | 52 | fcntl(socket_desc, F_SETFL, fcntl(socket_desc, F_GETFL) | O_NONBLOCK); 53 | bind(socket_desc,(struct sockaddr *)&server , sizeof(server)); 54 | int optval = 1; 55 | setsockopt(socket_desc, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval); 56 | listen(socket_desc, 20000); 57 | 58 | while (1) { 59 | int fd = accept(socket_desc, NULL, NULL); 60 | 61 | //printf("accept %d, count:%d\n", fd, count); 62 | if (fd > 0) { 63 | coroutine_task_attr_t attr; 64 | attr.enable_sys_hook = 1; 65 | attr.max_timeout_ms = 5000; 66 | attr.arg = &fd; 67 | attr.fun = echo; 68 | attr.timeout = timeout; 69 | coroutine_new_task(&attr); 70 | } 71 | } 72 | 73 | return NULL; 74 | } 75 | 76 | int main() { 77 | coroutine_options_t options; 78 | options.stack_size = 8 * 1024; 79 | 80 | coroutine_init_env(&options); 81 | 82 | coroutine_task_attr_t attr; 83 | attr.enable_sys_hook = 1; 84 | attr.max_timeout_ms = -1; 85 | attr.arg = NULL; 86 | attr.fun = listener; 87 | attr.timeout = NULL; 88 | 89 | coroutine_new_task(&attr); 90 | coroutine_eventloop(NULL); 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /example/sleep_coroutine.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include //inet_addr 6 | #include 7 | #include 8 | #include 9 | #include "coroutine.h" 10 | 11 | // this demo show that: in coroutine,call sleep function should not block the other coroutine in the same thread 12 | void *fun1(void *arg) { 13 | while (1) { 14 | printf("in fun1\n"); 15 | sleep(1); 16 | } 17 | 18 | return NULL; 19 | } 20 | 21 | void *fun2(void *arg) { 22 | while (1) { 23 | printf("in fun2\n"); 24 | sleep(5); 25 | } 26 | return NULL; 27 | } 28 | 29 | int main() { 30 | coroutine_options_t options; 31 | options.stack_size = 8 * 1024; 32 | 33 | coroutine_init_env(&options); 34 | 35 | coroutine_task_attr_t attr; 36 | attr.enable_sys_hook = 1; 37 | attr.max_timeout_ms = -1; 38 | attr.arg = NULL; 39 | attr.fun = fun1; 40 | attr.timeout = NULL; 41 | 42 | coroutine_new_task(&attr); 43 | 44 | attr.fun = fun2; 45 | coroutine_new_task(&attr); 46 | coroutine_eventloop(NULL); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /example/test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include //inet_addr 5 | #include 6 | #include 7 | #include 8 | #include "coroutine.h" 9 | 10 | void *echo(void *arg) { 11 | int fd = *((int*)arg); 12 | char buf[1024]; 13 | 14 | printf("in echo\n"); 15 | while (true) { 16 | ssize_t n = recv(fd, buf, sizeof(buf), 0); 17 | if (n <= 0) { 18 | break; 19 | } 20 | buf[n] = '\0'; 21 | printf("echo recv: %s\n", buf); 22 | 23 | n = send(fd, buf, n, 0); 24 | if (n <= 0) { 25 | break; 26 | } 27 | } 28 | 29 | return NULL; 30 | } 31 | 32 | void *listener(void *arg) { 33 | struct sockaddr_in server; 34 | int socket_desc = socket(AF_INET , SOCK_STREAM , 0); 35 | 36 | server.sin_family = AF_INET; 37 | server.sin_addr.s_addr = INADDR_ANY; 38 | server.sin_port = htons(18888); 39 | 40 | fcntl(socket_desc, F_SETFL, fcntl(socket_desc, F_GETFL) | O_NONBLOCK); 41 | bind(socket_desc,(struct sockaddr *)&server , sizeof(server)); 42 | int optval = 1; 43 | setsockopt(socket_desc, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval); 44 | listen(socket_desc, 200); 45 | 46 | while (true) { 47 | int fd = accept(socket_desc, NULL, NULL); 48 | 49 | if (fd != -1) { 50 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); 51 | 52 | pthread_t id; 53 | pthread_create(&id, NULL, echo, &fd); 54 | } 55 | } 56 | return NULL; 57 | } 58 | 59 | int main() { 60 | pthread_t thread; 61 | 62 | pthread_create(&thread, NULL, listener, NULL); 63 | 64 | gSched.Run(); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | # Libraries 6 | *.lib 7 | *.a 8 | 9 | # Shared objects (inc. Windows DLLs) 10 | *.dll 11 | *.so 12 | *.so.* 13 | *.dylib 14 | 15 | # Executables 16 | *.exe 17 | *.out 18 | *.app 19 | -------------------------------------------------------------------------------- /obj/.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | # Libraries 6 | *.lib 7 | *.a 8 | 9 | # Shared objects (inc. Windows DLLs) 10 | *.dll 11 | *.so 12 | *.so.* 13 | *.dylib 14 | 15 | # Executables 16 | *.exe 17 | *.out 18 | *.app 19 | -------------------------------------------------------------------------------- /oldsrc/coroutine.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "coroutine.h" 9 | 10 | // stack size 11 | static const int kProtectStackSize = 10 * 1024; 12 | static const int kDefaulaStackSize = 100 * 1024; 13 | 14 | // coroutine num 15 | static const int kCoroNum = 256; 16 | 17 | static const int kStatusDead = 0; 18 | static const int kStatusReady = 1; 19 | static const int kStatusRunning = 2; 20 | static const int kStatusSuspend = 3; 21 | 22 | typedef int (*sysAccept)(int, struct sockaddr *, socklen_t *); 23 | typedef ssize_t (*sysRecv)(int fd, void *buf, size_t len, int flags); 24 | typedef ssize_t (*sysSend)(int fd, const void *buf, size_t len, int flags); 25 | typedef int (*sysClose)(int fd); 26 | 27 | static sysAccept gSysAccept; 28 | static sysRecv gSysRecv; 29 | static sysSend gSysSend; 30 | static sysClose gSysClose; 31 | 32 | Scheduler gSched; 33 | 34 | struct Coroutine { 35 | void *arg_; 36 | cfunc fun_; 37 | int id_; 38 | char *stack_; 39 | int status_; 40 | ucontext_t ctx_; 41 | 42 | Coroutine(void *arg, cfunc fun, int id) 43 | : arg_(arg), 44 | fun_(fun), 45 | id_(id), 46 | stack_(NULL), 47 | status_(kStatusReady) { 48 | stack_ = new char[kProtectStackSize + kDefaulaStackSize]; 49 | } 50 | 51 | ~Coroutine() { 52 | delete [] stack_; 53 | } 54 | }; 55 | 56 | struct Socket { 57 | int fd_; 58 | Coroutine* coro_; 59 | }; 60 | 61 | Scheduler::Scheduler() 62 | : epfd_(-1), 63 | running_(-1), 64 | num_(0) { 65 | coros_.resize(kCoroNum, NULL); 66 | socks_.resize(kCoroNum, NULL); 67 | 68 | epfd_ = epoll_create(1024); 69 | 70 | gSysAccept = (sysAccept)dlsym(RTLD_NEXT, "accept"); 71 | gSysRecv = (sysRecv)dlsym(RTLD_NEXT, "recv"); 72 | gSysSend = (sysSend)dlsym(RTLD_NEXT, "send"); 73 | gSysClose = (sysClose)dlsym(RTLD_NEXT, "close"); 74 | } 75 | 76 | Scheduler::~Scheduler() { 77 | size_t i; 78 | 79 | for (i = 0; i < coros_.size(); ++i) { 80 | if (coros_[i]) { 81 | delete coros_[i]; 82 | } 83 | if (socks_[i]) { 84 | delete socks_[i]; 85 | } 86 | } 87 | } 88 | 89 | int 90 | Scheduler::Spawn(void *arg, cfunc fun) { 91 | int id; 92 | Coroutine *coro; 93 | 94 | id = id_map_.Allocate(); 95 | if (id < 0) { 96 | return id; 97 | } 98 | coro = new Coroutine(arg, fun, id); 99 | coros_[id] = coro; 100 | active_.push_back(coro); 101 | 102 | return id; 103 | } 104 | 105 | void 106 | Scheduler::Yield() { 107 | int id; 108 | Coroutine *coro; 109 | 110 | id = running_; 111 | coro = coros_[id]; 112 | coro->status_ = kStatusSuspend; 113 | running_ = -1; 114 | swapcontext(&coro->ctx_, &main_); 115 | } 116 | 117 | int 118 | Scheduler::Status(int id) { 119 | Coroutine *coro; 120 | 121 | coro = coros_[id]; 122 | if (coro == NULL) { 123 | return kStatusDead; 124 | } 125 | 126 | return coro->status_; 127 | } 128 | 129 | void 130 | mainfunc(void *) { 131 | Scheduler *sched = &gSched; 132 | int id = sched->running_; 133 | Coroutine *coro = sched->coros_[id]; 134 | coro->fun_(coro->arg_); 135 | 136 | sched->id_map_.Free(coro->id_); 137 | delete coro; 138 | sched->coros_[id] = NULL; 139 | --sched->num_; 140 | sched->running_ = -1; 141 | } 142 | 143 | void 144 | Scheduler::Resume(int id) { 145 | Coroutine *coro = coros_[id]; 146 | 147 | if (coro == NULL) { 148 | return; 149 | } 150 | int status = coro->status_; 151 | switch(status) { 152 | case kStatusReady: 153 | getcontext(&coro->ctx_); 154 | coro->ctx_.uc_stack.ss_sp = coro->stack_; 155 | coro->ctx_.uc_stack.ss_size = kDefaulaStackSize; 156 | coro->ctx_.uc_link = &main_; 157 | running_ = id; 158 | coro->status_ = kStatusRunning; 159 | makecontext(&coro->ctx_, (void (*)(void))mainfunc, 1, this); 160 | swapcontext(&main_, &coro->ctx_); 161 | break; 162 | case kStatusSuspend: 163 | running_ = id; 164 | coro->status_ = kStatusRunning; 165 | swapcontext(&main_, &coro->ctx_); 166 | break; 167 | default: 168 | break; 169 | } 170 | } 171 | 172 | void 173 | Scheduler::Run() { 174 | while (1) { 175 | list::iterator iter; 176 | for (iter = active_.begin(); iter != active_.end(); ++iter) { 177 | Resume((*iter)->id_); 178 | } 179 | CheckNetwork(); 180 | } 181 | } 182 | 183 | int 184 | Scheduler::Accept(int fd, struct sockaddr *addr, 185 | socklen_t *addrlen) { 186 | Coroutine *coro = coros_[running_]; 187 | 188 | epoll_event ev; 189 | memset(&ev, 0, sizeof(struct epoll_event)); 190 | Socket *sock = socks_[fd]; 191 | if (sock == NULL) { 192 | sock = new Socket(); 193 | socks_[fd] = sock; 194 | sock->fd_ = fd; 195 | sock->coro_ = coro; 196 | } 197 | ev.data.ptr = (void*)sock; 198 | ev.events = EPOLLIN; 199 | 200 | if (epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ev) != 0) { 201 | printf("epoll_ctl error:%s\n", strerror(errno)); 202 | return -1; 203 | } 204 | 205 | swap: 206 | coro->status_ = kStatusSuspend; 207 | swapcontext(&coro->ctx_, &main_); 208 | epoll_ctl(epfd_, EPOLL_CTL_DEL, fd, &ev); 209 | 210 | int s; 211 | do { 212 | s = gSysAccept(fd, addr, addrlen); 213 | if (s < 0) { 214 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 215 | goto swap; 216 | } else if (errno != EINTR) { 217 | printf("accept errno: %s\n", strerror(errno)); 218 | return -1; 219 | } else { 220 | // EINTR 221 | continue; 222 | } 223 | } 224 | fcntl(s, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); 225 | break; 226 | } while(true); 227 | 228 | return s; 229 | } 230 | 231 | void 232 | Scheduler::CheckNetwork() { 233 | epoll_event events[1024]; 234 | int nfds = epoll_wait(epfd_, events, 1024, -1); 235 | 236 | if (nfds > 0) { 237 | int i; 238 | Socket *sock; 239 | 240 | for(i = 0 ; i < nfds ; ++i) { 241 | if(events[i].events & (EPOLLIN || EPOLLOUT)) { 242 | sock = (Socket*)events[i].data.ptr; 243 | active_.push_back(sock->coro_); 244 | continue; 245 | } 246 | } 247 | } else { 248 | printf("epoll error: %s:%d\n", strerror(errno), errno); 249 | } 250 | } 251 | 252 | ssize_t 253 | Scheduler::Recv(int fd, void *buf, size_t len, int flags) { 254 | ssize_t ret; 255 | Coroutine *coro = coros_[running_]; 256 | epoll_event ev; 257 | 258 | memset(&ev, 0, sizeof(struct epoll_event)); 259 | Socket *sock = socks_[fd]; 260 | if (sock == NULL) { 261 | sock = new Socket(); 262 | socks_[fd] = sock; 263 | sock->fd_ = fd; 264 | sock->coro_ = coro; 265 | } 266 | 267 | ev.data.ptr = (void*)sock; 268 | ev.events = EPOLLIN; 269 | if (epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ev) != 0) { 270 | printf("recv add epoll error: %s\n", strerror(errno)); 271 | return -1; 272 | } 273 | 274 | ret = 0; 275 | 276 | swap: 277 | coro->status_ = kStatusSuspend; 278 | swapcontext(&coro->ctx_, &main_); 279 | epoll_ctl(epfd_, EPOLL_CTL_DEL, fd, &ev); 280 | 281 | while (ret < (ssize_t)len) { 282 | ssize_t nbytes = gSysRecv(fd, (char*)buf + ret, len - ret, flags); 283 | if (nbytes == -1) { 284 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 285 | goto swap; 286 | } else if (errno != EINTR) { 287 | return -1; 288 | } else { 289 | // EINTR 290 | continue; 291 | } 292 | } 293 | 294 | if (nbytes == 0) { 295 | return -1; 296 | } 297 | 298 | ret += nbytes; 299 | if (nbytes < (ssize_t)len - ret) { 300 | break; 301 | } 302 | } 303 | 304 | return ret; 305 | } 306 | 307 | ssize_t 308 | Scheduler::Send(int fd, const void *buf, size_t len, int flags) { 309 | Coroutine *coro = coros_[running_]; 310 | ssize_t ret; 311 | 312 | epoll_event ev; 313 | memset(&ev, 0, sizeof(struct epoll_event)); 314 | Socket *sock = socks_[fd]; 315 | if (sock == NULL) { 316 | sock = new Socket(); 317 | socks_[fd] = sock; 318 | sock->fd_ = fd; 319 | sock->coro_ = coro; 320 | } 321 | 322 | ev.data.ptr = (void*)sock; 323 | ev.events = EPOLLOUT; 324 | if (epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ev) != 0) { 325 | return -1; 326 | } 327 | ret = 0; 328 | swap: 329 | coro->status_ = kStatusSuspend; 330 | swapcontext(&coro->ctx_, &main_); 331 | epoll_ctl(epfd_, EPOLL_CTL_DEL, fd, &ev); 332 | 333 | while (ret < (ssize_t)len) { 334 | ssize_t nbytes = gSysSend(fd, (char*)buf + ret, len - ret, flags | MSG_NOSIGNAL); 335 | if (nbytes == -1) { 336 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 337 | goto swap; 338 | } else if (errno != EINTR) { 339 | return -1; 340 | } else { 341 | // EINTR 342 | continue; 343 | } 344 | } 345 | 346 | if (nbytes == 0) { 347 | return -1; 348 | } 349 | 350 | ret += nbytes; 351 | if (ret == (ssize_t)len) { 352 | break; 353 | } 354 | } 355 | 356 | return ret; 357 | } 358 | 359 | int 360 | Scheduler::Close(int fd) { 361 | if (socks_[fd]) { 362 | delete socks_[fd]; 363 | socks_[fd] = NULL; 364 | } 365 | return gSysClose(fd); 366 | } 367 | -------------------------------------------------------------------------------- /oldsrc/coroutine.h: -------------------------------------------------------------------------------- 1 | #ifndef __COROUTINE_H__ 2 | #define __COROUTINE_H__ 3 | 4 | #include 5 | #include 6 | #include //inet_addr 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "idmap.h" 13 | 14 | using namespace std; 15 | 16 | typedef void* (*cfunc)(void*); 17 | 18 | struct Coroutine; 19 | struct Socket; 20 | void mainfunc(void *ptr); 21 | 22 | class Scheduler { 23 | friend void mainfunc(void *ptr); 24 | public: 25 | Scheduler(); 26 | ~Scheduler(); 27 | 28 | int Spawn(void *arg, cfunc fun); 29 | void Yield(); 30 | void Resume(int id); 31 | int Status(int id); 32 | 33 | void Run(); 34 | 35 | int Accept(int sockfd, struct sockaddr *addr, 36 | socklen_t *addrlen); 37 | ssize_t Recv(int fd, void *buf, size_t len, int flags); 38 | ssize_t Send(int fd, const void *buf, size_t len, int flags); 39 | int Close(int fd); 40 | private: 41 | void CheckNetwork(); 42 | 43 | private: 44 | int epfd_; 45 | int running_; 46 | size_t num_; 47 | list active_; 48 | vector coros_; 49 | vector socks_; 50 | ucontext_t main_; 51 | IdMap id_map_; 52 | }; 53 | 54 | extern Scheduler gSched; 55 | 56 | #endif /* __COROUTINE_H__ */ 57 | -------------------------------------------------------------------------------- /oldsrc/hooks.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "coroutine.h" 4 | 5 | extern Scheduler gSched; 6 | 7 | extern "C" { 8 | #undef pthread_create 9 | int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 10 | void *(*start_routine) (void *), void *arg) { 11 | *thread = gSched.Spawn(arg, start_routine); 12 | 13 | return 0; 14 | } 15 | 16 | #undef accept 17 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { 18 | return gSched.Accept(sockfd, addr, addrlen); 19 | } 20 | 21 | #undef recv 22 | ssize_t recv(int fd, void *buf, size_t len, int flags) { 23 | return gSched.Recv(fd, buf, len, flags); 24 | } 25 | 26 | #undef send 27 | ssize_t send(int fd, const void *buf, size_t len, int flags) { 28 | return gSched.Send(fd, buf, len, flags); 29 | } 30 | 31 | #undef close 32 | int close(int fd) { 33 | return gSched.Close(fd); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /oldsrc/idmap.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "idmap.h" 3 | 4 | /* page size = 2^12 = 4K */ 5 | #define PAGE_SHIFT 12 6 | #define PAGE_SIZE (1UL << PAGE_SHIFT) 7 | 8 | #define BITS_PER_BYTE 8 9 | #define BITS_PER_PAGE (PAGE_SIZE * BITS_PER_BYTE) 10 | #define BITS_PER_PAGE_MASK (BITS_PER_PAGE - 1) 11 | 12 | IdMap::IdMap() 13 | : nr_free_(ID_MAX_DEFAULT), 14 | last_id_(-1) { 15 | memset(page_, 0, ID_MAX_DEFAULT); 16 | } 17 | 18 | IdMap::~IdMap() { 19 | } 20 | 21 | static int 22 | test_and_set_bit(int offset, void *addr) { 23 | unsigned long mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1)); 24 | unsigned long *p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1)); 25 | unsigned long old = *p; 26 | 27 | *p = old | mask; 28 | 29 | return (old & mask) != 0; 30 | } 31 | 32 | static void 33 | clear_bit(int offset, void *addr) { 34 | unsigned long mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1)); 35 | unsigned long *p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1)); 36 | unsigned long old = *p; 37 | 38 | *p = old & ~mask; 39 | } 40 | 41 | static int 42 | find_next_zero_bit(void *addr, int size, int offset) { 43 | unsigned long *p; 44 | unsigned long mask; 45 | 46 | while (offset < size) { 47 | p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1)); 48 | mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1)); 49 | 50 | if ((~(*p) & mask)) { 51 | break; 52 | } 53 | 54 | ++offset; 55 | } 56 | 57 | return offset; 58 | } 59 | 60 | int 61 | IdMap::Allocate() { 62 | int id = last_id_ + 1; 63 | int offset = id & BITS_PER_PAGE_MASK; 64 | 65 | if (!nr_free_) { 66 | return -1; 67 | } 68 | 69 | offset = find_next_zero_bit(&page_, BITS_PER_PAGE, offset); 70 | if (BITS_PER_PAGE != offset && 71 | !test_and_set_bit(offset, &page_)) { 72 | --nr_free_; 73 | last_id_ = offset; 74 | return offset; 75 | } 76 | 77 | return -1; 78 | } 79 | 80 | void 81 | IdMap::Free(int id) { 82 | int offset = id & BITS_PER_PAGE_MASK; 83 | 84 | nr_free_++; 85 | clear_bit(offset, &page_); 86 | } 87 | -------------------------------------------------------------------------------- /oldsrc/idmap.h: -------------------------------------------------------------------------------- 1 | #ifndef __ID_MAP_H__ 2 | #define __ID_MAP_H__ 3 | 4 | /* max pid, equal to 2^15=32768 */ 5 | #define ID_MAX_DEFAULT 0x8000 6 | 7 | class IdMap { 8 | public: 9 | IdMap(); 10 | ~IdMap(); 11 | 12 | int Allocate(); 13 | void Free(int id); 14 | 15 | private: 16 | int nr_free_; 17 | int last_id_; 18 | char page_[ID_MAX_DEFAULT]; 19 | }; 20 | 21 | #endif /* __ID_MAP_H__ */ 22 | -------------------------------------------------------------------------------- /src/assert.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASSERT_H__ 2 | #define __ASSERT_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | #define ASSERT(cond) assert(cond) 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | #endif // __ASSERT_H__ 16 | -------------------------------------------------------------------------------- /src/context.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "context.h" 3 | 4 | #define ESP 0 5 | #define EIP 1 6 | #define EAX 2 7 | #define ECX 3 8 | // ----------- 9 | #define RSP 0 10 | #define RIP 1 11 | #define RBX 2 12 | #define RDI 3 13 | #define RSI 4 14 | 15 | #define RBP 5 16 | #define R12 6 17 | #define R13 7 18 | #define R14 8 19 | #define R15 9 20 | #define RDX 10 21 | #define RCX 11 22 | #define R8 12 23 | #define R9 13 24 | 25 | //----- -------- 26 | // 32 bit 27 | // | regs[0]: ret | 28 | // | regs[1]: ebx | 29 | // | regs[2]: ecx | 30 | // | regs[3]: edx | 31 | // | regs[4]: edi | 32 | // | regs[5]: esi | 33 | // | regs[6]: ebp | 34 | // | regs[7]: eax | = esp 35 | enum 36 | { 37 | kEIP = 0, 38 | kESP = 7, 39 | }; 40 | 41 | //------------- 42 | // 64 bit 43 | //low | regs[0]: r15 | 44 | // | regs[1]: r14 | 45 | // | regs[2]: r13 | 46 | // | regs[3]: r12 | 47 | // | regs[4]: r9 | 48 | // | regs[5]: r8 | 49 | // | regs[6]: rbp | 50 | // | regs[7]: rdi | 51 | // | regs[8]: rsi | 52 | // | regs[9]: ret | //ret func addr 53 | // | regs[10]: rdx | 54 | // | regs[11]: rcx | 55 | // | regs[12]: rbx | 56 | //hig | regs[13]: rsp | 57 | enum 58 | { 59 | kRDI = 7, 60 | kRSI = 8, 61 | kRETAddr = 9, 62 | kRSP = 13, 63 | }; 64 | 65 | typedef struct param_t { 66 | const void *s1; 67 | } param_t; 68 | 69 | //64 bit 70 | /* 71 | extern "C" 72 | { 73 | extern void context_swap( context_t *,context_t* ) asm("context_swap"); 74 | }; 75 | */ 76 | 77 | int context_init(context_t *context) { 78 | memset(context, 0, sizeof(context_t)); 79 | return 0; 80 | } 81 | 82 | #ifdef USE_UCONTEXT 83 | void ucontext_swap(context_t* curr, context_t* pending) { 84 | swapcontext(&curr->ut, &pending->ut); 85 | } 86 | 87 | int context_make(context_t *ctx, context_fun_t fun, const void *s) { 88 | getcontext(&ctx->ut); 89 | ctx->ut.uc_stack.ss_sp = ctx->sp; 90 | ctx->ut.uc_stack.ss_size = ctx->size; 91 | ctx->ut.uc_link = NULL; 92 | 93 | makecontext(&ctx->ut, (void(*)(void))fun, 1); 94 | 95 | return 0; 96 | } 97 | #elif defined(__i386__) 98 | int context_make(context_t *ctx, context_fun_t fun, const void *s) { 99 | //make room for coctx_param 100 | char *sp = ctx->sp + ctx->size - sizeof(param_t); 101 | sp = (char*)((unsigned long)sp & -16L); 102 | 103 | param_t* param = (param_t*)sp ; 104 | param->s1 = s; 105 | 106 | memset(ctx->regs, 0, sizeof(ctx->regs)); 107 | 108 | ctx->regs[kESP] = (char*)(sp) - sizeof(void*); 109 | ctx->regs[kEIP] = (char*)fun; 110 | 111 | return 0; 112 | } 113 | 114 | #elif defined(__x86_64__) 115 | int context_make(context_t *ctx, context_fun_t fun, const void *s) { 116 | char *sp = ctx->sp + ctx->size; 117 | sp = (char*) ((unsigned long)sp & -16LL); 118 | 119 | memset(ctx->regs, 0, sizeof(ctx->regs)); 120 | 121 | ctx->regs[kRSP] = sp - 8; 122 | 123 | ctx->regs[kRETAddr] = (char*)fun; 124 | 125 | ctx->regs[kRDI] = (char*)s; 126 | return 0; 127 | } 128 | #endif 129 | -------------------------------------------------------------------------------- /src/context.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONTEXT_H__ 2 | #define __CONTEXT_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "coroutine.h" 9 | #include "typedef.h" 10 | #ifdef USE_UCONTEXT 11 | #include 12 | #endif 13 | 14 | struct context_t { 15 | #ifdef USE_UCONTEXT 16 | ucontext_t ut; 17 | #elif defined(__i386__) 18 | void *regs[ 8 ]; 19 | #else 20 | void *regs[ 14 ]; 21 | #endif 22 | size_t size; 23 | char *sp; 24 | }; 25 | 26 | typedef void (*context_fun_t)(void* s); 27 | 28 | int context_init(context_t *context); 29 | int context_make(context_t *ctx, context_fun_t fun, const void *s); 30 | 31 | #ifdef USE_UCONTEXT 32 | void ucontext_swap(context_t* curr, context_t* pending); 33 | #else 34 | void context_swap(context_t* curr, context_t* pending); 35 | #endif 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | 41 | #endif // __CONTEXT_H__ 42 | -------------------------------------------------------------------------------- /src/context_swap.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Libco available. 3 | 4 | * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | .globl context_swap 20 | #if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) 21 | .type context_swap, @function 22 | #endif 23 | context_swap: 24 | 25 | #if defined(__i386__) 26 | leal 4(%esp), %eax //sp 27 | movl 4(%esp), %esp 28 | leal 32(%esp), %esp //parm a : ®s[7] + sizeof(void*) 29 | 30 | pushl %eax //esp ->parm a 31 | 32 | pushl %ebp 33 | pushl %esi 34 | pushl %edi 35 | pushl %edx 36 | pushl %ecx 37 | pushl %ebx 38 | pushl -4(%eax) 39 | 40 | 41 | movl 4(%eax), %esp //parm b -> ®s[0] 42 | 43 | popl %eax //ret func addr 44 | popl %ebx 45 | popl %ecx 46 | popl %edx 47 | popl %edi 48 | popl %esi 49 | popl %ebp 50 | popl %esp 51 | pushl %eax //set ret func addr 52 | 53 | xorl %eax, %eax 54 | ret 55 | 56 | #elif defined(__x86_64__) 57 | leaq 8(%rsp),%rax 58 | leaq 112(%rdi),%rsp 59 | pushq %rax 60 | pushq %rbx 61 | pushq %rcx 62 | pushq %rdx 63 | 64 | pushq -8(%rax) //ret func addr 65 | 66 | pushq %rsi 67 | pushq %rdi 68 | pushq %rbp 69 | pushq %r8 70 | pushq %r9 71 | pushq %r12 72 | pushq %r13 73 | pushq %r14 74 | pushq %r15 75 | 76 | movq %rsi, %rsp 77 | popq %r15 78 | popq %r14 79 | popq %r13 80 | popq %r12 81 | popq %r9 82 | popq %r8 83 | popq %rbp 84 | popq %rdi 85 | popq %rsi 86 | popq %rax //ret func addr 87 | popq %rdx 88 | popq %rcx 89 | popq %rbx 90 | popq %rsp 91 | pushq %rax 92 | 93 | xorl %eax, %eax 94 | ret 95 | #endif 96 | -------------------------------------------------------------------------------- /src/coroutine.h: -------------------------------------------------------------------------------- 1 | #ifndef __COROUTINE_H__ 2 | #define __COROUTINE_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | typedef void* (*coroutine_fun_t)(void *); 9 | 10 | struct coroutine_t; 11 | typedef struct coroutine_t coroutine_t; 12 | 13 | typedef struct coroutine_options_t { 14 | int stack_size; 15 | 16 | int task_per_thread; 17 | } coroutine_options_t; 18 | 19 | void coroutine_init_env(const coroutine_options_t *options); 20 | 21 | typedef struct coroutine_task_attr_t { 22 | char enable_sys_hook; 23 | 24 | // -1 means no timeout 25 | int max_timeout_ms; 26 | 27 | void *arg; 28 | 29 | coroutine_fun_t fun; 30 | 31 | // coroutine timeout callback 32 | // if max_timeout_ms == -1, then ignored 33 | coroutine_fun_t timeout; 34 | } coroutine_task_attr_t; 35 | 36 | // return 0 success, return -1 fail 37 | int coroutine_new_task(coroutine_task_attr_t *attr); 38 | 39 | void* coroutine_arg(); 40 | 41 | void* coroutine_thread_arg(); 42 | 43 | void coroutine_init_eventloop(void *arg); 44 | void coroutine_eventloop(); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | #endif // __COROUTINE_H__ 50 | -------------------------------------------------------------------------------- /src/coroutine_impl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "assert.h" 11 | #include "coroutine.h" 12 | #include "coroutine_impl.h" 13 | #include "coroutine_specific.h" 14 | #include "context.h" 15 | #include "misc.h" 16 | 17 | static const int kMaxCallStack = 128; 18 | static const int kMinStackSize = 128 * 1024; 19 | static const int kMaxStackSize = 8 * 1024 * 1024; 20 | // attention: kMaxTimeoutMs MUST NOT bigger than epoll timer size 21 | static const int kMaxTimeoutMs = 10; 22 | static const int kDefaultIoTimeoutMs = 1000; 23 | static const int kDefaultTaskPerThread = 1000; 24 | 25 | coroutine_options_t gOptions; 26 | 27 | static env_t* gEnv[204800] = { NULL }; 28 | 29 | static void yield_env(env_t *env); 30 | 31 | static inline pid_t get_pid() { 32 | char **p = (char**)pthread_self(); 33 | return p ? *(pid_t*)(p + 18) : getpid(); 34 | } 35 | 36 | env_t* get_curr_thread_env() { 37 | return gEnv[get_pid()]; 38 | } 39 | 40 | static inline coroutine_stack_t* alloc_stack(int size) { 41 | coroutine_stack_t *stack = (coroutine_stack_t*)malloc(sizeof(coroutine_stack_t)); 42 | stack->size = size; 43 | stack->start = (char*)malloc(sizeof(char) * size); 44 | stack->end = stack->start + size; 45 | 46 | return stack; 47 | } 48 | 49 | static coroutine_t* create_env(env_t *env, coroutine_fun_t func, void *arg) { 50 | int stack_size = gOptions.stack_size; 51 | 52 | coroutine_t *co = (coroutine_t*)malloc(sizeof(coroutine_t)); 53 | memset(co, 0, sizeof(coroutine_t)); 54 | co->env = env; 55 | co->fun = func; 56 | co->arg = arg; 57 | co->state = INIT; 58 | 59 | coroutine_stack_t *stack = alloc_stack(stack_size); 60 | co->stack = stack; 61 | co->context.sp = stack->start; 62 | co->context.size = stack_size; 63 | 64 | co->save_size = 0; 65 | co->save_buffer = NULL; 66 | 67 | return co; 68 | } 69 | 70 | static void coroutine_main(void *arg) { 71 | coroutine_t *co = (coroutine_t*)arg; 72 | if (co->fun) { 73 | co->fun(co->arg); 74 | } 75 | co->state = STOPPED; 76 | 77 | env_t *env = co->env; 78 | //coroutine_free(co); 79 | 80 | yield_env(env); 81 | } 82 | 83 | env_t* init_curr_thread_env() { 84 | pid_t pid = 0; 85 | env_t *env = NULL; 86 | 87 | pid = get_pid(); 88 | env = (env_t*)calloc(1, sizeof(env_t)); 89 | env->callstack = (coroutine_t**)calloc(kMaxCallStack, sizeof(coroutine_t*)); 90 | env->callstacksize = 0; 91 | gEnv[pid] = env; 92 | 93 | coroutine_t *co = create_env(env, NULL, NULL); 94 | #ifdef USE_UCONTEXT 95 | context_make(&co->context, coroutine_main, co); 96 | #endif 97 | co->main = 1; 98 | 99 | context_init(&co->context); 100 | env->callstack[env->callstacksize++] = co; 101 | env->epoll = alloc_epoll(10240); 102 | 103 | return env; 104 | } 105 | 106 | void do_init_curr_thread_env() { 107 | env_t *env = init_curr_thread_env(); 108 | env->pool = create_thread_taskpool(env, gOptions.task_per_thread); 109 | } 110 | 111 | coroutine_t* coroutine_new(coroutine_fun_t fun, void *arg) { 112 | env_t *env = get_curr_thread_env(); 113 | if (env == NULL) { 114 | do_init_curr_thread_env(); 115 | env = get_curr_thread_env(); 116 | } 117 | 118 | return create_env(env, fun, arg); 119 | } 120 | 121 | int coroutine_new_task(coroutine_task_attr_t *attr) { 122 | env_t *env = get_curr_thread_env(); 123 | if (env == NULL) { 124 | do_init_curr_thread_env(); 125 | env = get_curr_thread_env(); 126 | } 127 | 128 | return new_task(env->pool, attr); 129 | } 130 | 131 | void* coroutine_arg() { 132 | coroutine_t *co = coroutine_self(); 133 | if (co && co->task) { 134 | return co->task->arg; 135 | } 136 | 137 | return NULL; 138 | } 139 | 140 | void* coroutine_thread_arg() { 141 | env_t *env = get_curr_thread_env(); 142 | if (env == NULL) { 143 | return NULL; 144 | } 145 | 146 | return env->arg; 147 | } 148 | 149 | void coroutine_free(coroutine_t *co) { 150 | free(co); 151 | } 152 | 153 | void coroutine_swap(coroutine_t* curr, coroutine_t* pending) { 154 | //swap context 155 | #ifdef USE_UCONTEXT 156 | ucontext_swap(&(curr->context), &(pending->context)); 157 | #else 158 | env_t* env = get_curr_thread_env(); 159 | 160 | //get curr stack sp 161 | char c = 0; 162 | curr->stack_sp = &c; 163 | 164 | env->pending = NULL; 165 | env->occupy = NULL; 166 | context_swap(&(curr->context), &(pending->context)); 167 | //stack buffer may be overwrite, so get again; 168 | env_t* curr_env = get_curr_thread_env(); 169 | coroutine_t* update_occupy = curr_env->occupy; 170 | coroutine_t* update_pending = curr_env->pending; 171 | 172 | if (update_occupy && update_pending && update_occupy != update_pending) { 173 | //resume stack buffer 174 | if (update_pending->save_buffer && update_pending->save_size > 0) { 175 | memcpy(update_pending->stack_sp, update_pending->save_buffer, update_pending->save_size); 176 | } 177 | } 178 | #endif 179 | } 180 | 181 | static void yield_env(env_t *env) { 182 | int size = env->callstacksize; 183 | coroutine_t *last = env->callstack[size - 2]; 184 | coroutine_t *curr = env->callstack[size - 1]; 185 | env->callstacksize--; 186 | 187 | coroutine_swap(curr, last); 188 | } 189 | 190 | void coroutine_resume(coroutine_t *co) { 191 | env_t *env = co->env; 192 | coroutine_t *curr = env->callstack[env->callstacksize - 1]; 193 | if (co->state != RUNNING) { 194 | context_make(&co->context, coroutine_main, co); 195 | co->state = RUNNING; 196 | } 197 | 198 | env->callstack[env->callstacksize++] = co; 199 | coroutine_swap(curr, co); 200 | } 201 | 202 | void coroutine_yield(coroutine_t *co) { 203 | yield_env(co->env); 204 | } 205 | 206 | void coroutine_yield_context() { 207 | yield_env(get_curr_thread_env()); 208 | } 209 | 210 | static coroutine_t *curr_coroutine(env_t *env) { 211 | return env->callstack[env->callstacksize - 1]; 212 | } 213 | 214 | coroutine_t* coroutine_self() { 215 | env_t *env = get_curr_thread_env(); 216 | if (env == NULL) { 217 | return NULL; 218 | } 219 | 220 | return curr_coroutine(env); 221 | } 222 | 223 | coroutine_t *get_curr_coroutine(env_t *env) { 224 | return env->callstack[env->callstacksize - 1]; 225 | } 226 | 227 | struct poll_context_t; 228 | 229 | typedef struct poll_item_t { 230 | struct pollfd *self; 231 | struct poll_context_t *poll; 232 | timer_item_t time; 233 | 234 | struct epoll_event event; 235 | } poll_item_t; 236 | 237 | typedef struct poll_context_t { 238 | struct pollfd *fds; 239 | nfds_t nfds; // typedef unsigned long int nfds_t; 240 | 241 | timer_item_t time; 242 | poll_item_t *items; 243 | 244 | char allEventDetatch; 245 | 246 | int raise_cnt; 247 | } poll_context_t; 248 | 249 | /* 250 | * EPOLLPRI POLLPRI // There is urgent data to read. 251 | * EPOLLMSG POLLMSG 252 | * 253 | * POLLREMOVE 254 | * POLLRDHUP 255 | * POLLNVAL 256 | * 257 | * */ 258 | static inline uint32_t poll_event_to_epoll(short events) { 259 | uint32_t e = 0; 260 | if( events & POLLIN ) e |= EPOLLIN; 261 | if( events & POLLOUT ) e |= EPOLLOUT; 262 | if( events & POLLHUP ) e |= EPOLLHUP; 263 | if( events & POLLERR ) e |= EPOLLERR; 264 | if( events & POLLIN ) e |= EPOLLIN; 265 | if( events & POLLOUT ) e |= EPOLLOUT; 266 | return e; 267 | } 268 | 269 | static inline short epoll_event_to_poll(uint32_t events) { 270 | short e = 0; 271 | if( events & EPOLLIN ) e |= POLLIN; 272 | if( events & EPOLLOUT ) e |= POLLOUT; 273 | if( events & EPOLLHUP ) e |= POLLHUP; 274 | if( events & EPOLLERR ) e |= POLLERR; 275 | if( events & EPOLLIN ) e |= POLLIN; 276 | if( events & EPOLLOUT ) e |= POLLOUT; 277 | return e; 278 | } 279 | 280 | static void process_poll_event(timer_item_t *item) { 281 | coroutine_resume(item->coroutine); 282 | } 283 | 284 | static void prepare_poll_event(timer_item_t *item,struct epoll_event *ev,timer_list_t *active) { 285 | poll_item_t *poll_item = (poll_item_t*)item->arg; 286 | poll_item->self->revents = epoll_event_to_poll(ev->events); 287 | 288 | poll_context_t *poll = poll_item->poll; 289 | poll->raise_cnt++; 290 | 291 | if (!poll->allEventDetatch) { 292 | poll->allEventDetatch = 1; 293 | remove_from_link(&poll->time); 294 | add_tail(active, &poll->time); 295 | } 296 | } 297 | 298 | unsigned long long get_now() { 299 | env_t *env = get_curr_thread_env(); 300 | return get_epoll_now(env->epoll); 301 | } 302 | 303 | // return: 304 | // > 0: timeout ms 305 | // < 0: no timeout limit 306 | // = 0: already timeout 307 | static int fix_coroutine_timeout(coroutine_t *co, int timeout, char *istimeout) { 308 | task_t *task = co->task; 309 | *istimeout = 0; 310 | 311 | // attr no timeout limit 312 | if (task->attr.max_timeout_ms == -1) { 313 | if (timeout == -1 || timeout > kMaxTimeoutMs) { 314 | timeout = kMaxTimeoutMs; 315 | } 316 | return timeout; 317 | } 318 | 319 | if (task->timeout) { 320 | // already timeout 321 | *istimeout = 1; 322 | return 0; 323 | } 324 | unsigned long long now = get_now(); 325 | int diff = now - task->last; 326 | task->leftmsec -= diff; 327 | task->last = now; 328 | if (task->leftmsec <= 0) { 329 | task->timeout = 1; 330 | *istimeout = 1; 331 | return 0; 332 | } 333 | 334 | if (timeout == -1) { 335 | timeout = task->leftmsec; 336 | } else { 337 | timeout = timeout > task->leftmsec ? task->leftmsec : timeout; 338 | } 339 | 340 | if (timeout == -1 || timeout > kMaxTimeoutMs) { 341 | timeout = kMaxTimeoutMs; 342 | } 343 | return timeout; 344 | } 345 | 346 | 347 | int poll_inner(epoll_context_t *ctx, struct pollfd fds[], nfds_t nfds, int timeout, poll_fun_t pollfunc) { 348 | coroutine_t* self = coroutine_self(); 349 | char istimeout; 350 | timeout = fix_coroutine_timeout(self, timeout, &istimeout); 351 | if (istimeout == 1) { 352 | // timeout 353 | return -1; 354 | } 355 | 356 | if(timeout == -1 || timeout > kMaxTimeoutMs) { 357 | timeout = kMaxTimeoutMs; 358 | } 359 | int epfd = ctx->fd; 360 | 361 | //1.struct change 362 | poll_context_t arg; 363 | memset(&arg,0,sizeof(arg)); 364 | 365 | arg.fds = (struct pollfd*)calloc(nfds, sizeof(struct pollfd)); 366 | arg.nfds = nfds; 367 | 368 | poll_item_t items[2]; 369 | if( nfds < sizeof(items) / sizeof(items[0])) { 370 | arg.items = items; 371 | } else { 372 | arg.items = (poll_item_t*)malloc(nfds * sizeof(poll_item_t)); 373 | } 374 | memset(arg.items, 0, nfds * sizeof(poll_item_t)); 375 | 376 | arg.time.process = process_poll_event; 377 | arg.time.coroutine = self; 378 | arg.time.prev = arg.time.next = NULL; 379 | //arg.time.arg = &arg; 380 | 381 | //2. add epoll 382 | for(nfds_t i = 0; i < nfds; i++) { 383 | arg.items[i].self = arg.fds + i; 384 | arg.items[i].poll = &arg; 385 | 386 | arg.items[i].time.prepare = prepare_poll_event; 387 | arg.items[i].time.coroutine = self; 388 | arg.items[i].time.arg = &(arg.items[i]); 389 | arg.items[i].time.prev = arg.items[i].time.next = NULL; 390 | 391 | if(fds[i].fd > -1) { 392 | struct epoll_event *ev = &(arg.items[i].event); 393 | ev->data.ptr = &(arg.items[i].time); 394 | ev->events = poll_event_to_epoll(fds[i].events); 395 | 396 | int ret = do_epoll_ctl(epfd,EPOLL_CTL_ADD, fds[i].fd, ev); 397 | if (ret < 0 && errno == EPERM && nfds == 1 && pollfunc != NULL) { 398 | if(arg.items != items) { 399 | free(arg.items); 400 | arg.items = NULL; 401 | } 402 | free(arg.fds); 403 | return pollfunc(fds, nfds, timeout); 404 | } 405 | } 406 | //if fail,the timeout would work 407 | } 408 | 409 | //3.add timeout 410 | unsigned long long now = GetTickMS(); 411 | arg.time.expire = now + timeout; 412 | int ret = add_timeout(ctx->timer, &arg.time, now); 413 | if(ret != 0) { 414 | errno = EINVAL; 415 | 416 | if(arg.items != items) { 417 | free(arg.items); 418 | arg.items = NULL; 419 | } 420 | free(arg.fds); 421 | 422 | for(nfds_t i = 0; i < nfds; i++) { 423 | int fd = fds[i].fd; 424 | if(fd > -1) { 425 | do_epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&arg.items[i].event); 426 | } 427 | } 428 | return -1; 429 | } 430 | 431 | yield_env(get_curr_thread_env()); 432 | 433 | remove_from_link(&arg.time); 434 | 435 | for(nfds_t i = 0; i < nfds; i++) { 436 | int fd = fds[i].fd; 437 | if(fd > -1) { 438 | do_epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&arg.items[i].event); 439 | } 440 | fds[i].revents = arg.fds[i].revents; 441 | } 442 | 443 | int raise_cnt = arg.raise_cnt; 444 | if(arg.items != items) { 445 | free(arg.items); 446 | arg.items = NULL; 447 | } 448 | free(arg.fds); 449 | 450 | return raise_cnt; 451 | } 452 | 453 | epoll_context_t *get_epoll_context() { 454 | env_t *env = get_curr_thread_env(); 455 | 456 | if (env == NULL) { 457 | do_init_curr_thread_env(); 458 | env = get_curr_thread_env(); 459 | } 460 | 461 | return env->epoll; 462 | } 463 | 464 | coroutine_t *get_curr_thread_coroutine( ) { 465 | env_t *env = get_curr_thread_env(); 466 | if(!env) { 467 | return 0; 468 | } 469 | return get_curr_coroutine(env); 470 | } 471 | 472 | void *coroutine_getspecific(pthread_key_t key) { 473 | coroutine_t *co = get_curr_thread_coroutine(); 474 | if(!co || co->main) { 475 | return pthread_getspecific(key); 476 | } 477 | return co->spec[key].value; 478 | } 479 | 480 | int coroutine_setspecific(pthread_key_t key, const void *value) { 481 | coroutine_t *co = get_curr_thread_coroutine(); 482 | if(!co || co->main) { 483 | return pthread_setspecific(key,value); 484 | } 485 | co->spec[key].value = (void*)value; 486 | return 0; 487 | } 488 | 489 | int coroutine_poll(epoll_context_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms) { 490 | return poll_inner(ctx, fds, nfds, timeout_ms, NULL); 491 | } 492 | 493 | 494 | void coroutine_init_env(const coroutine_options_t *options) { 495 | coroutine_pthread_key_init(); 496 | 497 | memcpy(&gOptions, options, sizeof(coroutine_options_t)); 498 | int stack_size = gOptions.stack_size; 499 | 500 | if (stack_size < kMinStackSize) { 501 | stack_size = kMinStackSize; 502 | } else if (stack_size > kMaxStackSize) { 503 | stack_size = kMaxStackSize; 504 | } 505 | 506 | if (stack_size & 0XFFF) { 507 | stack_size &= ~0XFFF; 508 | stack_size += 0x1000; 509 | } 510 | gOptions.stack_size = stack_size; 511 | 512 | if (gOptions.task_per_thread == 0) { 513 | gOptions.task_per_thread = kDefaultTaskPerThread; 514 | } 515 | 516 | signal(SIGPIPE, SIG_IGN); 517 | } 518 | -------------------------------------------------------------------------------- /src/coroutine_impl.h: -------------------------------------------------------------------------------- 1 | #ifndef __COROUTINE_IMPL_H__ 2 | #define __COROUTINE_IMPL_H__ 3 | 4 | #include 5 | #include "typedef.h" 6 | #include "coroutine.h" 7 | #include "coroutine_task.h" 8 | #include "context.h" 9 | #include "epoll.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | struct env_t { 16 | coroutine_t **callstack; 17 | int callstacksize; 18 | coroutine_t *main; 19 | epoll_context_t *epoll; 20 | 21 | taskpool_t *pool; 22 | 23 | void *arg; 24 | 25 | coroutine_t *occupy; 26 | coroutine_t *pending; 27 | }; 28 | 29 | struct coroutine_stack_t { 30 | coroutine_t *coroutine; 31 | int size; 32 | char *end; 33 | char *start; 34 | }; 35 | 36 | typedef enum state_t { 37 | INIT, 38 | RUNNING, 39 | STOPPED 40 | } state_t; 41 | 42 | struct coroutine_specific_t { 43 | void *value; 44 | }; 45 | 46 | struct coroutine_t { 47 | coroutine_fun_t fun; 48 | env_t *env; 49 | void *arg; 50 | char main; 51 | state_t state; 52 | 53 | context_t context; 54 | 55 | char *stack_sp; 56 | unsigned int save_size; 57 | char *save_buffer; 58 | 59 | coroutine_stack_t *stack; 60 | 61 | task_t *task; 62 | 63 | coroutine_specific_t spec[1024]; 64 | }; 65 | 66 | typedef int (*poll_fun_t)(struct pollfd fds[], nfds_t nfds, int timeout); 67 | int poll_inner(epoll_context_t *ctx, struct pollfd fds[], nfds_t nfds, int timeout, poll_fun_t pollfunc); 68 | 69 | int coroutine_poll(epoll_context_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms); 70 | epoll_context_t *get_epoll_context(); 71 | 72 | env_t* get_curr_thread_env(); 73 | void do_init_curr_thread_env(); 74 | 75 | coroutine_t* coroutine_new(coroutine_fun_t fun, void *arg); 76 | void coroutine_resume(coroutine_t *co); 77 | 78 | void coroutine_yield_context(); 79 | coroutine_t* coroutine_self(); 80 | unsigned long long get_now(); 81 | 82 | #ifdef __cplusplus 83 | } 84 | #endif 85 | 86 | #endif // __COROUTINE_IMPL_H__ 87 | -------------------------------------------------------------------------------- /src/coroutine_specific.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "coroutine_specific.h" 3 | 4 | pthread_key_t gCoroutineHostbufKey; 5 | 6 | void coroutine_pthread_key_init() { 7 | pthread_key_create(&gCoroutineHostbufKey, NULL); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/coroutine_specific.h: -------------------------------------------------------------------------------- 1 | #ifndef __COROUTINE_SPECIFIC_H__ 2 | #define __COROUTINE_SPECIFIC_H__ 3 | 4 | extern pthread_key_t gCoroutineHostbufKey; 5 | 6 | extern void *coroutine_getspecific(pthread_key_t key); 7 | extern int coroutine_setspecific(pthread_key_t key, const void *value); 8 | 9 | void coroutine_pthread_key_init(); 10 | 11 | #endif // __COROUTINE_SPECIFIC_H__ 12 | -------------------------------------------------------------------------------- /src/coroutine_task.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "assert.h" 4 | #include "coroutine_impl.h" 5 | #include "coroutine_task.h" 6 | 7 | static void *task_main(void *arg) { 8 | task_t *task = (task_t*)arg; 9 | 10 | while (1) { 11 | if (task->state == TASK_WAITING) { 12 | coroutine_yield_context(); 13 | continue; 14 | } 15 | 16 | if (task->state == TASK_END) { 17 | break; 18 | } 19 | 20 | task->pool->working++; 21 | task->coroutine->fun(task->arg); 22 | 23 | if (task->timeout && task->attr.timeout) { 24 | task->attr.timeout(task->coroutine->arg); 25 | } 26 | 27 | // return to free list 28 | task->next = task->pool->free; 29 | task->pool->free = task; 30 | task->pool->working--; 31 | 32 | // reset param 33 | task->arg = NULL; 34 | task->coroutine->fun = NULL; 35 | task->state = TASK_WAITING; 36 | } 37 | 38 | return NULL; 39 | } 40 | 41 | int new_task(taskpool_t *pool, coroutine_task_attr_t *attr) { 42 | if (pool->working == pool->size) { 43 | return -1; 44 | } 45 | 46 | task_t *task = pool->free; 47 | if (task == NULL) { 48 | return -1; 49 | } 50 | 51 | pool->free = task->next; 52 | task->next = NULL; 53 | 54 | task->arg = attr->arg; 55 | task->coroutine->fun = attr->fun; 56 | task->state = TASK_RUNNING; 57 | task->leftmsec = attr->max_timeout_ms; 58 | task->timeout = 0; 59 | task->last = get_now(); 60 | memcpy(&task->attr, attr, sizeof(coroutine_task_attr_t)); 61 | coroutine_resume(task->coroutine); 62 | 63 | return 0; 64 | } 65 | 66 | taskpool_t* create_thread_taskpool(env_t *env, int size) { 67 | taskpool_t *pool = (taskpool_t*)malloc(sizeof(taskpool_t)); 68 | memset(pool, 0, sizeof(taskpool_t)); 69 | pool->tasks = (task_t**)malloc(sizeof(task_t*)); 70 | 71 | for (int i = 0;i < size; ++i) { 72 | task_t *task = (task_t*)malloc(sizeof(task_t)); 73 | memset(task, 0, sizeof(task_t)); 74 | pool->tasks[i] = task; 75 | task->pool = pool; 76 | coroutine_t *co = coroutine_new(task_main, task); 77 | co->task = task; 78 | task->coroutine = co; 79 | task->next = pool->free; 80 | task->state = TASK_WAITING; 81 | pool->free = task; 82 | coroutine_resume(co); 83 | } 84 | pool->env = env; 85 | pool->size = size; 86 | 87 | return pool; 88 | } 89 | -------------------------------------------------------------------------------- /src/coroutine_task.h: -------------------------------------------------------------------------------- 1 | #ifndef __COROUTINE_TASK_H__ 2 | #define __COROUTINE_TASK_H__ 3 | 4 | #include "coroutine.h" 5 | #include "typedef.h" 6 | 7 | typedef enum task_state_t { 8 | TASK_RUNNING, 9 | TASK_WAITING, 10 | TASK_END, 11 | } task_state_t; 12 | 13 | struct task_t { 14 | coroutine_t *coroutine; 15 | void *arg; 16 | task_state_t state; 17 | 18 | taskpool_t *pool; 19 | 20 | // for free list 21 | task_t *next; 22 | 23 | coroutine_task_attr_t attr; 24 | char timeout; 25 | 26 | int leftmsec; 27 | unsigned long long start; 28 | unsigned long long last; 29 | }; 30 | 31 | struct taskpool_t { 32 | task_t **tasks; 33 | 34 | task_t *free; 35 | env_t *env; 36 | int size; 37 | int working; 38 | }; 39 | 40 | taskpool_t* create_thread_taskpool(env_t *env, int size); 41 | int new_task(taskpool_t *, coroutine_task_attr_t *attr); 42 | 43 | #endif // __COROUTINE_TASK_H__ 44 | -------------------------------------------------------------------------------- /src/epoll.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "coroutine_impl.h" 4 | #include "epoll.h" 5 | #include "misc.h" 6 | 7 | int do_epoll_wait(int epfd, epoll_result_t *result,int maxevents,int timeout) { 8 | return epoll_wait(epfd,result->events,maxevents,timeout); 9 | } 10 | 11 | int do_epoll_ctl(int epfd,int op,int fd,struct epoll_event *ev) { 12 | return epoll_ctl(epfd,op,fd,ev); 13 | } 14 | 15 | int do_epoll_create(int size) { 16 | return epoll_create(size); 17 | } 18 | 19 | static epoll_result_t* alloc_epoll_result(int n) { 20 | epoll_result_t* ret = (epoll_result_t*)malloc(sizeof(epoll_result_t)); 21 | 22 | ret->size = n; 23 | ret->events = (struct epoll_event*)calloc(1,n * sizeof(struct epoll_event)); 24 | 25 | return ret; 26 | } 27 | 28 | static void free_epoll_result(epoll_result_t *ret) { 29 | if(!ret) { 30 | return; 31 | } 32 | 33 | if(ret->events) { 34 | free(ret->events); 35 | } 36 | 37 | free(ret); 38 | } 39 | 40 | static epoll_timer_t* alloc_timer(int size) { 41 | epoll_timer_t *timer = (epoll_timer_t*)calloc(1, sizeof(epoll_timer_t)); 42 | timer->size = size; 43 | 44 | timer->items = (timer_list_t*)calloc(size, sizeof(timer_list_t)); 45 | int i = 0; 46 | for (i = 0; i < size; ++i) { 47 | timer->items[i].head = timer->items[i].tail = NULL; 48 | } 49 | timer->start_idx = 0; 50 | timer->start = GetTickMS(); 51 | 52 | return timer; 53 | } 54 | 55 | void free_timer(epoll_timer_t *timer) { 56 | free(timer->items); 57 | free(timer); 58 | } 59 | 60 | epoll_context_t* alloc_epoll(int size) { 61 | epoll_context_t *ep = (epoll_context_t*)calloc(1, sizeof(epoll_context_t)); 62 | 63 | ep->fd = do_epoll_create(size); 64 | ep->timer = alloc_timer(60000); 65 | ep->active_list = (timer_list_t*)calloc(1, sizeof(timer_list_t)); 66 | ep->timeout_list = (timer_list_t*)calloc(1, sizeof(timer_list_t)); 67 | ep->result = NULL; 68 | ep->size = size; 69 | 70 | return ep; 71 | } 72 | 73 | void free_epoll(epoll_context_t *epoll) { 74 | if (epoll != NULL) { 75 | free(epoll->active_list); 76 | free(epoll->timeout_list); 77 | free_epoll_result(epoll->result); 78 | free_timer(epoll->timer); 79 | 80 | free(epoll); 81 | } 82 | } 83 | 84 | static void popHead(timer_list_t *list) { 85 | if (!list->head) { 86 | return; 87 | } 88 | 89 | timer_item_t *item = list->head; 90 | if (list->head == list->tail) { 91 | list->head = list->tail = NULL; 92 | } else { 93 | list->head = list->head->next; 94 | } 95 | 96 | item->prev = item->next = NULL; 97 | item->parent = NULL; 98 | 99 | if (list->head != NULL) { 100 | list->head->prev = NULL; 101 | } 102 | } 103 | 104 | static void join(timer_list_t *list, timer_list_t *other) { 105 | if (!other->head) { 106 | return; 107 | } 108 | 109 | timer_item_t *item = other->head; 110 | while (item != NULL) { 111 | item->parent = list; 112 | item = item->next; 113 | } 114 | 115 | item = other->head; 116 | if (list->tail != NULL) { 117 | list->tail->next = item; 118 | item->prev = list->tail; 119 | list->tail = other->tail; 120 | } else { 121 | list->head = other->head; 122 | list->tail = other->tail; 123 | } 124 | 125 | other->head = other->tail = NULL; 126 | } 127 | 128 | static void takeAllTimeout(epoll_timer_t *timer, unsigned long long now, timer_list_t *result) { 129 | if (timer->start == 0) { 130 | timer->start = now; 131 | timer->start_idx = 0; 132 | } 133 | 134 | if (now < timer->start) { 135 | return; 136 | } 137 | 138 | int cnt = now - timer->start + 1; 139 | if (cnt > timer->size) { 140 | cnt = timer->size; 141 | } 142 | if (cnt < 0) { 143 | return; 144 | } 145 | 146 | int i; 147 | for (i = 0; i < cnt; i++) { 148 | int idx = (timer->start_idx + i) % timer->size; 149 | join(result, timer->items + idx); 150 | } 151 | 152 | timer->start = now; 153 | timer->start_idx += cnt - 1; 154 | } 155 | 156 | void add_tail(timer_list_t *list, timer_item_t *item) { 157 | if (item->parent != NULL) { 158 | return; 159 | } 160 | 161 | if (list->tail != NULL) { 162 | list->tail->next = item; 163 | item->next = NULL; 164 | item->prev = list->tail; 165 | list->tail = item; 166 | } else { 167 | list->head = list->tail = item; 168 | item->next = item->prev = NULL; 169 | } 170 | 171 | item->parent = list; 172 | } 173 | 174 | void remove_from_link(timer_item_t *item) { 175 | timer_list_t *list = item->parent; 176 | if (!list) { 177 | return; 178 | } 179 | 180 | if (item == list->head) { 181 | list->head = item->next; 182 | if (list->head) { 183 | list->head->prev = NULL; 184 | } 185 | } else { 186 | if (item->prev) { 187 | item->prev->next = item->next; 188 | } 189 | } 190 | 191 | if (item == list->tail) { 192 | list->tail = item->prev; 193 | if (list->tail) { 194 | list->tail->next = NULL; 195 | } 196 | } else { 197 | item->next->prev = item->prev; 198 | } 199 | 200 | item->prev = item->next = NULL; 201 | item->parent = NULL; 202 | } 203 | 204 | void coroutine_init_eventloop(void *arg) { 205 | env_t *env = get_curr_thread_env(); 206 | if (env == NULL) { 207 | do_init_curr_thread_env(); 208 | env = get_curr_thread_env(); 209 | } 210 | 211 | env->arg = arg; 212 | } 213 | 214 | void coroutine_eventloop(void *arg) { 215 | env_t *env = get_curr_thread_env(); 216 | if (env == NULL) { 217 | do_init_curr_thread_env(); 218 | env = get_curr_thread_env(); 219 | } 220 | 221 | epoll_context_t *epoll = env->epoll; 222 | epoll->now = GetTickMS(); 223 | 224 | if (epoll->result == NULL) { 225 | epoll->result = alloc_epoll_result(epoll->size); 226 | } 227 | epoll_result_t *result = epoll->result; 228 | 229 | while (1) { 230 | int i; 231 | int ret = do_epoll_wait(epoll->fd, result, epoll->size, 1); 232 | 233 | timer_list_t *active = epoll->active_list; 234 | timer_list_t *timeout = epoll->timeout_list; 235 | 236 | memset(timeout, 0, sizeof(timer_list_t)); 237 | for (i = 0; i < ret; i++) { 238 | timer_item_t *item = (timer_item_t*)result->events[i].data.ptr; 239 | if (item->prepare) { 240 | item->prepare(item, &result->events[i], active); 241 | } else { 242 | add_tail(active, item); 243 | } 244 | } 245 | 246 | unsigned long long now = GetTickMS(); 247 | epoll->now = now; 248 | takeAllTimeout(epoll->timer, now, timeout); 249 | 250 | timer_item_t *item = timeout->head; 251 | while (item != NULL) { 252 | item->timeout = 1; 253 | item = item->next; 254 | } 255 | 256 | join(active, timeout); 257 | 258 | item = active->head; 259 | while (item != NULL) { 260 | popHead(active); 261 | if (item->process != NULL) { 262 | item->process(item); 263 | } 264 | 265 | item = active->head; 266 | } 267 | } 268 | } 269 | 270 | unsigned long long get_epoll_now(epoll_context_t *epoll) { 271 | return epoll->now; 272 | } 273 | 274 | int add_timeout(epoll_timer_t *timer,timer_item_t *item, unsigned long long now) { 275 | if(timer->start == 0) { 276 | timer->start = now; 277 | timer->start_idx = 0; 278 | } 279 | if(now < timer->start) { 280 | return -1; 281 | } 282 | 283 | if(item->expire < now) { 284 | return -1; 285 | } 286 | int diff = item->expire - timer->start; 287 | 288 | if(diff >= timer->size) { 289 | return -1; 290 | } 291 | 292 | add_tail(timer->items + (timer->start_idx + diff) % timer->size, item); 293 | 294 | return 0; 295 | } 296 | -------------------------------------------------------------------------------- /src/epoll.h: -------------------------------------------------------------------------------- 1 | #ifndef __EPOLL_H__ 2 | #define __EPOLL_H__ 3 | 4 | #include 5 | #include "typedef.h" 6 | #include "coroutine.h" 7 | 8 | typedef void (*prepare_fun_t)(timer_item_t *,struct epoll_event *ev, timer_list_t *active); 9 | typedef void (*process_fun_t)(timer_item_t *); 10 | 11 | struct timer_item_t { 12 | timer_item_t *prev; 13 | timer_item_t *next; 14 | timer_list_t *parent; 15 | 16 | unsigned long long expire; 17 | prepare_fun_t prepare; 18 | process_fun_t process; 19 | 20 | coroutine_t *coroutine; 21 | void *arg; 22 | char timeout; 23 | }; 24 | 25 | struct timer_list_t { 26 | timer_item_t *head; 27 | timer_item_t *tail; 28 | }; 29 | 30 | struct epoll_timer_t { 31 | int size; 32 | timer_list_t *items; 33 | 34 | unsigned long long start; 35 | long long start_idx; 36 | }; 37 | 38 | struct epoll_result_t { 39 | int size; 40 | struct epoll_event *events; 41 | }; 42 | 43 | struct epoll_context_t { 44 | int fd; 45 | int size; 46 | 47 | unsigned long long now; 48 | 49 | epoll_timer_t *timer; 50 | timer_list_t *timeout_list; 51 | timer_list_t *active_list; 52 | 53 | epoll_result_t *result; 54 | }; 55 | 56 | epoll_context_t* alloc_epoll(int size); 57 | int do_epoll_wait(int epfd, epoll_result_t *result,int maxevents,int timeout); 58 | int do_epoll_ctl(int epfd,int op,int fd,struct epoll_event *ev); 59 | int do_epoll_create(int size); 60 | 61 | int add_timeout(epoll_timer_t *timer,timer_item_t *item, unsigned long long now); 62 | void remove_from_link(timer_item_t *item); 63 | void add_tail(timer_list_t *list, timer_item_t *item); 64 | 65 | int do_epoll_ctl(int epfd,int op,int fd,struct epoll_event *ev); 66 | unsigned long long get_epoll_now(epoll_context_t *epoll); 67 | 68 | #endif // __EPOLL_H__ 69 | -------------------------------------------------------------------------------- /src/hook_sys_call.c: -------------------------------------------------------------------------------- 1 | //#define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | //#include 25 | #include 26 | 27 | #include 28 | #include "coroutine_impl.h" 29 | #include "coroutine_task.h" 30 | #include "coroutine_specific.h" 31 | 32 | extern coroutine_options_t gOptions; 33 | 34 | #define HOOK_SYS_FUNC(name) if( !g_sys_##name ) { g_sys_##name = (name##_fun_t)dlsym(RTLD_NEXT,#name); } 35 | 36 | typedef int (*socket_fun_t)(int domain, int type, int protocol); 37 | typedef int (*accept_fun_t)(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 38 | typedef int (*connect_fun_t)(int socket, const struct sockaddr *address, socklen_t address_len); 39 | typedef int (*close_fun_t)(int fd); 40 | 41 | typedef int (*select_fun_t)(int nfds, fd_set *readfds, fd_set *writefds, 42 | fd_set *exceptfds, struct timeval *timeout); 43 | 44 | typedef ssize_t (*read_fun_t)(int fildes, void *buf, size_t nbyte); 45 | typedef ssize_t (*write_fun_t)(int fildes, const void *buf, size_t nbyte); 46 | 47 | typedef ssize_t (*sendto_fun_t)(int socket, const void *message, size_t length, 48 | int flags, const struct sockaddr *dest_addr, 49 | socklen_t dest_len); 50 | 51 | typedef ssize_t (*recvfrom_fun_t)(int socket, void *buffer, size_t length, 52 | int flags, struct sockaddr *address, 53 | socklen_t *address_len); 54 | 55 | typedef size_t (*send_fun_t)(int socket, const void *buffer, size_t length, int flags); 56 | typedef ssize_t (*recv_fun_t)(int socket, void *buffer, size_t length, int flags); 57 | 58 | typedef int (*setsockopt_fun_t)(int socket, int level, int option_name, 59 | const void *option_value, socklen_t option_len); 60 | 61 | typedef unsigned int (*sleep_fun_t)(unsigned int seconds); 62 | typedef int (*usleep_fun_t)(useconds_t usec); 63 | typedef int (*nanosleep_fun_t)(const struct timespec *req, struct timespec *rem); 64 | 65 | typedef int (*fcntl_fun_t)(int fildes, int cmd, ...); 66 | typedef struct tm *(*localtime_r_fun_t)( const time_t *timep, struct tm *result ); 67 | 68 | typedef void *(*pthread_getspecific_fun_t)(pthread_key_t key); 69 | typedef int (*pthread_setspecific_fun_t)(pthread_key_t key, const void *value); 70 | 71 | typedef int (*setenv_fun_t)(const char *name, const char *value, int overwrite); 72 | typedef int (*unsetenv_fun_t)(const char *name); 73 | typedef char *(*getenv_fun_t)(const char *name); 74 | typedef struct hostent* (*gethostbyname_fun_t)(const char *name); 75 | //typedef res_state (*__res_state_fun_t)(); 76 | 77 | // hook system functions 78 | static socket_fun_t g_sys_socket = NULL; 79 | static accept_fun_t g_sys_accept = NULL; 80 | static connect_fun_t g_sys_connect = NULL; 81 | static close_fun_t g_sys_close = NULL; 82 | 83 | static select_fun_t g_sys_select = NULL; 84 | 85 | static sleep_fun_t g_sys_sleep = NULL; 86 | static usleep_fun_t g_sys_usleep = NULL; 87 | static nanosleep_fun_t g_sys_nanosleep = NULL; 88 | 89 | static read_fun_t g_sys_read = NULL; 90 | static write_fun_t g_sys_write = NULL; 91 | 92 | static send_fun_t g_sys_send = NULL; 93 | static recv_fun_t g_sys_recv = NULL; 94 | 95 | static poll_fun_t g_sys_poll = NULL; 96 | 97 | static setsockopt_fun_t g_sys_setsockopt = NULL; 98 | static fcntl_fun_t g_sys_fcntl = NULL; 99 | 100 | static gethostbyname_fun_t g_sys_gethostbyname = NULL; 101 | 102 | typedef struct rpchook_t { 103 | int user_flag; 104 | struct sockaddr_in dest; //maybe sockaddr_un; 105 | int domain; //AF_LOCAL , AF_INET 106 | 107 | struct timeval read_timeout; 108 | struct timeval write_timeout; 109 | } rpchook_t; 110 | static rpchook_t *gSocketFd[102400] = { 0 }; 111 | 112 | static inline char is_enable_sys_hook(coroutine_t *co) { 113 | if (!co || !co->task) { 114 | return 0; 115 | } 116 | return co->task->attr.enable_sys_hook; 117 | } 118 | 119 | static inline rpchook_t* get_by_fd(int fd) { 120 | if( fd > -1 && fd < (int)sizeof(gSocketFd) / (int)sizeof(gSocketFd[0]) ) { 121 | return gSocketFd[fd]; 122 | } 123 | return NULL; 124 | } 125 | 126 | static inline rpchook_t * alloc_by_fd(int fd) { 127 | if( fd > -1 && fd < (int)sizeof(gSocketFd) / (int)sizeof(gSocketFd[0]) ) { 128 | rpchook_t *lp = (rpchook_t*)calloc(1,sizeof(rpchook_t)); 129 | lp->read_timeout.tv_usec = 1000; 130 | lp->write_timeout.tv_usec = 1000; 131 | gSocketFd[fd] = lp; 132 | return lp; 133 | } 134 | return NULL; 135 | } 136 | 137 | static inline void free_by_fd(int fd) { 138 | if(fd > -1 && fd < (int)sizeof(gSocketFd) / (int)sizeof(gSocketFd[0])) { 139 | rpchook_t *lp = gSocketFd[fd]; 140 | if(lp) { 141 | gSocketFd[fd] = NULL; 142 | free(lp); 143 | } 144 | } 145 | return; 146 | } 147 | 148 | 149 | int socket(int domain, int type, int protocol) { 150 | HOOK_SYS_FUNC(socket); 151 | if(!is_enable_sys_hook(coroutine_self())) { 152 | return g_sys_socket(domain,type,protocol); 153 | } 154 | int fd = g_sys_socket(domain,type,protocol); 155 | if( fd < 0 ) { 156 | return fd; 157 | } 158 | 159 | rpchook_t *lp = alloc_by_fd(fd); 160 | lp->domain = domain; 161 | 162 | // set socket fd as non block by default 163 | HOOK_SYS_FUNC(fcntl); 164 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL,0) | O_NONBLOCK); 165 | 166 | return fd; 167 | } 168 | 169 | int select(int nfds, fd_set *readfds, fd_set *writefds, 170 | fd_set *exceptfds, struct timeval *timeout) { 171 | HOOK_SYS_FUNC(select); 172 | if(!is_enable_sys_hook(coroutine_self())) { 173 | return g_sys_select(nfds, readfds, writefds, exceptfds, timeout); 174 | } 175 | 176 | int timeout_ms = -1; 177 | if (timeout) { 178 | timeout_ms = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; 179 | } 180 | 181 | if (timeout == 0) { 182 | return g_sys_select(nfds, readfds, writefds, exceptfds, timeout); 183 | } 184 | 185 | fd_set rfs, wfs, efs; 186 | FD_ZERO(&rfs); 187 | FD_ZERO(&wfs); 188 | FD_ZERO(&efs); 189 | if (readfds) rfs = *readfds; 190 | if (writefds) wfs = *writefds; 191 | if (exceptfds) efs = *exceptfds; 192 | struct timeval zero_tv = {0, 0}; 193 | int n = g_sys_select(nfds, (readfds ? &rfs : NULL), 194 | (writefds ? &wfs : NULL), 195 | (exceptfds ? &efs : NULL), &zero_tv); 196 | if (n != 0) { 197 | if (readfds) *readfds = rfs; 198 | if (writefds) *writefds = wfs; 199 | if (exceptfds) *exceptfds = efs; 200 | return n; 201 | } 202 | 203 | // convert select to poll 204 | // first compute total fd num 205 | fd_set* fds[3] = {readfds, writefds, exceptfds}; 206 | int total = 0; 207 | for (int i = 0; i < 3; ++i) { 208 | fd_set *set = fds[i]; 209 | if (!set) { 210 | continue; 211 | } 212 | for (int fd = 0; fd < nfds; ++fd) { 213 | if (FD_ISSET(fd, set)) { 214 | total++; 215 | } 216 | } 217 | } 218 | 219 | // allocate poll array 220 | struct pollfd *poll_fds = (struct pollfd*)calloc(total, sizeof(struct pollfd)); 221 | uint32_t poll_events[3] = {POLLIN, POLLOUT, 0}; 222 | for (int i = 0; i < 3; ++i) { 223 | fd_set *set = fds[i]; 224 | if (!set) { 225 | continue; 226 | } 227 | for (int fd = 0; fd < nfds; ++fd) { 228 | if (FD_ISSET(fd, set)) { 229 | poll_fds[i].fd = fd; 230 | poll_fds[i].events = poll_events[i]; 231 | } 232 | } 233 | } 234 | 235 | // OK, do the poll work 236 | n = poll(poll_fds, total, timeout_ms); 237 | if (n <= 0) { 238 | goto out; 239 | } 240 | 241 | // convert pollfd to fd_set 242 | int ret = 0; 243 | for (int i = 0; i < total; ++i) { 244 | struct pollfd *pfd = &poll_fds[i]; 245 | if ((pfd->events & POLLIN) && readfds) { 246 | FD_SET(pfd->fd, readfds); 247 | ++ret; 248 | } 249 | 250 | if ((pfd->events & POLLOUT) && writefds) { 251 | FD_SET(pfd->fd, writefds); 252 | ++ret; 253 | } 254 | 255 | if ((pfd->events & ~(POLLIN | POLLOUT)) && exceptfds) { 256 | FD_SET(pfd->fd, exceptfds); 257 | ++ret; 258 | } 259 | } 260 | 261 | out: 262 | free(poll_fds); 263 | return ret; 264 | } 265 | 266 | int accept(int listen_fd, struct sockaddr *addr, socklen_t *len) { 267 | HOOK_SYS_FUNC(accept); 268 | coroutine_t *co = coroutine_self(); 269 | if(!is_enable_sys_hook(co)) { 270 | return g_sys_accept(listen_fd,addr,len); 271 | } 272 | rpchook_t *lp = get_by_fd(listen_fd); 273 | if(!lp) { 274 | alloc_by_fd(listen_fd); 275 | fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL,0) | O_NONBLOCK); 276 | } 277 | int fd = g_sys_accept(listen_fd,addr,len); 278 | 279 | while (fd < 0) { 280 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 281 | struct pollfd pf = {0}; 282 | pf.fd = listen_fd; 283 | pf.events = (POLLIN|POLLERR|POLLHUP); 284 | int n = coroutine_poll(get_epoll_context(),&pf,1, -1); 285 | if (n <= 0 && co->task->timeout) { 286 | close(listen_fd); 287 | errno = EBADF; 288 | return -1; 289 | } 290 | fd = g_sys_accept(listen_fd,addr,len); 291 | } else { 292 | // fd < 0 293 | return fd; 294 | } 295 | }; 296 | 297 | lp = alloc_by_fd(fd); 298 | 299 | int current_flags = fcntl(fd ,F_GETFL, 0); 300 | int flag = current_flags; 301 | flag |= O_NONBLOCK; 302 | 303 | // set socket fd as non block by default 304 | int ret = fcntl(fd ,F_SETFL, flag); 305 | if (0 == ret && lp) { 306 | lp->user_flag = current_flags; 307 | } 308 | 309 | return fd; 310 | } 311 | 312 | int connect(int fd, const struct sockaddr *address, socklen_t address_len) { 313 | HOOK_SYS_FUNC(connect); 314 | coroutine_t *co = coroutine_self(); 315 | if(!is_enable_sys_hook(co)) { 316 | return g_sys_connect(fd,address,address_len); 317 | } 318 | 319 | //1.sys call 320 | int ret = g_sys_connect(fd,address,address_len); 321 | 322 | rpchook_t *lp = get_by_fd(fd); 323 | if(!lp) { 324 | return ret; 325 | } 326 | 327 | if(sizeof(lp->dest) >= address_len) { 328 | memcpy(&(lp->dest),address,(int)address_len); 329 | } 330 | 331 | /* 332 | if(O_NONBLOCK & lp->user_flag) { 333 | return ret; 334 | } 335 | */ 336 | 337 | if (!(ret < 0 && errno == EINPROGRESS)) { 338 | return ret; 339 | } 340 | 341 | //2.wait 342 | struct pollfd pf = { 0 }; 343 | 344 | for (int i = 0; i < 10; ++i) { 345 | memset(&pf,0,sizeof(pf)); 346 | pf.fd = fd; 347 | pf.events = (POLLOUT | POLLERR | POLLHUP); 348 | 349 | /* 350 | if(!poll(&pf,1,-1) && co->task->timeout) { 351 | close(fd); 352 | errno = EBADF; 353 | return -1; 354 | } 355 | */ 356 | int pollret = poll(&pf,1,10); 357 | if (pollret == 1) { 358 | break; 359 | } 360 | } 361 | 362 | if(pf.revents & POLLOUT) { 363 | errno = 0; 364 | return 0; 365 | } 366 | 367 | /* 368 | //3.set errno 369 | int err = 0; 370 | socklen_t errlen = sizeof(err); 371 | getsockopt(fd,SOL_SOCKET,SO_ERROR,&err,&errlen); 372 | if(err) { 373 | errno = err; 374 | } else { 375 | errno = ETIMEDOUT; 376 | } 377 | */ 378 | return ret; 379 | } 380 | 381 | int close(int fd) { 382 | HOOK_SYS_FUNC(close); 383 | if(!is_enable_sys_hook(coroutine_self())) { 384 | return g_sys_close(fd); 385 | } 386 | 387 | if (get_by_fd(fd) != NULL) { 388 | free_by_fd(fd); 389 | } 390 | return g_sys_close(fd); 391 | } 392 | 393 | // 1: io ready 394 | // -1: timeout 395 | static int wait_io_ready(coroutine_t *co, int fd, int events, int timeout) { 396 | struct pollfd pf = {0}; 397 | pf.fd = fd; 398 | pf.events = events; 399 | 400 | if(!poll(&pf,1,timeout) && co->task->timeout) { 401 | close(fd); 402 | errno = EBADF; 403 | return -1; 404 | } 405 | 406 | return 1; 407 | } 408 | 409 | ssize_t read(int fd, void *buf, size_t nbyte) { 410 | HOOK_SYS_FUNC(read); 411 | coroutine_t *co = coroutine_self(); 412 | if(!is_enable_sys_hook(co)) { 413 | return g_sys_read(fd,buf,nbyte); 414 | } 415 | 416 | rpchook_t *lp = get_by_fd(fd); 417 | 418 | if(!lp) { 419 | return g_sys_read(fd,buf,nbyte); 420 | } 421 | 422 | int timeout = (lp->read_timeout.tv_sec * 1000) + (lp->read_timeout.tv_usec / 1000); 423 | 424 | ssize_t n = 0; 425 | int ready; 426 | do { 427 | ready = wait_io_ready(co, fd, POLLIN | POLLERR | POLLHUP, timeout); 428 | if (ready <= 0) { 429 | break; 430 | } 431 | 432 | ssize_t ret = g_sys_read(fd, buf + n, nbyte - n); 433 | if (ret < 0) { 434 | if (errno != EAGAIN && errno != EWOULDBLOCK) { 435 | break; 436 | } 437 | continue; 438 | } else if (ret == 0) { 439 | break; 440 | } 441 | 442 | n += ret; 443 | } while (n <= 0 && n < nbyte); 444 | if (ready <= 0 && n == 0) { 445 | return ready; 446 | } 447 | return n; 448 | } 449 | 450 | ssize_t write(int fd, const void *buf, size_t nbyte) { 451 | HOOK_SYS_FUNC(write); 452 | coroutine_t *co = coroutine_self(); 453 | if(!is_enable_sys_hook(co)) { 454 | return g_sys_write(fd,buf,nbyte); 455 | } 456 | 457 | rpchook_t *lp = get_by_fd(fd); 458 | 459 | if(!lp) { 460 | return g_sys_write(fd,buf,nbyte); 461 | } 462 | 463 | int timeout = (lp->write_timeout.tv_sec * 1000) + (lp->write_timeout.tv_usec / 1000); 464 | ssize_t n = 0; 465 | ssize_t ret = g_sys_write(fd, buf, nbyte); 466 | if (ret == 0) { 467 | return ret; 468 | } 469 | 470 | if (ret > 0) { 471 | n += ret; 472 | } 473 | 474 | while (n < nbyte) { 475 | ret = wait_io_ready(co, fd, POLLOUT | POLLERR | POLLHUP, timeout); 476 | if (ret <= 0) { 477 | break; 478 | } 479 | 480 | ret = g_sys_write(fd, buf + n, nbyte - n); 481 | 482 | if (ret < 0) { 483 | if (errno != EAGAIN && errno != EWOULDBLOCK) { 484 | break; 485 | } 486 | continue; 487 | } else if (ret == 0) { 488 | break; 489 | } 490 | 491 | n += ret; 492 | } 493 | 494 | if (ret <= 0 && n == 0) { 495 | return ret; 496 | } 497 | return n; 498 | } 499 | 500 | ssize_t send(int socket, const void *buffer, size_t length, int flags) { 501 | HOOK_SYS_FUNC(send); 502 | coroutine_t *co = coroutine_self(); 503 | if(!is_enable_sys_hook(co)) { 504 | return g_sys_send(socket,buffer,length,flags); 505 | } 506 | 507 | rpchook_t *lp = get_by_fd(socket); 508 | 509 | if( !lp) { 510 | return g_sys_send(socket,buffer,length,flags); 511 | } 512 | 513 | int timeout = (lp->write_timeout.tv_sec * 1000) + (lp->write_timeout.tv_usec / 1000); 514 | ssize_t n = 0; 515 | ssize_t ret = g_sys_send(socket, buffer, length, flags); 516 | if (ret == 0) { 517 | return ret; 518 | } 519 | 520 | if (ret > 0) { 521 | n += ret; 522 | } 523 | 524 | while (n < length) { 525 | ret = wait_io_ready(co, socket, POLLOUT | POLLERR | POLLHUP, timeout); 526 | if (ret <= 0) { 527 | break; 528 | } 529 | 530 | ret = g_sys_send(socket, buffer + n, length - n, flags); 531 | if (ret < 0) { 532 | if (errno != EAGAIN && errno != EWOULDBLOCK) { 533 | break; 534 | } 535 | continue; 536 | } else if (ret == 0) { 537 | break; 538 | } 539 | n += ret; 540 | } 541 | 542 | if (ret <= 0 && n == 0) { 543 | return ret; 544 | } 545 | return n; 546 | } 547 | 548 | ssize_t recv(int socket, void *buffer, size_t length, int flags) { 549 | HOOK_SYS_FUNC(recv); 550 | coroutine_t *co = coroutine_self(); 551 | if(!is_enable_sys_hook(co)) { 552 | return g_sys_recv(socket,buffer,length,flags); 553 | } 554 | rpchook_t *lp = get_by_fd(socket); 555 | 556 | if(!lp) { 557 | return g_sys_recv(socket,buffer,length,flags); 558 | } 559 | 560 | int timeout = (lp->read_timeout.tv_sec * 1000) + (lp->read_timeout.tv_usec / 1000); 561 | 562 | ssize_t n = 0; 563 | int ready = 0; 564 | 565 | do { 566 | ready = wait_io_ready(co, socket, POLLIN | POLLERR | POLLHUP, timeout); 567 | if (ready <= 0) { 568 | break; 569 | } 570 | 571 | ssize_t ret = g_sys_recv(socket, buffer + n, length - n, flags); 572 | if (ret < 0) { 573 | if (errno != EAGAIN && errno != EWOULDBLOCK) { 574 | break; 575 | } 576 | continue; 577 | } else if (ret == 0) { 578 | break; 579 | } 580 | 581 | n += ret; 582 | } while (n <= 0 && n < length); 583 | if (ready <= 0 && n == 0) { 584 | return ready; 585 | } 586 | return n; 587 | } 588 | 589 | int poll(struct pollfd fds[], nfds_t nfds, int timeout) { 590 | HOOK_SYS_FUNC(poll); 591 | coroutine_t *co = coroutine_self(); 592 | if(!is_enable_sys_hook(co)) { 593 | return g_sys_poll(fds,nfds,timeout); 594 | } 595 | 596 | return poll_inner(get_epoll_context(),fds,nfds,timeout, g_sys_poll); 597 | } 598 | 599 | int __poll(struct pollfd fds[], nfds_t nfds, int timeout) { 600 | return poll(fds, nfds, timeout); 601 | } 602 | 603 | static void doSleep(unsigned long long timeout_ms) { 604 | struct pollfd fds[1]; 605 | 606 | poll_inner(get_epoll_context(), fds, 0, timeout_ms, NULL); 607 | } 608 | 609 | unsigned int sleep(unsigned int seconds) { 610 | HOOK_SYS_FUNC(sleep); 611 | coroutine_t *co = coroutine_self(); 612 | if(!is_enable_sys_hook(co)) { 613 | return g_sys_sleep(seconds); 614 | } 615 | 616 | doSleep(seconds * 1000); 617 | return 0; 618 | } 619 | 620 | int usleep(useconds_t usec) { 621 | HOOK_SYS_FUNC(usleep); 622 | coroutine_t *co = coroutine_self(); 623 | if(!is_enable_sys_hook(co)) { 624 | return g_sys_usleep(usec); 625 | } 626 | 627 | doSleep(usec / 1000); 628 | 629 | return 0; 630 | } 631 | 632 | int nanosleep(const struct timespec *req, struct timespec *rem) { 633 | HOOK_SYS_FUNC(nanosleep); 634 | coroutine_t *co = coroutine_self(); 635 | if(!is_enable_sys_hook(co)) { 636 | return g_sys_nanosleep(req, rem); 637 | } 638 | int timeout_ms = req->tv_sec * 1000 + req->tv_nsec / 1000000; 639 | 640 | doSleep(timeout_ms); 641 | 642 | return 0; 643 | } 644 | 645 | int setsockopt(int fd, int level, int option_name, 646 | const void *option_value, socklen_t option_len) { 647 | HOOK_SYS_FUNC(setsockopt); 648 | coroutine_t *co = coroutine_self(); 649 | if(!is_enable_sys_hook(co)) { 650 | return g_sys_setsockopt(fd,level,option_name,option_value,option_len); 651 | } 652 | 653 | rpchook_t *lp = get_by_fd(fd); 654 | 655 | if(lp && SOL_SOCKET == level) { 656 | struct timeval *val = (struct timeval*)option_value; 657 | if(SO_RCVTIMEO == option_name) { 658 | memcpy(&lp->read_timeout,val,sizeof(*val)); 659 | } else if( SO_SNDTIMEO == option_name ) { 660 | memcpy(&lp->write_timeout,val,sizeof(*val)); 661 | } 662 | } 663 | return g_sys_setsockopt(fd,level,option_name,option_value,option_len); 664 | } 665 | 666 | int fcntl(int fildes, int cmd, ...) { 667 | HOOK_SYS_FUNC(fcntl); 668 | if(fildes < 0) { 669 | return -1; 670 | } 671 | 672 | va_list arg_list; 673 | va_start(arg_list,cmd); 674 | 675 | int ret = -1; 676 | rpchook_t *lp = get_by_fd(fildes); 677 | switch(cmd) { 678 | case F_DUPFD: { 679 | int param = va_arg(arg_list,int); 680 | ret = g_sys_fcntl(fildes,cmd,param); 681 | break; 682 | } 683 | case F_GETFD: { 684 | ret = g_sys_fcntl(fildes,cmd); 685 | break; 686 | } 687 | case F_SETFD: { 688 | int param = va_arg(arg_list,int); 689 | ret = g_sys_fcntl(fildes,cmd,param); 690 | break; 691 | } 692 | case F_GETFL: { 693 | ret = g_sys_fcntl(fildes,cmd); 694 | break; 695 | } 696 | case F_SETFL: { 697 | int param = va_arg(arg_list,int); 698 | int flag = param; 699 | // set as non block by default 700 | if(is_enable_sys_hook(coroutine_self()) && lp) { 701 | flag |= O_NONBLOCK; 702 | } 703 | ret = g_sys_fcntl(fildes,cmd,flag); 704 | if(0 == ret && lp) { 705 | lp->user_flag = param; 706 | } 707 | break; 708 | } 709 | case F_GETOWN: { 710 | ret = g_sys_fcntl(fildes,cmd); 711 | break; 712 | } 713 | case F_SETOWN: { 714 | int param = va_arg(arg_list,int); 715 | ret = g_sys_fcntl(fildes,cmd,param); 716 | break; 717 | } 718 | case F_GETLK: { 719 | struct flock *param = va_arg(arg_list,struct flock *); 720 | ret = g_sys_fcntl(fildes,cmd,param); 721 | break; 722 | } 723 | case F_SETLK: { 724 | struct flock *param = va_arg(arg_list,struct flock *); 725 | ret = g_sys_fcntl(fildes,cmd,param); 726 | break; 727 | } 728 | case F_SETLKW: { 729 | struct flock *param = va_arg(arg_list,struct flock *); 730 | ret = g_sys_fcntl(fildes,cmd,param); 731 | break; 732 | } 733 | } 734 | 735 | va_end(arg_list); 736 | 737 | return ret; 738 | } 739 | 740 | typedef struct hostbuf_wrap { 741 | struct hostent host; 742 | char* buffer; 743 | size_t buffer_size; 744 | int host_errno; 745 | } hostbuf_wrap; 746 | 747 | struct hostent *coroutine_gethostbyname(const char *name) { 748 | if (!name) { 749 | return NULL; 750 | } 751 | 752 | hostbuf_wrap *__co_hostbuf_wrap = (hostbuf_wrap*)coroutine_getspecific(gCoroutineHostbufKey); 753 | if (__co_hostbuf_wrap == NULL) { 754 | __co_hostbuf_wrap = (hostbuf_wrap*)calloc(1, sizeof(hostbuf_wrap)); 755 | int ret = coroutine_setspecific(gCoroutineHostbufKey, __co_hostbuf_wrap); 756 | if (ret != 0) { 757 | free(__co_hostbuf_wrap); 758 | __co_hostbuf_wrap = NULL; 759 | } 760 | } 761 | 762 | if (__co_hostbuf_wrap->buffer && __co_hostbuf_wrap->buffer_size > 1024) { 763 | free(__co_hostbuf_wrap->buffer); 764 | __co_hostbuf_wrap->buffer = NULL; 765 | } 766 | if (!__co_hostbuf_wrap->buffer) { 767 | __co_hostbuf_wrap->buffer = (char*)malloc(1024); 768 | __co_hostbuf_wrap->buffer_size = 1024; 769 | } 770 | 771 | struct hostent *host = &__co_hostbuf_wrap->host; 772 | struct hostent *result = NULL; 773 | int *h_errnop = &(__co_hostbuf_wrap->host_errno); 774 | 775 | int ret = -1; 776 | while ((ret = gethostbyname_r(name, host, __co_hostbuf_wrap->buffer, 777 | __co_hostbuf_wrap->buffer_size, &result, h_errnop)) == ERANGE && 778 | (*h_errnop == NETDB_INTERNAL)) { 779 | free(__co_hostbuf_wrap->buffer); 780 | __co_hostbuf_wrap->buffer_size = __co_hostbuf_wrap->buffer_size * 2; 781 | __co_hostbuf_wrap->buffer = (char*)malloc(__co_hostbuf_wrap->buffer_size); 782 | } 783 | 784 | if (ret == 0 && (host == result)) { 785 | return host; 786 | } 787 | return NULL; 788 | } 789 | 790 | struct hostent *gethostbyname(const char *name) { 791 | if (!is_enable_sys_hook(coroutine_self())) { 792 | return g_sys_gethostbyname(name); 793 | } 794 | return coroutine_gethostbyname(name); 795 | } 796 | -------------------------------------------------------------------------------- /src/misc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "misc.h" 4 | 5 | unsigned long long GetTickMS() { 6 | #if defined( __LIBCO_RDTSCP__) 7 | static uint32_t khz = getCpuKhz(); 8 | return counter() / khz; 9 | #else 10 | struct timeval now = { 0 }; 11 | gettimeofday( &now,NULL ); 12 | unsigned long long u = now.tv_sec; 13 | u *= 1000; 14 | u += now.tv_usec / 1000; 15 | return u; 16 | #endif 17 | } 18 | -------------------------------------------------------------------------------- /src/misc.h: -------------------------------------------------------------------------------- 1 | #ifndef __MISC_H__ 2 | #define __MISC_H__ 3 | 4 | unsigned long long GetTickMS(); 5 | 6 | #endif // __MISC_H__ 7 | -------------------------------------------------------------------------------- /src/typedef.h: -------------------------------------------------------------------------------- 1 | #ifndef __TYPEDEF_H__ 2 | #define __TYPEDEF_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | // innner struct typedef 9 | 10 | struct env_t; 11 | typedef struct env_t env_t; 12 | 13 | struct epoll_context_t; 14 | typedef struct epoll_context_t epoll_context_t; 15 | 16 | struct epoll_result_t; 17 | typedef struct epoll_result_t epoll_result_t; 18 | 19 | struct context_t; 20 | typedef struct context_t context_t; 21 | 22 | struct coroutine_stack_t; 23 | typedef struct coroutine_stack_t coroutine_stack_t; 24 | 25 | struct timer_item_t; 26 | typedef struct timer_item_t timer_item_t; 27 | 28 | struct timer_list_t; 29 | typedef struct timer_list_t timer_list_t; 30 | 31 | struct epoll_timer_t; 32 | typedef struct epoll_timer_t epoll_timer_t; 33 | 34 | struct task_t; 35 | typedef struct task_t task_t; 36 | 37 | struct taskpool_t; 38 | typedef struct taskpool_t taskpool_t; 39 | 40 | struct coroutine_specific_t; 41 | typedef struct coroutine_specific_t coroutine_specific_t; 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | 47 | #endif // __TYPEDEF_H__ 48 | --------------------------------------------------------------------------------