add NtyCo as submodule & 搭建设计ebpf主从同步代码框架
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,6 +4,8 @@
|
|||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
|
|
||||||
|
/ebpf/libbpf-bootstrap
|
||||||
|
|
||||||
kvstore
|
kvstore
|
||||||
testcase
|
testcase
|
||||||
|
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "NtyCo"]
|
||||||
|
path = NtyCo
|
||||||
|
url = https://github.com/wangbojing/NtyCo.git
|
||||||
5
Makefile
5
Makefile
@@ -3,11 +3,12 @@ CC = gcc
|
|||||||
CFLAGS = -g -DJEMALLOC_NO_DEMANGLE
|
CFLAGS = -g -DJEMALLOC_NO_DEMANGLE
|
||||||
|
|
||||||
NET_SRCS = ntyco.c proactor.c reactor.c kvstore.c
|
NET_SRCS = ntyco.c proactor.c reactor.c kvstore.c
|
||||||
KV_SRCS = kvs_array_bin.c kvs_rbtree_bin.c kvs_hash_bin.c kvs_rw_tools.c kvs_protocol_resp.c kvs_oplog.c kvs_slave.c
|
KV_SRCS = kvs_array_bin.c kvs_rbtree_bin.c kvs_hash_bin.c kvs_rw_tools.c kvs_protocol_resp.c kvs_slave.c
|
||||||
MEM_SRCS = ./memory/mempool.c ./memory/alloc_dispatch.c
|
MEM_SRCS = ./memory/mempool.c ./memory/alloc_dispatch.c
|
||||||
COMMON_SRCS = ./common/config.c ./diskuring/diskuring.c
|
COMMON_SRCS = ./common/config.c ./diskuring/diskuring.c
|
||||||
|
DUMP_SRCS = ./dump/kvs_snapshot.c ./dump/kvs_oplog.c
|
||||||
|
|
||||||
SRCS = $(NET_SRCS) $(KV_SRCS) $(MEM_SRCS) $(COMMON_SRCS)
|
SRCS = $(NET_SRCS) $(KV_SRCS) $(MEM_SRCS) $(COMMON_SRCS) $(DUMP_SRCS)
|
||||||
|
|
||||||
INC = -I./NtyCo/core/ -I/usr/include/libxml2 -I./
|
INC = -I./NtyCo/core/ -I/usr/include/libxml2 -I./
|
||||||
LIBDIRS = -L./NtyCo/
|
LIBDIRS = -L./NtyCo/
|
||||||
|
|||||||
1
NtyCo
Submodule
1
NtyCo
Submodule
Submodule NtyCo added at 72ab5fd04f
4
NtyCo/.gitignore
vendored
4
NtyCo/.gitignore
vendored
@@ -1,4 +0,0 @@
|
|||||||
_build/
|
|
||||||
bin/
|
|
||||||
objs/
|
|
||||||
lib/
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
|
|
||||||
CC = gcc
|
|
||||||
ECHO = echo
|
|
||||||
|
|
||||||
SUB_DIR = core/
|
|
||||||
SAMPLE_DIR = sample/
|
|
||||||
ROOT_DIR = $(shell pwd)
|
|
||||||
OBJS_DIR = $(ROOT_DIR)/objs
|
|
||||||
BIN_DIR = $(ROOT_DIR)/bin
|
|
||||||
|
|
||||||
BIN = nty_server nty_client nty_bench nty_server_mulcore hook_tcpserver nty_http_server nty_mysql_client nty_mysql_oper nty_websocket_server nty_http_server_mulcore ntyco_httpd nty_rediscli
|
|
||||||
|
|
||||||
LIB = libntyco.a
|
|
||||||
|
|
||||||
FLAG = -lpthread -O3 -ldl -I $(ROOT_DIR)/core
|
|
||||||
|
|
||||||
THIRDFLAG = -lcrypto -lssl -lmysqlclient -lhiredis -I /usr/include/mysql/ -I /usr/local/include/hiredis/
|
|
||||||
|
|
||||||
CUR_SOURCE = ${wildcard *.c}
|
|
||||||
CUR_OBJS = ${patsubst %.c, %.o, %(CUR_SOURCE)}
|
|
||||||
|
|
||||||
export CC BIN_DIR OBJS_DIR ROOT_IDR FLAG BIN ECHO EFLAG
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
all : check_objs check_bin $(SUB_DIR) $(LIB)
|
|
||||||
.PHONY : all
|
|
||||||
|
|
||||||
bin: $(SAMPLE_DIR) $(BIN)
|
|
||||||
|
|
||||||
# lib: $(SUB_DIR) $(LIB)
|
|
||||||
|
|
||||||
$(SUB_DIR) : ECHO
|
|
||||||
make -C $@
|
|
||||||
|
|
||||||
#DEBUG : ECHO
|
|
||||||
# make -C bin
|
|
||||||
|
|
||||||
ECHO :
|
|
||||||
@echo $(SUB_DIR)
|
|
||||||
|
|
||||||
check_objs:
|
|
||||||
if [ ! -d "objs" ]; then \
|
|
||||||
mkdir -p objs; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
check_bin:
|
|
||||||
if [ ! -d "bin" ]; then \
|
|
||||||
mkdir -p bin; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
nty_server : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_server.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG)
|
|
||||||
|
|
||||||
nty_client : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_client.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG)
|
|
||||||
|
|
||||||
nty_bench : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_bench.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG)
|
|
||||||
|
|
||||||
nty_server_mulcore : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_server_mulcore.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG)
|
|
||||||
|
|
||||||
nty_http_server : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_http_server.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG)
|
|
||||||
|
|
||||||
nty_websocket_server : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_websocket_server.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG) $(THIRDFLAG)
|
|
||||||
|
|
||||||
nty_mysql_client : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_mysql_client.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG) $(THIRDFLAG)
|
|
||||||
|
|
||||||
nty_mysql_oper : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_mysql_oper.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG) $(THIRDFLAG)
|
|
||||||
|
|
||||||
nty_rediscli : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_rediscli.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG) $(THIRDFLAG)
|
|
||||||
|
|
||||||
hook_tcpserver: $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/hook_tcpserver.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG)
|
|
||||||
|
|
||||||
nty_http_server_mulcore : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/nty_http_server_mulcore.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG)
|
|
||||||
|
|
||||||
ntyco_httpd : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o $(SAMPLE_DIR)/ntyco_httpd.c
|
|
||||||
$(CC) -o $(BIN_DIR)/$@ $^ $(FLAG)
|
|
||||||
|
|
||||||
libntyco.a : $(OBJS_DIR)/nty_socket.o $(OBJS_DIR)/nty_coroutine.o $(OBJS_DIR)/nty_epoll.o $(OBJS_DIR)/nty_schedule.o
|
|
||||||
ar rcs $@ $^
|
|
||||||
|
|
||||||
clean :
|
|
||||||
rm -rf $(BIN_DIR)/* $(OBJS_DIR)/* libntyco.a
|
|
||||||
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
## NtyCo
|
|
||||||
|
|
||||||
#### coroutine
|
|
||||||
[实现原理](https://github.com/wangbojing/NtyCo/wiki/NtyCo%E7%9A%84%E5%AE%9E%E7%8E%B0)
|
|
||||||
[配套视频讲解](https://it.0voice.com/p/t_pc/goods_pc_detail/goods_detail/course_2QFAeORw45TjJA1y9tq8CmdVJTQ)
|
|
||||||
|
|
||||||
## details
|
|
||||||
#### coroutine FSM
|
|
||||||

|
|
||||||
|
|
||||||
#### storage structure (ready, wait, sleep, status)
|
|
||||||

|
|
||||||
|
|
||||||
#### nty_server process
|
|
||||||

|
|
||||||
|
|
||||||
#### compile
|
|
||||||
|
|
||||||
```
|
|
||||||
编译ntyco的core文件与编译libntyco.a的静态库
|
|
||||||
$ make
|
|
||||||
|
|
||||||
// 编译sample
|
|
||||||
$ make bin
|
|
||||||
```
|
|
||||||
|
|
||||||
#### err info
|
|
||||||
```
|
|
||||||
nty_mysql_oper.c:8:19: fatal error: mysql.h: No such file or directory
|
|
||||||
|
|
||||||
解决方案:
|
|
||||||
# sudo apt-get install libmysqlclient-dev
|
|
||||||
|
|
||||||
nty_rediscli.c:11:21: fatal error: hiredis.h: No such file or directory
|
|
||||||
|
|
||||||
解决方案:
|
|
||||||
需要编译安装hiredis: https://github.com/redis/hiredis
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### server
|
|
||||||
```
|
|
||||||
$ ./bin/nty_server
|
|
||||||
```
|
|
||||||
#### client
|
|
||||||
```
|
|
||||||
./bin/nty_client
|
|
||||||
```
|
|
||||||
|
|
||||||
#### mul_process, mul_core
|
|
||||||
```
|
|
||||||
$ ./bin/nty_server_mulcore
|
|
||||||
```
|
|
||||||
#### websocket
|
|
||||||
```
|
|
||||||
$ ./bin/nty_websocket_server
|
|
||||||
```
|
|
||||||
|
|
||||||
#### bench
|
|
||||||
```
|
|
||||||
$ ./bin/nty_bench
|
|
||||||
```
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
#### http server
|
|
||||||
```
|
|
||||||
$ ./bin/nty_http_server_mulcore
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
##### [对应视频讲解](https://ke.qq.com/course/2705727?tuin=1bf84273)
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
aux_source_directory(. SRC)
|
|
||||||
|
|
||||||
add_library(nty_core ${SRC})
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
CUR_SOURCE = ${wildcard *.c}
|
|
||||||
|
|
||||||
CUR_OBJS = ${patsubst %.c, %.o, $(CUR_SOURCE)}
|
|
||||||
|
|
||||||
all : $(SUB_DIR) $(CUR_OBJS)
|
|
||||||
|
|
||||||
$(SUB_DIR) : ECHO
|
|
||||||
make -C $@
|
|
||||||
|
|
||||||
$(CUR_OBJS) : %.o : %.c
|
|
||||||
$(CC) -c $^ -o $(OBJS_DIR)/$@ $(FLAG)
|
|
||||||
|
|
||||||
ECHO :
|
|
||||||
@echo $(SUB_DIR)
|
|
||||||
@@ -1,353 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
pthread_key_t global_sched_key;
|
|
||||||
static pthread_once_t sched_key_once = PTHREAD_ONCE_INIT;
|
|
||||||
|
|
||||||
// https://github.com/halayli/lthread/blob/master/src/lthread.c#L58
|
|
||||||
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
|
|
||||||
static void
|
|
||||||
_save_stack(nty_coroutine *co) {
|
|
||||||
char* top = co->sched->stack + co->sched->stack_size;
|
|
||||||
char dummy = 0;
|
|
||||||
assert(top - &dummy <= NTY_CO_MAX_STACKSIZE);
|
|
||||||
if (co->stack_size < top - &dummy) {
|
|
||||||
co->stack = realloc(co->stack, top - &dummy);
|
|
||||||
assert(co->stack != NULL);
|
|
||||||
}
|
|
||||||
co->stack_size = top - &dummy;
|
|
||||||
memcpy(co->stack, &dummy, co->stack_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_load_stack(nty_coroutine *co) {
|
|
||||||
memcpy(co->sched->stack + co->sched->stack_size - co->stack_size, co->stack, co->stack_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _exec(void *lt) {
|
|
||||||
nty_coroutine *co = (nty_coroutine*)lt;
|
|
||||||
co->func(co->arg);
|
|
||||||
co->status |= (BIT(NTY_COROUTINE_STATUS_EXITED) | BIT(NTY_COROUTINE_STATUS_FDEOF) | BIT(NTY_COROUTINE_STATUS_DETACH));
|
|
||||||
nty_coroutine_yield(co);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
int _switch(nty_cpu_ctx *new_ctx, nty_cpu_ctx *cur_ctx);
|
|
||||||
|
|
||||||
#ifdef __i386__
|
|
||||||
__asm__ (
|
|
||||||
" .text \n"
|
|
||||||
" .p2align 2,,3 \n"
|
|
||||||
".globl _switch \n"
|
|
||||||
"_switch: \n"
|
|
||||||
"__switch: \n"
|
|
||||||
"movl 8(%esp), %edx # fs->%edx \n"
|
|
||||||
"movl %esp, 0(%edx) # save esp \n"
|
|
||||||
"movl %ebp, 4(%edx) # save ebp \n"
|
|
||||||
"movl (%esp), %eax # save eip \n"
|
|
||||||
"movl %eax, 8(%edx) \n"
|
|
||||||
"movl %ebx, 12(%edx) # save ebx,esi,edi \n"
|
|
||||||
"movl %esi, 16(%edx) \n"
|
|
||||||
"movl %edi, 20(%edx) \n"
|
|
||||||
"movl 4(%esp), %edx # ts->%edx \n"
|
|
||||||
"movl 20(%edx), %edi # restore ebx,esi,edi \n"
|
|
||||||
"movl 16(%edx), %esi \n"
|
|
||||||
"movl 12(%edx), %ebx \n"
|
|
||||||
"movl 0(%edx), %esp # restore esp \n"
|
|
||||||
"movl 4(%edx), %ebp # restore ebp \n"
|
|
||||||
"movl 8(%edx), %eax # restore eip \n"
|
|
||||||
"movl %eax, (%esp) \n"
|
|
||||||
"ret \n"
|
|
||||||
);
|
|
||||||
#elif defined(__x86_64__)
|
|
||||||
|
|
||||||
__asm__ (
|
|
||||||
" .text \n"
|
|
||||||
" .p2align 4,,15 \n"
|
|
||||||
".globl _switch \n"
|
|
||||||
".globl __switch \n"
|
|
||||||
"_switch: \n"
|
|
||||||
"__switch: \n"
|
|
||||||
" movq %rsp, 0(%rsi) # save stack_pointer \n"
|
|
||||||
" movq %rbp, 8(%rsi) # save frame_pointer \n"
|
|
||||||
" movq (%rsp), %rax # save insn_pointer \n"
|
|
||||||
" movq %rax, 16(%rsi) \n"
|
|
||||||
" movq %rbx, 24(%rsi) # save rbx,r12-r15 \n"
|
|
||||||
" movq %r12, 32(%rsi) \n"
|
|
||||||
" movq %r13, 40(%rsi) \n"
|
|
||||||
" movq %r14, 48(%rsi) \n"
|
|
||||||
" movq %r15, 56(%rsi) \n"
|
|
||||||
" movq 56(%rdi), %r15 \n"
|
|
||||||
" movq 48(%rdi), %r14 \n"
|
|
||||||
" movq 40(%rdi), %r13 # restore rbx,r12-r15 \n"
|
|
||||||
" movq 32(%rdi), %r12 \n"
|
|
||||||
" movq 24(%rdi), %rbx \n"
|
|
||||||
" movq 8(%rdi), %rbp # restore frame_pointer \n"
|
|
||||||
" movq 0(%rdi), %rsp # restore stack_pointer \n"
|
|
||||||
" movq 16(%rdi), %rax # restore insn_pointer \n"
|
|
||||||
" movq %rax, (%rsp) \n"
|
|
||||||
" ret \n"
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static void _exec(void *lt) {
|
|
||||||
#if defined(__lvm__) && defined(__x86_64__)
|
|
||||||
__asm__("movq 16(%%rbp), %[lt]" : [lt] "=r" (lt));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
nty_coroutine *co = (nty_coroutine*)lt;
|
|
||||||
co->func(co->arg);
|
|
||||||
co->status |= (BIT(NTY_COROUTINE_STATUS_EXITED) | BIT(NTY_COROUTINE_STATUS_FDEOF) | BIT(NTY_COROUTINE_STATUS_DETACH));
|
|
||||||
#if 1
|
|
||||||
nty_coroutine_yield(co);
|
|
||||||
#else
|
|
||||||
co->ops = 0;
|
|
||||||
_switch(&co->sched->ctx, &co->ctx);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void nty_coroutine_madvise(nty_coroutine *co) {
|
|
||||||
|
|
||||||
size_t current_stack = (co->stack + co->stack_size) - co->ctx.esp;
|
|
||||||
assert(current_stack <= co->stack_size);
|
|
||||||
|
|
||||||
if (current_stack < co->last_stack_size &&
|
|
||||||
co->last_stack_size > co->sched->page_size) {
|
|
||||||
size_t tmp = current_stack + (-current_stack & (co->sched->page_size - 1));
|
|
||||||
assert(madvise(co->stack, co->stack_size-tmp, MADV_DONTNEED) == 0);
|
|
||||||
}
|
|
||||||
co->last_stack_size = current_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern int nty_schedule_create(int stack_size);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void nty_coroutine_free(nty_coroutine *co) {
|
|
||||||
if (co == NULL) return ;
|
|
||||||
co->sched->spawned_coroutines --;
|
|
||||||
#if 1
|
|
||||||
if (co->stack) {
|
|
||||||
free(co->stack);
|
|
||||||
co->stack = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (co) {
|
|
||||||
free(co);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nty_coroutine_init(nty_coroutine *co) {
|
|
||||||
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
getcontext(&co->ctx);
|
|
||||||
co->ctx.uc_stack.ss_sp = co->sched->stack;
|
|
||||||
co->ctx.uc_stack.ss_size = co->sched->stack_size;
|
|
||||||
co->ctx.uc_link = &co->sched->ctx;
|
|
||||||
// printf("TAG21\n");
|
|
||||||
makecontext(&co->ctx, (void (*)(void)) _exec, 1, (void*)co);
|
|
||||||
// printf("TAG22\n");
|
|
||||||
#else
|
|
||||||
void **stack = (void **)(co->stack + co->stack_size);
|
|
||||||
|
|
||||||
stack[-3] = NULL;
|
|
||||||
stack[-2] = (void *)co;
|
|
||||||
|
|
||||||
co->ctx.esp = (void*)stack - (4 * sizeof(void*));
|
|
||||||
co->ctx.ebp = (void*)stack - (3 * sizeof(void*));
|
|
||||||
co->ctx.eip = (void*)_exec;
|
|
||||||
#endif
|
|
||||||
co->status = BIT(NTY_COROUTINE_STATUS_READY);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void nty_coroutine_yield(nty_coroutine *co) {
|
|
||||||
co->ops = 0;
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
if ((co->status & BIT(NTY_COROUTINE_STATUS_EXITED)) == 0) {
|
|
||||||
_save_stack(co);
|
|
||||||
}
|
|
||||||
swapcontext(&co->ctx, &co->sched->ctx);
|
|
||||||
#else
|
|
||||||
_switch(&co->sched->ctx, &co->ctx);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int nty_coroutine_resume(nty_coroutine *co) {
|
|
||||||
|
|
||||||
if (co->status & BIT(NTY_COROUTINE_STATUS_NEW)) {
|
|
||||||
nty_coroutine_init(co);
|
|
||||||
}
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
else {
|
|
||||||
_load_stack(co);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
sched->curr_thread = co;
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
swapcontext(&sched->ctx, &co->ctx);
|
|
||||||
#else
|
|
||||||
_switch(&co->ctx, &co->sched->ctx);
|
|
||||||
nty_coroutine_madvise(co);
|
|
||||||
#endif
|
|
||||||
sched->curr_thread = NULL;
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
if (co->status & BIT(NTY_COROUTINE_STATUS_EXITED)) {
|
|
||||||
if (co->status & BIT(NTY_COROUTINE_STATUS_DETACH)) {
|
|
||||||
nty_coroutine_free(co);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void nty_coroutine_renice(nty_coroutine *co) {
|
|
||||||
co->ops ++;
|
|
||||||
#if 1
|
|
||||||
if (co->ops < 5) return ;
|
|
||||||
#endif
|
|
||||||
TAILQ_INSERT_TAIL(&nty_coroutine_get_sched()->ready, co, ready_next);
|
|
||||||
nty_coroutine_yield(co);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void nty_coroutine_sleep(uint64_t msecs) {
|
|
||||||
nty_coroutine *co = nty_coroutine_get_sched()->curr_thread;
|
|
||||||
|
|
||||||
if (msecs == 0) {
|
|
||||||
TAILQ_INSERT_TAIL(&co->sched->ready, co, ready_next);
|
|
||||||
nty_coroutine_yield(co);
|
|
||||||
} else {
|
|
||||||
nty_schedule_sched_sleepdown(co, msecs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nty_coroutine_detach(void) {
|
|
||||||
nty_coroutine *co = nty_coroutine_get_sched()->curr_thread;
|
|
||||||
co->status |= BIT(NTY_COROUTINE_STATUS_DETACH);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nty_coroutine_sched_key_destructor(void *data) {
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __attribute__((constructor(1000))) nty_coroutine_sched_key_creator(void) {
|
|
||||||
assert(pthread_key_create(&global_sched_key, nty_coroutine_sched_key_destructor) == 0);
|
|
||||||
assert(pthread_setspecific(global_sched_key, NULL) == 0);
|
|
||||||
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// coroutine -->
|
|
||||||
// create
|
|
||||||
//
|
|
||||||
int nty_coroutine_create(nty_coroutine **new_co, proc_coroutine func, void *arg) {
|
|
||||||
|
|
||||||
assert(pthread_once(&sched_key_once, nty_coroutine_sched_key_creator) == 0);
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
|
|
||||||
if (sched == NULL) {
|
|
||||||
nty_schedule_create(0);
|
|
||||||
|
|
||||||
sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
printf("Failed to create scheduler\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_coroutine *co = calloc(1, sizeof(nty_coroutine));
|
|
||||||
if (co == NULL) {
|
|
||||||
printf("Failed to allocate memory for new coroutine\n");
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
co->stack = NULL;
|
|
||||||
co->stack_size = 0;
|
|
||||||
#else
|
|
||||||
int ret = posix_memalign(&co->stack, getpagesize(), sched->stack_size);
|
|
||||||
if (ret) {
|
|
||||||
printf("Failed to allocate stack for new coroutine\n");
|
|
||||||
free(co);
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
co->stack_size = sched->stack_size;
|
|
||||||
#endif
|
|
||||||
co->sched = sched;
|
|
||||||
co->status = BIT(NTY_COROUTINE_STATUS_NEW); //
|
|
||||||
co->id = sched->spawned_coroutines ++;
|
|
||||||
co->func = func;
|
|
||||||
#if CANCEL_FD_WAIT_UINT64
|
|
||||||
co->fd = -1;
|
|
||||||
co->events = 0;
|
|
||||||
#else
|
|
||||||
co->fd_wait = -1;
|
|
||||||
#endif
|
|
||||||
co->arg = arg;
|
|
||||||
co->birth = nty_coroutine_usec_now();
|
|
||||||
*new_co = co;
|
|
||||||
|
|
||||||
TAILQ_INSERT_TAIL(&co->sched->ready, co, ready_next);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,389 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef __NTY_COROUTINE_H__
|
|
||||||
#define __NTY_COROUTINE_H__
|
|
||||||
|
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <dlfcn.h>
|
|
||||||
|
|
||||||
#define _USE_UCONTEXT
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <netinet/tcp.h>
|
|
||||||
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
#include <ucontext.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include "nty_queue.h"
|
|
||||||
#include "nty_tree.h"
|
|
||||||
|
|
||||||
#define NTY_CO_MAX_EVENTS (1024*1024)
|
|
||||||
#define NTY_CO_MAX_STACKSIZE (128*1024) // {http: 16*1024, tcp: 4*1024}
|
|
||||||
|
|
||||||
#define BIT(x) (1 << (x))
|
|
||||||
#define CLEARBIT(x) ~(1 << (x))
|
|
||||||
|
|
||||||
#define CANCEL_FD_WAIT_UINT64 1
|
|
||||||
|
|
||||||
typedef void (*proc_coroutine)(void *);
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NTY_COROUTINE_STATUS_WAIT_READ,
|
|
||||||
NTY_COROUTINE_STATUS_WAIT_WRITE,
|
|
||||||
NTY_COROUTINE_STATUS_NEW,
|
|
||||||
NTY_COROUTINE_STATUS_READY,
|
|
||||||
NTY_COROUTINE_STATUS_EXITED,
|
|
||||||
NTY_COROUTINE_STATUS_BUSY,
|
|
||||||
NTY_COROUTINE_STATUS_SLEEPING,
|
|
||||||
NTY_COROUTINE_STATUS_EXPIRED,
|
|
||||||
NTY_COROUTINE_STATUS_FDEOF,
|
|
||||||
NTY_COROUTINE_STATUS_DETACH,
|
|
||||||
NTY_COROUTINE_STATUS_CANCELLED,
|
|
||||||
NTY_COROUTINE_STATUS_PENDING_RUNCOMPUTE,
|
|
||||||
NTY_COROUTINE_STATUS_RUNCOMPUTE,
|
|
||||||
NTY_COROUTINE_STATUS_WAIT_IO_READ,
|
|
||||||
NTY_COROUTINE_STATUS_WAIT_IO_WRITE,
|
|
||||||
NTY_COROUTINE_STATUS_WAIT_MULTI
|
|
||||||
} nty_coroutine_status;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NTY_COROUTINE_COMPUTE_BUSY,
|
|
||||||
NTY_COROUTINE_COMPUTE_FREE
|
|
||||||
} nty_coroutine_compute_status;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NTY_COROUTINE_EV_READ,
|
|
||||||
NTY_COROUTINE_EV_WRITE
|
|
||||||
} nty_coroutine_event;
|
|
||||||
|
|
||||||
|
|
||||||
LIST_HEAD(_nty_coroutine_link, _nty_coroutine);
|
|
||||||
TAILQ_HEAD(_nty_coroutine_queue, _nty_coroutine);
|
|
||||||
|
|
||||||
RB_HEAD(_nty_coroutine_rbtree_sleep, _nty_coroutine);
|
|
||||||
RB_HEAD(_nty_coroutine_rbtree_wait, _nty_coroutine);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct _nty_coroutine_link nty_coroutine_link;
|
|
||||||
typedef struct _nty_coroutine_queue nty_coroutine_queue;
|
|
||||||
|
|
||||||
typedef struct _nty_coroutine_rbtree_sleep nty_coroutine_rbtree_sleep;
|
|
||||||
typedef struct _nty_coroutine_rbtree_wait nty_coroutine_rbtree_wait;
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _USE_UCONTEXT
|
|
||||||
typedef struct _nty_cpu_ctx {
|
|
||||||
void *esp; //
|
|
||||||
void *ebp;
|
|
||||||
void *eip;
|
|
||||||
void *edi;
|
|
||||||
void *esi;
|
|
||||||
void *ebx;
|
|
||||||
void *r1;
|
|
||||||
void *r2;
|
|
||||||
void *r3;
|
|
||||||
void *r4;
|
|
||||||
void *r5;
|
|
||||||
} nty_cpu_ctx;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///
|
|
||||||
typedef struct _nty_schedule {
|
|
||||||
uint64_t birth;
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
ucontext_t ctx;
|
|
||||||
#else
|
|
||||||
nty_cpu_ctx ctx;
|
|
||||||
#endif
|
|
||||||
void *stack;
|
|
||||||
size_t stack_size;
|
|
||||||
int spawned_coroutines;
|
|
||||||
uint64_t default_timeout;
|
|
||||||
struct _nty_coroutine *curr_thread;
|
|
||||||
int page_size;
|
|
||||||
|
|
||||||
int poller_fd;
|
|
||||||
int eventfd;
|
|
||||||
struct epoll_event eventlist[NTY_CO_MAX_EVENTS];
|
|
||||||
int nevents;
|
|
||||||
|
|
||||||
int num_new_events;
|
|
||||||
pthread_mutex_t defer_mutex;
|
|
||||||
|
|
||||||
nty_coroutine_queue ready;
|
|
||||||
nty_coroutine_queue defer;
|
|
||||||
|
|
||||||
nty_coroutine_link busy;
|
|
||||||
|
|
||||||
nty_coroutine_rbtree_sleep sleeping;
|
|
||||||
nty_coroutine_rbtree_wait waiting;
|
|
||||||
|
|
||||||
//private
|
|
||||||
|
|
||||||
} nty_schedule;
|
|
||||||
|
|
||||||
typedef struct _nty_coroutine {
|
|
||||||
|
|
||||||
//private
|
|
||||||
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
ucontext_t ctx;
|
|
||||||
#else
|
|
||||||
nty_cpu_ctx ctx;
|
|
||||||
#endif
|
|
||||||
proc_coroutine func;
|
|
||||||
void *arg;
|
|
||||||
void *data;
|
|
||||||
size_t stack_size;
|
|
||||||
size_t last_stack_size;
|
|
||||||
|
|
||||||
nty_coroutine_status status;
|
|
||||||
nty_schedule *sched;
|
|
||||||
|
|
||||||
uint64_t birth;
|
|
||||||
uint64_t id;
|
|
||||||
#if CANCEL_FD_WAIT_UINT64
|
|
||||||
int fd;
|
|
||||||
unsigned short events; //POLL_EVENT
|
|
||||||
#else
|
|
||||||
int64_t fd_wait;
|
|
||||||
#endif
|
|
||||||
char funcname[64];
|
|
||||||
struct _nty_coroutine *co_join;
|
|
||||||
|
|
||||||
void **co_exit_ptr;
|
|
||||||
void *stack;
|
|
||||||
void *ebp;
|
|
||||||
uint32_t ops;
|
|
||||||
uint64_t sleep_usecs;
|
|
||||||
|
|
||||||
RB_ENTRY(_nty_coroutine) sleep_node;
|
|
||||||
RB_ENTRY(_nty_coroutine) wait_node;
|
|
||||||
|
|
||||||
LIST_ENTRY(_nty_coroutine) busy_next;
|
|
||||||
|
|
||||||
TAILQ_ENTRY(_nty_coroutine) ready_next;
|
|
||||||
TAILQ_ENTRY(_nty_coroutine) defer_next;
|
|
||||||
TAILQ_ENTRY(_nty_coroutine) cond_next;
|
|
||||||
|
|
||||||
TAILQ_ENTRY(_nty_coroutine) io_next;
|
|
||||||
TAILQ_ENTRY(_nty_coroutine) compute_next;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
void *buf;
|
|
||||||
size_t nbytes;
|
|
||||||
int fd;
|
|
||||||
int ret;
|
|
||||||
int err;
|
|
||||||
} io;
|
|
||||||
|
|
||||||
struct _nty_coroutine_compute_sched *compute_sched;
|
|
||||||
int ready_fds;
|
|
||||||
struct pollfd *pfds;
|
|
||||||
nfds_t nfds;
|
|
||||||
} nty_coroutine;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct _nty_coroutine_compute_sched {
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
ucontext_t ctx;
|
|
||||||
#else
|
|
||||||
nty_cpu_ctx ctx;
|
|
||||||
#endif
|
|
||||||
nty_coroutine_queue coroutines;
|
|
||||||
|
|
||||||
nty_coroutine *curr_coroutine;
|
|
||||||
|
|
||||||
pthread_mutex_t run_mutex;
|
|
||||||
pthread_cond_t run_cond;
|
|
||||||
|
|
||||||
pthread_mutex_t co_mutex;
|
|
||||||
LIST_ENTRY(_nty_coroutine_compute_sched) compute_next;
|
|
||||||
|
|
||||||
nty_coroutine_compute_status compute_status;
|
|
||||||
} nty_coroutine_compute_sched;
|
|
||||||
|
|
||||||
extern pthread_key_t global_sched_key;
|
|
||||||
static inline nty_schedule *nty_coroutine_get_sched(void) {
|
|
||||||
return pthread_getspecific(global_sched_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint64_t nty_coroutine_diff_usecs(uint64_t t1, uint64_t t2) {
|
|
||||||
return t2-t1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint64_t nty_coroutine_usec_now(void) {
|
|
||||||
struct timeval t1 = {0, 0};
|
|
||||||
gettimeofday(&t1, NULL);
|
|
||||||
|
|
||||||
return t1.tv_sec * 1000000 + t1.tv_usec;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int nty_epoller_create(void);
|
|
||||||
|
|
||||||
|
|
||||||
void nty_schedule_cancel_event(nty_coroutine *co);
|
|
||||||
void nty_schedule_sched_event(nty_coroutine *co, int fd, nty_coroutine_event e, uint64_t timeout);
|
|
||||||
|
|
||||||
void nty_schedule_desched_sleepdown(nty_coroutine *co);
|
|
||||||
void nty_schedule_sched_sleepdown(nty_coroutine *co, uint64_t msecs);
|
|
||||||
|
|
||||||
nty_coroutine* nty_schedule_desched_wait(int fd);
|
|
||||||
void nty_schedule_sched_wait(nty_coroutine *co, int fd, unsigned short events, uint64_t timeout);
|
|
||||||
|
|
||||||
void nty_schedule_run(void);
|
|
||||||
|
|
||||||
int nty_epoller_ev_register_trigger(void);
|
|
||||||
int nty_epoller_wait(struct timespec t);
|
|
||||||
int nty_coroutine_resume(nty_coroutine *co);
|
|
||||||
void nty_coroutine_free(nty_coroutine *co);
|
|
||||||
int nty_coroutine_create(nty_coroutine **new_co, proc_coroutine func, void *arg);
|
|
||||||
void nty_coroutine_yield(nty_coroutine *co);
|
|
||||||
|
|
||||||
void nty_coroutine_sleep(uint64_t msecs);
|
|
||||||
|
|
||||||
|
|
||||||
int nty_socket(int domain, int type, int protocol);
|
|
||||||
int nty_accept(int fd, struct sockaddr *addr, socklen_t *len);
|
|
||||||
ssize_t nty_recv(int fd, void *buf, size_t len, int flags);
|
|
||||||
ssize_t nty_send(int fd, const void *buf, size_t len, int flags);
|
|
||||||
int nty_close(int fd);
|
|
||||||
int nty_poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
|
||||||
int nty_connect(int fd, struct sockaddr *name, socklen_t namelen);
|
|
||||||
|
|
||||||
ssize_t nty_sendto(int fd, const void *buf, size_t len, int flags,
|
|
||||||
const struct sockaddr *dest_addr, socklen_t addrlen);
|
|
||||||
ssize_t nty_recvfrom(int fd, void *buf, size_t len, int flags,
|
|
||||||
struct sockaddr *src_addr, socklen_t *addrlen);
|
|
||||||
|
|
||||||
|
|
||||||
#define COROUTINE_HOOK
|
|
||||||
|
|
||||||
#ifdef COROUTINE_HOOK
|
|
||||||
|
|
||||||
|
|
||||||
typedef int (*socket_t)(int domain, int type, int protocol);
|
|
||||||
extern socket_t socket_f;
|
|
||||||
|
|
||||||
typedef int(*connect_t)(int, const struct sockaddr *, socklen_t);
|
|
||||||
extern connect_t connect_f;
|
|
||||||
|
|
||||||
typedef ssize_t(*read_t)(int, void *, size_t);
|
|
||||||
extern read_t read_f;
|
|
||||||
|
|
||||||
|
|
||||||
typedef ssize_t(*recv_t)(int sockfd, void *buf, size_t len, int flags);
|
|
||||||
extern recv_t recv_f;
|
|
||||||
|
|
||||||
typedef ssize_t(*recvfrom_t)(int sockfd, void *buf, size_t len, int flags,
|
|
||||||
struct sockaddr *src_addr, socklen_t *addrlen);
|
|
||||||
extern recvfrom_t recvfrom_f;
|
|
||||||
|
|
||||||
typedef ssize_t(*write_t)(int, const void *, size_t);
|
|
||||||
extern write_t write_f;
|
|
||||||
|
|
||||||
typedef ssize_t(*send_t)(int sockfd, const void *buf, size_t len, int flags);
|
|
||||||
extern send_t send_f;
|
|
||||||
|
|
||||||
typedef ssize_t(*sendto_t)(int sockfd, const void *buf, size_t len, int flags,
|
|
||||||
const struct sockaddr *dest_addr, socklen_t addrlen);
|
|
||||||
extern sendto_t sendto_f;
|
|
||||||
|
|
||||||
typedef int(*accept_t)(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
|
|
||||||
extern accept_t accept_f;
|
|
||||||
|
|
||||||
// new-syscall
|
|
||||||
typedef int(*close_t)(int);
|
|
||||||
extern close_t close_f;
|
|
||||||
|
|
||||||
|
|
||||||
int init_hook(void);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
typedef int(*fcntl_t)(int __fd, int __cmd, ...);
|
|
||||||
extern fcntl_t fcntl_f;
|
|
||||||
|
|
||||||
typedef int (*getsockopt_t)(int sockfd, int level, int optname,
|
|
||||||
void *optval, socklen_t *optlen);
|
|
||||||
extern getsockopt_t getsockopt_f;
|
|
||||||
|
|
||||||
typedef int (*setsockopt_t)(int sockfd, int level, int optname,
|
|
||||||
const void *optval, socklen_t optlen);
|
|
||||||
extern setsockopt_t setsockopt_f;
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/eventfd.h>
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
|
|
||||||
int nty_epoller_create(void) {
|
|
||||||
return epoll_create(1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
int nty_epoller_wait(struct timespec t) {
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
return epoll_wait(sched->poller_fd, sched->eventlist, NTY_CO_MAX_EVENTS, t.tv_sec*1000.0 + t.tv_nsec/1000000.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int nty_epoller_ev_register_trigger(void) {
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
|
|
||||||
if (!sched->eventfd) {
|
|
||||||
sched->eventfd = eventfd(0, EFD_NONBLOCK);
|
|
||||||
assert(sched->eventfd != -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct epoll_event ev;
|
|
||||||
ev.events = EPOLLIN;
|
|
||||||
ev.data.fd = sched->eventfd;
|
|
||||||
int ret = epoll_ctl(sched->poller_fd, EPOLL_CTL_ADD, sched->eventfd, &ev);
|
|
||||||
|
|
||||||
assert(ret != -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,589 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __NTY_QUEUE_H__
|
|
||||||
#define __NTY_QUEUE_H__
|
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
|
||||||
|
|
||||||
#ifdef QUEUE_MACRO_DEBUG
|
|
||||||
/* Store the last 2 places the queue element or head was altered */
|
|
||||||
struct qm_trace {
|
|
||||||
char * lastfile;
|
|
||||||
int lastline;
|
|
||||||
char * prevfile;
|
|
||||||
int prevline;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define TRACEBUF struct qm_trace trace;
|
|
||||||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
|
|
||||||
|
|
||||||
#define QMD_TRACE_HEAD(head) do { \
|
|
||||||
(head)->trace.prevline = (head)->trace.lastline; \
|
|
||||||
(head)->trace.prevfile = (head)->trace.lastfile; \
|
|
||||||
(head)->trace.lastline = __LINE__; \
|
|
||||||
(head)->trace.lastfile = __FILE__; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define QMD_TRACE_ELEM(elem) do { \
|
|
||||||
(elem)->trace.prevline = (elem)->trace.lastline; \
|
|
||||||
(elem)->trace.prevfile = (elem)->trace.lastfile; \
|
|
||||||
(elem)->trace.lastline = __LINE__; \
|
|
||||||
(elem)->trace.lastfile = __FILE__; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define QMD_TRACE_ELEM(elem)
|
|
||||||
#define QMD_TRACE_HEAD(head)
|
|
||||||
#define TRACEBUF
|
|
||||||
#define TRASHIT(x)
|
|
||||||
#endif /* QUEUE_MACRO_DEBUG */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List declarations.
|
|
||||||
*/
|
|
||||||
#define SLIST_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *slh_first; /* first element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SLIST_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define SLIST_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *sle_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List functions.
|
|
||||||
*/
|
|
||||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
|
||||||
|
|
||||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
|
||||||
|
|
||||||
#define SLIST_FOREACH(var, head, field) \
|
|
||||||
for ((var) = SLIST_FIRST((head)); \
|
|
||||||
(var); \
|
|
||||||
(var) = SLIST_NEXT((var), field))
|
|
||||||
|
|
||||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = SLIST_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
|
|
||||||
for ((varp) = &SLIST_FIRST((head)); \
|
|
||||||
((var) = *(varp)) != NULL; \
|
|
||||||
(varp) = &SLIST_NEXT((var), field))
|
|
||||||
|
|
||||||
#define SLIST_INIT(head) do { \
|
|
||||||
SLIST_FIRST((head)) = NULL; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
|
||||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
|
||||||
SLIST_NEXT((slistelm), field) = (elm); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
|
||||||
SLIST_FIRST((head)) = (elm); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
|
||||||
if (SLIST_FIRST((head)) == (elm)) { \
|
|
||||||
SLIST_REMOVE_HEAD((head), field); \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
struct type *curelm = SLIST_FIRST((head)); \
|
|
||||||
while (SLIST_NEXT(curelm, field) != (elm)) \
|
|
||||||
curelm = SLIST_NEXT(curelm, field); \
|
|
||||||
SLIST_REMOVE_AFTER(curelm, field); \
|
|
||||||
} \
|
|
||||||
TRASHIT((elm)->field.sle_next); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
|
||||||
SLIST_NEXT(elm, field) = \
|
|
||||||
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
|
||||||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue declarations.
|
|
||||||
*/
|
|
||||||
#define STAILQ_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *stqh_first;/* first element */ \
|
|
||||||
struct type **stqh_last;/* addr of last next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL, &(head).stqh_first }
|
|
||||||
|
|
||||||
#define STAILQ_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *stqe_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue functions.
|
|
||||||
*/
|
|
||||||
#define STAILQ_CONCAT(head1, head2) do { \
|
|
||||||
if (!STAILQ_EMPTY((head2))) { \
|
|
||||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
|
||||||
(head1)->stqh_last = (head2)->stqh_last; \
|
|
||||||
STAILQ_INIT((head2)); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
|
||||||
|
|
||||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH(var, head, field) \
|
|
||||||
for((var) = STAILQ_FIRST((head)); \
|
|
||||||
(var); \
|
|
||||||
(var) = STAILQ_NEXT((var), field))
|
|
||||||
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = STAILQ_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define STAILQ_INIT(head) do { \
|
|
||||||
STAILQ_FIRST((head)) = NULL; \
|
|
||||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
|
|
||||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
|
|
||||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
|
||||||
STAILQ_NEXT((tqelm), field) = (elm); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
|
||||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
|
||||||
STAILQ_FIRST((head)) = (elm); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
STAILQ_NEXT((elm), field) = NULL; \
|
|
||||||
*(head)->stqh_last = (elm); \
|
|
||||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_LAST(head, type, field) \
|
|
||||||
(STAILQ_EMPTY((head)) ? \
|
|
||||||
NULL : \
|
|
||||||
((struct type *)(void *) \
|
|
||||||
((char *)((head)->stqh_last) - __offsetof(struct type, field))))
|
|
||||||
|
|
||||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
|
||||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
|
||||||
STAILQ_REMOVE_HEAD((head), field); \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
struct type *curelm = STAILQ_FIRST((head)); \
|
|
||||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
|
||||||
curelm = STAILQ_NEXT(curelm, field); \
|
|
||||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
|
||||||
} \
|
|
||||||
TRASHIT((elm)->field.stqe_next); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
|
||||||
if ((STAILQ_FIRST((head)) = \
|
|
||||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
|
||||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
|
||||||
if ((STAILQ_NEXT(elm, field) = \
|
|
||||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
|
||||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define STAILQ_SWAP(head1, head2, type) do { \
|
|
||||||
struct type *swap_first = STAILQ_FIRST(head1); \
|
|
||||||
struct type **swap_last = (head1)->stqh_last; \
|
|
||||||
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
|
|
||||||
(head1)->stqh_last = (head2)->stqh_last; \
|
|
||||||
STAILQ_FIRST(head2) = swap_first; \
|
|
||||||
(head2)->stqh_last = swap_last; \
|
|
||||||
if (STAILQ_EMPTY(head1)) \
|
|
||||||
(head1)->stqh_last = &STAILQ_FIRST(head1); \
|
|
||||||
if (STAILQ_EMPTY(head2)) \
|
|
||||||
(head2)->stqh_last = &STAILQ_FIRST(head2); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List declarations.
|
|
||||||
*/
|
|
||||||
#define LIST_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *lh_first; /* first element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LIST_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define LIST_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *le_next; /* next element */ \
|
|
||||||
struct type **le_prev; /* address of previous next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List functions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if (defined(_KERNEL) && defined(INVARIANTS))
|
|
||||||
#define QMD_LIST_CHECK_HEAD(head, field) do { \
|
|
||||||
if (LIST_FIRST((head)) != NULL && \
|
|
||||||
LIST_FIRST((head))->field.le_prev != \
|
|
||||||
&LIST_FIRST((head))) \
|
|
||||||
panic("Bad list head %p first->prev != head", (head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define QMD_LIST_CHECK_NEXT(elm, field) do { \
|
|
||||||
if (LIST_NEXT((elm), field) != NULL && \
|
|
||||||
LIST_NEXT((elm), field)->field.le_prev != \
|
|
||||||
&((elm)->field.le_next)) \
|
|
||||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define QMD_LIST_CHECK_PREV(elm, field) do { \
|
|
||||||
if (*(elm)->field.le_prev != (elm)) \
|
|
||||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
|
||||||
} while (0)
|
|
||||||
#else
|
|
||||||
#define QMD_LIST_CHECK_HEAD(head, field)
|
|
||||||
#define QMD_LIST_CHECK_NEXT(elm, field)
|
|
||||||
#define QMD_LIST_CHECK_PREV(elm, field)
|
|
||||||
#endif /* (_KERNEL && INVARIANTS) */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
|
||||||
|
|
||||||
#define LIST_FIRST(head) ((head)->lh_first)
|
|
||||||
|
|
||||||
#define LIST_FOREACH(var, head, field) \
|
|
||||||
for ((var) = LIST_FIRST((head)); \
|
|
||||||
(var); \
|
|
||||||
(var) = LIST_NEXT((var), field))
|
|
||||||
|
|
||||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = LIST_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define LIST_INIT(head) do { \
|
|
||||||
LIST_FIRST((head)) = NULL; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
|
||||||
QMD_LIST_CHECK_NEXT(listelm, field); \
|
|
||||||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
|
|
||||||
LIST_NEXT((listelm), field)->field.le_prev = \
|
|
||||||
&LIST_NEXT((elm), field); \
|
|
||||||
LIST_NEXT((listelm), field) = (elm); \
|
|
||||||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
|
||||||
QMD_LIST_CHECK_PREV(listelm, field); \
|
|
||||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
|
||||||
LIST_NEXT((elm), field) = (listelm); \
|
|
||||||
*(listelm)->field.le_prev = (elm); \
|
|
||||||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
QMD_LIST_CHECK_HEAD((head), field); \
|
|
||||||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
|
|
||||||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
|
|
||||||
LIST_FIRST((head)) = (elm); \
|
|
||||||
(elm)->field.le_prev = &LIST_FIRST((head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
|
||||||
|
|
||||||
#define LIST_REMOVE(elm, field) do { \
|
|
||||||
QMD_LIST_CHECK_NEXT(elm, field); \
|
|
||||||
QMD_LIST_CHECK_PREV(elm, field); \
|
|
||||||
if (LIST_NEXT((elm), field) != NULL) \
|
|
||||||
LIST_NEXT((elm), field)->field.le_prev = \
|
|
||||||
(elm)->field.le_prev; \
|
|
||||||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
|
|
||||||
TRASHIT((elm)->field.le_next); \
|
|
||||||
TRASHIT((elm)->field.le_prev); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LIST_SWAP(head1, head2, type, field) do { \
|
|
||||||
struct type *swap_tmp = LIST_FIRST((head1)); \
|
|
||||||
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
|
|
||||||
LIST_FIRST((head2)) = swap_tmp; \
|
|
||||||
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
|
|
||||||
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
|
|
||||||
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
|
|
||||||
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue declarations.
|
|
||||||
*/
|
|
||||||
#define TAILQ_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *tqh_first; /* first element */ \
|
|
||||||
struct type **tqh_last; /* addr of last next element */ \
|
|
||||||
TRACEBUF \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL, &(head).tqh_first }
|
|
||||||
|
|
||||||
#define TAILQ_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *tqe_next; /* next element */ \
|
|
||||||
struct type **tqe_prev; /* address of previous next element */ \
|
|
||||||
TRACEBUF \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue functions.
|
|
||||||
*/
|
|
||||||
#if (defined(_KERNEL) && defined(INVARIANTS))
|
|
||||||
#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
|
|
||||||
if (!TAILQ_EMPTY(head) && \
|
|
||||||
TAILQ_FIRST((head))->field.tqe_prev != \
|
|
||||||
&TAILQ_FIRST((head))) \
|
|
||||||
panic("Bad tailq head %p first->prev != head", (head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
|
|
||||||
if (*(head)->tqh_last != NULL) \
|
|
||||||
panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
|
|
||||||
if (TAILQ_NEXT((elm), field) != NULL && \
|
|
||||||
TAILQ_NEXT((elm), field)->field.tqe_prev != \
|
|
||||||
&((elm)->field.tqe_next)) \
|
|
||||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
|
|
||||||
if (*(elm)->field.tqe_prev != (elm)) \
|
|
||||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
|
||||||
} while (0)
|
|
||||||
#else
|
|
||||||
#define QMD_TAILQ_CHECK_HEAD(head, field)
|
|
||||||
#define QMD_TAILQ_CHECK_TAIL(head, headname)
|
|
||||||
#define QMD_TAILQ_CHECK_NEXT(elm, field)
|
|
||||||
#define QMD_TAILQ_CHECK_PREV(elm, field)
|
|
||||||
#endif /* (_KERNEL && INVARIANTS) */
|
|
||||||
|
|
||||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
|
||||||
if (!TAILQ_EMPTY(head2)) { \
|
|
||||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
|
||||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
|
||||||
(head1)->tqh_last = (head2)->tqh_last; \
|
|
||||||
TAILQ_INIT((head2)); \
|
|
||||||
QMD_TRACE_HEAD(head1); \
|
|
||||||
QMD_TRACE_HEAD(head2); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
|
||||||
|
|
||||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = TAILQ_FIRST((head)); \
|
|
||||||
(var); \
|
|
||||||
(var) = TAILQ_NEXT((var), field))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = TAILQ_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
|
||||||
for ((var) = TAILQ_LAST((head), headname); \
|
|
||||||
(var); \
|
|
||||||
(var) = TAILQ_PREV((var), headname, field))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
|
||||||
for ((var) = TAILQ_LAST((head), headname); \
|
|
||||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define TAILQ_INIT(head) do { \
|
|
||||||
TAILQ_FIRST((head)) = NULL; \
|
|
||||||
(head)->tqh_last = &TAILQ_FIRST((head)); \
|
|
||||||
QMD_TRACE_HEAD(head); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
QMD_TAILQ_CHECK_NEXT(listelm, field); \
|
|
||||||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
|
|
||||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
|
||||||
&TAILQ_NEXT((elm), field); \
|
|
||||||
else { \
|
|
||||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
|
||||||
QMD_TRACE_HEAD(head); \
|
|
||||||
} \
|
|
||||||
TAILQ_NEXT((listelm), field) = (elm); \
|
|
||||||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
|
|
||||||
QMD_TRACE_ELEM(&(elm)->field); \
|
|
||||||
QMD_TRACE_ELEM(&listelm->field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
|
||||||
QMD_TAILQ_CHECK_PREV(listelm, field); \
|
|
||||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
|
||||||
TAILQ_NEXT((elm), field) = (listelm); \
|
|
||||||
*(listelm)->field.tqe_prev = (elm); \
|
|
||||||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
|
|
||||||
QMD_TRACE_ELEM(&(elm)->field); \
|
|
||||||
QMD_TRACE_ELEM(&listelm->field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
QMD_TAILQ_CHECK_HEAD(head, field); \
|
|
||||||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
|
|
||||||
TAILQ_FIRST((head))->field.tqe_prev = \
|
|
||||||
&TAILQ_NEXT((elm), field); \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
|
||||||
TAILQ_FIRST((head)) = (elm); \
|
|
||||||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
|
|
||||||
QMD_TRACE_HEAD(head); \
|
|
||||||
QMD_TRACE_ELEM(&(elm)->field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
QMD_TAILQ_CHECK_TAIL(head, field); \
|
|
||||||
TAILQ_NEXT((elm), field) = NULL; \
|
|
||||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
|
||||||
*(head)->tqh_last = (elm); \
|
|
||||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
|
||||||
QMD_TRACE_HEAD(head); \
|
|
||||||
QMD_TRACE_ELEM(&(elm)->field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_LAST(head, headname) \
|
|
||||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
|
||||||
|
|
||||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
|
||||||
|
|
||||||
#define TAILQ_PREV(elm, headname, field) \
|
|
||||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
|
||||||
|
|
||||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
|
||||||
QMD_TAILQ_CHECK_NEXT(elm, field); \
|
|
||||||
QMD_TAILQ_CHECK_PREV(elm, field); \
|
|
||||||
if ((TAILQ_NEXT((elm), field)) != NULL) \
|
|
||||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
|
||||||
(elm)->field.tqe_prev; \
|
|
||||||
else { \
|
|
||||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
|
||||||
QMD_TRACE_HEAD(head); \
|
|
||||||
} \
|
|
||||||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
|
|
||||||
TRASHIT((elm)->field.tqe_next); \
|
|
||||||
TRASHIT((elm)->field.tqe_prev); \
|
|
||||||
QMD_TRACE_ELEM(&(elm)->field); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TAILQ_SWAP(head1, head2, type, field) do { \
|
|
||||||
struct type *swap_first = (head1)->tqh_first; \
|
|
||||||
struct type **swap_last = (head1)->tqh_last; \
|
|
||||||
(head1)->tqh_first = (head2)->tqh_first; \
|
|
||||||
(head1)->tqh_last = (head2)->tqh_last; \
|
|
||||||
(head2)->tqh_first = swap_first; \
|
|
||||||
(head2)->tqh_last = swap_last; \
|
|
||||||
if ((swap_first = (head1)->tqh_first) != NULL) \
|
|
||||||
swap_first->field.tqe_prev = &(head1)->tqh_first; \
|
|
||||||
else \
|
|
||||||
(head1)->tqh_last = &(head1)->tqh_first; \
|
|
||||||
if ((swap_first = (head2)->tqh_first) != NULL) \
|
|
||||||
swap_first->field.tqe_prev = &(head2)->tqh_first; \
|
|
||||||
else \
|
|
||||||
(head2)->tqh_last = &(head2)->tqh_first; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,369 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define FD_KEY(f,e) (((int64_t)(f) << (sizeof(int32_t) * 8)) | e)
|
|
||||||
#define FD_EVENT(f) ((int32_t)(f))
|
|
||||||
#define FD_ONLY(f) ((f) >> ((sizeof(int32_t) * 8)))
|
|
||||||
|
|
||||||
|
|
||||||
static inline int nty_coroutine_sleep_cmp(nty_coroutine *co1, nty_coroutine *co2) {
|
|
||||||
if (co1->sleep_usecs < co2->sleep_usecs) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (co1->sleep_usecs == co2->sleep_usecs) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int nty_coroutine_wait_cmp(nty_coroutine *co1, nty_coroutine *co2) {
|
|
||||||
#if CANCEL_FD_WAIT_UINT64
|
|
||||||
if (co1->fd < co2->fd) return -1;
|
|
||||||
else if (co1->fd == co2->fd) return 0;
|
|
||||||
else return 1;
|
|
||||||
#else
|
|
||||||
if (co1->fd_wait < co2->fd_wait) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (co1->fd_wait == co2->fd_wait) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RB_GENERATE(_nty_coroutine_rbtree_sleep, _nty_coroutine, sleep_node, nty_coroutine_sleep_cmp);
|
|
||||||
RB_GENERATE(_nty_coroutine_rbtree_wait, _nty_coroutine, wait_node, nty_coroutine_wait_cmp);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void nty_schedule_sched_sleepdown(nty_coroutine *co, uint64_t msecs) {
|
|
||||||
uint64_t usecs = msecs * 1000u;
|
|
||||||
|
|
||||||
nty_coroutine *co_tmp = RB_FIND(_nty_coroutine_rbtree_sleep, &co->sched->sleeping, co);
|
|
||||||
if (co_tmp != NULL) {
|
|
||||||
RB_REMOVE(_nty_coroutine_rbtree_sleep, &co->sched->sleeping, co_tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
co->sleep_usecs = nty_coroutine_diff_usecs(co->sched->birth, nty_coroutine_usec_now()) + usecs;
|
|
||||||
|
|
||||||
while (msecs) {
|
|
||||||
co_tmp = RB_INSERT(_nty_coroutine_rbtree_sleep, &co->sched->sleeping, co);
|
|
||||||
if (co_tmp) {
|
|
||||||
printf("1111 sleep_usecs %"PRIu64"\n", co->sleep_usecs);
|
|
||||||
co->sleep_usecs ++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
co->status |= BIT(NTY_COROUTINE_STATUS_SLEEPING);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//yield
|
|
||||||
}
|
|
||||||
|
|
||||||
void nty_schedule_desched_sleepdown(nty_coroutine *co) {
|
|
||||||
if (co->status & BIT(NTY_COROUTINE_STATUS_SLEEPING)) {
|
|
||||||
RB_REMOVE(_nty_coroutine_rbtree_sleep, &co->sched->sleeping, co);
|
|
||||||
|
|
||||||
co->status &= CLEARBIT(NTY_COROUTINE_STATUS_SLEEPING);
|
|
||||||
co->status |= BIT(NTY_COROUTINE_STATUS_READY);
|
|
||||||
co->status &= CLEARBIT(NTY_COROUTINE_STATUS_EXPIRED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_coroutine *nty_schedule_search_wait(int fd) {
|
|
||||||
nty_coroutine find_it = {0};
|
|
||||||
find_it.fd = fd;
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
|
|
||||||
nty_coroutine *co = RB_FIND(_nty_coroutine_rbtree_wait, &sched->waiting, &find_it);
|
|
||||||
co->status = 0;
|
|
||||||
|
|
||||||
return co;
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_coroutine* nty_schedule_desched_wait(int fd) {
|
|
||||||
|
|
||||||
nty_coroutine find_it = {0};
|
|
||||||
find_it.fd = fd;
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
|
|
||||||
nty_coroutine *co = RB_FIND(_nty_coroutine_rbtree_wait, &sched->waiting, &find_it);
|
|
||||||
if (co != NULL) {
|
|
||||||
RB_REMOVE(_nty_coroutine_rbtree_wait, &co->sched->waiting, co);
|
|
||||||
}
|
|
||||||
co->status = 0;
|
|
||||||
nty_schedule_desched_sleepdown(co);
|
|
||||||
|
|
||||||
return co;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nty_schedule_sched_wait(nty_coroutine *co, int fd, unsigned short events, uint64_t timeout) {
|
|
||||||
|
|
||||||
if (co->status & BIT(NTY_COROUTINE_STATUS_WAIT_READ) ||
|
|
||||||
co->status & BIT(NTY_COROUTINE_STATUS_WAIT_WRITE)) {
|
|
||||||
printf("Unexpected event. lt id %"PRIu64" fd %"PRId32" already in %"PRId32" state\n",
|
|
||||||
co->id, co->fd, co->status);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (events & POLLIN) {
|
|
||||||
co->status |= NTY_COROUTINE_STATUS_WAIT_READ;
|
|
||||||
} else if (events & POLLOUT) {
|
|
||||||
co->status |= NTY_COROUTINE_STATUS_WAIT_WRITE;
|
|
||||||
} else {
|
|
||||||
printf("events : %d\n", events);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
co->fd = fd;
|
|
||||||
co->events = events;
|
|
||||||
nty_coroutine *co_tmp = RB_INSERT(_nty_coroutine_rbtree_wait, &co->sched->waiting, co);
|
|
||||||
|
|
||||||
assert(co_tmp == NULL);
|
|
||||||
|
|
||||||
//printf("timeout --> %"PRIu64"\n", timeout);
|
|
||||||
if (timeout == 1) return ; //Error
|
|
||||||
|
|
||||||
nty_schedule_sched_sleepdown(co, timeout);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void nty_schedule_cancel_wait(nty_coroutine *co) {
|
|
||||||
RB_REMOVE(_nty_coroutine_rbtree_wait, &co->sched->waiting, co);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nty_schedule_free(nty_schedule *sched) {
|
|
||||||
if (sched->poller_fd > 0) {
|
|
||||||
close(sched->poller_fd);
|
|
||||||
}
|
|
||||||
if (sched->eventfd > 0) {
|
|
||||||
close(sched->eventfd);
|
|
||||||
}
|
|
||||||
if (sched->stack != NULL) {
|
|
||||||
free(sched->stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(sched);
|
|
||||||
|
|
||||||
assert(pthread_setspecific(global_sched_key, NULL) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int nty_schedule_create(int stack_size) {
|
|
||||||
|
|
||||||
int sched_stack_size = stack_size ? stack_size : NTY_CO_MAX_STACKSIZE;
|
|
||||||
|
|
||||||
nty_schedule *sched = (nty_schedule*)calloc(1, sizeof(nty_schedule));
|
|
||||||
if (sched == NULL) {
|
|
||||||
printf("Failed to initialize scheduler\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(pthread_setspecific(global_sched_key, sched) == 0);
|
|
||||||
|
|
||||||
sched->poller_fd = nty_epoller_create();
|
|
||||||
if (sched->poller_fd == -1) {
|
|
||||||
printf("Failed to initialize epoller\n");
|
|
||||||
nty_schedule_free(sched);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_epoller_ev_register_trigger();
|
|
||||||
|
|
||||||
sched->stack_size = sched_stack_size;
|
|
||||||
sched->page_size = getpagesize();
|
|
||||||
|
|
||||||
#ifdef _USE_UCONTEXT
|
|
||||||
int ret = posix_memalign(&sched->stack, sched->page_size, sched->stack_size);
|
|
||||||
assert(ret == 0);
|
|
||||||
#else
|
|
||||||
sched->stack = NULL;
|
|
||||||
bzero(&sched->ctx, sizeof(nty_cpu_ctx));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sched->spawned_coroutines = 0;
|
|
||||||
sched->default_timeout = 3000000u;
|
|
||||||
|
|
||||||
RB_INIT(&sched->sleeping);
|
|
||||||
RB_INIT(&sched->waiting);
|
|
||||||
|
|
||||||
sched->birth = nty_coroutine_usec_now();
|
|
||||||
|
|
||||||
TAILQ_INIT(&sched->ready);
|
|
||||||
TAILQ_INIT(&sched->defer);
|
|
||||||
LIST_INIT(&sched->busy);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static nty_coroutine *nty_schedule_expired(nty_schedule *sched) {
|
|
||||||
|
|
||||||
uint64_t t_diff_usecs = nty_coroutine_diff_usecs(sched->birth, nty_coroutine_usec_now());
|
|
||||||
nty_coroutine *co = RB_MIN(_nty_coroutine_rbtree_sleep, &sched->sleeping);
|
|
||||||
if (co == NULL) return NULL;
|
|
||||||
|
|
||||||
if (co->sleep_usecs <= t_diff_usecs) {
|
|
||||||
RB_REMOVE(_nty_coroutine_rbtree_sleep, &co->sched->sleeping, co);
|
|
||||||
return co;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int nty_schedule_isdone(nty_schedule *sched) {
|
|
||||||
return (RB_EMPTY(&sched->waiting) &&
|
|
||||||
LIST_EMPTY(&sched->busy) &&
|
|
||||||
RB_EMPTY(&sched->sleeping) &&
|
|
||||||
TAILQ_EMPTY(&sched->ready));
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t nty_schedule_min_timeout(nty_schedule *sched) {
|
|
||||||
uint64_t t_diff_usecs = nty_coroutine_diff_usecs(sched->birth, nty_coroutine_usec_now());
|
|
||||||
uint64_t min = sched->default_timeout;
|
|
||||||
|
|
||||||
nty_coroutine *co = RB_MIN(_nty_coroutine_rbtree_sleep, &sched->sleeping);
|
|
||||||
if (!co) return min;
|
|
||||||
|
|
||||||
min = co->sleep_usecs;
|
|
||||||
if (min > t_diff_usecs) {
|
|
||||||
return min - t_diff_usecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nty_schedule_epoll(nty_schedule *sched) {
|
|
||||||
|
|
||||||
sched->num_new_events = 0;
|
|
||||||
|
|
||||||
struct timespec t = {0, 0};
|
|
||||||
uint64_t usecs = nty_schedule_min_timeout(sched);
|
|
||||||
if (usecs && TAILQ_EMPTY(&sched->ready)) {
|
|
||||||
t.tv_sec = usecs / 1000000u;
|
|
||||||
if (t.tv_sec != 0) {
|
|
||||||
t.tv_nsec = (usecs % 1000u) * 1000u;
|
|
||||||
} else {
|
|
||||||
t.tv_nsec = usecs * 1000u;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nready = 0;
|
|
||||||
while (1) {
|
|
||||||
nready = nty_epoller_wait(t);
|
|
||||||
if (nready == -1) {
|
|
||||||
if (errno == EINTR) continue;
|
|
||||||
else assert(0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sched->nevents = 0;
|
|
||||||
sched->num_new_events = nready;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nty_schedule_run(void) {
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) return ;
|
|
||||||
|
|
||||||
while (!nty_schedule_isdone(sched)) {
|
|
||||||
|
|
||||||
// 1. expired --> sleep rbtree
|
|
||||||
nty_coroutine *expired = NULL;
|
|
||||||
while ((expired = nty_schedule_expired(sched)) != NULL) {
|
|
||||||
nty_coroutine_resume(expired);
|
|
||||||
}
|
|
||||||
// 2. ready queue
|
|
||||||
nty_coroutine *last_co_ready = TAILQ_LAST(&sched->ready, _nty_coroutine_queue);
|
|
||||||
while (!TAILQ_EMPTY(&sched->ready)) {
|
|
||||||
nty_coroutine *co = TAILQ_FIRST(&sched->ready);
|
|
||||||
TAILQ_REMOVE(&co->sched->ready, co, ready_next);
|
|
||||||
|
|
||||||
if (co->status & BIT(NTY_COROUTINE_STATUS_FDEOF)) {
|
|
||||||
nty_coroutine_free(co);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_coroutine_resume(co);
|
|
||||||
if (co == last_co_ready) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. wait rbtree
|
|
||||||
nty_schedule_epoll(sched);
|
|
||||||
while (sched->num_new_events) {
|
|
||||||
int idx = --sched->num_new_events;
|
|
||||||
struct epoll_event *ev = sched->eventlist+idx;
|
|
||||||
|
|
||||||
int fd = ev->data.fd;
|
|
||||||
int is_eof = ev->events & EPOLLHUP;
|
|
||||||
if (is_eof) errno = ECONNRESET;
|
|
||||||
|
|
||||||
nty_coroutine *co = nty_schedule_search_wait(fd);
|
|
||||||
if (co != NULL) {
|
|
||||||
if (is_eof) {
|
|
||||||
co->status |= BIT(NTY_COROUTINE_STATUS_FDEOF);
|
|
||||||
}
|
|
||||||
nty_coroutine_resume(co);
|
|
||||||
}
|
|
||||||
|
|
||||||
is_eof = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_schedule_free(sched);
|
|
||||||
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,672 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static uint32_t nty_pollevent_2epoll( 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 nty_epollevent_2poll( 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;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* nty_poll_inner --> 1. sockfd--> epoll, 2 yield, 3. epoll x sockfd
|
|
||||||
* fds :
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int nty_poll_inner(struct pollfd *fds, nfds_t nfds, int timeout) {
|
|
||||||
|
|
||||||
if (timeout == 0)
|
|
||||||
{
|
|
||||||
return poll(fds, nfds, timeout);
|
|
||||||
}
|
|
||||||
if (timeout < 0)
|
|
||||||
{
|
|
||||||
timeout = INT_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
printf("scheduler not exit!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_coroutine *co = sched->curr_thread;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (i = 0;i < nfds;i ++) {
|
|
||||||
|
|
||||||
struct epoll_event ev;
|
|
||||||
ev.events = nty_pollevent_2epoll(fds[i].events);
|
|
||||||
ev.data.fd = fds[i].fd;
|
|
||||||
epoll_ctl(sched->poller_fd, EPOLL_CTL_ADD, fds[i].fd, &ev);
|
|
||||||
|
|
||||||
co->events = fds[i].events;
|
|
||||||
nty_schedule_sched_wait(co, fds[i].fd, fds[i].events, timeout);
|
|
||||||
}
|
|
||||||
nty_coroutine_yield(co);
|
|
||||||
|
|
||||||
for (i = 0;i < nfds;i ++) {
|
|
||||||
|
|
||||||
struct epoll_event ev;
|
|
||||||
ev.events = nty_pollevent_2epoll(fds[i].events);
|
|
||||||
ev.data.fd = fds[i].fd;
|
|
||||||
epoll_ctl(sched->poller_fd, EPOLL_CTL_DEL, fds[i].fd, &ev);
|
|
||||||
|
|
||||||
nty_schedule_desched_wait(fds[i].fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nfds;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int nty_socket(int domain, int type, int protocol) {
|
|
||||||
|
|
||||||
int fd = socket(domain, type, protocol);
|
|
||||||
if (fd == -1) {
|
|
||||||
printf("Failed to create a new socket\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int ret = fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
||||||
if (ret == -1) {
|
|
||||||
close(ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int reuse = 1;
|
|
||||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
//nty_accept
|
|
||||||
//return failed == -1, success > 0
|
|
||||||
|
|
||||||
int nty_accept(int fd, struct sockaddr *addr, socklen_t *len) {
|
|
||||||
int sockfd = -1;
|
|
||||||
int timeout = 1;
|
|
||||||
nty_coroutine *co = nty_coroutine_get_sched()->curr_thread;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN | POLLERR | POLLHUP;
|
|
||||||
nty_poll_inner(&fds, 1, timeout);
|
|
||||||
|
|
||||||
sockfd = accept(fd, addr, len);
|
|
||||||
if (sockfd < 0) {
|
|
||||||
if (errno == EAGAIN) {
|
|
||||||
continue;
|
|
||||||
} else if (errno == ECONNABORTED) {
|
|
||||||
printf("accept : ECONNABORTED\n");
|
|
||||||
|
|
||||||
} else if (errno == EMFILE || errno == ENFILE) {
|
|
||||||
printf("accept : EMFILE || ENFILE\n");
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
|
||||||
if (ret == -1) {
|
|
||||||
close(sockfd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int reuse = 1;
|
|
||||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
|
|
||||||
|
|
||||||
return sockfd;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int nty_connect(int fd, struct sockaddr *name, socklen_t namelen) {
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLOUT | POLLERR | POLLHUP;
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
|
|
||||||
ret = connect(fd, name, namelen);
|
|
||||||
if (ret == 0) break;
|
|
||||||
|
|
||||||
if (ret == -1 && (errno == EAGAIN ||
|
|
||||||
errno == EWOULDBLOCK ||
|
|
||||||
errno == EINPROGRESS)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//recv
|
|
||||||
// add epoll first
|
|
||||||
//
|
|
||||||
ssize_t nty_recv(int fd, void *buf, size_t len, int flags) {
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
|
|
||||||
int ret = recv(fd, buf, len, flags);
|
|
||||||
if (ret < 0) {
|
|
||||||
//if (errno == EAGAIN) return ret;
|
|
||||||
if (errno == ECONNRESET) return -1;
|
|
||||||
//printf("recv error : %d, ret : %d\n", errno, ret);
|
|
||||||
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ssize_t nty_send(int fd, const void *buf, size_t len, int flags) {
|
|
||||||
|
|
||||||
int sent = 0;
|
|
||||||
|
|
||||||
int ret = send(fd, ((char*)buf)+sent, len-sent, flags);
|
|
||||||
if (ret == 0) return ret;
|
|
||||||
if (ret > 0) sent += ret;
|
|
||||||
|
|
||||||
while (sent < len) {
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLOUT | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
ret = send(fd, ((char*)buf)+sent, len-sent, flags);
|
|
||||||
//printf("send --> len : %d\n", ret);
|
|
||||||
if (ret <= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sent += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret <= 0 && sent == 0) return ret;
|
|
||||||
|
|
||||||
return sent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ssize_t nty_sendto(int fd, const void *buf, size_t len, int flags,
|
|
||||||
const struct sockaddr *dest_addr, socklen_t addrlen) {
|
|
||||||
|
|
||||||
|
|
||||||
int sent = 0;
|
|
||||||
|
|
||||||
while (sent < len) {
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLOUT | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
int ret = sendto(fd, ((char*)buf)+sent, len-sent, flags, dest_addr, addrlen);
|
|
||||||
if (ret <= 0) {
|
|
||||||
if (errno == EAGAIN) continue;
|
|
||||||
else if (errno == ECONNRESET) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
printf("send errno : %d, ret : %d\n", errno, ret);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
sent += ret;
|
|
||||||
}
|
|
||||||
return sent;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t nty_recvfrom(int fd, void *buf, size_t len, int flags,
|
|
||||||
struct sockaddr *src_addr, socklen_t *addrlen) {
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
|
|
||||||
int ret = recvfrom(fd, buf, len, flags, src_addr, addrlen);
|
|
||||||
if (ret < 0) {
|
|
||||||
if (errno == EAGAIN) return ret;
|
|
||||||
if (errno == ECONNRESET) return 0;
|
|
||||||
|
|
||||||
printf("recv error : %d, ret : %d\n", errno, ret);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int nty_close(int fd) {
|
|
||||||
#if 0
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
|
|
||||||
nty_coroutine *co = sched->curr_thread;
|
|
||||||
if (co) {
|
|
||||||
TAILQ_INSERT_TAIL(&nty_coroutine_get_sched()->ready, co, ready_next);
|
|
||||||
co->status |= BIT(NTY_COROUTINE_STATUS_FDEOF);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef COROUTINE_HOOK
|
|
||||||
|
|
||||||
socket_t socket_f = NULL;
|
|
||||||
|
|
||||||
read_t read_f = NULL;
|
|
||||||
recv_t recv_f = NULL;
|
|
||||||
recvfrom_t recvfrom_f = NULL;
|
|
||||||
|
|
||||||
write_t write_f = NULL;
|
|
||||||
send_t send_f = NULL;
|
|
||||||
sendto_t sendto_f = NULL;
|
|
||||||
|
|
||||||
accept_t accept_f = NULL;
|
|
||||||
close_t close_f = NULL;
|
|
||||||
connect_t connect_f = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
int init_hook(void) {
|
|
||||||
|
|
||||||
socket_f = (socket_t)dlsym(RTLD_NEXT, "socket");
|
|
||||||
|
|
||||||
read_f = (read_t)dlsym(RTLD_NEXT, "read");
|
|
||||||
recv_f = (recv_t)dlsym(RTLD_NEXT, "recv");
|
|
||||||
recvfrom_f = (recvfrom_t)dlsym(RTLD_NEXT, "recvfrom");
|
|
||||||
|
|
||||||
write_f = (write_t)dlsym(RTLD_NEXT, "write");
|
|
||||||
send_f = (send_t)dlsym(RTLD_NEXT, "send");
|
|
||||||
sendto_f = (sendto_t)dlsym(RTLD_NEXT, "sendto");
|
|
||||||
|
|
||||||
accept_f = (accept_t)dlsym(RTLD_NEXT, "accept");
|
|
||||||
close_f = (close_t)dlsym(RTLD_NEXT, "close");
|
|
||||||
connect_f = (connect_t)dlsym(RTLD_NEXT, "connect");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int socket(int domain, int type, int protocol) {
|
|
||||||
|
|
||||||
if (!socket_f) init_hook();
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
return socket_f(domain, type, protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd = socket_f(domain, type, protocol);
|
|
||||||
if (fd == -1) {
|
|
||||||
printf("Failed to create a new socket\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int ret = fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
||||||
if (ret == -1) {
|
|
||||||
close(ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int reuse = 1;
|
|
||||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t read(int fd, void *buf, size_t count) {
|
|
||||||
|
|
||||||
if (!read_f) init_hook();
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
return read_f(fd, buf, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
|
|
||||||
int ret = read_f(fd, buf, count);
|
|
||||||
if (ret < 0) {
|
|
||||||
//if (errno == EAGAIN) return ret;
|
|
||||||
if (errno == ECONNRESET) return -1;
|
|
||||||
//printf("recv error : %d, ret : %d\n", errno, ret);
|
|
||||||
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t recv(int fd, void *buf, size_t len, int flags) {
|
|
||||||
|
|
||||||
if (!recv_f) init_hook();
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
return recv_f(fd, buf, len, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
|
|
||||||
int ret = recv_f(fd, buf, len, flags);
|
|
||||||
if (ret < 0) {
|
|
||||||
//if (errno == EAGAIN) return ret;
|
|
||||||
if (errno == ECONNRESET) return -1;
|
|
||||||
//printf("recv error : %d, ret : %d\n", errno, ret);
|
|
||||||
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ssize_t recvfrom(int fd, void *buf, size_t len, int flags,
|
|
||||||
struct sockaddr *src_addr, socklen_t *addrlen) {
|
|
||||||
|
|
||||||
if (!recvfrom_f) init_hook();
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
return recvfrom_f(fd, buf, len, flags, src_addr, addrlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
|
|
||||||
int ret = recvfrom_f(fd, buf, len, flags, src_addr, addrlen);
|
|
||||||
if (ret < 0) {
|
|
||||||
if (errno == EAGAIN) return ret;
|
|
||||||
if (errno == ECONNRESET) return 0;
|
|
||||||
|
|
||||||
printf("recv error : %d, ret : %d\n", errno, ret);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ssize_t write(int fd, const void *buf, size_t count) {
|
|
||||||
|
|
||||||
if (!write_f) init_hook();
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
return write_f(fd, buf, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sent = 0;
|
|
||||||
|
|
||||||
int ret = write_f(fd, ((char*)buf)+sent, count-sent);
|
|
||||||
if (ret == 0) return ret;
|
|
||||||
if (ret > 0) sent += ret;
|
|
||||||
|
|
||||||
while (sent < count) {
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLOUT | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
ret = write_f(fd, ((char*)buf)+sent, count-sent);
|
|
||||||
//printf("send --> len : %d\n", ret);
|
|
||||||
if (ret <= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sent += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret <= 0 && sent == 0) return ret;
|
|
||||||
|
|
||||||
return sent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ssize_t send(int fd, const void *buf, size_t len, int flags) {
|
|
||||||
|
|
||||||
if (!send_f) init_hook();
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
return send_f(fd, buf, len, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sent = 0;
|
|
||||||
|
|
||||||
int ret = send_f(fd, ((char*)buf)+sent, len-sent, flags);
|
|
||||||
if (ret == 0) return ret;
|
|
||||||
if (ret > 0) sent += ret;
|
|
||||||
|
|
||||||
while (sent < len) {
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLOUT | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
ret = send_f(fd, ((char*)buf)+sent, len-sent, flags);
|
|
||||||
//printf("send --> len : %d\n", ret);
|
|
||||||
if (ret <= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sent += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret <= 0 && sent == 0) return ret;
|
|
||||||
|
|
||||||
return sent;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
|
|
||||||
const struct sockaddr *dest_addr, socklen_t addrlen) {
|
|
||||||
|
|
||||||
if (!sendto_f) init_hook();
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
return sendto_f(sockfd, buf, len, flags, dest_addr, addrlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = sockfd;
|
|
||||||
fds.events = POLLOUT | POLLERR | POLLHUP;
|
|
||||||
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
|
|
||||||
int ret = sendto_f(sockfd, buf, len, flags, dest_addr, addrlen);
|
|
||||||
if (ret < 0) {
|
|
||||||
if (errno == EAGAIN) return ret;
|
|
||||||
if (errno == ECONNRESET) return 0;
|
|
||||||
|
|
||||||
printf("recv error : %d, ret : %d\n", errno, ret);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int accept(int fd, struct sockaddr *addr, socklen_t *len) {
|
|
||||||
|
|
||||||
if (!accept_f) init_hook();
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
return accept_f(fd, addr, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sockfd = -1;
|
|
||||||
int timeout = 1;
|
|
||||||
nty_coroutine *co = nty_coroutine_get_sched()->curr_thread;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN | POLLERR | POLLHUP;
|
|
||||||
nty_poll_inner(&fds, 1, timeout);
|
|
||||||
|
|
||||||
sockfd = accept_f(fd, addr, len);
|
|
||||||
if (sockfd < 0) {
|
|
||||||
if (errno == EAGAIN) {
|
|
||||||
continue;
|
|
||||||
} else if (errno == ECONNABORTED) {
|
|
||||||
printf("accept : ECONNABORTED\n");
|
|
||||||
|
|
||||||
} else if (errno == EMFILE || errno == ENFILE) {
|
|
||||||
printf("accept : EMFILE || ENFILE\n");
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
|
||||||
if (ret == -1) {
|
|
||||||
close(sockfd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int reuse = 1;
|
|
||||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
|
|
||||||
|
|
||||||
return sockfd;
|
|
||||||
}
|
|
||||||
|
|
||||||
int close(int fd) {
|
|
||||||
|
|
||||||
if (!close_f) init_hook();
|
|
||||||
|
|
||||||
return close_f(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int connect(int fd, const struct sockaddr *addr, socklen_t addrlen) {
|
|
||||||
|
|
||||||
if (!connect_f) init_hook();
|
|
||||||
|
|
||||||
nty_schedule *sched = nty_coroutine_get_sched();
|
|
||||||
if (sched == NULL) {
|
|
||||||
return connect_f(fd, addr, addrlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLOUT | POLLERR | POLLHUP;
|
|
||||||
nty_poll_inner(&fds, 1, 1);
|
|
||||||
|
|
||||||
ret = connect_f(fd, addr, addrlen);
|
|
||||||
if (ret == 0) break;
|
|
||||||
|
|
||||||
if (ret == -1 && (errno == EAGAIN ||
|
|
||||||
errno == EWOULDBLOCK ||
|
|
||||||
errno == EINPROGRESS)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,754 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __NTY_TREE_H__
|
|
||||||
#define __NTY_TREE_H__
|
|
||||||
|
|
||||||
|
|
||||||
#define SPLAY_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *sph_root; /* root of the tree */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPLAY_INITIALIZER(root) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define SPLAY_INIT(root) do { \
|
|
||||||
(root)->sph_root = NULL; \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *spe_left; /* left element */ \
|
|
||||||
struct type *spe_right; /* right element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
|
||||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
|
||||||
#define SPLAY_ROOT(head) (head)->sph_root
|
|
||||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
|
||||||
|
|
||||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
|
||||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
|
||||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
|
||||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
|
||||||
(head)->sph_root = tmp; \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
|
||||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
|
||||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
|
||||||
(head)->sph_root = tmp; \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
|
||||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
|
||||||
tmp = (head)->sph_root; \
|
|
||||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
|
||||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
|
||||||
tmp = (head)->sph_root; \
|
|
||||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
|
||||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
|
||||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
|
||||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
|
||||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
|
||||||
} while (/*CONSTCOND*/ 0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
|
||||||
void name##_SPLAY(struct name *, struct type *); \
|
|
||||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
|
||||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
|
||||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
|
||||||
\
|
|
||||||
/* Finds the node with the same key as elm */ \
|
|
||||||
static __inline struct type * \
|
|
||||||
name##_SPLAY_FIND(struct name *head, struct type *elm) { \ \
|
|
||||||
if (SPLAY_EMPTY(head)) \
|
|
||||||
return(NULL); \
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
|
||||||
return (head->sph_root); \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
static __inline struct type * \
|
|
||||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) { \
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
|
||||||
elm = SPLAY_RIGHT(elm, field); \
|
|
||||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
|
||||||
elm = SPLAY_LEFT(elm, field); \
|
|
||||||
} \
|
|
||||||
} else \
|
|
||||||
elm = NULL; \
|
|
||||||
return (elm); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
static __inline struct type * \
|
|
||||||
name##_SPLAY_MIN_MAX(struct name *head, int val) { \
|
|
||||||
name##_SPLAY_MINMAX(head, val); \
|
|
||||||
return (SPLAY_ROOT(head)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main splay operation.
|
|
||||||
* Moves node close to the key of elm to top
|
|
||||||
*/
|
|
||||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
|
||||||
struct type * \
|
|
||||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) { \
|
|
||||||
if (SPLAY_EMPTY(head)) { \
|
|
||||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
|
||||||
} else { \
|
|
||||||
int __comp; \
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
__comp = (cmp)(elm, (head)->sph_root); \
|
|
||||||
if(__comp < 0) { \
|
|
||||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
|
|
||||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
|
||||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
|
||||||
} else if (__comp > 0) { \
|
|
||||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
|
||||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
|
||||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
|
||||||
} else \
|
|
||||||
return ((head)->sph_root); \
|
|
||||||
} \
|
|
||||||
(head)->sph_root = (elm); \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
struct type * \
|
|
||||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) { \
|
|
||||||
struct type *__tmp; \
|
|
||||||
if (SPLAY_EMPTY(head)) \
|
|
||||||
return (NULL); \
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
|
||||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
|
||||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
|
|
||||||
} else { \
|
|
||||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
|
||||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
|
|
||||||
name##_SPLAY(head, elm); \
|
|
||||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
|
||||||
} \
|
|
||||||
return (elm); \
|
|
||||||
} \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
void \
|
|
||||||
name##_SPLAY(struct name *head, struct type *elm) { \
|
|
||||||
struct type __node, *__left, *__right, *__tmp; \
|
|
||||||
int __comp; \
|
|
||||||
\
|
|
||||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
|
||||||
__left = __right = &__node; \
|
|
||||||
\
|
|
||||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {\
|
|
||||||
if (__comp < 0) { \
|
|
||||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
|
||||||
if (__tmp == NULL) \
|
|
||||||
break; \
|
|
||||||
if ((cmp)(elm, __tmp) < 0){ \
|
|
||||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
|
||||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
SPLAY_LINKLEFT(head, __right, field); \
|
|
||||||
} else if (__comp > 0) { \
|
|
||||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
|
||||||
if (__tmp == NULL) \
|
|
||||||
break; \
|
|
||||||
if ((cmp)(elm, __tmp) > 0){ \
|
|
||||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
|
||||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
SPLAY_LINKRIGHT(head, __left, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
|
||||||
{ \
|
|
||||||
struct type __node, *__left, *__right, *__tmp; \
|
|
||||||
\
|
|
||||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
|
||||||
__left = __right = &__node; \
|
|
||||||
\
|
|
||||||
while (1) { \
|
|
||||||
if (__comp < 0) { \
|
|
||||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
|
||||||
if (__tmp == NULL) \
|
|
||||||
break; \
|
|
||||||
if (__comp < 0){ \
|
|
||||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
|
||||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
SPLAY_LINKLEFT(head, __right, field); \
|
|
||||||
} else if (__comp > 0) { \
|
|
||||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
|
||||||
if (__tmp == NULL) \
|
|
||||||
break; \
|
|
||||||
if (__comp > 0) { \
|
|
||||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
|
||||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
SPLAY_LINKRIGHT(head, __left, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPLAY_NEGINF -1
|
|
||||||
#define SPLAY_INF 1
|
|
||||||
|
|
||||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
|
||||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
|
||||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
|
||||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
|
||||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
|
||||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
|
||||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
|
||||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
|
||||||
|
|
||||||
#define SPLAY_FOREACH(x, name, head) \
|
|
||||||
for ((x) = SPLAY_MIN(name, head); \
|
|
||||||
(x) != NULL; \
|
|
||||||
(x) = SPLAY_NEXT(name, head, x))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Macros that define a red-black tree */
|
|
||||||
#define RB_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *rbh_root; /* root of the tree */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RB_INITIALIZER(root) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define RB_INIT(root) do { \
|
|
||||||
(root)->rbh_root = NULL; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
#define RB_BLACK 0
|
|
||||||
#define RB_RED 1
|
|
||||||
|
|
||||||
#define RB_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *rbe_left; /* left element */ \
|
|
||||||
struct type *rbe_right; /* right element */ \
|
|
||||||
struct type *rbe_parent; /* parent element */ \
|
|
||||||
int rbe_color; /* node color */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RB_LEFT(elm, field) (elm)->field.rbe_left
|
|
||||||
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
|
|
||||||
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
|
|
||||||
#define RB_COLOR(elm, field) (elm)->field.rbe_color
|
|
||||||
#define RB_ROOT(head) (head)->rbh_root
|
|
||||||
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
|
|
||||||
|
|
||||||
#define RB_SET(elm, parent, field) do { \
|
|
||||||
RB_PARENT(elm, field) = parent; \
|
|
||||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
|
||||||
RB_COLOR(elm, field) = RB_RED; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define RB_SET_BLACKRED(black, red, field) do { \
|
|
||||||
RB_COLOR(black, field) = RB_BLACK; \
|
|
||||||
RB_COLOR(red, field) = RB_RED; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#ifndef RB_AUGMENT
|
|
||||||
#define RB_AUGMENT(x) do {} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
|
|
||||||
(tmp) = RB_RIGHT(elm, field); \
|
|
||||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
|
|
||||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
|
||||||
} \
|
|
||||||
RB_AUGMENT(elm); \
|
|
||||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
|
||||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
|
||||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
|
||||||
} else \
|
|
||||||
(head)->rbh_root = (tmp); \
|
|
||||||
RB_LEFT(tmp, field) = (elm); \
|
|
||||||
RB_PARENT(elm, field) = (tmp); \
|
|
||||||
RB_AUGMENT(tmp); \
|
|
||||||
if ((RB_PARENT(tmp, field))) \
|
|
||||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
|
|
||||||
(tmp) = RB_LEFT(elm, field); \
|
|
||||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
|
|
||||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
|
||||||
} \
|
|
||||||
RB_AUGMENT(elm); \
|
|
||||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
|
||||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
|
||||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
|
||||||
} else \
|
|
||||||
(head)->rbh_root = (tmp); \
|
|
||||||
RB_RIGHT(tmp, field) = (elm); \
|
|
||||||
RB_PARENT(elm, field) = (tmp); \
|
|
||||||
RB_AUGMENT(tmp); \
|
|
||||||
if ((RB_PARENT(tmp, field))) \
|
|
||||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
|
||||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
|
||||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
|
||||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
|
|
||||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
|
||||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
|
|
||||||
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
|
|
||||||
attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
|
|
||||||
attr struct type *name##_RB_INSERT(struct name *, struct type *); \
|
|
||||||
attr struct type *name##_RB_FIND(struct name *, struct type *); \
|
|
||||||
attr struct type *name##_RB_NFIND(struct name *, struct type *); \
|
|
||||||
attr struct type *name##_RB_NEXT(struct type *); \
|
|
||||||
attr struct type *name##_RB_PREV(struct type *); \
|
|
||||||
attr struct type *name##_RB_MINMAX(struct name *, int); \
|
|
||||||
|
|
||||||
/* Main rb operation.
|
|
||||||
* Moves node close to the key of elm to top
|
|
||||||
*/
|
|
||||||
#define RB_GENERATE(name, type, field, cmp) \
|
|
||||||
RB_GENERATE_INTERNAL(name, type, field, cmp,)
|
|
||||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
|
||||||
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
|
|
||||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
|
||||||
attr void \
|
|
||||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) { \
|
|
||||||
struct type *parent, *gparent, *tmp; \
|
|
||||||
while ((parent = RB_PARENT(elm, field)) != NULL && \
|
|
||||||
RB_COLOR(parent, field) == RB_RED) { \
|
|
||||||
gparent = RB_PARENT(parent, field); \
|
|
||||||
if (parent == RB_LEFT(gparent, field)) { \
|
|
||||||
tmp = RB_RIGHT(gparent, field); \
|
|
||||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
|
||||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
|
||||||
RB_SET_BLACKRED(parent, gparent, field); \
|
|
||||||
elm = gparent; \
|
|
||||||
continue; \
|
|
||||||
} \
|
|
||||||
if (RB_RIGHT(parent, field) == elm) { \
|
|
||||||
RB_ROTATE_LEFT(head, parent, tmp, field); \
|
|
||||||
tmp = parent; \
|
|
||||||
parent = elm; \
|
|
||||||
elm = tmp; \
|
|
||||||
} \
|
|
||||||
RB_SET_BLACKRED(parent, gparent, field); \
|
|
||||||
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
|
|
||||||
} else { \
|
|
||||||
tmp = RB_LEFT(gparent, field); \
|
|
||||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
|
||||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
|
||||||
RB_SET_BLACKRED(parent, gparent, field); \
|
|
||||||
elm = gparent; \
|
|
||||||
continue; \
|
|
||||||
} \
|
|
||||||
if (RB_LEFT(parent, field) == elm) { \
|
|
||||||
RB_ROTATE_RIGHT(head, parent, tmp, field); \
|
|
||||||
tmp = parent; \
|
|
||||||
parent = elm; \
|
|
||||||
elm = tmp; \
|
|
||||||
} \
|
|
||||||
RB_SET_BLACKRED(parent, gparent, field); \
|
|
||||||
RB_ROTATE_LEFT(head, gparent, tmp, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
attr void \
|
|
||||||
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *tmp; \
|
|
||||||
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
|
|
||||||
elm != RB_ROOT(head)) { \
|
|
||||||
if (RB_LEFT(parent, field) == elm) { \
|
|
||||||
tmp = RB_RIGHT(parent, field); \
|
|
||||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
|
||||||
RB_SET_BLACKRED(tmp, parent, field); \
|
|
||||||
RB_ROTATE_LEFT(head, parent, tmp, field); \
|
|
||||||
tmp = RB_RIGHT(parent, field); \
|
|
||||||
} \
|
|
||||||
if ((RB_LEFT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \
|
|
||||||
(RB_RIGHT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \
|
|
||||||
RB_COLOR(tmp, field) = RB_RED; \
|
|
||||||
elm = parent; \
|
|
||||||
parent = RB_PARENT(elm, field); \
|
|
||||||
} else { \
|
|
||||||
if (RB_RIGHT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) { \
|
|
||||||
struct type *oleft; \
|
|
||||||
if ((oleft = RB_LEFT(tmp, field)) \
|
|
||||||
!= NULL) \
|
|
||||||
RB_COLOR(oleft, field) = RB_BLACK; \
|
|
||||||
RB_COLOR(tmp, field) = RB_RED; \
|
|
||||||
RB_ROTATE_RIGHT(head, tmp, oleft, field); \
|
|
||||||
tmp = RB_RIGHT(parent, field); \
|
|
||||||
} \
|
|
||||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field); \
|
|
||||||
RB_COLOR(parent, field) = RB_BLACK; \
|
|
||||||
if (RB_RIGHT(tmp, field)) \
|
|
||||||
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; \
|
|
||||||
RB_ROTATE_LEFT(head, parent, tmp, field); \
|
|
||||||
elm = RB_ROOT(head); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
} else { \
|
|
||||||
tmp = RB_LEFT(parent, field); \
|
|
||||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
|
||||||
RB_SET_BLACKRED(tmp, parent, field); \
|
|
||||||
RB_ROTATE_RIGHT(head, parent, tmp, field); \
|
|
||||||
tmp = RB_LEFT(parent, field); \
|
|
||||||
} \
|
|
||||||
if ((RB_LEFT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \
|
|
||||||
(RB_RIGHT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \
|
|
||||||
RB_COLOR(tmp, field) = RB_RED; \
|
|
||||||
elm = parent; \
|
|
||||||
parent = RB_PARENT(elm, field); \
|
|
||||||
} else { \
|
|
||||||
if (RB_LEFT(tmp, field) == NULL || \
|
|
||||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) { \
|
|
||||||
struct type *oright; \
|
|
||||||
if ((oright = RB_RIGHT(tmp, field)) \
|
|
||||||
!= NULL) \
|
|
||||||
RB_COLOR(oright, field) = RB_BLACK; \
|
|
||||||
RB_COLOR(tmp, field) = RB_RED; \
|
|
||||||
RB_ROTATE_LEFT(head, tmp, oright, field); \
|
|
||||||
tmp = RB_LEFT(parent, field); \
|
|
||||||
} \
|
|
||||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field); \
|
|
||||||
RB_COLOR(parent, field) = RB_BLACK; \
|
|
||||||
if (RB_LEFT(tmp, field)) \
|
|
||||||
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; \
|
|
||||||
RB_ROTATE_RIGHT(head, parent, tmp, field); \
|
|
||||||
elm = RB_ROOT(head); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
if (elm) \
|
|
||||||
RB_COLOR(elm, field) = RB_BLACK; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_REMOVE(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *child, *parent, *old = elm; \
|
|
||||||
int color; \
|
|
||||||
if (RB_LEFT(elm, field) == NULL) \
|
|
||||||
child = RB_RIGHT(elm, field); \
|
|
||||||
else if (RB_RIGHT(elm, field) == NULL) \
|
|
||||||
child = RB_LEFT(elm, field); \
|
|
||||||
else { \
|
|
||||||
struct type *left; \
|
|
||||||
elm = RB_RIGHT(elm, field); \
|
|
||||||
while ((left = RB_LEFT(elm, field)) != NULL) \
|
|
||||||
elm = left; \
|
|
||||||
child = RB_RIGHT(elm, field); \
|
|
||||||
parent = RB_PARENT(elm, field); \
|
|
||||||
color = RB_COLOR(elm, field); \
|
|
||||||
if (child) \
|
|
||||||
RB_PARENT(child, field) = parent; \
|
|
||||||
if (parent) { \
|
|
||||||
if (RB_LEFT(parent, field) == elm) \
|
|
||||||
RB_LEFT(parent, field) = child; \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(parent, field) = child; \
|
|
||||||
RB_AUGMENT(parent); \
|
|
||||||
} else \
|
|
||||||
RB_ROOT(head) = child; \
|
|
||||||
if (RB_PARENT(elm, field) == old) \
|
|
||||||
parent = elm; \
|
|
||||||
(elm)->field = (old)->field; \
|
|
||||||
if (RB_PARENT(old, field)) { \
|
|
||||||
if (RB_LEFT(RB_PARENT(old, field), field) == old) \
|
|
||||||
RB_LEFT(RB_PARENT(old, field), field) = elm; \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(RB_PARENT(old, field), field) = elm; \
|
|
||||||
RB_AUGMENT(RB_PARENT(old, field)); \
|
|
||||||
} else \
|
|
||||||
RB_ROOT(head) = elm; \
|
|
||||||
RB_PARENT(RB_LEFT(old, field), field) = elm; \
|
|
||||||
if (RB_RIGHT(old, field)) \
|
|
||||||
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
|
|
||||||
if (parent) { \
|
|
||||||
left = parent; \
|
|
||||||
do { \
|
|
||||||
RB_AUGMENT(left); \
|
|
||||||
} while ((left = RB_PARENT(left, field)) != NULL); \
|
|
||||||
} \
|
|
||||||
goto color; \
|
|
||||||
} \
|
|
||||||
parent = RB_PARENT(elm, field); \
|
|
||||||
color = RB_COLOR(elm, field); \
|
|
||||||
if (child) \
|
|
||||||
RB_PARENT(child, field) = parent; \
|
|
||||||
if (parent) { \
|
|
||||||
if (RB_LEFT(parent, field) == elm) \
|
|
||||||
RB_LEFT(parent, field) = child; \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(parent, field) = child; \
|
|
||||||
RB_AUGMENT(parent); \
|
|
||||||
} else \
|
|
||||||
RB_ROOT(head) = child; \
|
|
||||||
color: \
|
|
||||||
if (color == RB_BLACK) \
|
|
||||||
name##_RB_REMOVE_COLOR(head, parent, child); \
|
|
||||||
return (old); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* Inserts a node into the RB tree */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_INSERT(struct name *head, struct type *elm) { \
|
|
||||||
struct type *tmp; \
|
|
||||||
struct type *parent = NULL; \
|
|
||||||
int comp = 0; \
|
|
||||||
tmp = RB_ROOT(head); \
|
|
||||||
while (tmp) { \
|
|
||||||
parent = tmp; \
|
|
||||||
comp = (cmp)(elm, parent); \
|
|
||||||
if (comp < 0) \
|
|
||||||
tmp = RB_LEFT(tmp, field); \
|
|
||||||
else if (comp > 0) \
|
|
||||||
tmp = RB_RIGHT(tmp, field); \
|
|
||||||
else \
|
|
||||||
return (tmp); \
|
|
||||||
} \
|
|
||||||
RB_SET(elm, parent, field); \
|
|
||||||
if (parent != NULL) { \
|
|
||||||
if (comp < 0) \
|
|
||||||
RB_LEFT(parent, field) = elm; \
|
|
||||||
else \
|
|
||||||
RB_RIGHT(parent, field) = elm; \
|
|
||||||
RB_AUGMENT(parent); \
|
|
||||||
} else \
|
|
||||||
RB_ROOT(head) = elm; \
|
|
||||||
name##_RB_INSERT_COLOR(head, elm); \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* Finds the node with the same key as elm */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_FIND(struct name *head, struct type *elm) { \
|
|
||||||
struct type *tmp = RB_ROOT(head); \
|
|
||||||
int comp; \
|
|
||||||
while (tmp) { \
|
|
||||||
comp = cmp(elm, tmp); \
|
|
||||||
if (comp < 0) \
|
|
||||||
tmp = RB_LEFT(tmp, field); \
|
|
||||||
else if (comp > 0) \
|
|
||||||
tmp = RB_RIGHT(tmp, field); \
|
|
||||||
else \
|
|
||||||
return (tmp); \
|
|
||||||
} \
|
|
||||||
return (NULL); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* Finds the first node greater than or equal to the search key */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_NFIND(struct name *head, struct type *elm) \
|
|
||||||
{ \
|
|
||||||
struct type *tmp = RB_ROOT(head); \
|
|
||||||
struct type *res = NULL; \
|
|
||||||
int comp; \
|
|
||||||
while (tmp) { \
|
|
||||||
comp = cmp(elm, tmp); \
|
|
||||||
if (comp < 0) { \
|
|
||||||
res = tmp; \
|
|
||||||
tmp = RB_LEFT(tmp, field); \
|
|
||||||
} \
|
|
||||||
else if (comp > 0) \
|
|
||||||
tmp = RB_RIGHT(tmp, field); \
|
|
||||||
else \
|
|
||||||
return (tmp); \
|
|
||||||
} \
|
|
||||||
return (res); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* ARGSUSED */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_NEXT(struct type *elm) \
|
|
||||||
{ \
|
|
||||||
if (RB_RIGHT(elm, field)) { \
|
|
||||||
elm = RB_RIGHT(elm, field); \
|
|
||||||
while (RB_LEFT(elm, field)) \
|
|
||||||
elm = RB_LEFT(elm, field); \
|
|
||||||
} else { \
|
|
||||||
if (RB_PARENT(elm, field) && \
|
|
||||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
else { \
|
|
||||||
while (RB_PARENT(elm, field) && \
|
|
||||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
return (elm); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* ARGSUSED */ \
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_PREV(struct type *elm) \
|
|
||||||
{ \
|
|
||||||
if (RB_LEFT(elm, field)) { \
|
|
||||||
elm = RB_LEFT(elm, field); \
|
|
||||||
while (RB_RIGHT(elm, field)) \
|
|
||||||
elm = RB_RIGHT(elm, field); \
|
|
||||||
} else { \
|
|
||||||
if (RB_PARENT(elm, field) && \
|
|
||||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
else { \
|
|
||||||
while (RB_PARENT(elm, field) && \
|
|
||||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
elm = RB_PARENT(elm, field); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
return (elm); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
attr struct type * \
|
|
||||||
name##_RB_MINMAX(struct name *head, int val) \
|
|
||||||
{ \
|
|
||||||
struct type *tmp = RB_ROOT(head); \
|
|
||||||
struct type *parent = NULL; \
|
|
||||||
while (tmp) { \
|
|
||||||
parent = tmp; \
|
|
||||||
if (val < 0) \
|
|
||||||
tmp = RB_LEFT(tmp, field); \
|
|
||||||
else \
|
|
||||||
tmp = RB_RIGHT(tmp, field); \
|
|
||||||
} \
|
|
||||||
return (parent); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RB_NEGINF -1
|
|
||||||
#define RB_INF 1
|
|
||||||
|
|
||||||
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
|
|
||||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
|
||||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
|
||||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
|
||||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
|
||||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
|
||||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
|
||||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
|
||||||
|
|
||||||
#define RB_FOREACH(x, name, head) \
|
|
||||||
for ((x) = RB_MIN(name, head); \
|
|
||||||
(x) != NULL; \
|
|
||||||
(x) = name##_RB_NEXT(x))
|
|
||||||
|
|
||||||
#define RB_FOREACH_FROM(x, name, y) \
|
|
||||||
for ((x) = (y); \
|
|
||||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
|
||||||
(x) = (y))
|
|
||||||
|
|
||||||
#define RB_FOREACH_SAFE(x, name, head, y) \
|
|
||||||
for ((x) = RB_MIN(name, head); \
|
|
||||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
|
||||||
(x) = (y))
|
|
||||||
|
|
||||||
#define RB_FOREACH_REVERSE(x, name, head) \
|
|
||||||
for ((x) = RB_MAX(name, head); \
|
|
||||||
(x) != NULL; \
|
|
||||||
(x) = name##_RB_PREV(x))
|
|
||||||
|
|
||||||
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
|
|
||||||
for ((x) = (y); \
|
|
||||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
|
||||||
(x) = (y))
|
|
||||||
|
|
||||||
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
|
|
||||||
for ((x) = RB_MAX(name, head); \
|
|
||||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
|
||||||
(x) = (y))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
@@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/perl -Tw
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use CGI;
|
|
||||||
|
|
||||||
my($cgi) = new CGI;
|
|
||||||
|
|
||||||
print $cgi->header('text/html');
|
|
||||||
print $cgi->start_html(-title => "Example CGI script",
|
|
||||||
-BGCOLOR => 'red');
|
|
||||||
print $cgi->h1("CGI Example");
|
|
||||||
print $cgi->p, "This is an example of CGI\n";
|
|
||||||
print $cgi->p, "Parameters given to this script:\n";
|
|
||||||
print "<UL>\n";
|
|
||||||
foreach my $param ($cgi->param)
|
|
||||||
{
|
|
||||||
print "<LI>", "$param ", $cgi->param($param), "\n";
|
|
||||||
}
|
|
||||||
print "</UL>";
|
|
||||||
print $cgi->end_html, "\n";
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/perl -Tw
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use CGI;
|
|
||||||
|
|
||||||
my($cgi) = new CGI;
|
|
||||||
|
|
||||||
print $cgi->header;
|
|
||||||
my($color) = "blue";
|
|
||||||
$color = $cgi->param('color') if defined $cgi->param('color');
|
|
||||||
|
|
||||||
print $cgi->start_html(-title => uc($color),
|
|
||||||
-BGCOLOR => $color);
|
|
||||||
print $cgi->h1("This is $color");
|
|
||||||
print $cgi->end_html;
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<HTML>
|
|
||||||
<TITLE>Index</TITLE>
|
|
||||||
<BODY>
|
|
||||||
<P>Welcome to J. David's webserver.
|
|
||||||
<H1>CGI demo
|
|
||||||
<FORM ACTION="color.cgi" METHOD="POST">
|
|
||||||
Enter a color: <INPUT TYPE="text" NAME="color">
|
|
||||||
<INPUT TYPE="submit">
|
|
||||||
</FORM>
|
|
||||||
</BODY>
|
|
||||||
</HTML>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
aux_source_directory(. SRC)
|
|
||||||
|
|
||||||
# 编译不过的,先放这里
|
|
||||||
set(NOT_COMPILE )
|
|
||||||
|
|
||||||
foreach(FILE_PATH ${SRC})
|
|
||||||
string(REGEX REPLACE ".+/(.+)\\..*" "\\1" FILE_NAME ${FILE_PATH})
|
|
||||||
list(FIND NOT_COMPILE ${FILE_NAME} RESULT)
|
|
||||||
if(${RESULT} EQUAL -1)
|
|
||||||
message(${FILE_NAME})
|
|
||||||
add_executable(${FILE_NAME} ${FILE_PATH})
|
|
||||||
target_link_libraries(${FILE_NAME} nty_core ${LIBS})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
CUR_SOURCE = ${wildcard *.c}
|
|
||||||
|
|
||||||
CUR_OBJS = ${patsubst %.c, %.o, $(CUR_SOURCE)}
|
|
||||||
|
|
||||||
all : $(SUB_DIR) $(CUR_OBJS)
|
|
||||||
|
|
||||||
$(SUB_DIR) : ECHO
|
|
||||||
make -C $@
|
|
||||||
|
|
||||||
$(CUR_OBJS) : %.o : %.c
|
|
||||||
$(CC) -c $^ -o $(OBJS_DIR)/$@ $(FLAG)
|
|
||||||
|
|
||||||
ECHO :
|
|
||||||
@echo $(SUB_DIR)
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void server_reader(void *arg) {
|
|
||||||
int fd = *(int *)arg;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
char buf[1024] = {0};
|
|
||||||
ret = recv(fd, buf, 1024, 0);
|
|
||||||
if (ret > 0) {
|
|
||||||
printf("read from server: %.*s\n", ret, buf);
|
|
||||||
|
|
||||||
ret = send(fd, buf, strlen(buf), 0);
|
|
||||||
if (ret == -1) {
|
|
||||||
close(fd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ret == 0) {
|
|
||||||
close(fd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void server(void *arg) {
|
|
||||||
|
|
||||||
unsigned short port = *(unsigned short *)arg;
|
|
||||||
|
|
||||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (fd < 0) return ;
|
|
||||||
|
|
||||||
struct sockaddr_in local, remote;
|
|
||||||
local.sin_family = AF_INET;
|
|
||||||
local.sin_port = htons(port);
|
|
||||||
local.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
bind(fd, (struct sockaddr*)&local, sizeof(struct sockaddr_in));
|
|
||||||
|
|
||||||
listen(fd, 20);
|
|
||||||
printf("listen port : %d\n", port);
|
|
||||||
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
socklen_t len = sizeof(struct sockaddr_in);
|
|
||||||
int cli_fd = accept(fd, (struct sockaddr*)&remote, &len);
|
|
||||||
|
|
||||||
|
|
||||||
nty_coroutine *read_co;
|
|
||||||
nty_coroutine_create(&read_co, server_reader, &cli_fd);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
int port = atoi(argv[1]);
|
|
||||||
|
|
||||||
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
nty_coroutine_create(&co, server, &port);
|
|
||||||
|
|
||||||
nty_schedule_run();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,727 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (C) Radim Kolar 1997-2004
|
|
||||||
* This is free software, see GNU Public License version 2 for
|
|
||||||
* details.
|
|
||||||
*
|
|
||||||
* Simple forking WWW Server benchmark:
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* webbench --help
|
|
||||||
*
|
|
||||||
* Return codes:
|
|
||||||
* 0 - sucess
|
|
||||||
* 1 - benchmark failed (server is not on-line)
|
|
||||||
* 2 - bad param
|
|
||||||
* 3 - internal error, fork failed
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
#define ENABLE_NTYCO 1
|
|
||||||
#define MAX_BUFFER_LENGTH 1024
|
|
||||||
|
|
||||||
|
|
||||||
#define write(a, b, c) nty_send(a, b, c, 0)
|
|
||||||
#define send(a, b, c, d) nty_send(a, b, c, d)
|
|
||||||
#define read(a, b, c) nty_recv(a, b, c, 0)
|
|
||||||
#define recv(a, b, c, d) nty_recv(a, b, c, d)
|
|
||||||
#define connect(a, b, c) nty_connect(a, b, c)
|
|
||||||
#define socket(a, b, c) nty_socket(a, b, c)
|
|
||||||
#define close(a) nty_close(a)
|
|
||||||
#define accept(a, b, c) nty_accept(a, b, c)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int Socket(const char *host, int clientPort)
|
|
||||||
{
|
|
||||||
int sock;
|
|
||||||
unsigned long inaddr;
|
|
||||||
struct sockaddr_in ad;
|
|
||||||
struct hostent *hp;
|
|
||||||
|
|
||||||
memset(&ad, 0, sizeof(ad));
|
|
||||||
ad.sin_family = AF_INET;
|
|
||||||
|
|
||||||
inaddr = inet_addr(host);
|
|
||||||
if (inaddr != INADDR_NONE)
|
|
||||||
memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hp = gethostbyname(host);
|
|
||||||
if (hp == NULL)
|
|
||||||
return -1;
|
|
||||||
memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
|
|
||||||
}
|
|
||||||
ad.sin_port = htons(clientPort);
|
|
||||||
|
|
||||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (sock < 0)
|
|
||||||
return sock;
|
|
||||||
if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)
|
|
||||||
return -1;
|
|
||||||
return sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <rpc/types.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
/* values */
|
|
||||||
volatile int timerexpired=0;
|
|
||||||
int speed=0;
|
|
||||||
int failed=0;
|
|
||||||
int bytes=0;
|
|
||||||
/* globals */
|
|
||||||
int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */
|
|
||||||
/* Allow: GET, HEAD, OPTIONS, TRACE */
|
|
||||||
#define METHOD_GET 0
|
|
||||||
#define METHOD_HEAD 1
|
|
||||||
#define METHOD_OPTIONS 2
|
|
||||||
#define METHOD_TRACE 3
|
|
||||||
#define PROGRAM_VERSION "1.5"
|
|
||||||
int method=METHOD_GET;
|
|
||||||
int clients=1;
|
|
||||||
|
|
||||||
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
// clients --> fork num
|
|
||||||
// nreqs --> http request
|
|
||||||
// socket --> io num
|
|
||||||
// socket per fork --> sockets / client
|
|
||||||
// request per socket --> request / sockets
|
|
||||||
|
|
||||||
int nreqs = 0;
|
|
||||||
int sockets = 0;
|
|
||||||
#endif
|
|
||||||
int force=0;
|
|
||||||
int force_reload=0;
|
|
||||||
int proxyport=80;
|
|
||||||
char *proxyhost=NULL;
|
|
||||||
int benchtime=30;
|
|
||||||
/* internal */
|
|
||||||
int mypipe[2];
|
|
||||||
char host[MAXHOSTNAMELEN];
|
|
||||||
#define REQUEST_SIZE 2048
|
|
||||||
char request[REQUEST_SIZE] = {0};
|
|
||||||
|
|
||||||
static const struct option long_options[]=
|
|
||||||
{
|
|
||||||
{"force",no_argument,&force,1},
|
|
||||||
{"reload",no_argument,&force_reload,1},
|
|
||||||
{"time",required_argument,NULL,'t'},
|
|
||||||
{"help",no_argument,NULL,'?'},
|
|
||||||
{"http09",no_argument,NULL,'9'},
|
|
||||||
{"http10",no_argument,NULL,'1'},
|
|
||||||
{"http11",no_argument,NULL,'2'},
|
|
||||||
{"get",no_argument,&method,METHOD_GET},
|
|
||||||
{"head",no_argument,&method,METHOD_HEAD},
|
|
||||||
{"options",no_argument,&method,METHOD_OPTIONS},
|
|
||||||
{"trace",no_argument,&method,METHOD_TRACE},
|
|
||||||
{"version",no_argument,NULL,'V'},
|
|
||||||
{"proxy",required_argument,NULL,'p'},
|
|
||||||
{"clients",required_argument,NULL,'c'},
|
|
||||||
{NULL,0,NULL,0}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* prototypes */
|
|
||||||
static void benchcore(const char* host,const int port, const char *request);
|
|
||||||
static int bench(void);
|
|
||||||
static void build_request(const char *url);
|
|
||||||
|
|
||||||
static void alarm_handler(int signal)
|
|
||||||
{
|
|
||||||
timerexpired=1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usage(void)
|
|
||||||
{
|
|
||||||
fprintf(stderr,
|
|
||||||
"webbench [option]... URL\n"
|
|
||||||
" -f|--force Don't wait for reply from server.\n"
|
|
||||||
" -r|--reload Send reload request - Pragma: no-cache.\n"
|
|
||||||
" -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n"
|
|
||||||
" -n|--request <num> Sum of requestion\n"
|
|
||||||
" -s|--socket <num> Sum of socket\n"
|
|
||||||
" -p|--proxy <server:port> Use proxy server for request.\n"
|
|
||||||
" -c|--clients <n> Run <n> HTTP clients at once. Default one.\n"
|
|
||||||
" -9|--http09 Use HTTP/0.9 style requests.\n"
|
|
||||||
" -1|--http10 Use HTTP/1.0 protocol.\n"
|
|
||||||
" -2|--http11 Use HTTP/1.1 protocol.\n"
|
|
||||||
" --get Use GET request method.\n"
|
|
||||||
" --head Use HEAD request method.\n"
|
|
||||||
" --options Use OPTIONS request method.\n"
|
|
||||||
" --trace Use TRACE request method.\n"
|
|
||||||
" -?|-h|--help This information.\n"
|
|
||||||
" -V|--version Display program version.\n"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int opt=0;
|
|
||||||
int options_index=0;
|
|
||||||
char *tmp=NULL;
|
|
||||||
|
|
||||||
if(argc==1)
|
|
||||||
{
|
|
||||||
usage();
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
while((opt=getopt_long(argc,argv,"912Vfrt:p:n:s:c:?h",long_options,&options_index))!=EOF )
|
|
||||||
{
|
|
||||||
switch(opt)
|
|
||||||
{
|
|
||||||
case 0 : break;
|
|
||||||
case 'f': force=1;break;
|
|
||||||
case 'r': force_reload=1;break;
|
|
||||||
case '9': http10=0;break;
|
|
||||||
case '1': http10=1;break;
|
|
||||||
case '2': http10=2;break;
|
|
||||||
case 'V': printf(PROGRAM_VERSION"\n");exit(0);
|
|
||||||
case 't': benchtime=atoi(optarg);break;
|
|
||||||
case 'p':
|
|
||||||
/* proxy server parsing server:port */
|
|
||||||
tmp=strrchr(optarg,':');
|
|
||||||
proxyhost=optarg;
|
|
||||||
if(tmp==NULL)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(tmp==optarg)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if(tmp==optarg+strlen(optarg)-1)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
*tmp='\0';
|
|
||||||
proxyport=atoi(tmp+1);break;
|
|
||||||
case ':':
|
|
||||||
case 'h':
|
|
||||||
case '?': usage();return 2;break;
|
|
||||||
case 'c': clients=atoi(optarg);break;
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
case 'n': nreqs=atoi(optarg);break;
|
|
||||||
case 's': sockets=atoi(optarg);break;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(optind==argc) {
|
|
||||||
fprintf(stderr,"webbench: Missing URL!\n");
|
|
||||||
usage();
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(clients==0) clients=1;
|
|
||||||
if(benchtime==0) benchtime=60;
|
|
||||||
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
if (clients > 10) clients = 10;
|
|
||||||
if (nreqs < 1000) nreqs = 1000;
|
|
||||||
//if (sockets < clients) sockets = clients;
|
|
||||||
if (sockets % clients != 0) sockets = sockets / clients * clients;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Copyright */
|
|
||||||
fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
|
|
||||||
"Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
|
|
||||||
);
|
|
||||||
build_request(argv[optind]);
|
|
||||||
/* print bench info */
|
|
||||||
printf("\nBenchmarking: ");
|
|
||||||
switch(method)
|
|
||||||
{
|
|
||||||
case METHOD_GET:
|
|
||||||
default:
|
|
||||||
printf("GET");break;
|
|
||||||
case METHOD_OPTIONS:
|
|
||||||
printf("OPTIONS");break;
|
|
||||||
case METHOD_HEAD:
|
|
||||||
printf("HEAD");break;
|
|
||||||
case METHOD_TRACE:
|
|
||||||
printf("TRACE");break;
|
|
||||||
}
|
|
||||||
printf(" %s",argv[optind]);
|
|
||||||
switch(http10)
|
|
||||||
{
|
|
||||||
case 0: printf(" (using HTTP/0.9)");break;
|
|
||||||
case 2: printf(" (using HTTP/1.1)");break;
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
if(clients==1) printf("1 client");
|
|
||||||
else
|
|
||||||
printf("%d clients",clients);
|
|
||||||
|
|
||||||
printf(", running %d sec", benchtime);
|
|
||||||
if(force) printf(", early socket close");
|
|
||||||
if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);
|
|
||||||
if(force_reload) printf(", forcing reload");
|
|
||||||
printf(".\n");
|
|
||||||
return bench();
|
|
||||||
}
|
|
||||||
|
|
||||||
void build_request(const char *url)
|
|
||||||
{
|
|
||||||
char tmp[10];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
bzero(host,MAXHOSTNAMELEN);
|
|
||||||
bzero(request,REQUEST_SIZE);
|
|
||||||
|
|
||||||
if(force_reload && proxyhost!=NULL && http10<1) http10=1;
|
|
||||||
if(method==METHOD_HEAD && http10<1) http10=1;
|
|
||||||
if(method==METHOD_OPTIONS && http10<2) http10=2;
|
|
||||||
if(method==METHOD_TRACE && http10<2) http10=2;
|
|
||||||
|
|
||||||
switch(method)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case METHOD_GET: strcpy(request,"GET");break;
|
|
||||||
case METHOD_HEAD: strcpy(request,"HEAD");break;
|
|
||||||
case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
|
|
||||||
case METHOD_TRACE: strcpy(request,"TRACE");break;
|
|
||||||
}
|
|
||||||
|
|
||||||
strcat(request," ");
|
|
||||||
|
|
||||||
if(NULL==strstr(url,"://"))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n%s: is not a valid URL.\n",url);
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
if(strlen(url)>1500)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"URL is too long.\n");
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
if(proxyhost==NULL)
|
|
||||||
if (0!=strncasecmp("http://",url,7))
|
|
||||||
{ fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
/* protocol/host delimiter */
|
|
||||||
i=strstr(url,"://")-url+3;
|
|
||||||
/* printf("%d\n",i); */
|
|
||||||
|
|
||||||
if(strchr(url+i,'/')==NULL) {
|
|
||||||
fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
if(proxyhost==NULL)
|
|
||||||
{
|
|
||||||
/* get port from hostname */
|
|
||||||
if(index(url+i,':')!=NULL &&
|
|
||||||
index(url+i,':')<index(url+i,'/'))
|
|
||||||
{
|
|
||||||
strncpy(host,url+i,strchr(url+i,':')-url-i);
|
|
||||||
bzero(tmp,10);
|
|
||||||
strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);
|
|
||||||
/* printf("tmp=%s\n",tmp); */
|
|
||||||
proxyport=atoi(tmp);
|
|
||||||
if(proxyport==0) proxyport=80;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
strncpy(host,url+i,strcspn(url+i,"/"));
|
|
||||||
}
|
|
||||||
// printf("Host=%s\n",host);
|
|
||||||
strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
// printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
|
|
||||||
strcat(request,url);
|
|
||||||
}
|
|
||||||
if(http10==1)
|
|
||||||
strcat(request," HTTP/1.0");
|
|
||||||
else if (http10==2)
|
|
||||||
strcat(request," HTTP/1.1");
|
|
||||||
strcat(request,"\r\n");
|
|
||||||
if(http10>0)
|
|
||||||
strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");
|
|
||||||
if(proxyhost==NULL && http10>0)
|
|
||||||
{
|
|
||||||
strcat(request,"Host: ");
|
|
||||||
strcat(request,host);
|
|
||||||
strcat(request,"\r\n");
|
|
||||||
}
|
|
||||||
if(force_reload && proxyhost!=NULL)
|
|
||||||
{
|
|
||||||
strcat(request,"Pragma: no-cache\r\n");
|
|
||||||
}
|
|
||||||
if(http10>1)
|
|
||||||
strcat(request,"Connection: close\r\n");
|
|
||||||
/* add empty line at end */
|
|
||||||
if(http10>0) strcat(request,"\r\n");
|
|
||||||
// printf("Req=%s\n",request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vraci system rc error kod */
|
|
||||||
static int bench(void)
|
|
||||||
{
|
|
||||||
int i,j,k;
|
|
||||||
pid_t pid=0;
|
|
||||||
FILE *f;
|
|
||||||
#if ENABLE_NTYCO == 0
|
|
||||||
/* check avaibility of target server */
|
|
||||||
i=Socket(proxyhost==NULL?host:proxyhost,proxyport);
|
|
||||||
if(i<0) {
|
|
||||||
fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
close(i);
|
|
||||||
#endif
|
|
||||||
/* create pipe */
|
|
||||||
if(pipe(mypipe))
|
|
||||||
{
|
|
||||||
perror("pipe failed.");
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* not needed, since we have alarm() in childrens */
|
|
||||||
/* wait 4 next system clock tick */
|
|
||||||
/*
|
|
||||||
cas=time(NULL);
|
|
||||||
while(time(NULL)==cas)
|
|
||||||
sched_yield();
|
|
||||||
*/
|
|
||||||
printf("bench enter\n");
|
|
||||||
|
|
||||||
/* fork childs */
|
|
||||||
for(i=0;i<clients;i++)
|
|
||||||
{
|
|
||||||
pid=fork();
|
|
||||||
if(pid <= (pid_t) 0)
|
|
||||||
{
|
|
||||||
/* child process or error*/
|
|
||||||
sleep(1); /* make childs faster */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( pid< (pid_t) 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"problems forking worker no. %d\n",i);
|
|
||||||
perror("fork failed.");
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(pid== (pid_t) 0)
|
|
||||||
{
|
|
||||||
/* I am a child */
|
|
||||||
if(proxyhost==NULL)
|
|
||||||
benchcore(host,proxyport,request);
|
|
||||||
else
|
|
||||||
benchcore(proxyhost,proxyport,request);
|
|
||||||
|
|
||||||
/* write results to pipe */
|
|
||||||
f=fdopen(mypipe[1],"w");
|
|
||||||
if(f==NULL)
|
|
||||||
{
|
|
||||||
perror("open pipe for writing failed.");
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
/* fprintf(stderr,"Child - %d %d\n",speed,failed); */
|
|
||||||
fprintf(f,"%d %d %d\n",speed,failed,bytes);
|
|
||||||
fclose(f);
|
|
||||||
return 0;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
f=fdopen(mypipe[0],"r");
|
|
||||||
if(f==NULL)
|
|
||||||
{
|
|
||||||
perror("open pipe for reading failed.");
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
setvbuf(f,NULL,_IONBF,0);
|
|
||||||
speed=0;
|
|
||||||
failed=0;
|
|
||||||
bytes=0;
|
|
||||||
|
|
||||||
while(1)
|
|
||||||
{
|
|
||||||
pid=fscanf(f,"%d %d %d",&i,&j,&k);
|
|
||||||
if(pid<2)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Some of our childrens died.\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
speed+=i;
|
|
||||||
failed+=j;
|
|
||||||
bytes+=k;
|
|
||||||
/* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
|
|
||||||
if(--clients==0) break;
|
|
||||||
}
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
|
|
||||||
(int)((speed+failed)/(benchtime/60.0f)),
|
|
||||||
(int)(bytes/(float)benchtime),
|
|
||||||
speed,
|
|
||||||
failed);
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct serv_host {
|
|
||||||
const char *host;
|
|
||||||
const int port;
|
|
||||||
const char *req;
|
|
||||||
};
|
|
||||||
|
|
||||||
int testok(char *response) {
|
|
||||||
if (response == NULL) return -1;
|
|
||||||
|
|
||||||
char* p;
|
|
||||||
char* q;
|
|
||||||
|
|
||||||
p = strchr(response, '\n');
|
|
||||||
*p = '\0';
|
|
||||||
if(NULL != (q = strstr(response, "200 OK")))
|
|
||||||
return 1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int send_buffer(int sockfd, char *buffer, int length) {
|
|
||||||
|
|
||||||
int idx = 0;
|
|
||||||
while (idx < length) {
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
if ((idx+MAX_BUFFER_LENGTH) < length) {
|
|
||||||
count = send(sockfd, buffer+idx, MAX_BUFFER_LENGTH, 0);
|
|
||||||
} else {
|
|
||||||
count = send(sockfd, buffer+idx, length-idx, 0);
|
|
||||||
}
|
|
||||||
if (count < 0) break;
|
|
||||||
|
|
||||||
idx += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int recv_buffer(int sockfd, char *buffer, int length, int *ret) {
|
|
||||||
|
|
||||||
int idx = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
int count = recv(sockfd, buffer+idx, MAX_BUFFER_LENGTH, 0);
|
|
||||||
//printf("sockfd : %d, recv_buffer --> %d\n", sockfd, count);
|
|
||||||
if (count < 0) {
|
|
||||||
*ret = -1;
|
|
||||||
break;
|
|
||||||
} else if (count == 0) {
|
|
||||||
*ret = 0;
|
|
||||||
close(sockfd);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
//printf("sockfd : %d, recv_buffer --> %d\n", sockfd, count);
|
|
||||||
idx += count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (idx > 0) *ret = 1;
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void httprequest_commit(void *arg) {
|
|
||||||
char buf[1500] = {0};
|
|
||||||
struct serv_host *shost = (struct serv_host *)arg;
|
|
||||||
|
|
||||||
int slen = strlen(shost->req);
|
|
||||||
|
|
||||||
int idx = 0;
|
|
||||||
for (idx = 0;idx < (nreqs / sockets);idx ++) {
|
|
||||||
|
|
||||||
int s = Socket(shost->host, shost->port);
|
|
||||||
if (s < 0) {
|
|
||||||
failed ++;
|
|
||||||
continue ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slen != send(s, shost->req, slen, 0)) {
|
|
||||||
failed ++;
|
|
||||||
close(s);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(buf, 0, 1500);
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
int ret = 0;
|
|
||||||
int rlen = recv_buffer(s, buf, 1500, &ret);
|
|
||||||
#else
|
|
||||||
int rlen = recv(s, buf, 1500, 0);
|
|
||||||
#endif
|
|
||||||
if (ret < 0) {
|
|
||||||
failed ++;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if (testok(buf)) {
|
|
||||||
speed ++;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
failed ++;
|
|
||||||
|
|
||||||
}
|
|
||||||
bytes += rlen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void benchcore(const char *host,const int port,const char *req)
|
|
||||||
{
|
|
||||||
int rlen;
|
|
||||||
char buf[1500];
|
|
||||||
int s,i;
|
|
||||||
struct sigaction sa;
|
|
||||||
|
|
||||||
/* setup alarm signal handler */
|
|
||||||
sa.sa_handler=alarm_handler;
|
|
||||||
sa.sa_flags=0;
|
|
||||||
if(sigaction(SIGALRM,&sa,NULL))
|
|
||||||
exit(3);
|
|
||||||
alarm(benchtime);
|
|
||||||
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
|
|
||||||
//int i = 0;
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
struct serv_host shost = {host, port, req};
|
|
||||||
|
|
||||||
for (i = 0;i < sockets / clients;i ++) {
|
|
||||||
|
|
||||||
nty_coroutine_create(&co, httprequest_commit, &shost);
|
|
||||||
}
|
|
||||||
nty_schedule_run();
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
rlen=strlen(req);
|
|
||||||
nexttry:while(1)
|
|
||||||
{
|
|
||||||
if(timerexpired)
|
|
||||||
{
|
|
||||||
if(failed>0)
|
|
||||||
{
|
|
||||||
/* fprintf(stderr,"Correcting failed by signal\n"); */
|
|
||||||
failed--;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
s=Socket(host,port);
|
|
||||||
if(s<0) { failed++;continue;}
|
|
||||||
if(rlen!=write(s, req, rlen)) {failed++;close(s);continue;}
|
|
||||||
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
if(http10==0)
|
|
||||||
close(s);
|
|
||||||
#else
|
|
||||||
if(http10==0)
|
|
||||||
if(shutdown(s,1)) { failed++;close(s);continue;}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
if(force==0)
|
|
||||||
{
|
|
||||||
/* read all available data from socket */
|
|
||||||
while(1)
|
|
||||||
{
|
|
||||||
if(timerexpired) break;
|
|
||||||
i=read(s,buf,1500);
|
|
||||||
/* fprintf(stderr,"%d\n",i); */
|
|
||||||
if(i<0)
|
|
||||||
{
|
|
||||||
failed++;
|
|
||||||
close(s);
|
|
||||||
goto nexttry;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if(i==0) break;
|
|
||||||
else
|
|
||||||
bytes+=i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(close(s)) {failed++;continue;}
|
|
||||||
speed++;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define NTY_SERVER_IPADDR "127.0.0.1"
|
|
||||||
#define NTY_SERVER_PORT 9096
|
|
||||||
|
|
||||||
int init_client(void) {
|
|
||||||
|
|
||||||
int clientfd = nty_socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (clientfd <= 0) {
|
|
||||||
printf("socket failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in serveraddr = {0};
|
|
||||||
serveraddr.sin_family = AF_INET;
|
|
||||||
serveraddr.sin_port = htons(NTY_SERVER_PORT);
|
|
||||||
serveraddr.sin_addr.s_addr = inet_addr(NTY_SERVER_IPADDR);
|
|
||||||
|
|
||||||
int result = nty_connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
|
|
||||||
if (result != 0) {
|
|
||||||
printf("connect failed\n");
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return clientfd;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void client(void *arg) {
|
|
||||||
|
|
||||||
int clientfd = init_client();
|
|
||||||
char *buffer = "ntyco_client\r\n";
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
int length = nty_send(clientfd, buffer, strlen(buffer), 0);
|
|
||||||
printf("echo length : %d\n", length);
|
|
||||||
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
|
|
||||||
nty_coroutine_create(&co, client, NULL);
|
|
||||||
|
|
||||||
nty_schedule_run(); //run
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,745 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/sysinfo.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
#include <sys/shm.h>
|
|
||||||
#include <sys/sem.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
|
|
||||||
#define __USE_GNU
|
|
||||||
|
|
||||||
#include <sched.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#define MAX_CLIENT_NUM 1000000
|
|
||||||
#define TOTALFDS 16
|
|
||||||
|
|
||||||
typedef struct _shm_area {
|
|
||||||
int totalfds[TOTALFDS];
|
|
||||||
char cpu_lb[TOTALFDS];
|
|
||||||
//mtx : default 0
|
|
||||||
// 1 : lock -- del epoll
|
|
||||||
// 2 : lock -- del complete
|
|
||||||
// 3 : unlock -- add
|
|
||||||
// 0 : unlock -- add complete
|
|
||||||
int accept_mtx;
|
|
||||||
} shm_area;
|
|
||||||
|
|
||||||
static shm_area *global_shmaddr = NULL;
|
|
||||||
static int global_shmid = -1;
|
|
||||||
|
|
||||||
int cpu_size = 0;
|
|
||||||
int accept_disable = 1000;
|
|
||||||
int enable_accept = 1;
|
|
||||||
pid_t self_id = 0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned long cmpxchg(void *addr, unsigned long _old, unsigned long _new, int size) {
|
|
||||||
unsigned long prev;
|
|
||||||
volatile unsigned int *_ptr = (volatile unsigned int *)(addr);
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 1: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchgb %b1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchgw %w1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 4: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchg %1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
int atomic_add(volatile int *value, int add)
|
|
||||||
{
|
|
||||||
__asm__ volatile (
|
|
||||||
|
|
||||||
"lock;"
|
|
||||||
" addl %0, %1; "
|
|
||||||
|
|
||||||
: "+r" (add) : "m" (*value) : "cc", "memory");
|
|
||||||
|
|
||||||
return add;
|
|
||||||
}
|
|
||||||
|
|
||||||
int atomic_sub(volatile int *value, int sub)
|
|
||||||
{
|
|
||||||
__asm__ volatile (
|
|
||||||
|
|
||||||
"lock;"
|
|
||||||
" subl %0, %1; "
|
|
||||||
|
|
||||||
: "+r" (sub) : "m" (*value) : "cc", "memory");
|
|
||||||
|
|
||||||
return sub;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define ISspace(x) isspace((int)(x))
|
|
||||||
|
|
||||||
#define SERVER_STRING "Server: ntyco_httpd/0.1.0\r\n"
|
|
||||||
#define STDIN 0
|
|
||||||
#define STDOUT 1
|
|
||||||
#define STDERR 2
|
|
||||||
|
|
||||||
#define ENABLE_NTYCO 0
|
|
||||||
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
|
|
||||||
#define socket nty_socket
|
|
||||||
#define accept nty_accept
|
|
||||||
#define recv(a, b, c, d) nty_recv(a, b, c, d)
|
|
||||||
#define send(a, b, c, d) nty_send(a, b, c, d)
|
|
||||||
|
|
||||||
#define MAX_BUFFER_LENGTH 1024
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define INC_COUNTFD do { \
|
|
||||||
atomic_add(&global_shmaddr->totalfds[self_id % cpu_size], 1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
#define DEC_COUNTFD do { \
|
|
||||||
atomic_sub(&global_shmaddr->totalfds[self_id % cpu_size], 1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
int get_countfd(void) {
|
|
||||||
return global_shmaddr->totalfds[self_id % cpu_size];
|
|
||||||
}
|
|
||||||
|
|
||||||
int max_countfd(void) {
|
|
||||||
|
|
||||||
int count = -1;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
if (count < global_shmaddr->totalfds[i]) {
|
|
||||||
count = global_shmaddr->totalfds[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int min_countfd(void) {
|
|
||||||
|
|
||||||
int count = 0xffffffff;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
if (count > global_shmaddr->totalfds[i]) {
|
|
||||||
count = global_shmaddr->totalfds[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare_out_countfd(void) {
|
|
||||||
|
|
||||||
int current = get_countfd();
|
|
||||||
int min = min_countfd();
|
|
||||||
|
|
||||||
if ((current * 7 / 8) > min) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare_in_countfd(void) {
|
|
||||||
|
|
||||||
int current = get_countfd();
|
|
||||||
int max = max_countfd();
|
|
||||||
|
|
||||||
if ((current * 8 / 7) < max) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_countfds(void) {
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
printf("%5d : %5d ", i, global_shmaddr->totalfds[i]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void lock_accept(void) {
|
|
||||||
|
|
||||||
global_shmaddr->cpu_lb[self_id % cpu_size] = 1;
|
|
||||||
|
|
||||||
int count = 0xffffffff;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
if (count > global_shmaddr->totalfds[i]) {
|
|
||||||
count = global_shmaddr->totalfds[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
if (count == global_shmaddr->totalfds[i]) {
|
|
||||||
global_shmaddr->cpu_lb[i] = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
char read_accept(void) {
|
|
||||||
return global_shmaddr->cpu_lb[self_id % cpu_size];
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_accept(char state) { //0, 1, 2, 3
|
|
||||||
global_shmaddr->cpu_lb[self_id % cpu_size] = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lock(void) {
|
|
||||||
|
|
||||||
return cmpxchg(&global_shmaddr->accept_mtx, 0, 1, 4); //zero:success, non-zero:failed
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock(void) {
|
|
||||||
|
|
||||||
global_shmaddr->accept_mtx = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void accept_request(int fd, int epfd);
|
|
||||||
void bad_request(int);
|
|
||||||
void cat(int);
|
|
||||||
void cannot_execute(int);
|
|
||||||
void error_die(const char *);
|
|
||||||
void execute_cgi(int, const char *, const char *, const char *);
|
|
||||||
int get_line(int, char *, int);
|
|
||||||
void headers(int);
|
|
||||||
void not_found(int);
|
|
||||||
void serve_file(int);
|
|
||||||
int startup(u_short *);
|
|
||||||
void unimplemented(int);
|
|
||||||
|
|
||||||
|
|
||||||
ssize_t nsend(int fd, const void *buf, size_t len, int flags) {
|
|
||||||
|
|
||||||
int sent = 0;
|
|
||||||
|
|
||||||
int ret = send(fd, ((char*)buf)+sent, len-sent, flags);
|
|
||||||
if (ret == 0) return ret;
|
|
||||||
if (ret > 0) sent += ret;
|
|
||||||
|
|
||||||
while (sent < len) {
|
|
||||||
#if 0
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN | POLLERR | POLLHUP;
|
|
||||||
poll(&fds, 1, -1);
|
|
||||||
#endif
|
|
||||||
ret = send(fd, ((char*)buf)+sent, len-sent, flags);
|
|
||||||
//printf("send --> len : %d\n", ret);
|
|
||||||
if (ret <= 0) {
|
|
||||||
if (errno == EAGAIN) continue;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sent += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret <= 0 && sent == 0) return ret;
|
|
||||||
|
|
||||||
return sent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntySetNonblock(int fd) {
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
flags = fcntl(fd, F_GETFL, 0);
|
|
||||||
if (flags < 0) return flags;
|
|
||||||
flags |= O_NONBLOCK;
|
|
||||||
if (fcntl(fd, F_SETFL, flags) < 0) return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntySetReUseAddr(int fd) {
|
|
||||||
int reuse = 1;
|
|
||||||
return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* A request has caused a call to accept() on the server port to
|
|
||||||
* return. Process the request appropriately.
|
|
||||||
* Parameters: the socket connected to the client */
|
|
||||||
/**********************************************************************/
|
|
||||||
void accept_request(int fd, int epfd)
|
|
||||||
{
|
|
||||||
int client = fd;
|
|
||||||
|
|
||||||
char buf[1024];
|
|
||||||
size_t numchars;
|
|
||||||
char method[16];
|
|
||||||
char url[32];
|
|
||||||
char path[64];
|
|
||||||
size_t i, j;
|
|
||||||
struct stat st;
|
|
||||||
int cgi = 0; /* becomes true if server decides this is a CGI
|
|
||||||
* program */
|
|
||||||
char *query_string = NULL;
|
|
||||||
|
|
||||||
numchars = recv(client, buf, sizeof(buf), 0);
|
|
||||||
if (numchars == -1) {
|
|
||||||
//printf("recv errno : %d\n", errno);
|
|
||||||
if (errno == ECONNRESET) {
|
|
||||||
close(client);
|
|
||||||
|
|
||||||
struct epoll_event ev;
|
|
||||||
ev.events = EPOLLIN | EPOLLET;
|
|
||||||
ev.data.fd = client;
|
|
||||||
epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev);
|
|
||||||
|
|
||||||
DEC_COUNTFD;
|
|
||||||
}
|
|
||||||
} else if (numchars == 0) {
|
|
||||||
close(client);
|
|
||||||
|
|
||||||
struct epoll_event ev;
|
|
||||||
ev.events = EPOLLIN | EPOLLET;
|
|
||||||
ev.data.fd = client;
|
|
||||||
epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev);
|
|
||||||
|
|
||||||
DEC_COUNTFD;
|
|
||||||
} else {
|
|
||||||
//printf("recv numchars : %ld\n", numchars);
|
|
||||||
}
|
|
||||||
//serve_file(client, path);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
#define HTML_PAGE "<HTML> \
|
|
||||||
<TITLE>Index</TITLE> \
|
|
||||||
<BODY> \
|
|
||||||
<P>Welcome to J. David's webserver.\
|
|
||||||
<H1>CGI demo \
|
|
||||||
<FORM ACTION=\"color.cgi\" METHOD=\"POST\">\
|
|
||||||
Enter a color: <INPUT TYPE=\"text\" NAME=\"color\">\
|
|
||||||
<INPUT TYPE=\"submit\">\
|
|
||||||
</FORM>\
|
|
||||||
</BODY>\
|
|
||||||
</HTML>"
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define HTML_PAGE "<!DOCTYPE html> \
|
|
||||||
<html> \
|
|
||||||
<head> \
|
|
||||||
<title>Welcome to nginx!</title> \
|
|
||||||
<style> \
|
|
||||||
body { \
|
|
||||||
width: 35em;\
|
|
||||||
margin: 0 auto;\
|
|
||||||
font-family: Tahoma, Verdana, Arial, sans-serif;\
|
|
||||||
}\
|
|
||||||
</style>\
|
|
||||||
</head>\
|
|
||||||
<body>\
|
|
||||||
<h1>Welcome to nginx!</h1>\
|
|
||||||
<p>If you see this page, the nginx web server is successfully installed and\
|
|
||||||
working. Further configuration is required.</p>\
|
|
||||||
\
|
|
||||||
<p>For online documentation and support please refer to\
|
|
||||||
<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\
|
|
||||||
Commercial support is available at\
|
|
||||||
<a href=\"http://nginx.com/\">nginx.com</a>.</p>\
|
|
||||||
\
|
|
||||||
<p><em>Thank you for using nginx.</em></p>\
|
|
||||||
</body>\
|
|
||||||
</html>"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void cat(int client)
|
|
||||||
{
|
|
||||||
|
|
||||||
//char buf[1024] = HTML_PAGE;
|
|
||||||
|
|
||||||
int ret = send(client, HTML_PAGE, strlen(HTML_PAGE), 0);
|
|
||||||
if (ret == -1) {
|
|
||||||
printf("send : errno : %d\n", errno);
|
|
||||||
} else {
|
|
||||||
printf("cat send ret : %d\n", ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void error_die(const char *sc)
|
|
||||||
{
|
|
||||||
printf("%s\n", sc);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void headers(int client)
|
|
||||||
{
|
|
||||||
char buf[1024] = {0};
|
|
||||||
char content[128] = {0};
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
|
||||||
strcat(buf, SERVER_STRING);
|
|
||||||
strcat(buf, "Content-Type: text/html\r\n");
|
|
||||||
#if 0
|
|
||||||
strcat(buf, "Transfer-Encoding: chunked\r\n");
|
|
||||||
#else
|
|
||||||
sprintf(content, "Content-Length: %ld\r\n", strlen(HTML_PAGE));
|
|
||||||
strcat(buf, content);
|
|
||||||
#endif
|
|
||||||
strcat(buf, "\r\n");
|
|
||||||
|
|
||||||
strcat(buf, HTML_PAGE);
|
|
||||||
|
|
||||||
int ret = send(client, buf, strlen(buf), 0);
|
|
||||||
if (ret == -1) {
|
|
||||||
printf("send : errno : %d\n", errno);
|
|
||||||
} else {
|
|
||||||
//printf("headers send ret : %d\n", ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Send a regular file to the client. Use headers, and report
|
|
||||||
* errors to client if they occur.
|
|
||||||
* Parameters: a pointer to a file structure produced from the socket
|
|
||||||
* file descriptor
|
|
||||||
* the name of the file to serve */
|
|
||||||
/**********************************************************************/
|
|
||||||
void serve_file(int client)
|
|
||||||
{
|
|
||||||
headers(client);
|
|
||||||
//cat(client);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_EPOLLSIZE 10240
|
|
||||||
|
|
||||||
void server(void *arg) {
|
|
||||||
|
|
||||||
int fd = *(int *)arg;
|
|
||||||
|
|
||||||
int epfd = epoll_create(1);
|
|
||||||
struct epoll_event events[MAX_EPOLLSIZE];
|
|
||||||
|
|
||||||
struct epoll_event ev;
|
|
||||||
ev.events = EPOLLIN;
|
|
||||||
ev.data.fd = fd;
|
|
||||||
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
|
|
||||||
int acc = 1; //
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
#if 0
|
|
||||||
char lock = read_accept();
|
|
||||||
if (lock == 1) { //lock
|
|
||||||
ev.events = EPOLLIN;
|
|
||||||
ev.data.fd = fd;
|
|
||||||
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
|
|
||||||
|
|
||||||
write_accept(2); //add complete
|
|
||||||
|
|
||||||
} else if (acc == 0 && lock == 3) {
|
|
||||||
ev.events = EPOLLIN;
|
|
||||||
ev.data.fd = fd;
|
|
||||||
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
|
|
||||||
|
|
||||||
write_accept(0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
int nready = epoll_wait(epfd, events, MAX_EPOLLSIZE, 1);
|
|
||||||
if (nready == -1) {
|
|
||||||
if (errno == EINTR) {
|
|
||||||
printf("errno : EINTR\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (i = 0;i < nready;i ++) {
|
|
||||||
int sockfd = events[i].data.fd;
|
|
||||||
if (sockfd == fd) {
|
|
||||||
|
|
||||||
struct sockaddr_in client_addr;
|
|
||||||
memset(&client_addr, 0, sizeof(struct sockaddr_in));
|
|
||||||
socklen_t client_len = sizeof(client_addr);
|
|
||||||
#if 0
|
|
||||||
if (get_countfd() % accept_disable == 999) {
|
|
||||||
lock_accept();
|
|
||||||
acc = 0;
|
|
||||||
}
|
|
||||||
char lock = read_accept();
|
|
||||||
if (lock != 0 && lock != 3) continue;
|
|
||||||
#endif
|
|
||||||
if (lock()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
|
|
||||||
unlock();
|
|
||||||
|
|
||||||
if (clientfd < 0) {
|
|
||||||
if (errno == EAGAIN) {
|
|
||||||
//printf("accept : EAGAIN\n");
|
|
||||||
continue;
|
|
||||||
} else if (errno == ECONNABORTED) {
|
|
||||||
printf("accept : ECONNABORTED\n");
|
|
||||||
|
|
||||||
} else if (errno == EMFILE || errno == ENFILE) {
|
|
||||||
printf("accept : EMFILE || ENFILE\n");
|
|
||||||
}
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
INC_COUNTFD;
|
|
||||||
|
|
||||||
ntySetNonblock(clientfd);
|
|
||||||
ntySetReUseAddr(clientfd);
|
|
||||||
|
|
||||||
struct epoll_event ev;
|
|
||||||
ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
|
|
||||||
ev.data.fd = clientfd;
|
|
||||||
epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (events[i].events & EPOLLIN) {
|
|
||||||
|
|
||||||
accept_request(sockfd, epfd);
|
|
||||||
#if 1
|
|
||||||
//struct epoll_event ev;
|
|
||||||
ev.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
|
|
||||||
ev.data.fd = sockfd;
|
|
||||||
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
|
|
||||||
|
|
||||||
//close(sockfd);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (events[i].events & EPOLLOUT) {
|
|
||||||
|
|
||||||
serve_file(sockfd);
|
|
||||||
|
|
||||||
//close(sockfd);
|
|
||||||
|
|
||||||
//struct epoll_event ev;
|
|
||||||
ev.events = EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR;
|
|
||||||
ev.data.fd = sockfd;
|
|
||||||
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
|
|
||||||
|
|
||||||
DEC_COUNTFD;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (events[i].events & (EPOLLHUP|EPOLLRDHUP|EPOLLERR)) {
|
|
||||||
|
|
||||||
close(sockfd);
|
|
||||||
|
|
||||||
printf(" EPOLLHUP | EPOLLRDHUP\n");
|
|
||||||
//struct epoll_event ev;
|
|
||||||
ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
|
|
||||||
ev.data.fd = sockfd;
|
|
||||||
epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, &ev);
|
|
||||||
|
|
||||||
DEC_COUNTFD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// print_countfds();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int mulcore_entry(int begin_port) {
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
unsigned short base_port = begin_port;
|
|
||||||
|
|
||||||
unsigned short *port = calloc(1, sizeof(unsigned short));
|
|
||||||
*port = base_port;
|
|
||||||
server(port);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_bind(int fd) {
|
|
||||||
|
|
||||||
int num = sysconf(_SC_NPROCESSORS_CONF);
|
|
||||||
|
|
||||||
self_id = syscall(__NR_gettid);
|
|
||||||
//printf("selfid --> %d\n", self_id);
|
|
||||||
|
|
||||||
cpu_set_t mask;
|
|
||||||
|
|
||||||
CPU_ZERO(&mask);
|
|
||||||
CPU_SET(self_id % num, &mask);
|
|
||||||
|
|
||||||
sched_setaffinity(0, sizeof(mask), &mask);
|
|
||||||
server(&fd);
|
|
||||||
//mulcore_entry(9000);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int init_shmvalue(int num) {
|
|
||||||
|
|
||||||
global_shmid = shmget(IPC_PRIVATE, sizeof(shm_area), IPC_CREAT|0600);
|
|
||||||
if (global_shmid < 0) {
|
|
||||||
perror("shmget failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
global_shmaddr = (shm_area*)shmat(global_shmid, NULL, 0);
|
|
||||||
if (global_shmaddr == (shm_area*)-1) {
|
|
||||||
perror("shmat addr error");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memset(global_shmaddr->totalfds, 0, TOTALFDS * sizeof(int));
|
|
||||||
memset(global_shmaddr->cpu_lb, 0, TOTALFDS * sizeof(char));
|
|
||||||
global_shmaddr->accept_mtx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
short port = 9000;
|
|
||||||
struct sockaddr_in client_name;
|
|
||||||
socklen_t client_name_len = sizeof(client_name);
|
|
||||||
|
|
||||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (fd < 0) return -1;
|
|
||||||
|
|
||||||
ntySetNonblock(fd);
|
|
||||||
ntySetReUseAddr(fd);
|
|
||||||
|
|
||||||
|
|
||||||
struct sockaddr_in local, remote;
|
|
||||||
local.sin_family = AF_INET;
|
|
||||||
local.sin_port = htons(port);
|
|
||||||
local.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
bind(fd, (struct sockaddr*)&local, sizeof(struct sockaddr_in));
|
|
||||||
|
|
||||||
listen(fd, 20);
|
|
||||||
|
|
||||||
int num = sysconf(_SC_NPROCESSORS_CONF);
|
|
||||||
cpu_size = num;
|
|
||||||
init_shmvalue(num);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
pid_t pid=0;
|
|
||||||
for(i = 0;i < num;i ++) {
|
|
||||||
pid=fork();
|
|
||||||
if(pid <= (pid_t) 0)
|
|
||||||
{
|
|
||||||
usleep(1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if 1
|
|
||||||
if (pid > 0) {
|
|
||||||
printf("ntyco_httpd server running ...\n");
|
|
||||||
getchar();
|
|
||||||
} else if (pid == 0) {
|
|
||||||
process_bind(fd);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
|
|
||||||
|
|
||||||
INC_COUNTFD;
|
|
||||||
|
|
||||||
INC_COUNTFD;
|
|
||||||
print_countfds();
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,634 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* J. David's webserver */
|
|
||||||
/* This is a simple webserver.
|
|
||||||
* Created November 1999 by J. David Blackstone.
|
|
||||||
* CSE 4344 (Network concepts), Prof. Zeigler
|
|
||||||
* University of Texas at Arlington
|
|
||||||
*/
|
|
||||||
/* This program compiles for Sparc Solaris 2.6.
|
|
||||||
* To compile for Linux:
|
|
||||||
* 1) Comment out the #include <pthread.h> line.
|
|
||||||
* 2) Comment out the line that defines the variable newthread.
|
|
||||||
* 3) Comment out the two lines that run pthread_create().
|
|
||||||
* 4) Uncomment the line that runs accept_request().
|
|
||||||
* 5) Remove -lsocket from the Makefile.
|
|
||||||
*/
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define ISspace(x) isspace((int)(x))
|
|
||||||
|
|
||||||
#define SERVER_STRING "Server: ntyco_httpd/0.1.0\r\n"
|
|
||||||
#define STDIN 0
|
|
||||||
#define STDOUT 1
|
|
||||||
#define STDERR 2
|
|
||||||
|
|
||||||
#define ENABLE_NTYCO 1
|
|
||||||
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
#define socket nty_socket
|
|
||||||
#define accept nty_accept
|
|
||||||
#define recv(a, b, c, d) nty_recv(a, b, c, d)
|
|
||||||
#define send(a, b, c, d) nty_send(a, b, c, d)
|
|
||||||
|
|
||||||
#define MAX_BUFFER_LENGTH 1024
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void accept_request(void *);
|
|
||||||
void bad_request(int);
|
|
||||||
void cat(int, FILE *);
|
|
||||||
void cannot_execute(int);
|
|
||||||
void error_die(const char *);
|
|
||||||
void execute_cgi(int, const char *, const char *, const char *);
|
|
||||||
int get_line(int, char *, int);
|
|
||||||
void headers(int, const char *);
|
|
||||||
void not_found(int);
|
|
||||||
void serve_file(int, const char *);
|
|
||||||
int startup(u_short *);
|
|
||||||
void unimplemented(int);
|
|
||||||
|
|
||||||
int readline(char* allbuf,int level,char* linebuf){
|
|
||||||
int len = strlen(allbuf);
|
|
||||||
|
|
||||||
for (;level < len; ++level) {
|
|
||||||
if(allbuf[level]=='\r' && allbuf[level+1]=='\n')
|
|
||||||
return level+2;
|
|
||||||
else
|
|
||||||
*(linebuf++) = allbuf[level];
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* A request has caused a call to accept() on the server port to
|
|
||||||
* return. Process the request appropriately.
|
|
||||||
* Parameters: the socket connected to the client */
|
|
||||||
/**********************************************************************/
|
|
||||||
void accept_request(void *arg)
|
|
||||||
{
|
|
||||||
int *pclient = (int*)arg;
|
|
||||||
int client = *pclient;
|
|
||||||
|
|
||||||
char buf[1024];
|
|
||||||
size_t numchars;
|
|
||||||
char method[255];
|
|
||||||
char url[255];
|
|
||||||
char path[512];
|
|
||||||
size_t i, j;
|
|
||||||
struct stat st;
|
|
||||||
int cgi = 0; /* becomes true if server decides this is a CGI
|
|
||||||
* program */
|
|
||||||
char *query_string = NULL;
|
|
||||||
|
|
||||||
numchars = nty_recv(client, buf, sizeof(buf), 0);
|
|
||||||
|
|
||||||
i = 0; j = 0;
|
|
||||||
while (!ISspace(buf[i]) && (i < sizeof(method) - 1))
|
|
||||||
{
|
|
||||||
method[i] = buf[i];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
j=i;
|
|
||||||
method[i] = '\0';
|
|
||||||
|
|
||||||
if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
|
|
||||||
{
|
|
||||||
unimplemented(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcasecmp(method, "POST") == 0)
|
|
||||||
cgi = 1;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (ISspace(buf[j]) && (j < numchars))
|
|
||||||
j++;
|
|
||||||
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < numchars))
|
|
||||||
{
|
|
||||||
url[i] = buf[j];
|
|
||||||
i++; j++;
|
|
||||||
}
|
|
||||||
url[i] = '\0';
|
|
||||||
|
|
||||||
if (strcasecmp(method, "GET") == 0)
|
|
||||||
{
|
|
||||||
query_string = url;
|
|
||||||
while ((*query_string != '?') && (*query_string != '\0'))
|
|
||||||
query_string++;
|
|
||||||
if (*query_string == '?')
|
|
||||||
{
|
|
||||||
cgi = 1;
|
|
||||||
*query_string = '\0';
|
|
||||||
query_string++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(path, "htdocs%s", url);
|
|
||||||
if (path[strlen(path) - 1] == '/')
|
|
||||||
strcat(path, "index.html");
|
|
||||||
if (stat(path, &st) == -1) {
|
|
||||||
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
not_found(client);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((st.st_mode & S_IFMT) == S_IFDIR)
|
|
||||||
strcat(path, "/index.html");
|
|
||||||
if ((st.st_mode & S_IXUSR) ||
|
|
||||||
(st.st_mode & S_IXGRP) ||
|
|
||||||
(st.st_mode & S_IXOTH) )
|
|
||||||
cgi = 1;
|
|
||||||
|
|
||||||
cgi = 0;
|
|
||||||
|
|
||||||
if (!cgi)
|
|
||||||
serve_file(client, path);
|
|
||||||
else
|
|
||||||
execute_cgi(client, path, method, query_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_close(client);
|
|
||||||
free(pclient);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Inform the client that a request it has made has a problem.
|
|
||||||
* Parameters: client socket */
|
|
||||||
/**********************************************************************/
|
|
||||||
void bad_request(int client)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
sprintf(buf, "Content-type: text/html\r\n");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
sprintf(buf, "\r\n");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
sprintf(buf, "<P>Your browser sent a bad request, ");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
sprintf(buf, "such as a POST without a Content-Length.\r\n");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Put the entire contents of a file out on a socket. This function
|
|
||||||
* is named after the UNIX "cat" command, because it might have been
|
|
||||||
* easier just to do something like pipe, fork, and exec("cat").
|
|
||||||
* Parameters: the client socket descriptor
|
|
||||||
* FILE pointer for the file to cat */
|
|
||||||
/**********************************************************************/
|
|
||||||
void cat(int client, FILE *resource)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
char *ch = fgets(buf, sizeof(buf), resource);
|
|
||||||
while (!feof(resource))
|
|
||||||
{
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
ch = fgets(buf, sizeof(buf), resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Inform the client that a CGI script could not be executed.
|
|
||||||
* Parameter: the client socket descriptor. */
|
|
||||||
/**********************************************************************/
|
|
||||||
void cannot_execute(int client)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "Content-type: text/html\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<P>Error prohibited CGI execution.\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Print out an error message with perror() (for system errors; based
|
|
||||||
* on value of errno, which indicates system call errors) and exit the
|
|
||||||
* program indicating an error. */
|
|
||||||
/**********************************************************************/
|
|
||||||
void error_die(const char *sc)
|
|
||||||
{
|
|
||||||
perror(sc);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Execute a CGI script. Will need to set environment variables as
|
|
||||||
* appropriate.
|
|
||||||
* Parameters: client socket descriptor
|
|
||||||
* path to the CGI script */
|
|
||||||
/**********************************************************************/
|
|
||||||
void execute_cgi(int client, const char *path,
|
|
||||||
const char *method, const char *query_string)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
int cgi_output[2];
|
|
||||||
int cgi_input[2];
|
|
||||||
pid_t pid;
|
|
||||||
int status;
|
|
||||||
int i;
|
|
||||||
char c;
|
|
||||||
int numchars = 1;
|
|
||||||
int content_length = -1;
|
|
||||||
|
|
||||||
buf[0] = 'A'; buf[1] = '\0';
|
|
||||||
if (strcasecmp(method, "GET") == 0)
|
|
||||||
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
else if (strcasecmp(method, "POST") == 0) /*POST*/
|
|
||||||
{
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
while ((numchars > 0) && strcmp("\n", buf))
|
|
||||||
{
|
|
||||||
buf[15] = '\0';
|
|
||||||
if (strcasecmp(buf, "Content-Length:") == 0)
|
|
||||||
content_length = atoi(&(buf[16]));
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
if (content_length == -1) {
|
|
||||||
bad_request(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else/*HEAD or other*/
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (pipe(cgi_output) < 0) {
|
|
||||||
cannot_execute(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pipe(cgi_input) < 0) {
|
|
||||||
cannot_execute(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (pid = fork()) < 0 ) {
|
|
||||||
cannot_execute(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
if (pid == 0) /* child: CGI script */
|
|
||||||
{
|
|
||||||
char meth_env[255];
|
|
||||||
char query_env[255];
|
|
||||||
char length_env[255];
|
|
||||||
|
|
||||||
dup2(cgi_output[1], STDOUT);
|
|
||||||
dup2(cgi_input[0], STDIN);
|
|
||||||
close(cgi_output[0]);
|
|
||||||
close(cgi_input[1]);
|
|
||||||
sprintf(meth_env, "REQUEST_METHOD=%s", method);
|
|
||||||
putenv(meth_env);
|
|
||||||
if (strcasecmp(method, "GET") == 0) {
|
|
||||||
sprintf(query_env, "QUERY_STRING=%s", query_string);
|
|
||||||
putenv(query_env);
|
|
||||||
}
|
|
||||||
else { /* POST */
|
|
||||||
sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
|
|
||||||
putenv(length_env);
|
|
||||||
}
|
|
||||||
execl(path, "", NULL);
|
|
||||||
exit(0);
|
|
||||||
} else { /* parent */
|
|
||||||
close(cgi_output[1]);
|
|
||||||
close(cgi_input[0]);
|
|
||||||
if (strcasecmp(method, "POST") == 0)
|
|
||||||
for (i = 0; i < content_length; i++) {
|
|
||||||
recv(client, &c, 1, 0);
|
|
||||||
int len = write(cgi_input[1], &c, 1);
|
|
||||||
}
|
|
||||||
while (read(cgi_output[0], &c, 1) > 0)
|
|
||||||
send(client, &c, 1, 0);
|
|
||||||
|
|
||||||
close(cgi_output[0]);
|
|
||||||
close(cgi_input[1]);
|
|
||||||
waitpid(pid, &status, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Get a line from a socket, whether the line ends in a newline,
|
|
||||||
* carriage return, or a CRLF combination. Terminates the string read
|
|
||||||
* with a null character. If no newline indicator is found before the
|
|
||||||
* end of the buffer, the string is terminated with a null. If any of
|
|
||||||
* the above three line terminators is read, the last character of the
|
|
||||||
* string will be a linefeed and the string will be terminated with a
|
|
||||||
* null character.
|
|
||||||
* Parameters: the socket descriptor
|
|
||||||
* the buffer to save the data in
|
|
||||||
* the size of the buffer
|
|
||||||
* Returns: the number of bytes stored (excluding null) */
|
|
||||||
/**********************************************************************/
|
|
||||||
int get_line(int sock, char *buf, int size)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
char c = '\0';
|
|
||||||
int n;
|
|
||||||
|
|
||||||
while ((i < size - 1) && (c != '\n'))
|
|
||||||
{
|
|
||||||
|
|
||||||
n = recv(sock, &c, 1, 0);
|
|
||||||
|
|
||||||
if (n > 0)
|
|
||||||
{
|
|
||||||
if (c == '\r')
|
|
||||||
{
|
|
||||||
n = recv(sock, &c, 1, MSG_PEEK);
|
|
||||||
|
|
||||||
if ((n > 0) && (c == '\n'))
|
|
||||||
recv(sock, &c, 1, 0);
|
|
||||||
else
|
|
||||||
c = '\n';
|
|
||||||
}
|
|
||||||
buf[i] = c;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
c = '\n';
|
|
||||||
}
|
|
||||||
buf[i] = '\0';
|
|
||||||
|
|
||||||
return(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Return the informational HTTP headers about a file. */
|
|
||||||
/* Parameters: the socket to print the headers on
|
|
||||||
* the name of the file */
|
|
||||||
/**********************************************************************/
|
|
||||||
void headers(int client, const char *filename)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
(void)filename; /* could use filename to determine file type */
|
|
||||||
|
|
||||||
strcpy(buf, "HTTP/1.0 200 OK\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
strcpy(buf, SERVER_STRING);
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "Content-Type: text/html\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
strcpy(buf, "\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Give a client a 404 not found status message. */
|
|
||||||
/**********************************************************************/
|
|
||||||
void not_found(int client)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, SERVER_STRING);
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "Content-Type: text/html\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "your request because the resource specified\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "is unavailable or nonexistent.\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "</BODY></HTML>\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Send a regular file to the client. Use headers, and report
|
|
||||||
* errors to client if they occur.
|
|
||||||
* Parameters: a pointer to a file structure produced from the socket
|
|
||||||
* file descriptor
|
|
||||||
* the name of the file to serve */
|
|
||||||
/**********************************************************************/
|
|
||||||
void serve_file(int client, const char *filename)
|
|
||||||
{
|
|
||||||
FILE *resource = NULL;
|
|
||||||
int numchars = 1;
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
#if (ENABLE_NTYCO == 0)
|
|
||||||
buf[0] = 'A'; buf[1] = '\0';
|
|
||||||
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
resource = fopen(filename, "r");
|
|
||||||
if (resource == NULL)
|
|
||||||
not_found(client);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
headers(client, filename);
|
|
||||||
cat(client, resource);
|
|
||||||
}
|
|
||||||
fclose(resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* This function starts the process of listening for web connections
|
|
||||||
* on a specified port. If the port is 0, then dynamically allocate a
|
|
||||||
* port and modify the original port variable to reflect the actual
|
|
||||||
* port.
|
|
||||||
* Parameters: pointer to variable containing the port to connect on
|
|
||||||
* Returns: the socket */
|
|
||||||
/**********************************************************************/
|
|
||||||
int startup(u_short *port)
|
|
||||||
{
|
|
||||||
int httpd = 0;
|
|
||||||
int on = 1;
|
|
||||||
struct sockaddr_in name;
|
|
||||||
|
|
||||||
httpd = socket(PF_INET, SOCK_STREAM, 0);
|
|
||||||
if (httpd == -1)
|
|
||||||
error_die("socket");
|
|
||||||
memset(&name, 0, sizeof(name));
|
|
||||||
name.sin_family = AF_INET;
|
|
||||||
name.sin_port = htons(*port);
|
|
||||||
name.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
if ((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)
|
|
||||||
{
|
|
||||||
error_die("setsockopt failed");
|
|
||||||
}
|
|
||||||
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
|
|
||||||
error_die("bind");
|
|
||||||
if (*port == 0) /* if dynamically allocating a port */
|
|
||||||
{
|
|
||||||
socklen_t namelen = sizeof(name);
|
|
||||||
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
|
|
||||||
error_die("getsockname");
|
|
||||||
*port = ntohs(name.sin_port);
|
|
||||||
}
|
|
||||||
if (listen(httpd, 5) < 0)
|
|
||||||
error_die("listen");
|
|
||||||
|
|
||||||
|
|
||||||
return(httpd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Inform the client that the requested web method has not been
|
|
||||||
* implemented.
|
|
||||||
* Parameter: the client socket */
|
|
||||||
/**********************************************************************/
|
|
||||||
void unimplemented(int client)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, SERVER_STRING);
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "Content-Type: text/html\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "</TITLE></HEAD>\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "</BODY></HTML>\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
void server(void *arg)
|
|
||||||
{
|
|
||||||
int server_sock = -1;
|
|
||||||
u_short port = 4000;
|
|
||||||
struct sockaddr_in client_name;
|
|
||||||
socklen_t client_name_len = sizeof(client_name);
|
|
||||||
|
|
||||||
server_sock = startup(&port);
|
|
||||||
printf("httpd running on port %d\n", port);
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
int *client_sock = (int *)malloc(sizeof(int));
|
|
||||||
*client_sock = accept(server_sock,
|
|
||||||
(struct sockaddr *)&client_name,
|
|
||||||
&client_name_len);
|
|
||||||
if (*client_sock == -1)
|
|
||||||
error_die("accept");
|
|
||||||
/* accept_request(&client_sock); */
|
|
||||||
#if 1 //ENABLE_NTYCO
|
|
||||||
|
|
||||||
printf(" %d.%d.%d.%d:%d clientfd:%d --> New Client Connected \n",
|
|
||||||
*(unsigned char*)(&client_name.sin_addr.s_addr), *((unsigned char*)(&client_name.sin_addr.s_addr)+1),
|
|
||||||
*((unsigned char*)(&client_name.sin_addr.s_addr)+2), *((unsigned char*)(&client_name.sin_addr.s_addr)+3),
|
|
||||||
client_name.sin_port, *client_sock);
|
|
||||||
|
|
||||||
nty_coroutine *read_co;
|
|
||||||
nty_coroutine_create(&read_co, accept_request, client_sock);
|
|
||||||
|
|
||||||
#else
|
|
||||||
pthread_t newthread;
|
|
||||||
if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0)
|
|
||||||
perror("pthread_create");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_close(server_sock);
|
|
||||||
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
nty_coroutine_create(&co, server, 0); ////////no run
|
|
||||||
|
|
||||||
|
|
||||||
nty_schedule_run(); //run
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,891 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/sysinfo.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
#include <sys/shm.h>
|
|
||||||
#include <sys/sem.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define __USE_GNU
|
|
||||||
|
|
||||||
#include <sched.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
static int global_shmid = -1;
|
|
||||||
static int global_semid = -1;
|
|
||||||
|
|
||||||
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct _shm_area {
|
|
||||||
int total;
|
|
||||||
int accept_lock;
|
|
||||||
struct timeval tv_begin;
|
|
||||||
} shm_area;
|
|
||||||
|
|
||||||
static shm_area *global_shmaddr = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
#define MAX_CLIENT_NUM 1000000
|
|
||||||
|
|
||||||
|
|
||||||
struct timeval* get_shm_timevalue(void);
|
|
||||||
void set_shm_timevalue(struct timeval *tv);
|
|
||||||
|
|
||||||
|
|
||||||
int add_shmvalue(void);
|
|
||||||
int sub_shmvalue(void);
|
|
||||||
|
|
||||||
int init_shmvalue(void);
|
|
||||||
|
|
||||||
int lock_accept_mutex(void);
|
|
||||||
int unlock_accept_mutex(void);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned long cmpxchg(void *addr, unsigned long _old, unsigned long _new, int size) {
|
|
||||||
unsigned long prev;
|
|
||||||
volatile unsigned int *_ptr = (volatile unsigned int *)(addr);
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 1: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchgb %b1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchgw %w1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 4: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchg %1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define ISspace(x) isspace((int)(x))
|
|
||||||
|
|
||||||
#define SERVER_STRING "Server: ntyco_httpd/0.1.0\r\n"
|
|
||||||
#define STDIN 0
|
|
||||||
#define STDOUT 1
|
|
||||||
#define STDERR 2
|
|
||||||
|
|
||||||
#define ENABLE_NTYCO 1
|
|
||||||
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
|
|
||||||
#define socket nty_socket
|
|
||||||
#define accept nty_accept
|
|
||||||
#define recv(a, b, c, d) nty_recv(a, b, c, d)
|
|
||||||
#define send(a, b, c, d) nty_send(a, b, c, d)
|
|
||||||
#define close(a) nty_close(a)
|
|
||||||
|
|
||||||
#define MAX_BUFFER_LENGTH 1024
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void accept_request(void *);
|
|
||||||
void bad_request(int);
|
|
||||||
void cat(int, FILE *);
|
|
||||||
void cannot_execute(int);
|
|
||||||
void error_die(const char *);
|
|
||||||
void execute_cgi(int, const char *, const char *, const char *);
|
|
||||||
int get_line(int, char *, int);
|
|
||||||
void headers(int, const char *);
|
|
||||||
void not_found(int);
|
|
||||||
void serve_file(int, const char *);
|
|
||||||
int startup(u_short *);
|
|
||||||
void unimplemented(int);
|
|
||||||
|
|
||||||
int readline(char* allbuf, int level, char* linebuf) {
|
|
||||||
int len = strlen(allbuf);
|
|
||||||
|
|
||||||
for (;level < len; ++level) {
|
|
||||||
if(allbuf[level]=='\r' && allbuf[level+1]=='\n')
|
|
||||||
return level+2;
|
|
||||||
else
|
|
||||||
*(linebuf++) = allbuf[level];
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* A request has caused a call to accept() on the server port to
|
|
||||||
* return. Process the request appropriately.
|
|
||||||
* Parameters: the socket connected to the client */
|
|
||||||
/**********************************************************************/
|
|
||||||
void accept_request(void *arg)
|
|
||||||
{
|
|
||||||
int *pclient = (int*)arg;
|
|
||||||
int client = *pclient;
|
|
||||||
|
|
||||||
char buf[1024];
|
|
||||||
size_t numchars;
|
|
||||||
char method[16];
|
|
||||||
char url[32];
|
|
||||||
char path[64];
|
|
||||||
size_t i, j;
|
|
||||||
struct stat st;
|
|
||||||
int cgi = 0; /* becomes true if server decides this is a CGI
|
|
||||||
* program */
|
|
||||||
char *query_string = NULL;
|
|
||||||
|
|
||||||
numchars = recv(client, buf, sizeof(buf), 0);
|
|
||||||
|
|
||||||
i = 0; j = 0;
|
|
||||||
while (!ISspace(buf[i]) && (i < sizeof(method) - 1))
|
|
||||||
{
|
|
||||||
method[i] = buf[i];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
j=i;
|
|
||||||
method[i] = '\0';
|
|
||||||
|
|
||||||
if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
|
|
||||||
{
|
|
||||||
unimplemented(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcasecmp(method, "POST") == 0)
|
|
||||||
cgi = 1;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (ISspace(buf[j]) && (j < numchars))
|
|
||||||
j++;
|
|
||||||
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < numchars))
|
|
||||||
{
|
|
||||||
url[i] = buf[j];
|
|
||||||
i++; j++;
|
|
||||||
}
|
|
||||||
url[i] = '\0';
|
|
||||||
|
|
||||||
if (strcasecmp(method, "GET") == 0)
|
|
||||||
{
|
|
||||||
query_string = url;
|
|
||||||
while ((*query_string != '?') && (*query_string != '\0'))
|
|
||||||
query_string++;
|
|
||||||
if (*query_string == '?')
|
|
||||||
{
|
|
||||||
cgi = 1;
|
|
||||||
*query_string = '\0';
|
|
||||||
query_string++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(path, "htdocs%s", url);
|
|
||||||
if (path[strlen(path) - 1] == '/')
|
|
||||||
strcat(path, "index.html");
|
|
||||||
if (stat(path, &st) == -1) {
|
|
||||||
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
not_found(client);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((st.st_mode & S_IFMT) == S_IFDIR)
|
|
||||||
strcat(path, "/index.html");
|
|
||||||
if ((st.st_mode & S_IXUSR) ||
|
|
||||||
(st.st_mode & S_IXGRP) ||
|
|
||||||
(st.st_mode & S_IXOTH) )
|
|
||||||
cgi = 1;
|
|
||||||
|
|
||||||
cgi = 0;
|
|
||||||
|
|
||||||
if (!cgi)
|
|
||||||
serve_file(client, path);
|
|
||||||
else
|
|
||||||
execute_cgi(client, path, method, query_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(client);
|
|
||||||
free(pclient);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Inform the client that a request it has made has a problem.
|
|
||||||
* Parameters: client socket */
|
|
||||||
/**********************************************************************/
|
|
||||||
void bad_request(int client)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
sprintf(buf, "Content-type: text/html\r\n");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
sprintf(buf, "\r\n");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
sprintf(buf, "<P>Your browser sent a bad request, ");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
sprintf(buf, "such as a POST without a Content-Length.\r\n");
|
|
||||||
send(client, buf, sizeof(buf), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Put the entire contents of a file out on a socket. This function
|
|
||||||
* is named after the UNIX "cat" command, because it might have been
|
|
||||||
* easier just to do something like pipe, fork, and exec("cat").
|
|
||||||
* Parameters: the client socket descriptor
|
|
||||||
* FILE pointer for the file to cat */
|
|
||||||
/**********************************************************************/
|
|
||||||
|
|
||||||
#define HTML_PAGE "<HTML> \
|
|
||||||
<TITLE>Index</TITLE> \
|
|
||||||
<BODY> \
|
|
||||||
<P>Welcome to J. David's webserver.\
|
|
||||||
<H1>CGI demo \
|
|
||||||
<FORM ACTION=\"color.cgi\" METHOD=\"POST\">\
|
|
||||||
Enter a color: <INPUT TYPE=\"text\" NAME=\"color\">\
|
|
||||||
<INPUT TYPE=\"submit\">\
|
|
||||||
</FORM>\
|
|
||||||
</BODY>\
|
|
||||||
</HTML>"
|
|
||||||
|
|
||||||
void cat(int client, FILE *resource)
|
|
||||||
{
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
char buf[1024] = HTML_PAGE;
|
|
||||||
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
|
|
||||||
#else
|
|
||||||
char buf[1024] = {0};
|
|
||||||
fgets(buf, sizeof(buf), resource);
|
|
||||||
while (!feof(resource))
|
|
||||||
{
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
fgets(buf, sizeof(buf), resource);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Inform the client that a CGI script could not be executed.
|
|
||||||
* Parameter: the client socket descriptor. */
|
|
||||||
/**********************************************************************/
|
|
||||||
void cannot_execute(int client)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "Content-type: text/html\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<P>Error prohibited CGI execution.\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Print out an error message with perror() (for system errors; based
|
|
||||||
* on value of errno, which indicates system call errors) and exit the
|
|
||||||
* program indicating an error. */
|
|
||||||
/**********************************************************************/
|
|
||||||
void error_die(const char *sc)
|
|
||||||
{
|
|
||||||
perror(sc);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Execute a CGI script. Will need to set environment variables as
|
|
||||||
* appropriate.
|
|
||||||
* Parameters: client socket descriptor
|
|
||||||
* path to the CGI script */
|
|
||||||
/**********************************************************************/
|
|
||||||
void execute_cgi(int client, const char *path,
|
|
||||||
const char *method, const char *query_string)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
int cgi_output[2];
|
|
||||||
int cgi_input[2];
|
|
||||||
pid_t pid;
|
|
||||||
int status;
|
|
||||||
int i;
|
|
||||||
char c;
|
|
||||||
int numchars = 1;
|
|
||||||
int content_length = -1;
|
|
||||||
|
|
||||||
buf[0] = 'A'; buf[1] = '\0';
|
|
||||||
if (strcasecmp(method, "GET") == 0)
|
|
||||||
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
else if (strcasecmp(method, "POST") == 0) /*POST*/
|
|
||||||
{
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
while ((numchars > 0) && strcmp("\n", buf))
|
|
||||||
{
|
|
||||||
buf[15] = '\0';
|
|
||||||
if (strcasecmp(buf, "Content-Length:") == 0)
|
|
||||||
content_length = atoi(&(buf[16]));
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
if (content_length == -1) {
|
|
||||||
bad_request(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else/*HEAD or other*/
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (pipe(cgi_output) < 0) {
|
|
||||||
cannot_execute(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pipe(cgi_input) < 0) {
|
|
||||||
cannot_execute(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (pid = fork()) < 0 ) {
|
|
||||||
cannot_execute(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
if (pid == 0) /* child: CGI script */
|
|
||||||
{
|
|
||||||
char meth_env[255];
|
|
||||||
char query_env[255];
|
|
||||||
char length_env[255];
|
|
||||||
|
|
||||||
dup2(cgi_output[1], STDOUT);
|
|
||||||
dup2(cgi_input[0], STDIN);
|
|
||||||
close(cgi_output[0]);
|
|
||||||
close(cgi_input[1]);
|
|
||||||
sprintf(meth_env, "REQUEST_METHOD=%s", method);
|
|
||||||
putenv(meth_env);
|
|
||||||
if (strcasecmp(method, "GET") == 0) {
|
|
||||||
sprintf(query_env, "QUERY_STRING=%s", query_string);
|
|
||||||
putenv(query_env);
|
|
||||||
}
|
|
||||||
else { /* POST */
|
|
||||||
sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
|
|
||||||
putenv(length_env);
|
|
||||||
}
|
|
||||||
execl(path, "", NULL);
|
|
||||||
exit(0);
|
|
||||||
} else { /* parent */
|
|
||||||
close(cgi_output[1]);
|
|
||||||
close(cgi_input[0]);
|
|
||||||
if (strcasecmp(method, "POST") == 0)
|
|
||||||
for (i = 0; i < content_length; i++) {
|
|
||||||
|
|
||||||
recv(client, &c, 1, 0);
|
|
||||||
|
|
||||||
int len = write(cgi_input[1], &c, 1);
|
|
||||||
}
|
|
||||||
while (read(cgi_output[0], &c, 1) > 0)
|
|
||||||
|
|
||||||
send(client, &c, 1, 0);
|
|
||||||
|
|
||||||
|
|
||||||
close(cgi_output[0]);
|
|
||||||
close(cgi_input[1]);
|
|
||||||
waitpid(pid, &status, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Get a line from a socket, whether the line ends in a newline,
|
|
||||||
* carriage return, or a CRLF combination. Terminates the string read
|
|
||||||
* with a null character. If no newline indicator is found before the
|
|
||||||
* end of the buffer, the string is terminated with a null. If any of
|
|
||||||
* the above three line terminators is read, the last character of the
|
|
||||||
* string will be a linefeed and the string will be terminated with a
|
|
||||||
* null character.
|
|
||||||
* Parameters: the socket descriptor
|
|
||||||
* the buffer to save the data in
|
|
||||||
* the size of the buffer
|
|
||||||
* Returns: the number of bytes stored (excluding null) */
|
|
||||||
/**********************************************************************/
|
|
||||||
int get_line(int sock, char *buf, int size)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
char c = '\0';
|
|
||||||
int n;
|
|
||||||
|
|
||||||
while ((i < size - 1) && (c != '\n'))
|
|
||||||
{
|
|
||||||
|
|
||||||
n = recv(sock, &c, 1, 0);
|
|
||||||
|
|
||||||
if (n > 0)
|
|
||||||
{
|
|
||||||
if (c == '\r')
|
|
||||||
{
|
|
||||||
|
|
||||||
n = recv(sock, &c, 1, MSG_PEEK);
|
|
||||||
|
|
||||||
if ((n > 0) && (c == '\n'))
|
|
||||||
|
|
||||||
recv(sock, &c, 1, 0);
|
|
||||||
|
|
||||||
else
|
|
||||||
c = '\n';
|
|
||||||
}
|
|
||||||
buf[i] = c;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
c = '\n';
|
|
||||||
}
|
|
||||||
buf[i] = '\0';
|
|
||||||
|
|
||||||
return(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Return the informational HTTP headers about a file. */
|
|
||||||
/* Parameters: the socket to print the headers on
|
|
||||||
* the name of the file */
|
|
||||||
/**********************************************************************/
|
|
||||||
void headers(int client, const char *filename)
|
|
||||||
{
|
|
||||||
char buf[1024] = {0};
|
|
||||||
(void)filename; /* could use filename to determine file type */
|
|
||||||
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
|
||||||
strcat(buf, SERVER_STRING);
|
|
||||||
strcat(buf, "Content-Type: text/html\r\n");
|
|
||||||
strcat(buf, "\r\n");
|
|
||||||
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
|
|
||||||
#else
|
|
||||||
strcpy(buf, "HTTP/1.0 200 OK\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
strcpy(buf, SERVER_STRING);
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "Content-Type: text/html\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
strcpy(buf, "\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Give a client a 404 not found status message. */
|
|
||||||
/**********************************************************************/
|
|
||||||
void not_found(int client)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
|
|
||||||
strcat(buf, SERVER_STRING);
|
|
||||||
strcat(buf, "Content-Type: text/html\r\n");
|
|
||||||
strcat(buf, "\r\n");
|
|
||||||
strcat(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
|
|
||||||
strcat(buf, "<BODY><P>The server could not fulfill\r\n");
|
|
||||||
strcat(buf, "your request because the resource specified\r\n");
|
|
||||||
strcat(buf, "is unavailable or nonexistent.\r\n");
|
|
||||||
strcat(buf, "</BODY></HTML>\r\n");
|
|
||||||
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
|
|
||||||
#else
|
|
||||||
sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, SERVER_STRING);
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "Content-Type: text/html\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "your request because the resource specified\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "is unavailable or nonexistent.\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "</BODY></HTML>\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Send a regular file to the client. Use headers, and report
|
|
||||||
* errors to client if they occur.
|
|
||||||
* Parameters: a pointer to a file structure produced from the socket
|
|
||||||
* file descriptor
|
|
||||||
* the name of the file to serve */
|
|
||||||
/**********************************************************************/
|
|
||||||
void serve_file(int client, const char *filename)
|
|
||||||
{
|
|
||||||
FILE *resource = NULL;
|
|
||||||
int numchars = 1;
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
#if (ENABLE_NTYCO == 0)
|
|
||||||
buf[0] = 'A'; buf[1] = '\0';
|
|
||||||
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
|
|
||||||
numchars = get_line(client, buf, sizeof(buf));
|
|
||||||
resource = fopen(filename, "r");
|
|
||||||
if (resource == NULL)
|
|
||||||
not_found(client);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
headers(client, filename);
|
|
||||||
cat(client, resource);
|
|
||||||
}
|
|
||||||
fclose(resource);
|
|
||||||
#else
|
|
||||||
headers(client, filename);
|
|
||||||
cat(client, resource);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* This function starts the process of listening for web connections
|
|
||||||
* on a specified port. If the port is 0, then dynamically allocate a
|
|
||||||
* port and modify the original port variable to reflect the actual
|
|
||||||
* port.
|
|
||||||
* Parameters: pointer to variable containing the port to connect on
|
|
||||||
* Returns: the socket */
|
|
||||||
/**********************************************************************/
|
|
||||||
int startup(u_short *port)
|
|
||||||
{
|
|
||||||
int httpd = 0;
|
|
||||||
int on = 1;
|
|
||||||
struct sockaddr_in name;
|
|
||||||
|
|
||||||
httpd = socket(PF_INET, SOCK_STREAM, 0);
|
|
||||||
if (httpd == -1)
|
|
||||||
error_die("socket");
|
|
||||||
memset(&name, 0, sizeof(name));
|
|
||||||
name.sin_family = AF_INET;
|
|
||||||
name.sin_port = htons(*port);
|
|
||||||
name.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
if ((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)
|
|
||||||
{
|
|
||||||
error_die("setsockopt failed");
|
|
||||||
}
|
|
||||||
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
|
|
||||||
error_die("bind");
|
|
||||||
if (*port == 0) /* if dynamically allocating a port */
|
|
||||||
{
|
|
||||||
socklen_t namelen = sizeof(name);
|
|
||||||
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
|
|
||||||
error_die("getsockname");
|
|
||||||
*port = ntohs(name.sin_port);
|
|
||||||
}
|
|
||||||
if (listen(httpd, 5) < 0)
|
|
||||||
error_die("listen");
|
|
||||||
|
|
||||||
|
|
||||||
return(httpd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************/
|
|
||||||
/* Inform the client that the requested web method has not been
|
|
||||||
* implemented.
|
|
||||||
* Parameter: the client socket */
|
|
||||||
/**********************************************************************/
|
|
||||||
void unimplemented(int client)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
|
|
||||||
strcat(buf, SERVER_STRING);
|
|
||||||
strcat(buf, "Content-Type: text/html\r\n");
|
|
||||||
strcat(buf, "\r\n");
|
|
||||||
strcat(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
|
|
||||||
strcat(buf, "</TITLE></HEAD>\r\n");
|
|
||||||
strcat(buf, "<BODY><P>HTTP request method not supported.\r\n");
|
|
||||||
strcat(buf, "</BODY></HTML>\r\n");
|
|
||||||
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
#else
|
|
||||||
sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, SERVER_STRING);
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "Content-Type: text/html\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "</TITLE></HEAD>\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
sprintf(buf, "</BODY></HTML>\r\n");
|
|
||||||
send(client, buf, strlen(buf), 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void server(void *arg) {
|
|
||||||
|
|
||||||
unsigned short port = *(unsigned short *)arg;
|
|
||||||
struct sockaddr_in client_name;
|
|
||||||
socklen_t client_name_len = sizeof(client_name);
|
|
||||||
free(arg);
|
|
||||||
|
|
||||||
int fd = nty_socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (fd < 0) return ;
|
|
||||||
|
|
||||||
struct sockaddr_in local, remote;
|
|
||||||
local.sin_family = AF_INET;
|
|
||||||
local.sin_port = htons(port);
|
|
||||||
local.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
bind(fd, (struct sockaddr*)&local, sizeof(struct sockaddr_in));
|
|
||||||
|
|
||||||
listen(fd, 20);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int *client_sock = (int *)malloc(sizeof(int));
|
|
||||||
*client_sock = accept(fd,
|
|
||||||
(struct sockaddr *)&client_name,
|
|
||||||
&client_name_len);
|
|
||||||
if (*client_sock == -1)
|
|
||||||
error_die("accept");
|
|
||||||
/* accept_request(&client_sock); */
|
|
||||||
#if 0
|
|
||||||
printf(" %d.%d.%d.%d:%d clientfd:%d --> New Client Connected \n",
|
|
||||||
*(unsigned char*)(&client_name.sin_addr.s_addr), *((unsigned char*)(&client_name.sin_addr.s_addr)+1),
|
|
||||||
*((unsigned char*)(&client_name.sin_addr.s_addr)+2), *((unsigned char*)(&client_name.sin_addr.s_addr)+3),
|
|
||||||
client_name.sin_port, *client_sock);
|
|
||||||
#endif
|
|
||||||
nty_coroutine *read_co;
|
|
||||||
nty_coroutine_create(&read_co, accept_request, client_sock);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int mulcore_entry(int begin_port) {
|
|
||||||
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
unsigned short base_port = begin_port;
|
|
||||||
|
|
||||||
unsigned short *port = calloc(1, sizeof(unsigned short));
|
|
||||||
*port = base_port + i;
|
|
||||||
nty_coroutine_create(&co, server, port); ////////no run
|
|
||||||
|
|
||||||
|
|
||||||
nty_schedule_run(); //run
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_bind(void) {
|
|
||||||
|
|
||||||
int num = sysconf(_SC_NPROCESSORS_CONF);
|
|
||||||
|
|
||||||
pid_t self_id = syscall(__NR_gettid);
|
|
||||||
//printf("selfid --> %d\n", self_id);
|
|
||||||
|
|
||||||
cpu_set_t mask;
|
|
||||||
|
|
||||||
CPU_ZERO(&mask);
|
|
||||||
CPU_SET(self_id % num, &mask);
|
|
||||||
|
|
||||||
sched_setaffinity(0, sizeof(mask), &mask);
|
|
||||||
|
|
||||||
mulcore_entry(9096);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct timeval* get_shm_timevalue(void) {
|
|
||||||
|
|
||||||
return global_shmaddr ? &global_shmaddr->tv_begin : NULL;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_shm_timevalue(struct timeval *tv) {
|
|
||||||
if (global_shmaddr == NULL || tv == NULL) {
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&global_shmaddr->tv_begin, tv, sizeof(struct timeval));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int add_shmvalue(void) {
|
|
||||||
int value = 0;
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
value = global_shmaddr->total;
|
|
||||||
ret = cmpxchg(&global_shmaddr->total, (unsigned long)value, (unsigned long)(value+1), 4);
|
|
||||||
|
|
||||||
|
|
||||||
} while (ret != value);
|
|
||||||
|
|
||||||
return global_shmaddr->total;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sub_shmvalue(void) {
|
|
||||||
int value = 0;
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
value = global_shmaddr->total;
|
|
||||||
ret = cmpxchg(&global_shmaddr->total, (unsigned long)value, (unsigned long)(value-1), 4);
|
|
||||||
|
|
||||||
|
|
||||||
} while (ret != value);
|
|
||||||
|
|
||||||
return global_shmaddr->total;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lock_accept_mutex(void) {
|
|
||||||
|
|
||||||
int ret = cmpxchg(&global_shmaddr->accept_lock, (unsigned long)0, (unsigned long)1, 4);
|
|
||||||
|
|
||||||
return ret; //success 0, failed 1.
|
|
||||||
}
|
|
||||||
|
|
||||||
int unlock_accept_mutex(void) {
|
|
||||||
|
|
||||||
int ret = cmpxchg(&global_shmaddr->accept_lock, (unsigned long)1, (unsigned long)0, 4);
|
|
||||||
|
|
||||||
return ret; //unlock 1, lock 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int init_shmvalue(void) {
|
|
||||||
|
|
||||||
global_shmid = shmget(IPC_PRIVATE, sizeof(shm_area), IPC_CREAT|0600);
|
|
||||||
if (global_shmid < 0) {
|
|
||||||
perror("shmget failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
global_shmaddr = (shm_area*)shmat(global_shmid, NULL, 0);
|
|
||||||
if (global_shmaddr == (shm_area*)-1) {
|
|
||||||
perror("shmat addr error");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
global_shmaddr->total = 0;
|
|
||||||
gettimeofday(&global_shmaddr->tv_begin, NULL);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
int ret = init_shmvalue();
|
|
||||||
if (ret == -1) {
|
|
||||||
printf("init share memory failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int num = sysconf(_SC_NPROCESSORS_CONF);
|
|
||||||
int i = 0;
|
|
||||||
pid_t pid=0;
|
|
||||||
for(i = 0;i < num;i ++) {
|
|
||||||
pid=fork();
|
|
||||||
if(pid <= (pid_t) 0)
|
|
||||||
{
|
|
||||||
usleep(1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid > 0) {
|
|
||||||
printf("ntyco_httpd server running ...\n");
|
|
||||||
getchar();
|
|
||||||
} else if (pid == 0) {
|
|
||||||
process_bind();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <mysql.h>
|
|
||||||
|
|
||||||
|
|
||||||
void func (void *arg) {
|
|
||||||
|
|
||||||
|
|
||||||
MYSQL* m_mysql = mysql_init(NULL);
|
|
||||||
if (!m_mysql) {
|
|
||||||
printf("mysql_init failed\n");
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mysql_real_connect(m_mysql,
|
|
||||||
"192.168.233.133", "king", "123456",
|
|
||||||
"KING_DB", 3306,
|
|
||||||
NULL, CLIENT_FOUND_ROWS)) {
|
|
||||||
printf("mysql_real_connect failed: %s\n", mysql_error(m_mysql));
|
|
||||||
return ;
|
|
||||||
} else{
|
|
||||||
printf("mysql_real_connect success\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
#if 1
|
|
||||||
init_hook();
|
|
||||||
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
nty_coroutine_create(&co, func, NULL);
|
|
||||||
nty_schedule_run(); //run
|
|
||||||
#else
|
|
||||||
|
|
||||||
func(NULL);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,315 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <mysql.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define KING_DB_SERVER_IP "192.168.233.133"
|
|
||||||
#define KING_DB_SERVER_PORT 3306
|
|
||||||
|
|
||||||
#define KING_DB_USERNAME "king"
|
|
||||||
#define KING_DB_PASSWORD "123456"
|
|
||||||
|
|
||||||
#define KING_DB_DEFAULTDB "KING_DB"
|
|
||||||
|
|
||||||
|
|
||||||
#define SQL_INSERT_TBL_USER "INSERT TBL_USER(U_NAME, U_GENDER) VALUES('King', 'man');"
|
|
||||||
#define SQL_SELECT_TBL_USER "SELECT * FROM TBL_USER;"
|
|
||||||
|
|
||||||
#define SQL_DELETE_TBL_USER "CALL PROC_DELETE_USER('King')"
|
|
||||||
#define SQL_INSERT_IMG_USER "INSERT TBL_USER(U_NAME, U_GENDER, U_IMG) VALUES('King', 'man', ?);"
|
|
||||||
|
|
||||||
#define SQL_SELECT_IMG_USER "SELECT U_IMG FROM TBL_USER WHERE U_NAME='King';"
|
|
||||||
|
|
||||||
|
|
||||||
#define FILE_IMAGE_LENGTH (64*1024)
|
|
||||||
// C U R D -->
|
|
||||||
//
|
|
||||||
|
|
||||||
int king_mysql_select(MYSQL *handle) { //
|
|
||||||
|
|
||||||
// mysql_real_query --> sql
|
|
||||||
if (mysql_real_query(handle, SQL_SELECT_TBL_USER, strlen(SQL_SELECT_TBL_USER))) {
|
|
||||||
printf("mysql_real_query : %s\n", mysql_error(handle));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// store -->
|
|
||||||
MYSQL_RES *res = mysql_store_result(handle);
|
|
||||||
if (res == NULL) {
|
|
||||||
printf("mysql_store_result : %s\n", mysql_error(handle));
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// rows / fields
|
|
||||||
int rows = mysql_num_rows(res);
|
|
||||||
printf("rows: %d\n", rows);
|
|
||||||
|
|
||||||
int fields = mysql_num_fields(res);
|
|
||||||
printf("fields: %d\n", fields);
|
|
||||||
|
|
||||||
// fetch
|
|
||||||
MYSQL_ROW row;
|
|
||||||
while ((row = mysql_fetch_row(res))) {
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (i = 0;i < fields;i ++) {
|
|
||||||
printf("%s\t", row[i]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
mysql_free_result(res);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// filename : path + file name
|
|
||||||
// buffer : store image data
|
|
||||||
|
|
||||||
int read_image(char *filename, char *buffer) {
|
|
||||||
|
|
||||||
if (filename == NULL || buffer == NULL) return -1;
|
|
||||||
|
|
||||||
FILE *fp = fopen(filename, "rb"); //
|
|
||||||
if (fp == NULL) {
|
|
||||||
printf("fopen failed\n");
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// file size
|
|
||||||
fseek(fp, 0, SEEK_END);
|
|
||||||
int length = ftell(fp); // file size
|
|
||||||
fseek(fp, 0, SEEK_SET);
|
|
||||||
|
|
||||||
int size = fread(buffer, 1, length, fp);
|
|
||||||
if (size != length) {
|
|
||||||
printf("fread failed: %d\n", size);
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// filename :
|
|
||||||
// buffer :
|
|
||||||
// length :
|
|
||||||
|
|
||||||
int write_image(char *filename, char *buffer, int length) {
|
|
||||||
|
|
||||||
if (filename == NULL || buffer == NULL || length <= 0) return -1;
|
|
||||||
|
|
||||||
FILE *fp = fopen(filename, "wb+"); //
|
|
||||||
if (fp == NULL) {
|
|
||||||
printf("fopen failed\n");
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = fwrite(buffer, 1, length, fp);
|
|
||||||
if (size != length) {
|
|
||||||
printf("fwrite failed: %d\n", size);
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mysql_write(MYSQL *handle, char *buffer, int length) {
|
|
||||||
|
|
||||||
if (handle == NULL || buffer == NULL || length <= 0) return -1;
|
|
||||||
|
|
||||||
MYSQL_STMT *stmt = mysql_stmt_init(handle);
|
|
||||||
int ret = mysql_stmt_prepare(stmt, SQL_INSERT_IMG_USER, strlen(SQL_INSERT_IMG_USER));
|
|
||||||
if (ret) {
|
|
||||||
printf("mysql_stmt_prepare : %s\n", mysql_error(handle));
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
MYSQL_BIND param = {0};
|
|
||||||
param.buffer_type = MYSQL_TYPE_LONG_BLOB;
|
|
||||||
param.buffer = NULL;
|
|
||||||
param.is_null = 0;
|
|
||||||
param.length = NULL;
|
|
||||||
|
|
||||||
ret = mysql_stmt_bind_param(stmt, ¶m);
|
|
||||||
if (ret) {
|
|
||||||
printf("mysql_stmt_bind_param : %s\n", mysql_error(handle));
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mysql_stmt_send_long_data(stmt, 0, buffer, length);
|
|
||||||
if (ret) {
|
|
||||||
printf("mysql_stmt_send_long_data : %s\n", mysql_error(handle));
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mysql_stmt_execute(stmt);
|
|
||||||
if (ret) {
|
|
||||||
printf("mysql_stmt_execute : %s\n", mysql_error(handle));
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mysql_stmt_close(stmt);
|
|
||||||
if (ret) {
|
|
||||||
printf("mysql_stmt_close : %s\n", mysql_error(handle));
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mysql_read(MYSQL *handle, char *buffer, int length) {
|
|
||||||
|
|
||||||
if (handle == NULL || buffer == NULL || length <= 0) return -1;
|
|
||||||
|
|
||||||
MYSQL_STMT *stmt = mysql_stmt_init(handle);
|
|
||||||
int ret = mysql_stmt_prepare(stmt, SQL_SELECT_IMG_USER, strlen(SQL_SELECT_IMG_USER));
|
|
||||||
if (ret) {
|
|
||||||
printf("mysql_stmt_prepare : %s\n", mysql_error(handle));
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MYSQL_BIND result = {0};
|
|
||||||
|
|
||||||
result.buffer_type = MYSQL_TYPE_LONG_BLOB;
|
|
||||||
unsigned long total_length = 0;
|
|
||||||
result.length = &total_length;
|
|
||||||
|
|
||||||
ret = mysql_stmt_bind_result(stmt, &result);
|
|
||||||
if (ret) {
|
|
||||||
printf("mysql_stmt_bind_result : %s\n", mysql_error(handle));
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mysql_stmt_execute(stmt);
|
|
||||||
if (ret) {
|
|
||||||
printf("mysql_stmt_execute : %s\n", mysql_error(handle));
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mysql_stmt_store_result(stmt);
|
|
||||||
if (ret) {
|
|
||||||
printf("mysql_stmt_store_result : %s\n", mysql_error(handle));
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
ret = mysql_stmt_fetch(stmt);
|
|
||||||
if (ret != 0 && ret != MYSQL_DATA_TRUNCATED) break; //
|
|
||||||
|
|
||||||
int start = 0;
|
|
||||||
while (start < (int)total_length) {
|
|
||||||
result.buffer = buffer + start;
|
|
||||||
result.buffer_length = 1;
|
|
||||||
mysql_stmt_fetch_column(stmt, &result, 0, start);
|
|
||||||
start += result.buffer_length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mysql_stmt_close(stmt);
|
|
||||||
|
|
||||||
return total_length;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void coroutine_func(void *arg) {
|
|
||||||
|
|
||||||
MYSQL mysql;
|
|
||||||
|
|
||||||
printf("coroutine_func\n");
|
|
||||||
if (NULL == mysql_init(&mysql)) {
|
|
||||||
printf("mysql_init : %s\n", mysql_error(&mysql));
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mysql_real_connect(&mysql, KING_DB_SERVER_IP, KING_DB_USERNAME, KING_DB_PASSWORD,
|
|
||||||
KING_DB_DEFAULTDB, KING_DB_SERVER_PORT, NULL, 0)) {
|
|
||||||
|
|
||||||
printf("mysql_real_connect : %s\n", mysql_error(&mysql));
|
|
||||||
goto Exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mysql --> insert
|
|
||||||
printf("case 1 : mysql --> insert \n");
|
|
||||||
#if 1
|
|
||||||
if (mysql_real_query(&mysql, SQL_INSERT_TBL_USER, strlen(SQL_INSERT_TBL_USER))) {
|
|
||||||
printf("mysql_real_query : %s\n", mysql_error(&mysql));
|
|
||||||
goto Exit;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
king_mysql_select(&mysql);
|
|
||||||
|
|
||||||
// mysql --> delete
|
|
||||||
|
|
||||||
printf("case 2 : mysql --> delete \n");
|
|
||||||
#if 1
|
|
||||||
if (mysql_real_query(&mysql, SQL_DELETE_TBL_USER, strlen(SQL_DELETE_TBL_USER))) {
|
|
||||||
printf("mysql_real_query : %s\n", mysql_error(&mysql));
|
|
||||||
goto Exit;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
king_mysql_select(&mysql);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
printf("case 3 : mysql --> read image and write mysql\n");
|
|
||||||
|
|
||||||
char buffer[FILE_IMAGE_LENGTH] = {0};
|
|
||||||
int length = read_image("0voice.jpg", buffer);
|
|
||||||
if (length < 0) goto Exit;
|
|
||||||
|
|
||||||
mysql_write(&mysql, buffer, length); ///
|
|
||||||
|
|
||||||
|
|
||||||
printf("case 4 : mysql --> read mysql and write image\n");
|
|
||||||
|
|
||||||
memset(buffer, 0, FILE_IMAGE_LENGTH);
|
|
||||||
length = mysql_read(&mysql, buffer, FILE_IMAGE_LENGTH);
|
|
||||||
|
|
||||||
write_image("a.jpg", buffer, length);
|
|
||||||
|
|
||||||
Exit:
|
|
||||||
mysql_close(&mysql);
|
|
||||||
|
|
||||||
return ;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
#if 1
|
|
||||||
//init_hook();
|
|
||||||
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
nty_coroutine_create(&co, coroutine_func, NULL);
|
|
||||||
nty_schedule_run(); //run
|
|
||||||
#else
|
|
||||||
|
|
||||||
coroutine_func(NULL);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <hiredis.h>
|
|
||||||
|
|
||||||
|
|
||||||
void coroutine_func(void *arg) {
|
|
||||||
unsigned int j, isunix = 0;
|
|
||||||
redisContext *c;
|
|
||||||
redisReply *reply;
|
|
||||||
const char *hostname = "192.168.233.133";
|
|
||||||
int port = 6379;
|
|
||||||
|
|
||||||
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
|
|
||||||
if (isunix) {
|
|
||||||
c = redisConnectUnixWithTimeout(hostname, timeout);
|
|
||||||
} else {
|
|
||||||
c = redisConnectWithTimeout(hostname, port, timeout);
|
|
||||||
}
|
|
||||||
if (c == NULL || c->err) {
|
|
||||||
if (c) {
|
|
||||||
printf("Connection error: %s\n", c->errstr);
|
|
||||||
redisFree(c);
|
|
||||||
} else {
|
|
||||||
printf("Connection error: can't allocate redis context\n");
|
|
||||||
}
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PING server */
|
|
||||||
reply = redisCommand(c,"PING");
|
|
||||||
printf("PING: %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Set a key */
|
|
||||||
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
|
|
||||||
printf("SET: %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Set a key using binary safe API */
|
|
||||||
reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
|
|
||||||
printf("SET (binary API): %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Try a GET and two INCR */
|
|
||||||
reply = redisCommand(c,"GET foo");
|
|
||||||
printf("GET foo: %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
reply = redisCommand(c,"INCR counter");
|
|
||||||
printf("INCR counter: %lld\n", reply->integer);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
/* again ... */
|
|
||||||
reply = redisCommand(c,"INCR counter");
|
|
||||||
printf("INCR counter: %lld\n", reply->integer);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Create a list of numbers, from 0 to 9 */
|
|
||||||
reply = redisCommand(c,"DEL mylist");
|
|
||||||
freeReplyObject(reply);
|
|
||||||
for (j = 0; j < 10; j++) {
|
|
||||||
char buf[64];
|
|
||||||
|
|
||||||
snprintf(buf,64,"%u",j);
|
|
||||||
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Let's check what we have inside the list */
|
|
||||||
reply = redisCommand(c,"LRANGE mylist 0 -1");
|
|
||||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
|
||||||
for (j = 0; j < reply->elements; j++) {
|
|
||||||
printf("%u) %s\n", j, reply->element[j]->str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Disconnects and frees the context */
|
|
||||||
redisFree(c);
|
|
||||||
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
|
|
||||||
//init_hook();
|
|
||||||
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
nty_coroutine_create(&co, coroutine_func, NULL);
|
|
||||||
nty_schedule_run(); //run
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#define MAX_CLIENT_NUM 1000000
|
|
||||||
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
|
|
||||||
|
|
||||||
|
|
||||||
void server_reader(void *arg) {
|
|
||||||
int fd = *(int *)arg;
|
|
||||||
free(arg);
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
char buf[1024] = {0};
|
|
||||||
ret = nty_recv(fd, buf, 1024, 0);
|
|
||||||
if (ret > 0) {
|
|
||||||
if(fd > MAX_CLIENT_NUM)
|
|
||||||
printf("read from server: %.*s\n", ret, buf);
|
|
||||||
|
|
||||||
ret = nty_send(fd, buf, strlen(buf), 0);
|
|
||||||
if (ret == -1) {
|
|
||||||
nty_close(fd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ret == 0) {
|
|
||||||
nty_close(fd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void server(void *arg) {
|
|
||||||
|
|
||||||
unsigned short port = *(unsigned short *)arg;
|
|
||||||
free(arg);
|
|
||||||
|
|
||||||
int fd = nty_socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (fd < 0) return ;
|
|
||||||
|
|
||||||
struct sockaddr_in local, remote;
|
|
||||||
local.sin_family = AF_INET;
|
|
||||||
local.sin_port = htons(port);
|
|
||||||
local.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
bind(fd, (struct sockaddr*)&local, sizeof(struct sockaddr_in));
|
|
||||||
|
|
||||||
listen(fd, 20);
|
|
||||||
printf("listen port : %d\n", port);
|
|
||||||
|
|
||||||
|
|
||||||
struct timeval tv_begin;
|
|
||||||
gettimeofday(&tv_begin, NULL);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
socklen_t len = sizeof(struct sockaddr_in);
|
|
||||||
int cli_fd = nty_accept(fd, (struct sockaddr*)&remote, &len);
|
|
||||||
if (cli_fd % 1000 == 999) {
|
|
||||||
|
|
||||||
struct timeval tv_cur;
|
|
||||||
memcpy(&tv_cur, &tv_begin, sizeof(struct timeval));
|
|
||||||
|
|
||||||
gettimeofday(&tv_begin, NULL);
|
|
||||||
int time_used = TIME_SUB_MS(tv_begin, tv_cur);
|
|
||||||
|
|
||||||
printf("client fd : %d, time_used: %d\n", cli_fd, time_used);
|
|
||||||
}
|
|
||||||
printf("new client comming\n");
|
|
||||||
|
|
||||||
nty_coroutine *read_co;
|
|
||||||
int *arg = malloc(sizeof(int));
|
|
||||||
*arg = cli_fd;
|
|
||||||
nty_coroutine_create(&read_co, server_reader, arg);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
unsigned short base_port = 9096;
|
|
||||||
for (i = 0;i < 100;i ++) {
|
|
||||||
unsigned short *port = calloc(1, sizeof(unsigned short));
|
|
||||||
*port = base_port + i;
|
|
||||||
nty_coroutine_create(&co, server, port); ////////no run
|
|
||||||
}
|
|
||||||
|
|
||||||
nty_schedule_run(); //run
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,351 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/sysinfo.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
#include <sys/shm.h>
|
|
||||||
#include <sys/sem.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define __USE_GNU
|
|
||||||
|
|
||||||
#include <sched.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
static int global_shmid = -1;
|
|
||||||
static int global_semid = -1;
|
|
||||||
|
|
||||||
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct _shm_area {
|
|
||||||
int total;
|
|
||||||
int lock;
|
|
||||||
struct timeval tv_begin;
|
|
||||||
} shm_area;
|
|
||||||
|
|
||||||
static shm_area *global_shmaddr = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
#define MAX_CLIENT_NUM 1000000
|
|
||||||
|
|
||||||
|
|
||||||
struct timeval* get_shm_timevalue(void);
|
|
||||||
void set_shm_timevalue(struct timeval *tv);
|
|
||||||
|
|
||||||
|
|
||||||
int add_shmvalue(void);
|
|
||||||
int sub_shmvalue(void);
|
|
||||||
|
|
||||||
int init_shmvalue(void);
|
|
||||||
|
|
||||||
|
|
||||||
unsigned long cmpxchg(void *addr, unsigned long _old, unsigned long _new, int size) {
|
|
||||||
unsigned long prev;
|
|
||||||
volatile unsigned int *_ptr = (volatile unsigned int *)(addr);
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 1: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchgb %b1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchgw %w1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 4: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchg %1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SERVER_STRING "Server: ntyco\r\n"
|
|
||||||
|
|
||||||
|
|
||||||
int headers(char *buffer)
|
|
||||||
{
|
|
||||||
strcat(buffer, "HTTP/1.0 200 OK\r\n");
|
|
||||||
strcat(buffer, SERVER_STRING);
|
|
||||||
strcat(buffer, "Content-Type: text/html\r\n");
|
|
||||||
strcat(buffer, "\r\n");
|
|
||||||
|
|
||||||
return strlen(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
int body(char *buffer) {
|
|
||||||
strcat(buffer, "<html><h1> coroutine --> ntyco<h1></html>");
|
|
||||||
|
|
||||||
return strlen(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void server_reader(void *arg) {
|
|
||||||
int fd = *(int *)arg;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
char buf[1024] = {0};
|
|
||||||
ret = nty_recv(fd, buf, 1024, 0);
|
|
||||||
if (ret > 0) {
|
|
||||||
if(fd > MAX_CLIENT_NUM)
|
|
||||||
printf("read from server: %.*s\n", ret, buf);
|
|
||||||
|
|
||||||
memset(buf, 0, 1024);
|
|
||||||
int hlen = headers(buf);
|
|
||||||
int blen = body(buf+hlen);
|
|
||||||
|
|
||||||
ret = nty_send(fd, buf, strlen(buf), 0);
|
|
||||||
if (ret == -1) {
|
|
||||||
nty_close(fd);
|
|
||||||
sub_shmvalue();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ret == 0) {
|
|
||||||
nty_close(fd);
|
|
||||||
|
|
||||||
sub_shmvalue();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void server(void *arg) {
|
|
||||||
|
|
||||||
unsigned short port = *(unsigned short *)arg;
|
|
||||||
free(arg);
|
|
||||||
|
|
||||||
int fd = nty_socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (fd < 0) return ;
|
|
||||||
|
|
||||||
struct sockaddr_in local, remote;
|
|
||||||
local.sin_family = AF_INET;
|
|
||||||
local.sin_port = htons(port);
|
|
||||||
local.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
bind(fd, (struct sockaddr*)&local, sizeof(struct sockaddr_in));
|
|
||||||
|
|
||||||
listen(fd, 20);
|
|
||||||
printf("listen port : %d\n", port);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
socklen_t len = sizeof(struct sockaddr_in);
|
|
||||||
int cli_fd = nty_accept(fd, (struct sockaddr*)&remote, &len);
|
|
||||||
|
|
||||||
printf("new client comming\n");
|
|
||||||
|
|
||||||
nty_coroutine *read_co;
|
|
||||||
nty_coroutine_create(&read_co, server_reader, &cli_fd);
|
|
||||||
|
|
||||||
int value = add_shmvalue();
|
|
||||||
if (value % 1000 == 999) {
|
|
||||||
struct timeval *tv_begin = get_shm_timevalue();
|
|
||||||
|
|
||||||
struct timeval tv_cur;
|
|
||||||
memcpy(&tv_cur, tv_begin, sizeof(struct timeval));
|
|
||||||
gettimeofday(tv_begin, NULL);
|
|
||||||
|
|
||||||
int time_used = TIME_SUB_MS((*tv_begin), tv_cur);
|
|
||||||
|
|
||||||
printf("client sum: %d, time_used: %d\n", value, time_used);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int mulcore_entry(int begin_port) {
|
|
||||||
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
unsigned short base_port = begin_port;
|
|
||||||
//for (i = 0;i < 10;i ++) {
|
|
||||||
unsigned short *port = calloc(1, sizeof(unsigned short));
|
|
||||||
*port = base_port + i;
|
|
||||||
nty_coroutine_create(&co, server, port); ////////no run
|
|
||||||
//}
|
|
||||||
|
|
||||||
nty_schedule_run(); //run
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_bind(void) {
|
|
||||||
|
|
||||||
int num = sysconf(_SC_NPROCESSORS_CONF);
|
|
||||||
|
|
||||||
pid_t self_id = syscall(__NR_gettid);
|
|
||||||
//printf("selfid --> %d\n", self_id);
|
|
||||||
|
|
||||||
cpu_set_t mask;
|
|
||||||
|
|
||||||
CPU_ZERO(&mask);
|
|
||||||
CPU_SET(self_id % num, &mask);
|
|
||||||
|
|
||||||
sched_setaffinity(0, sizeof(mask), &mask);
|
|
||||||
|
|
||||||
mulcore_entry(9096);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct timeval* get_shm_timevalue(void) {
|
|
||||||
|
|
||||||
return global_shmaddr ? &global_shmaddr->tv_begin : NULL;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_shm_timevalue(struct timeval *tv) {
|
|
||||||
if (global_shmaddr == NULL || tv == NULL) {
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&global_shmaddr->tv_begin, tv, sizeof(struct timeval));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int add_shmvalue(void) {
|
|
||||||
int value = 0;
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
value = global_shmaddr->total;
|
|
||||||
ret = cmpxchg(&global_shmaddr->total, (unsigned long)value, (unsigned long)(value+1), 4);
|
|
||||||
|
|
||||||
|
|
||||||
} while (ret != value);
|
|
||||||
|
|
||||||
return global_shmaddr->total;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sub_shmvalue(void) {
|
|
||||||
int value = 0;
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
value = global_shmaddr->total;
|
|
||||||
ret = cmpxchg(&global_shmaddr->total, (unsigned long)value, (unsigned long)(value-1), 4);
|
|
||||||
|
|
||||||
|
|
||||||
} while (ret != value);
|
|
||||||
|
|
||||||
return global_shmaddr->total;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int init_shmvalue(void) {
|
|
||||||
|
|
||||||
global_shmid = shmget(IPC_PRIVATE, sizeof(shm_area), IPC_CREAT|0600);
|
|
||||||
if (global_shmid < 0) {
|
|
||||||
perror("shmget failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
global_shmaddr = (shm_area*)shmat(global_shmid, NULL, 0);
|
|
||||||
if (global_shmaddr == (shm_area*)-1) {
|
|
||||||
perror("shmat addr error");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
global_shmaddr->total = 0;
|
|
||||||
gettimeofday(&global_shmaddr->tv_begin, NULL);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
int ret = init_shmvalue();
|
|
||||||
if (ret == -1) {
|
|
||||||
printf("init share memory failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fork();
|
|
||||||
fork();
|
|
||||||
//fork();
|
|
||||||
//fork();
|
|
||||||
//fork();
|
|
||||||
|
|
||||||
process_bind();
|
|
||||||
|
|
||||||
@@ -1,377 +0,0 @@
|
|||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
#include <openssl/pem.h>
|
|
||||||
#include <openssl/bio.h>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define MAX_CLIENT_NUM 1000000
|
|
||||||
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
|
|
||||||
|
|
||||||
#define NTY_WEBSOCKET_SERVER_PORT 9096
|
|
||||||
#define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
||||||
#define MAX_BUFFER_LENGTH 1024
|
|
||||||
|
|
||||||
struct _nty_ophdr {
|
|
||||||
|
|
||||||
unsigned char opcode:4,
|
|
||||||
rsv3:1,
|
|
||||||
rsv2:1,
|
|
||||||
rsv1:1,
|
|
||||||
fin:1;
|
|
||||||
unsigned char payload_length:7,
|
|
||||||
mask:1;
|
|
||||||
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
struct _nty_websocket_head_126 {
|
|
||||||
unsigned short payload_length;
|
|
||||||
char mask_key[4];
|
|
||||||
unsigned char data[8];
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
struct _nty_websocket_head_127 {
|
|
||||||
|
|
||||||
unsigned long long payload_length;
|
|
||||||
char mask_key[4];
|
|
||||||
|
|
||||||
unsigned char data[8];
|
|
||||||
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
#define UNION_HEADER(type1, type2) \
|
|
||||||
struct { \
|
|
||||||
struct type1 hdr; \
|
|
||||||
struct type2 len; \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct _nty_websocket_head_127 nty_websocket_head_127;
|
|
||||||
typedef struct _nty_websocket_head_126 nty_websocket_head_126;
|
|
||||||
typedef struct _nty_ophdr nty_ophdr;
|
|
||||||
|
|
||||||
|
|
||||||
static int ntySetNonblock(int fd) {
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
flags = fcntl(fd, F_GETFL, 0);
|
|
||||||
if (flags < 0) return flags;
|
|
||||||
flags |= O_NONBLOCK;
|
|
||||||
if (fcntl(fd, F_SETFL, flags) < 0) return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntySetBlock(int fd)
|
|
||||||
{
|
|
||||||
int flags = fcntl(fd, F_GETFL, 0);
|
|
||||||
if (flags < 0) return flags;
|
|
||||||
flags &=~O_NONBLOCK;
|
|
||||||
if (fcntl(fd, F_SETFL, flags) < 0) return -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int base64_encode(char *in_str, int in_len, char *out_str) {
|
|
||||||
BIO *b64, *bio;
|
|
||||||
BUF_MEM *bptr = NULL;
|
|
||||||
size_t size = 0;
|
|
||||||
|
|
||||||
if (in_str == NULL || out_str == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
b64 = BIO_new(BIO_f_base64());
|
|
||||||
bio = BIO_new(BIO_s_mem());
|
|
||||||
bio = BIO_push(b64, bio);
|
|
||||||
|
|
||||||
BIO_write(bio, in_str, in_len);
|
|
||||||
BIO_flush(bio);
|
|
||||||
|
|
||||||
BIO_get_mem_ptr(bio, &bptr);
|
|
||||||
memcpy(out_str, bptr->data, bptr->length);
|
|
||||||
out_str[bptr->length-1] = '\0';
|
|
||||||
size = bptr->length;
|
|
||||||
|
|
||||||
BIO_free_all(bio);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int readline(char* allbuf,int level,char* linebuf){
|
|
||||||
int len = strlen(allbuf);
|
|
||||||
|
|
||||||
for (;level < len; ++level) {
|
|
||||||
if(allbuf[level]=='\r' && allbuf[level+1]=='\n')
|
|
||||||
return level+2;
|
|
||||||
else
|
|
||||||
*(linebuf++) = allbuf[level];
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void umask(char *data,int len,char *mask){
|
|
||||||
int i;
|
|
||||||
for (i = 0;i < len;i ++)
|
|
||||||
*(data+i) ^= *(mask+(i%4));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char* decode_packet(unsigned char *stream, char *mask, int length, int *ret) {
|
|
||||||
|
|
||||||
nty_ophdr *hdr = (nty_ophdr*)stream;
|
|
||||||
unsigned char *data = stream + sizeof(nty_ophdr);
|
|
||||||
int size = 0;
|
|
||||||
int start = 0;
|
|
||||||
//char mask[4] = {0};
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if ((hdr->mask & 0x7F) == 126) {
|
|
||||||
|
|
||||||
nty_websocket_head_126 *hdr126 = (nty_websocket_head_126*)data;
|
|
||||||
size = hdr126->payload_length;
|
|
||||||
|
|
||||||
for (i = 0;i < 4;i ++) {
|
|
||||||
mask[i] = hdr126->mask_key[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
start = 8;
|
|
||||||
|
|
||||||
} else if ((hdr->mask & 0x7F) == 127) {
|
|
||||||
|
|
||||||
nty_websocket_head_127 *hdr127 = (nty_websocket_head_127*)data;
|
|
||||||
size = hdr127->payload_length;
|
|
||||||
|
|
||||||
for (i = 0;i < 4;i ++) {
|
|
||||||
mask[i] = hdr127->mask_key[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
start = 14;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
size = hdr->payload_length;
|
|
||||||
|
|
||||||
memcpy(mask, data, 4);
|
|
||||||
start = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ret = size;
|
|
||||||
umask(stream+start, size, mask);
|
|
||||||
|
|
||||||
return stream + start;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int encode_packet(char *buffer,char *mask, char *stream, int length) {
|
|
||||||
|
|
||||||
nty_ophdr head = {0};
|
|
||||||
head.fin = 1;
|
|
||||||
head.opcode = 1;
|
|
||||||
int size = 0;
|
|
||||||
|
|
||||||
if (length < 126) {
|
|
||||||
head.payload_length = length;
|
|
||||||
memcpy(buffer, &head, sizeof(nty_ophdr));
|
|
||||||
size = 2;
|
|
||||||
} else if (length < 0xffff) {
|
|
||||||
nty_websocket_head_126 hdr = {0};
|
|
||||||
hdr.payload_length = length;
|
|
||||||
memcpy(hdr.mask_key, mask, 4);
|
|
||||||
|
|
||||||
memcpy(buffer, &head, sizeof(nty_ophdr));
|
|
||||||
memcpy(buffer+sizeof(nty_ophdr), &hdr, sizeof(nty_websocket_head_126));
|
|
||||||
size = sizeof(nty_websocket_head_126);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
nty_websocket_head_127 hdr = {0};
|
|
||||||
hdr.payload_length = length;
|
|
||||||
memcpy(hdr.mask_key, mask, 4);
|
|
||||||
|
|
||||||
memcpy(buffer, &head, sizeof(nty_ophdr));
|
|
||||||
memcpy(buffer+sizeof(nty_ophdr), &hdr, sizeof(nty_websocket_head_127));
|
|
||||||
|
|
||||||
size = sizeof(nty_websocket_head_127);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(buffer+2, stream, length);
|
|
||||||
|
|
||||||
return length + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void server_reader(void *arg) {
|
|
||||||
int fd = *(int *)arg;
|
|
||||||
int length = 0;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
struct pollfd fds;
|
|
||||||
fds.fd = fd;
|
|
||||||
fds.events = POLLIN;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
char stream[MAX_BUFFER_LENGTH] = {0};
|
|
||||||
length = nty_recv(fd, stream, MAX_BUFFER_LENGTH, 0);
|
|
||||||
if (length > 0) {
|
|
||||||
if(fd > MAX_CLIENT_NUM)
|
|
||||||
printf("read from server: %.*s\n", length, stream);
|
|
||||||
|
|
||||||
char mask[4] = {0};
|
|
||||||
char *data = decode_packet(stream, mask, length, &ret);
|
|
||||||
|
|
||||||
printf(" data : %s , length : %d\n", data, ret);
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
char buffer[MAX_BUFFER_LENGTH+14] = {0};
|
|
||||||
ret = encode_packet(buffer, mask, data, ret);
|
|
||||||
ret = nty_send(fd, buffer, ret, 0);
|
|
||||||
#endif
|
|
||||||
} else if (length == 0) {
|
|
||||||
nty_close(fd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int handshake(int cli_fd) {
|
|
||||||
int level = 0;
|
|
||||||
char buffer[MAX_BUFFER_LENGTH];
|
|
||||||
char linebuf[128]; //Sec-WebSocket-Accept
|
|
||||||
char sec_accept[32] = {0}; //sha1 data
|
|
||||||
unsigned char sha1_data[SHA_DIGEST_LENGTH+1] = {0}; //reponse head buffer
|
|
||||||
//char head[MAX_BUFFER_LENGTH] = {0};
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
if (read(cli_fd, buffer, sizeof(buffer))<=0)
|
|
||||||
perror("read");
|
|
||||||
#else
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
int length = recv_buffer(cli_fd, buffer, MAX_BUFFER_LENGTH, &ret);
|
|
||||||
if (ret < 0) perror("read");
|
|
||||||
|
|
||||||
#endif
|
|
||||||
printf("request\n");
|
|
||||||
printf("%s\n",buffer);
|
|
||||||
|
|
||||||
do {
|
|
||||||
memset(linebuf, 0, sizeof(linebuf));
|
|
||||||
level = readline(buffer,level,linebuf);
|
|
||||||
|
|
||||||
if (strstr(linebuf,"Sec-WebSocket-Key") != NULL) {
|
|
||||||
|
|
||||||
strcat(linebuf, GUID);
|
|
||||||
|
|
||||||
SHA1((unsigned char*)&linebuf+19,strlen(linebuf+19),(unsigned char*)&sha1_data);
|
|
||||||
|
|
||||||
memset(buffer, 0, MAX_BUFFER_LENGTH);
|
|
||||||
base64_encode(sha1_data,strlen(sha1_data),sec_accept);
|
|
||||||
sprintf(buffer, "HTTP/1.1 101 Switching Protocols\r\n" \
|
|
||||||
"Upgrade: websocket\r\n" \
|
|
||||||
"Connection: Upgrade\r\n" \
|
|
||||||
"Sec-WebSocket-Accept: %s\r\n" \
|
|
||||||
"\r\n", sec_accept);
|
|
||||||
|
|
||||||
|
|
||||||
printf("response\n");
|
|
||||||
printf("%s",buffer);
|
|
||||||
#if 1
|
|
||||||
if (write(cli_fd, buffer, strlen(buffer)) < 0)
|
|
||||||
perror("write");
|
|
||||||
#else
|
|
||||||
|
|
||||||
length = send_buffer(cli_fd, head, strlen(head));
|
|
||||||
assert(length == strlen(head));
|
|
||||||
|
|
||||||
#endif
|
|
||||||
printf("accept : %s\n", sec_accept);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}while((buffer[level]!='\r' || buffer[level+1]!='\n') && level!=-1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int init_server(void) {
|
|
||||||
|
|
||||||
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (sockfd < 0) {
|
|
||||||
printf("socket failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(NTY_WEBSOCKET_SERVER_PORT);
|
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
|
|
||||||
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0) {
|
|
||||||
printf("bind failed\n");
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listen(sockfd, 5) < 0) {
|
|
||||||
printf("listen failed\n");
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sockfd;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void server(void *arg) {
|
|
||||||
|
|
||||||
int sockfd = init_server();
|
|
||||||
|
|
||||||
struct timeval tv_begin;
|
|
||||||
gettimeofday(&tv_begin, NULL);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
socklen_t len = sizeof(struct sockaddr_in);
|
|
||||||
struct sockaddr_in remote;
|
|
||||||
|
|
||||||
int cli_fd = nty_accept(sockfd, (struct sockaddr*)&remote, &len);
|
|
||||||
if (cli_fd % 1000 == 999) {
|
|
||||||
|
|
||||||
struct timeval tv_cur;
|
|
||||||
memcpy(&tv_cur, &tv_begin, sizeof(struct timeval));
|
|
||||||
|
|
||||||
gettimeofday(&tv_begin, NULL);
|
|
||||||
int time_used = TIME_SUB_MS(tv_begin, tv_cur);
|
|
||||||
|
|
||||||
printf("client fd : %d, time_used: %d\n", cli_fd, time_used);
|
|
||||||
}
|
|
||||||
printf("new client comming\n");
|
|
||||||
ntySetBlock(cli_fd);
|
|
||||||
handshake(cli_fd);
|
|
||||||
ntySetNonblock(cli_fd);
|
|
||||||
|
|
||||||
nty_coroutine *read_co = NULL;
|
|
||||||
nty_coroutine_create(&read_co, server_reader, &cli_fd);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
nty_coroutine_create(&co, server, NULL);
|
|
||||||
|
|
||||||
nty_schedule_run();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,536 +0,0 @@
|
|||||||
/*
|
|
||||||
* Author : WangBoJing , email : 1989wangbojing@gmail.com
|
|
||||||
*
|
|
||||||
* Copyright Statement:
|
|
||||||
* --------------------
|
|
||||||
* This software is protected by Copyright and the information contained
|
|
||||||
* herein is confidential. The software may not be copied and the information
|
|
||||||
* contained herein may not be used or disclosed except with the written
|
|
||||||
* permission of Author. (C) 2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
|
|
||||||
**** ***** *****
|
|
||||||
*** * ** ***
|
|
||||||
*** * * * **
|
|
||||||
* ** * * ** **
|
|
||||||
* ** * * ** *
|
|
||||||
* ** * ** ** *
|
|
||||||
* ** * *** **
|
|
||||||
* ** * *********** ***** ***** ** ****
|
|
||||||
* ** * ** ** ** ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** ** * ** ** **
|
|
||||||
* ** * ** * * ** ** **
|
|
||||||
* ** * ** ** * ** * ** **
|
|
||||||
* *** ** * * ** * ** **
|
|
||||||
* *** ** * ** * * ** **
|
|
||||||
* ** ** * ** ** * ** **
|
|
||||||
* ** ** * * ** * ** **
|
|
||||||
***** * **** * ***** ****
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*****
|
|
||||||
****
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/sysinfo.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
#include <sys/shm.h>
|
|
||||||
#include <sys/sem.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
|
|
||||||
#define __USE_GNU
|
|
||||||
|
|
||||||
#include <sched.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#include "nty_coroutine.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define MAX_CLIENT_NUM 1000000
|
|
||||||
#define TOTALFDS 16
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct _shm_area {
|
|
||||||
int totalfds[TOTALFDS];
|
|
||||||
char cpu_lb[TOTALFDS];
|
|
||||||
//mtx : default 0
|
|
||||||
// 1 : lock -- del epoll
|
|
||||||
// 2 : lock -- del complete
|
|
||||||
// 3 : unlock -- add
|
|
||||||
// 0 : unlock -- add complete
|
|
||||||
int accept_mtx;
|
|
||||||
} shm_area;
|
|
||||||
|
|
||||||
static shm_area *global_shmaddr = NULL;
|
|
||||||
static int global_shmid = -1;
|
|
||||||
|
|
||||||
int cpu_size = 0;
|
|
||||||
int accept_disable = 1000;
|
|
||||||
int enable_accept = 1;
|
|
||||||
pid_t self_id = 0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned long cmpxchg(void *addr, unsigned long _old, unsigned long _new, int size) {
|
|
||||||
unsigned long prev;
|
|
||||||
volatile unsigned int *_ptr = (volatile unsigned int *)(addr);
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 1: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchgb %b1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchgw %w1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 4: {
|
|
||||||
__asm__ volatile (
|
|
||||||
"lock; cmpxchg %1, %2"
|
|
||||||
: "=a" (prev)
|
|
||||||
: "r" (_new), "m" (*_ptr), "0" (_old)
|
|
||||||
: "memory");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int atomic_add(volatile int *value, int add)
|
|
||||||
{
|
|
||||||
__asm__ volatile (
|
|
||||||
|
|
||||||
"lock;"
|
|
||||||
" addl %0, %1; "
|
|
||||||
|
|
||||||
: "+r" (add) : "m" (*value) : "cc", "memory");
|
|
||||||
|
|
||||||
return add;
|
|
||||||
}
|
|
||||||
|
|
||||||
int atomic_sub(volatile int *value, int sub)
|
|
||||||
{
|
|
||||||
__asm__ volatile (
|
|
||||||
|
|
||||||
"lock;"
|
|
||||||
" subl %0, %1; "
|
|
||||||
|
|
||||||
: "+r" (sub) : "m" (*value) : "cc", "memory");
|
|
||||||
|
|
||||||
return sub;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define ISspace(x) isspace((int)(x))
|
|
||||||
|
|
||||||
#define SERVER_STRING "Server: ntyco_httpd/0.1.0\r\n"
|
|
||||||
#define STDIN 0
|
|
||||||
#define STDOUT 1
|
|
||||||
#define STDERR 2
|
|
||||||
|
|
||||||
#define ENABLE_NTYCO 0
|
|
||||||
|
|
||||||
#if ENABLE_NTYCO
|
|
||||||
|
|
||||||
#define socket nty_socket
|
|
||||||
#define accept nty_accept
|
|
||||||
#define recv(a, b, c, d) nty_recv(a, b, c, d)
|
|
||||||
#define send(a, b, c, d) nty_send(a, b, c, d)
|
|
||||||
|
|
||||||
#define MAX_BUFFER_LENGTH 1024
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define INC_COUNTFD do { \
|
|
||||||
atomic_add(&global_shmaddr->totalfds[self_id % cpu_size], 1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
#define DEC_COUNTFD do { \
|
|
||||||
atomic_sub(&global_shmaddr->totalfds[self_id % cpu_size], 1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
int get_countfd(void) {
|
|
||||||
return global_shmaddr->totalfds[self_id % cpu_size];
|
|
||||||
}
|
|
||||||
|
|
||||||
int max_countfd(void) {
|
|
||||||
|
|
||||||
int count = -1;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
if (count < global_shmaddr->totalfds[i]) {
|
|
||||||
count = global_shmaddr->totalfds[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int min_countfd(void) {
|
|
||||||
|
|
||||||
int count = 0xffffffff;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
if (count > global_shmaddr->totalfds[i]) {
|
|
||||||
count = global_shmaddr->totalfds[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare_out_countfd(void) {
|
|
||||||
|
|
||||||
int current = get_countfd();
|
|
||||||
int min = min_countfd();
|
|
||||||
|
|
||||||
if ((current * 7 / 8) > min) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare_in_countfd(void) {
|
|
||||||
|
|
||||||
int current = get_countfd();
|
|
||||||
int max = max_countfd();
|
|
||||||
|
|
||||||
if ((current * 8 / 7) < max) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_countfds(void) {
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
printf("%5d : %5d ", i, global_shmaddr->totalfds[i]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void lock_accept(void) {
|
|
||||||
|
|
||||||
global_shmaddr->cpu_lb[self_id % cpu_size] = 1;
|
|
||||||
|
|
||||||
int count = 0xffffffff;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
if (count > global_shmaddr->totalfds[i]) {
|
|
||||||
count = global_shmaddr->totalfds[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0;i < cpu_size;i ++) {
|
|
||||||
if (count == global_shmaddr->totalfds[i]) {
|
|
||||||
global_shmaddr->cpu_lb[i] = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
char read_accept(void) {
|
|
||||||
return global_shmaddr->cpu_lb[self_id % cpu_size];
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_accept(char state) { //0, 1, 2, 3
|
|
||||||
global_shmaddr->cpu_lb[self_id % cpu_size] = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lock(void) {
|
|
||||||
|
|
||||||
return cmpxchg(&global_shmaddr->accept_mtx, 0, 1, 4); //zero:success, non-zero:failed
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock(void) {
|
|
||||||
|
|
||||||
global_shmaddr->accept_mtx = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void accept_request(void *arg);
|
|
||||||
void bad_request(int);
|
|
||||||
void cat(int);
|
|
||||||
void cannot_execute(int);
|
|
||||||
void error_die(const char *);
|
|
||||||
void execute_cgi(int, const char *, const char *, const char *);
|
|
||||||
int get_line(int, char *, int);
|
|
||||||
void headers(int);
|
|
||||||
void not_found(int);
|
|
||||||
void serve_file(int);
|
|
||||||
int startup(u_short *);
|
|
||||||
void unimplemented(int);
|
|
||||||
|
|
||||||
|
|
||||||
void accept_request(void* arg)
|
|
||||||
{
|
|
||||||
int client = *(int*)arg;
|
|
||||||
|
|
||||||
char buf[1024];
|
|
||||||
size_t numchars;
|
|
||||||
char method[16];
|
|
||||||
char url[32];
|
|
||||||
char path[64];
|
|
||||||
size_t i, j;
|
|
||||||
struct stat st;
|
|
||||||
int cgi = 0; /* becomes true if server decides this is a CGI
|
|
||||||
* program */
|
|
||||||
char *query_string = NULL;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
numchars = nty_recv(client, buf, sizeof(buf), 0);
|
|
||||||
if (numchars > 0) {
|
|
||||||
serve_file(client);
|
|
||||||
} else if (numchars == 0) {
|
|
||||||
nty_close(client);
|
|
||||||
DEC_COUNTFD;
|
|
||||||
break;
|
|
||||||
} else if (numchars == -1) {
|
|
||||||
if (errno == EAGAIN) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nty_close(client);
|
|
||||||
DEC_COUNTFD;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define HTML_PAGE "<!DOCTYPE html> \
|
|
||||||
<html> \
|
|
||||||
<head> \
|
|
||||||
<title>Welcome to nginx!</title> \
|
|
||||||
<style> \
|
|
||||||
body { \
|
|
||||||
width: 35em;\
|
|
||||||
margin: 0 auto;\
|
|
||||||
font-family: Tahoma, Verdana, Arial, sans-serif;\
|
|
||||||
}\
|
|
||||||
</style>\
|
|
||||||
</head>\
|
|
||||||
<body>\
|
|
||||||
<h1>Welcome to nginx!</h1>\
|
|
||||||
<p>If you see this page, the nginx web server is successfully installed and\
|
|
||||||
working. Further configuration is required.</p>\
|
|
||||||
\
|
|
||||||
<p>For online documentation and support please refer to\
|
|
||||||
<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\
|
|
||||||
Commercial support is available at\
|
|
||||||
<a href=\"http://nginx.com/\">nginx.com</a>.</p>\
|
|
||||||
\
|
|
||||||
<p><em>Thank you for using nginx.</em></p>\
|
|
||||||
</body>\
|
|
||||||
</html>"
|
|
||||||
|
|
||||||
|
|
||||||
void error_die(const char *sc)
|
|
||||||
{
|
|
||||||
printf("%s\n", sc);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void headers(int client)
|
|
||||||
{
|
|
||||||
char buf[1024] = {0};
|
|
||||||
char content[128] = {0};
|
|
||||||
|
|
||||||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
|
||||||
strcat(buf, SERVER_STRING);
|
|
||||||
strcat(buf, "Content-Type: text/html\r\n");
|
|
||||||
#if 0
|
|
||||||
strcat(buf, "Transfer-Encoding: chunked\r\n");
|
|
||||||
#else
|
|
||||||
sprintf(content, "Content-Length: %ld\r\n", strlen(HTML_PAGE));
|
|
||||||
strcat(buf, content);
|
|
||||||
#endif
|
|
||||||
strcat(buf, "\r\n");
|
|
||||||
|
|
||||||
strcat(buf, HTML_PAGE);
|
|
||||||
|
|
||||||
int ret = nty_send(client, buf, strlen(buf), 0);
|
|
||||||
if (ret == -1) {
|
|
||||||
printf("send : errno : %d\n", errno);
|
|
||||||
} else {
|
|
||||||
//printf("headers send ret : %d\n", ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void serve_file(int client)
|
|
||||||
{
|
|
||||||
headers(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_EPOLLSIZE 10240
|
|
||||||
|
|
||||||
void server(void *arg) {
|
|
||||||
|
|
||||||
int fd = *(int *)arg;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
struct sockaddr_in client_addr;
|
|
||||||
memset(&client_addr, 0, sizeof(struct sockaddr_in));
|
|
||||||
socklen_t client_len = sizeof(client_addr);
|
|
||||||
|
|
||||||
if (lock()) {
|
|
||||||
nty_coroutine_sleep(0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int clientfd = nty_accept(fd, (struct sockaddr*)&client_addr, &client_len);
|
|
||||||
unlock();
|
|
||||||
|
|
||||||
if (clientfd < 0) {
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
INC_COUNTFD;
|
|
||||||
|
|
||||||
nty_coroutine *read_co;
|
|
||||||
int *client_sock = (int *)malloc(sizeof(int));
|
|
||||||
*client_sock = clientfd;
|
|
||||||
nty_coroutine_create(&read_co, accept_request, client_sock);
|
|
||||||
|
|
||||||
print_countfds();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_bind(int fd) {
|
|
||||||
|
|
||||||
int num = sysconf(_SC_NPROCESSORS_CONF);
|
|
||||||
|
|
||||||
self_id = syscall(__NR_gettid);
|
|
||||||
//printf("selfid --> %d\n", self_id);
|
|
||||||
|
|
||||||
cpu_set_t mask;
|
|
||||||
|
|
||||||
CPU_ZERO(&mask);
|
|
||||||
CPU_SET(self_id % num, &mask);
|
|
||||||
|
|
||||||
sched_setaffinity(0, sizeof(mask), &mask);
|
|
||||||
|
|
||||||
nty_coroutine *co = NULL;
|
|
||||||
nty_coroutine_create(&co, server, &fd);
|
|
||||||
|
|
||||||
nty_schedule_run();
|
|
||||||
}
|
|
||||||
|
|
||||||
int init_shmvalue(int num) {
|
|
||||||
|
|
||||||
global_shmid = shmget(IPC_PRIVATE, sizeof(shm_area), IPC_CREAT|0600);
|
|
||||||
if (global_shmid < 0) {
|
|
||||||
perror("shmget failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
global_shmaddr = (shm_area*)shmat(global_shmid, NULL, 0);
|
|
||||||
if (global_shmaddr == (shm_area*)-1) {
|
|
||||||
perror("shmat addr error");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memset(global_shmaddr->totalfds, 0, TOTALFDS * sizeof(int));
|
|
||||||
memset(global_shmaddr->cpu_lb, 0, TOTALFDS * sizeof(char));
|
|
||||||
global_shmaddr->accept_mtx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
short port = 9001;
|
|
||||||
struct sockaddr_in client_name;
|
|
||||||
socklen_t client_name_len = sizeof(client_name);
|
|
||||||
|
|
||||||
int fd = nty_socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (fd < 0) return -1;
|
|
||||||
|
|
||||||
struct sockaddr_in local, remote;
|
|
||||||
local.sin_family = AF_INET;
|
|
||||||
local.sin_port = htons(port);
|
|
||||||
local.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
bind(fd, (struct sockaddr*)&local, sizeof(struct sockaddr_in));
|
|
||||||
|
|
||||||
listen(fd, 5);
|
|
||||||
|
|
||||||
int num = sysconf(_SC_NPROCESSORS_CONF);
|
|
||||||
cpu_size = num;
|
|
||||||
init_shmvalue(num);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
pid_t pid = 0;
|
|
||||||
for(i = 0;i < num;i ++) {
|
|
||||||
pid = fork();
|
|
||||||
if(pid <= (pid_t) 0)
|
|
||||||
{
|
|
||||||
usleep(1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid > 0) {
|
|
||||||
printf("ntyco_httpd server running ...\n");
|
|
||||||
getchar();
|
|
||||||
} else if (pid == 0) {
|
|
||||||
process_bind(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<button onclick="svc_connectPlatform()"> connect</button>
|
|
||||||
<button onclick="svc_send('hello, websocket')"> send</button>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
function svc_connectPlatform() {
|
|
||||||
//alert("");
|
|
||||||
var wsServer = 'ws://192.168.189.128:9096/';
|
|
||||||
try {
|
|
||||||
svc_websocket = new WebSocket(wsServer);
|
|
||||||
} catch (evt) {
|
|
||||||
console.log("new WebSocket error:" + evt.data);
|
|
||||||
svc_websocket = null;
|
|
||||||
if (typeof(connCb) != "undefined" && connCb != null)
|
|
||||||
connCb("-1", "connect error!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//alert("");
|
|
||||||
svc_websocket.onopen = svc_onOpen;
|
|
||||||
svc_websocket.onclose = svc_onClose;
|
|
||||||
svc_websocket.onmessage = svc_onMessage;
|
|
||||||
svc_websocket.onerror = svc_onError;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function svc_onOpen(evt) {
|
|
||||||
console.log("Connected to WebSocket server.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function svc_onClose(evt) {
|
|
||||||
console.log("Disconnected");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function svc_onMessage(evt) {
|
|
||||||
alert('Retrieved data from server: ' + evt.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function svc_onError(evt) {
|
|
||||||
console.log('Error occured: ' + evt.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function svc_send(msg) {
|
|
||||||
if (svc_websocket.readyState == WebSocket.OPEN) {
|
|
||||||
svc_websocket.send(msg);
|
|
||||||
} else {
|
|
||||||
console.log("send failed. websocket not open. please check.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
21
README.md
21
README.md
@@ -2,29 +2,17 @@
|
|||||||
|
|
||||||
## 需求
|
## 需求
|
||||||
1. ntyco需要作为kvstore的submodule,通过git clone一次下载。 **完成**。
|
1. ntyco需要作为kvstore的submodule,通过git clone一次下载。 **完成**。
|
||||||
2. README需要包含编译步骤,测试方案与可行性,性能数据。
|
2. README需要包含编译步骤,测试方案与可行性,性能数据。 **完成**。
|
||||||
3. 全量持久化保存数据集。 **BUG FIX,完成**。
|
3. 全量持久化保存数据集。 **BUG FIX,完成**。
|
||||||
4. 持久化的性能数据。
|
4. 持久化的性能数据。
|
||||||
5. 特殊字符,可以解决redis的resp协议。 **完成**。
|
5. 特殊字符,可以解决redis的resp协议。 **完成**。
|
||||||
```
|
|
||||||
简单字符串
|
|
||||||
+OK\r\n
|
|
||||||
错误
|
|
||||||
-ERR message\r\n
|
|
||||||
整数
|
|
||||||
:1000\r\n
|
|
||||||
批量字符串
|
|
||||||
$6\r\nfoobar\r\n
|
|
||||||
数组
|
|
||||||
*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n
|
|
||||||
```
|
|
||||||
6. 实现配置文件,把日志级别,端口ip,主从模式,持久化方案。 **完成**。
|
6. 实现配置文件,把日志级别,端口ip,主从模式,持久化方案。 **完成**。
|
||||||
7. 持久化落盘用io_uring,加载配置文件用mmap。
|
7. 持久化落盘用io_uring,加载配置文件用mmap。 **完成**。
|
||||||
8. 主从同步的性能,开启与关闭性能做到。
|
8. 主从同步的性能,开启与关闭性能做到。
|
||||||
9. 主从同步600w条,出现的coredump。
|
9. 主从同步600w条,出现的coredump。
|
||||||
10. 主从同步用ebpf实现。
|
10. 主从同步用ebpf实现。
|
||||||
11. 内存池测试qps与虚拟内存,物理内存。
|
11. 内存池测试qps与虚拟内存,物理内存。
|
||||||
12. 实现一个内存泄露检测组件。
|
12. 实现一个内存泄露检测组件。 **完成**。
|
||||||
|
|
||||||
|
|
||||||
## 环境安装与编译
|
## 环境安装与编译
|
||||||
@@ -33,10 +21,13 @@ $6\r\nfoobar\r\n
|
|||||||
sudo apt install libxml2 libxml2-dev
|
sudo apt install libxml2 libxml2-dev
|
||||||
# hiredis client
|
# hiredis client
|
||||||
sudo apt install -y libhiredis-dev
|
sudo apt install -y libhiredis-dev
|
||||||
|
# bpftrace
|
||||||
|
sudo apt install -y bpftrace
|
||||||
|
|
||||||
git clone git@gitlab.0voice.com:lianyiheng/9.1-kvstore.git
|
git clone git@gitlab.0voice.com:lianyiheng/9.1-kvstore.git
|
||||||
cd 9.1-kvstore/
|
cd 9.1-kvstore/
|
||||||
make
|
make
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 测试
|
## 测试
|
||||||
|
|||||||
26
dump/kvs_dump.h
Normal file
26
dump/kvs_dump.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef __KVS_DUMP_H__
|
||||||
|
#define __KVS_DUMP_H__
|
||||||
|
|
||||||
|
#include "kvstore.h"
|
||||||
|
|
||||||
|
extern char global_oplog_file[256];
|
||||||
|
extern char global_array_file[256];
|
||||||
|
extern char global_rbtree_file[256];
|
||||||
|
extern char global_hash_file[256];
|
||||||
|
|
||||||
|
int kvs_create_snapshot(const char* array_file, const char* rbtree_file, const char* hash_file);
|
||||||
|
int kvs_create_snapshot_async(const char *ip, int port);
|
||||||
|
void __create_snapshot_ok(const char* array_file, const char* rbtree_file, const char* hash_file);
|
||||||
|
|
||||||
|
extern int global_oplog_fd;
|
||||||
|
|
||||||
|
|
||||||
|
int init_cmd_log(const char *file, int *logfd);
|
||||||
|
int destroy_cmd_log(int logfd);
|
||||||
|
|
||||||
|
int kvs_oplog_append(const uint8_t *cmd, size_t len, int logfd);
|
||||||
|
int kvs_replay_log(int logfd);
|
||||||
|
int ksv_clear_log(int logfd);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "kvstore.h"
|
#include "kvs_dump.h"
|
||||||
#include "kvs_rw_tools.h"
|
#include "kvs_rw_tools.h"
|
||||||
#include "memory/alloc_dispatch.h"
|
#include "memory/alloc_dispatch.h"
|
||||||
#include "kvs_protocol_resp.h"
|
#include "kvs_protocol_resp.h"
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
int global_cmd_log_fd = -1;
|
int global_oplog_fd = -1;
|
||||||
static off_t g_log_off = -1;
|
static off_t g_log_off = -1;
|
||||||
|
|
||||||
int init_cmd_log(const char *file, int *logfd){
|
int init_cmd_log(const char *file, int *logfd){
|
||||||
@@ -24,7 +24,7 @@ int init_cmd_log(const char *file, int *logfd){
|
|||||||
int destroy_cmd_log(int logfd){
|
int destroy_cmd_log(int logfd){
|
||||||
fsync(logfd);
|
fsync(logfd);
|
||||||
close(logfd);
|
close(logfd);
|
||||||
global_cmd_log_fd = -1;
|
global_oplog_fd = -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
78
dump/kvs_snapshot.c
Normal file
78
dump/kvs_snapshot.c
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#include "kvstore.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
char global_oplog_file[256] = "kvs_oplog.default.db";
|
||||||
|
char global_array_file[256] = "kvs_array.default.db";
|
||||||
|
char global_rbtree_file[256] = "kvs_rbtree.default.db";
|
||||||
|
char global_hash_file[256] = "kvs_hash.default.db";
|
||||||
|
|
||||||
|
int kvs_create_snapshot(const char* array_file, const char* rbtree_file, const char* hash_file){
|
||||||
|
int ret = 0;
|
||||||
|
int rc = 0;
|
||||||
|
#if ENABLE_ARRAY
|
||||||
|
rc = kvs_array_save(&global_array, array_file);
|
||||||
|
if(rc < 0){
|
||||||
|
printf("kvs_engine_array save error\n");
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_RBTREE
|
||||||
|
rc = kvs_rbtree_save(&global_rbtree, rbtree_file);
|
||||||
|
if(rc < 0){
|
||||||
|
printf("kvs_engine_rbtree save error\n");
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_HASH
|
||||||
|
rc = kvs_hash_save(&global_hash, hash_file);
|
||||||
|
if(rc < 0){
|
||||||
|
printf("kvs_engine_hash save error\n");
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __create_snapshot_ok(const char* array_file, const char* rbtree_file, const char* hash_file){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvs_create_snapshot_async(const char *ip, int port){
|
||||||
|
int pipefd[2]; // 用于子进程通知主进程
|
||||||
|
if (pipe(pipefd) == -1) { perror("pipe"); return -1; }
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == -1) { perror("fork"); return -1; }
|
||||||
|
|
||||||
|
if (pid == 0) { // 子进程
|
||||||
|
close(pipefd[0]); // 关闭读端
|
||||||
|
// 指定临时文件路径,避免覆盖 global_xxx_file
|
||||||
|
char tmp_array[128]; // 可写缓冲区
|
||||||
|
char tmp_rbtree[128];
|
||||||
|
char tmp_hash[128];
|
||||||
|
|
||||||
|
snprintf(tmp_array, sizeof(tmp_array), "snapshot_array_%s.tmp", ip);
|
||||||
|
snprintf(tmp_rbtree, sizeof(tmp_rbtree), "snapshot_rbtree_%s.tmp", ip);
|
||||||
|
snprintf(tmp_hash, sizeof(tmp_hash), "snapshot_hash_%s.tmp", ip);
|
||||||
|
|
||||||
|
int ret = kvs_create_snapshot(tmp_array, tmp_rbtree, tmp_hash);
|
||||||
|
if (ret == 0) {
|
||||||
|
// 成功:rename 到最终路径,或直接通知
|
||||||
|
write(pipefd[1], "OK", 2); // 通知主进程
|
||||||
|
} else {
|
||||||
|
write(pipefd[1], "ERR", 3);
|
||||||
|
}
|
||||||
|
close(pipefd[1]);
|
||||||
|
_exit(0); // 子进程退出
|
||||||
|
|
||||||
|
// hook point
|
||||||
|
__create_snapshot_ok(tmp_array, tmp_rbtree, tmp_hash);
|
||||||
|
} else { // 主进程
|
||||||
|
close(pipefd[1]); // 关闭写端
|
||||||
|
// 立即返回,继续处理其他请求(不阻塞)
|
||||||
|
// 可以记录 pid,在别处 waitpid(pid, NULL, WNOHANG) 检查完成
|
||||||
|
// 或用信号:signal(SIGCHLD, handler); 在 handler 中 read(pipefd[0]) 检查 "OK"
|
||||||
|
return 0; // SYNC 响应成功,主进程继续
|
||||||
|
}
|
||||||
|
}
|
||||||
14
kvs_oplog.h
14
kvs_oplog.h
@@ -1,14 +0,0 @@
|
|||||||
#ifndef __KVS_INC_LOG_H__
|
|
||||||
#define __KVS_INC_LOG_H__
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int init_cmd_log(const char *file, int *logfd);
|
|
||||||
int destroy_cmd_log(int logfd);
|
|
||||||
|
|
||||||
int kvs_oplog_append(const uint8_t *cmd, size_t len, int logfd);
|
|
||||||
int kvs_replay_log(int logfd);
|
|
||||||
int ksv_clear_log(int logfd);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "kvs_protocol_resp.h"
|
#include "kvs_protocol_resp.h"
|
||||||
#include "kvs_rw_tools.h"
|
#include "kvs_rw_tools.h"
|
||||||
|
#include "dump/kvs_dump.h"
|
||||||
|
|
||||||
#if ENABLE_ARRAY
|
#if ENABLE_ARRAY
|
||||||
extern kvs_array_t global_array;
|
extern kvs_array_t global_array;
|
||||||
@@ -535,12 +536,14 @@ int resp_dispatch(const resp_cmd_t *cmd, resp_value_t *out_value) {
|
|||||||
/* ---------------- misc ---------------- */
|
/* ---------------- misc ---------------- */
|
||||||
case KVS_CMD_SAVE: {
|
case KVS_CMD_SAVE: {
|
||||||
if (cmd->argc != 1) { *out_value = resp_error("ERR wrong number of arguments for 'save'"); return 0; }
|
if (cmd->argc != 1) { *out_value = resp_error("ERR wrong number of arguments for 'save'"); return 0; }
|
||||||
int r = kvs_save_to_file();
|
int r = kvs_create_snapshot(global_array_file, global_rbtree_file, global_hash_file);
|
||||||
|
if(r == 0) ksv_clear_log(global_oplog_fd);
|
||||||
if (r < 0) { *out_value = resp_error("ERR save failed"); return 0; }
|
if (r < 0) { *out_value = resp_error("ERR save failed"); return 0; }
|
||||||
*out_value = resp_simple("OK");
|
*out_value = resp_simple("OK");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case KVS_CMD_PSYNC:
|
case KVS_CMD_PSYNC:
|
||||||
|
kvs_create_snapshot_async(cmd->argv[1].ptr, atoi(cmd->argv[2].ptr));
|
||||||
*out_value = resp_simple("OK");
|
*out_value = resp_simple("OK");
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "kvs_rw_tools.h"
|
#include "kvs_rw_tools.h"
|
||||||
#include "memory/alloc_dispatch.h"
|
#include "memory/alloc_dispatch.h"
|
||||||
#include "kvs_oplog.h"
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -17,7 +16,7 @@ extern kvs_rbtree_t global_rbtree;
|
|||||||
extern kvs_hash_t global_hash;
|
extern kvs_hash_t global_hash;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int global_cmd_log_fd;
|
extern int global_oplog_fd;
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
int write_full(int fd, const void *buf, size_t len)
|
int write_full(int fd, const void *buf, size_t len)
|
||||||
|
|||||||
168
kvs_slave.c
168
kvs_slave.c
@@ -1,84 +1,110 @@
|
|||||||
#include <stdint.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "server.h"
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
/* 创建并监听用于接收快照的 TCP 监听 socket,成功返回 listen fd,失败返回 -1 */
|
||||||
|
static int create_listen_socket(const char *ip, int port){
|
||||||
|
|
||||||
static int kvs_write_u8(uint8_t **pp, uint8_t v) {
|
|
||||||
uint8_t *p = *pp;
|
|
||||||
*p = v;
|
|
||||||
*pp = p + 1;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvs_write_u32(uint8_t **pp, uint32_t v) {
|
/* 主动连接 master 指定地址,用于控制面通信(SSYNC / SREADY),返回连接 fd 或 -1 */
|
||||||
uint8_t *p = *pp;
|
static int connect_master(const char *master_ip, int master_port){
|
||||||
uint32_t be = htonl(v);
|
|
||||||
memcpy(p, &be, 4);
|
|
||||||
*pp = p + 4;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t kvs_get_log_tail_offset(void) {
|
/* 通过控制连接向 master 发送 SSYNC 请求,声明本 slave 的快照接收地址 */
|
||||||
int fd = open("kvs_cmd_log.db", O_RDONLY);
|
static int send_ssync(int ctrl_fd, const char *listen_ip, int listen_port){
|
||||||
if (fd < 0) {
|
|
||||||
// 文件不存在:从 0 开始同步
|
}
|
||||||
if (errno == ENOENT) return 0;
|
|
||||||
// 其他错误:保守起见从 0 开始
|
/* 接收并校验 master 对 SSYNC 的确认响应(如 +OK),成功返回 0 */
|
||||||
return 0;
|
static int recv_ssync_ok(int ctrl_fd){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 在快照监听 socket 上阻塞等待 master 的快照发送连接,返回已建立连接的 fd */
|
||||||
|
static int accept_snapshot_conn(int listen_fd){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 从快照连接中接收完整快照数据并构建内存状态,确保快照已完全应用 */
|
||||||
|
static int recv_and_apply_snapshot(int snapshot_fd){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 通过控制连接向 master 发送 SREADY 通知,表示快照已应用,slave 即将进入服务态 */
|
||||||
|
int send_sready(int ctrl_fd){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int slave_bootstrap(
|
||||||
|
const char *listen_ip,
|
||||||
|
int listen_port,
|
||||||
|
const char *master_ip,
|
||||||
|
int master_port
|
||||||
|
) {
|
||||||
|
int listen_fd = -1;
|
||||||
|
int ctrl_fd = -1;
|
||||||
|
int snap_fd = -1;
|
||||||
|
|
||||||
|
/* 1. 监听 snapshot port */
|
||||||
|
listen_fd = create_listen_socket(listen_ip, listen_port);
|
||||||
|
if (listen_fd < 0) {
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
off_t end = lseek(fd, 0, SEEK_END);
|
/* 2. 连接 master, 发送 SSYNC */
|
||||||
close(fd);
|
ctrl_fd = connect_master(master_ip, master_port);
|
||||||
|
if (ctrl_fd < 0) {
|
||||||
if (end < 0) return 0;
|
goto fail;
|
||||||
return (uint64_t)end; // 指向 EOF(下一次写入的位置)
|
|
||||||
}
|
|
||||||
|
|
||||||
int try_connect_master(char *ip, int port){
|
|
||||||
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
memset(&addr, 0, sizeof(struct sockaddr));
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(port);
|
|
||||||
addr.sin_addr.s_addr = inet_addr(ip);
|
|
||||||
|
|
||||||
int rt = 1;
|
|
||||||
while(1){
|
|
||||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if(fd < 0){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 == connect(fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))){
|
|
||||||
// [OP KVS_CMD_PSYNC=15][ARGC 1][ARGLEN 4][ARG offset]
|
|
||||||
|
|
||||||
char buf[100];
|
|
||||||
char *p = buf;
|
|
||||||
|
|
||||||
kvs_write_u8((uint8_t**)&p, 15);
|
|
||||||
kvs_write_u8((uint8_t**)&p, 1);
|
|
||||||
|
|
||||||
uint64_t len = sizeof(uint64_t);
|
|
||||||
kvs_write_u32((uint8_t**)&p, len);
|
|
||||||
|
|
||||||
uint64_t offset = kvs_get_log_tail_offset();
|
|
||||||
memcpy(p, (void*)&offset, len);
|
|
||||||
p += sizeof(offset);
|
|
||||||
|
|
||||||
send(fd, buf, p-buf, 0);
|
|
||||||
recv(fd, buf, 100, 0);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
if (send_ssync(ctrl_fd, listen_ip, listen_port) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recv_ssync_ok(ctrl_fd) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(ctrl_fd);
|
||||||
|
ctrl_fd = -1;
|
||||||
|
|
||||||
|
/* 3. accept snapshot 连接 */
|
||||||
|
snap_fd = accept_snapshot_conn(listen_fd);
|
||||||
|
if (snap_fd < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. 接收 snapshot */
|
||||||
|
if (recv_and_apply_snapshot(snap_fd) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(snap_fd);
|
||||||
|
snap_fd = -1;
|
||||||
|
|
||||||
|
|
||||||
|
close(listen_fd);
|
||||||
|
listen_fd = -1;
|
||||||
|
|
||||||
|
/* 5. 通知 master 快照传输完毕 */
|
||||||
|
ctrl_fd = connect_master(master_ip, master_port);
|
||||||
|
if (ctrl_fd >= 0) {
|
||||||
|
send_sready(ctrl_fd);
|
||||||
|
close(ctrl_fd);
|
||||||
|
ctrl_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 6. bootstrap complete */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (snap_fd >= 0) close(snap_fd);
|
||||||
|
if (ctrl_fd >= 0) close(ctrl_fd);
|
||||||
|
if (listen_fd >= 0) close(listen_fd);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
210
kvstore.c
210
kvstore.c
@@ -4,7 +4,7 @@
|
|||||||
#include "kvstore.h"
|
#include "kvstore.h"
|
||||||
#include "kvs_rw_tools.h"
|
#include "kvs_rw_tools.h"
|
||||||
#include "kvs_protocol_resp.h"
|
#include "kvs_protocol_resp.h"
|
||||||
#include "kvs_oplog.h"
|
#include "dump/kvs_dump.h"
|
||||||
#include "memory/alloc_dispatch.h"
|
#include "memory/alloc_dispatch.h"
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "diskuring/diskuring.h"
|
#include "diskuring/diskuring.h"
|
||||||
@@ -19,17 +19,7 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
|
|
||||||
#if ENABLE_ARRAY
|
extern int slave_bootstrap(const char *listen_ip, int listen_port, const char *master_ip, int master_port);
|
||||||
extern kvs_array_t global_array;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_RBTREE
|
|
||||||
extern kvs_rbtree_t global_rbtree;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_HASH
|
|
||||||
extern kvs_hash_t global_hash;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MEMORY_SELECT_MALLOC == MEMORY_USE_MYMALLOC
|
#if MEMORY_SELECT_MALLOC == MEMORY_USE_MYMALLOC
|
||||||
extern mp_pool_t global_mempool;
|
extern mp_pool_t global_mempool;
|
||||||
@@ -37,13 +27,10 @@ extern mp_pool_t global_mempool;
|
|||||||
|
|
||||||
AppConfig global_cfg;
|
AppConfig global_cfg;
|
||||||
|
|
||||||
extern int global_cmd_log_fd;
|
extern int global_oplog_fd;
|
||||||
extern iouring_ctx_t global_uring_ctx;
|
extern iouring_ctx_t global_uring_ctx;
|
||||||
|
|
||||||
char global_oplog_file[256] = "kvs_oplog.default.db";
|
|
||||||
char global_array_file[256] = "kvs_array.default.db";
|
|
||||||
char global_rbtree_file[256] = "kvs_rbtree.default.db";
|
|
||||||
char global_hash_file[256] = "kvs_hash.default.db";
|
|
||||||
|
|
||||||
int kvs_protocol(struct conn* conn){
|
int kvs_protocol(struct conn* conn){
|
||||||
if (!conn) return -1;
|
if (!conn) return -1;
|
||||||
@@ -118,24 +105,12 @@ int kvs_protocol(struct conn* conn){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_update) {
|
if (is_update) {
|
||||||
kvs_oplog_append(p, len, global_cmd_log_fd);
|
kvs_oplog_append(p, len, global_oplog_fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* PSYNC:触发同步线程(按你原来逻辑:从 argv[1] 取参数) */
|
|
||||||
if (cmd.argc > 0 && cmd.argv[0].ptr &&
|
|
||||||
ascii_casecmp(cmd.argv[0].ptr, cmd.argv[0].len, "PSYNC") == 0) {
|
|
||||||
if (cmd.argc >= 2 && cmd.argv[1].ptr) {
|
|
||||||
build_thread_to_sync((const char *)cmd.argv[1].ptr, conn);
|
|
||||||
} else {
|
|
||||||
/* 如果你希望 PSYNC 无参也能触发,可以传 NULL 或空串 */
|
|
||||||
build_thread_to_sync(NULL, conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 构建响应 */
|
/* 构建响应 */
|
||||||
int cap = KVS_MAX_RESPONSE - out_len;
|
int cap = KVS_MAX_RESPONSE - out_len;
|
||||||
if (cap <= 0) {
|
if (cap <= 0) {
|
||||||
@@ -153,177 +128,11 @@ int kvs_protocol(struct conn* conn){
|
|||||||
consumed += len;
|
consumed += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// slave 暂时不需要回报,或者回一个new_offset
|
|
||||||
if(conn->is_from_master){
|
|
||||||
conn->wlength = 0;
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
*response_length = out_len;
|
*response_length = out_len;
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvs_save_to_file(){
|
|
||||||
int ret = 0;
|
|
||||||
int rc = 0;
|
|
||||||
#if ENABLE_ARRAY
|
|
||||||
rc = kvs_array_save(&global_array, global_array_file);
|
|
||||||
if(rc < 0){
|
|
||||||
printf("kvs_engine_array save error\n");
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_RBTREE
|
|
||||||
rc = kvs_rbtree_save(&global_rbtree, global_rbtree_file);
|
|
||||||
if(rc < 0){
|
|
||||||
printf("kvs_engine_rbtree save error\n");
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_HASH
|
|
||||||
rc = kvs_hash_save(&global_hash, global_hash_file);
|
|
||||||
if(rc < 0){
|
|
||||||
printf("kvs_engine_hash save error\n");
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ksv_clear_log(global_cmd_log_fd);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern void sync_wakeup(int fd);
|
|
||||||
static int g_slavefd = -1;
|
|
||||||
static uint64_t g_offset = 0;
|
|
||||||
|
|
||||||
static void *sync_thread_main(void *arg) {
|
|
||||||
struct conn *conn = (struct conn*) arg;
|
|
||||||
|
|
||||||
int logfd = open(global_oplog_file, O_RDONLY);
|
|
||||||
if (logfd < 0) {
|
|
||||||
printf("open replaylog failed: %s\n", strerror(errno));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&conn->g_sync_lock);
|
|
||||||
uint64_t off = g_offset;
|
|
||||||
pthread_mutex_unlock(&conn->g_sync_lock);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
// 单槽位:等 reactor 发完再填
|
|
||||||
pthread_mutex_lock(&conn->g_sync_lock);
|
|
||||||
int busy = (conn->wlength > 0);
|
|
||||||
pthread_mutex_unlock(&conn->g_sync_lock);
|
|
||||||
if (busy) { usleep(10 * 1000); continue; }
|
|
||||||
|
|
||||||
size_t filled = 0;
|
|
||||||
int records = 0;
|
|
||||||
|
|
||||||
// 试图攒一批
|
|
||||||
while (filled < (size_t)KVS_MAX_RESPONSE && records < 128) {
|
|
||||||
|
|
||||||
// 读 len 头
|
|
||||||
uint32_t nlen = 0;
|
|
||||||
ssize_t r = pread(logfd, &nlen, sizeof(nlen), (off_t)off);
|
|
||||||
|
|
||||||
if (r == 0) {
|
|
||||||
// EOF:文件当前没更多数据
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (r < 0) {
|
|
||||||
if (errno == EINTR) continue;
|
|
||||||
printf("pread len error: %s\n", strerror(errno));
|
|
||||||
close(logfd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (r < (ssize_t)sizeof(nlen)) {
|
|
||||||
// 半截 len:writer 还没写完头
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t len = ntohl(nlen);
|
|
||||||
if(len <= 0) {
|
|
||||||
printf("sync error\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 这一条放不进本批次,就先发已有的
|
|
||||||
if (filled + len > (size_t)KVS_MAX_RESPONSE) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读 payload(cmd)
|
|
||||||
ssize_t pr = pread(logfd, conn->wbuffer + filled, len,
|
|
||||||
(off_t)(off + sizeof(nlen)));
|
|
||||||
|
|
||||||
if (pr == 0) {
|
|
||||||
// payload 还没写到
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (pr < 0) {
|
|
||||||
if (errno == EINTR) continue;
|
|
||||||
printf("pread payload error: %s\n", strerror(errno));
|
|
||||||
close(logfd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (pr < (ssize_t)len) {
|
|
||||||
// 半截 payload:writer 还没写完这一条
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 成功拿到一条完整记录:推进
|
|
||||||
off += sizeof(nlen) + (uint64_t)len;
|
|
||||||
filled += (size_t)len;
|
|
||||||
records++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filled > 0) {
|
|
||||||
// 提交给 reactor 发送
|
|
||||||
pthread_mutex_lock(&conn->g_sync_lock);
|
|
||||||
conn->wlength = (int)filled;
|
|
||||||
g_offset = off;
|
|
||||||
pthread_mutex_unlock(&conn->g_sync_lock);
|
|
||||||
|
|
||||||
// 唤醒 reactor 发
|
|
||||||
sync_wakeup(conn->fd); // 或 g_slavefd
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 没攒到任何完整记录:说明真到末尾/半条记录,等一会儿
|
|
||||||
usleep(10*1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(logfd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void build_thread_to_sync(const uint8_t *offset, struct conn* conn){
|
|
||||||
uint64_t off64 = 0;
|
|
||||||
memcpy(&off64, offset, 8);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&conn->g_sync_lock);
|
|
||||||
g_slavefd = conn->fd;
|
|
||||||
g_offset = (uint64_t)off64;
|
|
||||||
printf("offset:%ld\n", off64);
|
|
||||||
|
|
||||||
conn->wlength = 0;
|
|
||||||
pthread_mutex_unlock(&conn->g_sync_lock);
|
|
||||||
|
|
||||||
pthread_t tid;
|
|
||||||
|
|
||||||
int rc = pthread_create(&tid, NULL, sync_thread_main, conn);
|
|
||||||
if (rc != 0) {
|
|
||||||
printf("pthread_create failed: %s\n", strerror(rc));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_detach(tid);
|
|
||||||
}
|
|
||||||
|
|
||||||
int init_kvengine(void) {
|
int init_kvengine(void) {
|
||||||
|
|
||||||
@@ -349,8 +158,8 @@ int init_kvengine(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(global_cfg.persistence == PERSIST_INCREMENTAL){
|
if(global_cfg.persistence == PERSIST_INCREMENTAL){
|
||||||
init_cmd_log(global_oplog_file, &global_cmd_log_fd);
|
init_cmd_log(global_oplog_file, &global_oplog_fd);
|
||||||
kvs_replay_log(global_cmd_log_fd);
|
kvs_replay_log(global_oplog_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("kvengine init complete\n");
|
printf("kvengine init complete\n");
|
||||||
@@ -368,7 +177,7 @@ void dest_kvengine(void) {
|
|||||||
kvs_hash_destroy(&global_hash);
|
kvs_hash_destroy(&global_hash);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
destroy_cmd_log(global_cmd_log_fd);
|
destroy_cmd_log(global_oplog_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_memory_pool(AppConfig *cfg){
|
void init_memory_pool(AppConfig *cfg){
|
||||||
@@ -477,6 +286,7 @@ int main(int argc, char *argv[]) {
|
|||||||
if(global_cfg.mode == MODE_SLAVE){
|
if(global_cfg.mode == MODE_SLAVE){
|
||||||
master_ip = global_cfg.master_ip;
|
master_ip = global_cfg.master_ip;
|
||||||
master_port = global_cfg.master_port;
|
master_port = global_cfg.master_port;
|
||||||
|
slave_bootstrap(global_cfg.ip, port, master_ip, master_port);
|
||||||
}else if(global_cfg.mode == MODE_MASTER){
|
}else if(global_cfg.mode == MODE_MASTER){
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -487,7 +297,7 @@ int main(int argc, char *argv[]) {
|
|||||||
init_kvengine();
|
init_kvengine();
|
||||||
|
|
||||||
#if (NETWORK_SELECT == NETWORK_REACTOR)
|
#if (NETWORK_SELECT == NETWORK_REACTOR)
|
||||||
reactor_start(port, kvs_protocol, master_ip, master_port); //
|
reactor_start(port, kvs_protocol); //
|
||||||
#elif (NETWORK_SELECT == NETWORK_PROACTOR)
|
#elif (NETWORK_SELECT == NETWORK_PROACTOR)
|
||||||
proactor_start(port, kvs_protocol);
|
proactor_start(port, kvs_protocol);
|
||||||
#elif (NETWORK_SELECT == NETWORK_NTYCO)
|
#elif (NETWORK_SELECT == NETWORK_NTYCO)
|
||||||
|
|||||||
18
kvstore.h
18
kvstore.h
@@ -42,12 +42,11 @@
|
|||||||
// typedef int (*msg_handler)(char *request, int request_length, char *response, int *response_length);
|
// typedef int (*msg_handler)(char *request, int request_length, char *response, int *response_length);
|
||||||
typedef int (*msg_handler)(struct conn* conn);
|
typedef int (*msg_handler)(struct conn* conn);
|
||||||
|
|
||||||
extern int reactor_start(unsigned short port, msg_handler handler, const char *m_ip, int m_port);
|
extern int reactor_start(unsigned short port, msg_handler handler);
|
||||||
extern int proactor_start(unsigned short port, msg_handler handler);
|
extern int proactor_start(unsigned short port, msg_handler handler);
|
||||||
extern int ntyco_start(unsigned short port, msg_handler handler);
|
extern int ntyco_start(unsigned short port, msg_handler handler);
|
||||||
|
|
||||||
extern int try_connect_master(char *ip, int port);
|
|
||||||
void build_thread_to_sync(const uint8_t *offset, struct conn* conn);
|
|
||||||
|
|
||||||
#if ENABLE_ARRAY
|
#if ENABLE_ARRAY
|
||||||
|
|
||||||
@@ -268,8 +267,19 @@ int kvs_hash_exist(kvs_hash_t *hash, char *key);
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_ARRAY
|
||||||
|
extern kvs_array_t global_array;
|
||||||
|
#endif
|
||||||
|
|
||||||
int kvs_save_to_file();
|
#if ENABLE_RBTREE
|
||||||
|
extern kvs_rbtree_t global_rbtree;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_HASH
|
||||||
|
extern kvs_hash_t global_hash;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void __compeleted_cmd(const uint8_t *cmd, size_t len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
11
reactor.c
11
reactor.c
@@ -31,8 +31,6 @@
|
|||||||
// typedef int (*msg_handler)(char *request, int request_length, char *response, int *response_length);
|
// typedef int (*msg_handler)(char *request, int request_length, char *response, int *response_length);
|
||||||
typedef int (*msg_handler)(struct conn* conn);
|
typedef int (*msg_handler)(struct conn* conn);
|
||||||
|
|
||||||
extern int try_connect_master(char *ip, int port);
|
|
||||||
|
|
||||||
static msg_handler kvs_handler;
|
static msg_handler kvs_handler;
|
||||||
|
|
||||||
// 0 need more, -1 error, =1 suc
|
// 0 need more, -1 error, =1 suc
|
||||||
@@ -354,7 +352,7 @@ int r_init_server(unsigned short port) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int reactor_start(unsigned short port, msg_handler handler, char *m_ip, int m_port) {
|
int reactor_start(unsigned short port, msg_handler handler) {
|
||||||
|
|
||||||
//unsigned short port = 2000;
|
//unsigned short port = 2000;
|
||||||
kvs_handler = handler;
|
kvs_handler = handler;
|
||||||
@@ -366,13 +364,6 @@ int reactor_start(unsigned short port, msg_handler handler, char *m_ip, int m_po
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// slave
|
|
||||||
if(m_ip != NULL){
|
|
||||||
int masterfd = try_connect_master(m_ip, m_port);
|
|
||||||
event_register(masterfd, EPOLLIN);
|
|
||||||
conn_list[masterfd].is_from_master = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for (i = 0;i < MAX_PORTS;i ++) {
|
for (i = 0;i < MAX_PORTS;i ++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user