From 7c8a35aea9b6108bffdc39390cdb00db525be2ac Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Sun, 16 Feb 2020 21:07:54 +0800 Subject: [PATCH 01/21] use libco instead of state-thread(st), still have some bug --- trunk/3rdparty/libco/.gitignore | 1 + trunk/3rdparty/libco/CMakeLists.txt | 55 + trunk/3rdparty/libco/LICENSE.txt | Bin 0 -> 18962 bytes trunk/3rdparty/libco/Makefile | 84 + trunk/3rdparty/libco/co.mk | 85 + trunk/3rdparty/libco/co_closure.h | 96 + trunk/3rdparty/libco/co_epoll.cpp | 318 ++ trunk/3rdparty/libco/co_epoll.h | 93 + trunk/3rdparty/libco/co_hook_sys_call.cpp | 1003 ++++++ trunk/3rdparty/libco/co_routine.cpp | 1196 +++++++ trunk/3rdparty/libco/co_routine.h | 93 + trunk/3rdparty/libco/co_routine_inner.h | 111 + trunk/3rdparty/libco/co_routine_specific.h | 86 + trunk/3rdparty/libco/coctx.cpp | 132 + trunk/3rdparty/libco/coctx.h | 42 + trunk/3rdparty/libco/coctx_swap.S | 83 + trunk/3rdparty/libco/example_closure.cpp | 91 + trunk/3rdparty/libco/example_cond.cpp | 83 + trunk/3rdparty/libco/example_copystack.cpp | 61 + trunk/3rdparty/libco/example_echocli.cpp | 218 ++ trunk/3rdparty/libco/example_echosvr.cpp | 255 ++ trunk/3rdparty/libco/example_poll.cpp | 211 ++ trunk/3rdparty/libco/example_setenv.cpp | 89 + trunk/3rdparty/libco/example_specific.cpp | 61 + trunk/3rdparty/libco/example_thread.cpp | 56 + trunk/3rdparty/st-srs/.gitignore | 4 - trunk/3rdparty/st-srs/Makefile | 474 --- trunk/3rdparty/st-srs/README | 394 --- trunk/3rdparty/st-srs/README.md | 88 - trunk/3rdparty/st-srs/common.h | 479 --- trunk/3rdparty/st-srs/docs/fig.gif | Bin 5374 -> 0 bytes trunk/3rdparty/st-srs/docs/notes.html | 434 --- trunk/3rdparty/st-srs/docs/reference.html | 3120 ----------------- trunk/3rdparty/st-srs/docs/st.html | 504 --- trunk/3rdparty/st-srs/docs/timeout_heap.txt | 60 - trunk/3rdparty/st-srs/event.c | 1413 -------- trunk/3rdparty/st-srs/examples/Makefile | 115 - trunk/3rdparty/st-srs/examples/README | 98 - trunk/3rdparty/st-srs/examples/error.c | 168 - trunk/3rdparty/st-srs/examples/lookupdns.c | 103 - trunk/3rdparty/st-srs/examples/proxy.c | 541 --- trunk/3rdparty/st-srs/examples/res.c | 305 -- trunk/3rdparty/st-srs/examples/server.c | 1025 ------ trunk/3rdparty/st-srs/extensions/Makefile | 91 - trunk/3rdparty/st-srs/extensions/README | 42 - trunk/3rdparty/st-srs/extensions/common.h | 77 - trunk/3rdparty/st-srs/extensions/dnscache.c | 190 - trunk/3rdparty/st-srs/extensions/dnsres.c | 305 -- trunk/3rdparty/st-srs/extensions/lrucache.c | 343 -- .../st-srs/extensions/print_stk.patch | 367 -- trunk/3rdparty/st-srs/extensions/stx.h | 91 - trunk/3rdparty/st-srs/extensions/stx_fileio.c | 197 -- trunk/3rdparty/st-srs/extensions/stx_fileio.h | 52 - trunk/3rdparty/st-srs/extensions/testdns.c | 112 - trunk/3rdparty/st-srs/io.c | 769 ---- trunk/3rdparty/st-srs/key.c | 121 - trunk/3rdparty/st-srs/libst.def | 51 - trunk/3rdparty/st-srs/md.S | 644 ---- trunk/3rdparty/st-srs/md.h | 641 ---- trunk/3rdparty/st-srs/osguess.sh | 45 - trunk/3rdparty/st-srs/public.h | 166 - trunk/3rdparty/st-srs/sched.c | 705 ---- trunk/3rdparty/st-srs/st.pc.in | 10 - trunk/3rdparty/st-srs/st.spec | 79 - trunk/3rdparty/st-srs/stk.c | 173 - trunk/3rdparty/st-srs/sync.c | 368 -- trunk/auto/depends.sh | 31 +- trunk/configure | 34 +- trunk/research/st/Makefile | 100 - trunk/research/st/common.h | 445 --- trunk/research/st/event.c | 483 --- trunk/research/st/io.c | 792 ----- trunk/research/st/key.c | 116 - trunk/research/st/md.S | 151 - trunk/research/st/md.h | 193 - trunk/research/st/public.h | 164 - trunk/research/st/sched.c | 680 ---- trunk/research/st/srs.c | 497 --- trunk/research/st/st/init | 3 - trunk/research/st/st/st.upp | 18 - trunk/research/st/stk.c | 169 - trunk/research/st/sync.c | 352 -- trunk/src/app/srs_app_listener.cpp | 10 +- trunk/src/app/srs_app_server.cpp | 12 +- trunk/src/app/srs_app_st.cpp | 27 +- trunk/src/app/srs_app_st.hpp | 3 + trunk/src/service/srs_service_st.cpp | 260 +- trunk/src/service/srs_service_st.hpp | 2 +- 88 files changed, 4836 insertions(+), 19273 deletions(-) create mode 100644 trunk/3rdparty/libco/.gitignore create mode 100644 trunk/3rdparty/libco/CMakeLists.txt create mode 100644 trunk/3rdparty/libco/LICENSE.txt create mode 100644 trunk/3rdparty/libco/Makefile create mode 100644 trunk/3rdparty/libco/co.mk create mode 100644 trunk/3rdparty/libco/co_closure.h create mode 100644 trunk/3rdparty/libco/co_epoll.cpp create mode 100644 trunk/3rdparty/libco/co_epoll.h create mode 100644 trunk/3rdparty/libco/co_hook_sys_call.cpp create mode 100644 trunk/3rdparty/libco/co_routine.cpp create mode 100644 trunk/3rdparty/libco/co_routine.h create mode 100644 trunk/3rdparty/libco/co_routine_inner.h create mode 100644 trunk/3rdparty/libco/co_routine_specific.h create mode 100644 trunk/3rdparty/libco/coctx.cpp create mode 100644 trunk/3rdparty/libco/coctx.h create mode 100644 trunk/3rdparty/libco/coctx_swap.S create mode 100644 trunk/3rdparty/libco/example_closure.cpp create mode 100644 trunk/3rdparty/libco/example_cond.cpp create mode 100644 trunk/3rdparty/libco/example_copystack.cpp create mode 100644 trunk/3rdparty/libco/example_echocli.cpp create mode 100644 trunk/3rdparty/libco/example_echosvr.cpp create mode 100644 trunk/3rdparty/libco/example_poll.cpp create mode 100644 trunk/3rdparty/libco/example_setenv.cpp create mode 100644 trunk/3rdparty/libco/example_specific.cpp create mode 100644 trunk/3rdparty/libco/example_thread.cpp delete mode 100644 trunk/3rdparty/st-srs/.gitignore delete mode 100644 trunk/3rdparty/st-srs/Makefile delete mode 100644 trunk/3rdparty/st-srs/README delete mode 100644 trunk/3rdparty/st-srs/README.md delete mode 100644 trunk/3rdparty/st-srs/common.h delete mode 100644 trunk/3rdparty/st-srs/docs/fig.gif delete mode 100644 trunk/3rdparty/st-srs/docs/notes.html delete mode 100644 trunk/3rdparty/st-srs/docs/reference.html delete mode 100644 trunk/3rdparty/st-srs/docs/st.html delete mode 100644 trunk/3rdparty/st-srs/docs/timeout_heap.txt delete mode 100644 trunk/3rdparty/st-srs/event.c delete mode 100644 trunk/3rdparty/st-srs/examples/Makefile delete mode 100644 trunk/3rdparty/st-srs/examples/README delete mode 100644 trunk/3rdparty/st-srs/examples/error.c delete mode 100644 trunk/3rdparty/st-srs/examples/lookupdns.c delete mode 100644 trunk/3rdparty/st-srs/examples/proxy.c delete mode 100644 trunk/3rdparty/st-srs/examples/res.c delete mode 100644 trunk/3rdparty/st-srs/examples/server.c delete mode 100644 trunk/3rdparty/st-srs/extensions/Makefile delete mode 100644 trunk/3rdparty/st-srs/extensions/README delete mode 100644 trunk/3rdparty/st-srs/extensions/common.h delete mode 100644 trunk/3rdparty/st-srs/extensions/dnscache.c delete mode 100644 trunk/3rdparty/st-srs/extensions/dnsres.c delete mode 100644 trunk/3rdparty/st-srs/extensions/lrucache.c delete mode 100644 trunk/3rdparty/st-srs/extensions/print_stk.patch delete mode 100644 trunk/3rdparty/st-srs/extensions/stx.h delete mode 100644 trunk/3rdparty/st-srs/extensions/stx_fileio.c delete mode 100644 trunk/3rdparty/st-srs/extensions/stx_fileio.h delete mode 100644 trunk/3rdparty/st-srs/extensions/testdns.c delete mode 100644 trunk/3rdparty/st-srs/io.c delete mode 100644 trunk/3rdparty/st-srs/key.c delete mode 100644 trunk/3rdparty/st-srs/libst.def delete mode 100644 trunk/3rdparty/st-srs/md.S delete mode 100644 trunk/3rdparty/st-srs/md.h delete mode 100644 trunk/3rdparty/st-srs/osguess.sh delete mode 100644 trunk/3rdparty/st-srs/public.h delete mode 100644 trunk/3rdparty/st-srs/sched.c delete mode 100644 trunk/3rdparty/st-srs/st.pc.in delete mode 100644 trunk/3rdparty/st-srs/st.spec delete mode 100644 trunk/3rdparty/st-srs/stk.c delete mode 100644 trunk/3rdparty/st-srs/sync.c delete mode 100755 trunk/research/st/Makefile delete mode 100644 trunk/research/st/common.h delete mode 100644 trunk/research/st/event.c delete mode 100644 trunk/research/st/io.c delete mode 100644 trunk/research/st/key.c delete mode 100755 trunk/research/st/md.S delete mode 100644 trunk/research/st/md.h delete mode 100644 trunk/research/st/public.h delete mode 100755 trunk/research/st/sched.c delete mode 100644 trunk/research/st/srs.c delete mode 100644 trunk/research/st/st/init delete mode 100755 trunk/research/st/st/st.upp delete mode 100644 trunk/research/st/stk.c delete mode 100644 trunk/research/st/sync.c diff --git a/trunk/3rdparty/libco/.gitignore b/trunk/3rdparty/libco/.gitignore new file mode 100644 index 000000000..42061c01a --- /dev/null +++ b/trunk/3rdparty/libco/.gitignore @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/trunk/3rdparty/libco/CMakeLists.txt b/trunk/3rdparty/libco/CMakeLists.txt new file mode 100644 index 000000000..04399a55a --- /dev/null +++ b/trunk/3rdparty/libco/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 2.8) +project(libco) + +# This for mac osx only +set(CMAKE_MACOSX_RPATH 0) + +# Set lib version +set(LIBCO_VERSION 0.5) + +# Set cflags +set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -g -fno-strict-aliasing -O2 -Wall -export-dynamic -Wall -pipe -D_GNU_SOURCE -D_REENTRANT -fPIC -Wno-deprecated -m64) + +# Use c and asm +enable_language(C ASM) + +# Add source files +set(SOURCE_FILES + co_epoll.cpp + co_hook_sys_call.cpp + co_routine.cpp + coctx.cpp + coctx_swap.S) + +# Add static and shared library target +add_library(colib_static STATIC ${SOURCE_FILES}) +add_library(colib_shared SHARED ${SOURCE_FILES}) + +# Set library output name +set_target_properties(colib_static PROPERTIES OUTPUT_NAME colib) +set_target_properties(colib_shared PROPERTIES OUTPUT_NAME colib) + +set_target_properties(colib_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) +set_target_properties(colib_shared PROPERTIES CLEAN_DIRECT_OUTPUT 1) + +# Set shared library version, will generate libcolib.${LIBCO_VERSION}.so and a symbol link named libcolib.so +# For mac osx, the extension name will be .dylib +set_target_properties(colib_shared PROPERTIES VERSION ${LIBCO_VERSION} SOVERSION ${LIBCO_VERSION}) + + + +# Macro for add example target +macro(add_example_target EXAMPLE_TARGET) + add_executable("example_${EXAMPLE_TARGET}" "example_${EXAMPLE_TARGET}.cpp") + target_link_libraries("example_${EXAMPLE_TARGET}" colib_static pthread dl) +endmacro(add_example_target) + +add_example_target(closure) +add_example_target(cond) +add_example_target(copystack) +add_example_target(echocli) +add_example_target(echosvr) +add_example_target(poll) +add_example_target(setenv) +add_example_target(specific) +add_example_target(thread) diff --git a/trunk/3rdparty/libco/LICENSE.txt b/trunk/3rdparty/libco/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..bff49d4bc3a55637e1ed4bd7eeb9834a2b16dbd4 GIT binary patch literal 18962 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 literal 0 HcmV?d00001 diff --git a/trunk/3rdparty/libco/Makefile b/trunk/3rdparty/libco/Makefile new file mode 100644 index 000000000..7db374946 --- /dev/null +++ b/trunk/3rdparty/libco/Makefile @@ -0,0 +1,84 @@ +# +# Tencent is pleased to support the open source community by making Libco available. +# +# Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +COMM_MAKE = 1 +COMM_ECHO = 1 +version=0.5 +v=debug +include co.mk + +########## options ########## +CFLAGS += -g -fno-strict-aliasing -O2 -Wall -export-dynamic \ + -Wall -pipe -D_GNU_SOURCE -D_REENTRANT -fPIC -Wno-deprecated -m64 + +UNAME := $(shell uname -s) + +ifeq ($(UNAME), FreeBSD) +LINKS += -g -L./lib -lcolib -lpthread +else +LINKS += -g -L./lib -lcolib -lpthread -ldl +endif + +COLIB_OBJS=co_epoll.o co_routine.o co_hook_sys_call.o coctx_swap.o coctx.o +#co_swapcontext.o + +PROGS = colib example_poll example_echosvr example_echocli example_thread example_cond example_specific example_copystack example_closure + +all:$(PROGS) + +colib:libcolib.a libcolib.so + +libcolib.a: $(COLIB_OBJS) + $(ARSTATICLIB) +libcolib.so: $(COLIB_OBJS) + $(BUILDSHARELIB) + +example_echosvr:example_echosvr.o + $(BUILDEXE) +example_echocli:example_echocli.o + $(BUILDEXE) +example_thread:example_thread.o + $(BUILDEXE) +example_poll:example_poll.o + $(BUILDEXE) +example_exit:example_exit.o + $(BUILDEXE) +example_cond:example_cond.o + $(BUILDEXE) +example_specific:example_specific.o + $(BUILDEXE) +example_copystack:example_copystack.o + $(BUILDEXE) +example_setenv:example_setenv.o + $(BUILDEXE) +example_closure:example_closure.o + $(BUILDEXE) + +dist: clean libco-$(version).src.tar.gz + +libco-$(version).src.tar.gz: + @find . -type f | grep -v CVS | grep -v .svn | sed s:^./:libco-$(version)/: > MANIFEST + @(cd ..; ln -s libco_pub libco-$(version)) + (cd ..; tar cvf - `cat libco_pub/MANIFEST` | gzip > libco_pub/libco-$(version).src.tar.gz) + @(cd ..; rm libco-$(version)) + +clean: + $(CLEAN) *.o $(PROGS) + rm -fr MANIFEST lib solib libco-$(version).src.tar.gz libco-$(version) + diff --git a/trunk/3rdparty/libco/co.mk b/trunk/3rdparty/libco/co.mk new file mode 100644 index 000000000..29658b511 --- /dev/null +++ b/trunk/3rdparty/libco/co.mk @@ -0,0 +1,85 @@ +# +# Tencent is pleased to support the open source community by making Libco available. +# +# Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + + +##### Makefile Rules ########## +MAIL_ROOT=. +SRCROOT=. + +##define the compliers +CPP = $(CXX) +AR = ar -rc +RANLIB = ranlib + +CPPSHARE = $(CPP) -fPIC -shared -O2 -pipe -L$(SRCROOT)/solib/ -o +CSHARE = $(CC) -fPIC -shared -O2 -pipe -L$(SRCROOT)/solib/ -o + +ifeq ($v,release) +CFLAGS= -O2 $(INCLS) -fPIC -DLINUX -pipe -Wno-deprecated -c +else +CFLAGS= -g $(INCLS) -fPIC -DLINUX -pipe -c -fno-inline +endif + +ifneq ($v,release) +BFLAGS= -g +endif + +STATICLIBPATH=$(SRCROOT)/lib +DYNAMICLIBPATH=$(SRCROOT)/solib + +INCLS += -I$(SRCROOT) + +## default links +ifeq ($(LINKS_DYNAMIC), 1) +LINKS += -L$(DYNAMICLIBPATH) -L$(STATICLIBPATH) +else +LINKS += -L$(STATICLIBPATH) +endif + +CPPSRCS = $(wildcard *.cpp) +CSRCS = $(wildcard *.c) +CPPOBJS = $(patsubst %.cpp,%.o,$(CPPSRCS)) +COBJS = $(patsubst %.c,%.o,$(CSRCS)) + +SRCS = $(CPPSRCS) $(CSRCS) +OBJS = $(CPPOBJS) $(COBJS) + +CPPCOMPI=$(CPP) $(CFLAGS) -Wno-deprecated +CCCOMPI=$(CC) $(CFLAGS) + +BUILDEXE = $(CPP) $(BFLAGS) -o $@ $^ $(LINKS) +CLEAN = rm -f *.o + +CPPCOMPILE = $(CPPCOMPI) $< $(FLAGS) $(INCLS) $(MTOOL_INCL) -o $@ +CCCOMPILE = $(CCCOMPI) $< $(FLAGS) $(INCLS) $(MTOOL_INCL) -o $@ + +ARSTATICLIB = $(AR) $@.tmp $^ $(AR_FLAGS); \ + if [ $$? -ne 0 ]; then exit 1; fi; \ + test -d $(STATICLIBPATH) || mkdir -p $(STATICLIBPATH); \ + mv -f $@.tmp $(STATICLIBPATH)/$@; + +BUILDSHARELIB = $(CPPSHARE) $@.tmp $^ $(BS_FLAGS); \ + if [ $$? -ne 0 ]; then exit 1; fi; \ + test -d $(DYNAMICLIBPATH) || mkdir -p $(DYNAMICLIBPATH); \ + mv -f $@.tmp $(DYNAMICLIBPATH)/$@; + +.cpp.o: + $(CPPCOMPILE) +.c.o: + $(CCCOMPILE) diff --git a/trunk/3rdparty/libco/co_closure.h b/trunk/3rdparty/libco/co_closure.h new file mode 100644 index 000000000..87d5c6891 --- /dev/null +++ b/trunk/3rdparty/libco/co_closure.h @@ -0,0 +1,96 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __CO_CLOSURE_H__ +#define __CO_CLOSURE_H__ +struct stCoClosure_t +{ +public: + virtual void exec() = 0; +}; + +//1.base +//-- 1.1 comac_argc + +#define comac_get_args_cnt( ... ) comac_arg_n( __VA_ARGS__ ) +#define comac_arg_n( _0,_1,_2,_3,_4,_5,_6,_7,N,...) N +#define comac_args_seqs() 7,6,5,4,3,2,1,0 +#define comac_join_1( x,y ) x##y + +#define comac_argc( ... ) comac_get_args_cnt( 0,##__VA_ARGS__,comac_args_seqs() ) +#define comac_join( x,y) comac_join_1( x,y ) + +//-- 1.2 repeat +#define repeat_0( fun,a,... ) +#define repeat_1( fun,a,... ) fun( 1,a,__VA_ARGS__ ) repeat_0( fun,__VA_ARGS__ ) +#define repeat_2( fun,a,... ) fun( 2,a,__VA_ARGS__ ) repeat_1( fun,__VA_ARGS__ ) +#define repeat_3( fun,a,... ) fun( 3,a,__VA_ARGS__ ) repeat_2( fun,__VA_ARGS__ ) +#define repeat_4( fun,a,... ) fun( 4,a,__VA_ARGS__ ) repeat_3( fun,__VA_ARGS__ ) +#define repeat_5( fun,a,... ) fun( 5,a,__VA_ARGS__ ) repeat_4( fun,__VA_ARGS__ ) +#define repeat_6( fun,a,... ) fun( 6,a,__VA_ARGS__ ) repeat_5( fun,__VA_ARGS__ ) + +#define repeat( n,fun,... ) comac_join( repeat_,n )( fun,__VA_ARGS__) + +//2.implement +#if __cplusplus <= 199711L +#define decl_typeof( i,a,... ) typedef typeof( a ) typeof_##a; +#else +#define decl_typeof( i,a,... ) typedef decltype( a ) typeof_##a; +#endif +#define impl_typeof( i,a,... ) typeof_##a & a; +#define impl_typeof_cpy( i,a,... ) typeof_##a a; +#define con_param_typeof( i,a,... ) typeof_##a & a##r, +#define param_init_typeof( i,a,... ) a(a##r), + + +//2.1 reference + +#define co_ref( name,... )\ +repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )\ +class type_##name\ +{\ +public:\ + repeat( comac_argc(__VA_ARGS__) ,impl_typeof,__VA_ARGS__ )\ + int _member_cnt;\ + type_##name( \ + repeat( comac_argc(__VA_ARGS__),con_param_typeof,__VA_ARGS__ ) ... ): \ + repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__)) \ + {}\ +} name( __VA_ARGS__ ) ; + + +//2.2 function + +#define co_func(name,...)\ +repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )\ +class name:public stCoClosure_t\ +{\ +public:\ + repeat( comac_argc(__VA_ARGS__) ,impl_typeof_cpy,__VA_ARGS__ )\ + int _member_cnt;\ +public:\ + name( repeat( comac_argc(__VA_ARGS__),con_param_typeof,__VA_ARGS__ ) ... ): \ + repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__))\ + {}\ + void exec() + +#define co_func_end } + + +#endif + diff --git a/trunk/3rdparty/libco/co_epoll.cpp b/trunk/3rdparty/libco/co_epoll.cpp new file mode 100644 index 000000000..12726218d --- /dev/null +++ b/trunk/3rdparty/libco/co_epoll.cpp @@ -0,0 +1,318 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "co_epoll.h" +#include +#include +#include +#include + +#if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) + +int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ) +{ + return epoll_wait( epfd,events->events,maxevents,timeout ); +} +int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ev ) +{ + return epoll_ctl( epfd,op,fd,ev ); +} +int co_epoll_create( int size ) +{ + return epoll_create( size ); +} + +struct co_epoll_res *co_epoll_res_alloc( int n ) +{ + struct co_epoll_res * ptr = + (struct co_epoll_res *)malloc( sizeof( struct co_epoll_res ) ); + + ptr->size = n; + ptr->events = (struct epoll_event*)calloc( 1,n * sizeof( struct epoll_event ) ); + + return ptr; + +} +void co_epoll_res_free( struct co_epoll_res * ptr ) +{ + if( !ptr ) return; + if( ptr->events ) free( ptr->events ); + free( ptr ); +} + +#else +class clsFdMap // million of fd , 1024 * 1024 +{ +private: + static const int row_size = 1024; + static const int col_size = 1024; + + void **m_pp[ 1024 ]; +public: + clsFdMap() + { + memset( m_pp,0,sizeof(m_pp) ); + } + ~clsFdMap() + { + for(int i=0;i= sizeof(m_pp)/sizeof(m_pp[0]) ) + { + assert( __LINE__ == 0 ); + return -__LINE__; + } + if( !m_pp[ idx ] ) + { + m_pp[ idx ] = (void**)calloc( 1,sizeof(void*) * col_size ); + } + m_pp[ idx ][ fd % col_size ] = (void*)ptr; + return 0; + } + inline void *get( int fd ) + { + int idx = fd / row_size; + if( idx < 0 || idx >= sizeof(m_pp)/sizeof(m_pp[0]) ) + { + return NULL; + } + void **lp = m_pp[ idx ]; + if( !lp ) return NULL; + + return lp[ fd % col_size ]; + } +}; + +__thread clsFdMap *s_fd_map = NULL; + +static inline clsFdMap *get_fd_map() +{ + if( !s_fd_map ) + { + s_fd_map = new clsFdMap(); + } + return s_fd_map; +} + +struct kevent_pair_t +{ + int fire_idx; + int events; + uint64_t u64; +}; +int co_epoll_create( int size ) +{ + return kqueue(); +} +int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ) +{ + struct timespec t = { 0 }; + if( timeout > 0 ) + { + t.tv_sec = timeout; + } + int ret = kevent( epfd, + NULL, 0, //register null + events->eventlist, maxevents,//just retrival + ( -1 == timeout ) ? NULL : &t ); + int j = 0; + for(int i=0;ieventlist[i]; + struct kevent_pair_t *ptr = (struct kevent_pair_t*)kev.udata; + struct epoll_event *ev = events->events + i; + if( 0 == ptr->fire_idx ) + { + ptr->fire_idx = i + 1; + memset( ev,0,sizeof(*ev) ); + ++j; + } + else + { + ev = events->events + ptr->fire_idx - 1; + } + if( EVFILT_READ == kev.filter ) + { + ev->events |= EPOLLIN; + } + else if( EVFILT_WRITE == kev.filter ) + { + ev->events |= EPOLLOUT; + } + ev->data.u64 = ptr->u64; + } + for(int i=0;ieventlist[i].udata) )->fire_idx = 0; + } + return j; +} +int co_epoll_del( int epfd,int fd ) +{ + + struct timespec t = { 0 }; + struct kevent_pair_t *ptr = ( struct kevent_pair_t* )get_fd_map()->get( fd ); + if( !ptr ) return 0; + if( EPOLLIN & ptr->events ) + { + struct kevent kev = { 0 }; + kev.ident = fd; + kev.filter = EVFILT_READ; + kev.flags = EV_DELETE; + kevent( epfd,&kev,1, NULL,0,&t ); + } + if( EPOLLOUT & ptr->events ) + { + struct kevent kev = { 0 }; + kev.ident = fd; + kev.filter = EVFILT_WRITE; + kev.flags = EV_DELETE; + kevent( epfd,&kev,1, NULL,0,&t ); + } + get_fd_map()->clear( fd ); + free( ptr ); + return 0; +} +int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ev ) +{ + if( EPOLL_CTL_DEL == op ) + { + return co_epoll_del( epfd,fd ); + } + + const int flags = ( EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP ); + if( ev->events & ~flags ) + { + return -1; + } + + if( EPOLL_CTL_ADD == op && get_fd_map()->get( fd ) ) + { + errno = EEXIST; + return -1; + } + else if( EPOLL_CTL_MOD == op && !get_fd_map()->get( fd ) ) + { + errno = ENOENT; + return -1; + } + + struct kevent_pair_t *ptr = (struct kevent_pair_t*)get_fd_map()->get( fd ); + if( !ptr ) + { + ptr = (kevent_pair_t*)calloc(1,sizeof(kevent_pair_t)); + get_fd_map()->set( fd,ptr ); + } + + int ret = 0; + struct timespec t = { 0 }; + + // printf("ptr->events 0x%X\n",ptr->events); + + if( EPOLL_CTL_MOD == op ) + { + //1.delete if exists + if( ptr->events & EPOLLIN ) + { + struct kevent kev = { 0 }; + EV_SET( &kev,fd,EVFILT_READ,EV_DELETE,0,0,NULL ); + kevent( epfd, &kev,1, NULL,0, &t ); + } + //1.delete if exists + if( ptr->events & EPOLLOUT ) + { + struct kevent kev = { 0 }; + EV_SET( &kev,fd,EVFILT_WRITE,EV_DELETE,0,0,NULL ); + ret = kevent( epfd, &kev,1, NULL,0, &t ); + // printf("delete write ret %d\n",ret ); + } + } + + do + { + if( ev->events & EPOLLIN ) + { + + //2.add + struct kevent kev = { 0 }; + EV_SET( &kev,fd,EVFILT_READ,EV_ADD,0,0,ptr ); + ret = kevent( epfd, &kev,1, NULL,0, &t ); + if( ret ) break; + } + if( ev->events & EPOLLOUT ) + { + //2.add + struct kevent kev = { 0 }; + EV_SET( &kev,fd,EVFILT_WRITE,EV_ADD,0,0,ptr ); + ret = kevent( epfd, &kev,1, NULL,0, &t ); + if( ret ) break; + } + } while( 0 ); + + if( ret ) + { + get_fd_map()->clear( fd ); + free( ptr ); + return ret; + } + + ptr->events = ev->events; + ptr->u64 = ev->data.u64; + + + return ret; +} + +struct co_epoll_res *co_epoll_res_alloc( int n ) +{ + struct co_epoll_res * ptr = + (struct co_epoll_res *)malloc( sizeof( struct co_epoll_res ) ); + + ptr->size = n; + ptr->events = (struct epoll_event*)calloc( 1,n * sizeof( struct epoll_event ) ); + ptr->eventlist = (struct kevent*)calloc( 1,n * sizeof( struct kevent) ); + + return ptr; +} + +void co_epoll_res_free( struct co_epoll_res * ptr ) +{ + if( !ptr ) return; + if( ptr->events ) free( ptr->events ); + if( ptr->eventlist ) free( ptr->eventlist ); + free( ptr ); +} + +#endif + + diff --git a/trunk/3rdparty/libco/co_epoll.h b/trunk/3rdparty/libco/co_epoll.h new file mode 100644 index 000000000..a1b0e4a7d --- /dev/null +++ b/trunk/3rdparty/libco/co_epoll.h @@ -0,0 +1,93 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __CO_EPOLL_H__ +#define __CO_EPOLL_H__ +#include +#include +#include +#include +#include +#include +#include + +#if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) + +#include + +struct co_epoll_res +{ + int size; + struct epoll_event *events; + struct kevent *eventlist; +}; +int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ); +int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ); +int co_epoll_create( int size ); +struct co_epoll_res *co_epoll_res_alloc( int n ); +void co_epoll_res_free( struct co_epoll_res * ); + +#else + +#include +enum EPOLL_EVENTS +{ + EPOLLIN = 0X001, + EPOLLPRI = 0X002, + EPOLLOUT = 0X004, + + EPOLLERR = 0X008, + EPOLLHUP = 0X010, + + EPOLLRDNORM = 0x40, + EPOLLWRNORM = 0x004, +}; +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_DEL 2 +#define EPOLL_CTL_MOD 3 +typedef union epoll_data +{ + void *ptr; + int fd; + uint32_t u32; + uint64_t u64; + +} epoll_data_t; + +struct epoll_event +{ + uint32_t events; + epoll_data_t data; +}; + +struct co_epoll_res +{ + int size; + struct epoll_event *events; + struct kevent *eventlist; +}; +int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ); +int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ); +int co_epoll_create( int size ); +struct co_epoll_res *co_epoll_res_alloc( int n ); +void co_epoll_res_free( struct co_epoll_res * ); + +#endif +#endif + + diff --git a/trunk/3rdparty/libco/co_hook_sys_call.cpp b/trunk/3rdparty/libco/co_hook_sys_call.cpp new file mode 100644 index 000000000..7bb0c417b --- /dev/null +++ b/trunk/3rdparty/libco/co_hook_sys_call.cpp @@ -0,0 +1,1003 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "co_routine.h" +#include "co_routine_inner.h" +#include "co_routine_specific.h" + +typedef long long ll64_t; + +struct rpchook_t +{ + int user_flag; + struct sockaddr_in dest; //maybe sockaddr_un; + int domain; //AF_LOCAL , AF_INET + + struct timeval read_timeout; + struct timeval write_timeout; +}; +static inline pid_t GetPid() +{ + char **p = (char**)pthread_self(); + return p ? *(pid_t*)(p + 18) : getpid(); +} +static rpchook_t *g_rpchook_socket_fd[ 102400 ] = { 0 }; + +typedef int (*socket_pfn_t)(int domain, int type, int protocol); +typedef int (*connect_pfn_t)(int socket, const struct sockaddr *address, socklen_t address_len); +typedef int (*close_pfn_t)(int fd); + +typedef ssize_t (*read_pfn_t)(int fildes, void *buf, size_t nbyte); +typedef ssize_t (*write_pfn_t)(int fildes, const void *buf, size_t nbyte); + +typedef ssize_t (*sendto_pfn_t)(int socket, const void *message, size_t length, + int flags, const struct sockaddr *dest_addr, + socklen_t dest_len); + +typedef ssize_t (*recvfrom_pfn_t)(int socket, void *buffer, size_t length, + int flags, struct sockaddr *address, + socklen_t *address_len); + +typedef size_t (*send_pfn_t)(int socket, const void *buffer, size_t length, int flags); +typedef ssize_t (*recv_pfn_t)(int socket, void *buffer, size_t length, int flags); + +typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); +typedef int (*setsockopt_pfn_t)(int socket, int level, int option_name, + const void *option_value, socklen_t option_len); + +typedef int (*fcntl_pfn_t)(int fildes, int cmd, ...); +typedef struct tm *(*localtime_r_pfn_t)( const time_t *timep, struct tm *result ); + +typedef void *(*pthread_getspecific_pfn_t)(pthread_key_t key); +typedef int (*pthread_setspecific_pfn_t)(pthread_key_t key, const void *value); + +typedef int (*setenv_pfn_t)(const char *name, const char *value, int overwrite); +typedef int (*unsetenv_pfn_t)(const char *name); +typedef char *(*getenv_pfn_t)(const char *name); +typedef hostent* (*gethostbyname_pfn_t)(const char *name); +typedef res_state (*__res_state_pfn_t)(); +typedef int (*__poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); + +static socket_pfn_t g_sys_socket_func = (socket_pfn_t)dlsym(RTLD_NEXT,"socket"); +static connect_pfn_t g_sys_connect_func = (connect_pfn_t)dlsym(RTLD_NEXT,"connect"); +static close_pfn_t g_sys_close_func = (close_pfn_t)dlsym(RTLD_NEXT,"close"); + +static read_pfn_t g_sys_read_func = (read_pfn_t)dlsym(RTLD_NEXT,"read"); +static write_pfn_t g_sys_write_func = (write_pfn_t)dlsym(RTLD_NEXT,"write"); + +static sendto_pfn_t g_sys_sendto_func = (sendto_pfn_t)dlsym(RTLD_NEXT,"sendto"); +static recvfrom_pfn_t g_sys_recvfrom_func = (recvfrom_pfn_t)dlsym(RTLD_NEXT,"recvfrom"); + +static send_pfn_t g_sys_send_func = (send_pfn_t)dlsym(RTLD_NEXT,"send"); +static recv_pfn_t g_sys_recv_func = (recv_pfn_t)dlsym(RTLD_NEXT,"recv"); + +static poll_pfn_t g_sys_poll_func = (poll_pfn_t)dlsym(RTLD_NEXT,"poll"); + +static setsockopt_pfn_t g_sys_setsockopt_func + = (setsockopt_pfn_t)dlsym(RTLD_NEXT,"setsockopt"); +static fcntl_pfn_t g_sys_fcntl_func = (fcntl_pfn_t)dlsym(RTLD_NEXT,"fcntl"); + +static setenv_pfn_t g_sys_setenv_func = (setenv_pfn_t)dlsym(RTLD_NEXT,"setenv"); +static unsetenv_pfn_t g_sys_unsetenv_func = (unsetenv_pfn_t)dlsym(RTLD_NEXT,"unsetenv"); +static getenv_pfn_t g_sys_getenv_func = (getenv_pfn_t)dlsym(RTLD_NEXT,"getenv"); +static __res_state_pfn_t g_sys___res_state_func = (__res_state_pfn_t)dlsym(RTLD_NEXT,"__res_state"); + +static gethostbyname_pfn_t g_sys_gethostbyname_func = (gethostbyname_pfn_t)dlsym(RTLD_NEXT, "gethostbyname"); + +static __poll_pfn_t g_sys___poll_func = (__poll_pfn_t)dlsym(RTLD_NEXT, "__poll"); + + +/* +static pthread_getspecific_pfn_t g_sys_pthread_getspecific_func + = (pthread_getspecific_pfn_t)dlsym(RTLD_NEXT,"pthread_getspecific"); + +static pthread_setspecific_pfn_t g_sys_pthread_setspecific_func + = (pthread_setspecific_pfn_t)dlsym(RTLD_NEXT,"pthread_setspecific"); + +static pthread_rwlock_rdlock_pfn_t g_sys_pthread_rwlock_rdlock_func + = (pthread_rwlock_rdlock_pfn_t)dlsym(RTLD_NEXT,"pthread_rwlock_rdlock"); + +static pthread_rwlock_wrlock_pfn_t g_sys_pthread_rwlock_wrlock_func + = (pthread_rwlock_wrlock_pfn_t)dlsym(RTLD_NEXT,"pthread_rwlock_wrlock"); + +static pthread_rwlock_unlock_pfn_t g_sys_pthread_rwlock_unlock_func + = (pthread_rwlock_unlock_pfn_t)dlsym(RTLD_NEXT,"pthread_rwlock_unlock"); +*/ + + + +static inline unsigned long long get_tick_count() +{ + uint32_t lo, hi; + __asm__ __volatile__ ( + "rdtscp" : "=a"(lo), "=d"(hi) + ); + return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); +} + +struct rpchook_connagent_head_t +{ + unsigned char bVersion; + struct in_addr iIP; + unsigned short hPort; + unsigned int iBodyLen; + unsigned int iOssAttrID; + unsigned char bIsRespNotExist; + unsigned char sReserved[6]; +}__attribute__((packed)); + + +#define HOOK_SYS_FUNC(name) if( !g_sys_##name##_func ) { g_sys_##name##_func = (name##_pfn_t)dlsym(RTLD_NEXT,#name); } + +static inline ll64_t diff_ms(struct timeval &begin,struct timeval &end) +{ + ll64_t u = (end.tv_sec - begin.tv_sec) ; + u *= 1000 * 10; + u += ( end.tv_usec - begin.tv_usec ) / ( 100 ); + return u; +} + + + +static inline rpchook_t * get_by_fd( int fd ) +{ + if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) + { + return g_rpchook_socket_fd[ fd ]; + } + return NULL; +} +static inline rpchook_t * alloc_by_fd( int fd ) +{ + if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) + { + rpchook_t *lp = (rpchook_t*)calloc( 1,sizeof(rpchook_t) ); + lp->read_timeout.tv_sec = 1; + lp->write_timeout.tv_sec = 1; + g_rpchook_socket_fd[ fd ] = lp; + return lp; + } + return NULL; +} +static inline void free_by_fd( int fd ) +{ + if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) + { + rpchook_t *lp = g_rpchook_socket_fd[ fd ]; + if( lp ) + { + g_rpchook_socket_fd[ fd ] = NULL; + free(lp); + } + } + return; + +} +int socket(int domain, int type, int protocol) +{ + HOOK_SYS_FUNC( socket ); + + if( !co_is_enable_sys_hook() ) + { + return g_sys_socket_func( domain,type,protocol ); + } + int fd = g_sys_socket_func(domain,type,protocol); + if( fd < 0 ) + { + return fd; + } + + rpchook_t *lp = alloc_by_fd( fd ); + lp->domain = domain; + + fcntl( fd, F_SETFL, g_sys_fcntl_func(fd, F_GETFL,0 ) ); + + return fd; +} + +int co_accept( int fd, struct sockaddr *addr, socklen_t *len ) +{ + int cli = accept( fd,addr,len ); + if( cli < 0 ) + { + return cli; + } + alloc_by_fd( cli ); + return cli; +} +int connect(int fd, const struct sockaddr *address, socklen_t address_len) +{ + HOOK_SYS_FUNC( connect ); + + if( !co_is_enable_sys_hook() ) + { + return g_sys_connect_func(fd,address,address_len); + } + + //1.sys call + int ret = g_sys_connect_func( fd,address,address_len ); + + rpchook_t *lp = get_by_fd( fd ); + if( !lp ) return ret; + + if( sizeof(lp->dest) >= address_len ) + { + memcpy( &(lp->dest),address,(int)address_len ); + } + if( O_NONBLOCK & lp->user_flag ) + { + return ret; + } + + if (!(ret < 0 && errno == EINPROGRESS)) + { + return ret; + } + + //2.wait + int pollret = 0; + struct pollfd pf = { 0 }; + + for(int i=0;i<3;i++) //25s * 3 = 75s + { + memset( &pf,0,sizeof(pf) ); + pf.fd = fd; + pf.events = ( POLLOUT | POLLERR | POLLHUP ); + + pollret = poll( &pf,1,25000 ); + + if( 1 == pollret ) + { + break; + } + } + + if( pf.revents & POLLOUT ) //connect succ + { + // 3.check getsockopt ret + int err = 0; + socklen_t errlen = sizeof(err); + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen); + if (ret < 0) { + return ret; + } else if (err != 0) { + errno = err; + return -1; + } + errno = 0; + return 0; + } + + errno = ETIMEDOUT; + return ret; +} + + +int close(int fd) +{ + HOOK_SYS_FUNC( close ); + + if( !co_is_enable_sys_hook() ) + { + return g_sys_close_func( fd ); + } + + free_by_fd( fd ); + int ret = g_sys_close_func(fd); + + return ret; +} +ssize_t read( int fd, void *buf, size_t nbyte ) +{ + HOOK_SYS_FUNC( read ); + + if( !co_is_enable_sys_hook() ) + { + return g_sys_read_func( fd,buf,nbyte ); + } + rpchook_t *lp = get_by_fd( fd ); + + if( !lp || ( O_NONBLOCK & lp->user_flag ) ) + { + ssize_t ret = g_sys_read_func( fd,buf,nbyte ); + return ret; + } + int timeout = ( lp->read_timeout.tv_sec * 1000 ) + + ( lp->read_timeout.tv_usec / 1000 ); + + struct pollfd pf = { 0 }; + pf.fd = fd; + pf.events = ( POLLIN | POLLERR | POLLHUP ); + + int pollret = poll( &pf,1,timeout ); + + ssize_t readret = g_sys_read_func( fd,(char*)buf ,nbyte ); + + if( readret < 0 ) + { + co_log_err("CO_ERR: read fd %d ret %ld errno %d poll ret %d timeout %d", + fd,readret,errno,pollret,timeout); + } + + return readret; + +} +ssize_t write( int fd, const void *buf, size_t nbyte ) +{ + HOOK_SYS_FUNC( write ); + + if( !co_is_enable_sys_hook() ) + { + return g_sys_write_func( fd,buf,nbyte ); + } + rpchook_t *lp = get_by_fd( fd ); + + if( !lp || ( O_NONBLOCK & lp->user_flag ) ) + { + ssize_t ret = g_sys_write_func( fd,buf,nbyte ); + return ret; + } + size_t wrotelen = 0; + int timeout = ( lp->write_timeout.tv_sec * 1000 ) + + ( lp->write_timeout.tv_usec / 1000 ); + + ssize_t writeret = g_sys_write_func( fd,(const char*)buf + wrotelen,nbyte - wrotelen ); + + if (writeret == 0) + { + return writeret; + } + + if( writeret > 0 ) + { + wrotelen += writeret; + } + while( wrotelen < nbyte ) + { + + struct pollfd pf = { 0 }; + pf.fd = fd; + pf.events = ( POLLOUT | POLLERR | POLLHUP ); + poll( &pf,1,timeout ); + + writeret = g_sys_write_func( fd,(const char*)buf + wrotelen,nbyte - wrotelen ); + + if( writeret <= 0 ) + { + break; + } + wrotelen += writeret ; + } + if (writeret <= 0 && wrotelen == 0) + { + return writeret; + } + return wrotelen; +} + +ssize_t sendto(int socket, const void *message, size_t length, + int flags, const struct sockaddr *dest_addr, + socklen_t dest_len) +{ + /* + 1.no enable sys call ? sys + 2.( !lp || lp is non block ) ? sys + 3.try + 4.wait + 5.try + */ + HOOK_SYS_FUNC( sendto ); + if( !co_is_enable_sys_hook() ) + { + return g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); + } + + rpchook_t *lp = get_by_fd( socket ); + if( !lp || ( O_NONBLOCK & lp->user_flag ) ) + { + return g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); + } + + ssize_t ret = g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); + if( ret < 0 && EAGAIN == errno ) + { + int timeout = ( lp->write_timeout.tv_sec * 1000 ) + + ( lp->write_timeout.tv_usec / 1000 ); + + + struct pollfd pf = { 0 }; + pf.fd = socket; + pf.events = ( POLLOUT | POLLERR | POLLHUP ); + poll( &pf,1,timeout ); + + ret = g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); + + } + return ret; +} + +ssize_t recvfrom(int socket, void *buffer, size_t length, + int flags, struct sockaddr *address, + socklen_t *address_len) +{ + HOOK_SYS_FUNC( recvfrom ); + if( !co_is_enable_sys_hook() ) + { + return g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); + } + + rpchook_t *lp = get_by_fd( socket ); + if( !lp || ( O_NONBLOCK & lp->user_flag ) ) + { + return g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); + } + + int timeout = ( lp->read_timeout.tv_sec * 1000 ) + + ( lp->read_timeout.tv_usec / 1000 ); + + + struct pollfd pf = { 0 }; + pf.fd = socket; + pf.events = ( POLLIN | POLLERR | POLLHUP ); + poll( &pf,1,timeout ); + + ssize_t ret = g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); + return ret; +} + +ssize_t send(int socket, const void *buffer, size_t length, int flags) +{ + HOOK_SYS_FUNC( send ); + + if( !co_is_enable_sys_hook() ) + { + return g_sys_send_func( socket,buffer,length,flags ); + } + rpchook_t *lp = get_by_fd( socket ); + + if( !lp || ( O_NONBLOCK & lp->user_flag ) ) + { + return g_sys_send_func( socket,buffer,length,flags ); + } + size_t wrotelen = 0; + int timeout = ( lp->write_timeout.tv_sec * 1000 ) + + ( lp->write_timeout.tv_usec / 1000 ); + + ssize_t writeret = g_sys_send_func( socket,buffer,length,flags ); + if (writeret == 0) + { + return writeret; + } + + if( writeret > 0 ) + { + wrotelen += writeret; + } + while( wrotelen < length ) + { + + struct pollfd pf = { 0 }; + pf.fd = socket; + pf.events = ( POLLOUT | POLLERR | POLLHUP ); + poll( &pf,1,timeout ); + + writeret = g_sys_send_func( socket,(const char*)buffer + wrotelen,length - wrotelen,flags ); + + if( writeret <= 0 ) + { + break; + } + wrotelen += writeret ; + } + if (writeret <= 0 && wrotelen == 0) + { + return writeret; + } + return wrotelen; +} + +ssize_t recv( int socket, void *buffer, size_t length, int flags ) +{ + HOOK_SYS_FUNC( recv ); + + if( !co_is_enable_sys_hook() ) + { + return g_sys_recv_func( socket,buffer,length,flags ); + } + rpchook_t *lp = get_by_fd( socket ); + + if( !lp || ( O_NONBLOCK & lp->user_flag ) ) + { + return g_sys_recv_func( socket,buffer,length,flags ); + } + int timeout = ( lp->read_timeout.tv_sec * 1000 ) + + ( lp->read_timeout.tv_usec / 1000 ); + + struct pollfd pf = { 0 }; + pf.fd = socket; + pf.events = ( POLLIN | POLLERR | POLLHUP ); + + int pollret = poll( &pf,1,timeout ); + + ssize_t readret = g_sys_recv_func( socket,buffer,length,flags ); + + if( readret < 0 ) + { + co_log_err("CO_ERR: read fd %d ret %ld errno %d poll ret %d timeout %d", + socket,readret,errno,pollret,timeout); + } + + return readret; + +} + +extern int co_poll_inner( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout, poll_pfn_t pollfunc); + +int poll(struct pollfd fds[], nfds_t nfds, int timeout) +{ + HOOK_SYS_FUNC( poll ); + + if (!co_is_enable_sys_hook() || timeout == 0) { + return g_sys_poll_func(fds, nfds, timeout); + } + pollfd *fds_merge = NULL; + nfds_t nfds_merge = 0; + std::map m; // fd --> idx + std::map::iterator it; + if (nfds > 1) { + fds_merge = (pollfd *)malloc(sizeof(pollfd) * nfds); + for (size_t i = 0; i < nfds; i++) { + if ((it = m.find(fds[i].fd)) == m.end()) { + fds_merge[nfds_merge] = fds[i]; + m[fds[i].fd] = nfds_merge; + nfds_merge++; + } else { + int j = it->second; + fds_merge[j].events |= fds[i].events; // merge in j slot + } + } + } + + int ret = 0; + if (nfds_merge == nfds || nfds == 1) { + ret = co_poll_inner(co_get_epoll_ct(), fds, nfds, timeout, g_sys_poll_func); + } else { + ret = co_poll_inner(co_get_epoll_ct(), fds_merge, nfds_merge, timeout, + g_sys_poll_func); + if (ret > 0) { + for (size_t i = 0; i < nfds; i++) { + it = m.find(fds[i].fd); + if (it != m.end()) { + int j = it->second; + fds[i].revents = fds_merge[j].revents & fds[i].events; + } + } + } + } + free(fds_merge); + return ret; + + +} +int setsockopt(int fd, int level, int option_name, + const void *option_value, socklen_t option_len) +{ + HOOK_SYS_FUNC( setsockopt ); + + if( !co_is_enable_sys_hook() ) + { + return g_sys_setsockopt_func( fd,level,option_name,option_value,option_len ); + } + rpchook_t *lp = get_by_fd( fd ); + + if( lp && SOL_SOCKET == level ) + { + struct timeval *val = (struct timeval*)option_value; + if( SO_RCVTIMEO == option_name ) + { + memcpy( &lp->read_timeout,val,sizeof(*val) ); + } + else if( SO_SNDTIMEO == option_name ) + { + memcpy( &lp->write_timeout,val,sizeof(*val) ); + } + } + return g_sys_setsockopt_func( fd,level,option_name,option_value,option_len ); +} + + +int fcntl(int fildes, int cmd, ...) +{ + HOOK_SYS_FUNC( fcntl ); + + if( fildes < 0 ) + { + return __LINE__; + } + + va_list arg_list; + va_start( arg_list,cmd ); + + int ret = -1; + rpchook_t *lp = get_by_fd( fildes ); + switch( cmd ) + { + case F_DUPFD: + { + int param = va_arg(arg_list,int); + ret = g_sys_fcntl_func( fildes,cmd,param ); + break; + } + case F_GETFD: + { + ret = g_sys_fcntl_func( fildes,cmd ); + if (lp && !(lp->user_flag & O_NONBLOCK)) { + ret = ret & (~O_NONBLOCK); + } + break; + } + case F_SETFD: + { + int param = va_arg(arg_list,int); + ret = g_sys_fcntl_func( fildes,cmd,param ); + break; + } + case F_GETFL: + { + ret = g_sys_fcntl_func( fildes,cmd ); + break; + } + case F_SETFL: + { + int param = va_arg(arg_list,int); + int flag = param; + if( co_is_enable_sys_hook() && lp ) + { + flag |= O_NONBLOCK; + } + ret = g_sys_fcntl_func( fildes,cmd,flag ); + if( 0 == ret && lp ) + { + lp->user_flag = param; + } + break; + } + case F_GETOWN: + { + ret = g_sys_fcntl_func( fildes,cmd ); + break; + } + case F_SETOWN: + { + int param = va_arg(arg_list,int); + ret = g_sys_fcntl_func( fildes,cmd,param ); + break; + } + case F_GETLK: + { + struct flock *param = va_arg(arg_list,struct flock *); + ret = g_sys_fcntl_func( fildes,cmd,param ); + break; + } + case F_SETLK: + { + struct flock *param = va_arg(arg_list,struct flock *); + ret = g_sys_fcntl_func( fildes,cmd,param ); + break; + } + case F_SETLKW: + { + struct flock *param = va_arg(arg_list,struct flock *); + ret = g_sys_fcntl_func( fildes,cmd,param ); + break; + } + } + + va_end( arg_list ); + + return ret; +} + +struct stCoSysEnv_t +{ + char *name; + char *value; +}; +struct stCoSysEnvArr_t +{ + stCoSysEnv_t *data; + size_t cnt; +}; +static stCoSysEnvArr_t *dup_co_sysenv_arr( stCoSysEnvArr_t * arr ) +{ + stCoSysEnvArr_t *lp = (stCoSysEnvArr_t*)calloc( sizeof(stCoSysEnvArr_t),1 ); + if( arr->cnt ) + { + lp->data = (stCoSysEnv_t*)calloc( sizeof(stCoSysEnv_t) * arr->cnt,1 ); + lp->cnt = arr->cnt; + memcpy( lp->data,arr->data,sizeof( stCoSysEnv_t ) * arr->cnt ); + } + return lp; +} + +static int co_sysenv_comp(const void *a, const void *b) +{ + return strcmp(((stCoSysEnv_t*)a)->name, ((stCoSysEnv_t*)b)->name); +} +static stCoSysEnvArr_t g_co_sysenv = { 0 }; + + + +void co_set_env_list( const char *name[],size_t cnt) +{ + if( g_co_sysenv.data ) + { + return ; + } + g_co_sysenv.data = (stCoSysEnv_t*)calloc( 1,sizeof(stCoSysEnv_t) * cnt ); + + for(size_t i=0;i 1 ) + { + qsort( g_co_sysenv.data,g_co_sysenv.cnt,sizeof(stCoSysEnv_t),co_sysenv_comp ); + stCoSysEnv_t *lp = g_co_sysenv.data; + stCoSysEnv_t *lq = g_co_sysenv.data + 1; + for(size_t i=1;iname,lq->name ) ) + { + ++lp; + if( lq != lp ) + { + *lp = *lq; + } + } + ++lq; + } + g_co_sysenv.cnt = lp - g_co_sysenv.data + 1; + } + +} + +int setenv(const char *n, const char *value, int overwrite) +{ + HOOK_SYS_FUNC( setenv ) + if( co_is_enable_sys_hook() && g_co_sysenv.data ) + { + stCoRoutine_t *self = co_self(); + if( self ) + { + if( !self->pvEnv ) + { + self->pvEnv = dup_co_sysenv_arr( &g_co_sysenv ); + } + stCoSysEnvArr_t *arr = (stCoSysEnvArr_t*)(self->pvEnv); + + stCoSysEnv_t name = { (char*)n,0 }; + + stCoSysEnv_t *e = (stCoSysEnv_t*)bsearch( &name,arr->data,arr->cnt,sizeof(name),co_sysenv_comp ); + + if( e ) + { + if( overwrite || !e->value ) + { + if( e->value ) free( e->value ); + e->value = ( value ? strdup( value ) : 0 ); + } + return 0; + } + } + + } + return g_sys_setenv_func( n,value,overwrite ); +} +int unsetenv(const char *n) +{ + HOOK_SYS_FUNC( unsetenv ) + if( co_is_enable_sys_hook() && g_co_sysenv.data ) + { + stCoRoutine_t *self = co_self(); + if( self ) + { + if( !self->pvEnv ) + { + self->pvEnv = dup_co_sysenv_arr( &g_co_sysenv ); + } + stCoSysEnvArr_t *arr = (stCoSysEnvArr_t*)(self->pvEnv); + + stCoSysEnv_t name = { (char*)n,0 }; + + stCoSysEnv_t *e = (stCoSysEnv_t*)bsearch( &name,arr->data,arr->cnt,sizeof(name),co_sysenv_comp ); + + if( e ) + { + if( e->value ) + { + free( e->value ); + e->value = 0; + } + return 0; + } + } + + } + return g_sys_unsetenv_func( n ); +} +char *getenv( const char *n ) +{ + HOOK_SYS_FUNC( getenv ) + if( co_is_enable_sys_hook() && g_co_sysenv.data ) + { + stCoRoutine_t *self = co_self(); + + stCoSysEnv_t name = { (char*)n,0 }; + + if( !self->pvEnv ) + { + self->pvEnv = dup_co_sysenv_arr( &g_co_sysenv ); + } + stCoSysEnvArr_t *arr = (stCoSysEnvArr_t*)(self->pvEnv); + + stCoSysEnv_t *e = (stCoSysEnv_t*)bsearch( &name,arr->data,arr->cnt,sizeof(name),co_sysenv_comp ); + + if( e ) + { + return e->value; + } + + } + return g_sys_getenv_func( n ); + +} +struct hostent* co_gethostbyname(const char *name); + +struct hostent *gethostbyname(const char *name) +{ + HOOK_SYS_FUNC( gethostbyname ); + +#if defined( __APPLE__ ) || defined( __FreeBSD__ ) + return g_sys_gethostbyname_func( name ); +#else + if (!co_is_enable_sys_hook()) + { + return g_sys_gethostbyname_func(name); + } + return co_gethostbyname(name); +#endif + +} + + +struct res_state_wrap +{ + struct __res_state state; +}; +CO_ROUTINE_SPECIFIC(res_state_wrap, __co_state_wrap); + +extern "C" +{ + res_state __res_state() + { + HOOK_SYS_FUNC(__res_state); + + if (!co_is_enable_sys_hook()) + { + return g_sys___res_state_func(); + } + + return &(__co_state_wrap->state); + } + int __poll(struct pollfd fds[], nfds_t nfds, int timeout) + { + return poll(fds, nfds, timeout); + } +} + +struct hostbuf_wrap +{ + struct hostent host; + char* buffer; + size_t iBufferSize; + int host_errno; +}; + +CO_ROUTINE_SPECIFIC(hostbuf_wrap, __co_hostbuf_wrap); + +#if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) +struct hostent *co_gethostbyname(const char *name) +{ + if (!name) + { + return NULL; + } + + if (__co_hostbuf_wrap->buffer && __co_hostbuf_wrap->iBufferSize > 1024) + { + free(__co_hostbuf_wrap->buffer); + __co_hostbuf_wrap->buffer = NULL; + } + if (!__co_hostbuf_wrap->buffer) + { + __co_hostbuf_wrap->buffer = (char*)malloc(1024); + __co_hostbuf_wrap->iBufferSize = 1024; + } + + struct hostent *host = &__co_hostbuf_wrap->host; + struct hostent *result = NULL; + int *h_errnop = &(__co_hostbuf_wrap->host_errno); + + int ret = -1; + while (ret = gethostbyname_r(name, host, __co_hostbuf_wrap->buffer, + __co_hostbuf_wrap->iBufferSize, &result, h_errnop) == ERANGE && + *h_errnop == NETDB_INTERNAL ) + { + free(__co_hostbuf_wrap->buffer); + __co_hostbuf_wrap->iBufferSize = __co_hostbuf_wrap->iBufferSize * 2; + __co_hostbuf_wrap->buffer = (char*)malloc(__co_hostbuf_wrap->iBufferSize); + } + + if (ret == 0 && (host == result)) + { + return host; + } + return NULL; +} +#endif + + +void co_enable_hook_sys() //这函数必须在这里,否则本文件会被忽略!!! +{ + stCoRoutine_t *co = GetCurrThreadCo(); + if( co ) + { + co->cEnableSysHook = 1; + } +} + diff --git a/trunk/3rdparty/libco/co_routine.cpp b/trunk/3rdparty/libco/co_routine.cpp new file mode 100644 index 000000000..352ae7ea0 --- /dev/null +++ b/trunk/3rdparty/libco/co_routine.cpp @@ -0,0 +1,1196 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "co_routine.h" +#include "co_routine_inner.h" +#include "co_epoll.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +extern "C" +{ + extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap"); +}; +using namespace std; +stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env ); +struct stCoEpoll_t; + +struct stCoRoutineEnv_t +{ + stCoRoutine_t *pCallStack[ 128 ]; + int iCallStackSize; + stCoEpoll_t *pEpoll; + + //for copy stack log lastco and nextco + stCoRoutine_t* pending_co; + stCoRoutine_t* occupy_co; +}; +//int socket(int domain, int type, int protocol); +void co_log_err( const char *fmt,... ) +{ +} + + +#if defined( __LIBCO_RDTSCP__) +static unsigned long long counter(void) +{ + register uint32_t lo, hi; + register unsigned long long o; + __asm__ __volatile__ ( + "rdtscp" : "=a"(lo), "=d"(hi)::"%rcx" + ); + o = hi; + o <<= 32; + return (o | lo); + +} +static unsigned long long getCpuKhz() +{ + FILE *fp = fopen("/proc/cpuinfo","r"); + if(!fp) return 1; + char buf[4096] = {0}; + fread(buf,1,sizeof(buf),fp); + fclose(fp); + + char *lp = strstr(buf,"cpu MHz"); + if(!lp) return 1; + lp += strlen("cpu MHz"); + while(*lp == ' ' || *lp == '\t' || *lp == ':') + { + ++lp; + } + + double mhz = atof(lp); + unsigned long long u = (unsigned long long)(mhz * 1000); + return u; +} +#endif + +static unsigned long long GetTickMS() +{ +#if defined( __LIBCO_RDTSCP__) + static uint32_t khz = getCpuKhz(); + return counter() / khz; +#else + struct timeval now = { 0 }; + gettimeofday( &now,NULL ); + unsigned long long u = now.tv_sec; + u *= 1000; + u += now.tv_usec / 1000; + return u; +#endif +} + +/* no longer use +static pid_t GetPid() +{ + static __thread pid_t pid = 0; + static __thread pid_t tid = 0; + if( !pid || !tid || pid != getpid() ) + { + pid = getpid(); +#if defined( __APPLE__ ) + tid = syscall( SYS_gettid ); + if( -1 == (long)tid ) + { + tid = pid; + } +#elif defined( __FreeBSD__ ) + syscall(SYS_thr_self, &tid); + if( tid < 0 ) + { + tid = pid; + } +#else + tid = syscall( __NR_gettid ); +#endif + + } + return tid; + +} +static pid_t GetPid() +{ + char **p = (char**)pthread_self(); + return p ? *(pid_t*)(p + 18) : getpid(); +} +*/ +template +void RemoveFromLink(T *ap) +{ + TLink *lst = ap->pLink; + if(!lst) return ; + assert( lst->head && lst->tail ); + + if( ap == lst->head ) + { + lst->head = ap->pNext; + if(lst->head) + { + lst->head->pPrev = NULL; + } + } + else + { + if(ap->pPrev) + { + ap->pPrev->pNext = ap->pNext; + } + } + + if( ap == lst->tail ) + { + lst->tail = ap->pPrev; + if(lst->tail) + { + lst->tail->pNext = NULL; + } + } + else + { + ap->pNext->pPrev = ap->pPrev; + } + + ap->pPrev = ap->pNext = NULL; + ap->pLink = NULL; +} + +template +void inline AddTail(TLink*apLink,TNode *ap) +{ + if( ap->pLink ) + { + return ; + } + if(apLink->tail) + { + apLink->tail->pNext = (TNode*)ap; + ap->pNext = NULL; + ap->pPrev = apLink->tail; + apLink->tail = ap; + } + else + { + apLink->head = apLink->tail = ap; + ap->pNext = ap->pPrev = NULL; + } + ap->pLink = apLink; +} +template +void inline PopHead( TLink*apLink ) +{ + if( !apLink->head ) + { + return ; + } + TNode *lp = apLink->head; + if( apLink->head == apLink->tail ) + { + apLink->head = apLink->tail = NULL; + } + else + { + apLink->head = apLink->head->pNext; + } + + lp->pPrev = lp->pNext = NULL; + lp->pLink = NULL; + + if( apLink->head ) + { + apLink->head->pPrev = NULL; + } +} + +template +void inline Join( TLink*apLink,TLink *apOther ) +{ + //printf("apOther %p\n",apOther); + if( !apOther->head ) + { + return ; + } + TNode *lp = apOther->head; + while( lp ) + { + lp->pLink = apLink; + lp = lp->pNext; + } + lp = apOther->head; + if(apLink->tail) + { + apLink->tail->pNext = (TNode*)lp; + lp->pPrev = apLink->tail; + apLink->tail = apOther->tail; + } + else + { + apLink->head = apOther->head; + apLink->tail = apOther->tail; + } + + apOther->head = apOther->tail = NULL; +} + +/////////////////for copy stack ////////////////////////// +stStackMem_t* co_alloc_stackmem(unsigned int stack_size) +{ + stStackMem_t* stack_mem = (stStackMem_t*)malloc(sizeof(stStackMem_t)); + stack_mem->occupy_co= NULL; + stack_mem->stack_size = stack_size; + stack_mem->stack_buffer = (char*)malloc(stack_size); + stack_mem->stack_bp = stack_mem->stack_buffer + stack_size; + return stack_mem; +} + +stShareStack_t* co_alloc_sharestack(int count, int stack_size) +{ + stShareStack_t* share_stack = (stShareStack_t*)malloc(sizeof(stShareStack_t)); + share_stack->alloc_idx = 0; + share_stack->stack_size = stack_size; + + //alloc stack array + share_stack->count = count; + stStackMem_t** stack_array = (stStackMem_t**)calloc(count, sizeof(stStackMem_t*)); + for (int i = 0; i < count; i++) + { + stack_array[i] = co_alloc_stackmem(stack_size); + } + share_stack->stack_array = stack_array; + return share_stack; +} + +static stStackMem_t* co_get_stackmem(stShareStack_t* share_stack) +{ + if (!share_stack) + { + return NULL; + } + int idx = share_stack->alloc_idx % share_stack->count; + share_stack->alloc_idx++; + + return share_stack->stack_array[idx]; +} + + +// ---------------------------------------------------------------------------- +struct stTimeoutItemLink_t; +struct stTimeoutItem_t; +struct stCoEpoll_t +{ + int iEpollFd; + static const int _EPOLL_SIZE = 1024 * 10; + + struct stTimeout_t *pTimeout; + + struct stTimeoutItemLink_t *pstTimeoutList; + + struct stTimeoutItemLink_t *pstActiveList; + + co_epoll_res *result; + +}; +typedef void (*OnPreparePfn_t)( stTimeoutItem_t *,struct epoll_event &ev, stTimeoutItemLink_t *active ); +typedef void (*OnProcessPfn_t)( stTimeoutItem_t *); +struct stTimeoutItem_t +{ + + enum + { + eMaxTimeout = 40 * 1000 //40s + }; + stTimeoutItem_t *pPrev; + stTimeoutItem_t *pNext; + stTimeoutItemLink_t *pLink; + + unsigned long long ullExpireTime; + + OnPreparePfn_t pfnPrepare; + OnProcessPfn_t pfnProcess; + + void *pArg; // routine + bool bTimeout; +}; +struct stTimeoutItemLink_t +{ + stTimeoutItem_t *head; + stTimeoutItem_t *tail; + +}; +struct stTimeout_t +{ + stTimeoutItemLink_t *pItems; + int iItemSize; + + unsigned long long ullStart; + long long llStartIdx; +}; +stTimeout_t *AllocTimeout( int iSize ) +{ + stTimeout_t *lp = (stTimeout_t*)calloc( 1,sizeof(stTimeout_t) ); + + lp->iItemSize = iSize; + lp->pItems = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) * lp->iItemSize ); + + lp->ullStart = GetTickMS(); + lp->llStartIdx = 0; + + return lp; +} +void FreeTimeout( stTimeout_t *apTimeout ) +{ + free( apTimeout->pItems ); + free ( apTimeout ); +} +int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,unsigned long long allNow ) +{ + if( apTimeout->ullStart == 0 ) + { + apTimeout->ullStart = allNow; + apTimeout->llStartIdx = 0; + } + if( allNow < apTimeout->ullStart ) + { + co_log_err("CO_ERR: AddTimeout line %d allNow %llu apTimeout->ullStart %llu", + __LINE__,allNow,apTimeout->ullStart); + + return __LINE__; + } + if( apItem->ullExpireTime < allNow ) + { + co_log_err("CO_ERR: AddTimeout line %d apItem->ullExpireTime %llu allNow %llu apTimeout->ullStart %llu", + __LINE__,apItem->ullExpireTime,allNow,apTimeout->ullStart); + + return __LINE__; + } + unsigned long long diff = apItem->ullExpireTime - apTimeout->ullStart; + + if( diff >= (unsigned long long)apTimeout->iItemSize ) + { + diff = apTimeout->iItemSize - 1; + co_log_err("CO_ERR: AddTimeout line %d diff %d", + __LINE__,diff); + + //return __LINE__; + } + AddTail( apTimeout->pItems + ( apTimeout->llStartIdx + diff ) % apTimeout->iItemSize , apItem ); + + return 0; +} +inline void TakeAllTimeout( stTimeout_t *apTimeout,unsigned long long allNow,stTimeoutItemLink_t *apResult ) +{ + if( apTimeout->ullStart == 0 ) + { + apTimeout->ullStart = allNow; + apTimeout->llStartIdx = 0; + } + + if( allNow < apTimeout->ullStart ) + { + return ; + } + int cnt = allNow - apTimeout->ullStart + 1; + if( cnt > apTimeout->iItemSize ) + { + cnt = apTimeout->iItemSize; + } + if( cnt < 0 ) + { + return; + } + for( int i = 0;illStartIdx + i) % apTimeout->iItemSize; + Join( apResult,apTimeout->pItems + idx ); + } + apTimeout->ullStart = allNow; + apTimeout->llStartIdx += cnt - 1; + + +} +static int CoRoutineFunc( stCoRoutine_t *co,void * ) +{ + if( co->pfn ) + { + co->pfn( co->arg ); + } + co->cEnd = 1; + + stCoRoutineEnv_t *env = co->env; + + co_yield_env( env ); + + return 0; +} + + + +struct stCoRoutine_t *co_create_env( stCoRoutineEnv_t * env, const stCoRoutineAttr_t* attr, + pfn_co_routine_t pfn,void *arg ) +{ + + stCoRoutineAttr_t at; + if( attr ) + { + memcpy( &at,attr,sizeof(at) ); + } + if( at.stack_size <= 0 ) + { + at.stack_size = 128 * 1024; + } + else if( at.stack_size > 1024 * 1024 * 8 ) + { + at.stack_size = 1024 * 1024 * 8; + } + + if( at.stack_size & 0xFFF ) + { + at.stack_size &= ~0xFFF; + at.stack_size += 0x1000; + } + + stCoRoutine_t *lp = (stCoRoutine_t*)malloc( sizeof(stCoRoutine_t) ); + + memset( lp,0,(long)(sizeof(stCoRoutine_t))); + + + lp->env = env; + lp->pfn = pfn; + lp->arg = arg; + + stStackMem_t* stack_mem = NULL; + if( at.share_stack ) + { + stack_mem = co_get_stackmem( at.share_stack); + at.stack_size = at.share_stack->stack_size; + } + else + { + stack_mem = co_alloc_stackmem(at.stack_size); + } + lp->stack_mem = stack_mem; + + lp->ctx.ss_sp = stack_mem->stack_buffer; + lp->ctx.ss_size = at.stack_size; + + lp->cStart = 0; + lp->cEnd = 0; + lp->cIsMain = 0; + lp->cEnableSysHook = 0; + lp->cIsShareStack = at.share_stack != NULL; + + lp->save_size = 0; + lp->save_buffer = NULL; + + return lp; +} + +int co_create( stCoRoutine_t **ppco,const stCoRoutineAttr_t *attr,pfn_co_routine_t pfn,void *arg ) +{ + if( !co_get_curr_thread_env() ) + { + co_init_curr_thread_env(); + } + stCoRoutine_t *co = co_create_env( co_get_curr_thread_env(), attr, pfn,arg ); + *ppco = co; + return 0; +} +void co_free( stCoRoutine_t *co ) +{ + if (!co->cIsShareStack) + { + free(co->stack_mem->stack_buffer); + free(co->stack_mem); + } + //walkerdu fix at 2018-01-20 + //瀛樺湪鍐呭瓨娉勬紡 + else + { + if(co->save_buffer) + free(co->save_buffer); + + if(co->stack_mem->occupy_co == co) + co->stack_mem->occupy_co = NULL; + } + + free( co ); +} +void co_release( stCoRoutine_t *co ) +{ + co_free( co ); +} + +void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co); + +void co_resume( stCoRoutine_t *co ) +{ + stCoRoutineEnv_t *env = co->env; + stCoRoutine_t *lpCurrRoutine = env->pCallStack[ env->iCallStackSize - 1 ]; + if( !co->cStart ) + { + coctx_make( &co->ctx,(coctx_pfn_t)CoRoutineFunc,co,0 ); + co->cStart = 1; + } + env->pCallStack[ env->iCallStackSize++ ] = co; + co_swap( lpCurrRoutine, co ); + + +} + + +// walkerdu 2018-01-14 +// 鐢ㄤ簬reset瓒呮椂鏃犳硶閲嶅浣跨敤鐨勫崗绋 +void co_reset(stCoRoutine_t * co) +{ + if(!co->cStart || co->cIsMain) + return; + + co->cStart = 0; + co->cEnd = 0; + + // 濡傛灉褰撳墠鍗忕▼鏈夊叡浜爤琚垏鍑虹殑buff锛岃杩涜閲婃斁 + if(co->save_buffer) + { + free(co->save_buffer); + co->save_buffer = NULL; + co->save_size = 0; + } + + // 濡傛灉鍏变韩鏍堣褰撳墠鍗忕▼鍗犵敤锛岃閲婃斁鍗犵敤鏍囧織锛屽惁鍒欒鍒囨崲锛屼細鎵цsave_stack_buffer() + if(co->stack_mem->occupy_co == co) + co->stack_mem->occupy_co = NULL; +} + +void co_yield_env( stCoRoutineEnv_t *env ) +{ + + stCoRoutine_t *last = env->pCallStack[ env->iCallStackSize - 2 ]; + stCoRoutine_t *curr = env->pCallStack[ env->iCallStackSize - 1 ]; + + env->iCallStackSize--; + + co_swap( curr, last); +} + +void co_yield_ct() +{ + + co_yield_env( co_get_curr_thread_env() ); +} +void co_yield( stCoRoutine_t *co ) +{ + co_yield_env( co->env ); +} + +void save_stack_buffer(stCoRoutine_t* occupy_co) +{ + ///copy out + stStackMem_t* stack_mem = occupy_co->stack_mem; + int len = stack_mem->stack_bp - occupy_co->stack_sp; + + if (occupy_co->save_buffer) + { + free(occupy_co->save_buffer), occupy_co->save_buffer = NULL; + } + + occupy_co->save_buffer = (char*)malloc(len); //malloc buf; + occupy_co->save_size = len; + + memcpy(occupy_co->save_buffer, occupy_co->stack_sp, len); +} + +void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co) +{ + stCoRoutineEnv_t* env = co_get_curr_thread_env(); + + //get curr stack sp + char c; + curr->stack_sp= &c; + + if (!pending_co->cIsShareStack) + { + env->pending_co = NULL; + env->occupy_co = NULL; + } + else + { + env->pending_co = pending_co; + //get last occupy co on the same stack mem + stCoRoutine_t* occupy_co = pending_co->stack_mem->occupy_co; + //set pending co to occupy thest stack mem; + pending_co->stack_mem->occupy_co = pending_co; + + env->occupy_co = occupy_co; + if (occupy_co && occupy_co != pending_co) + { + save_stack_buffer(occupy_co); + } + } + + //swap context + coctx_swap(&(curr->ctx),&(pending_co->ctx) ); + + //stack buffer may be overwrite, so get again; + stCoRoutineEnv_t* curr_env = co_get_curr_thread_env(); + stCoRoutine_t* update_occupy_co = curr_env->occupy_co; + stCoRoutine_t* update_pending_co = curr_env->pending_co; + + if (update_occupy_co && update_pending_co && update_occupy_co != update_pending_co) + { + //resume stack buffer + if (update_pending_co->save_buffer && update_pending_co->save_size > 0) + { + memcpy(update_pending_co->stack_sp, update_pending_co->save_buffer, update_pending_co->save_size); + } + } +} + + + +//int poll(struct pollfd fds[], nfds_t nfds, int timeout); +// { fd,events,revents } +struct stPollItem_t ; +struct stPoll_t : public stTimeoutItem_t +{ + struct pollfd *fds; + nfds_t nfds; // typedef unsigned long int nfds_t; + + stPollItem_t *pPollItems; + + int iAllEventDetach; + + int iEpollFd; + + int iRaiseCnt; + + +}; +struct stPollItem_t : public stTimeoutItem_t +{ + struct pollfd *pSelf; + stPoll_t *pPoll; + + struct epoll_event stEvent; +}; +/* + * EPOLLPRI POLLPRI // There is urgent data to read. + * EPOLLMSG POLLMSG + * + * POLLREMOVE + * POLLRDHUP + * POLLNVAL + * + * */ +static uint32_t PollEvent2Epoll( short events ) +{ + uint32_t e = 0; + if( events & POLLIN ) e |= EPOLLIN; + if( events & POLLOUT ) e |= EPOLLOUT; + if( events & POLLHUP ) e |= EPOLLHUP; + if( events & POLLERR ) e |= EPOLLERR; + if( events & POLLRDNORM ) e |= EPOLLRDNORM; + if( events & POLLWRNORM ) e |= EPOLLWRNORM; + return e; +} +static short EpollEvent2Poll( uint32_t events ) +{ + short e = 0; + if( events & EPOLLIN ) e |= POLLIN; + if( events & EPOLLOUT ) e |= POLLOUT; + if( events & EPOLLHUP ) e |= POLLHUP; + if( events & EPOLLERR ) e |= POLLERR; + if( events & EPOLLRDNORM ) e |= POLLRDNORM; + if( events & EPOLLWRNORM ) e |= POLLWRNORM; + return e; +} + +static __thread stCoRoutineEnv_t* gCoEnvPerThread = NULL; + +void co_init_curr_thread_env() +{ + gCoEnvPerThread = (stCoRoutineEnv_t*)calloc( 1, sizeof(stCoRoutineEnv_t) ); + stCoRoutineEnv_t *env = gCoEnvPerThread; + + env->iCallStackSize = 0; + struct stCoRoutine_t *self = co_create_env( env, NULL, NULL,NULL ); + self->cIsMain = 1; + + env->pending_co = NULL; + env->occupy_co = NULL; + + coctx_init( &self->ctx ); + + env->pCallStack[ env->iCallStackSize++ ] = self; + + stCoEpoll_t *ev = AllocEpoll(); + SetEpoll( env,ev ); +} +stCoRoutineEnv_t *co_get_curr_thread_env() +{ + return gCoEnvPerThread; +} + +void OnPollProcessEvent( stTimeoutItem_t * ap ) +{ + stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; + co_resume( co ); +} + +void OnPollPreparePfn( stTimeoutItem_t * ap,struct epoll_event &e,stTimeoutItemLink_t *active ) +{ + stPollItem_t *lp = (stPollItem_t *)ap; + lp->pSelf->revents = EpollEvent2Poll( e.events ); + + + stPoll_t *pPoll = lp->pPoll; + pPoll->iRaiseCnt++; + + if( !pPoll->iAllEventDetach ) + { + pPoll->iAllEventDetach = 1; + + RemoveFromLink( pPoll ); + + AddTail( active,pPoll ); + + } +} + + +void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg ) +{ + if( !ctx->result ) + { + ctx->result = co_epoll_res_alloc( stCoEpoll_t::_EPOLL_SIZE ); + } + co_epoll_res *result = ctx->result; + + + for(;;) + { + int ret = co_epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 ); + + stTimeoutItemLink_t *active = (ctx->pstActiveList); + stTimeoutItemLink_t *timeout = (ctx->pstTimeoutList); + + memset( timeout,0,sizeof(stTimeoutItemLink_t) ); + + for(int i=0;ievents[i].data.ptr; + if( item->pfnPrepare ) + { + item->pfnPrepare( item,result->events[i],active ); + } + else + { + AddTail( active,item ); + } + } + + + unsigned long long now = GetTickMS(); + TakeAllTimeout( ctx->pTimeout,now,timeout ); + + stTimeoutItem_t *lp = timeout->head; + while( lp ) + { + //printf("raise timeout %p\n",lp); + lp->bTimeout = true; + lp = lp->pNext; + } + + Join( active,timeout ); + + lp = active->head; + while( lp ) + { + + PopHead( active ); + if (lp->bTimeout && now < lp->ullExpireTime) + { + int ret = AddTimeout(ctx->pTimeout, lp, now); + if (!ret) + { + lp->bTimeout = false; + lp = active->head; + continue; + } + } + if( lp->pfnProcess ) + { + lp->pfnProcess( lp ); + } + + lp = active->head; + } + if( pfn ) + { + if( -1 == pfn( arg ) ) + { + break; + } + } + + } +} +void OnCoroutineEvent( stTimeoutItem_t * ap ) +{ + stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; + co_resume( co ); +} + + +stCoEpoll_t *AllocEpoll() +{ + stCoEpoll_t *ctx = (stCoEpoll_t*)calloc( 1,sizeof(stCoEpoll_t) ); + + ctx->iEpollFd = co_epoll_create( stCoEpoll_t::_EPOLL_SIZE ); + ctx->pTimeout = AllocTimeout( 60 * 1000 ); + + ctx->pstActiveList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) ); + ctx->pstTimeoutList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) ); + + + return ctx; +} + +void FreeEpoll( stCoEpoll_t *ctx ) +{ + if( ctx ) + { + free( ctx->pstActiveList ); + free( ctx->pstTimeoutList ); + FreeTimeout( ctx->pTimeout ); + co_epoll_res_free( ctx->result ); + } + free( ctx ); +} + +stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env ) +{ + return env->pCallStack[ env->iCallStackSize - 1 ]; +} +stCoRoutine_t *GetCurrThreadCo( ) +{ + stCoRoutineEnv_t *env = co_get_curr_thread_env(); + if( !env ) return 0; + return GetCurrCo(env); +} + + + +typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); +int co_poll_inner( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout, poll_pfn_t pollfunc) +{ + if (timeout == 0) + { + return pollfunc(fds, nfds, timeout); + } + if (timeout < 0) + { + timeout = INT_MAX; + } + int epfd = ctx->iEpollFd; + stCoRoutine_t* self = co_self(); + + //1.struct change + stPoll_t& arg = *((stPoll_t*)malloc(sizeof(stPoll_t))); + memset( &arg,0,sizeof(arg) ); + + arg.iEpollFd = epfd; + arg.fds = (pollfd*)calloc(nfds, sizeof(pollfd)); + arg.nfds = nfds; + + stPollItem_t arr[2]; + if( nfds < sizeof(arr) / sizeof(arr[0]) && !self->cIsShareStack) + { + arg.pPollItems = arr; + } + else + { + arg.pPollItems = (stPollItem_t*)malloc( nfds * sizeof( stPollItem_t ) ); + } + memset( arg.pPollItems,0,nfds * sizeof(stPollItem_t) ); + + arg.pfnProcess = OnPollProcessEvent; + arg.pArg = GetCurrCo( co_get_curr_thread_env() ); + + + //2. add epoll + for(nfds_t i=0;i -1 ) + { + ev.data.ptr = arg.pPollItems + i; + ev.events = PollEvent2Epoll( fds[i].events ); + + int ret = co_epoll_ctl( epfd,EPOLL_CTL_ADD, fds[i].fd, &ev ); + if (ret < 0 && errno == EPERM && nfds == 1 && pollfunc != NULL) + { + if( arg.pPollItems != arr ) + { + free( arg.pPollItems ); + arg.pPollItems = NULL; + } + free(arg.fds); + free(&arg); + return pollfunc(fds, nfds, timeout); + } + } + //if fail,the timeout would work + } + + //3.add timeout + + unsigned long long now = GetTickMS(); + arg.ullExpireTime = now + timeout; + int ret = AddTimeout( ctx->pTimeout,&arg,now ); + int iRaiseCnt = 0; + if( ret != 0 ) + { + co_log_err("CO_ERR: AddTimeout ret %d now %lld timeout %d arg.ullExpireTime %lld", + ret,now,timeout,arg.ullExpireTime); + errno = EINVAL; + iRaiseCnt = -1; + + } + else + { + co_yield_env( co_get_curr_thread_env() ); + iRaiseCnt = arg.iRaiseCnt; + } + + { + //clear epoll status and memory + RemoveFromLink( &arg ); + for(nfds_t i = 0;i < nfds;i++) + { + int fd = fds[i].fd; + if( fd > -1 ) + { + co_epoll_ctl( epfd,EPOLL_CTL_DEL,fd,&arg.pPollItems[i].stEvent ); + } + fds[i].revents = arg.fds[i].revents; + } + + + if( arg.pPollItems != arr ) + { + free( arg.pPollItems ); + arg.pPollItems = NULL; + } + + free(arg.fds); + free(&arg); + } + + return iRaiseCnt; +} + +int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms ) +{ + return co_poll_inner(ctx, fds, nfds, timeout_ms, NULL); +} + +void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev ) +{ + env->pEpoll = ev; +} +stCoEpoll_t *co_get_epoll_ct() +{ + if( !co_get_curr_thread_env() ) + { + co_init_curr_thread_env(); + } + return co_get_curr_thread_env()->pEpoll; +} +struct stHookPThreadSpec_t +{ + stCoRoutine_t *co; + void *value; + + enum + { + size = 1024 + }; +}; +void *co_getspecific(pthread_key_t key) +{ + stCoRoutine_t *co = GetCurrThreadCo(); + if( !co || co->cIsMain ) + { + return pthread_getspecific( key ); + } + return co->aSpec[ key ].value; +} +int co_setspecific(pthread_key_t key, const void *value) +{ + stCoRoutine_t *co = GetCurrThreadCo(); + if( !co || co->cIsMain ) + { + return pthread_setspecific( key,value ); + } + co->aSpec[ key ].value = (void*)value; + return 0; +} + + + +void co_disable_hook_sys() +{ + stCoRoutine_t *co = GetCurrThreadCo(); + if( co ) + { + co->cEnableSysHook = 0; + } +} +bool co_is_enable_sys_hook() +{ + stCoRoutine_t *co = GetCurrThreadCo(); + return ( co && co->cEnableSysHook ); +} + +stCoRoutine_t *co_self() +{ + return GetCurrThreadCo(); +} + +//co cond +struct stCoCond_t; +struct stCoCondItem_t +{ + stCoCondItem_t *pPrev; + stCoCondItem_t *pNext; + stCoCond_t *pLink; + + stTimeoutItem_t timeout; +}; +struct stCoCond_t +{ + stCoCondItem_t *head; + stCoCondItem_t *tail; +}; +static void OnSignalProcessEvent( stTimeoutItem_t * ap ) +{ + stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; + co_resume( co ); +} + +stCoCondItem_t *co_cond_pop( stCoCond_t *link ); +int co_cond_signal( stCoCond_t *si ) +{ + stCoCondItem_t * sp = co_cond_pop( si ); + if( !sp ) + { + return 0; + } + RemoveFromLink( &sp->timeout ); + + AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout ); + + return 0; +} +int co_cond_broadcast( stCoCond_t *si ) +{ + for(;;) + { + stCoCondItem_t * sp = co_cond_pop( si ); + if( !sp ) return 0; + + RemoveFromLink( &sp->timeout ); + + AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout ); + } + + return 0; +} + + +int co_cond_timedwait( stCoCond_t *link,int ms ) +{ + stCoCondItem_t* psi = (stCoCondItem_t*)calloc(1, sizeof(stCoCondItem_t)); + psi->timeout.pArg = GetCurrThreadCo(); + psi->timeout.pfnProcess = OnSignalProcessEvent; + + if( ms > 0 ) + { + unsigned long long now = GetTickMS(); + psi->timeout.ullExpireTime = now + ms; + + int ret = AddTimeout( co_get_curr_thread_env()->pEpoll->pTimeout,&psi->timeout,now ); + if( ret != 0 ) + { + free(psi); + return ret; + } + } + AddTail( link, psi); + + co_yield_ct(); + + + RemoveFromLink( psi ); + free(psi); + + return 0; +} +stCoCond_t *co_cond_alloc() +{ + return (stCoCond_t*)calloc( 1,sizeof(stCoCond_t) ); +} +int co_cond_free( stCoCond_t * cc ) +{ + free( cc ); + return 0; +} + + +stCoCondItem_t *co_cond_pop( stCoCond_t *link ) +{ + stCoCondItem_t *p = link->head; + if( p ) + { + PopHead( link ); + } + return p; +} diff --git a/trunk/3rdparty/libco/co_routine.h b/trunk/3rdparty/libco/co_routine.h new file mode 100644 index 000000000..d6f478928 --- /dev/null +++ b/trunk/3rdparty/libco/co_routine.h @@ -0,0 +1,93 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __CO_ROUTINE_H__ +#define __CO_ROUTINE_H__ + +#include +#include +#include + +//1.struct + +struct stCoRoutine_t; +struct stShareStack_t; + +struct stCoRoutineAttr_t +{ + int stack_size; + stShareStack_t* share_stack; + stCoRoutineAttr_t() + { + stack_size = 128 * 1024; + share_stack = NULL; + } +}__attribute__ ((packed)); + +struct stCoEpoll_t; +typedef int (*pfn_co_eventloop_t)(void *); +typedef void *(*pfn_co_routine_t)( void * ); + +//2.co_routine + +int co_create( stCoRoutine_t **co,const stCoRoutineAttr_t *attr,void *(*routine)(void*),void *arg ); +void co_resume( stCoRoutine_t *co ); +void co_yield( stCoRoutine_t *co ); +void co_yield_ct(); //ct = current thread +void co_release( stCoRoutine_t *co ); +void co_reset(stCoRoutine_t * co); + +stCoRoutine_t *co_self(); + +int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms ); +void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg ); + +//3.specific + +int co_setspecific( pthread_key_t key, const void *value ); +void * co_getspecific( pthread_key_t key ); + +//4.event + +stCoEpoll_t * co_get_epoll_ct(); //ct = current thread + +//5.hook syscall ( poll/read/write/recv/send/recvfrom/sendto ) + +void co_enable_hook_sys(); +void co_disable_hook_sys(); +bool co_is_enable_sys_hook(); + +//6.sync +struct stCoCond_t; + +stCoCond_t *co_cond_alloc(); +int co_cond_free( stCoCond_t * cc ); + +int co_cond_signal( stCoCond_t * ); +int co_cond_broadcast( stCoCond_t * ); +int co_cond_timedwait( stCoCond_t *,int timeout_ms ); + +//7.share stack +stShareStack_t* co_alloc_sharestack(int iCount, int iStackSize); + +//8.init envlist for hook get/set env +void co_set_env_list( const char *name[],size_t cnt); + +void co_log_err( const char *fmt,... ); +#endif + diff --git a/trunk/3rdparty/libco/co_routine_inner.h b/trunk/3rdparty/libco/co_routine_inner.h new file mode 100644 index 000000000..9d0e092de --- /dev/null +++ b/trunk/3rdparty/libco/co_routine_inner.h @@ -0,0 +1,111 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + +#ifndef __CO_ROUTINE_INNER_H__ + +#include "co_routine.h" +#include "coctx.h" +struct stCoRoutineEnv_t; +struct stCoSpec_t +{ + void *value; +}; + +struct stStackMem_t +{ + stCoRoutine_t* occupy_co; + int stack_size; + char* stack_bp; //stack_buffer + stack_size + char* stack_buffer; + +}; + +struct stShareStack_t +{ + unsigned int alloc_idx; + int stack_size; + int count; + stStackMem_t** stack_array; +}; + + + +struct stCoRoutine_t +{ + stCoRoutineEnv_t *env; + pfn_co_routine_t pfn; + void *arg; + coctx_t ctx; + + char cStart; + char cEnd; + char cIsMain; + char cEnableSysHook; + char cIsShareStack; + + void *pvEnv; + + //char sRunStack[ 1024 * 128 ]; + stStackMem_t* stack_mem; + + + //save satck buffer while confilct on same stack_buffer; + char* stack_sp; + unsigned int save_size; + char* save_buffer; + + stCoSpec_t aSpec[1024]; + +}; + + + +//1.env +void co_init_curr_thread_env(); +stCoRoutineEnv_t * co_get_curr_thread_env(); + +//2.coroutine +void co_free( stCoRoutine_t * co ); +void co_yield_env( stCoRoutineEnv_t *env ); + +//3.func + + + +//----------------------------------------------------------------------------------------------- + +struct stTimeout_t; +struct stTimeoutItem_t ; + +stTimeout_t *AllocTimeout( int iSize ); +void FreeTimeout( stTimeout_t *apTimeout ); +int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,uint64_t allNow ); + +struct stCoEpoll_t; +stCoEpoll_t * AllocEpoll(); +void FreeEpoll( stCoEpoll_t *ctx ); + +stCoRoutine_t * GetCurrThreadCo(); +void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev ); + +typedef void (*pfnCoRoutineFunc_t)(); + +#endif + +#define __CO_ROUTINE_INNER_H__ diff --git a/trunk/3rdparty/libco/co_routine_specific.h b/trunk/3rdparty/libco/co_routine_specific.h new file mode 100644 index 000000000..1b451caa5 --- /dev/null +++ b/trunk/3rdparty/libco/co_routine_specific.h @@ -0,0 +1,86 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once +#include +#include + +/* +invoke only once in the whole program +CoRoutineSetSpecificCallBack(CoRoutineGetSpecificFunc_t pfnGet,CoRoutineSetSpecificFunc_t pfnSet) + +struct MyData_t +{ + int iValue; + char szValue[100]; +}; +CO_ROUTINE_SPECIFIC( MyData_t,__routine ); + +int main() +{ + CoRoutineSetSpecificCallBack( co_getspecific,co_setspecific ); + + __routine->iValue = 10; + strcpy( __routine->szValue,"hello world" ); + + return 0; +} +*/ +extern int co_setspecific( pthread_key_t key, const void *value ); +extern void * co_getspecific( pthread_key_t key ); + +#define CO_ROUTINE_SPECIFIC( name,y ) \ +\ +static pthread_once_t _routine_once_##name = PTHREAD_ONCE_INIT; \ +static pthread_key_t _routine_key_##name;\ +static int _routine_init_##name = 0;\ +static void _routine_make_key_##name() \ +{\ + (void) pthread_key_create(&_routine_key_##name, NULL); \ +}\ +template \ +class clsRoutineData_routine_##name\ +{\ +public:\ + inline T *operator->()\ + {\ + if( !_routine_init_##name ) \ + {\ + pthread_once( &_routine_once_##name,_routine_make_key_##name );\ + _routine_init_##name = 1;\ + }\ + T* p = (T*)co_getspecific( _routine_key_##name );\ + if( !p )\ + {\ + p = (T*)calloc(1,sizeof( T ));\ + int ret = co_setspecific( _routine_key_##name,p) ;\ + if ( ret )\ + {\ + if ( p )\ + {\ + free(p);\ + p = NULL;\ + }\ + }\ + }\ + return p;\ + }\ +};\ +\ +static clsRoutineData_routine_##name y; + diff --git a/trunk/3rdparty/libco/coctx.cpp b/trunk/3rdparty/libco/coctx.cpp new file mode 100644 index 000000000..d5eeed148 --- /dev/null +++ b/trunk/3rdparty/libco/coctx.cpp @@ -0,0 +1,132 @@ +/* +* Tencent is pleased to support the open source community by making Libco +available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "coctx.h" +#include +#include + +#define ESP 0 +#define EIP 1 +#define EAX 2 +#define ECX 3 +// ----------- +#define RSP 0 +#define RIP 1 +#define RBX 2 +#define RDI 3 +#define RSI 4 + +#define RBP 5 +#define R12 6 +#define R13 7 +#define R14 8 +#define R15 9 +#define RDX 10 +#define RCX 11 +#define R8 12 +#define R9 13 + +//----- -------- +// 32 bit +// | regs[0]: ret | +// | regs[1]: ebx | +// | regs[2]: ecx | +// | regs[3]: edx | +// | regs[4]: edi | +// | regs[5]: esi | +// | regs[6]: ebp | +// | regs[7]: eax | = esp +enum { + kEIP = 0, + kEBP = 6, + kESP = 7, +}; + +//------------- +// 64 bit +// low | regs[0]: r15 | +// | regs[1]: r14 | +// | regs[2]: r13 | +// | regs[3]: r12 | +// | regs[4]: r9 | +// | regs[5]: r8 | +// | regs[6]: rbp | +// | regs[7]: rdi | +// | regs[8]: rsi | +// | regs[9]: ret | //ret func addr +// | regs[10]: rdx | +// | regs[11]: rcx | +// | regs[12]: rbx | +// hig | regs[13]: rsp | +enum { + kRDI = 7, + kRSI = 8, + kRETAddr = 9, + kRSP = 13, +}; + +// 64 bit +extern "C" { +extern void coctx_swap(coctx_t*, coctx_t*) asm("coctx_swap"); +}; +#if defined(__i386__) +int coctx_init(coctx_t* ctx) { + memset(ctx, 0, sizeof(*ctx)); + return 0; +} +int coctx_make(coctx_t* ctx, coctx_pfn_t pfn, const void* s, const void* s1) { + // make room for coctx_param + char* sp = ctx->ss_sp + ctx->ss_size - sizeof(coctx_param_t); + sp = (char*)((unsigned long)sp & -16L); + + coctx_param_t* param = (coctx_param_t*)sp; + void** ret_addr = (void**)(sp - sizeof(void*) * 2); + *ret_addr = (void*)pfn; + param->s1 = s; + param->s2 = s1; + + memset(ctx->regs, 0, sizeof(ctx->regs)); + + ctx->regs[kESP] = (char*)(sp) - sizeof(void*) * 2; + return 0; +} +#elif defined(__x86_64__) +int coctx_make(coctx_t* ctx, coctx_pfn_t pfn, const void* s, const void* s1) { + char* sp = ctx->ss_sp + ctx->ss_size - sizeof(void*); + sp = (char*)((unsigned long)sp & -16LL); + + memset(ctx->regs, 0, sizeof(ctx->regs)); + void** ret_addr = (void**)(sp); + *ret_addr = (void*)pfn; + + ctx->regs[kRSP] = sp; + + ctx->regs[kRETAddr] = (char*)pfn; + + ctx->regs[kRDI] = (char*)s; + ctx->regs[kRSI] = (char*)s1; + return 0; +} + +int coctx_init(coctx_t* ctx) { + memset(ctx, 0, sizeof(*ctx)); + return 0; +} + +#endif diff --git a/trunk/3rdparty/libco/coctx.h b/trunk/3rdparty/libco/coctx.h new file mode 100644 index 000000000..c1fdfa9da --- /dev/null +++ b/trunk/3rdparty/libco/coctx.h @@ -0,0 +1,42 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __CO_CTX_H__ +#define __CO_CTX_H__ +#include +typedef void* (*coctx_pfn_t)( void* s, void* s2 ); +struct coctx_param_t +{ + const void *s1; + const void *s2; +}; +struct coctx_t +{ +#if defined(__i386__) + void *regs[ 8 ]; +#else + void *regs[ 14 ]; +#endif + size_t ss_size; + char *ss_sp; + +}; + +int coctx_init( coctx_t *ctx ); +int coctx_make( coctx_t *ctx,coctx_pfn_t pfn,const void *s,const void *s1 ); +#endif diff --git a/trunk/3rdparty/libco/coctx_swap.S b/trunk/3rdparty/libco/coctx_swap.S new file mode 100644 index 000000000..0e4ce1c92 --- /dev/null +++ b/trunk/3rdparty/libco/coctx_swap.S @@ -0,0 +1,83 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +.globl coctx_swap +#if !defined( __APPLE__ ) +.type coctx_swap, @function +#endif +coctx_swap: + +#if defined(__i386__) + movl 4(%esp), %eax + movl %esp, 28(%eax) + movl %ebp, 24(%eax) + movl %esi, 20(%eax) + movl %edi, 16(%eax) + movl %edx, 12(%eax) + movl %ecx, 8(%eax) + movl %ebx, 4(%eax) + + + movl 8(%esp), %eax + movl 4(%eax), %ebx + movl 8(%eax), %ecx + movl 12(%eax), %edx + movl 16(%eax), %edi + movl 20(%eax), %esi + movl 24(%eax), %ebp + movl 28(%eax), %esp + + ret + +#elif defined(__x86_64__) + leaq (%rsp),%rax + movq %rax, 104(%rdi) + movq %rbx, 96(%rdi) + movq %rcx, 88(%rdi) + movq %rdx, 80(%rdi) + movq 0(%rax), %rax + movq %rax, 72(%rdi) + movq %rsi, 64(%rdi) + movq %rdi, 56(%rdi) + movq %rbp, 48(%rdi) + movq %r8, 40(%rdi) + movq %r9, 32(%rdi) + movq %r12, 24(%rdi) + movq %r13, 16(%rdi) + movq %r14, 8(%rdi) + movq %r15, (%rdi) + xorq %rax, %rax + + movq 48(%rsi), %rbp + movq 104(%rsi), %rsp + movq (%rsi), %r15 + movq 8(%rsi), %r14 + movq 16(%rsi), %r13 + movq 24(%rsi), %r12 + movq 32(%rsi), %r9 + movq 40(%rsi), %r8 + movq 56(%rsi), %rdi + movq 80(%rsi), %rdx + movq 88(%rsi), %rcx + movq 96(%rsi), %rbx + leaq 8(%rsp), %rsp + pushq 72(%rsi) + + movq 64(%rsi), %rsi + ret +#endif diff --git a/trunk/3rdparty/libco/example_closure.cpp b/trunk/3rdparty/libco/example_closure.cpp new file mode 100644 index 000000000..c5bae1cc4 --- /dev/null +++ b/trunk/3rdparty/libco/example_closure.cpp @@ -0,0 +1,91 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "co_closure.h" +#include +#include +#include +#include +#include +using namespace std; + +static void *thread_func( void * arg ) +{ + stCoClosure_t *p = (stCoClosure_t*) arg; + p->exec(); + return 0; +} +static void batch_exec( vector &v ) +{ + vector ths; + for( size_t i=0;i v; + + pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + + int total = 100; + vector v2; + co_ref( ref,total,v2,m); + for(int i=0;i<10;i++) + { + co_func( f,ref,i ) + { + printf("ref.total %d i %d\n",ref.total,i ); + //lock + pthread_mutex_lock(&ref.m); + ref.v2.push_back( i ); + pthread_mutex_unlock(&ref.m); + //unlock + } + co_func_end; + v.push_back( new f( ref,i ) ); + } + for(int i=0;i<2;i++) + { + co_func( f2,i ) + { + printf("i: %d\n",i); + for(int j=0;j<2;j++) + { + usleep( 1000 ); + printf("i %d j %d\n",i,j); + } + } + co_func_end; + v.push_back( new f2( i ) ); + } + + batch_exec( v ); + printf("done\n"); + + return 0; +} + + diff --git a/trunk/3rdparty/libco/example_cond.cpp b/trunk/3rdparty/libco/example_cond.cpp new file mode 100644 index 000000000..526256807 --- /dev/null +++ b/trunk/3rdparty/libco/example_cond.cpp @@ -0,0 +1,83 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include +#include +#include "co_routine.h" +using namespace std; +struct stTask_t +{ + int id; +}; +struct stEnv_t +{ + stCoCond_t* cond; + queue task_queue; +}; +void* Producer(void* args) +{ + co_enable_hook_sys(); + stEnv_t* env= (stEnv_t*)args; + int id = 0; + while (true) + { + stTask_t* task = (stTask_t*)calloc(1, sizeof(stTask_t)); + task->id = id++; + env->task_queue.push(task); + printf("%s:%d produce task %d\n", __func__, __LINE__, task->id); + co_cond_signal(env->cond); + poll(NULL, 0, 1000); + } + return NULL; +} +void* Consumer(void* args) +{ + co_enable_hook_sys(); + stEnv_t* env = (stEnv_t*)args; + while (true) + { + if (env->task_queue.empty()) + { + co_cond_timedwait(env->cond, -1); + continue; + } + stTask_t* task = env->task_queue.front(); + env->task_queue.pop(); + printf("%s:%d consume task %d\n", __func__, __LINE__, task->id); + free(task); + } + return NULL; +} +int main() +{ + stEnv_t* env = new stEnv_t; + env->cond = co_cond_alloc(); + + stCoRoutine_t* consumer_routine; + co_create(&consumer_routine, NULL, Consumer, env); + co_resume(consumer_routine); + + stCoRoutine_t* producer_routine; + co_create(&producer_routine, NULL, Producer, env); + co_resume(producer_routine); + + co_eventloop(co_get_epoll_ct(), NULL, NULL); + return 0; +} diff --git a/trunk/3rdparty/libco/example_copystack.cpp b/trunk/3rdparty/libco/example_copystack.cpp new file mode 100644 index 000000000..92062a6ea --- /dev/null +++ b/trunk/3rdparty/libco/example_copystack.cpp @@ -0,0 +1,61 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include "coctx.h" +#include "co_routine.h" +#include "co_routine_inner.h" + +void* RoutineFunc(void* args) +{ + co_enable_hook_sys(); + int* routineid = (int*)args; + while (true) + { + char sBuff[128]; + sprintf(sBuff, "from routineid %d stack addr %p\n", *routineid, sBuff); + + printf("%s", sBuff); + poll(NULL, 0, 1000); //sleep 1s + } + return NULL; +} + +int main() +{ + stShareStack_t* share_stack= co_alloc_sharestack(1, 1024 * 128); + stCoRoutineAttr_t attr; + attr.stack_size = 0; + attr.share_stack = share_stack; + + stCoRoutine_t* co[2]; + int routineid[2]; + for (int i = 0; i < 2; i++) + { + routineid[i] = i; + co_create(&co[i], &attr, RoutineFunc, routineid + i); + co_resume(co[i]); + } + co_eventloop(co_get_epoll_ct(), NULL, NULL); + return 0; +} diff --git a/trunk/3rdparty/libco/example_echocli.cpp b/trunk/3rdparty/libco/example_echocli.cpp new file mode 100644 index 000000000..083c1e700 --- /dev/null +++ b/trunk/3rdparty/libco/example_echocli.cpp @@ -0,0 +1,218 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "co_routine.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +struct stEndPoint +{ + char *ip; + unsigned short int port; +}; + +static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) +{ + bzero(&addr,sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(shPort); + int nIP = 0; + if( !pszIP || '\0' == *pszIP + || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") + || 0 == strcmp(pszIP,"*") + ) + { + nIP = htonl(INADDR_ANY); + } + else + { + nIP = inet_addr(pszIP); + } + addr.sin_addr.s_addr = nIP; + +} + +static int iSuccCnt = 0; +static int iFailCnt = 0; +static int iTime = 0; + +void AddSuccCnt() +{ + int now = time(NULL); + if (now >iTime) + { + printf("time %d Succ Cnt %d Fail Cnt %d\n", iTime, iSuccCnt, iFailCnt); + iTime = now; + iSuccCnt = 0; + iFailCnt = 0; + } + else + { + iSuccCnt++; + } +} +void AddFailCnt() +{ + int now = time(NULL); + if (now >iTime) + { + printf("time %d Succ Cnt %d Fail Cnt %d\n", iTime, iSuccCnt, iFailCnt); + iTime = now; + iSuccCnt = 0; + iFailCnt = 0; + } + else + { + iFailCnt++; + } +} + +static void *readwrite_routine( void *arg ) +{ + + co_enable_hook_sys(); + + stEndPoint *endpoint = (stEndPoint *)arg; + char str[8]="sarlmol"; + char buf[ 1024 * 16 ]; + int fd = -1; + int ret = 0; + for(;;) + { + if ( fd < 0 ) + { + fd = socket(PF_INET, SOCK_STREAM, 0); + struct sockaddr_in addr; + SetAddr(endpoint->ip, endpoint->port, addr); + ret = connect(fd,(struct sockaddr*)&addr,sizeof(addr)); + + if ( errno == EALREADY || errno == EINPROGRESS ) + { + struct pollfd pf = { 0 }; + pf.fd = fd; + pf.events = (POLLOUT|POLLERR|POLLHUP); + co_poll( co_get_epoll_ct(),&pf,1,200); + //check connect + int error = 0; + uint32_t socklen = sizeof(error); + errno = 0; + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR,(void *)&error, &socklen); + if ( ret == -1 ) + { + //printf("getsockopt ERROR ret %d %d:%s\n", ret, errno, strerror(errno)); + close(fd); + fd = -1; + AddFailCnt(); + continue; + } + if ( error ) + { + errno = error; + //printf("connect ERROR ret %d %d:%s\n", error, errno, strerror(errno)); + close(fd); + fd = -1; + AddFailCnt(); + continue; + } + } + + } + + ret = write( fd,str, 8); + if ( ret > 0 ) + { + ret = read( fd,buf, sizeof(buf) ); + if ( ret <= 0 ) + { + //printf("co %p read ret %d errno %d (%s)\n", + // co_self(), ret,errno,strerror(errno)); + close(fd); + fd = -1; + AddFailCnt(); + } + else + { + //printf("echo %s fd %d\n", buf,fd); + AddSuccCnt(); + } + } + else + { + //printf("co %p write ret %d errno %d (%s)\n", + // co_self(), ret,errno,strerror(errno)); + close(fd); + fd = -1; + AddFailCnt(); + } + } + return 0; +} + +int main(int argc,char *argv[]) +{ + stEndPoint endpoint; + endpoint.ip = argv[1]; + endpoint.port = atoi(argv[2]); + int cnt = atoi( argv[3] ); + int proccnt = atoi( argv[4] ); + + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sigaction( SIGPIPE, &sa, NULL ); + + for(int k=0;k 0 ) + { + continue; + } + else if( pid < 0 ) + { + break; + } + for(int i=0;i +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include +#include +#include +#endif + +using namespace std; +struct task_t +{ + stCoRoutine_t *co; + int fd; +}; + +static stack g_readwrite; +static int g_listen_fd = -1; +static int SetNonBlock(int iSock) +{ + int iFlags; + + iFlags = fcntl(iSock, F_GETFL, 0); + iFlags |= O_NONBLOCK; + iFlags |= O_NDELAY; + int ret = fcntl(iSock, F_SETFL, iFlags); + return ret; +} + +static void *readwrite_routine( void *arg ) +{ + + co_enable_hook_sys(); + + task_t *co = (task_t*)arg; + char buf[ 1024 * 16 ]; + for(;;) + { + if( -1 == co->fd ) + { + g_readwrite.push( co ); + co_yield_ct(); + continue; + } + + int fd = co->fd; + co->fd = -1; + + for(;;) + { + struct pollfd pf = { 0 }; + pf.fd = fd; + pf.events = (POLLIN|POLLERR|POLLHUP); + co_poll( co_get_epoll_ct(),&pf,1,1000); + + int ret = read( fd,buf,sizeof(buf) ); + if( ret > 0 ) + { + ret = write( fd,buf,ret ); + } + if( ret > 0 || ( -1 == ret && EAGAIN == errno ) ) + { + continue; + } + close( fd ); + break; + } + + } + return 0; +} +int co_accept(int fd, struct sockaddr *addr, socklen_t *len ); +static void *accept_routine( void * ) +{ + co_enable_hook_sys(); + printf("accept_routine\n"); + fflush(stdout); + for(;;) + { + //printf("pid %ld g_readwrite.size %ld\n",getpid(),g_readwrite.size()); + if( g_readwrite.empty() ) + { + printf("empty\n"); //sleep + struct pollfd pf = { 0 }; + pf.fd = -1; + poll( &pf,1,1000); + + continue; + + } + struct sockaddr_in addr; //maybe sockaddr_un; + memset( &addr,0,sizeof(addr) ); + socklen_t len = sizeof(addr); + + int fd = co_accept(g_listen_fd, (struct sockaddr *)&addr, &len); + if( fd < 0 ) + { + struct pollfd pf = { 0 }; + pf.fd = g_listen_fd; + pf.events = (POLLIN|POLLERR|POLLHUP); + co_poll( co_get_epoll_ct(),&pf,1,1000 ); + continue; + } + if( g_readwrite.empty() ) + { + close( fd ); + continue; + } + SetNonBlock( fd ); + task_t *co = g_readwrite.top(); + co->fd = fd; + g_readwrite.pop(); + co_resume( co->co ); + } + return 0; +} + +static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) +{ + bzero(&addr,sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(shPort); + int nIP = 0; + if( !pszIP || '\0' == *pszIP + || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") + || 0 == strcmp(pszIP,"*") + ) + { + nIP = htonl(INADDR_ANY); + } + else + { + nIP = inet_addr(pszIP); + } + addr.sin_addr.s_addr = nIP; + +} + +static int CreateTcpSocket(const unsigned short shPort /* = 0 */,const char *pszIP /* = "*" */,bool bReuse /* = false */) +{ + int fd = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP); + if( fd >= 0 ) + { + if(shPort != 0) + { + if(bReuse) + { + int nReuseAddr = 1; + setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&nReuseAddr,sizeof(nReuseAddr)); + } + struct sockaddr_in addr ; + SetAddr(pszIP,shPort,addr); + int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); + if( ret != 0) + { + close(fd); + return -1; + } + } + } + return fd; +} + + +int main(int argc,char *argv[]) +{ + if(argc<5){ + printf("Usage:\n" + "example_echosvr [IP] [PORT] [TASK_COUNT] [PROCESS_COUNT]\n" + "example_echosvr [IP] [PORT] [TASK_COUNT] [PROCESS_COUNT] -d # daemonize mode\n"); + return -1; + } + const char *ip = argv[1]; + int port = atoi( argv[2] ); + int cnt = atoi( argv[3] ); + int proccnt = atoi( argv[4] ); + bool deamonize = argc >= 6 && strcmp(argv[5], "-d") == 0; + + g_listen_fd = CreateTcpSocket( port,ip,true ); + listen( g_listen_fd,1024 ); + if(g_listen_fd==-1){ + printf("Port %d is in use\n", port); + return -1; + } + printf("listen %d %s:%d\n",g_listen_fd,ip,port); + + SetNonBlock( g_listen_fd ); + + for(int k=0;k 0 ) + { + continue; + } + else if( pid < 0 ) + { + break; + } + for(int i=0;ifd = -1; + + co_create( &(task->co),NULL,readwrite_routine,task ); + co_resume( task->co ); + + } + stCoRoutine_t *accept_co = NULL; + co_create( &accept_co,NULL,accept_routine,0 ); + co_resume( accept_co ); + + co_eventloop( co_get_epoll_ct(),0,0 ); + + exit(0); + } + if(!deamonize) wait(NULL); + return 0; +} + diff --git a/trunk/3rdparty/libco/example_poll.cpp b/trunk/3rdparty/libco/example_poll.cpp new file mode 100644 index 000000000..eb92f219e --- /dev/null +++ b/trunk/3rdparty/libco/example_poll.cpp @@ -0,0 +1,211 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "co_routine.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include +#endif + +using namespace std; + +struct task_t +{ + stCoRoutine_t *co; + int fd; + struct sockaddr_in addr; +}; + +static int SetNonBlock(int iSock) +{ + int iFlags; + + iFlags = fcntl(iSock, F_GETFL, 0); + iFlags |= O_NONBLOCK; + iFlags |= O_NDELAY; + int ret = fcntl(iSock, F_SETFL, iFlags); + return ret; +} + + + +static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) +{ + bzero(&addr,sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(shPort); + int nIP = 0; + if( !pszIP || '\0' == *pszIP + || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") + || 0 == strcmp(pszIP,"*") + ) + { + nIP = htonl(INADDR_ANY); + } + else + { + nIP = inet_addr(pszIP); + } + addr.sin_addr.s_addr = nIP; + +} + +static int CreateTcpSocket(const unsigned short shPort = 0 ,const char *pszIP = "*" ,bool bReuse = false ) +{ + int fd = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP); + if( fd >= 0 ) + { + if(shPort != 0) + { + if(bReuse) + { + int nReuseAddr = 1; + setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&nReuseAddr,sizeof(nReuseAddr)); + } + struct sockaddr_in addr ; + SetAddr(pszIP,shPort,addr); + int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); + if( ret != 0) + { + close(fd); + return -1; + } + } + } + return fd; +} + +static void *poll_routine( void *arg ) +{ + co_enable_hook_sys(); + + vector &v = *(vector*)arg; + for(size_t i=0;i setRaiseFds; + size_t iWaitCnt = v.size(); + for(;;) + { + int ret = poll( pf,iWaitCnt,1000 ); + printf("co %p poll wait %ld ret %d\n", + co_self(),iWaitCnt,ret); + for(int i=0;i<(int)iWaitCnt;i++) + { + printf("co %p fire fd %d revents 0x%X POLLOUT 0x%X POLLERR 0x%X POLLHUP 0x%X\n", + co_self(), + pf[i].fd, + pf[i].revents, + POLLOUT, + POLLERR, + POLLHUP + ); + setRaiseFds.insert( pf[i].fd ); + } + if( setRaiseFds.size() == v.size()) + { + break; + } + if( ret <= 0 ) + { + break; + } + + iWaitCnt = 0; + for(size_t i=0;i v; + for(int i=1;i v2 = v; + poll_routine( &v2 ); + printf("--------------------- routine -------------------\n"); + + for(int i=0;i<10;i++) + { + stCoRoutine_t *co = 0; + vector *v2 = new vector(); + *v2 = v; + co_create( &co,NULL,poll_routine,v2 ); + printf("routine i %d\n",i); + co_resume( co ); + } + + co_eventloop( co_get_epoll_ct(),0,0 ); + + return 0; +} +//./example_poll 127.0.0.1 12365 127.0.0.1 12222 192.168.1.1 1000 192.168.1.2 1111 + diff --git a/trunk/3rdparty/libco/example_setenv.cpp b/trunk/3rdparty/libco/example_setenv.cpp new file mode 100644 index 000000000..520cfb633 --- /dev/null +++ b/trunk/3rdparty/libco/example_setenv.cpp @@ -0,0 +1,89 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include "co_routine.h" + +const char* CGI_ENV_HOOK_LIST [] = +{ + "CGINAME", +}; +struct stRoutineArgs_t +{ + int iRoutineID; +}; +void SetAndGetEnv(int iRoutineID) +{ + printf("routineid %d begin\n", iRoutineID); + + //use poll as sleep + poll(NULL, 0, 500); + + char sBuf[128]; + sprintf(sBuf, "cgi_routine_%d", iRoutineID); + int ret = setenv("CGINAME", sBuf, 1); + if (ret) + { + printf("%s:%d set env err ret %d errno %d %s\n", __func__, __LINE__, + ret, errno, strerror(errno)); + return; + } + printf("routineid %d set env CGINAME %s\n", iRoutineID, sBuf); + + poll(NULL, 0, 500); + + char* env = getenv("CGINAME"); + if (!env) + { + printf("%s:%d get env err errno %d %s\n", __func__, __LINE__, + errno, strerror(errno)); + return; + } + printf("routineid %d get env CGINAME %s\n", iRoutineID, env); +} + +void* RoutineFunc(void* args) +{ + co_enable_hook_sys(); + + stRoutineArgs_t* g = (stRoutineArgs_t*)args; + + SetAndGetEnv(g->iRoutineID); + return NULL; +} + +int main(int argc, char* argv[]) +{ + co_set_env_list(CGI_ENV_HOOK_LIST, sizeof(CGI_ENV_HOOK_LIST) / sizeof(char*)); + stRoutineArgs_t args[3]; + for (int i = 0; i < 3; i++) + { + stCoRoutine_t* co = NULL; + args[i].iRoutineID = i; + co_create(&co, NULL, RoutineFunc, &args[i]); + co_resume(co); + } + co_eventloop(co_get_epoll_ct(), NULL, NULL); + return 0; +} + diff --git a/trunk/3rdparty/libco/example_specific.cpp b/trunk/3rdparty/libco/example_specific.cpp new file mode 100644 index 000000000..5d20a8b1a --- /dev/null +++ b/trunk/3rdparty/libco/example_specific.cpp @@ -0,0 +1,61 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "co_routine_specific.h" +#include "co_routine.h" +#include +#include +#include +#include +using namespace std; +struct stRoutineArgs_t +{ + stCoRoutine_t* co; + int routine_id; +}; +struct stRoutineSpecificData_t +{ + int idx; +}; + +CO_ROUTINE_SPECIFIC(stRoutineSpecificData_t, __routine); + +void* RoutineFunc(void* args) +{ + co_enable_hook_sys(); + stRoutineArgs_t* routine_args = (stRoutineArgs_t*)args; + __routine->idx = routine_args->routine_id; + while (true) + { + printf("%s:%d routine specific data idx %d\n", __func__, __LINE__, __routine->idx); + poll(NULL, 0, 1000); + } + return NULL; +} +int main() +{ + stRoutineArgs_t args[10]; + for (int i = 0; i < 10; i++) + { + args[i].routine_id = i; + co_create(&args[i].co, NULL, RoutineFunc, (void*)&args[i]); + co_resume(args[i].co); + } + co_eventloop(co_get_epoll_ct(), NULL, NULL); + return 0; +} diff --git a/trunk/3rdparty/libco/example_thread.cpp b/trunk/3rdparty/libco/example_thread.cpp new file mode 100644 index 000000000..401771071 --- /dev/null +++ b/trunk/3rdparty/libco/example_thread.cpp @@ -0,0 +1,56 @@ +/* +* Tencent is pleased to support the open source community by making Libco available. + +* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + + +#include "co_routine.h" +#include "co_routine_inner.h" + +#include +#include +#include +#include +#include + +int loop(void *) +{ + return 0; +} +static void *routine_func( void * ) +{ + stCoEpoll_t * ev = co_get_epoll_ct(); //ct = current thread + co_eventloop( ev,loop,0 ); + return 0; +} +int main(int argc,char *argv[]) +{ + int cnt = atoi( argv[1] ); + + pthread_t tid[ cnt ]; + for(int i=0;i -# -# To start with more than the default 64 initial pollfd slots -# (but the table grows dynamically anyway): -# DEFINES += -DST_MIN_POLLFDS_SIZE= -# -# Note that you can also add these defines by specifying them as -# make/gmake arguments (without editing this Makefile). For example: -# -# make EXTRA_CFLAGS=-DUSE_POLL -# -# (replace make with gmake if needed). -# -# You can also modify the default selection of an alternative event -# notification mechanism. E.g., to enable kqueue(2) support (if it's not -# enabled by default): -# -# gmake EXTRA_CFLAGS=-DMD_HAVE_KQUEUE -# -# or to disable default epoll(4) support: -# -# make EXTRA_CFLAGS=-UMD_HAVE_EPOLL -# -########################## - -CFLAGS += $(DEFINES) $(OTHER_FLAGS) $(EXTRA_CFLAGS) - -OBJS = $(TARGETDIR)/sched.o \ - $(TARGETDIR)/stk.o \ - $(TARGETDIR)/sync.o \ - $(TARGETDIR)/key.o \ - $(TARGETDIR)/io.o \ - $(TARGETDIR)/event.o -OBJS += $(EXTRA_OBJS) -HEADER = $(TARGETDIR)/st.h -SLIBRARY = $(TARGETDIR)/libst.a -DLIBRARY = $(TARGETDIR)/libst.$(DSO_SUFFIX).$(VERSION) -EXAMPLES = examples - -LINKNAME = libst.$(DSO_SUFFIX) -SONAME = libst.$(DSO_SUFFIX).$(MAJOR) -FULLNAME = libst.$(DSO_SUFFIX).$(VERSION) - -ifeq ($(OS), CYGWIN) -SONAME = cygst.$(DSO_SUFFIX) -SLIBRARY = $(TARGETDIR)/libst.dll.a -DLIBRARY = $(TARGETDIR)/$(SONAME) -LINKNAME = -# examples directory does not compile under cygwin -EXAMPLES = -endif - -# for SRS -# disable examples for ubuntu crossbuild failed. -# @see https://github.com/winlinvip/simple-rtmp-server/issues/308 -EXAMPLES = - -ifeq ($(OS), DARWIN) -LINKNAME = libst.$(DSO_SUFFIX) -SONAME = libst.$(MAJOR).$(DSO_SUFFIX) -FULLNAME = libst.$(VERSION).$(DSO_SUFFIX) -endif - -ifeq ($(STATIC_ONLY), yes) -LIBRARIES = $(SLIBRARY) -else -LIBRARIES = $(SLIBRARY) $(DLIBRARY) -endif - -ifeq ($(OS),) -ST_ALL = unknown -else -ST_ALL = $(TARGETDIR) $(LIBRARIES) $(HEADER) $(EXAMPLES) $(DESC) -endif - -all: $(ST_ALL) - -unknown: - @echo - @echo "Please specify one of the following targets:" - @echo - @for target in $(TARGETS); do echo $$target; done - @echo - -st.pc: st.pc.in - sed "s/@VERSION@/${VERSION}/g" < $< > $@ - -$(TARGETDIR): - if [ ! -d $(TARGETDIR) ]; then mkdir $(TARGETDIR); fi - -$(SLIBRARY): $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - $(RANLIB) $@ - rm -f obj; $(LN) $(LNFLAGS) $(TARGETDIR) obj - -$(DLIBRARY): $(OBJS:%.o=%-pic.o) - $(LD) $(LDFLAGS) $^ -o $@ - if test "$(LINKNAME)"; then \ - cd $(TARGETDIR); \ - rm -f $(SONAME) $(LINKNAME); \ - $(LN) $(LNFLAGS) $(FULLNAME) $(SONAME); \ - $(LN) $(LNFLAGS) $(FULLNAME) $(LINKNAME); \ - fi - -$(HEADER): public.h - rm -f $@ - cp public.h $@ - -$(TARGETDIR)/md.o: md.S - $(CC) $(CFLAGS) -c $< -o $@ - -$(TARGETDIR)/%.o: %.c common.h md.h - $(CC) $(CFLAGS) -c $< -o $@ - -examples:: - @echo Making $@ - @cd $@; $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" OS="$(OS)" TARGETDIR="$(TARGETDIR)" - -clean: - rm -rf *_OPT *_DBG obj st.pc - -########################## -# Pattern rules: - -ifneq ($(SFLAGS),) -# Compile with shared library options if it's a C file -$(TARGETDIR)/%-pic.o: %.c common.h md.h - $(CC) $(CFLAGS) $(SFLAGS) -c $< -o $@ -endif - -# Compile assembly as normal or C as normal if no SFLAGS -%-pic.o: %.o - rm -f $@; $(LN) $(LNFLAGS) $(. Install them with: - # rpm -i libst*.rpm -Requires GNU automake and rpm 3.0.3 or later. - -Debian users: - If you run potato, please upgrade to woody. - If you run woody, "apt-get install libst-dev" will get you v1.3. - If you run testing/unstable, you will get the newest available version. - If you *must* have the newest libst in woody, you may follow these - not-recommended instructions: - 1. Add "deb-src unstable main" to your - /etc/apt/sources.list - 2. apt-get update - 3. apt-get source st - 4. cd st-1.4 (or whatever version you got) - 5. debuild - 6. dpkg -i ../*.deb - -If your application uses autoconf to search for dependencies and you -want to search for a given version of libst, you can simply add - PKG_CHECK_MODULES(MYAPP, st >= 1.3 mumble >= 0.2.23) -to your configure.ac/in. This will define @MYAPP_LIBS@ and -@MYAPP_CFLAGS@ which you may then use in your Makefile.am/in files to -link against mumble and st. - - -LICENSE - -The State Threads library is a derivative of the Netscape Portable -Runtime library (NSPR). All source code in this directory is -distributed under the terms of the Mozilla Public License (MPL) version -1.1 or the GNU General Public License (GPL) version 2 or later. For -more information about these licenses please see -http://www.mozilla.org/MPL/ and http://www.gnu.org/copyleft/. - -All source code in the "examples" directory is distributed under the BSD -style license. - - -PLATFORMS - -Please see the "docs/notes.html" file for the list of currently -supported platforms. - - -DEBUGGER SUPPORT - -It's almost impossible to print SP and PC in a portable way. The only -way to see thread's stack platform-independently is to actually jump to -the saved context. That's what the _st_iterate_threads() function does. -Do the following to iterate over all threads: - -- set the _st_iterate_threads_flag to 1 in debugger -- set breakpoint at the _st_show_thread_stack() function - (which does nothing) -- call the _st_iterate_threads() function which jumps to the - next thread -- at each break you can explore thread's stack -- continue -- when iteration is complete, you return to the original - point (you can see thread id and a message as arguments of - the _st_show_thread_stack() function). - -You can call _st_iterate_threads() in three ways: - -- Insert it into your source code at the point you want to - go over threads. -- Just run application and this function will be called at - the first context switch. -- Call it directly from the debugger at any point. - -This works with gdb and dbx. - -Example using gdb: - -(gdb) set _st_iterate_threads_flag = 1 -(gdb) b _st_show_thread_stack -... -(gdb) call _st_iterate_threads() -... -(gdb) bt -... -(gdb) c -... -(gdb) bt -... -(gdb) c -... -and so on... - -_st_iterate_threads_flag will be set to 0 automatically -after iteration is over or you can set it to 0 at any time -to stop iteration. - -Sometimes gdb complains about SIGSEGV when you call a function -directly at gdb command-line. It can be ignored -- just call the -same function right away again, it works just fine. For example: - -(gdb) set _st_iterate_threads_flag = 1 -(gdb) b _st_show_thread_stack -Breakpoint 1 at 0x809bbbb: file sched.c, line 856. -(gdb) call _st_iterate_threads() -Program received signal SIGSEGV, Segmentation fault. -.... -(gdb) # just call the function again: -(gdb) call _st_iterate_threads() -Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 -"Iteration started") at sched.c:856 -856 } -.... - -You can use simple gdb command-line scripting to display -all threads and their stack traces at once: - -(gdb) while _st_iterate_threads_flag - >bt - >c - >end -.... - -Another script to stop at the thread with the specific thread id -(e.g., 0x40252ee4): - -(gdb) # set the flag again: -(gdb) set _st_iterate_threads_flag = 1 -(gdb) call _st_iterate_threads() -Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 -"Iteration started") at sched.c:856 -856 } -.... -(gdb) while thread != 0x40252ee4 - >c - >end -.... -.... -Breakpoint 1, _st_show_thread_stack (thread=0x40252ee4, messg=0x0) at -sched.c:856 -856 } -(gdb) bt -.... -(gdb) # don't want to continue iteration, unset the flag: -(gdb) set _st_iterate_threads_flag = 0 -(gdb) c -Continuing. -Breakpoint 1, _st_show_thread_stack (thread=0x0, messg=0x80ae78e "Iteration -completed") - at sched.c:856 -856 } -(gdb) c -Continuing. -(gdb) return -Make selected stack frame return now? (y or n) y -#0 0x4011254e in __select () - from /lib/libc.so.6 -(gdb) detach - - -CHANGE LOG - -Changes from 1.8 to 1.9. ------------------------- -o Support 32-bit and 64-bit Intel Macs. - -o Added ST_VERSION string, and ST_VERSION_MAJOR and ST_VERSION_MINOR - [bug 1796801]. - -o Fixed some compiler warnings, based on a patch from Brian Wellington - [bug 1932741]. - - -Changes from 1.7 to 1.8. --------------------------- -o Added support for kqueue and epoll on platforms that support them. - Added ability to choose the event notification system at program - startup. - -o Long-overdue public definitions of ST_UTIME_NO_TIMEOUT (-1ULL) and - ST_UTIME_NO_WAIT (0) [bug 1514436]. - -o Documentation patch for st_utime() [bug 1514484]. - -o Documentation patch for st_timecache_set() [bug 1514486]. - -o Documentation patch for st_netfd_serialize_accept() [bug 1514494]. - -o Added st_writev_resid() [rfe 1538344]. - -o Added st_readv_resid() [rfe 1538768] and, for symmetry, st_readv(). - - -Changes from 1.6 to 1.7. ------------------------- -o Support glibc 2.4, which breaks programs that manipulate jump buffers. - Replaced Linux IA64 special cases with new md.S that covers all - Linux. - - -Changes from 1.5.2 to 1.6. --------------------------- -none - - -Changes from 1.5.1 to 1.5.2. ----------------------------- -o Alfred Perlstein's context switch callback feature. - -o Claus Assmann's st_recvmsg/st_sendmsg wrappers. - -o Extra stack padding for platforms that need it. - -o Ron Arts's timeout clarifications in the reference manual. - -o Raymond Bero and Anton Berezin's AMD64 FreeBSD port. - -o Claus Assmann's AMD64 SunOS 5.10 port. - -o Claus Assmann's AMD64 OpenBSD port. - -o Michael Abd-El-Malek's Mac OS X port. - -o Michael Abd-El-Malek's stack printing patch. - - -Changes from 1.5.0 to 1.5.1. ----------------------------- -o Andreas Gustafsson's USE_POLL fix. - -o Gene's st_set_utime_function() enhancement. - - -Changes from 1.4 to 1.5.0. --------------------------- -o Andreas Gustafsson's performance patch. - -o New extensions: Improved DNS resolver, generic LRU cache, in-process - DNS cache, and a program to test the resolver and cache. - -o Support for AMD Opteron 64-bit CPUs under Linux. - -o Support for SPARC-64 under Solaris. - -o Andreas Gustafsson's support for VAX under NetBSD. - -o Changed unportable #warning directives in md.h to #error. - - -Changes from 1.3 to 1.4. ------------------------- -o Andreas Gustafsson's NetBSD port. - -o Wesley W. Terpstra's Darwin (MacOS X) port. - -o Support for many CPU architectures under Linux and *BSD. - -o Renamed private typedefs so they don't conflict with public ones any - more. - -o common.h now includes public.h for strict prototyping. - -o Joshua Levy's recommendation to make st_connect() and st_sendto() - accept const struct sockaddr pointers, as the originals do. - -o Clarified the documentation regarding blocking vs. non-blocking I/O. - -o Cygwin support. - -o Created the extensions directory. - -o Fixed warnings from ia64asm.S. - - -Changes from 1.2 to 1.3. ------------------------- -o Added st_read_resid() and st_write_resid() to allow the caller to know - how much data was transferred before an error occurred. Updated - documentation. - -o Updated project link, copyrights, and documentation regarding - timeouts. Added comment to st_connect(). - -o Optimized the _st_add_sleep_q() function in sched.c. Now we walk the - sleep queue *backward* when inserting a thread into it. When you - have lots (hundreds) of threads and several timeout values, it takes - a while to insert a thread at the appropriate point in the sleep - queue. The idea is that often this appropriate point is closer to - the end of the queue rather than the beginning. Measurements show - performance improves with this change. In any case this change - should do no harm. - -o Added a hint of when to define USE_POLL and when not to, to the - Makefile. - -o Added debugging support (files common.h and sched.c). See above. - -o Decreased the number of reallocations of _ST_POLLFDS in sched.c. - Inspired by Lev Walkin. - -o Fixed st_usleep(-1) and st_sleep(-1), and added a warning to the - documentation about too-large timeouts. - -o Linux/*BSD Alpha port. - -o Wesley W. Terpstra modernized the build process: - - properly build relocatable libraries under bsd and linux - - use library versioning - - added rpm spec file - - added debian/ files - See above for build instructions. - - -Changes from 1.1 to 1.2. ------------------------- -o Added st_randomize_stacks(). - -o Added a patch contributed by Sascha Schumann. - - -Changes from 1.0 to 1.1. ------------------------- -o Relicensed under dual MPL-GPL. - -o OpenBSD port. - -o Compile-time option to use poll() instead of select() for - event polling (see Makefile). - This is useful if you want to support a large number of open - file descriptors (larger than FD_SETSIZE) within a single - process. - -o Linux IA-64 port. - Two issues make IA-64 different from other platforms: - - - Besides the traditional call stack in memory, IA-64 uses the - general register stack. Thus each thread needs a backing store - for the register stack in addition to the memory stack. - - - Current implementation of setjmp()/longjmp() can not be used - for thread context-switching since it assumes that only one - register stack exists. Using special assembly functions for - context-switching is unavoidable. - -o Thread stack capping on IRIX. - This allows some profiling tools (such as SpeedShop) to know when - to stop unwinding the stack. Without this libexc, used by SpeedShop, - traces right off the stack and crashes. - -o Miscellaneous documentation additions. - - -COPYRIGHTS - -Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. -All Rights Reserved. diff --git a/trunk/3rdparty/st-srs/README.md b/trunk/3rdparty/st-srs/README.md deleted file mode 100644 index 60fc1a651..000000000 --- a/trunk/3rdparty/st-srs/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# state-threads - -![](http://ossrs.net:8000/gif/v1/sls.gif?site=github.com&path=/srs/srsst) -[![](https://cloud.githubusercontent.com/assets/2777660/22814959/c51cbe72-ef92-11e6-81cc-32b657b285d5.png)](https://github.com/ossrs/srs/wiki/v1_CN_Contact#wechat) - -Fork from http://sourceforge.net/projects/state-threads, patched for [SRS](https://github.com/ossrs/srs/tree/2.0release). - -> See: https://github.com/ossrs/state-threads/blob/srs/README - -For original ST without any changes, checkout the [ST master branch](https://github.com/ossrs/state-threads/tree/master). - -## Branch SRS - -The branch [srs](https://github.com/ossrs/state-threads/tree/srs) will be patched the following patches: - -- [x] Patch [st.arm.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/1.st.arm.patch), for ARM. -- [x] Patch [st.osx.kqueue.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/3.st.osx.kqueue.patch), for osx. -- [x] Patch [st.disable.examples.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/4.st.disable.examples.patch), for ubuntu. -- [x] [Refine TAB of code](https://github.com/ossrs/state-threads/compare/c2001d30ca58f55d72a6cc6b9b6c70391eaf14db...d2101b26988b0e0db0aabc53ddf452068c1e2cbc). -- [x] Merge from [michaeltalyansky](https://github.com/michaeltalyansky/state-threads) and [xzh3836598](https://github.com/ossrs/state-threads/commit/9a17dec8f9c2814d93761665df7c5575a4d2d8a3), support [ARM](https://github.com/ossrs/state-threads/issues/1). -- [x] Merge from [toffaletti](https://github.com/toffaletti/state-threads), support [valgrind](https://github.com/ossrs/state-threads/issues/2) for ST. -- [x] Patch [st.osx10.14.build.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/6.st.osx10.14.build.patch), for osx 10.14 build. -- [x] Support macro `MD_ST_NO_ASM` to disable ASM, [#8](https://github.com/ossrs/state-threads/issues/8). -- [x] Merge patch [srs#1282](https://github.com/ossrs/srs/issues/1282#issuecomment-445539513) to support aarch64, [#9](https://github.com/ossrs/state-threads/issues/9). - -## Docs - -* Introduction: http://ossrs.github.io/state-threads/docs/st.html -* API reference: http://ossrs.github.io/state-threads/docs/reference.html -* Programming notes: http://ossrs.github.io/state-threads/docs/notes.html - -## Usage - -Get code: - -``` -git clone https://github.com/ossrs/state-threads.git st-1.9 && -git checkout -b srs origin/srs -``` - -For Linux: - -``` -make linux-debug EXTRA_CFLAGS="-DMD_HAVE_EPOLL" -``` - -For OSX: - -``` -make darwin-debug EXTRA_CFLAGS="-DMD_HAVE_KQUEUE" -``` - -Linux with valgrind: - -``` -make linux-debug EXTRA_CFLAGS="-DMD_VALGRIND" -``` - -> Remark: User must install valgrind, for instance, in centos6 `sudo yum install -y valgrind valgrind-devel`. - -Linux with valgrind and epoll: - -``` -make linux-debug EXTRA_CFLAGS="-DMD_HAVE_EPOLL -DMD_VALGRIND" -``` - -For OSX, user must specifies the valgrind header files: - -``` -make darwin-debug EXTRA_CFLAGS="-DMD_HAVE_KQUEUE -DMD_VALGRIND -I/usr/local/include" -``` - -> Remark: Latest OSX does not support ST, please use docker to run ST. - -## Valgrind - -How to debug with gdb under valgrind, read [valgrind manual](http://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.gdbserver-simple). - -About startup parameters, read [valgrind cli](http://valgrind.org/docs/manual/mc-manual.html#mc-manual.options). - -Important cli options: - -1. `--undef-value-errors= [default: yes]`, Controls whether Memcheck reports uses of undefined value errors. Set this to no if you don't want to see undefined value errors. It also has the side effect of speeding up Memcheck somewhat. -1. `--leak-check= [default: summary]`, When enabled, search for memory leaks when the client program finishes. If set to summary, it says how many leaks occurred. If set to full or yes, each individual leak will be shown in detail and/or counted as an error, as specified by the options `--show-leak-kinds` and `--errors-for-leak-kinds`. -1. `--track-origins= [default: no]`, Controls whether Memcheck tracks the origin of uninitialised values. By default, it does not, which means that although it can tell you that an uninitialised value is being used in a dangerous way, it cannot tell you where the uninitialised value came from. This often makes it difficult to track down the root problem. -1. `--show-reachable= , --show-possibly-lost=`, to show the using memory. - -Winlin 2016 diff --git a/trunk/3rdparty/st-srs/common.h b/trunk/3rdparty/st-srs/common.h deleted file mode 100644 index 0c0685b9a..000000000 --- a/trunk/3rdparty/st-srs/common.h +++ /dev/null @@ -1,479 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#ifndef __ST_COMMON_H__ -#define __ST_COMMON_H__ - -#include -#include -#include -#include -#include - -/* Enable assertions only if DEBUG is defined */ -#ifndef DEBUG - #define NDEBUG -#endif -#include -#define ST_ASSERT(expr) assert(expr) - -#define ST_BEGIN_MACRO { -#define ST_END_MACRO } - -#ifdef DEBUG - #define ST_HIDDEN /*nothing*/ -#else - #define ST_HIDDEN static -#endif - -#include "public.h" -#include "md.h" - -/* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ -#ifndef MD_VALGRIND - #ifndef NVALGRIND - #define NVALGRIND - #endif -#else - #undef NVALGRIND -#endif - - -/***************************************** - * Circular linked list definitions - */ - -typedef struct _st_clist { - struct _st_clist *next; - struct _st_clist *prev; -} _st_clist_t; - -/* Insert element "_e" into the list, before "_l" */ -#define ST_INSERT_BEFORE(_e,_l) \ - ST_BEGIN_MACRO \ - (_e)->next = (_l); \ - (_e)->prev = (_l)->prev; \ - (_l)->prev->next = (_e); \ - (_l)->prev = (_e); \ - ST_END_MACRO - -/* Insert element "_e" into the list, after "_l" */ -#define ST_INSERT_AFTER(_e,_l) \ - ST_BEGIN_MACRO \ - (_e)->next = (_l)->next; \ - (_e)->prev = (_l); \ - (_l)->next->prev = (_e); \ - (_l)->next = (_e); \ - ST_END_MACRO - -/* Return the element following element "_e" */ -#define ST_NEXT_LINK(_e) ((_e)->next) - -/* Append an element "_e" to the end of the list "_l" */ -#define ST_APPEND_LINK(_e,_l) ST_INSERT_BEFORE(_e,_l) - -/* Insert an element "_e" at the head of the list "_l" */ -#define ST_INSERT_LINK(_e,_l) ST_INSERT_AFTER(_e,_l) - -/* Return the head/tail of the list */ -#define ST_LIST_HEAD(_l) (_l)->next -#define ST_LIST_TAIL(_l) (_l)->prev - -/* Remove the element "_e" from it's circular list */ -#define ST_REMOVE_LINK(_e) \ - ST_BEGIN_MACRO \ - (_e)->prev->next = (_e)->next; \ - (_e)->next->prev = (_e)->prev; \ - ST_END_MACRO - -/* Return non-zero if the given circular list "_l" is empty, */ -/* zero if the circular list is not empty */ -#define ST_CLIST_IS_EMPTY(_l) \ - ((_l)->next == (_l)) - -/* Initialize a circular list */ -#define ST_INIT_CLIST(_l) \ - ST_BEGIN_MACRO \ - (_l)->next = (_l); \ - (_l)->prev = (_l); \ - ST_END_MACRO - -#define ST_INIT_STATIC_CLIST(_l) \ - {(_l), (_l)} - - -/***************************************** - * Basic types definitions - */ - -typedef void (*_st_destructor_t)(void *); - - -typedef struct _st_stack { - _st_clist_t links; - char *vaddr; /* Base of stack's allocated memory */ - int vaddr_size; /* Size of stack's allocated memory */ - int stk_size; /* Size of usable portion of the stack */ - char *stk_bottom; /* Lowest address of stack's usable portion */ - char *stk_top; /* Highest address of stack's usable portion */ - void *sp; /* Stack pointer from C's point of view */ -#ifdef __ia64__ - void *bsp; /* Register stack backing store pointer */ -#endif - /* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ -#ifndef NVALGRIND - /* id returned by VALGRIND_STACK_REGISTER */ - /* http://valgrind.org/docs/manual/manual-core-adv.html */ - unsigned long valgrind_stack_id; -#endif -} _st_stack_t; - - -typedef struct _st_cond { - _st_clist_t wait_q; /* Condition variable wait queue */ -} _st_cond_t; - - -typedef struct _st_thread _st_thread_t; - -struct _st_thread { - int state; /* Thread's state */ - int flags; /* Thread's flags */ - - void *(*start)(void *arg); /* The start function of the thread */ - void *arg; /* Argument of the start function */ - void *retval; /* Return value of the start function */ - - _st_stack_t *stack; /* Info about thread's stack */ - - _st_clist_t links; /* For putting on run/sleep/zombie queue */ - _st_clist_t wait_links; /* For putting on mutex/condvar wait queue */ -#ifdef DEBUG - _st_clist_t tlink; /* For putting on thread queue */ -#endif - - st_utime_t due; /* Wakeup time when thread is sleeping */ - _st_thread_t *left; /* For putting in timeout heap */ - _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ - int heap_index; - - void **private_data; /* Per thread private data */ - - _st_cond_t *term; /* Termination condition variable for join */ - - jmp_buf context; /* Thread's context */ -}; - - -typedef struct _st_mutex { - _st_thread_t *owner; /* Current mutex owner */ - _st_clist_t wait_q; /* Mutex wait queue */ -} _st_mutex_t; - - -typedef struct _st_pollq { - _st_clist_t links; /* For putting on io queue */ - _st_thread_t *thread; /* Polling thread */ - struct pollfd *pds; /* Array of poll descriptors */ - int npds; /* Length of the array */ - int on_ioq; /* Is it on ioq? */ -} _st_pollq_t; - - -typedef struct _st_eventsys_ops { - const char *name; /* Name of this event system */ - int val; /* Type of this event system */ - int (*init)(void); /* Initialization */ - void (*dispatch)(void); /* Dispatch function */ - int (*pollset_add)(struct pollfd *, int); /* Add descriptor set */ - void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */ - int (*fd_new)(int); /* New descriptor allocated */ - int (*fd_close)(int); /* Descriptor closed */ - int (*fd_getlimit)(void); /* Descriptor hard limit */ -} _st_eventsys_t; - - -typedef struct _st_vp { - _st_thread_t *idle_thread; /* Idle thread for this vp */ - st_utime_t last_clock; /* The last time we went into vp_check_clock() */ - - _st_clist_t run_q; /* run queue for this vp */ - _st_clist_t io_q; /* io queue for this vp */ - _st_clist_t zombie_q; /* zombie queue for this vp */ -#ifdef DEBUG - _st_clist_t thread_q; /* all threads of this vp */ -#endif - int pagesize; - - _st_thread_t *sleep_q; /* sleep queue for this vp */ - int sleepq_size; /* number of threads on sleep queue */ - -#ifdef ST_SWITCH_CB - st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ - st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ -#endif -} _st_vp_t; - - -typedef struct _st_netfd { - int osfd; /* Underlying OS file descriptor */ - int inuse; /* In-use flag */ - void *private_data; /* Per descriptor private data */ - _st_destructor_t destructor; /* Private data destructor function */ - void *aux_data; /* Auxiliary data for internal use */ - struct _st_netfd *next; /* For putting on the free list */ -} _st_netfd_t; - - -/***************************************** - * Current vp, thread, and event system - */ - -extern _st_vp_t _st_this_vp; -extern _st_thread_t *_st_this_thread; -extern _st_eventsys_t *_st_eventsys; - -#define _ST_CURRENT_THREAD() (_st_this_thread) -#define _ST_SET_CURRENT_THREAD(_thread) (_st_this_thread = (_thread)) - -#define _ST_LAST_CLOCK (_st_this_vp.last_clock) - -#define _ST_RUNQ (_st_this_vp.run_q) -#define _ST_IOQ (_st_this_vp.io_q) -#define _ST_ZOMBIEQ (_st_this_vp.zombie_q) -#ifdef DEBUG - #define _ST_THREADQ (_st_this_vp.thread_q) -#endif - -#define _ST_PAGE_SIZE (_st_this_vp.pagesize) - -#define _ST_SLEEPQ (_st_this_vp.sleep_q) -#define _ST_SLEEPQ_SIZE (_st_this_vp.sleepq_size) - -#define _ST_VP_IDLE() (*_st_eventsys->dispatch)() - - -/***************************************** - * vp queues operations - */ - -#define _ST_ADD_IOQ(_pq) ST_APPEND_LINK(&_pq.links, &_ST_IOQ) -#define _ST_DEL_IOQ(_pq) ST_REMOVE_LINK(&_pq.links) - -#define _ST_ADD_RUNQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_RUNQ) -#define _ST_DEL_RUNQ(_thr) ST_REMOVE_LINK(&(_thr)->links) - -#define _ST_ADD_SLEEPQ(_thr, _timeout) _st_add_sleep_q(_thr, _timeout) -#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr) - -#define _ST_ADD_ZOMBIEQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ) -#define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links) - -#ifdef DEBUG - #define _ST_ADD_THREADQ(_thr) ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ) - #define _ST_DEL_THREADQ(_thr) ST_REMOVE_LINK(&(_thr)->tlink) -#endif - - -/***************************************** - * Thread states and flags - */ - -#define _ST_ST_RUNNING 0 -#define _ST_ST_RUNNABLE 1 -#define _ST_ST_IO_WAIT 2 -#define _ST_ST_LOCK_WAIT 3 -#define _ST_ST_COND_WAIT 4 -#define _ST_ST_SLEEPING 5 -#define _ST_ST_ZOMBIE 6 -#define _ST_ST_SUSPENDED 7 - -#define _ST_FL_PRIMORDIAL 0x01 -#define _ST_FL_IDLE_THREAD 0x02 -#define _ST_FL_ON_SLEEPQ 0x04 -#define _ST_FL_INTERRUPT 0x08 -#define _ST_FL_TIMEDOUT 0x10 - - -/***************************************** - * Pointer conversion - */ - -#ifndef offsetof - #define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) -#endif - -#define _ST_THREAD_PTR(_qp) \ - ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, links))) - -#define _ST_THREAD_WAITQ_PTR(_qp) \ - ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, wait_links))) - -#define _ST_THREAD_STACK_PTR(_qp) \ - ((_st_stack_t *)((char*)(_qp) - offsetof(_st_stack_t, links))) - -#define _ST_POLLQUEUE_PTR(_qp) \ - ((_st_pollq_t *)((char *)(_qp) - offsetof(_st_pollq_t, links))) - -#ifdef DEBUG - #define _ST_THREAD_THREADQ_PTR(_qp) \ - ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink))) -#endif - - -/***************************************** - * Constants - */ - -#ifndef ST_UTIME_NO_TIMEOUT - #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) -#endif - -#ifndef __ia64__ - #define ST_DEFAULT_STACK_SIZE (64*1024) -#else - #define ST_DEFAULT_STACK_SIZE (128*1024) /* Includes register stack size */ -#endif - -#ifndef ST_KEYS_MAX - #define ST_KEYS_MAX 16 -#endif - -#ifndef ST_MIN_POLLFDS_SIZE - #define ST_MIN_POLLFDS_SIZE 64 -#endif - - -/***************************************** - * Threads context switching - */ - -#ifdef DEBUG - void _st_iterate_threads(void); - #define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() -#else - #define ST_DEBUG_ITERATE_THREADS() -#endif - -#ifdef ST_SWITCH_CB - #define ST_SWITCH_OUT_CB(_thread) \ - if (_st_this_vp.switch_out_cb != NULL && \ - _thread != _st_this_vp.idle_thread && \ - _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_out_cb(); \ - } - #define ST_SWITCH_IN_CB(_thread) \ - if (_st_this_vp.switch_in_cb != NULL && \ - _thread != _st_this_vp.idle_thread && \ - _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_in_cb(); \ - } -#else - #define ST_SWITCH_OUT_CB(_thread) - #define ST_SWITCH_IN_CB(_thread) -#endif - -/* - * Switch away from the current thread context by saving its state and - * calling the thread scheduler - */ -#define _ST_SWITCH_CONTEXT(_thread) \ - ST_BEGIN_MACRO \ - ST_SWITCH_OUT_CB(_thread); \ - if (!MD_SETJMP((_thread)->context)) { \ - _st_vp_schedule(); \ - } \ - ST_DEBUG_ITERATE_THREADS(); \ - ST_SWITCH_IN_CB(_thread); \ - ST_END_MACRO - -/* - * Restore a thread context that was saved by _ST_SWITCH_CONTEXT or - * initialized by _ST_INIT_CONTEXT - */ -#define _ST_RESTORE_CONTEXT(_thread) \ - ST_BEGIN_MACRO \ - _ST_SET_CURRENT_THREAD(_thread); \ - MD_LONGJMP((_thread)->context, 1); \ - ST_END_MACRO - -/* - * Initialize the thread context preparing it to execute _main - */ -#ifdef MD_INIT_CONTEXT - #define _ST_INIT_CONTEXT MD_INIT_CONTEXT -#else - #error Unknown OS -#endif - -/* - * Number of bytes reserved under the stack "bottom" - */ -#define _ST_STACK_PAD_SIZE MD_STACK_PAD_SIZE - - -/***************************************** - * Forward declarations - */ - -void _st_vp_schedule(void); -void _st_vp_check_clock(void); -void *_st_idle_thread_start(void *arg); -void _st_thread_main(void); -void _st_thread_cleanup(_st_thread_t *thread); -void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout); -void _st_del_sleep_q(_st_thread_t *thread); -_st_stack_t *_st_stack_new(int stack_size); -void _st_stack_free(_st_stack_t *ts); -int _st_io_init(void); - -st_utime_t st_utime(void); -_st_cond_t *st_cond_new(void); -int st_cond_destroy(_st_cond_t *cvar); -int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout); -int st_cond_signal(_st_cond_t *cvar); -ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout); -ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout); -int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); -_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size); - -#endif /* !__ST_COMMON_H__ */ - diff --git a/trunk/3rdparty/st-srs/docs/fig.gif b/trunk/3rdparty/st-srs/docs/fig.gif deleted file mode 100644 index 7265a05db4f516b44fcf37c3949922ff4f62999d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5374 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 diff --git a/trunk/3rdparty/st-srs/docs/notes.html b/trunk/3rdparty/st-srs/docs/notes.html deleted file mode 100644 index 5a24369e2..000000000 --- a/trunk/3rdparty/st-srs/docs/notes.html +++ /dev/null @@ -1,434 +0,0 @@ - - -State Threads Library Programming Notes - - -

Programming Notes

-

- -

- -

-


-

- -

Porting

-The State Threads library uses OS concepts that are available in some -form on most UNIX platforms, making the library very portable across -many flavors of UNIX. However, there are several parts of the library -that rely on platform-specific features. Here is the list of such parts: -

-

    -
  • Thread context initialization: Two ingredients of the -jmp_buf -data structure (the program counter and the stack pointer) have to be -manually set in the thread creation routine. The jmp_buf data -structure is defined in the setjmp.h header file and differs from -platform to platform. Usually the program counter is a structure member -with PC in the name and the stack pointer is a structure member -with SP in the name. One can also look in the -Netscape's NSPR library source -which already has this code for many UNIX-like platforms -(mozilla/nsprpub/pr/include/md/*.h files). -

    -Note that on some BSD-derived platforms _setjmp(3)/_longjmp(3) -calls should be used instead of setjmp(3)/longjmp(3) (that is -the calls that manipulate only the stack and registers and do not -save and restore the process's signal mask).

  • -

    -Starting with glibc 2.4 on Linux the opacity of the jmp_buf data -structure is enforced by setjmp(3)/longjmp(3) so the -jmp_buf ingredients cannot be accessed directly anymore (unless -special environmental variable LD_POINTER_GUARD is set before application -execution). To avoid dependency on custom environment, the State Threads -library provides setjmp/longjmp replacement functions for -all Intel CPU architectures. Other CPU architectures can also be easily -supported (the setjmp/longjmp source code is widely available for -many CPU architectures). -

    -

  • High resolution time function: Some platforms (IRIX, Solaris) -provide a high resolution time function based on the free running hardware -counter. This function returns the time counted since some arbitrary -moment in the past (usually machine power up time). It is not correlated in -any way to the time of day, and thus is not subject to resetting, -drifting, etc. This type of time is ideal for tasks where cheap, accurate -interval timing is required. If such a function is not available on a -particular platform, the gettimeofday(3) function can be used -(though on some platforms it involves a system call). -

    -

  • The stack growth direction: The library needs to know whether the -stack grows toward lower (down) or higher (up) memory addresses. -One can write a simple test program that detects the stack growth direction -on a particular platform.
  • -

    -

  • Non-blocking attribute inheritance: On some platforms (e.g. IRIX) -the socket created as a result of the accept(2) call inherits the -non-blocking attribute of the listening socket. One needs to consult the manual -pages or write a simple test program to see if this applies to a specific -platform.
  • -

    -

  • Anonymous memory mapping: The library allocates memory segments -for thread stacks by doing anonymous memory mapping (mmap(2)). This -mapping is somewhat different on SVR4 and BSD4.3 derived platforms. -

    -The memory mapping can be avoided altogether by using malloc(3) for -stack allocation. In this case the MALLOC_STACK macro should be -defined.

  • -
-

-All machine-dependent feature test macros should be defined in the -md.h header file. The assembly code for setjmp/longjmp -replacement functions for all CPU architectures should be placed in -the md.S file. -

-The current version of the library is ported to: -

    -
  • IRIX 6.x (both 32 and 64 bit)
  • -
  • Linux (kernel 2.x and glibc 2.x) on x86, Alpha, MIPS and MIPSEL, - SPARC, ARM, PowerPC, 68k, HPPA, S390, IA-64, and Opteron (AMD-64)
  • -
  • Solaris 2.x (SunOS 5.x) on x86, AMD64, SPARC, and SPARC-64
  • -
  • AIX 4.x
  • -
  • HP-UX 11 (both 32 and 64 bit)
  • -
  • Tru64/OSF1
  • -
  • FreeBSD on x86, AMD64, and Alpha
  • -
  • OpenBSD on x86, AMD64, Alpha, and SPARC
  • -
  • NetBSD on x86, Alpha, SPARC, and VAX
  • -
  • MacOS X (Darwin) on PowerPC (32 bit) and Intel (both 32 and 64 bit) [universal]
  • -
  • Cygwin
  • -
-

- - -

Signals

-Signal handling in an application using State Threads should be treated the -same way as in a classical UNIX process application. There is no such -thing as per-thread signal mask, all threads share the same signal handlers, -and only asynchronous-safe functions can be used in signal handlers. -However, there is a way to process signals synchronously by converting a -signal event to an I/O event: a signal catching function does a write to -a pipe which will be processed synchronously by a dedicated signal handling -thread. The following code demonstrates this technique (error handling is -omitted for clarity): -
-
-/* Per-process pipe which is used as a signal queue. */
-/* Up to PIPE_BUF/sizeof(int) signals can be queued up. */
-int sig_pipe[2];
-
-/* Signal catching function. */
-/* Converts signal event to I/O event. */
-void sig_catcher(int signo)
-{
-  int err;
-
-  /* Save errno to restore it after the write() */
-  err = errno;
-  /* write() is reentrant/async-safe */
-  write(sig_pipe[1], &signo, sizeof(int));
-  errno = err;
-}
-
-/* Signal processing function. */
-/* This is the "main" function of the signal processing thread. */
-void *sig_process(void *arg)
-{
-  st_netfd_t nfd;
-  int signo;
-
-  nfd = st_netfd_open(sig_pipe[0]);
-
-  for ( ; ; ) {
-    /* Read the next signal from the pipe */
-    st_read(nfd, &signo, sizeof(int), ST_UTIME_NO_TIMEOUT);
-
-    /* Process signal synchronously */
-    switch (signo) {
-    case SIGHUP:
-      /* do something here - reread config files, etc. */
-      break;
-    case SIGTERM:
-      /* do something here - cleanup, etc. */
-      break;
-      /*      .
-              .
-         Other signals
-              .
-              .
-      */
-    }
-  }
-
-  return NULL;
-}
-
-int main(int argc, char *argv[])
-{
-  struct sigaction sa;
-        .
-        .
-        .
-
-  /* Create signal pipe */
-  pipe(sig_pipe);
-
-  /* Create signal processing thread */
-  st_thread_create(sig_process, NULL, 0, 0);
-
-  /* Install sig_catcher() as a signal handler */
-  sa.sa_handler = sig_catcher;
-  sigemptyset(&sa.sa_mask);
-  sa.sa_flags = 0;
-  sigaction(SIGHUP, &sa, NULL);
-
-  sa.sa_handler = sig_catcher;
-  sigemptyset(&sa.sa_mask);
-  sa.sa_flags = 0;
-  sigaction(SIGTERM, &sa, NULL);
-
-        .
-        .
-        .
-      
-}
-
-
-

-Note that if multiple processes are used (see below), the signal pipe should -be initialized after the fork(2) call so that each process has its -own private pipe. -

- - -

Intra-Process Synchronization

-Due to the event-driven nature of the library scheduler, the thread context -switch (process state change) can only happen in a well-known set of -library functions. This set includes functions in which a thread may -"block": I/O functions (st_read(), st_write(), etc.), -sleep functions (st_sleep(), etc.), and thread synchronization -functions (st_thread_join(), st_cond_wait(), etc.). As a result, -process-specific global data need not to be protected by locks since a thread -cannot be rescheduled while in a critical section (and only one thread at a -time can access the same memory location). By the same token, -non thread-safe functions (in a traditional sense) can be safely used with -the State Threads. The library's mutex facilities are practically useless -for a correctly written application (no blocking functions in critical -section) and are provided mostly for completeness. This absence of locking -greatly simplifies an application design and provides a foundation for -scalability. -

- - -

Inter-Process Synchronization

-The State Threads library makes it possible to multiplex a large number -of simultaneous connections onto a much smaller number of separate -processes, where each process uses a many-to-one user-level threading -implementation (N of M:1 mappings rather than one M:N -mapping used in native threading libraries on some platforms). This design -is key to the application's scalability. One can think about it as if a -set of all threads is partitioned into separate groups (processes) where -each group has a separate pool of resources (virtual address space, file -descriptors, etc.). An application designer has full control of how many -groups (processes) an application creates and what resources, if any, -are shared among different groups via standard UNIX inter-process -communication (IPC) facilities.

-There are several reasons for creating multiple processes: -

-

    -
  • To take advantage of multiple hardware entities (CPUs, disks, etc.) -available in the system (hardware parallelism).
  • -

    -

  • To reduce risk of losing a large number of user connections when one of -the processes crashes. For example, if C user connections (threads) -are multiplexed onto P processes and one of the processes crashes, -only a fraction (C/P) of all connections will be lost.
  • -

    -

  • To overcome per-process resource limitations imposed by the OS. For -example, if select(2) is used for event polling, the number of -simultaneous connections (threads) per process is -limited by the FD_SETSIZE parameter (see select(2)). -If FD_SETSIZE is equal to 1024 and each connection needs one file -descriptor, then an application should create 10 processes to support 10,000 -simultaneous connections.
  • -
-

-Ideally all user sessions are completely independent, so there is no need for -inter-process communication. It is always better to have several separate -smaller process-specific resources (e.g., data caches) than to have one large -resource shared (and modified) by all processes. Sometimes, however, there -is a need to share a common resource among different processes. In that case, -standard UNIX IPC facilities can be used. In addition to that, there is a way -to synchronize different processes so that only the thread accessing the -shared resource will be suspended (but not the entire process) if that resource -is unavailable. In the following code fragment a pipe is used as a counting -semaphore for inter-process synchronization: -

-#ifndef PIPE_BUF
-#define PIPE_BUF 512  /* POSIX */
-#endif
-
-/* Semaphore data structure */
-typedef struct ipc_sem {
-  st_netfd_t rdfd;  /* read descriptor */
-  st_netfd_t wrfd;  /* write descriptor */
-} ipc_sem_t;
-
-/* Create and initialize the semaphore. Should be called before fork(2). */
-/* 'value' must be less than PIPE_BUF. */
-/* If 'value' is 1, the semaphore works as mutex. */
-ipc_sem_t *ipc_sem_create(int value)
-{
-  ipc_sem_t *sem;
-  int p[2];
-  char b[PIPE_BUF];
-
-  /* Error checking is omitted for clarity */
-  sem = malloc(sizeof(ipc_sem_t));
-
-  /* Create the pipe */
-  pipe(p);
-  sem->rdfd = st_netfd_open(p[0]);
-  sem->wrfd = st_netfd_open(p[1]);
-
-  /* Initialize the semaphore: put 'value' bytes into the pipe */
-  write(p[1], b, value);
-
-  return sem;
-}
-
-/* Try to decrement the "value" of the semaphore. */
-/* If "value" is 0, the calling thread blocks on the semaphore. */
-int ipc_sem_wait(ipc_sem_t *sem)
-{
-  char c;
-
-  /* Read one byte from the pipe */
-  if (st_read(sem->rdfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
-    return -1;
-
-  return 0;
-}
-
-/* Increment the "value" of the semaphore. */
-int ipc_sem_post(ipc_sem_t *sem)
-{
-  char c;
-
-  if (st_write(sem->wrfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
-    return -1;
-
-  return 0;
-}
-
-
-

- -Generally, the following steps should be followed when writing an application -using the State Threads library: -

-

    -
  1. Initialize the library (st_init()).
  2. -

    -

  3. Create resources that will be shared among different processes: - create and bind listening sockets, create shared memory segments, IPC - channels, synchronization primitives, etc.
  4. -

    -

  5. Create several processes (fork(2)). The parent process should - either exit or become a "watchdog" (e.g., it starts a new process when - an existing one crashes, does a cleanup upon application termination, - etc.).
  6. -

    -

  7. In each child process create a pool of threads - (st_thread_create()) to handle user connections.
  8. -
-

- - -

Non-Network I/O

- -The State Threads architecture uses non-blocking I/O on -st_netfd_t objects for concurrent processing of multiple user -connections. This architecture has a drawback: the entire process and -all its threads may block for the duration of a disk or other -non-network I/O operation, whether through State Threads I/O functions, -direct system calls, or standard I/O functions. (This is applicable -mostly to disk reads; disk writes are usually performed -asynchronously -- data goes to the buffer cache to be written to disk -later.) Fortunately, disk I/O (unlike network I/O) usually takes a -finite and predictable amount of time, but this may not be true for -special devices or user input devices (including stdin). Nevertheless, -such I/O reduces throughput of the system and increases response times. -There are several ways to design an application to overcome this -drawback: - -

-

-

- - -

Timeouts

- -The timeout parameter to st_cond_timedwait() and the -I/O functions, and the arguments to st_sleep() and -st_usleep() specify a maximum time to wait since the last -context switch not since the beginning of the function call. - -

The State Threads' time resolution is actually the time interval -between context switches. That time interval may be large in some -situations, for example, when a single thread does a lot of work -continuously. Note that a steady, uninterrupted stream of network I/O -qualifies for this description; a context switch occurs only when a -thread blocks. - -

If a specified I/O timeout is less than the time interval between -context switches the function may return with a timeout error before -that amount of time has elapsed since the beginning of the function -call. For example, if eight milliseconds have passed since the last -context switch and an I/O function with a timeout of 10 milliseconds -blocks, causing a switch, the call may return with a timeout error as -little as two milliseconds after it was called. (On Linux, -select()'s timeout is an upper bound on the amount of -time elapsed before select returns.) Similarly, if 12 ms have passed -already, the function may return immediately. - -

In almost all cases I/O timeouts should be used only for detecting a -broken network connection or for preventing a peer from holding an idle -connection for too long. Therefore for most applications realistic I/O -timeouts should be on the order of seconds. Furthermore, there's -probably no point in retrying operations that time out. Rather than -retrying simply use a larger timeout in the first place. - -

The largest valid timeout value is platform-dependent and may be -significantly less than INT_MAX seconds for select() -or INT_MAX milliseconds for poll(). Generally, you -should not use timeouts exceeding several hours. Use -ST_UTIME_NO_TIMEOUT (-1) as a special value to -indicate infinite timeout or indefinite sleep. Use -ST_UTIME_NO_WAIT (0) to indicate no waiting at all. - -

-


-

- - - diff --git a/trunk/3rdparty/st-srs/docs/reference.html b/trunk/3rdparty/st-srs/docs/reference.html deleted file mode 100644 index 3c9c7bd78..000000000 --- a/trunk/3rdparty/st-srs/docs/reference.html +++ /dev/null @@ -1,3120 +0,0 @@ - - -State Threads Library Reference - - - -

State Threads Library Reference

- -
-
Types
-
st_thread_t
-
st_cond_t
-
st_mutex_t
-
st_utime_t
-
st_netfd_t
-
st_switch_cb_t
-

-

Error Handling
-

-

Library Initialization
-

-

st_init()
-
st_getfdlimit()
-
st_set_eventsys()
-
st_get_eventsys()
-
st_get_eventsys_name()
-
st_set_utime_function()
-
st_timecache_set()
-
st_randomize_stacks()
-

-

st_switch_cb_t type
-
st_set_switch_in_cb()
-
st_set_switch_out_cb()
-

-

Thread Control and Identification
-

-

st_thread_t type
-
st_thread_create()
-
st_thread_exit()
-
st_thread_join()
-
st_thread_self()
-
st_thread_interrupt()
-
st_sleep()
-
st_usleep()
-
st_randomize_stacks()
-

-

Per-Thread Private Data
-

-

st_key_create()
-
st_key_getlimit()
-
st_thread_setspecific()
-
st_thread_getspecific()
-

-

Synchronization
-

-

st_cond_t type
-
st_cond_new()
-
st_cond_destroy()
-
st_cond_wait()
-
st_cond_timedwait()
-
st_cond_signal()
-
st_cond_broadcast()
-

-

st_mutex_t type
-
st_mutex_new()
-
st_mutex_destroy()
-
st_mutex_lock()
-
st_mutex_trylock()
-
st_mutex_unlock()
-

-

Timing
-

-

st_utime_t type
-
st_utime()
-
st_set_utime_function()
-
st_timecache_set()
-
st_time()
-

-

I/O Functions
-

-

st_netfd_t type
-
st_netfd_open()
-
st_netfd_open_socket()
-
st_netfd_free()
-
st_netfd_close()
-
st_netfd_fileno()
-
st_netfd_setspecific()
-
st_netfd_getspecific()
-
st_netfd_serialize_accept()
-
-
st_netfd_poll()
-

-

st_accept()
-
st_connect()
-
st_read()
-
st_read_fully()
-
st_read_resid()
-
st_readv()
-
st_readv_resid()
-
st_write()
-
st_write_resid()
-
st_writev()
-
st_writev_resid()
-
st_recvfrom()
-
st_sendto()
-
st_recvmsg()
-
st_sendmsg()
-

-

st_open()
-
st_poll()
-

-

Program Structure
-

-

List of Blocking Functions
-

-

-

-


-

- - - -

Types

- -The State Thread library defines the following types in the st.h -header file: -

-

-
st_thread_t
-
st_cond_t
-
st_mutex_t
-
st_utime_t
-
st_netfd_t
-
-

-


-

- - -

st_thread_t

- -Thread type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void *  st_thread_t;
-
-

-

Description
- -A thread is represented and identified by a pointer to an opaque data -structure. This pointer is a required parameter for most of the functions -that operate on threads. -

-The thread identifier remains valid until the thread returns from its root -function and, if the thread was created joinable, is joined. -

-


-

- - -

st_cond_t

- -Condition variable type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void *  st_cond_t;
-
-

-

Description
- -A condition variable is an opaque object identified by a pointer. -Condition variables provide synchronization primitives to wait for or wake -up threads waiting for certain conditions to be satisfied. -

-In the State Threads library there is no need to lock a mutex before -waiting on a condition variable. -

-


-

- - -

st_mutex_t

- -Mutex type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void *  st_mutex_t;
-
-

-

Description
- -A mutex is an opaque object identified by a pointer. -Mutual exclusion locks (mutexes) are used to serialize the execution of -threads through critical sections of code. -

-If application using the State Threads library is written with no -I/O or control yielding in critical sections (that is no -blocking functions in critical sections), then there is -no need for mutexes.

-These mutexes can only be used for intra-process thread synchronization. -They cannot be used for inter-process synchronization. -

-


-

- - -

st_utime_t

- -High resolution time type ("u" stands for "micro"). -

-

Syntax
- -
-#include <st.h>
-
-typedef unsigned long long  st_utime_t;
-
-

-

Description
- -This datatype (unsigned 64-bit integer) represents high-resolution real time -expressed in microseconds since some arbitrary time in the past. It is not -correlated in any way to the time of day. -

-


-

- - -

st_netfd_t

- -File descriptor type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void *  st_netfd_t;
-
-

-

Description
- -This datatype typically represents any open end point of network -communication (socket, end point of a pipe, FIFO, etc.) but can -encapsulate any open file descriptor. Objects of this type are -identified by a pointer to an opaque data structure. - -

-


-

- - -

st_switch_cb_t

- -Context switch callback function type. -

-

Syntax
- -
-#include <st.h>
-
-typedef void (*st_switch_cb_t)(void);
-
-

-

Description
- -This datatype is a convenience type for describing a pointer -to a function that will be called when a thread is set to stop -or set to run. -This feature is available only when ST_SWITCH_CB is defined -in <st.h>. - -

-


-

- - -

Error Handling

- - -All State Threads library non-void functions return on success either a -non-negative integer or a pointer to a newly created object (constructor-type -functions). On failure they return either -1 or a NULL -pointer respectively and set global errno to indicate the error. -It is safe to use errno because it is set right before the function -return and only one thread at a time can modify its value.

-The perror(3) function can be used to produce an error message on the -standard error output. -

-


-

- - -

Library Initialization

- -

-

-
st_init()
-
st_getfdlimit()
-
st_set_eventsys()
-
st_get_eventsys()
-
st_get_eventsys_name()
-

-These functions operate on a callback function of type -st_switch_cb_t: -

st_set_switch_in_cb()
-
st_set_switch_out_cb()
-
-

-


-

- - -

st_init()

- -Initializes the runtime. -

-

Syntax
- -
-#include <st.h>
-
-int st_init(void);
-
-

-

Parameters
-None. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. -

-

Description
-This function initializes the library runtime. It should be called near -the beginning of the application's main() function before any other -State Threads library function is called.

-Among other things, this function limits the number of open file descriptors -to the OS imposed per-process maximum number or, if select(2) is -used, to FD_SETSIZE, whichever is less (getrlimit(2)). -This limit can be -retrieved by st_getfdlimit(). It also sets the -disposition of the SIGPIPE signal to SIG_IGN (to be ignored) -(signal(5)). -

-Unlike POSIX threads, a new process created by the fork(2) system -call is an exact copy of the calling process and all state threads -which are running in the parent do exist in the child. That means that -st_init() may be called either before or after multiple processes -are created by fork(2). -

-If the library runtime is not properly initialized (e.g., st_init() -is accidentally omitted), then the process will receive either an arithmetic -exception (SIGFPE or SIGTRAP) or segmentation fault (SIGSEGV) signal upon -new thread creation or the first context switch, respectively. -

-


-

- -

st_getfdlimit()

- -Returns the maximum number of file descriptors that the calling process -can open. -

-

Syntax
- -
-#include <st.h>
-
-int st_getfdlimit(void);
-
-

-

Parameters
-None. -

-

Returns
-The maximum number of file descriptors that the calling process can open. -If this function is called before the library is successfully initialized by -st_init(), a value of -1 is returned. -

-

Description
-This function returns the limit on the number of open file descriptors which -is set by the st_init() function. -

-


-

- - -

st_set_eventsys()

- -Sets event notification mechanism. -

-

Syntax
- -
-#include <st.h>
-
-int st_set_eventsys(int eventsys);
-
-

-

Parameters
-st_set_eventsys() has the following parameter:

-eventsys

-An integer value identifying selected event notification mechanism. The -following values are defined in the st.h header file: -

- - - - - - - - - - - - - - - -
ST_EVENTSYS_DEFAULTUse default event notification mechanism. Usually it's select(2) -but if the library was compiled with the USE_POLL macro defined -then the default is poll(2).
ST_EVENTSYS_SELECTUse select(2) as an event notification mechanism.
ST_EVENTSYS_POLLUse poll(2) as an event notification mechanism.
ST_EVENTSYS_ALTUse an alternative event notification mechanism. The actual -mechanism selected depends on OS support. For example, epoll(4) -will be used on Linux if supported and kqueue(2) will be used -on FreeBSD/OpenBSD. If the OS supports no alternative event -notification mechanism, setting ST_EVENTSYS_ALT has no effect -and the ST_EVENTSYS_DEFAULT mechanism will be used.
-

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - - - -
EINVAL -The supplied eventsys parameter has an invalid value. -
EBUSY -The event notification mechanism has already been set. -
-

-

Description
-This function sets the event notification mechanism that will be used by -the State Threads library. To have any effect, it must be called -before the st_init() function which performs -the actual initialization. If st_set_eventsys() is not called, -st_init() will set the ST_EVENTSYS_DEFAULT -mechanism. The mechanism cannot be changed once set. -

-There are no strict rules for selecting an event notification -mechanism. The "best" one depends on how your application behaves. -Try a few to see which one works best for you. As a rule of -thumb, you should use the ST_EVENTSYS_ALT mechanism if your -application deals with a very large number of network connections of -which only a few are active at once. -

-


-

- -

st_get_eventsys()

- -Returns the integer value identifying the event notification mechanism -being used by the State Threads library. -

-

Syntax
- -
-#include <st.h>
-
-int st_get_eventsys(void);
-
-

-

Parameters
-None. -

-

Returns
-The integer value identifying the current event notification mechanism. -This value can be one of the following (see st_set_eventsys()): -ST_EVENTSYS_SELECT, ST_EVENTSYS_POLL, or -ST_EVENTSYS_ALT. Future versions of the library may return other -values. If a mechanism hasn't been set yet, a value of -1 is returned. -

-

Description
-This function returns the integer value identifying the event notification -mechanism which is actually being used by the State Threads library. -

-


-

- -

st_get_eventsys_name()

- -Returns the name of the event notification mechanism being used by the -State Threads library. -

-

Syntax
- -
-#include <st.h>
-
-const char *st_get_eventsys_name(void);
-
-

-

Parameters
-None. -

-

Returns
-The string identifying the current event notification mechanism. If a -mechanism hasn't been set yet (see st_set_eventsys()), an empty string is -returned. Possible return values are "select", -"poll", "kqueue", or "epoll". Future versions -of the library may return other values. -

-

Description
-This function returns the string identifying the event notification -mechanism which is actually being used by the State Threads library. -

-


-

- - -

st_set_switch_in_cb()

- - -

st_set_switch_out_cb()

-
-Set the optional callback function for thread switches. -

-

Syntax
- -
-#include <st.h>
-
-st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb);
-st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb);
-
-

-

Parameters
-st_set_switch_in_cb() and st_set_switch_out_cb() have the -following parameter:

-cb

-A function to be called when a thread is resumed and stopped respectively.

-

Returns
-The previous callback function pointer. -

-

Description
-These functions set the callback for when a thread is resumed and stopped -respectively. After being called any thread switch will call the callback. -Use a NULL pointer to disable the callback (this is the default). -Use st_thread_self() or thread -specific data to differentiate between threads.

-These functions can be called at any time.

-This feature is available only when ST_SWITCH_CB is defined -in <st.h>. -

-


-

- - -

Thread Control and Identification

- -

-These functions operate on a thread object of type -st_thread_t. -

-

-
st_thread_create()
-
st_thread_exit()
-
st_thread_join()
-
st_thread_self()
-
st_thread_interrupt()
-
st_sleep()
-
st_usleep()
-
st_randomize_stacks()
-
-

-


-

- -

st_thread_create()

- -Creates a new thread. -

-

Syntax
- -
-#include <st.h>
-
-st_thread_t st_thread_create(void *(*start)(void *arg), void *arg,
-                             int joinable, int stack_size);
-
-
-

-

Parameters
-st_thread_create() has the following parameters:

-start

-A pointer to the thread's start function, which is called as the root of the -new thread. Return from this function terminates a thread.

-arg

-A pointer to the root function's only parameter.

-joinable

-Specifies whether the thread is joinable or unjoinable. If this parameter -is zero, the thread is unjoinable. Otherwise, it is joinable. -See also st_thread_join().

-stack_size

-Specifies your preference for the size of the stack, in bytes, associated -with the newly created thread. If you pass zero in this parameter, the -default stack size will be used. The default stack size is 128 KB on IA-64 -and 64 KB on all other platforms. On IA-64 only a half of stack_size -bytes is used for the memory stack. The other half is used for the register -stack backing store. -

-

Returns
-Upon successful completion, a new thread identifier is returned (this -identifier remains valid until the thread returns from its start function). -Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new thread. Note that the total number of threads -created by the application is limited by the amount of swap space available. -Upon thread creation, stack_size bytes are reserved on the swap -space. The stack pages are not actually used (valid) until touched by the -application. -

-


-

- -

st_thread_exit()

- -Terminates the calling thread. -

-

Syntax
- -
-#include <st.h>
-
-void st_thread_exit(void *retval);
-
-

-

Parameters
-st_thread_exit() has the following parameters:

-retval

-If the thread is joinable, then the value retval may be retrieved -by st_thread_join(). If a thread returns from its -start function, it acts as if it had called st_thread_exit() with -retval as the value returned. -

-

Returns
-Nothing. -

-

Description
-This function terminates the calling thread. When a thread exits, per-thread -private data is destroyed by invoking the destructor function for any -non-NULL thread specific values associated with active keys (see -st_key_create()). This function is implicitly called -when a thread returns from its start function.

-When the last thread terminates the process exits with a zero status value. -

-


-

- -

st_thread_join()

- -Blocks the calling thread until a specified thread terminates. -

-

Syntax
- -
-#include <st.h>
-
-int st_thread_join(st_thread_t thread, void **retvalp);
-
-

-

Parameters
-st_thread_join() has the following parameters:

-thread

-A valid identifier for the thread that is to be joined.

-retvalp

-If this parameter is not NULL, then the exit value of the -thread will be placed in the location referenced by this parameter -(see st_thread_exit()). -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - - - -
EINVALTarget thread is unjoinable.
EINVALOther thread already waits on the same -joinable thread.
EDEADLKTarget thread is the same as the -calling thread.
EINTRCurrent thread was interrupted by -st_thread_interrupt().
-

-

Description
-This function is used to synchronize the termination of a thread and possibly -retrieve its exit value. Several threads cannot wait for the same thread -to complete - one of the calling threads operates successfully, and the others -terminate with the error. The calling thread is not blocked if the target -thread has already terminated. -

-


-

- -

st_thread_self()

- -Identifies the calling thread. -

-

Syntax
- -
-#include <st.h>
-
-st_thread_t st_thread_self(void);
-
-

-

Parameters
-None. -

-

Returns
-Always returns a valid reference to the calling thread - a self-identity. -

-

Description
-This function identifies the calling thread. This is the same identifier -that the creating thread obtains from -st_thread_create(). -

-


-

- -

st_thread_interrupt()

- -Interrupts a target thread. -

-

Syntax
- -
-#include <st.h>
-
-void st_thread_interrupt(st_thread_t thread);
-
-

-

Parameters
-st_thread_interrupt() has the following parameters:

-thread

-A valid identifier for the thread being interrupted. -

-

Returns
-Nothing. -

-

Description
-This function interrupts (unblocks) a target thread that is blocked in one -of the blocking functions. A function that was interrupted -returns an error and sets errno to EINTR. It is up to -the target thread to act upon an interrupt (e.g., it may exit or just -abort the current transaction).

-Note: State Threads library functions are never interrupted by a -caught signal. A blocking library function returns an error and sets -errno to EINTR only if the current thread was -interrupted via st_thread_interrupt(). -

-If a target thread is already runnable or running (e.g., it is a newly -created thread or calling thread itself), this function will prevent it -from subsequent blocking. In other words, the interrupt will be "delivered" -only when a target thread is about to block. -

-


-

- -

st_sleep(), st_usleep()

- -Suspends current thread for a specified amount of time. -

-

Syntax
- -
-#include <st.h>
-
-int st_sleep(int secs);
-
-int st_usleep(st_utime_t usecs);
-
-

-

Parameters
-st_sleep() has the following parameters:

-secs

-The number of seconds you want the thread to sleep for. -

-st_usleep() has the following parameters:

-usecs

-The number of microseconds you want the thread to sleep for. This parameter -is a variable of type st_utime_t. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
-

-

Description
-These functions suspend the calling thread from execution for a specified -number of seconds (st_sleep()) or microseconds (st_usleep()). -

- -If zero is passed as a parameter to st_sleep(), or -ST_UTIME_NO_WAIT (0) is passed to -st_usleep(), the calling thread yields, thus potentially -allowing another thread to run. - -

- -If -1 is passed as a parameter to st_sleep(), or -ST_UTIME_NO_TIMEOUT (-1) is passed to -st_usleep(), the calling thread will be suspended permanently. -It can be resumed again by interrupting it via st_thread_interrupt(). - -

-


-

- -

st_randomize_stacks()

- -Turns stack base address randomization on or off. -

-

Syntax
- -
-#include <st.h>
-
-int st_randomize_stacks(int on);
-
-

-

Parameters
-st_randomize_stacks() has the following parameters:

-on

-If this parameter has a non-zero value, the State Threads library -randomizes the base addresses of stacks allocated for threads created -after this call. Otherwise new threads' stacks are typically page -aligned. -

-

Returns
-The previous state of stack randomization (a value of 0 if it -was off and a non-zero value otherwise). -

-

Description
-Randomizing state threads' stack bases may improve cache performance on -some systems when large numbers of state threads all perform roughly the -same work, as when they all start from the same root function. On many -modern systems the performance increase is negligible. You should -compare your application's performance with this feature on and off to -see if you really need it. -

-When randomization is enabled, new stacks are allocated one page larger -to accomodate the randomization. -

-This call affects only threads created afterward. It has no effect on -existing threads. -

-


-

- - -

Per-Thread Private Data

- -These functions allow to associate private data with each of the threads in -a process. -

-

-
st_key_create()
-
st_key_getlimit()
-
st_thread_setspecific()
-
st_thread_getspecific()
-
-

-


-

- -

st_key_create()

- -Creates a key (non-negative integer) that can be used by all -threads in the process to get and set thread-specific data. -

-

Syntax
- -
-#include <st.h>
-
-int st_key_create(int *keyp, void (*destructor)(void *));
-
-

-

Parameters
-st_key_create() has the following parameters:

-keyp

-The newly created key is returned in the memory pointed to by this parameter. -The new key can be used with -st_thread_setspecific() and -st_thread_getspecific().

-destructor

-Specifies an optional destructor function for the private data associated -with the key. This function can be specified as NULL. -Upon thread exit (see st_thread_exit()), if a key -has a non-NULL destructor and has a non-NULL value -associated with that key, then the destructor function will be -called with the associated value. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EAGAINThe limit on the total number of keys per -process has been exceeded (see st_key_getlimit()). -
-

-

Description
-If this function is successful, every thread in the same process is capable -of associating private data with the new key. After a new key is created, all -active threads have the value NULL associated with that key. -After a new thread is created, the value NULL is associated with -all keys for that thread. If a non-NULL destructor function is -registered with a new key, it will be called at one of two times, as long as -the private data is not NULL: - -

-The key maintains independent data values for each binding thread. A thread -can get access only to its own thread-specific data. There is no way to -deallocate a private data key once it is allocated. -

-


-

- -

st_key_getlimit()

- -Returns the key limit. -

-

Syntax
- -
-#include <st.h>
-
-int st_key_getlimit(void);
-
-

-

Parameters
-None. -

-

Returns
-The limit on the total number of keys per process. -

-

Description
-This function can be used to obtain the limit on the total number of keys -per process (see st_key_create()). -

-


-

- -

st_thread_setspecific()

- -Sets per-thread private data. -

-

Syntax
- -
-#include <st.h>
-
-int st_thread_setspecific(int key, void *value);
-
-

-

Parameters
-st_thread_setspecific() has the following parameters:

-key

-This parameter represents a key with which thread-specific data is associated. -

-value

-The per-thread private data, or more likely, a pointer to the data which is -associated with key. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EINVALThe specified key is invalid.
-

-

Description
-This function associates a thread-specific value with key. -Different threads may bind different values to the same key.

-If the thread already has non-NULL private data associated with -key, and if the destructor function for that key is not -NULL, this destructor function will be called before setting the -new data value. -

-


-

- -

st_thread_getspecific()

- -Retrieves the per-thread private data for the current thread. -

-

Syntax
- -
-#include <st.h>
-
-void *st_thread_getspecific(int key);
-
-

-

Parameters
-st_thread_getspecific() has the following parameters:

-key

-This parameter represents a key with which thread-specific data is associated. -

-

Returns
-The thread-specific data associated with key. If no data is -associated with key, then NULL is returned. -

-

Description
-This function returns the calling thread's value that is bound to the -specified key (see -st_thread_setspecific()). -

-


-

- - -

Synchronization

- -

-These functions operate on condition variables -and mutual exclusion locks (mutexes).

-Functions are provided to wait on a condition variable and to wake up -(signal) threads that are waiting on the condition variable. -

-

-
st_cond_new()
-
st_cond_destroy()
-
st_cond_wait()
-
st_cond_timedwait()
-
st_cond_signal()
-
st_cond_broadcast()
-

-

st_mutex_new()
-
st_mutex_destroy()
-
st_mutex_lock()
-
st_mutex_trylock()
-
st_mutex_unlock()
-
-

-


-

- -

st_cond_new()

- -Creates a new condition variable. -

-

Syntax
- -
-#include <st.h>
-
-st_cond_t st_cond_new(void);
-
-

-

Parameters
-None. -

-

Returns
-Upon successful completion, a new condition variable identifier is returned. -Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new condition variable. -

-


-

- -

st_cond_destroy()

- -Destroys a condition variable. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_destroy(st_cond_t cvar);
-
-

-

Parameters
-st_cond_destroy() has the following parameters:

-cvar

-An identifier of the condition variable object to be destroyed. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EBUSYThe condition variable is currently being -used by one or more threads.
-

-

Description
-This function destroys a condition variable. The caller is responsible for -ensuring that the condition variable is no longer in use. -

-


-

- -

st_cond_wait()

- -Waits on a condition. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_wait(st_cond_t cvar);
-
-

-

Parameters
-st_cond_wait() has the following parameters:

-cvar

-The condition variable on which to wait. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
-

-

Description
-This function is used to block on a condition variable. A return from this -function does not guarantee that the condition or event for which the caller -was waiting actually occurred. It is the responsibility of the caller -to recheck the condition wait predicate before proceeding.

-Note: The State Threads library scheduling guarantees that the -condition cannot change between the checking and blocking, therefore there -is no need for mutex protection. You must not call any -blocking functions between the condition checking and -the st_cond_wait() call. -

-


-

- -

st_cond_timedwait()

- -Waits on a condition. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout);
-
-

-

Parameters
-st_cond_timedwait() has the following parameters:

-cvar

-The condition variable on which to wait.

-timeout

-If the number of microseconds specified by this parameter passes before the -waiting thread is signalled, an error is returned. This parameter is a -variable of type st_utime_t. Note that this -time value is a time delta; it is not an absolute time. -Also note that timeouts are measured since -the last context switch. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred before the thread was -awakened by st_cond_signal() or -st_cond_broadcast().
-

-

Description
-This function works the same way as st_cond_wait(), -except that an error is returned if the number of microseconds specified by -timeout passes before the waiting thread is signalled. -

-


-

- -

st_cond_signal()

- -Unblocks a thread waiting on a condition variable. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_signal(st_cond_t cvar);
-
-

-

Parameters
-st_cond_signal() has the following parameters:

-cvar

-The condition variable to signal. -

-

Returns
-Always zero. -

-

Description
-This function unblocks (signals) one of the threads that are blocked on -cvar at the time of the call. If no thread is waiting on the -condition variable, the signal operation is a no-op. -

-


-

- -

st_cond_broadcast()

- -Unblocks all threads waiting on a condition variable. -

-

Syntax
- -
-#include <st.h>
-
-int st_cond_broadcast(st_cond_t cvar);
-
-

-

Parameters
-st_cond_broadcast() has the following parameters:

-cvar

-The condition variable to broadcast. -

-

Returns
-Always zero. -

-

Description
-This function unblocks all threads blocked on the specified condition -variable at the time of the call. If no threads are waiting, this operation -is a no-op. -

-


-

- - -

st_mutex_new()

- -Creates a new mutual exclusion lock (mutex). -

-

Syntax
- -
-#include <st.h>
-
-st_mutex_t st_mutex_new(void);
-
-

-

Parameters
-None. -

-

Returns
-Upon successful completion, a new mutex identifier is returned. -Otherwise, NULL is returned and errno is set to -indicate the error. -

-

Description
-This function creates a new opaque mutual exclusion lock (see -st_mutex_t). -

-


-

- -

st_mutex_destroy()

- -Destroys a specified mutex object. -

-

Syntax
- -
-#include <st.h>
-
-int st_mutex_destroy(st_mutex_t lock);
-
-

-

Parameters
-st_mutex_destroy() has the following parameters:

-lock

-An identifier of the mutex object to be destroyed. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EBUSYThe mutex is currently being used by -other threads.
-

-

Description
-This function destroys a mutex. The caller is responsible for ensuring -that the mutex is no longer in use. -

-


-

- -

st_mutex_lock()

- -Locks a specified mutex object. -

-

Syntax
- -
-#include <st.h>
-
-int st_mutex_lock(st_mutex_t lock);
-
-

-

Parameters
-st_mutex_lock() has the following parameters:

-lock

-An identifier of the mutex object to be locked. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - - -
EDEADLKThe current thread already owns the mutex. -
EINTRThe current thread was interrupted by -st_thread_interrupt().
-

-

Description
-A thread that calls this function will block until it can gain exclusive -ownership of a mutex, and retains ownership until it calls -st_mutex_unlock(). -

-


-

- -

st_mutex_trylock()

- -Attempts to acquire a mutex. -

-

Syntax
- -
-#include <st.h>
-
-int st_mutex_trylock(st_mutex_t lock);
-
-

-

Parameters
-st_mutex_trylock() has the following parameters:

-lock

-An identifier of the mutex object to be locked. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EBUSYThe mutex is currently held by another -thread.
-

-

Description
-This function attempts to acquire a mutex. If the mutex object is locked -(by any thread, including the current thread), the call returns immediately -with an error. -

-


-

- -

st_mutex_unlock()

- -Releases a specified mutex object. -

-

Syntax
- -
-#include <st.h>
-
-int st_mutex_unlock(st_mutex_t lock);
-
-

-

Parameters
-st_mutex_unlock() has the following parameters:

-lock

-An identifier of the mutex object to be unlocked. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error:

- - -
EPERMThe current thread does not own the mutex. -
-

-

Description
-This function releases a specified mutex object previously acquired by -st_mutex_lock() or -st_mutex_trylock(). Only the thread that locked -a mutex should unlock it. -

-


-

- - -

Timing

- -

-

-
st_utime()
-
st_set_utime_function()
-
st_timecache_set()
-
st_time()
-
-

-


-

- -

st_utime()

- -Returns current high-resolution time. -

-

Syntax
- -
-#include <st.h>
-
-st_utime_t st_utime(void);
-
-

-

Parameters
-None. -

-

Returns
-Current high-resolution time value of type -st_utime_t. -

-

Description
-This function returns the current high-resolution time. Time is -expressed as microseconds since some arbitrary time in the past. It is -not correlated in any way to the time of day. See also st_utime_t and st_time(). -

-


-

- -

st_set_utime_function()

- -Set high-resolution time function. -

-

Syntax
- -
-#include <st.h>
-
-int st_set_utime_function(st_utime_t (*func)(void));
-
-

-

Parameters
-st_set_utime_function() has the following parameters:

-func

-This function will be called to get high-resolution time instead of the -default st_utime() function. It must return -number of microseconds since some arbitrary time in the past. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to EINVAL to indicate the error. -

-

Description
-This function may be called to replace the default implementation of the -st_utime() function. It must be called before the ST -library has been initialized (see st_init()). -The user-provided function func will be invoked whenever -st_utime() is called to obtain current high-resolution time. -Replacing default implementation may be useful, for example, for taking -advantage of high performance CPU cycle counters. -

-


-

- -

st_timecache_set()

- -Turns the time caching on or off. -

-

Syntax
- -
-#include <st.h>
-
-int st_timecache_set(int on);
-
-

-

Parameters
-st_timecache_set() has the following parameters:

-on

-If this parameter has a non-zero value, the time caching is turned on -(enabled). Otherwise, the time caching is turned off (disabled). -By default time caching is disabled. -

-

Returns
-The previous state of time caching (a value of 0 if it was off and -a value of 1 otherwise). -

-

Description
-The State Threads library has the ability to "cache" the time value that is -reported by the time(2) system call. If the time caching is enabled -by calling this function with a non-zero argument, then the result value -of time(2) will be stored and updated at most once per second. The -cached time can be retrieved by st_time(). -By default time caching is disabled. -You may enable or disable time caching at any time but generally -you enable it once (if desired) during program initialization.

-Note: There are some pathological cases (e.g., very heavy loads during -application benchmarking) when a single thread runs for a long time without -giving up control and the cached time value is not updated properly. If you -always need "real-time" time values, don't enable the time caching. -

-


-

- -

st_time()

- -Returns the value of time in seconds since 00:00:00 UTC, January 1, 1970. -

-

Syntax
- -
-#include <st.h>
-
-time_t st_time(void);
-
-

-

Parameters
-None. -

-

Returns
-The value of time in seconds since 00:00:00 UTC, January 1, 1970 as reported -by the time(2) system call. -

-

Description
-If the time caching was enabled by -st_timecache_set(), then this function returns -the cached result. Otherwise, it just calls time(2). -

-


-

- - -

I/O Functions

- -

-Most State Threads library I/O functions look like corresponding C library -functions with two exceptions: -

    -
  • They operate on file descriptor objects of type -st_netfd_t.
  • -
  • They take an additional argument of type -st_utime_t which represents an inactivity -timeout: if no I/O is possible during this amount of time, I/O functions -return an error code and set errno to ETIME. - -The boundary values ST_UTIME_NO_WAIT (0) and -ST_UTIME_NO_TIMEOUT (-1) for this argument indicate -that the thread should wait no time (function returns immediately) or -wait forever (never time out), respectively. - -Note that timeouts are measured since the -last context switch. -
  • -
-

-

-
st_netfd_open()
-
st_netfd_open_socket()
-
st_netfd_free()
-
st_netfd_close()
-
st_netfd_fileno()
-
st_netfd_setspecific()
-
st_netfd_getspecific()
-
st_netfd_serialize_accept()
-
st_netfd_poll()
-

-

st_accept()
-
st_connect()
-
st_read()
-
st_read_fully()
-
st_read_resid()
-
st_readv()
-
st_read_resid()
-
st_write()
-
st_write_resid()
-
st_writev()
-
st_writev_resid()
-
st_recvfrom()
-
st_sendto()
-
st_recvmsg()
-
st_sendmsg()
-
st_open()
-
st_poll()
-
-

-


-

- -

st_netfd_open()

- -Creates a new file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-st_netfd_t st_netfd_open(int osfd);
-
-

-

Parameters
-st_netfd_open() has the following parameters:

-osfd

- -Any open OS file descriptor; can be obtained from calls to -functions including, but not restricted to, pipe(2), socket(3), -socketpair(3), fcntl(2), dup(2), etc. - - -

-

Returns
-Upon successful completion, a new file descriptor object identifier is -returned. Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new file descriptor object of type -st_netfd_t.

- -Note: Among other things, this function sets a non-blocking -flag on the underlying OS file descriptor. You should not modify this -flag directly. Also, once an st_netfd_t -has been created with a given file descriptor, you should avoid -passing that descriptor to normal I/O or stdio functions. Since the -O_NONBLOCK flag is shared across dup(2), this applies to -dup()'ed file descriptors as well - for instance, if you pass -standard output or standard input to st_netfd_open(), then -you should use st_write() instead of write -or fprintf when writing to standard error as well - since all -three descriptors could point to the same terminal. If necessary, you -can still use write directly if you remember to check -errno for EAGAIN, but fprintf and other -stdio functions should be avoided completely because, at least on -Linux, the stdio library cannot be made to work reliably with -non-blocking files. (This only applies to file descriptors which are -passed to st_netfd_open() or st_netfd_open_socket(), or which are -related to such descriptors through dup(); other file -descriptors are untouched by State Threads.) -

-


-

- -

st_netfd_open_socket()

- -Creates a new file descriptor object from a socket. -

-

Syntax
- -
-#include <st.h>
-
-st_netfd_t st_netfd_open_socket(int osfd);
-
-

-

Parameters
-st_netfd_open_socket() has the following parameters:

-osfd

-An open OS file descriptor which is a socket initially obtained from a -socket(3) or socketpair(3) call. -

-

Returns
-Upon successful completion, a new file descriptor object identifier is -returned. Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new file descriptor object of type -st_netfd_t which represents an open end -point of network communication.

-Unlike the st_netfd_open() function which may be used -on OS file descriptors of any origin, st_netfd_open_socket() must -be used only on sockets. It is slightly more efficient than -st_netfd_open().

-Note: Among other things, this function sets a non-blocking flag -on the underlying OS socket. You should not modify this flag directly. -See st_netfd_open(). -

-


-

- -

st_netfd_free()

- -Frees a file descriptor object without closing the underlying OS file -descriptor. -

-

Syntax
- -
-#include <st.h>
-
-void st_netfd_free(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_free() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t). -

-

Returns
-Nothing. -

-

Description
-This function frees the memory and other resources identified by the -fd parameter without closing the underlying OS file descriptor. -Any non-NULL descriptor-specific data is destroyed by invoking -the specified destructor function (see st_netfd_setspecific()).

A thread should -not free file descriptor objects that are in use by other threads -because it may lead to unpredictable results (e.g., a freed file -descriptor may be reused without other threads knowing that). -

-


-

- -

st_netfd_close()

- -Closes a file descriptor. -

-

Syntax
- -
-#include <st.h>
-
-int st_netfd_close(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_close() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t). -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. -

-

Description
-This function closes the underlying OS file descriptor, frees the memory and -other resources identified by the fd parameter. Any non-NULL -descriptor-specific data is destroyed by invoking the specified destructor -function (see st_netfd_setspecific()).

-A thread should not close file descriptor objects that are in use by other -threads because it may lead to unpredictable results (e.g., a closed -file descriptor may be reused without other threads knowing that). -

-


-

- -

st_netfd_fileno()

- -Returns an underlying OS file descriptor. -

-

Syntax
- -
-#include <st.h>
-
-int st_netfd_fileno(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_fileno() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t). -

-

Returns
-An underlying OS file descriptor. -

-

Description
-This function returns the integer OS file descriptor associated with the named -file descriptor object. -

-


-

- -

st_netfd_setspecific()

- -Sets per-descriptor private data. -

-

Syntax
- -
-#include <st.h>
-
-void st_netfd_setspecific(st_netfd_t fd, void *value,
-                          void (*destructor)(void *));
-
-

-

Parameters
-st_netfd_setspecific() has the following parameters:

-fd

-A valid file descriptor object identifier (see -st_netfd_t). -

-value

-The per-descriptor private data, or more likely, a pointer to the data which -is being associated with the named file descriptor object. -

-destructor

-Specifies an optional destructor function for the private data associated -with fd. This function can be specified as NULL. -If value is not NULL, then this destructor function will -be called with value as an argument upon freeing the file descriptor -object (see st_netfd_free() and -st_netfd_close()). -

-

Returns
-Nothing. -

-

Description
-This function allows to associate any data with the specified file -descriptor object (network connection). If a non-NULL destructor -function is registered, it will be called at one of two times, as long as -the associated data is not NULL: -
    -
  • when private data is replaced by calling -st_netfd_setspecific() again -
  • upon freeing the file descriptor object (see -st_netfd_free() and -st_netfd_close()) -
-

-


-

- -

st_netfd_getspecific()

- -Retrieves the per-descriptor private data. -

-

Syntax
- -
-#include <st.h>
-
-void *st_netfd_getspecific(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_getspecific() has the following parameters:

-fd

-A valid file descriptor object identifier (see -st_netfd_t). -

-

Returns
-The data associated with the named file descriptor object. If no data is -associated with fd, then NULL is returned. -

-

Description
-This function allows to retrieve the data that was associated with the -specified file descriptor object (see -st_netfd_setspecific()). -

-


-

- -

st_netfd_serialize_accept()

- -Serializes all subsequent accept(3) calls on a specified file -descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_netfd_serialize_accept(st_netfd_t fd);
-
-

-

Parameters
-st_netfd_serialize_accept() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) which has been successfully created -from a valid listening socket by st_netfd_open() or -st_netfd_open_socket(). -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. -

-

Description
-On some platforms (e.g., Solaris 2.5 and possibly other SVR4 implementations) -accept(3) calls from different processes on -the same listening socket (see bind(3), listen(3)) must be -serialized. This function causes all subsequent accept(3) calls -made by st_accept() on the specified file descriptor -object to be serialized. -

-st_netfd_serialize_accept() must be called before -creating multiple server processes via fork(2). If the application -does not create multiple processes to accept network connections on -the same listening socket, there is no need to call this function. -

-Deciding whether or not to serialize accepts is tricky. On some -platforms (IRIX, Linux) it's not needed at all and -st_netfd_serialize_accept() is a no-op. On other platforms -it depends on the version of the OS (Solaris 2.6 doesn't need it but -earlier versions do). Serializing accepts does incur a slight -performance penalty so you want to enable it only if necessary. Read -your system's manual pages for accept(2) and select(2) -to see if accept serialization is necessary on your system. -

-st_netfd_serialize_accept() allocates resources that are -freed upon freeing of the specified file descriptor object (see -st_netfd_free() and -st_netfd_close()). -

-


-

- -

st_netfd_poll()

- -Waits for I/O on a single file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout);
-
-

-

Parameters
-st_netfd_poll() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t). -

-how

-Specifies I/O events of interest. This parameter can be constructed by -OR-ing any combination of the following event flags which are defined -in the poll.h header file:

- - - - - -
POLLINfd is readable.
POLLOUTfd is is writable.
POLLPRIfd has an exception condition.
-

-timeout

-Amount of time in microseconds the call will block waiting for I/O -to become ready. This parameter is a variable of type -st_utime_t. If this time expires without any -I/O becoming ready, st_netfd_poll() returns an error and sets -errno to ETIME. -Note that timeouts are measured since the -last context switch. -

-

Returns
-If the named file descriptor object is ready for I/O within the specified -amount of time, a value of 0 is returned. Otherwise, a value -of -1 is returned and errno is set to indicate the error: -

- - - - -
EBADFThe underlying OS file descriptor is invalid. -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred without any I/O -becoming ready.
-

-

Description
-This function returns as soon as I/O is ready on the named file -descriptor object or the specified amount of time expires. The -how parameter should be set to the I/O events (readable, -writable, exception, or some combination) that the caller is interested -in. If the value of timeout is ST_UTIME_NO_TIMEOUT -(-1), this function blocks until a requested I/O event occurs -or until the call is interrupted by st_thread_interrupt().

-Despite having an interface like poll(2), this function uses -the same event notification mechanism as the rest of the library. For -instance if an alternative event nofication mechanism was set using st_set_eventsys(), this function uses that -mechanism to check for events.

-Note: if kqueue(2) is used as an alternative event -notification mechanism (see st_set_eventsys()), the POLLPRI -event flag is not supported and st_netfd_poll() will return an error -if it's set (errno will be set to EINVAL). -

-


-

- -

st_accept()

- -Accepts a connection on a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen,
-                     st_utime_t timeout);
-
-

-

Parameters
-st_accept() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing the rendezvous socket -on which the caller is willing to accept new connections. This object has been -created from a valid listening socket by -st_netfd_open() or -st_netfd_open_socket().

-addr

-If this value is non-zero, it is a result parameter that is filled -in with the address of the connecting entity, as known to the communications -layer (see accept(3)).

-addrlen

-This parameter should initially contain the amount of space pointed to by -addr; on return it will contain the actual length (in bytes) of the -address returned (see accept(3)).

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the accept operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-Upon successful completion, a new file descriptor object identifier -representing the newly accepted connection is returned. Otherwise, -NULL is returned and errno is set to indicate the error. -Possible errno values are the same as set by the accept(3) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no pending -connection was accepted.
-

-

Description
-This function accepts the first connection from the queue of pending -connections and creates a new file descriptor object for the newly -accepted connection. The rendezvous socket can still be used to accept -more connections.

-st_accept() blocks the calling thread until either a new connection -is successfully accepted or an error occurs. If no pending connection can -be accepted before the time limit, this function returns NULL -and sets errno to ETIME. -

-


-

- -

st_connect()

- -Initiates a connection on a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_connect(st_netfd_t fd, struct sockaddr *addr, int addrlen,
-               st_utime_t timeout);
-
-

-

Parameters
-st_connect() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a socket.

-addr

-A pointer to the address of the peer to which the socket is to be connected. -

-addrlen

-This parameter specifies the amount of space pointed to by addr. -

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the connect operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-Upon successful completion, a value of 0 is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. Possible errno values are the same as set -by the connect(3) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and connection setup -was not completed.
-

-

Description
-This function is usually invoked on a file descriptor object representing -a TCP socket. Upon completion it establishes a TCP connection to the peer. -If the underlying OS socket is not bound, it will be bound to an arbitrary -local address (see connect(3)).

-st_connect() blocks the calling thread until either the connection -is successfully established or an error occurs. If the connection setup -cannot complete before the specified time limit, this function fails with -errno set to ETIME. -

-


-

- -

st_read()

- -Reads data from a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout);
-
-

-

Parameters
-st_read() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to a buffer to hold the data read in. On output the buffer -contains the data.

-nbyte

-The size of buf in bytes.

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the read operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes actually -read is returned (a value of 0 means the network connection is -closed or end of file is reached). Otherwise, a value of -1 is -returned and errno is set to indicate the error. -Possible errno values are the same as set by the read(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was read. -
-

-

Description
-This function blocks the calling thread until it encounters an end-of-stream -indication, some positive number of bytes (but no more than nbyte -bytes) are read in, a timeout occurs, or an error occurs. -

-


-

- -

st_read_fully()

- -Reads the specified amount of data in full from a file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte,
-                      st_utime_t timeout);
-
-

-

Parameters
-st_read_fully() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to a buffer to hold the data read in. On output the buffer -contains the data.

-nbyte

-The amount of data to be read in full (in bytes). It must not exceed the -size of buf.

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes actually -read is returned (a value less than nbyte means the network -connection is closed or end of file is reached). Otherwise, a value of --1 is returned and errno is set to indicate the error. -Possible errno values are the same as set by the read(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until the specified amount of data -is read in full, it encounters an end-of-stream indication, a timeout occurs, -or an error occurs. -

-


-

- -

st_read_resid()

- -Reads the specified amount of data in full from a file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_read_resid(st_netfd_t fd, void *buf, size_t *resid,
-		  st_utime_t timeout);
-
-

-

Parameters
-st_read_resid() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to a buffer to hold the data read in. On output the buffer -contains the data.

-resid

-A pointer to a number of bytes. -On entry, the amount of data to be read in full. -It must not exceed the size of buf. -On return, the amount of data remaining to be read. -(A non-zero returned value means some but not all of the data was read.)

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success, zero is returned. *resid may be zero, indicating -a complete read, or non-zero, indicating the network -connection is closed or end of file is reached. -

-Otherwise, a value of -1 is returned, *resid is non-zero, -and errno is set to indicate the error. -Possible errno values are the same as set by the read(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until the specified amount of data -is read in full, it encounters an end-of-stream indication, a timeout occurs, -or an error occurs. It differs from st_read_fully() only in that -it allows the caller to know how many bytes were transferred before an error -occurred. -

-


-

- -

st_readv()

- -Reads data from a specified file descriptor object into multiple buffers. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size,
-		 st_utime_t timeout);
-
-

-

Parameters
-st_readv() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-iov

-An array of iovec structures that identify the buffers for holding -the data read in. -On return the buffers contain the data.

-iov_size

-The number of iovec structures in the iov array.

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the read operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes actually -read is returned (a value of 0 means the network connection is -closed or end of file is reached). Otherwise, a value of -1 is -returned and errno is set to indicate the error. -Possible errno values are the same as set by the readv(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was read. -
-

-

Description
-This function blocks the calling thread until it encounters an end-of-stream -indication, some positive number of bytes (but no more than fit in the buffers) -are read in, a timeout occurs, or an error occurs. -

-


-

- -

st_readv_resid()

- -Reads the specified amount of data in full from a file descriptor object -into multiple buffers. -

-

Syntax
- -
-#include <st.h>
-
-int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
-		   st_utime_t timeout);
-
-

-

Parameters
-st_readv_resid() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-iov

-A pointer to an array of iovec structures. -On entry, the iovecs identify the buffers for holding the data read in. -On return, the incomplete iovecs. -This function modifies both the pointer and the array to which it points.

-iov_size

-A pointer to a number of iovec structures. -On entry, the number of iovec structures pointed to by *iov. -On return, the number of incomplete or unused iovec structures. -(A non-zero returned value means some but not all of the data was read.)

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success, zero is returned. *iov_size may be zero, indicating -a complete read, or non-zero, indicating the network connection is -closed or end of file is reached. *iov points to the first -iovec after the end of the original array on a complete read, or to the -first incomplete iovec on an incomplete read. -

-Otherwise, a value of -1 is returned, *iov_size is non-zero, -and errno is set to indicate the error. *iov points to the -first unused iovec. -Possible errno values are the same as set by the readv(2) -call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

All of the iovecs before *iov are modified such that -iov_base points to the end of the original buffer and -iov_len is zero. -

-

Description
-This function blocks the calling thread until the specified amount of data -is read in full, it encounters an end-of-stream indication, a timeout occurs, -or an error occurs. Like st_read_resid() it blocks the thread until -all of the requested data is read or an error occurs. Use -st_readv() to read up to the requested amount of data. -

-


-

- -

st_write()

- -Writes a buffer of data to a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte,
-                 st_utime_t timeout);
-
-

-

Parameters
-st_write() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to the buffer holding the data to be written.

-nbyte

-The amount of data in bytes to be written from the buffer.

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer equal to nbyte is returned. -Otherwise, a value of -1 is returned and errno is set -to indicate the error. Possible errno values are the same as set -by the write(2) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until all the data is written, -a timeout occurs, or the write operation fails. The return value is equal to -either nbyte (on success) or -1 (on failure). Note that if -st_write() returns -1, some data (less than nbyte -bytes) may have been written before an error occurred. -

-


-

- -

st_write_resid()

- -Writes a buffer of data to a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid,
-                   st_utime_t timeout);
-
-

-

Parameters
-st_write_resid() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-buf

-A pointer to the buffer holding the data to be written.

-resid

-A pointer to a number of bytes. -On entry, the amount of data to be written from the buffer. -On return, the amount of data remaining to be written. -(A non-zero returned value means some but not all of the data was written.)

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success, zero is returned and *resid is zero. -Otherwise, a value of -1 is returned, *resid is non-zero, -and errno is set -to indicate the error. Possible errno values are the same as set -by the write(2) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until all the data is written, -a timeout occurs, or the write operation fails. It differs from -st_write() only in that it allows the caller to know how many bytes -were transferred before an error occurred. -

-


-

- -

st_writev()

- -Writes data to a specified file descriptor object from multiple buffers. -

-

Syntax
- -
-#include <st.h>
-
-ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size,
-                  st_utime_t timeout);
-
-

-

Parameters
-st_writev() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-iov

-An array of iovec structures that describe the buffers to write -from (see writev(2)).

-iov_size

-Number of iovec structures in the iov array.

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer equal to the sum of all the buffer lengths -is returned. Otherwise, a value of -1 is returned and errno -is set to indicate the error. Possible errno values are the same as -set by the writev(2) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-

Description
-This function blocks the calling thread until all the data is written, -a timeout occurs, or the write operation fails. The return value is equal to -either the sum of all the buffer lengths (on success) or -1 (on -failure). Note that if st_writev() returns -1, part of the -data may have been written before an error occurred. -

-


-

- -

st_writev_resid()

- -Writes multiple buffers of data to a specified file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
-		    st_utime_t timeout);
-
-

-

Parameters
-st_writev_resid() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t).

-iov

-A pointer to an array of iovec structures. -On entry, the iovecs identify the buffers holding the data to write. -On return, the incomplete iovecs. -This function modifies both the pointer and the array to which it points.

-iov_size

-A pointer to a number of iovec structures. -On entry, the number of iovec structures pointed to by *iov. -On return, the number of incomplete or unused iovec structures. -(A non-zero returned value means some but not all of the data was written.)

-timeout

-A value of type st_utime_t specifying the -inactivity timeout (in microseconds). -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success, zero is returned, *iov_size is zero, and *iov -points to the first iovec after the end of the original array. -Otherwise, a value of -1 is returned, *iov_size is non-zero, -*iov points to the first incomplete iovec, and errno is set -to indicate the error. Possible errno values are the same as set -by the writev(2) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred. -
-

-All of the iovecs before *iov are modified such that -iov_base points to the end of the original buffer and -iov_len is zero. -

-

Description
-This function blocks the calling thread until all the data is written, -a timeout occurs, or the write operation fails. It differs from -st_writev() only in that it allows the caller to know how many bytes -were transferred before an error occurred. -

-


-

- -

st_recvfrom()

- -Receives bytes from a file descriptor object and stores the sending peer's -address. -

-

Syntax
- -
-#include <st.h>
-
-int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from,
-                int *fromlen, st_utime_t timeout);
-
-

-

Parameters
-st_recvfrom() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a UDP socket.

-buf

-A pointer to a buffer to hold the data received.

-len

-The size of buf in bytes.

-from

-If this parameter is not a NULL pointer, the source address of the -message is filled in (see recvfrom(3)).

-fromlen

-This is a value-result parameter, initialized to the size of the buffer -associated with from, and modified on return to indicate the actual -size of the address stored there.

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the receive operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the length of the received -message in bytes is returned. Otherwise, a value of -1 is returned -and errno is set to indicate the error. Possible errno -values are the same as set by the recvfrom(3) call with two -exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was received. -
-

-

Description
-This function receives up to a specified number of bytes from the specified -file descriptor object representing a UDP socket.

-st_recvfrom() blocks the calling thread until one or more bytes are -transferred, a timeout has occurred, or there is an error. No more than -len bytes will be transferred. -

-


-

- -

st_sendto()

- -Sends bytes to a specified destination. -

-

Syntax
- -
-#include <st.h>
-
-int st_sendto(st_netfd_t fd, const void *msg, int len, struct sockaddr *to,
-              int tolen, st_utime_t timeout);
-
-

-

Parameters
-st_sendto() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a UDP socket.

-msg

-A pointer to a buffer containing the message to be sent.

-len

-The length of the message to be sent (in bytes).

-to

-A pointer to the address of the destination (see sendto(3)).

-tolen

-This parameter specifies the size of the destination address.

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the send operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes sent is -returned. Otherwise, a value of -1 is returned and errno is -set to indicate the error. Possible errno values are the same as -set by the sendto(3) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was sent. -
-

-

Description
-This function sends a specified number of bytes from a file descriptor -object representing a UDP socket to the specified destination address. -If no buffer space is available at the underlying OS socket to hold the -message to be transmitted, then st_sendto() blocks the calling -thread until the space becomes available, a timeout occurs, or an error -occurs. -

-


-

- -

st_recvmsg()

- -Receives a message from a file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags,
-               st_utime_t timeout);
-
-

-

Parameters
-st_recvmsg() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a UDP socket.

-msg

-A pointer to a msghdr structure to describe the data received.

-flags

-Control flags for recvmsg(3).

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the receive operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes received -is returned. Otherwise, a value of -1 is returned -and errno is set to indicate the error. Possible errno -values are the same as set by the recvmsg(3) call with two -exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was received. -
-

-

Description
-This function receives bytes from the specified file descriptor object -representing a UDP socket. The operation is controlled by the in/out -msg parameter.

-st_recvmsg() blocks the calling thread until one or more bytes are -transferred, a timeout has occurred, or there is an error. -

-


-

- -

st_sendmsg()

- -Sends a message to a file descriptor object. -

-

Syntax
- -
-#include <st.h>
-
-int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags,
-               st_utime_t timeout);
-
-

-

Parameters
-st_sendmsg() has the following parameters:

-fd

-A file descriptor object identifier (see -st_netfd_t) representing a UDP socket.

-msg

-A pointer to a msghdr structure describing the message to be sent.

-flags

-Control flags for sendmsg(3).

-timeout

-A value of type st_utime_t specifying the time -limit in microseconds for completion of the send operation. -Note that timeouts are measured since the -last context switch. -

-

Returns
-On success a non-negative integer indicating the number of bytes sent is -returned. Otherwise, a value of -1 is returned and errno is -set to indicate the error. Possible errno values are the same as -set by the sendmsg(3) call with two exceptions:

- - - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
ETIMEThe timeout occurred and no data was sent. -
-

-

Description
-This function sends bytes to a file descriptor object representing a UDP -socket. The operation is controlled by the msg parameter. -If no buffer space is available at the underlying OS socket to hold the -message to be transmitted, then st_sendmsg() blocks the calling -thread until the space becomes available, a timeout occurs, or an error -occurs. -

-


-

- -

st_open()

- -Opens a file for reading, writing, or both. -

-

Syntax
- -
-#include <st.h>
-
-st_netfd_t st_open(const char *path, int oflags, mode_t mode);
-
-

-

Parameters
-st_open() has the following parameters:

-path

-The pathname of the file to be opened.

-oflags

-File status flags. These are the same flags that are used by the -open(2) system call.

-mode

-Access permission bits of the file mode, if the file is created when -O_CREAT is set in oflags (see open(2)). -

-

Returns
-Upon successful completion, a new file descriptor object identifier is -returned. Otherwise, NULL is returned and errno is set -to indicate the error. -

-

Description
-This function creates a new file descriptor object of type -st_netfd_t for the file with the pathname -path. This object can be freed by -st_netfd_free() or -st_netfd_close().

-The primary purpose of this function is to open FIFOs (named pipes) or -other special files in order to create an end point of communication. -However, it can be used on regular files as well.

-Among other things, this function always sets a non-blocking flag on the -underlying OS file descriptor, so there is no need to include that flag in -oflags. -

-


-

- -

st_poll()

- -Detects when I/O is ready for a set of OS file descriptors. -

-

Syntax
- -
-#include <st.h>
-
-int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);
-
-

-

Parameters
-st_poll() has the following parameters:

-pds

-A pointer to an array of pollfd structures (see poll(2)). -

-npds

-The number of elements in the pds array.

-timeout

-A value of type st_utime_t specifying the -amount of time in microseconds the call will block waiting for I/O -to become ready. If this time expires without any I/O becoming ready, -st_poll() returns zero. -Note that timeouts are measured since the -last context switch. -

-

Returns
-Upon successful completion, a non-negative value is returned. A positive -value indicates the total number of OS file descriptors in pds -that have events. A value of 0 indicates that the call timed out. -Upon failure, a value of -1 is returned and errno is set -to indicate the error:

- - -
EINTRThe current thread was interrupted by -st_thread_interrupt().
-

-If an alternative event notification mechanism has been set by -st_set_eventsys(), other values of -errno could be set upon failure as well. The values -depend on the specific mechanism in use. -

-

Description
-This function returns as soon as I/O is ready on one or more of the specified -OS file descriptors. A count of the number of ready descriptors is returned -unless a timeout occurs, in which case zero is returned.

-The pollfd structure is defined in the poll.h header file -and contains the following members:

-

-    int fd;             /* OS file descriptor */
-    short events;       /* requested events   */
-    short revents;      /* returned events    */
-
-The events field should be set to the I/O events (readable, -writable, exception, or some combination) that the caller is interested in. -On return, the revents field is set to indicate what kind of I/O -is ready on the respective descriptor.

-The events and revents fields are constructed by OR-ing -any combination of the following event flags (defined in poll.h): -

- - - - - - -
POLLINfd is readable.
POLLOUTfd is is writable.
POLLPRIfd has an exception condition.
POLLNVALfd is bad.
-

-The POLLNVAL flag is only valid in the revents field; -it is not used in the events field.

-Despite having an interface like poll(2), this function uses -the same event notification mechanism as the rest of the library. For -instance if an alternative event nofication mechanism was set using st_set_eventsys(), this function uses that -mechanism to check for events.

-Note that unlike the poll(2) call, this function has the -timeout parameter expressed in microseconds. If the value of -timeout is ST_UTIME_NO_TIMEOUT -(-1), this function blocks until a requested I/O -event occurs or until the call is interrupted by -st_thread_interrupt(). -

-Note: if kqueue(2) is used as an alternative event -notification mechanism (see st_set_eventsys()), the POLLPRI -event flag is not supported and st_poll() will return an error -if it's set (errno will be set to EINVAL). -

-


-

- - -

Program Structure

- -

-Generally, the following steps should be followed when writing an application -using the State Threads library: -

-

    -
  1. Configure the library by calling these pre-init functions, if desired. - -
  2. -

    -

  3. Initialize the library by calling st_init().
  4. -

    -

  5. Configure the library by calling these post-init functions, if desired. - -
  6. -

    -

  7. Create resources that will be shared among different processes: - create and bind listening sockets (see socket(3), - bind(3), listen(3), - st_netfd_open_socket(), and possibly - st_netfd_serialize_accept()), - create shared memory segments, inter-process communication (IPC) - channels and synchronization primitives (if any).
  8. -

    -

  9. Create several processes via fork(2). The parent process should - either exit or become a "watchdog" (e.g., it starts a new process when - an existing one crashes, does a cleanup upon application termination, - etc.).
  10. -

    -

  11. In each child process create a pool of threads (see - st_thread_create()) to handle user - connections. Each thread in the pool may accept client connections - (st_accept()), connect to other servers - (st_connect()), perform various network I/O - (st_read(), st_write(), etc.).
  12. -
-

-Note that only State Threads library I/O functions should -be used for a network I/O: any other I/O calls may block the calling process -indefinitely. For example, standard I/O functions (fgets(3), -fread(3), fwrite(3), fprintf(3), etc.) call -read(2) and write(2) directly and therefore should not be -used on sockets or pipes. -

-Also note that for short timeouts to work the program -should do context switches (for example by calling -st_usleep()) on a regular basis. -

-


-

- - -

List of Blocking Functions

- -

-The thread context switch (process state change) can only happen -in a well-known set of blocking functions. -Only the following functions can block the calling thread: -

-

-
st_thread_join()
-
st_sleep()
-
st_usleep()
-
st_cond_wait()
-
st_cond_timedwait()
-
st_mutex_lock()
-
st_netfd_poll()
-
st_accept()
-
st_connect()
-
st_read()
-
st_read_fully()
-
st_read_resid()
-
st_readv()
-
st_readv_resid()
-
st_write()
-
st_write_resid()
-
st_writev()
-
st_writev_resid()
-
st_recvfrom()
-
st_sendto()
-
st_recvmsg()
-
st_sendmsg()
-
st_poll()
-
-

-


-

- - - - diff --git a/trunk/3rdparty/st-srs/docs/st.html b/trunk/3rdparty/st-srs/docs/st.html deleted file mode 100644 index a6b932a81..000000000 --- a/trunk/3rdparty/st-srs/docs/st.html +++ /dev/null @@ -1,504 +0,0 @@ - - -State Threads for Internet Applications - - -

State Threads for Internet Applications

-

Introduction

-

-State Threads is an application library which provides a -foundation for writing fast and highly scalable Internet Applications -on UNIX-like platforms. It combines the simplicity of the multithreaded -programming paradigm, in which one thread supports each simultaneous -connection, with the performance and scalability of an event-driven -state machine architecture.

- -

1. Definitions

-

- -

1.1 Internet Applications

- -

-An Internet Application (IA) is either a server or client network -application that accepts connections from clients and may or may not -connect to servers. In an IA the arrival or departure of network data -often controls processing (that is, IA is a data-driven application). -For each connection, an IA does some finite amount of work -involving data exchange with its peer, where its peer may be either -a client or a server. -The typical transaction steps of an IA are to accept a connection, -read a request, do some finite and predictable amount of work to -process the request, then write a response to the peer that sent the -request. One example of an IA is a Web server; -the most general example of an IA is a proxy server, because it both -accepts connections from clients and connects to other servers.

-

-We assume that the performance of an IA is constrained by available CPU -cycles rather than network bandwidth or disk I/O (that is, CPU -is a bottleneck resource). -

- - -

1.2 Performance and Scalability

- -

-The performance of an IA is usually evaluated as its -throughput measured in transactions per second or bytes per second (one -can be converted to the other, given the average transaction size). There are -several benchmarks that can be used to measure throughput of Web serving -applications for specific workloads (such as -SPECweb96, -WebStone, -WebBench). -Although there is no common definition for scalability, in general it -expresses the ability of an application to sustain its performance when some -external condition changes. For IAs this external condition is either the -number of clients (also known as "users," "simultaneous connections," or "load -generators") or the underlying hardware system size (number of CPUs, memory -size, and so on). Thus there are two types of scalability: load -scalability and system scalability, respectively. -

-The figure below shows how the throughput of an idealized IA changes with -the increasing number of clients (solid blue line). Initially the throughput -grows linearly (the slope represents the maximal throughput that one client -can provide). Within this initial range, the IA is underutilized and CPUs are -partially idle. Further increase in the number of clients leads to a system -saturation, and the throughput gradually stops growing as all CPUs become fully -utilized. After that point, the throughput stays flat because there are no -more CPU cycles available. -In the real world, however, each simultaneous connection -consumes some computational and memory resources, even when idle, and this -overhead grows with the number of clients. Therefore, the throughput of the -real world IA starts dropping after some point (dashed blue line in the figure -below). The rate at which the throughput drops depends, among other things, on -application design. -

-We say that an application has a good load scalability if it can -sustain its throughput over a wide range of loads. -Interestingly, the SPECweb99 -benchmark somewhat reflects the Web server's load scalability because it -measures the number of clients (load generators) given a mandatory minimal -throughput per client (that is, it measures the server's capacity). -This is unlike SPECweb96 and -other benchmarks that use the throughput as their main metric (see the figure -below). -

-

Figure: Throughput vs. Number of clients -
-

-System scalability is the ability of an application to sustain its -performance per hardware unit (such as a CPU) with the increasing number of -these units. In other words, good system scalability means that doubling the -number of processors will roughly double the application's throughput (dashed -green line). We assume here that the underlying operating system also scales -well. Good system scalability allows you to initially run an application on -the smallest system possible, while retaining the ability to move that -application to a larger system if necessary, without excessive effort or -expense. That is, an application need not be rewritten or even undergo a -major porting effort when changing system size. -

-Although scalability and performance are more important in the case of server -IAs, they should also be considered for some client applications (such as -benchmark load generators). -

- - -

1.3 Concurrency

- -

-Concurrency reflects the parallelism in a system. The two unrelated types -are virtual concurrency and real concurrency. -

    -
  • Virtual (or apparent) concurrency is the number of simultaneous -connections that a system supports. -

    -
  • Real concurrency is the number of hardware devices, including -CPUs, network cards, and disks, that actually allow a system to perform -tasks in parallel. -
-

-An IA must provide virtual concurrency in order to serve many users -simultaneously. -To achieve maximum performance and scalability in doing so, the number of -programming entities than an IA creates to be scheduled by the OS kernel -should be -kept close to (within an order of magnitude of) the real concurrency found on -the system. These programming entities scheduled by the kernel are known as -kernel execution vehicles. Examples of kernel execution vehicles -include Solaris lightweight processes and IRIX kernel threads. -In other words, the number of kernel execution vehicles should be dictated by -the system size and not by the number of simultaneous connections. -

- -

2. Existing Architectures

-

-There are a few different architectures that are commonly used by IAs. -These include the Multi-Process, -Multi-Threaded, and Event-Driven State Machine -architectures. -

- -

2.1 Multi-Process Architecture

- -

-In the Multi-Process (MP) architecture, an individual process is -dedicated to each simultaneous connection. -A process performs all of a transaction's initialization steps -and services a connection completely before moving on to service -a new connection. -

-User sessions in IAs are relatively independent; therefore, no -synchronization between processes handling different connections is -necessary. Because each process has its own private address space, -this architecture is very robust. If a process serving one of the connections -crashes, the other sessions will not be affected. However, to serve many -concurrent connections, an equal number of processes must be employed. -Because processes are kernel entities (and are in fact the heaviest ones), -the number of kernel entities will be at least as large as the number of -concurrent sessions. On most systems, good performance will not be achieved -when more than a few hundred processes are created because of the high -context-switching overhead. In other words, MP applications have poor load -scalability. -

-On the other hand, MP applications have very good system scalability, because -no resources are shared among different processes and there is no -synchronization overhead. -

-The Apache Web Server 1.x ([Reference 1]) uses the MP -architecture on UNIX systems. -

- -

2.2 Multi-Threaded Architecture

- -

-In the Multi-Threaded (MT) architecture, multiple independent threads -of control are employed within a single shared address space. Like a -process in the MP architecture, each thread performs all of a -transaction's initialization steps and services a connection completely -before moving on to service a new connection. -

-Many modern UNIX operating systems implement a many-to-few model when -mapping user-level threads to kernel entities. In this model, an -arbitrarily large number of user-level threads is multiplexed onto a -lesser number of kernel execution vehicles. Kernel execution -vehicles are also known as virtual processors. Whenever a user-level -thread makes a blocking system call, the kernel execution vehicle it is using -will become blocked in the kernel. If there are no other non-blocked kernel -execution vehicles and there are other runnable user-level threads, a new -kernel execution vehicle will be created automatically. This prevents the -application from blocking when it can continue to make useful forward -progress. -

-Because IAs are by nature network I/O driven, all concurrent sessions block on -network I/O at various points. As a result, the number of virtual processors -created in the kernel grows close to the number of user-level threads -(or simultaneous connections). When this occurs, the many-to-few model -effectively degenerates to a one-to-one model. Again, like in -the MP architecture, the number of kernel execution vehicles is dictated by -the number of simultaneous connections rather than by number of CPUs. This -reduces an application's load scalability. However, because kernel threads -(lightweight processes) use fewer resources and are more light-weight than -traditional UNIX processes, an MT application should scale better with load -than an MP application. -

-Unexpectedly, the small number of virtual processors sharing the same address -space in the MT architecture destroys an application's system scalability -because of contention among the threads on various locks. Even if an -application itself is carefully -optimized to avoid lock contention around its own global data (a non-trivial -task), there are still standard library functions and system calls -that use common resources hidden from the application. For example, -on many platforms thread safety of memory allocation routines -(malloc(3), free(3), and so on) is achieved by using a single -global lock. Another example is a per-process file descriptor table. -This common resource table is shared by all kernel execution vehicles within -the same process and must be protected when one modifies it via -certain system calls (such as open(2), close(2), and so on). -In addition to that, maintaining the caches coherent -among CPUs on multiprocessor systems hurts performance when different threads -running on different CPUs modify data items on the same cache line. -

-In order to improve load scalability, some applications employ a different -type of MT architecture: they create one or more thread(s) per task -rather than one thread per connection. For example, one small group -of threads may be responsible for accepting client connections, another -for request processing, and yet another for serving responses. The main -advantage of this architecture is that it eliminates the tight coupling -between the number of threads and number of simultaneous connections. However, -in this architecture, different task-specific thread groups must share common -work queues that must be protected by mutual exclusion locks (a typical -producer-consumer problem). This adds synchronization overhead that causes an -application to perform badly on multiprocessor systems. In other words, in -this architecture, the application's system scalability is sacrificed for the -sake of load scalability. -

-Of course, the usual nightmares of threaded programming, including data -corruption, deadlocks, and race conditions, also make MT architecture (in any -form) non-simplistic to use. -

- - -

2.3 Event-Driven State Machine Architecture

- -

-In the Event-Driven State Machine (EDSM) architecture, a single process -is employed to concurrently process multiple connections. The basics of this -architecture are described in Comer and Stevens -[Reference 2]. -The EDSM architecture performs one basic data-driven step associated with -a particular connection at a time, thus multiplexing many concurrent -connections. The process operates as a state machine that receives an event -and then reacts to it. -

-In the idle state the EDSM calls select(2) or poll(2) to -wait for network I/O events. When a particular file descriptor is ready for -I/O, the EDSM completes the corresponding basic step (usually by invoking a -handler function) and starts the next one. This architecture uses -non-blocking system calls to perform asynchronous network I/O operations. -For more details on non-blocking I/O see Stevens -[Reference 3]. -

-To take advantage of hardware parallelism (real concurrency), multiple -identical processes may be created. This is called Symmetric Multi-Process -EDSM and is used, for example, in the Zeus Web Server -([Reference 4]). To more efficiently multiplex disk I/O, -special "helper" processes may be created. This is called Asymmetric -Multi-Process EDSM and was proposed for Web servers by Druschel -and others [Reference 5]. -

-EDSM is probably the most scalable architecture for IAs. -Because the number of simultaneous connections (virtual concurrency) is -completely decoupled from the number of kernel execution vehicles (processes), -this architecture has very good load scalability. It requires only minimal -user-level resources to create and maintain additional connection. -

-Like MP applications, Multi-Process EDSM has very good system scalability -because no resources are shared among different processes and there is no -synchronization overhead. -

-Unfortunately, the EDSM architecture is monolithic rather than based on the -concept of threads, so new applications generally need to be implemented from -the ground up. In effect, the EDSM architecture simulates threads and their -stacks the hard way. -

- - -

3. State Threads Library

- -

-The State Threads library combines the advantages of all of the above -architectures. The interface preserves the programming simplicity of thread -abstraction, allowing each simultaneous connection to be treated as a separate -thread of execution within a single process. The underlying implementation is -close to the EDSM architecture as the state of each particular concurrent -session is saved in a separate memory segment. -

- -

3.1 State Changes and Scheduling

-

-The state of each concurrent session includes its stack environment -(stack pointer, program counter, CPU registers) and its stack. Conceptually, -a thread context switch can be viewed as a process changing its state. There -are no kernel entities involved other than processes. -Unlike other general-purpose threading libraries, the State Threads library -is fully deterministic. The thread context switch (process state change) can -only happen in a well-known set of functions (at I/O points or at explicit -synchronization points). As a result, process-specific global data does not -have to be protected by mutual exclusion locks in most cases. The entire -application is free to use all the static variables and non-reentrant library -functions it wants, greatly simplifying programming and debugging while -increasing performance. This is somewhat similar to a co-routine model -(co-operatively multitasked threads), except that no explicit yield is needed --- -sooner or later, a thread performs a blocking I/O operation and thus surrenders -control. All threads of execution (simultaneous connections) have the -same priority, so scheduling is non-preemptive, like in the EDSM architecture. -Because IAs are data-driven (processing is limited by the size of network -buffers and data arrival rates), scheduling is non-time-slicing. -

-Only two types of external events are handled by the library's -scheduler, because only these events can be detected by -select(2) or poll(2): I/O events (a file descriptor is ready -for I/O) and time events -(some timeout has expired). However, other types of events (such as -a signal sent to a process) can also be handled by converting them to I/O -events. For example, a signal handling function can perform a write to a pipe -(write(2) is reentrant/asynchronous-safe), thus converting a signal -event to an I/O event. -

-To take advantage of hardware parallelism, as in the EDSM architecture, -multiple processes can be created in either a symmetric or asymmetric manner. -Process management is not in the library's scope but instead is left up to the -application. -

-There are several general-purpose threading libraries that implement a -many-to-one model (many user-level threads to one kernel execution -vehicle), using the same basic techniques as the State Threads library -(non-blocking I/O, event-driven scheduler, and so on). For an example, see GNU -Portable Threads ([Reference 6]). Because they are -general-purpose, these libraries have different objectives than the State -Threads library. The State Threads library is not a general-purpose -threading library, -but rather an application library that targets only certain types of -applications (IAs) in order to achieve the highest possible performance and -scalability for those applications. -

- -

3.2 Scalability

-

-State threads are very lightweight user-level entities, and therefore creating -and maintaining user connections requires minimal resources. An application -using the State Threads library scales very well with the increasing number -of connections. -

-On multiprocessor systems an application should create multiple processes -to take advantage of hardware parallelism. Using multiple separate processes -is the only way to achieve the highest possible system scalability. -This is because duplicating per-process resources is the only way to avoid -significant synchronization overhead on multiprocessor systems. Creating -separate UNIX processes naturally offers resource duplication. Again, -as in the EDSM architecture, there is no connection between the number of -simultaneous connections (which may be very large and changes within a wide -range) and the number of kernel entities (which is usually small and constant). -In other words, the State Threads library makes it possible to multiplex a -large number of simultaneous connections onto a much smaller number of -separate processes, thus allowing an application to scale well with both -the load and system size. -

- -

3.3 Performance

-

-Performance is one of the library's main objectives. The State Threads -library is implemented to minimize the number of system calls and -to make thread creation and context switching as fast as possible. -For example, per-thread signal mask does not exist (unlike -POSIX threads), so there is no need to save and restore a process's -signal mask on every thread context switch. This eliminates two system -calls per context switch. Signal events can be handled much more -efficiently by converting them to I/O events (see above). -

- -

3.4 Portability

-

-The library uses the same general, underlying concepts as the EDSM -architecture, including non-blocking I/O, file descriptors, and -I/O multiplexing. These concepts are available in some form on most -UNIX platforms, making the library very portable across many -flavors of UNIX. There are only a few platform-dependent sections in the -source. -

- -

3.5 State Threads and NSPR

-

-The State Threads library is a derivative of the Netscape Portable -Runtime library (NSPR) [Reference 7]. The primary goal of -NSPR is to provide a platform-independent layer for system facilities, -where system facilities include threads, thread synchronization, and I/O. -Performance and scalability are not the main concern of NSPR. The -State Threads library addresses performance and scalability while -remaining much smaller than NSPR. It is contained in 8 source files -as opposed to more than 400, but provides all the functionality that -is needed to write efficient IAs on UNIX-like platforms. -

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
NSPRState Threads
Lines of code~150,000~3000
Dynamic library size  
(debug version)
IRIX~700 KB~60 KB
Linux~900 KB~70 KB
-

- -

Conclusion

-

-State Threads is an application library which provides a foundation for -writing Internet Applications. To summarize, it has the -following advantages: -

-

    -
  • It allows the design of fast and highly scalable applications. An -application will scale well with both load and number of CPUs. -

    -

  • It greatly simplifies application programming and debugging because, as a -rule, no mutual exclusion locking is necessary and the entire application is -free to use static variables and non-reentrant library functions. -
-

-The library's main limitation: -

-

    -
  • All I/O operations on sockets must use the State Thread library's I/O -functions because only those functions perform thread scheduling and prevent -the application's processes from blocking. -
-

- -

References

-
    - -
  1. Apache Software Foundation, -http://www.apache.org. - -
  2. Douglas E. Comer, David L. Stevens, Internetworking With TCP/IP, -Vol. III: Client-Server Programming And Applications, Second Edition, -Ch. 8, 12. - -
  3. W. Richard Stevens, UNIX Network Programming, Second Edition, -Vol. 1, Ch. 15. - -
  4. Zeus Technology Limited, -http://www.zeus.co.uk. - -
  5. Peter Druschel, Vivek S. Pai, Willy Zwaenepoel, - -Flash: An Efficient and Portable Web Server. In Proceedings of the -USENIX 1999 Annual Technical Conference, Monterey, CA, June 1999. - -
  6. GNU Portable Threads, -http://www.gnu.org/software/pth/. - -
  7. Netscape Portable Runtime, -http://www.mozilla.org/docs/refList/refNSPR/. -
- -

Other resources covering various architectural issues in IAs

-
    -
  1. Dan Kegel, The C10K problem, -http://www.kegel.com/c10k.html. -
  2. -
  3. James C. Hu, Douglas C. Schmidt, Irfan Pyarali, JAWS: Understanding -High Performance Web Systems, -http://www.cs.wustl.edu/~jxh/research/research.html.
  4. -
-

-


-

- -

Portions created by SGI are Copyright © 2000 -Silicon Graphics, Inc. All rights reserved.
-

- - - - diff --git a/trunk/3rdparty/st-srs/docs/timeout_heap.txt b/trunk/3rdparty/st-srs/docs/timeout_heap.txt deleted file mode 100644 index 1582dc129..000000000 --- a/trunk/3rdparty/st-srs/docs/timeout_heap.txt +++ /dev/null @@ -1,60 +0,0 @@ -How the timeout heap works - -As of version 1.5, the State Threads Library represents the queue of -sleeping threads using a heap data structure rather than a sorted -linked list. This improves performance when there is a large number -of sleeping threads, since insertion into a heap takes O(log N) time -while insertion into a sorted list takes O(N) time. For example, in -one test 1000 threads were created, each thread called st_usleep() -with a random time interval, and then all the threads where -immediately interrupted and joined before the sleeps had a chance to -finish. The whole process was repeated 1000 times, for a total of a -million sleep queue insertions and removals. With the old list-based -sleep queue, this test took 100 seconds; now it takes only 12 seconds. - -Heap data structures are typically based on dynamically resized -arrays. However, since the existing ST code base was very nicely -structured around linking the thread objects into pointer-based lists -without the need for any auxiliary data structures, implementing the -heap using a similar nodes-and-pointers based approach seemed more -appropriate for ST than introducing a separate array. - -Thus, the new ST timeout heap works by organizing the existing -_st_thread_t objects in a balanced binary tree, just as they were -previously organized into a doubly-linked, sorted list. The global -_ST_SLEEPQ variable, formerly a linked list head, is now simply a -pointer to the root of this tree, and the root node of the tree is the -thread with the earliest timeout. Each thread object has two child -pointers, "left" and "right", pointing to threads with later timeouts. - -Each node in the tree is numbered with an integer index, corresponding -to the array index in an array-based heap, and the tree is kept fully -balanced and left-adjusted at all times. In other words, the tree -consists of any number of fully populated top levels, followed by a -single bottom level which may be partially populated, such that any -existing nodes form a contiguous block to the left and the spaces for -missing nodes form a contiguous block to the right. For example, if -there are nine threads waiting for a timeout, they are numbered and -arranged in a tree exactly as follows: - - 1 - / \ - 2 3 - / \ / \ - 4 5 6 7 - / \ - 8 9 - -Each node has either no children, only a left child, or both a left -and a right child. Children always time out later than their parents -(this is called the "heap invariant"), but when a node has two -children, their mutual order is unspecified - the left child may time -out before or after the right child. If a node is numbered N, its -left child is numbered 2N, and its right child is numbered 2N+1. - -There is no pointer from a child to its parent; all pointers point -downward. Additions and deletions both work by starting at the root -and traversing the tree towards the leaves, going left or right -according to the binary digits forming the index of the destination -node. As nodes are added or deleted, existing nodes are rearranged to -maintain the heap invariant. diff --git a/trunk/3rdparty/st-srs/event.c b/trunk/3rdparty/st-srs/event.c deleted file mode 100644 index 142882a51..000000000 --- a/trunk/3rdparty/st-srs/event.c +++ /dev/null @@ -1,1413 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * Yahoo! Inc. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#include -#include -#include -#include -#include -#include -#include "common.h" - -#ifdef MD_HAVE_KQUEUE -#include -#endif -#ifdef MD_HAVE_EPOLL -#include -#endif - -#if defined(USE_POLL) && !defined(MD_HAVE_POLL) - /* Force poll usage if explicitly asked for it */ - #define MD_HAVE_POLL -#endif - - -static struct _st_seldata { - fd_set fd_read_set, fd_write_set, fd_exception_set; - int fd_ref_cnts[FD_SETSIZE][3]; - int maxfd; -} *_st_select_data; - -#define _ST_SELECT_MAX_OSFD (_st_select_data->maxfd) -#define _ST_SELECT_READ_SET (_st_select_data->fd_read_set) -#define _ST_SELECT_WRITE_SET (_st_select_data->fd_write_set) -#define _ST_SELECT_EXCEP_SET (_st_select_data->fd_exception_set) -#define _ST_SELECT_READ_CNT(fd) (_st_select_data->fd_ref_cnts[fd][0]) -#define _ST_SELECT_WRITE_CNT(fd) (_st_select_data->fd_ref_cnts[fd][1]) -#define _ST_SELECT_EXCEP_CNT(fd) (_st_select_data->fd_ref_cnts[fd][2]) - - -#ifdef MD_HAVE_POLL -static struct _st_polldata { - struct pollfd *pollfds; - int pollfds_size; - int fdcnt; -} *_st_poll_data; - -#define _ST_POLL_OSFD_CNT (_st_poll_data->fdcnt) -#define _ST_POLLFDS (_st_poll_data->pollfds) -#define _ST_POLLFDS_SIZE (_st_poll_data->pollfds_size) -#endif /* MD_HAVE_POLL */ - - -#ifdef MD_HAVE_KQUEUE -typedef struct _kq_fd_data { - int rd_ref_cnt; - int wr_ref_cnt; - int revents; -} _kq_fd_data_t; - -static struct _st_kqdata { - _kq_fd_data_t *fd_data; - struct kevent *evtlist; - struct kevent *addlist; - struct kevent *dellist; - int fd_data_size; - int evtlist_size; - int addlist_size; - int addlist_cnt; - int dellist_size; - int dellist_cnt; - int kq; - pid_t pid; -} *_st_kq_data; - -#ifndef ST_KQ_MIN_EVTLIST_SIZE -#define ST_KQ_MIN_EVTLIST_SIZE 64 -#endif - -#define _ST_KQ_READ_CNT(fd) (_st_kq_data->fd_data[fd].rd_ref_cnt) -#define _ST_KQ_WRITE_CNT(fd) (_st_kq_data->fd_data[fd].wr_ref_cnt) -#define _ST_KQ_REVENTS(fd) (_st_kq_data->fd_data[fd].revents) -#endif /* MD_HAVE_KQUEUE */ - - -#ifdef MD_HAVE_EPOLL -typedef struct _epoll_fd_data { - int rd_ref_cnt; - int wr_ref_cnt; - int ex_ref_cnt; - int revents; -} _epoll_fd_data_t; - -static struct _st_epolldata { - _epoll_fd_data_t *fd_data; - struct epoll_event *evtlist; - int fd_data_size; - int evtlist_size; - int evtlist_cnt; - int fd_hint; - int epfd; - pid_t pid; -} *_st_epoll_data; - -#ifndef ST_EPOLL_EVTLIST_SIZE - /* Not a limit, just a hint */ - #define ST_EPOLL_EVTLIST_SIZE 4096 -#endif - -#define _ST_EPOLL_READ_CNT(fd) (_st_epoll_data->fd_data[fd].rd_ref_cnt) -#define _ST_EPOLL_WRITE_CNT(fd) (_st_epoll_data->fd_data[fd].wr_ref_cnt) -#define _ST_EPOLL_EXCEP_CNT(fd) (_st_epoll_data->fd_data[fd].ex_ref_cnt) -#define _ST_EPOLL_REVENTS(fd) (_st_epoll_data->fd_data[fd].revents) - -#define _ST_EPOLL_READ_BIT(fd) (_ST_EPOLL_READ_CNT(fd) ? EPOLLIN : 0) -#define _ST_EPOLL_WRITE_BIT(fd) (_ST_EPOLL_WRITE_CNT(fd) ? EPOLLOUT : 0) -#define _ST_EPOLL_EXCEP_BIT(fd) (_ST_EPOLL_EXCEP_CNT(fd) ? EPOLLPRI : 0) -#define _ST_EPOLL_EVENTS(fd) \ - (_ST_EPOLL_READ_BIT(fd)|_ST_EPOLL_WRITE_BIT(fd)|_ST_EPOLL_EXCEP_BIT(fd)) - -#endif /* MD_HAVE_EPOLL */ - -_st_eventsys_t *_st_eventsys = NULL; - - -/***************************************** - * select event system - */ - -ST_HIDDEN int _st_select_init(void) -{ - _st_select_data = (struct _st_seldata *) malloc(sizeof(*_st_select_data)); - if (!_st_select_data) - return -1; - - memset(_st_select_data, 0, sizeof(*_st_select_data)); - _st_select_data->maxfd = -1; - - return 0; -} - -ST_HIDDEN int _st_select_pollset_add(struct pollfd *pds, int npds) -{ - struct pollfd *pd; - struct pollfd *epd = pds + npds; - - /* Do checks up front */ - for (pd = pds; pd < epd; pd++) { - if (pd->fd < 0 || pd->fd >= FD_SETSIZE || !pd->events || - (pd->events & ~(POLLIN | POLLOUT | POLLPRI))) { - errno = EINVAL; - return -1; - } - } - - for (pd = pds; pd < epd; pd++) { - if (pd->events & POLLIN) { - FD_SET(pd->fd, &_ST_SELECT_READ_SET); - _ST_SELECT_READ_CNT(pd->fd)++; - } - if (pd->events & POLLOUT) { - FD_SET(pd->fd, &_ST_SELECT_WRITE_SET); - _ST_SELECT_WRITE_CNT(pd->fd)++; - } - if (pd->events & POLLPRI) { - FD_SET(pd->fd, &_ST_SELECT_EXCEP_SET); - _ST_SELECT_EXCEP_CNT(pd->fd)++; - } - if (_ST_SELECT_MAX_OSFD < pd->fd) - _ST_SELECT_MAX_OSFD = pd->fd; - } - - return 0; -} - -ST_HIDDEN void _st_select_pollset_del(struct pollfd *pds, int npds) -{ - struct pollfd *pd; - struct pollfd *epd = pds + npds; - - for (pd = pds; pd < epd; pd++) { - if (pd->events & POLLIN) { - if (--_ST_SELECT_READ_CNT(pd->fd) == 0) - FD_CLR(pd->fd, &_ST_SELECT_READ_SET); - } - if (pd->events & POLLOUT) { - if (--_ST_SELECT_WRITE_CNT(pd->fd) == 0) - FD_CLR(pd->fd, &_ST_SELECT_WRITE_SET); - } - if (pd->events & POLLPRI) { - if (--_ST_SELECT_EXCEP_CNT(pd->fd) == 0) - FD_CLR(pd->fd, &_ST_SELECT_EXCEP_SET); - } - } -} - -ST_HIDDEN void _st_select_find_bad_fd(void) -{ - _st_clist_t *q; - _st_pollq_t *pq; - int notify; - struct pollfd *pds, *epds; - int pq_max_osfd, osfd; - short events; - - _ST_SELECT_MAX_OSFD = -1; - - for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { - pq = _ST_POLLQUEUE_PTR(q); - notify = 0; - epds = pq->pds + pq->npds; - pq_max_osfd = -1; - - for (pds = pq->pds; pds < epds; pds++) { - osfd = pds->fd; - pds->revents = 0; - if (pds->events == 0) - continue; - if (fcntl(osfd, F_GETFL, 0) < 0) { - pds->revents = POLLNVAL; - notify = 1; - } - if (osfd > pq_max_osfd) { - pq_max_osfd = osfd; - } - } - - if (notify) { - ST_REMOVE_LINK(&pq->links); - pq->on_ioq = 0; - /* - * Decrement the count of descriptors for each descriptor/event - * because this I/O request is being removed from the ioq - */ - for (pds = pq->pds; pds < epds; pds++) { - osfd = pds->fd; - events = pds->events; - if (events & POLLIN) { - if (--_ST_SELECT_READ_CNT(osfd) == 0) { - FD_CLR(osfd, &_ST_SELECT_READ_SET); - } - } - if (events & POLLOUT) { - if (--_ST_SELECT_WRITE_CNT(osfd) == 0) { - FD_CLR(osfd, &_ST_SELECT_WRITE_SET); - } - } - if (events & POLLPRI) { - if (--_ST_SELECT_EXCEP_CNT(osfd) == 0) { - FD_CLR(osfd, &_ST_SELECT_EXCEP_SET); - } - } - } - - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(pq->thread); - pq->thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(pq->thread); - } else { - if (_ST_SELECT_MAX_OSFD < pq_max_osfd) - _ST_SELECT_MAX_OSFD = pq_max_osfd; - } - } -} - -ST_HIDDEN void _st_select_dispatch(void) -{ - struct timeval timeout, *tvp; - fd_set r, w, e; - fd_set *rp, *wp, *ep; - int nfd, pq_max_osfd, osfd; - _st_clist_t *q; - st_utime_t min_timeout; - _st_pollq_t *pq; - int notify; - struct pollfd *pds, *epds; - short events, revents; - - /* - * Assignment of fd_sets - */ - r = _ST_SELECT_READ_SET; - w = _ST_SELECT_WRITE_SET; - e = _ST_SELECT_EXCEP_SET; - - rp = &r; - wp = &w; - ep = &e; - - if (_ST_SLEEPQ == NULL) { - tvp = NULL; - } else { - min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : - (_ST_SLEEPQ->due - _ST_LAST_CLOCK); - timeout.tv_sec = (int) (min_timeout / 1000000); - timeout.tv_usec = (int) (min_timeout % 1000000); - tvp = &timeout; - } - - /* Check for I/O operations */ - nfd = select(_ST_SELECT_MAX_OSFD + 1, rp, wp, ep, tvp); - - /* Notify threads that are associated with the selected descriptors */ - if (nfd > 0) { - _ST_SELECT_MAX_OSFD = -1; - for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { - pq = _ST_POLLQUEUE_PTR(q); - notify = 0; - epds = pq->pds + pq->npds; - pq_max_osfd = -1; - - for (pds = pq->pds; pds < epds; pds++) { - osfd = pds->fd; - events = pds->events; - revents = 0; - if ((events & POLLIN) && FD_ISSET(osfd, rp)) { - revents |= POLLIN; - } - if ((events & POLLOUT) && FD_ISSET(osfd, wp)) { - revents |= POLLOUT; - } - if ((events & POLLPRI) && FD_ISSET(osfd, ep)) { - revents |= POLLPRI; - } - pds->revents = revents; - if (revents) { - notify = 1; - } - if (osfd > pq_max_osfd) { - pq_max_osfd = osfd; - } - } - if (notify) { - ST_REMOVE_LINK(&pq->links); - pq->on_ioq = 0; - /* - * Decrement the count of descriptors for each descriptor/event - * because this I/O request is being removed from the ioq - */ - for (pds = pq->pds; pds < epds; pds++) { - osfd = pds->fd; - events = pds->events; - if (events & POLLIN) { - if (--_ST_SELECT_READ_CNT(osfd) == 0) { - FD_CLR(osfd, &_ST_SELECT_READ_SET); - } - } - if (events & POLLOUT) { - if (--_ST_SELECT_WRITE_CNT(osfd) == 0) { - FD_CLR(osfd, &_ST_SELECT_WRITE_SET); - } - } - if (events & POLLPRI) { - if (--_ST_SELECT_EXCEP_CNT(osfd) == 0) { - FD_CLR(osfd, &_ST_SELECT_EXCEP_SET); - } - } - } - - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(pq->thread); - pq->thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(pq->thread); - } else { - if (_ST_SELECT_MAX_OSFD < pq_max_osfd) - _ST_SELECT_MAX_OSFD = pq_max_osfd; - } - } - } else if (nfd < 0) { - /* - * It can happen when a thread closes file descriptor - * that is being used by some other thread -- BAD! - */ - if (errno == EBADF) - _st_select_find_bad_fd(); - } -} - -ST_HIDDEN int _st_select_fd_new(int osfd) -{ - if (osfd >= FD_SETSIZE) { - errno = EMFILE; - return -1; - } - - return 0; -} - -ST_HIDDEN int _st_select_fd_close(int osfd) -{ - if (_ST_SELECT_READ_CNT(osfd) || _ST_SELECT_WRITE_CNT(osfd) || - _ST_SELECT_EXCEP_CNT(osfd)) { - errno = EBUSY; - return -1; - } - - return 0; -} - -ST_HIDDEN int _st_select_fd_getlimit(void) -{ - return FD_SETSIZE; -} - -static _st_eventsys_t _st_select_eventsys = { - "select", - ST_EVENTSYS_SELECT, - _st_select_init, - _st_select_dispatch, - _st_select_pollset_add, - _st_select_pollset_del, - _st_select_fd_new, - _st_select_fd_close, - _st_select_fd_getlimit -}; - - -#ifdef MD_HAVE_POLL -/***************************************** - * poll event system - */ - -ST_HIDDEN int _st_poll_init(void) -{ - _st_poll_data = (struct _st_polldata *) malloc(sizeof(*_st_poll_data)); - if (!_st_poll_data) - return -1; - - _ST_POLLFDS = (struct pollfd *) malloc(ST_MIN_POLLFDS_SIZE * - sizeof(struct pollfd)); - if (!_ST_POLLFDS) { - free(_st_poll_data); - _st_poll_data = NULL; - return -1; - } - _ST_POLLFDS_SIZE = ST_MIN_POLLFDS_SIZE; - _ST_POLL_OSFD_CNT = 0; - - return 0; -} - -ST_HIDDEN int _st_poll_pollset_add(struct pollfd *pds, int npds) -{ - struct pollfd *pd; - struct pollfd *epd = pds + npds; - - for (pd = pds; pd < epd; pd++) { - if (pd->fd < 0 || !pd->events) { - errno = EINVAL; - return -1; - } - } - - _ST_POLL_OSFD_CNT += npds; - - return 0; -} - -/* ARGSUSED */ -ST_HIDDEN void _st_poll_pollset_del(struct pollfd *pds, int npds) -{ - _ST_POLL_OSFD_CNT -= npds; - ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); -} - -ST_HIDDEN void _st_poll_dispatch(void) -{ - int timeout, nfd; - _st_clist_t *q; - st_utime_t min_timeout; - _st_pollq_t *pq; - struct pollfd *pds, *epds, *pollfds; - - /* - * Build up the array of struct pollfd to wait on. - * If existing array is not big enough, release it and allocate a new one. - */ - ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); - if (_ST_POLL_OSFD_CNT > _ST_POLLFDS_SIZE) { - free(_ST_POLLFDS); - _ST_POLLFDS = (struct pollfd *) malloc((_ST_POLL_OSFD_CNT + 10) * - sizeof(struct pollfd)); - ST_ASSERT(_ST_POLLFDS != NULL); - _ST_POLLFDS_SIZE = _ST_POLL_OSFD_CNT + 10; - } - pollfds = _ST_POLLFDS; - - /* Gather all descriptors into one array */ - for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { - pq = _ST_POLLQUEUE_PTR(q); - memcpy(pollfds, pq->pds, sizeof(struct pollfd) * pq->npds); - pollfds += pq->npds; - } - ST_ASSERT(pollfds <= _ST_POLLFDS + _ST_POLLFDS_SIZE); - - if (_ST_SLEEPQ == NULL) { - timeout = -1; - } else { - min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : - (_ST_SLEEPQ->due - _ST_LAST_CLOCK); - timeout = (int) (min_timeout / 1000); - } - - /* Check for I/O operations */ - nfd = poll(_ST_POLLFDS, _ST_POLL_OSFD_CNT, timeout); - - /* Notify threads that are associated with the selected descriptors */ - if (nfd > 0) { - pollfds = _ST_POLLFDS; - for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { - pq = _ST_POLLQUEUE_PTR(q); - epds = pollfds + pq->npds; - for (pds = pollfds; pds < epds; pds++) { - if (pds->revents) - break; - } - if (pds < epds) { - memcpy(pq->pds, pollfds, sizeof(struct pollfd) * pq->npds); - ST_REMOVE_LINK(&pq->links); - pq->on_ioq = 0; - - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(pq->thread); - pq->thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(pq->thread); - - _ST_POLL_OSFD_CNT -= pq->npds; - ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); - } - pollfds = epds; - } - } -} - -/* ARGSUSED */ -ST_HIDDEN int _st_poll_fd_new(int osfd) -{ - return 0; -} - -/* ARGSUSED */ -ST_HIDDEN int _st_poll_fd_close(int osfd) -{ - /* - * We don't maintain I/O counts for poll event system - * so nothing to check here. - */ - return 0; -} - -ST_HIDDEN int _st_poll_fd_getlimit(void) -{ - /* zero means no specific limit */ - return 0; -} - -static _st_eventsys_t _st_poll_eventsys = { - "poll", - ST_EVENTSYS_POLL, - _st_poll_init, - _st_poll_dispatch, - _st_poll_pollset_add, - _st_poll_pollset_del, - _st_poll_fd_new, - _st_poll_fd_close, - _st_poll_fd_getlimit -}; -#endif /* MD_HAVE_POLL */ - - -#ifdef MD_HAVE_KQUEUE -/***************************************** - * kqueue event system - */ - -ST_HIDDEN int _st_kq_init(void) -{ - int err = 0; - int rv = 0; - - _st_kq_data = (struct _st_kqdata *) calloc(1, sizeof(*_st_kq_data)); - if (!_st_kq_data) - return -1; - - if ((_st_kq_data->kq = kqueue()) < 0) { - err = errno; - rv = -1; - goto cleanup_kq; - } - fcntl(_st_kq_data->kq, F_SETFD, FD_CLOEXEC); - _st_kq_data->pid = getpid(); - - /* - * Allocate file descriptor data array. - * FD_SETSIZE looks like good initial size. - */ - _st_kq_data->fd_data_size = FD_SETSIZE; - _st_kq_data->fd_data = (_kq_fd_data_t *)calloc(_st_kq_data->fd_data_size, sizeof(_kq_fd_data_t)); - if (!_st_kq_data->fd_data) { - err = errno; - rv = -1; - goto cleanup_kq; - } - - /* Allocate event lists */ - _st_kq_data->evtlist_size = ST_KQ_MIN_EVTLIST_SIZE; - _st_kq_data->evtlist = (struct kevent *)malloc(_st_kq_data->evtlist_size * sizeof(struct kevent)); - _st_kq_data->addlist_size = ST_KQ_MIN_EVTLIST_SIZE; - _st_kq_data->addlist = (struct kevent *)malloc(_st_kq_data->addlist_size * sizeof(struct kevent)); - _st_kq_data->dellist_size = ST_KQ_MIN_EVTLIST_SIZE; - _st_kq_data->dellist = (struct kevent *)malloc(_st_kq_data->dellist_size * sizeof(struct kevent)); - if (!_st_kq_data->evtlist || !_st_kq_data->addlist || - !_st_kq_data->dellist) { - err = ENOMEM; - rv = -1; - } - - cleanup_kq: - if (rv < 0) { - if (_st_kq_data->kq >= 0) - close(_st_kq_data->kq); - free(_st_kq_data->fd_data); - free(_st_kq_data->evtlist); - free(_st_kq_data->addlist); - free(_st_kq_data->dellist); - free(_st_kq_data); - _st_kq_data = NULL; - errno = err; - } - - return rv; -} - -ST_HIDDEN int _st_kq_fd_data_expand(int maxfd) -{ - _kq_fd_data_t *ptr; - int n = _st_kq_data->fd_data_size; - - while (maxfd >= n) - n <<= 1; - - ptr = (_kq_fd_data_t *)realloc(_st_kq_data->fd_data, n * sizeof(_kq_fd_data_t)); - if (!ptr) - return -1; - - memset(ptr + _st_kq_data->fd_data_size, 0, (n - _st_kq_data->fd_data_size) * sizeof(_kq_fd_data_t)); - - _st_kq_data->fd_data = ptr; - _st_kq_data->fd_data_size = n; - - return 0; -} - -ST_HIDDEN int _st_kq_addlist_expand(int avail) -{ - struct kevent *ptr; - int n = _st_kq_data->addlist_size; - - while (avail > n - _st_kq_data->addlist_cnt) - n <<= 1; - - ptr = (struct kevent *)realloc(_st_kq_data->addlist, n * sizeof(struct kevent)); - if (!ptr) - return -1; - - _st_kq_data->addlist = ptr; - _st_kq_data->addlist_size = n; - - /* - * Try to expand the result event list too - * (although we don't have to do it). - */ - ptr = (struct kevent *)realloc(_st_kq_data->evtlist, n * sizeof(struct kevent)); - if (ptr) { - _st_kq_data->evtlist = ptr; - _st_kq_data->evtlist_size = n; - } - - return 0; -} - -ST_HIDDEN void _st_kq_addlist_add(const struct kevent *kev) -{ - ST_ASSERT(_st_kq_data->addlist_cnt < _st_kq_data->addlist_size); - memcpy(_st_kq_data->addlist + _st_kq_data->addlist_cnt, kev, sizeof(struct kevent)); - _st_kq_data->addlist_cnt++; -} - -ST_HIDDEN void _st_kq_dellist_add(const struct kevent *kev) -{ - int n = _st_kq_data->dellist_size; - - if (_st_kq_data->dellist_cnt >= n) { - struct kevent *ptr; - - n <<= 1; - ptr = (struct kevent *)realloc(_st_kq_data->dellist, n * sizeof(struct kevent)); - if (!ptr) { - /* See comment in _st_kq_pollset_del() */ - return; - } - - _st_kq_data->dellist = ptr; - _st_kq_data->dellist_size = n; - } - - memcpy(_st_kq_data->dellist + _st_kq_data->dellist_cnt, kev, sizeof(struct kevent)); - _st_kq_data->dellist_cnt++; -} - -ST_HIDDEN int _st_kq_pollset_add(struct pollfd *pds, int npds) -{ - struct kevent kev; - struct pollfd *pd; - struct pollfd *epd = pds + npds; - - /* - * Pollset adding is "atomic". That is, either it succeeded for - * all descriptors in the set or it failed. It means that we - * need to do all the checks up front so we don't have to - * "unwind" if adding of one of the descriptors failed. - */ - for (pd = pds; pd < epd; pd++) { - /* POLLIN and/or POLLOUT must be set, but nothing else */ - if (pd->fd < 0 || !pd->events || (pd->events & ~(POLLIN | POLLOUT))) { - errno = EINVAL; - return -1; - } - if (pd->fd >= _st_kq_data->fd_data_size && - _st_kq_fd_data_expand(pd->fd) < 0) - return -1; - } - - /* - * Make sure we have enough room in the addlist for twice as many - * descriptors as in the pollset (for both READ and WRITE filters). - */ - npds <<= 1; - if (npds > _st_kq_data->addlist_size - _st_kq_data->addlist_cnt && _st_kq_addlist_expand(npds) < 0) - return -1; - - for (pd = pds; pd < epd; pd++) { - if ((pd->events & POLLIN) && (_ST_KQ_READ_CNT(pd->fd)++ == 0)) { - memset(&kev, 0, sizeof(kev)); - kev.ident = pd->fd; - kev.filter = EVFILT_READ; -#ifdef NOTE_EOF - /* Make it behave like select() and poll() */ - kev.fflags = NOTE_EOF; -#endif - kev.flags = (EV_ADD | EV_ONESHOT); - _st_kq_addlist_add(&kev); - } - if ((pd->events & POLLOUT) && (_ST_KQ_WRITE_CNT(pd->fd)++ == 0)) { - memset(&kev, 0, sizeof(kev)); - kev.ident = pd->fd; - kev.filter = EVFILT_WRITE; - kev.flags = (EV_ADD | EV_ONESHOT); - _st_kq_addlist_add(&kev); - } - } - - return 0; -} - -ST_HIDDEN void _st_kq_pollset_del(struct pollfd *pds, int npds) -{ - struct kevent kev; - struct pollfd *pd; - struct pollfd *epd = pds + npds; - - /* - * It's OK if deleting fails because a descriptor will either be - * closed or fire only once (we set EV_ONESHOT flag). - */ - _st_kq_data->dellist_cnt = 0; - for (pd = pds; pd < epd; pd++) { - if ((pd->events & POLLIN) && (--_ST_KQ_READ_CNT(pd->fd) == 0)) { - memset(&kev, 0, sizeof(kev)); - kev.ident = pd->fd; - kev.filter = EVFILT_READ; - kev.flags = EV_DELETE; - _st_kq_dellist_add(&kev); - } - if ((pd->events & POLLOUT) && (--_ST_KQ_WRITE_CNT(pd->fd) == 0)) { - memset(&kev, 0, sizeof(kev)); - kev.ident = pd->fd; - kev.filter = EVFILT_WRITE; - kev.flags = EV_DELETE; - _st_kq_dellist_add(&kev); - } - } - - if (_st_kq_data->dellist_cnt > 0) { - /* - * We do "synchronous" kqueue deletes to avoid deleting - * closed descriptors and other possible problems. - */ - int rv; - do { - /* This kevent() won't block since result list size is 0 */ - rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, _st_kq_data->dellist_cnt, NULL, 0, NULL); - } while (rv < 0 && errno == EINTR); - } -} - -ST_HIDDEN void _st_kq_dispatch(void) -{ - struct timespec timeout, *tsp; - struct kevent kev; - st_utime_t min_timeout; - _st_clist_t *q; - _st_pollq_t *pq; - struct pollfd *pds, *epds; - int nfd, i, osfd, notify, filter; - short events, revents; - - if (_ST_SLEEPQ == NULL) { - tsp = NULL; - } else { - min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); - timeout.tv_sec = (time_t) (min_timeout / 1000000); - timeout.tv_nsec = (long) ((min_timeout % 1000000) * 1000); - tsp = &timeout; - } - - retry_kevent: - /* Check for I/O operations */ - nfd = kevent(_st_kq_data->kq, - _st_kq_data->addlist, _st_kq_data->addlist_cnt, - _st_kq_data->evtlist, _st_kq_data->evtlist_size, tsp); - - _st_kq_data->addlist_cnt = 0; - - if (nfd > 0) { - for (i = 0; i < nfd; i++) { - osfd = _st_kq_data->evtlist[i].ident; - filter = _st_kq_data->evtlist[i].filter; - - if (filter == EVFILT_READ) { - _ST_KQ_REVENTS(osfd) |= POLLIN; - } else if (filter == EVFILT_WRITE) { - _ST_KQ_REVENTS(osfd) |= POLLOUT; - } - if (_st_kq_data->evtlist[i].flags & EV_ERROR) { - if (_st_kq_data->evtlist[i].data == EBADF) { - _ST_KQ_REVENTS(osfd) |= POLLNVAL; - } else { - _ST_KQ_REVENTS(osfd) |= POLLERR; - } - } - } - - _st_kq_data->dellist_cnt = 0; - - for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { - pq = _ST_POLLQUEUE_PTR(q); - notify = 0; - epds = pq->pds + pq->npds; - - for (pds = pq->pds; pds < epds; pds++) { - osfd = pds->fd; - events = pds->events; - revents = (short)(_ST_KQ_REVENTS(osfd) & ~(POLLIN | POLLOUT)); - if ((events & POLLIN) && (_ST_KQ_REVENTS(osfd) & POLLIN)) { - revents |= POLLIN; - } - if ((events & POLLOUT) && (_ST_KQ_REVENTS(osfd) & POLLOUT)) { - revents |= POLLOUT; - } - pds->revents = revents; - if (revents) { - notify = 1; - } - } - if (notify) { - ST_REMOVE_LINK(&pq->links); - pq->on_ioq = 0; - for (pds = pq->pds; pds < epds; pds++) { - osfd = pds->fd; - events = pds->events; - /* - * We set EV_ONESHOT flag so we only need to delete - * descriptor if it didn't fire. - */ - if ((events & POLLIN) && (--_ST_KQ_READ_CNT(osfd) == 0) && ((_ST_KQ_REVENTS(osfd) & POLLIN) == 0)) { - memset(&kev, 0, sizeof(kev)); - kev.ident = osfd; - kev.filter = EVFILT_READ; - kev.flags = EV_DELETE; - _st_kq_dellist_add(&kev); - } - if ((events & POLLOUT) && (--_ST_KQ_WRITE_CNT(osfd) == 0) && ((_ST_KQ_REVENTS(osfd) & POLLOUT) == 0)) { - memset(&kev, 0, sizeof(kev)); - kev.ident = osfd; - kev.filter = EVFILT_WRITE; - kev.flags = EV_DELETE; - _st_kq_dellist_add(&kev); - } - } - - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(pq->thread); - pq->thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(pq->thread); - } - } - - if (_st_kq_data->dellist_cnt > 0) { - int rv; - do { - /* This kevent() won't block since result list size is 0 */ - rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, _st_kq_data->dellist_cnt, NULL, 0, NULL); - } while (rv < 0 && errno == EINTR); - } - - for (i = 0; i < nfd; i++) { - osfd = _st_kq_data->evtlist[i].ident; - _ST_KQ_REVENTS(osfd) = 0; - } - - } else if (nfd < 0) { - if (errno == EBADF && _st_kq_data->pid != getpid()) { - /* We probably forked, reinitialize kqueue */ - if ((_st_kq_data->kq = kqueue()) < 0) { - /* There is nothing we can do here, will retry later */ - return; - } - fcntl(_st_kq_data->kq, F_SETFD, FD_CLOEXEC); - _st_kq_data->pid = getpid(); - /* Re-register all descriptors on ioq with new kqueue */ - memset(_st_kq_data->fd_data, 0, _st_kq_data->fd_data_size * sizeof(_kq_fd_data_t)); - for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { - pq = _ST_POLLQUEUE_PTR(q); - _st_kq_pollset_add(pq->pds, pq->npds); - } - goto retry_kevent; - } - } -} - -ST_HIDDEN int _st_kq_fd_new(int osfd) -{ - if (osfd >= _st_kq_data->fd_data_size && _st_kq_fd_data_expand(osfd) < 0) - return -1; - - return 0; -} - -ST_HIDDEN int _st_kq_fd_close(int osfd) -{ - if (_ST_KQ_READ_CNT(osfd) || _ST_KQ_WRITE_CNT(osfd)) { - errno = EBUSY; - return -1; - } - - return 0; -} - -ST_HIDDEN int _st_kq_fd_getlimit(void) -{ - /* zero means no specific limit */ - return 0; -} - -static _st_eventsys_t _st_kq_eventsys = { - "kqueue", - ST_EVENTSYS_ALT, - _st_kq_init, - _st_kq_dispatch, - _st_kq_pollset_add, - _st_kq_pollset_del, - _st_kq_fd_new, - _st_kq_fd_close, - _st_kq_fd_getlimit -}; -#endif /* MD_HAVE_KQUEUE */ - - -#ifdef MD_HAVE_EPOLL -/***************************************** - * epoll event system - */ - -ST_HIDDEN int _st_epoll_init(void) -{ - int fdlim; - int err = 0; - int rv = 0; - - _st_epoll_data = (struct _st_epolldata *) calloc(1, sizeof(*_st_epoll_data)); - if (!_st_epoll_data) - return -1; - - fdlim = st_getfdlimit(); - _st_epoll_data->fd_hint = (fdlim > 0 && fdlim < ST_EPOLL_EVTLIST_SIZE) ? fdlim : ST_EPOLL_EVTLIST_SIZE; - - if ((_st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint)) < 0) { - err = errno; - rv = -1; - goto cleanup_epoll; - } - fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC); - _st_epoll_data->pid = getpid(); - - /* Allocate file descriptor data array */ - _st_epoll_data->fd_data_size = _st_epoll_data->fd_hint; - _st_epoll_data->fd_data = (_epoll_fd_data_t *)calloc(_st_epoll_data->fd_data_size, sizeof(_epoll_fd_data_t)); - if (!_st_epoll_data->fd_data) { - err = errno; - rv = -1; - goto cleanup_epoll; - } - - /* Allocate event lists */ - _st_epoll_data->evtlist_size = _st_epoll_data->fd_hint; - _st_epoll_data->evtlist = (struct epoll_event *)malloc(_st_epoll_data->evtlist_size * sizeof(struct epoll_event)); - if (!_st_epoll_data->evtlist) { - err = errno; - rv = -1; - } - - cleanup_epoll: - if (rv < 0) { - if (_st_epoll_data->epfd >= 0) - close(_st_epoll_data->epfd); - free(_st_epoll_data->fd_data); - free(_st_epoll_data->evtlist); - free(_st_epoll_data); - _st_epoll_data = NULL; - errno = err; - } - - return rv; -} - -ST_HIDDEN int _st_epoll_fd_data_expand(int maxfd) -{ - _epoll_fd_data_t *ptr; - int n = _st_epoll_data->fd_data_size; - - while (maxfd >= n) - n <<= 1; - - ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, n * sizeof(_epoll_fd_data_t)); - if (!ptr) - return -1; - - memset(ptr + _st_epoll_data->fd_data_size, 0, (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t)); - - _st_epoll_data->fd_data = ptr; - _st_epoll_data->fd_data_size = n; - - return 0; -} - -ST_HIDDEN void _st_epoll_evtlist_expand(void) -{ - struct epoll_event *ptr; - int n = _st_epoll_data->evtlist_size; - - while (_st_epoll_data->evtlist_cnt > n) - n <<= 1; - - ptr = (struct epoll_event *)realloc(_st_epoll_data->evtlist, n * sizeof(struct epoll_event)); - if (ptr) { - _st_epoll_data->evtlist = ptr; - _st_epoll_data->evtlist_size = n; - } -} - -ST_HIDDEN void _st_epoll_pollset_del(struct pollfd *pds, int npds) -{ - struct epoll_event ev; - struct pollfd *pd; - struct pollfd *epd = pds + npds; - int old_events, events, op; - - /* - * It's more or less OK if deleting fails because a descriptor - * will either be closed or deleted in dispatch function after - * it fires. - */ - for (pd = pds; pd < epd; pd++) { - old_events = _ST_EPOLL_EVENTS(pd->fd); - - if (pd->events & POLLIN) - _ST_EPOLL_READ_CNT(pd->fd)--; - if (pd->events & POLLOUT) - _ST_EPOLL_WRITE_CNT(pd->fd)--; - if (pd->events & POLLPRI) - _ST_EPOLL_EXCEP_CNT(pd->fd)--; - - events = _ST_EPOLL_EVENTS(pd->fd); - /* - * The _ST_EPOLL_REVENTS check below is needed so we can use - * this function inside dispatch(). Outside of dispatch() - * _ST_EPOLL_REVENTS is always zero for all descriptors. - */ - if (events != old_events && _ST_EPOLL_REVENTS(pd->fd) == 0) { - op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; - ev.events = events; - ev.data.fd = pd->fd; - if (epoll_ctl(_st_epoll_data->epfd, op, pd->fd, &ev) == 0 && op == EPOLL_CTL_DEL) { - _st_epoll_data->evtlist_cnt--; - } - } - } -} - -ST_HIDDEN int _st_epoll_pollset_add(struct pollfd *pds, int npds) -{ - struct epoll_event ev; - int i, fd; - int old_events, events, op; - - /* Do as many checks as possible up front */ - for (i = 0; i < npds; i++) { - fd = pds[i].fd; - if (fd < 0 || !pds[i].events || - (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) { - errno = EINVAL; - return -1; - } - if (fd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(fd) < 0) - return -1; - } - - for (i = 0; i < npds; i++) { - fd = pds[i].fd; - old_events = _ST_EPOLL_EVENTS(fd); - - if (pds[i].events & POLLIN) - _ST_EPOLL_READ_CNT(fd)++; - if (pds[i].events & POLLOUT) - _ST_EPOLL_WRITE_CNT(fd)++; - if (pds[i].events & POLLPRI) - _ST_EPOLL_EXCEP_CNT(fd)++; - - events = _ST_EPOLL_EVENTS(fd); - if (events != old_events) { - op = old_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; - ev.events = events; - ev.data.fd = fd; - if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 && (op != EPOLL_CTL_ADD || errno != EEXIST)) - break; - if (op == EPOLL_CTL_ADD) { - _st_epoll_data->evtlist_cnt++; - if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size) - _st_epoll_evtlist_expand(); - } - } - } - - if (i < npds) { - /* Error */ - int err = errno; - /* Unroll the state */ - _st_epoll_pollset_del(pds, i + 1); - errno = err; - return -1; - } - - return 0; -} - -ST_HIDDEN void _st_epoll_dispatch(void) -{ - st_utime_t min_timeout; - _st_clist_t *q; - _st_pollq_t *pq; - struct pollfd *pds, *epds; - struct epoll_event ev; - int timeout, nfd, i, osfd, notify; - int events, op; - short revents; - - if (_ST_SLEEPQ == NULL) { - timeout = -1; - } else { - min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); - timeout = (int) (min_timeout / 1000); - } - - if (_st_epoll_data->pid != getpid()) { - /* We probably forked, reinitialize epoll set */ - close(_st_epoll_data->epfd); - _st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint); - if (_st_epoll_data->epfd < 0) { - /* There is nothing we can do here, will retry later */ - return; - } - fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC); - _st_epoll_data->pid = getpid(); - - /* Put all descriptors on ioq into new epoll set */ - memset(_st_epoll_data->fd_data, 0, _st_epoll_data->fd_data_size * sizeof(_epoll_fd_data_t)); - _st_epoll_data->evtlist_cnt = 0; - for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { - pq = _ST_POLLQUEUE_PTR(q); - _st_epoll_pollset_add(pq->pds, pq->npds); - } - } - - /* Check for I/O operations */ - nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, _st_epoll_data->evtlist_size, timeout); - - if (nfd > 0) { - for (i = 0; i < nfd; i++) { - osfd = _st_epoll_data->evtlist[i].data.fd; - _ST_EPOLL_REVENTS(osfd) = _st_epoll_data->evtlist[i].events; - if (_ST_EPOLL_REVENTS(osfd) & (EPOLLERR | EPOLLHUP)) { - /* Also set I/O bits on error */ - _ST_EPOLL_REVENTS(osfd) |= _ST_EPOLL_EVENTS(osfd); - } - } - - for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { - pq = _ST_POLLQUEUE_PTR(q); - notify = 0; - epds = pq->pds + pq->npds; - - for (pds = pq->pds; pds < epds; pds++) { - if (_ST_EPOLL_REVENTS(pds->fd) == 0) { - pds->revents = 0; - continue; - } - osfd = pds->fd; - events = pds->events; - revents = 0; - if ((events & POLLIN) && (_ST_EPOLL_REVENTS(osfd) & EPOLLIN)) - revents |= POLLIN; - if ((events & POLLOUT) && (_ST_EPOLL_REVENTS(osfd) & EPOLLOUT)) - revents |= POLLOUT; - if ((events & POLLPRI) && (_ST_EPOLL_REVENTS(osfd) & EPOLLPRI)) - revents |= POLLPRI; - if (_ST_EPOLL_REVENTS(osfd) & EPOLLERR) - revents |= POLLERR; - if (_ST_EPOLL_REVENTS(osfd) & EPOLLHUP) - revents |= POLLHUP; - - pds->revents = revents; - if (revents) { - notify = 1; - } - } - if (notify) { - ST_REMOVE_LINK(&pq->links); - pq->on_ioq = 0; - /* - * Here we will only delete/modify descriptors that - * didn't fire (see comments in _st_epoll_pollset_del()). - */ - _st_epoll_pollset_del(pq->pds, pq->npds); - - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(pq->thread); - pq->thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(pq->thread); - } - } - - for (i = 0; i < nfd; i++) { - /* Delete/modify descriptors that fired */ - osfd = _st_epoll_data->evtlist[i].data.fd; - _ST_EPOLL_REVENTS(osfd) = 0; - events = _ST_EPOLL_EVENTS(osfd); - op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; - ev.events = events; - ev.data.fd = osfd; - if (epoll_ctl(_st_epoll_data->epfd, op, osfd, &ev) == 0 && op == EPOLL_CTL_DEL) { - _st_epoll_data->evtlist_cnt--; - } - } - } -} - -ST_HIDDEN int _st_epoll_fd_new(int osfd) -{ - if (osfd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(osfd) < 0) - return -1; - - return 0; -} - -ST_HIDDEN int _st_epoll_fd_close(int osfd) -{ - if (_ST_EPOLL_READ_CNT(osfd) || _ST_EPOLL_WRITE_CNT(osfd) || _ST_EPOLL_EXCEP_CNT(osfd)) { - errno = EBUSY; - return -1; - } - - return 0; -} - -ST_HIDDEN int _st_epoll_fd_getlimit(void) -{ - /* zero means no specific limit */ - return 0; -} - -/* - * Check if epoll functions are just stubs. - */ -ST_HIDDEN int _st_epoll_is_supported(void) -{ - struct epoll_event ev; - - ev.events = EPOLLIN; - ev.data.ptr = NULL; - /* Guaranteed to fail */ - epoll_ctl(-1, EPOLL_CTL_ADD, -1, &ev); - - return (errno != ENOSYS); -} - -static _st_eventsys_t _st_epoll_eventsys = { - "epoll", - ST_EVENTSYS_ALT, - _st_epoll_init, - _st_epoll_dispatch, - _st_epoll_pollset_add, - _st_epoll_pollset_del, - _st_epoll_fd_new, - _st_epoll_fd_close, - _st_epoll_fd_getlimit -}; -#endif /* MD_HAVE_EPOLL */ - - -/***************************************** - * Public functions - */ - -int st_set_eventsys(int eventsys) -{ - if (_st_eventsys) { - errno = EBUSY; - return -1; - } - - switch (eventsys) { - case ST_EVENTSYS_DEFAULT: -#ifdef USE_POLL - _st_eventsys = &_st_poll_eventsys; -#else - _st_eventsys = &_st_select_eventsys; -#endif - break; - case ST_EVENTSYS_SELECT: - _st_eventsys = &_st_select_eventsys; - break; -#ifdef MD_HAVE_POLL - case ST_EVENTSYS_POLL: - _st_eventsys = &_st_poll_eventsys; - break; -#endif - case ST_EVENTSYS_ALT: -#if defined (MD_HAVE_KQUEUE) - _st_eventsys = &_st_kq_eventsys; -#elif defined (MD_HAVE_EPOLL) - if (_st_epoll_is_supported()) - _st_eventsys = &_st_epoll_eventsys; -#endif - break; - default: - errno = EINVAL; - return -1; - } - - return 0; -} - -int st_get_eventsys(void) -{ - return _st_eventsys ? _st_eventsys->val : -1; -} - -const char *st_get_eventsys_name(void) -{ - return _st_eventsys ? _st_eventsys->name : ""; -} - diff --git a/trunk/3rdparty/st-srs/examples/Makefile b/trunk/3rdparty/st-srs/examples/Makefile deleted file mode 100644 index 31c0a6e24..000000000 --- a/trunk/3rdparty/st-srs/examples/Makefile +++ /dev/null @@ -1,115 +0,0 @@ -# -# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. -# All Rights Reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of Silicon Graphics, Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -########################## -# Supported OSes: -# -# AIX -# FREEBSD -# HPUX -# HPUX_64 -# IRIX -# IRIX_64 -# LINUX -# LINUX_IA64 -# NETBSD -# OPENBSD -# OSF1 -# SOLARIS -# SOLARIS_64 - -########################## - -CC = cc - -SHELL = /bin/sh -ECHO = /bin/echo - -DEPTH = .. -BUILD = -TARGETDIR = - -DEFINES = -CFLAGS = -OTHER_FLAGS = - -OBJDIR = $(DEPTH)/$(TARGETDIR) -INCDIR = $(DEPTH)/$(TARGETDIR) -LIBST = $(OBJDIR)/libst.a -HEADER = $(INCDIR)/st.h - -LIBRESOLV = -EXTRALIBS = - -ifeq ($(OS),) -EXAMPLES = unknown -else -EXAMPLES = $(OBJDIR)/lookupdns $(OBJDIR)/proxy $(OBJDIR)/server -endif - - -########################## -# Platform section. -# - -ifeq (DARWIN, $(findstring DARWIN, $(OS))) -LIBRESOLV = -lresolv -endif - -ifeq (LINUX, $(findstring LINUX, $(OS))) -LIBRESOLV = -lresolv -endif - -ifeq (SOLARIS, $(findstring SOLARIS, $(OS))) -LIBRESOLV = -lresolv -EXTRALIBS = -lsocket -lnsl -endif - -# -# End of platform section. -########################## - - -all: $(EXAMPLES) - -$(OBJDIR)/lookupdns: lookupdns.c $(OBJDIR)/res.o $(LIBST) $(HEADER) - $(CC) $(CFLAGS) -I$(INCDIR) lookupdns.c $(OBJDIR)/res.o $(LIBST) $(LIBRESOLV) $(EXTRALIBS) -o $@ - -$(OBJDIR)/proxy: proxy.c $(LIBST) $(HEADER) - $(CC) $(CFLAGS) -I$(INCDIR) proxy.c $(LIBST) $(EXTRALIBS) -o $@ - -$(OBJDIR)/server: server.c $(OBJDIR)/error.o $(LIBST) $(HEADER) - $(CC) $(CFLAGS) -I$(INCDIR) server.c $(OBJDIR)/error.o $(LIBST) $(EXTRALIBS) -o $@ - -$(OBJDIR)/%.o: %.c - $(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@ - -.DEFAULT: - @cd $(DEPTH); $(MAKE) $@ - diff --git a/trunk/3rdparty/st-srs/examples/README b/trunk/3rdparty/st-srs/examples/README deleted file mode 100644 index 646d4f623..000000000 --- a/trunk/3rdparty/st-srs/examples/README +++ /dev/null @@ -1,98 +0,0 @@ -Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. -All Rights Reserved. - - -This directory contains three example programs. - - ---------------------------------------------------------------------------- - -PROGRAM - - lookupdns - -FILES - - lookupdns.c - res.c - -USAGE - - lookupdns [] ... - -DESCRIPTION - - This program performs asynchronous DNS host name resolution and reports - IP address for each specified as a command line argument. - One ST thread is created for each host name. All threads do host name - resolution concurrently. - - ---------------------------------------------------------------------------- - -PROGRAM - - proxy - -FILES - - proxy.c - -USAGE - - proxy -l -r [-p ] [-S] - - -l bind to local address specified as []: - -r connect to remote address specified as : - -p create specified number of processes - -S serialize accept() calls from different processes - on the same listening socket (if needed). - -DESCRIPTION - - This program acts as a generic gateway. It listens for connections to a - local address. Upon accepting a client connection, it connects to the - specified remote address and then just pumps the data through without any - modification. - - ---------------------------------------------------------------------------- - -PROGRAM - - server - -FILES - - server.c - error.c - -USAGE - - server -l [] - - -l open all log files in specified directory. - - Possible options: - - -b : bind to specified address (multiple addresses - are permitted) - -p create specified number of processes - -t : specify thread limits per listening socket - across all processes - -u change server's user id to specified value - -q set max length of pending connections queue - -a enable access logging - -i run in interactive mode (useful for debugging) - -S serialize accept() calls from different processes - on the same listening socket (if needed). - -DESCRIPTION - - This program is a general server example. It accepts a client connection - and outputs a short HTML page. It can be easily adapted to provide - other services. - - ---------------------------------------------------------------------------- - diff --git a/trunk/3rdparty/st-srs/examples/error.c b/trunk/3rdparty/st-srs/examples/error.c deleted file mode 100644 index 0b2e77287..000000000 --- a/trunk/3rdparty/st-srs/examples/error.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include "st.h" - -/* - * Simple error reporting functions. - * Suggested in W. Richard Stevens' "Advanced Programming in UNIX - * Environment". - */ - -#define MAXLINE 4096 /* max line length */ - -static void err_doit(int, int, const char *, va_list); - - -/* - * Nonfatal error related to a system call. - * Print a message and return. - */ -void err_sys_report(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 1, fmt, ap); - va_end(ap); -} - - -/* - * Fatal error related to a system call. - * Print a message and terminate. - */ -void err_sys_quit(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 1, fmt, ap); - va_end(ap); - exit(1); -} - - -/* - * Fatal error related to a system call. - * Print a message, dump core, and terminate. - */ -void err_sys_dump(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 1, fmt, ap); - va_end(ap); - abort(); /* dump core and terminate */ - exit(1); /* shouldn't get here */ -} - - -/* - * Nonfatal error unrelated to a system call. - * Print a message and return. - */ -void err_report(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 0, fmt, ap); - va_end(ap); -} - - -/* - * Fatal error unrelated to a system call. - * Print a message and terminate. - */ -void err_quit(int fd, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - err_doit(fd, 0, fmt, ap); - va_end(ap); - exit(1); -} - - -/* - * Return a pointer to a string containing current time. - */ -char *err_tstamp(void) -{ - static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - static char str[32]; - static time_t lastt = 0; - struct tm *tmp; - time_t currt = st_time(); - - if (currt == lastt) - return str; - - tmp = localtime(&currt); - sprintf(str, "[%02d/%s/%d:%02d:%02d:%02d] ", tmp->tm_mday, - months[tmp->tm_mon], 1900 + tmp->tm_year, tmp->tm_hour, - tmp->tm_min, tmp->tm_sec); - lastt = currt; - - return str; -} - - -/* - * Print a message and return to caller. - * Caller specifies "errnoflag". - */ -static void err_doit(int fd, int errnoflag, const char *fmt, va_list ap) -{ - int errno_save; - char buf[MAXLINE]; - - errno_save = errno; /* value caller might want printed */ - strcpy(buf, err_tstamp()); /* prepend a message with time stamp */ - vsprintf(buf + strlen(buf), fmt, ap); - if (errnoflag) - sprintf(buf + strlen(buf), ": %s\n", strerror(errno_save)); - else - strcat(buf, "\n"); - write(fd, buf, strlen(buf)); - errno = errno_save; -} - diff --git a/trunk/3rdparty/st-srs/examples/lookupdns.c b/trunk/3rdparty/st-srs/examples/lookupdns.c deleted file mode 100644 index 98f6ec5d8..000000000 --- a/trunk/3rdparty/st-srs/examples/lookupdns.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) -#define NETDB_INTERNAL h_NETDB_INTERNAL -#endif - -/* Resolution timeout (in microseconds) */ -#define TIMEOUT (2*1000000LL) - -/* External function defined in the res.c file */ -int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout); - - -void *do_resolve(void *host) -{ - struct in_addr addr; - - /* Use dns_getaddr() instead of gethostbyname(3) to get IP address */ - if (dns_getaddr(host, &addr, TIMEOUT) < 0) { - fprintf(stderr, "dns_getaddr: can't resolve %s: ", (char *)host); - if (h_errno == NETDB_INTERNAL) - perror(""); - else - herror(""); - } else - printf("%-40s %s\n", (char *)host, inet_ntoa(addr)); - - return NULL; -} - - -/* - * Asynchronous DNS host name resolution. This program creates one - * ST thread for each host name (specified as command line arguments). - * All threads do host name resolution concurrently. - */ -int main(int argc, char *argv[]) -{ - int i; - - if (argc < 2) { - fprintf(stderr, "Usage: %s [] ...\n", argv[0]); - exit(1); - } - - if (st_init() < 0) { - perror("st_init"); - exit(1); - } - - for (i = 1; i < argc; i++) { - /* Create a separate thread for each host name */ - if (st_thread_create(do_resolve, argv[i], 0, 0) == NULL) { - perror("st_thread_create"); - exit(1); - } - } - - st_thread_exit(NULL); - - /* NOTREACHED */ - return 1; -} - diff --git a/trunk/3rdparty/st-srs/examples/proxy.c b/trunk/3rdparty/st-srs/examples/proxy.c deleted file mode 100644 index 2f4636d6b..000000000 --- a/trunk/3rdparty/st-srs/examples/proxy.c +++ /dev/null @@ -1,541 +0,0 @@ -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - -#define IOBUFSIZE (16*1024) - -#define IOV_LEN 256 -#define IOV_COUNT (IOBUFSIZE / IOV_LEN) - -#ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff -#endif - -static char *prog; /* Program name */ -static struct sockaddr_in rmt_addr; /* Remote address */ - -static unsigned long testing; -#define TESTING_VERBOSE 0x1 -#define TESTING_READV 0x2 -#define TESTING_READ_RESID 0x4 -#define TESTING_WRITEV 0x8 -#define TESTING_WRITE_RESID 0x10 - -static void read_address(const char *str, struct sockaddr_in *sin); -static void start_daemon(void); -static int cpu_count(void); -static void set_concurrency(int nproc); -static void *handle_request(void *arg); -static void print_sys_error(const char *msg); - - -/* - * This program acts as a generic gateway. It listens for connections - * to a local address ('-l' option). Upon accepting a client connection, - * it connects to the specified remote address ('-r' option) and then - * just pumps the data through without any modification. - */ -int main(int argc, char *argv[]) -{ - extern char *optarg; - int opt, sock, n; - int laddr, raddr, num_procs, alt_ev, one_process; - int serialize_accept = 0; - struct sockaddr_in lcl_addr, cli_addr; - st_netfd_t cli_nfd, srv_nfd; - - prog = argv[0]; - num_procs = laddr = raddr = alt_ev = one_process = 0; - - /* Parse arguments */ - while((opt = getopt(argc, argv, "l:r:p:Saht:X")) != EOF) { - switch (opt) { - case 'a': - alt_ev = 1; - break; - case 'l': - read_address(optarg, &lcl_addr); - laddr = 1; - break; - case 'r': - read_address(optarg, &rmt_addr); - if (rmt_addr.sin_addr.s_addr == INADDR_ANY) { - fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg); - exit(1); - } - raddr = 1; - break; - case 'p': - num_procs = atoi(optarg); - if (num_procs < 1) { - fprintf(stderr, "%s: invalid number of processes: %s\n", prog, optarg); - exit(1); - } - break; - case 'S': - /* - * Serialization decision is tricky on some platforms. For example, - * Solaris 2.6 and above has kernel sockets implementation, so supposedly - * there is no need for serialization. The ST library may be compiled - * on one OS version, but used on another, so the need for serialization - * should be determined at run time by the application. Since it's just - * an example, the serialization decision is left up to user. - * Only on platforms where the serialization is never needed on any OS - * version st_netfd_serialize_accept() is a no-op. - */ - serialize_accept = 1; - break; - case 't': - testing = strtoul(optarg, NULL, 0); - break; - case 'X': - one_process = 1; - break; - case 'h': - case '?': - fprintf(stderr, "Usage: %s [options] -l <[host]:port> -r \n", - prog); - fprintf(stderr, "options are:\n"); - fprintf(stderr, " -p number of parallel processes\n"); - fprintf(stderr, " -S serialize accepts\n"); - fprintf(stderr, " -a use alternate event system\n"); -#ifdef DEBUG - fprintf(stderr, " -t mask testing/debugging mode\n"); - fprintf(stderr, " -X one process, don't daemonize\n"); -#endif - exit(1); - } - } - if (!laddr) { - fprintf(stderr, "%s: local address required\n", prog); - exit(1); - } - if (!raddr) { - fprintf(stderr, "%s: remote address required\n", prog); - exit(1); - } - if (num_procs == 0) - num_procs = cpu_count(); - - fprintf(stderr, "%s: starting proxy daemon on %s:%d\n", prog, - inet_ntoa(lcl_addr.sin_addr), ntohs(lcl_addr.sin_port)); - - /* Start the daemon */ - if (one_process) - num_procs = 1; - else - start_daemon(); - - if (alt_ev) - st_set_eventsys(ST_EVENTSYS_ALT); - - /* Initialize the ST library */ - if (st_init() < 0) { - print_sys_error("st_init"); - exit(1); - } - - /* Create and bind listening socket */ - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - print_sys_error("socket"); - exit(1); - } - n = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) { - print_sys_error("setsockopt"); - exit(1); - } - if (bind(sock, (struct sockaddr *)&lcl_addr, sizeof(lcl_addr)) < 0) { - print_sys_error("bind"); - exit(1); - } - listen(sock, 128); - if ((srv_nfd = st_netfd_open_socket(sock)) == NULL) { - print_sys_error("st_netfd_open"); - exit(1); - } - /* See the comment regarding serialization decision above */ - if (num_procs > 1 && serialize_accept && st_netfd_serialize_accept(srv_nfd) - < 0) { - print_sys_error("st_netfd_serialize_accept"); - exit(1); - } - - /* Start server processes */ - if (!one_process) - set_concurrency(num_procs); - - for ( ; ; ) { - n = sizeof(cli_addr); - cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&cli_addr, &n, - ST_UTIME_NO_TIMEOUT); - if (cli_nfd == NULL) { - print_sys_error("st_accept"); - exit(1); - } - if (st_thread_create(handle_request, cli_nfd, 0, 0) == NULL) { - print_sys_error("st_thread_create"); - exit(1); - } - } - - /* NOTREACHED */ - return 1; -} - - -static void read_address(const char *str, struct sockaddr_in *sin) -{ - char host[128], *p; - struct hostent *hp; - unsigned short port; - - strcpy(host, str); - if ((p = strchr(host, ':')) == NULL) { - fprintf(stderr, "%s: invalid address: %s\n", prog, host); - exit(1); - } - *p++ = '\0'; - port = (unsigned short) atoi(p); - if (port < 1) { - fprintf(stderr, "%s: invalid port: %s\n", prog, p); - exit(1); - } - - memset(sin, 0, sizeof(struct sockaddr_in)); - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - if (host[0] == '\0') { - sin->sin_addr.s_addr = INADDR_ANY; - return; - } - sin->sin_addr.s_addr = inet_addr(host); - if (sin->sin_addr.s_addr == INADDR_NONE) { - /* not dotted-decimal */ - if ((hp = gethostbyname(host)) == NULL) { - fprintf(stderr, "%s: can't resolve address: %s\n", prog, host); - exit(1); - } - memcpy(&sin->sin_addr, hp->h_addr, hp->h_length); - } -} - -#ifdef DEBUG -static void show_iov(const struct iovec *iov, int niov) -{ - int i; - size_t total; - - printf("iov %p has %d entries:\n", iov, niov); - total = 0; - for (i = 0; i < niov; i++) { - printf("iov[%3d] iov_base=%p iov_len=0x%lx(%lu)\n", - i, iov[i].iov_base, (unsigned long) iov[i].iov_len, - (unsigned long) iov[i].iov_len); - total += iov[i].iov_len; - } - printf("total 0x%lx(%ld)\n", (unsigned long) total, (unsigned long) total); -} - -/* - * This version is tricked out to test all the - * st_(read|write)v?(_resid)? variants. Use the non-DEBUG version for - * anything serious. st_(read|write) are all this function really - * needs. - */ -static int pass(st_netfd_t in, st_netfd_t out) -{ - char buf[IOBUFSIZE]; - struct iovec iov[IOV_COUNT]; - int ioviter, nw, nr; - - if (testing & TESTING_READV) { - for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) { - iov[ioviter].iov_base = &buf[ioviter * IOV_LEN]; - iov[ioviter].iov_len = IOV_LEN; - } - if (testing & TESTING_VERBOSE) { - printf("readv(%p)...\n", in); - show_iov(iov, IOV_COUNT); - } - if (testing & TESTING_READ_RESID) { - struct iovec *riov = iov; - int riov_cnt = IOV_COUNT; - if (st_readv_resid(in, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { - if (testing & TESTING_VERBOSE) { - printf("resid\n"); - show_iov(riov, riov_cnt); - printf("full\n"); - show_iov(iov, IOV_COUNT); - } - nr = 0; - for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) - nr += iov[ioviter].iov_len; - nr = IOBUFSIZE - nr; - } else - nr = -1; - } else - nr = (int) st_readv(in, iov, IOV_COUNT, ST_UTIME_NO_TIMEOUT); - } else { - if (testing & TESTING_READ_RESID) { - size_t resid = IOBUFSIZE; - if (st_read_resid(in, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) - nr = IOBUFSIZE - resid; - else - nr = -1; - } else - nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); - } - if (testing & TESTING_VERBOSE) - printf("got 0x%x(%d) E=%d\n", nr, nr, errno); - - if (nr <= 0) - return 0; - - if (testing & TESTING_WRITEV) { - for (nw = 0, ioviter = 0; nw < nr; - nw += iov[ioviter].iov_len, ioviter++) { - iov[ioviter].iov_base = &buf[nw]; - iov[ioviter].iov_len = nr - nw; - if (iov[ioviter].iov_len > IOV_LEN) - iov[ioviter].iov_len = IOV_LEN; - } - if (testing & TESTING_VERBOSE) { - printf("writev(%p)...\n", out); - show_iov(iov, ioviter); - } - if (testing & TESTING_WRITE_RESID) { - struct iovec *riov = iov; - int riov_cnt = ioviter; - if (st_writev_resid(out, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { - if (testing & TESTING_VERBOSE) { - printf("resid\n"); - show_iov(riov, riov_cnt); - printf("full\n"); - show_iov(iov, ioviter); - } - nw = 0; - while (--ioviter >= 0) - nw += iov[ioviter].iov_len; - nw = nr - nw; - } else - nw = -1; - } else - nw = st_writev(out, iov, ioviter, ST_UTIME_NO_TIMEOUT); - } else { - if (testing & TESTING_WRITE_RESID) { - size_t resid = nr; - if (st_write_resid(out, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) - nw = nr - resid; - else - nw = -1; - } else - nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); - } - if (testing & TESTING_VERBOSE) - printf("put 0x%x(%d) E=%d\n", nw, nw, errno); - - if (nw != nr) - return 0; - - return 1; -} -#else /* DEBUG */ -/* - * This version is the simple one suitable for serious use. - */ -static int pass(st_netfd_t in, st_netfd_t out) -{ - char buf[IOBUFSIZE]; - int nw, nr; - - nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); - if (nr <= 0) - return 0; - - nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); - if (nw != nr) - return 0; - - return 1; -} -#endif - -static void *handle_request(void *arg) -{ - struct pollfd pds[2]; - st_netfd_t cli_nfd, rmt_nfd; - int sock; - - cli_nfd = (st_netfd_t) arg; - pds[0].fd = st_netfd_fileno(cli_nfd); - pds[0].events = POLLIN; - - /* Connect to remote host */ - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - print_sys_error("socket"); - goto done; - } - if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) { - print_sys_error("st_netfd_open_socket"); - close(sock); - goto done; - } - if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr, - sizeof(rmt_addr), ST_UTIME_NO_TIMEOUT) < 0) { - print_sys_error("st_connect"); - st_netfd_close(rmt_nfd); - goto done; - } - pds[1].fd = sock; - pds[1].events = POLLIN; - - /* - * Now just pump the data through. - * XXX This should use one thread for each direction for true full-duplex. - */ - for ( ; ; ) { - pds[0].revents = 0; - pds[1].revents = 0; - - if (st_poll(pds, 2, ST_UTIME_NO_TIMEOUT) <= 0) { - print_sys_error("st_poll"); - break; - } - - if (pds[0].revents & POLLIN) { - if (!pass(cli_nfd, rmt_nfd)) - break; - } - - if (pds[1].revents & POLLIN) { - if (!pass(rmt_nfd, cli_nfd)) - break; - } - } - st_netfd_close(rmt_nfd); - -done: - - st_netfd_close(cli_nfd); - - return NULL; -} - -static void start_daemon(void) -{ - pid_t pid; - - /* Start forking */ - if ((pid = fork()) < 0) { - print_sys_error("fork"); - exit(1); - } - if (pid > 0) - exit(0); /* parent */ - - /* First child process */ - setsid(); /* become session leader */ - - if ((pid = fork()) < 0) { - print_sys_error("fork"); - exit(1); - } - if (pid > 0) /* first child */ - exit(0); - - chdir("/"); - umask(022); -} - -/* - * Create separate processes ("virtual processors"). Since it's just an - * example, there is no watchdog - the parent just exits leaving children - * on their own. - */ -static void set_concurrency(int nproc) -{ - pid_t pid; - int i; - - if (nproc < 1) - nproc = 1; - - for (i = 0; i < nproc; i++) { - if ((pid = fork()) < 0) { - print_sys_error("fork"); - exit(1); - } - /* Child returns */ - if (pid == 0) - return; - } - - /* Parent just exits */ - exit(0); -} - -static int cpu_count(void) -{ - int n; - -#if defined (_SC_NPROCESSORS_ONLN) - n = (int) sysconf(_SC_NPROCESSORS_ONLN); -#elif defined (_SC_NPROC_ONLN) - n = (int) sysconf(_SC_NPROC_ONLN); -#elif defined (HPUX) -#include - n = mpctl(MPC_GETNUMSPUS, 0, 0); -#else - n = -1; - errno = ENOSYS; -#endif - - return n; -} - -static void print_sys_error(const char *msg) -{ - fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno)); -} - diff --git a/trunk/3rdparty/st-srs/examples/res.c b/trunk/3rdparty/st-srs/examples/res.c deleted file mode 100644 index 14ecd8c92..000000000 --- a/trunk/3rdparty/st-srs/examples/res.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (c) 1985, 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if defined (DARWIN) -#define BIND_8_COMPAT -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - -#define MAXPACKET 1024 - -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) -#define NETDB_INTERNAL h_NETDB_INTERNAL -#endif - -/* New in Solaris 7 */ -#if !defined(_getshort) && defined(ns_get16) -#define _getshort(cp) ns_get16(cp) -#endif - -typedef union { - HEADER hdr; - u_char buf[MAXPACKET]; -} querybuf_t; - - -static int parse_answer(querybuf_t *ans, int len, struct in_addr *addr) -{ - char buf[MAXPACKET]; - HEADER *ahp; - u_char *cp, *eoa; - int type, n; - - ahp = &ans->hdr; - eoa = ans->buf + len; - cp = ans->buf + sizeof(HEADER); - - while (ahp->qdcount > 0) { - ahp->qdcount--; - cp += dn_skipname(cp, eoa) + QFIXEDSZ; - } - while (ahp->ancount > 0 && cp < eoa) { - ahp->ancount--; - if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) - break; - cp += n; - type = _getshort(cp); - cp += 8; - n = _getshort(cp); - cp += 2; - if (type == T_CNAME) { - cp += n; - continue; - } - memcpy(addr, cp, n); - return 0; - } - - h_errno = TRY_AGAIN; - return -1; -} - - -static int query_domain(st_netfd_t nfd, const char *name, struct in_addr *addr, - st_utime_t timeout) -{ - querybuf_t qbuf; - u_char *buf = qbuf.buf; - HEADER *hp = &qbuf.hdr; - int blen = sizeof(qbuf); - int i, len, id; - - for (i = 0; i < _res.nscount; i++) { - len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); - if (len <= 0) { - h_errno = NO_RECOVERY; - return -1; - } - id = hp->id; - - if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), - sizeof(struct sockaddr), timeout) != len) { - h_errno = NETDB_INTERNAL; - /* EINTR means interrupt by other thread, NOT by a caught signal */ - if (errno == EINTR) - return -1; - continue; - } - - /* Wait for reply */ - do { - len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); - if (len <= 0) - break; - } while (id != hp->id); - - if (len < HFIXEDSZ) { - h_errno = NETDB_INTERNAL; - if (len >= 0) - errno = EMSGSIZE; - else if (errno == EINTR) /* see the comment above */ - return -1; - continue; - } - - hp->ancount = ntohs(hp->ancount); - hp->qdcount = ntohs(hp->qdcount); - if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { - switch (hp->rcode) { - case NXDOMAIN: - h_errno = HOST_NOT_FOUND; - break; - case SERVFAIL: - h_errno = TRY_AGAIN; - break; - case NOERROR: - h_errno = NO_DATA; - break; - case FORMERR: - case NOTIMP: - case REFUSED: - default: - h_errno = NO_RECOVERY; - } - continue; - } - - if (parse_answer(&qbuf, len, addr) == 0) - return 0; - } - - return -1; -} - - -#define CLOSE_AND_RETURN(ret) \ - { \ - n = errno; \ - st_netfd_close(nfd); \ - errno = n; \ - return (ret); \ - } - - -int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout) -{ - char name[MAXDNAME], **domain; - const char *cp; - int s, n, maxlen, dots; - int trailing_dot, tried_as_is; - st_netfd_t nfd; - - if ((_res.options & RES_INIT) == 0 && res_init() == -1) { - h_errno = NETDB_INTERNAL; - return -1; - } - if (_res.options & RES_USEVC) { - h_errno = NETDB_INTERNAL; - errno = ENOSYS; - return -1; - } - if (!host || *host == '\0') { - h_errno = HOST_NOT_FOUND; - return -1; - } - - /* Create UDP socket */ - if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - h_errno = NETDB_INTERNAL; - return -1; - } - if ((nfd = st_netfd_open_socket(s)) == NULL) { - h_errno = NETDB_INTERNAL; - n = errno; - close(s); - errno = n; - return -1; - } - - maxlen = sizeof(name) - 1; - n = 0; - dots = 0; - trailing_dot = 0; - tried_as_is = 0; - - for (cp = host; *cp && n < maxlen; cp++) { - dots += (*cp == '.'); - name[n++] = *cp; - } - if (name[n - 1] == '.') - trailing_dot = 1; - - /* - * If there are dots in the name already, let's just give it a try - * 'as is'. The threshold can be set with the "ndots" option. - */ - if (dots >= _res.ndots) { - if (query_domain(nfd, host, addr, timeout) == 0) - CLOSE_AND_RETURN(0); - if (h_errno == NETDB_INTERNAL && errno == EINTR) - CLOSE_AND_RETURN(-1); - tried_as_is = 1; - } - - /* - * We do at least one level of search if - * - there is no dot and RES_DEFNAME is set, or - * - there is at least one dot, there is no trailing dot, - * and RES_DNSRCH is set. - */ - if ((!dots && (_res.options & RES_DEFNAMES)) || - (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { - name[n++] = '.'; - for (domain = _res.dnsrch; *domain; domain++) { - strncpy(name + n, *domain, maxlen - n); - if (query_domain(nfd, name, addr, timeout) == 0) - CLOSE_AND_RETURN(0); - if (h_errno == NETDB_INTERNAL && errno == EINTR) - CLOSE_AND_RETURN(-1); - if (!(_res.options & RES_DNSRCH)) - break; - } - } - - /* - * If we have not already tried the name "as is", do that now. - * note that we do this regardless of how many dots were in the - * name or whether it ends with a dot. - */ - if (!tried_as_is) { - if (query_domain(nfd, host, addr, timeout) == 0) - CLOSE_AND_RETURN(0); - } - - CLOSE_AND_RETURN(-1); -} - diff --git a/trunk/3rdparty/st-srs/examples/server.c b/trunk/3rdparty/st-srs/examples/server.c deleted file mode 100644 index 5d5aa6d72..000000000 --- a/trunk/3rdparty/st-srs/examples/server.c +++ /dev/null @@ -1,1025 +0,0 @@ -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - - -/****************************************************************** - * Server configuration parameters - */ - -/* Log files */ -#define PID_FILE "pid" -#define ERRORS_FILE "errors" -#define ACCESS_FILE "access" - -/* Default server port */ -#define SERV_PORT_DEFAULT 8000 - -/* Socket listen queue size */ -#define LISTENQ_SIZE_DEFAULT 256 - -/* Max number of listening sockets ("hardware virtual servers") */ -#define MAX_BIND_ADDRS 16 - -/* Max number of "spare" threads per process per socket */ -#define MAX_WAIT_THREADS_DEFAULT 8 - -/* Number of file descriptors needed to handle one client session */ -#define FD_PER_THREAD 2 - -/* Access log buffer flushing interval (in seconds) */ -#define ACCLOG_FLUSH_INTERVAL 30 - -/* Request read timeout (in seconds) */ -#define REQUEST_TIMEOUT 30 - - -/****************************************************************** - * Global data - */ - -struct socket_info { - st_netfd_t nfd; /* Listening socket */ - char *addr; /* Bind address */ - unsigned int port; /* Port */ - int wait_threads; /* Number of threads waiting to accept */ - int busy_threads; /* Number of threads processing request */ - int rqst_count; /* Total number of processed requests */ -} srv_socket[MAX_BIND_ADDRS]; /* Array of listening sockets */ - -static int sk_count = 0; /* Number of listening sockets */ - -static int vp_count = 0; /* Number of server processes (VPs) */ -static pid_t *vp_pids; /* Array of VP pids */ - -static int my_index = -1; /* Current process index */ -static pid_t my_pid = -1; /* Current process pid */ - -static st_netfd_t sig_pipe[2]; /* Signal pipe */ - -/* - * Configuration flags/parameters - */ -static int interactive_mode = 0; -static int serialize_accept = 0; -static int log_access = 0; -static char *logdir = NULL; -static char *username = NULL; -static int listenq_size = LISTENQ_SIZE_DEFAULT; -static int errfd = STDERR_FILENO; - -/* - * Thread throttling parameters (all numbers are per listening socket). - * Zero values mean use default. - */ -static int max_threads = 0; /* Max number of threads */ -static int max_wait_threads = 0; /* Max number of "spare" threads */ -static int min_wait_threads = 2; /* Min number of "spare" threads */ - - -/****************************************************************** - * Useful macros - */ - -#ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff -#endif - -#define SEC2USEC(s) ((s)*1000000LL) - -#define WAIT_THREADS(i) (srv_socket[i].wait_threads) -#define BUSY_THREADS(i) (srv_socket[i].busy_threads) -#define TOTAL_THREADS(i) (WAIT_THREADS(i) + BUSY_THREADS(i)) -#define RQST_COUNT(i) (srv_socket[i].rqst_count) - - -/****************************************************************** - * Forward declarations - */ - -static void usage(const char *progname); -static void parse_arguments(int argc, char *argv[]); -static void start_daemon(void); -static void set_thread_throttling(void); -static void create_listeners(void); -static void change_user(void); -static void open_log_files(void); -static void start_processes(void); -static void wdog_sighandler(int signo); -static void child_sighandler(int signo); -static void install_sighandlers(void); -static void start_threads(void); -static void *process_signals(void *arg); -static void *flush_acclog_buffer(void *arg); -static void *handle_connections(void *arg); -static void dump_server_info(void); - -static void Signal(int sig, void (*handler)(int)); -static int cpu_count(void); - -extern void handle_session(long srv_socket_index, st_netfd_t cli_nfd); -extern void load_configs(void); -extern void logbuf_open(void); -extern void logbuf_flush(void); -extern void logbuf_close(void); - -/* Error reporting functions defined in the error.c file */ -extern void err_sys_report(int fd, const char *fmt, ...); -extern void err_sys_quit(int fd, const char *fmt, ...); -extern void err_sys_dump(int fd, const char *fmt, ...); -extern void err_report(int fd, const char *fmt, ...); -extern void err_quit(int fd, const char *fmt, ...); - - -/* - * General server example: accept a client connection and do something. - * This program just outputs a short HTML page, but can be easily adapted - * to do other things. - * - * This server creates a constant number of processes ("virtual processors" - * or VPs) and replaces them when they die. Each virtual processor manages - * its own independent set of state threads (STs), the number of which varies - * with load against the server. Each state thread listens to exactly one - * listening socket. The initial process becomes the watchdog, waiting for - * children (VPs) to die or for a signal requesting termination or restart. - * Upon receiving a restart signal (SIGHUP), all VPs close and then reopen - * log files and reload configuration. All currently active connections remain - * active. It is assumed that new configuration affects only request - * processing and not the general server parameters such as number of VPs, - * thread limits, bind addresses, etc. Those are specified as command line - * arguments, so the server has to be stopped and then started again in order - * to change them. - * - * Each state thread loops processing connections from a single listening - * socket. Only one ST runs on a VP at a time, and VPs do not share memory, - * so no mutual exclusion locking is necessary on any data, and the entire - * server is free to use all the static variables and non-reentrant library - * functions it wants, greatly simplifying programming and debugging and - * increasing performance (for example, it is safe to ++ and -- all global - * counters or call inet_ntoa(3) without any mutexes). The current thread on - * each VP maintains equilibrium on that VP, starting a new thread or - * terminating itself if the number of spare threads exceeds the lower or - * upper limit. - * - * All I/O operations on sockets must use the State Thread library's I/O - * functions because only those functions prevent blocking of the entire VP - * process and perform state thread scheduling. - */ -int main(int argc, char *argv[]) -{ - /* Parse command-line options */ - parse_arguments(argc, argv); - - /* Allocate array of server pids */ - if ((vp_pids = calloc(vp_count, sizeof(pid_t))) == NULL) - err_sys_quit(errfd, "ERROR: calloc failed"); - - /* Start the daemon */ - if (!interactive_mode) - start_daemon(); - - /* Initialize the ST library */ - if (st_init() < 0) - err_sys_quit(errfd, "ERROR: initialization failed: st_init"); - - /* Set thread throttling parameters */ - set_thread_throttling(); - - /* Create listening sockets */ - create_listeners(); - - /* Change the user */ - if (username) - change_user(); - - /* Open log files */ - open_log_files(); - - /* Start server processes (VPs) */ - start_processes(); - - /* Turn time caching on */ - st_timecache_set(1); - - /* Install signal handlers */ - install_sighandlers(); - - /* Load configuration from config files */ - load_configs(); - - /* Start all threads */ - start_threads(); - - /* Become a signal processing thread */ - process_signals(NULL); - - /* NOTREACHED */ - return 1; -} - - -/******************************************************************/ - -static void usage(const char *progname) -{ - fprintf(stderr, "Usage: %s -l []\n\n" - "Possible options:\n\n" - "\t-b : Bind to specified address. Multiple" - " addresses\n" - "\t are permitted.\n" - "\t-p Create specified number of processes.\n" - "\t-t : Specify thread limits per listening" - " socket\n" - "\t across all processes.\n" - "\t-u Change server's user id to specified" - " value.\n" - "\t-q Set max length of pending connections" - " queue.\n" - "\t-a Enable access logging.\n" - "\t-i Run in interactive mode.\n" - "\t-S Serialize all accept() calls.\n" - "\t-h Print this message.\n", - progname); - exit(1); -} - - -/******************************************************************/ - -static void parse_arguments(int argc, char *argv[]) -{ - extern char *optarg; - int opt; - char *c; - - while ((opt = getopt(argc, argv, "b:p:l:t:u:q:aiSh")) != EOF) { - switch (opt) { - case 'b': - if (sk_count >= MAX_BIND_ADDRS) - err_quit(errfd, "ERROR: max number of bind addresses (%d) exceeded", - MAX_BIND_ADDRS); - if ((c = strdup(optarg)) == NULL) - err_sys_quit(errfd, "ERROR: strdup"); - srv_socket[sk_count++].addr = c; - break; - case 'p': - vp_count = atoi(optarg); - if (vp_count < 1) - err_quit(errfd, "ERROR: invalid number of processes: %s", optarg); - break; - case 'l': - logdir = optarg; - break; - case 't': - max_wait_threads = (int) strtol(optarg, &c, 10); - if (*c++ == ':') - max_threads = atoi(c); - if (max_wait_threads < 0 || max_threads < 0) - err_quit(errfd, "ERROR: invalid number of threads: %s", optarg); - break; - case 'u': - username = optarg; - break; - case 'q': - listenq_size = atoi(optarg); - if (listenq_size < 1) - err_quit(errfd, "ERROR: invalid listen queue size: %s", optarg); - break; - case 'a': - log_access = 1; - break; - case 'i': - interactive_mode = 1; - break; - case 'S': - /* - * Serialization decision is tricky on some platforms. For example, - * Solaris 2.6 and above has kernel sockets implementation, so supposedly - * there is no need for serialization. The ST library may be compiled - * on one OS version, but used on another, so the need for serialization - * should be determined at run time by the application. Since it's just - * an example, the serialization decision is left up to user. - * Only on platforms where the serialization is never needed on any OS - * version st_netfd_serialize_accept() is a no-op. - */ - serialize_accept = 1; - break; - case 'h': - case '?': - usage(argv[0]); - } - } - - if (logdir == NULL && !interactive_mode) { - err_report(errfd, "ERROR: logging directory is required\n"); - usage(argv[0]); - } - - if (getuid() == 0 && username == NULL) - err_report(errfd, "WARNING: running as super-user!"); - - if (vp_count == 0 && (vp_count = cpu_count()) < 1) - vp_count = 1; - - if (sk_count == 0) { - sk_count = 1; - srv_socket[0].addr = "0.0.0.0"; - } -} - - -/******************************************************************/ - -static void start_daemon(void) -{ - pid_t pid; - - /* Start forking */ - if ((pid = fork()) < 0) - err_sys_quit(errfd, "ERROR: fork"); - if (pid > 0) - exit(0); /* parent */ - - /* First child process */ - setsid(); /* become session leader */ - - if ((pid = fork()) < 0) - err_sys_quit(errfd, "ERROR: fork"); - if (pid > 0) /* first child */ - exit(0); - - umask(022); - - if (chdir(logdir) < 0) - err_sys_quit(errfd, "ERROR: can't change directory to %s: chdir", logdir); -} - - -/****************************************************************** - * For simplicity, the minimal size of thread pool is considered - * as a maximum number of spare threads (max_wait_threads) that - * will be created upon server startup. The pool size can grow up - * to the max_threads value. Note that this is a per listening - * socket limit. It is also possible to limit the total number of - * threads for all sockets rather than impose a per socket limit. - */ - -static void set_thread_throttling(void) -{ - /* - * Calculate total values across all processes. - * All numbers are per listening socket. - */ - if (max_wait_threads == 0) - max_wait_threads = MAX_WAIT_THREADS_DEFAULT * vp_count; - /* Assuming that each client session needs FD_PER_THREAD file descriptors */ - if (max_threads == 0) - max_threads = (st_getfdlimit() * vp_count) / FD_PER_THREAD / sk_count; - if (max_wait_threads > max_threads) - max_wait_threads = max_threads; - - /* - * Now calculate per-process values. - */ - if (max_wait_threads % vp_count) - max_wait_threads = max_wait_threads / vp_count + 1; - else - max_wait_threads = max_wait_threads / vp_count; - if (max_threads % vp_count) - max_threads = max_threads / vp_count + 1; - else - max_threads = max_threads / vp_count; - - if (min_wait_threads > max_wait_threads) - min_wait_threads = max_wait_threads; -} - - -/******************************************************************/ - -static void create_listeners(void) -{ - int i, n, sock; - char *c; - struct sockaddr_in serv_addr; - struct hostent *hp; - unsigned short port; - - for (i = 0; i < sk_count; i++) { - port = 0; - if ((c = strchr(srv_socket[i].addr, ':')) != NULL) { - *c++ = '\0'; - port = (unsigned short) atoi(c); - } - if (srv_socket[i].addr[0] == '\0') - srv_socket[i].addr = "0.0.0.0"; - if (port == 0) - port = SERV_PORT_DEFAULT; - - /* Create server socket */ - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) - err_sys_quit(errfd, "ERROR: can't create socket: socket"); - n = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) - err_sys_quit(errfd, "ERROR: can't set SO_REUSEADDR: setsockopt"); - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(port); - serv_addr.sin_addr.s_addr = inet_addr(srv_socket[i].addr); - if (serv_addr.sin_addr.s_addr == INADDR_NONE) { - /* not dotted-decimal */ - if ((hp = gethostbyname(srv_socket[i].addr)) == NULL) - err_quit(errfd, "ERROR: can't resolve address: %s", - srv_socket[i].addr); - memcpy(&serv_addr.sin_addr, hp->h_addr, hp->h_length); - } - srv_socket[i].port = port; - - /* Do bind and listen */ - if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) - err_sys_quit(errfd, "ERROR: can't bind to address %s, port %hu", - srv_socket[i].addr, port); - if (listen(sock, listenq_size) < 0) - err_sys_quit(errfd, "ERROR: listen"); - - /* Create file descriptor object from OS socket */ - if ((srv_socket[i].nfd = st_netfd_open_socket(sock)) == NULL) - err_sys_quit(errfd, "ERROR: st_netfd_open_socket"); - /* - * On some platforms (e.g. IRIX, Linux) accept() serialization is never - * needed for any OS version. In that case st_netfd_serialize_accept() - * is just a no-op. Also see the comment above. - */ - if (serialize_accept && st_netfd_serialize_accept(srv_socket[i].nfd) < 0) - err_sys_quit(errfd, "ERROR: st_netfd_serialize_accept"); - } -} - - -/******************************************************************/ - -static void change_user(void) -{ - struct passwd *pw; - - if ((pw = getpwnam(username)) == NULL) - err_quit(errfd, "ERROR: can't find user '%s': getpwnam failed", username); - - if (setgid(pw->pw_gid) < 0) - err_sys_quit(errfd, "ERROR: can't change group id: setgid"); - if (setuid(pw->pw_uid) < 0) - err_sys_quit(errfd, "ERROR: can't change user id: setuid"); - - err_report(errfd, "INFO: changed process user id to '%s'", username); -} - - -/******************************************************************/ - -static void open_log_files(void) -{ - int fd; - char str[32]; - - if (interactive_mode) - return; - - /* Open access log */ - if (log_access) - logbuf_open(); - - /* Open and write pid to pid file */ - if ((fd = open(PID_FILE, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) - err_sys_quit(errfd, "ERROR: can't open pid file: open"); - sprintf(str, "%d\n", (int)getpid()); - if (write(fd, str, strlen(str)) != strlen(str)) - err_sys_quit(errfd, "ERROR: can't write to pid file: write"); - close(fd); - - /* Open error log file */ - if ((fd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) - err_sys_quit(errfd, "ERROR: can't open error log file: open"); - errfd = fd; - - err_report(errfd, "INFO: starting the server..."); -} - - -/******************************************************************/ - -static void start_processes(void) -{ - int i, status; - pid_t pid; - sigset_t mask, omask; - - if (interactive_mode) { - my_index = 0; - my_pid = getpid(); - return; - } - - for (i = 0; i < vp_count; i++) { - if ((pid = fork()) < 0) { - err_sys_report(errfd, "ERROR: can't create process: fork"); - if (i == 0) - exit(1); - err_report(errfd, "WARN: started only %d processes out of %d", i, - vp_count); - vp_count = i; - break; - } - if (pid == 0) { - my_index = i; - my_pid = getpid(); - /* Child returns to continue in main() */ - return; - } - vp_pids[i] = pid; - } - - /* - * Parent process becomes a "watchdog" and never returns to main(). - */ - - /* Install signal handlers */ - Signal(SIGTERM, wdog_sighandler); /* terminate */ - Signal(SIGHUP, wdog_sighandler); /* restart */ - Signal(SIGUSR1, wdog_sighandler); /* dump info */ - - /* Now go to sleep waiting for a child termination or a signal */ - for ( ; ; ) { - if ((pid = wait(&status)) < 0) { - if (errno == EINTR) - continue; - err_sys_quit(errfd, "ERROR: watchdog: wait"); - } - /* Find index of the exited child */ - for (i = 0; i < vp_count; i++) { - if (vp_pids[i] == pid) - break; - } - - /* Block signals while printing and forking */ - sigemptyset(&mask); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGHUP); - sigaddset(&mask, SIGUSR1); - sigprocmask(SIG_BLOCK, &mask, &omask); - - if (WIFEXITED(status)) - err_report(errfd, "WARN: watchdog: process %d (pid %d) exited" - " with status %d", i, pid, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated" - " by signal %d", i, pid, WTERMSIG(status)); - else if (WIFSTOPPED(status)) - err_report(errfd, "WARN: watchdog: process %d (pid %d) stopped" - " by signal %d", i, pid, WSTOPSIG(status)); - else - err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated:" - " unknown termination reason", i, pid); - - /* Fork another VP */ - if ((pid = fork()) < 0) { - err_sys_report(errfd, "ERROR: watchdog: can't create process: fork"); - } else if (pid == 0) { - my_index = i; - my_pid = getpid(); - /* Child returns to continue in main() */ - return; - } - vp_pids[i] = pid; - - /* Restore the signal mask */ - sigprocmask(SIG_SETMASK, &omask, NULL); - } -} - - -/******************************************************************/ - -static void wdog_sighandler(int signo) -{ - int i, err; - - /* Save errno */ - err = errno; - /* Forward the signal to all children */ - for (i = 0; i < vp_count; i++) { - if (vp_pids[i] > 0) - kill(vp_pids[i], signo); - } - /* - * It is safe to do pretty much everything here because process is - * sleeping in wait() which is async-safe. - */ - switch (signo) { - case SIGHUP: - err_report(errfd, "INFO: watchdog: caught SIGHUP"); - /* Reopen log files - needed for log rotation */ - if (log_access) { - logbuf_close(); - logbuf_open(); - } - close(errfd); - if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) - err_sys_quit(STDERR_FILENO, "ERROR: watchdog: open"); - break; - case SIGTERM: - /* Non-graceful termination */ - err_report(errfd, "INFO: watchdog: caught SIGTERM, terminating"); - unlink(PID_FILE); - exit(0); - case SIGUSR1: - err_report(errfd, "INFO: watchdog: caught SIGUSR1"); - break; - default: - err_report(errfd, "INFO: watchdog: caught signal %d", signo); - } - /* Restore errno */ - errno = err; -} - - -/******************************************************************/ - -static void install_sighandlers(void) -{ - sigset_t mask; - int p[2]; - - /* Create signal pipe */ - if (pipe(p) < 0) - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" - " signal pipe: pipe", my_index, my_pid); - if ((sig_pipe[0] = st_netfd_open(p[0])) == NULL || - (sig_pipe[1] = st_netfd_open(p[1])) == NULL) - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" - " signal pipe: st_netfd_open", my_index, my_pid); - - /* Install signal handlers */ - Signal(SIGTERM, child_sighandler); /* terminate */ - Signal(SIGHUP, child_sighandler); /* restart */ - Signal(SIGUSR1, child_sighandler); /* dump info */ - - /* Unblock signals */ - sigemptyset(&mask); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGHUP); - sigaddset(&mask, SIGUSR1); - sigprocmask(SIG_UNBLOCK, &mask, NULL); -} - - -/******************************************************************/ - -static void child_sighandler(int signo) -{ - int err, fd; - - err = errno; - fd = st_netfd_fileno(sig_pipe[1]); - - /* write() is async-safe */ - if (write(fd, &signo, sizeof(int)) != sizeof(int)) - err_sys_quit(errfd, "ERROR: process %d (pid %d): child's signal" - " handler: write", my_index, my_pid); - errno = err; -} - - -/****************************************************************** - * The "main" function of the signal processing thread. - */ - -/* ARGSUSED */ -static void *process_signals(void *arg) -{ - int signo; - - for ( ; ; ) { - /* Read the next signal from the signal pipe */ - if (st_read(sig_pipe[0], &signo, sizeof(int), - ST_UTIME_NO_TIMEOUT) != sizeof(int)) - err_sys_quit(errfd, "ERROR: process %d (pid %d): signal processor:" - " st_read", my_index, my_pid); - - switch (signo) { - case SIGHUP: - err_report(errfd, "INFO: process %d (pid %d): caught SIGHUP," - " reloading configuration", my_index, my_pid); - if (interactive_mode) { - load_configs(); - break; - } - /* Reopen log files - needed for log rotation */ - if (log_access) { - logbuf_flush(); - logbuf_close(); - logbuf_open(); - } - close(errfd); - if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) - err_sys_quit(STDERR_FILENO, "ERROR: process %d (pid %d): signal" - " processor: open", my_index, my_pid); - /* Reload configuration */ - load_configs(); - break; - case SIGTERM: - /* - * Terminate ungracefully since it is generally not known how long - * it will take to gracefully complete all client sessions. - */ - err_report(errfd, "INFO: process %d (pid %d): caught SIGTERM," - " terminating", my_index, my_pid); - if (log_access) - logbuf_flush(); - exit(0); - case SIGUSR1: - err_report(errfd, "INFO: process %d (pid %d): caught SIGUSR1", - my_index, my_pid); - /* Print server info to stderr */ - dump_server_info(); - break; - default: - err_report(errfd, "INFO: process %d (pid %d): caught signal %d", - my_index, my_pid, signo); - } - } - - /* NOTREACHED */ - return NULL; -} - - -/****************************************************************** - * The "main" function of the access log flushing thread. - */ - -/* ARGSUSED */ -static void *flush_acclog_buffer(void *arg) -{ - for ( ; ; ) { - st_sleep(ACCLOG_FLUSH_INTERVAL); - logbuf_flush(); - } - - /* NOTREACHED */ - return NULL; -} - - -/******************************************************************/ - -static void start_threads(void) -{ - long i, n; - - /* Create access log flushing thread */ - if (log_access && st_thread_create(flush_acclog_buffer, NULL, 0, 0) == NULL) - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" - " log flushing thread", my_index, my_pid); - - /* Create connections handling threads */ - for (i = 0; i < sk_count; i++) { - err_report(errfd, "INFO: process %d (pid %d): starting %d threads" - " on %s:%u", my_index, my_pid, max_wait_threads, - srv_socket[i].addr, srv_socket[i].port); - WAIT_THREADS(i) = 0; - BUSY_THREADS(i) = 0; - RQST_COUNT(i) = 0; - for (n = 0; n < max_wait_threads; n++) { - if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) - WAIT_THREADS(i)++; - else - err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" - " thread", my_index, my_pid); - } - if (WAIT_THREADS(i) == 0) - exit(1); - } -} - - -/******************************************************************/ - -static void *handle_connections(void *arg) -{ - st_netfd_t srv_nfd, cli_nfd; - struct sockaddr_in from; - int fromlen; - long i = (long) arg; - - srv_nfd = srv_socket[i].nfd; - fromlen = sizeof(from); - - while (WAIT_THREADS(i) <= max_wait_threads) { - cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&from, &fromlen, - ST_UTIME_NO_TIMEOUT); - if (cli_nfd == NULL) { - err_sys_report(errfd, "ERROR: can't accept connection: st_accept"); - continue; - } - /* Save peer address, so we can retrieve it later */ - st_netfd_setspecific(cli_nfd, &from.sin_addr, NULL); - - WAIT_THREADS(i)--; - BUSY_THREADS(i)++; - if (WAIT_THREADS(i) < min_wait_threads && TOTAL_THREADS(i) < max_threads) { - /* Create another spare thread */ - if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) - WAIT_THREADS(i)++; - else - err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" - " thread", my_index, my_pid); - } - - handle_session(i, cli_nfd); - - st_netfd_close(cli_nfd); - WAIT_THREADS(i)++; - BUSY_THREADS(i)--; - } - - WAIT_THREADS(i)--; - return NULL; -} - - -/******************************************************************/ - -static void dump_server_info(void) -{ - char *buf; - int i, len; - - if ((buf = malloc(sk_count * 512)) == NULL) { - err_sys_report(errfd, "ERROR: malloc failed"); - return; - } - - len = sprintf(buf, "\n\nProcess #%d (pid %d):\n", my_index, (int)my_pid); - for (i = 0; i < sk_count; i++) { - len += sprintf(buf + len, "\nListening Socket #%d:\n" - "-------------------------\n" - "Address %s:%u\n" - "Thread limits (min/max) %d/%d\n" - "Waiting threads %d\n" - "Busy threads %d\n" - "Requests served %d\n", - i, srv_socket[i].addr, srv_socket[i].port, - max_wait_threads, max_threads, - WAIT_THREADS(i), BUSY_THREADS(i), RQST_COUNT(i)); - } - - write(STDERR_FILENO, buf, len); - free(buf); -} - - -/****************************************************************** - * Stubs - */ - -/* - * Session handling function stub. Just dumps small HTML page. - */ -void handle_session(long srv_socket_index, st_netfd_t cli_nfd) -{ - static char resp[] = "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n" - "Connection: close\r\n\r\n

It worked!

\n"; - char buf[512]; - int n = sizeof(resp) - 1; - struct in_addr *from = st_netfd_getspecific(cli_nfd); - - if (st_read(cli_nfd, buf, sizeof(buf), SEC2USEC(REQUEST_TIMEOUT)) < 0) { - err_sys_report(errfd, "WARN: can't read request from %s: st_read", - inet_ntoa(*from)); - return; - } - if (st_write(cli_nfd, resp, n, ST_UTIME_NO_TIMEOUT) != n) { - err_sys_report(errfd, "WARN: can't write response to %s: st_write", - inet_ntoa(*from)); - return; - } - - RQST_COUNT(srv_socket_index)++; -} - - -/* - * Configuration loading function stub. - */ -void load_configs(void) -{ - err_report(errfd, "INFO: process %d (pid %d): configuration loaded", - my_index, my_pid); -} - - -/* - * Buffered access logging methods. - * Note that stdio functions (fopen(3), fprintf(3), fflush(3), etc.) cannot - * be used if multiple VPs are created since these functions can flush buffer - * at any point and thus write only partial log record to disk. - * Also, it is completely safe for all threads of the same VP to write to - * the same log buffer without any mutex protection (one buffer per VP, of - * course). - */ -void logbuf_open(void) -{ - -} - - -void logbuf_flush(void) -{ - -} - - -void logbuf_close(void) -{ - -} - - -/****************************************************************** - * Small utility functions - */ - -static void Signal(int sig, void (*handler)(int)) -{ - struct sigaction sa; - - sa.sa_handler = handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(sig, &sa, NULL); -} - -static int cpu_count(void) -{ - int n; - -#if defined (_SC_NPROCESSORS_ONLN) - n = (int) sysconf(_SC_NPROCESSORS_ONLN); -#elif defined (_SC_NPROC_ONLN) - n = (int) sysconf(_SC_NPROC_ONLN); -#elif defined (HPUX) -#include - n = mpctl(MPC_GETNUMSPUS, 0, 0); -#else - n = -1; - errno = ENOSYS; -#endif - - return n; -} - -/******************************************************************/ - diff --git a/trunk/3rdparty/st-srs/extensions/Makefile b/trunk/3rdparty/st-srs/extensions/Makefile deleted file mode 100644 index fc6634f93..000000000 --- a/trunk/3rdparty/st-srs/extensions/Makefile +++ /dev/null @@ -1,91 +0,0 @@ -# -# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. -# All Rights Reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of Silicon Graphics, Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -CC = cc - -SHELL = /bin/sh -ECHO = /bin/echo - -DEPTH = .. -BUILD = -TARGETDIR = obj - -DEFINES = -OTHER_FLAGS = -CFLAGS = - -OBJDIR = $(DEPTH)/$(TARGETDIR) -INCDIR = $(DEPTH)/$(TARGETDIR) - -LIBRESOLV = -EXTRALIBS = - -SLIBRARY = $(OBJDIR)/libstx.a -OBJS = $(OBJDIR)/dnscache.o $(OBJDIR)/dnsres.o $(OBJDIR)/lrucache.o - - -CFLAGS += -Wall -I$(INCDIR) -AR = ar -ARFLAGS = rv -RANLIB = ranlib - - -########################## -# Platform section. -# - -ifeq (LINUX, $(findstring LINUX, $(OS))) -LIBRESOLV = -lresolv -endif - -ifeq ($(OS), SOLARIS) -LIBRESOLV = -lresolv -EXTRALIBS = -lsocket -lnsl -endif - -# -# End of platform section. -########################## - - -all: $(SLIBRARY) - -$(SLIBRARY): $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - $(RANLIB) $@ - -$(OBJDIR)/%.o: %.c stx.h common.h - $(CC) $(CFLAGS) -c $< -o $@ - -clean: - rm -rf $(OBJS) $(SLIBRARY) - -#.DEFAULT: -# @cd $(DEPTH); $(MAKE) $@ - diff --git a/trunk/3rdparty/st-srs/extensions/README b/trunk/3rdparty/st-srs/extensions/README deleted file mode 100644 index f768aa712..000000000 --- a/trunk/3rdparty/st-srs/extensions/README +++ /dev/null @@ -1,42 +0,0 @@ -This directory contains extensions to the core State Threads Library -that were contributed by users. All files hereunder are not part of the -State Threads Library itself. They are provided as-is, without warranty -or support, and under whatever license terms their authors provided. To -contribute your own extensions, just mail them to the project -administrators or to one of the project's mailing lists; see -state-threads.sourceforge.net. Please indicate the license terms under -which the project may distribute your contribution. - -======================================================================== - -stx_fileio ----------- -Contributed by Jeff , 4 Nov 2002. - -Provides non-blocking random access file reading capability for -programs using the State Threads library. There is one public function: - -ssize_t stx_file_read(st_netfd_t fd, off_t offset, - void *buf, size_t nbytes, st_utime_t timeout); - -The implementation is not optimal in that the data is copied at least once -more than should be necessary. Its usefulness is limited to cases where -random access to a file is required and where starvation of other threads -is unacceptable. - -The particular application which motivated this implementation was a UDP -file transfer protocol. Because the OS does very little buffering of UDP -traffic it is important that UDP transmission threads are not starved for -periods of time which are long relative to the interval required to -maintain a steady send rate. - -Licensed under the same dual MPL/GPL as core State Threads. - -======================================================================== - -stx_dns -------- - -Documentation coming. - -======================================================================== diff --git a/trunk/3rdparty/st-srs/extensions/common.h b/trunk/3rdparty/st-srs/extensions/common.h deleted file mode 100644 index f6298ba09..000000000 --- a/trunk/3rdparty/st-srs/extensions/common.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _STX_COMMON_H_ -#define _STX_COMMON_H_ - -#include -#include - - -#define STX_BEGIN_MACRO { -#define STX_END_MACRO } - - -/***************************************** - * Circular linked list definitions - */ - -typedef struct _stx_clist { - struct _stx_clist *next; - struct _stx_clist *prev; -} stx_clist_t; - -/* Insert element "_e" into the list, before "_l" */ -#define STX_CLIST_INSERT_BEFORE(_e,_l) \ - STX_BEGIN_MACRO \ - (_e)->next = (_l); \ - (_e)->prev = (_l)->prev; \ - (_l)->prev->next = (_e); \ - (_l)->prev = (_e); \ - STX_END_MACRO - -/* Insert element "_e" into the list, after "_l" */ -#define STX_CLIST_INSERT_AFTER(_e,_l) \ - STX_BEGIN_MACRO \ - (_e)->next = (_l)->next; \ - (_e)->prev = (_l); \ - (_l)->next->prev = (_e); \ - (_l)->next = (_e); \ - STX_END_MACRO - -/* Append an element "_e" to the end of the list "_l" */ -#define STX_CLIST_APPEND_LINK(_e,_l) STX_CLIST_INSERT_BEFORE(_e,_l) - -/* Remove the element "_e" from it's circular list */ -#define STX_CLIST_REMOVE_LINK(_e) \ - STX_BEGIN_MACRO \ - (_e)->prev->next = (_e)->next; \ - (_e)->next->prev = (_e)->prev; \ - STX_END_MACRO - -/* Return the head/tail of the list */ -#define STX_CLIST_HEAD(_l) (_l)->next -#define STX_CLIST_TAIL(_l) (_l)->prev - -/* Return non-zero if the given circular list "_l" is empty, */ -/* zero if the circular list is not empty */ -#define STX_CLIST_IS_EMPTY(_l) \ - ((_l)->next == (_l)) - -/* Initialize a circular list */ -#define STX_CLIST_INIT_CLIST(_l) \ - STX_BEGIN_MACRO \ - (_l)->next = (_l); \ - (_l)->prev = (_l); \ - STX_END_MACRO - - -/***************************************** - * Useful macros - */ - -#ifndef offsetof -#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) -#endif - -#define STX_MIN(a, b) (((a) < (b)) ? (a) : (b)) - -#endif /* !_STX_COMMON_H_ */ - diff --git a/trunk/3rdparty/st-srs/extensions/dnscache.c b/trunk/3rdparty/st-srs/extensions/dnscache.c deleted file mode 100644 index ac49166a1..000000000 --- a/trunk/3rdparty/st-srs/extensions/dnscache.c +++ /dev/null @@ -1,190 +0,0 @@ -#include "stx.h" -#include "common.h" - - -/***************************************** - * Basic types definitions - */ - -typedef struct _stx_dns_data { - struct in_addr *addrs; - int num_addrs; - int cur; - time_t expires; -} stx_dns_data_t; - - -#define MAX_HOST_ADDRS 1024 - -static struct in_addr addr_list[MAX_HOST_ADDRS]; - -stx_cache_t *_stx_dns_cache = NULL; - -extern int _stx_dns_ttl; -extern int _stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout); - - -static unsigned long hash_hostname(const void *key) -{ - const char *name = (const char *)key; - unsigned long hash = 0; - - while (*name) - hash = (hash << 4) - hash + *name++; /* hash = hash * 15 + *name++ */ - - return hash; -} - -static void cleanup_entry(void *key, void *data) -{ - if (key) - free(key); - - if (data) { - if (((stx_dns_data_t *)data)->addrs) - free(((stx_dns_data_t *)data)->addrs); - free(data); - } -} - -static int lookup_entry(const char *host, struct in_addr *addrs, - int *num_addrs, int rotate) -{ - stx_cache_entry_t *entry; - stx_dns_data_t *data; - int n; - - entry = stx_cache_entry_lookup(_stx_dns_cache, host); - if (entry) { - data = (stx_dns_data_t *)stx_cache_entry_getdata(entry); - if (st_time() <= data->expires) { - if (*num_addrs == 1) { - if (rotate) { - *addrs = data->addrs[data->cur++]; - if (data->cur >= data->num_addrs) - data->cur = 0; - } else { - *addrs = data->addrs[0]; - } - } else { - n = STX_MIN(*num_addrs, data->num_addrs); - memcpy(addrs, data->addrs, n * sizeof(*addrs)); - *num_addrs = n; - } - - stx_cache_entry_release(_stx_dns_cache, entry); - return 1; - } - - /* - * Cache entry expired: decrement its refcount and purge it from cache. - */ - stx_cache_entry_release(_stx_dns_cache, entry); - stx_cache_entry_delete(_stx_dns_cache, entry); - } - - return 0; -} - -static void insert_entry(const char *host, struct in_addr *addrs, int count) -{ - stx_cache_entry_t *entry; - stx_dns_data_t *data; - char *key; - size_t n; - - if (_stx_dns_ttl > 0) { - key = strdup(host); - data = (stx_dns_data_t *)malloc(sizeof(stx_dns_data_t)); - n = count * sizeof(*addrs); - if (data) { - data->addrs = (struct in_addr *)malloc(n); - if (data->addrs) - memcpy(data->addrs, addrs, n); - data->num_addrs = count; - data->cur = 0; - data->expires = st_time() + _stx_dns_ttl; - } - entry = stx_cache_entry_create(key, data, strlen(host) + 1 + - sizeof(stx_dns_data_t) + n + - stx_cache_entry_sizeof()); - if (key && data && data->addrs && entry && - stx_cache_entry_insert(_stx_dns_cache, entry) == 0) { - stx_cache_entry_release(_stx_dns_cache, entry); - return; - } - - if (entry) - stx_cache_entry_delete(_stx_dns_cache, entry); - else - cleanup_entry(key, data); - } -} - - - -int _stx_dns_cache_getaddrlist(const char *hostname, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout, - int rotate) -{ - char host[128]; - int n, count; - - if (!_stx_dns_cache) - return _stx_dns_getaddrlist(hostname, addrs, num_addrs, timeout); - - for (n = 0; n < sizeof(host) - 1 && hostname[n]; n++) { - host[n] = tolower(hostname[n]); - } - host[n] = '\0'; - - if (lookup_entry(host, addrs, num_addrs, rotate)) - return 0; - - count = MAX_HOST_ADDRS; - if (_stx_dns_getaddrlist(host, addr_list, &count, timeout) < 0) - return -1; - n = STX_MIN(*num_addrs, count); - memcpy(addrs, addr_list, n * sizeof(*addrs)); - *num_addrs = n; - - insert_entry(host, addr_list, count); - return 0; -} - - -int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size) -{ - _stx_dns_cache = stx_cache_create(max_size, max_bytes, hash_size, - hash_hostname, - (long (*)(const void *, const void *))strcmp, - cleanup_entry); - if (!_stx_dns_cache) - return -1; - - return 0; -} - -void stx_dns_cache_getinfo(stx_cache_info_t *info) -{ - if (_stx_dns_cache) - stx_cache_getinfo(_stx_dns_cache, info); - else - memset(info, 0, sizeof(stx_cache_info_t)); -} - -int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout) -{ - return _stx_dns_cache_getaddrlist(hostname, addrs, num_addrs, timeout, 0); -} - -int stx_dns_getaddr(const char *hostname, struct in_addr *addr, - st_utime_t timeout) -{ - int n = 1; - - return _stx_dns_cache_getaddrlist(hostname, addr, &n, timeout, 1); -} - diff --git a/trunk/3rdparty/st-srs/extensions/dnsres.c b/trunk/3rdparty/st-srs/extensions/dnsres.c deleted file mode 100644 index 04a91ccaf..000000000 --- a/trunk/3rdparty/st-srs/extensions/dnsres.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (c) 1985, 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "stx.h" - -#define MAXPACKET 1024 - -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) -#define NETDB_INTERNAL h_NETDB_INTERNAL -#endif - -/* New in Solaris 7 */ -#if !defined(_getshort) && defined(ns_get16) -#define _getshort(cp) ns_get16(cp) -#define _getlong(cp) ns_get32(cp) -#endif - -typedef union { - HEADER hdr; - u_char buf[MAXPACKET]; -} querybuf_t; - -int _stx_dns_ttl; - - -static int parse_answer(querybuf_t *ans, int len, struct in_addr *addrs, - int *num_addrs) -{ - char buf[MAXPACKET]; - HEADER *ahp; - u_char *cp, *eoa; - int type, n, i; - - ahp = &ans->hdr; - eoa = ans->buf + len; - cp = ans->buf + sizeof(HEADER); - h_errno = TRY_AGAIN; - _stx_dns_ttl = -1; - i = 0; - - while (ahp->qdcount > 0) { - ahp->qdcount--; - cp += dn_skipname(cp, eoa) + QFIXEDSZ; - } - while (ahp->ancount > 0 && cp < eoa && i < *num_addrs) { - ahp->ancount--; - if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) - return -1; - cp += n; - if (cp + 4 + 4 + 2 >= eoa) - return -1; - type = _getshort(cp); - cp += 4; - if (type == T_A) - _stx_dns_ttl = _getlong(cp); - cp += 4; - n = _getshort(cp); - cp += 2; - if (type == T_A) { - if (n > sizeof(*addrs) || cp + n > eoa) - return -1; - memcpy(&addrs[i++], cp, n); - } - cp += n; - } - - *num_addrs = i; - return 0; -} - - -static int query_domain(st_netfd_t nfd, const char *name, - struct in_addr *addrs, int *num_addrs, - st_utime_t timeout) -{ - querybuf_t qbuf; - u_char *buf = qbuf.buf; - HEADER *hp = &qbuf.hdr; - int blen = sizeof(qbuf); - int i, len, id; - - for (i = 0; i < _res.nscount; i++) { - len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); - if (len <= 0) { - h_errno = NO_RECOVERY; - return -1; - } - id = hp->id; - - if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), - sizeof(struct sockaddr), timeout) != len) { - h_errno = NETDB_INTERNAL; - /* EINTR means interrupt by other thread, NOT by a caught signal */ - if (errno == EINTR) - return -1; - continue; - } - - /* Wait for reply */ - do { - len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); - if (len <= 0) - break; - } while (id != hp->id); - - if (len < HFIXEDSZ) { - h_errno = NETDB_INTERNAL; - if (len >= 0) - errno = EMSGSIZE; - else if (errno == EINTR) /* see the comment above */ - return -1; - continue; - } - - hp->ancount = ntohs(hp->ancount); - hp->qdcount = ntohs(hp->qdcount); - if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { - switch (hp->rcode) { - case NXDOMAIN: - h_errno = HOST_NOT_FOUND; - break; - case SERVFAIL: - h_errno = TRY_AGAIN; - break; - case NOERROR: - h_errno = NO_DATA; - break; - case FORMERR: - case NOTIMP: - case REFUSED: - default: - h_errno = NO_RECOVERY; - } - continue; - } - - if (parse_answer(&qbuf, len, addrs, num_addrs) == 0) - return 0; - } - - return -1; -} - - -#define CLOSE_AND_RETURN(ret) \ - { \ - n = errno; \ - st_netfd_close(nfd); \ - errno = n; \ - return (ret); \ - } - - -int _stx_dns_getaddrlist(const char *host, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout) -{ - char name[MAXDNAME], **domain; - const char *cp; - int s, n, maxlen, dots; - int trailing_dot, tried_as_is; - st_netfd_t nfd; - - if ((_res.options & RES_INIT) == 0 && res_init() == -1) { - h_errno = NETDB_INTERNAL; - return -1; - } - if (_res.options & RES_USEVC) { - h_errno = NETDB_INTERNAL; - errno = ENOSYS; - return -1; - } - if (!host || *host == '\0') { - h_errno = HOST_NOT_FOUND; - return -1; - } - - /* Create UDP socket */ - if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - h_errno = NETDB_INTERNAL; - return -1; - } - if ((nfd = st_netfd_open_socket(s)) == NULL) { - h_errno = NETDB_INTERNAL; - n = errno; - close(s); - errno = n; - return -1; - } - - maxlen = sizeof(name) - 1; - n = 0; - dots = 0; - trailing_dot = 0; - tried_as_is = 0; - - for (cp = host; *cp && n < maxlen; cp++) { - dots += (*cp == '.'); - name[n++] = *cp; - } - if (name[n - 1] == '.') - trailing_dot = 1; - - /* - * If there are dots in the name already, let's just give it a try - * 'as is'. The threshold can be set with the "ndots" option. - */ - if (dots >= _res.ndots) { - if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) - CLOSE_AND_RETURN(0); - if (h_errno == NETDB_INTERNAL && errno == EINTR) - CLOSE_AND_RETURN(-1); - tried_as_is = 1; - } - - /* - * We do at least one level of search if - * - there is no dot and RES_DEFNAME is set, or - * - there is at least one dot, there is no trailing dot, - * and RES_DNSRCH is set. - */ - if ((!dots && (_res.options & RES_DEFNAMES)) || - (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { - name[n++] = '.'; - for (domain = _res.dnsrch; *domain; domain++) { - strncpy(name + n, *domain, maxlen - n); - if (query_domain(nfd, name, addrs, num_addrs, timeout) == 0) - CLOSE_AND_RETURN(0); - if (h_errno == NETDB_INTERNAL && errno == EINTR) - CLOSE_AND_RETURN(-1); - if (!(_res.options & RES_DNSRCH)) - break; - } - } - - /* - * If we have not already tried the name "as is", do that now. - * note that we do this regardless of how many dots were in the - * name or whether it ends with a dot. - */ - if (!tried_as_is) { - if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) - CLOSE_AND_RETURN(0); - } - - CLOSE_AND_RETURN(-1); -} - diff --git a/trunk/3rdparty/st-srs/extensions/lrucache.c b/trunk/3rdparty/st-srs/extensions/lrucache.c deleted file mode 100644 index 33494fee6..000000000 --- a/trunk/3rdparty/st-srs/extensions/lrucache.c +++ /dev/null @@ -1,343 +0,0 @@ -#include "stx.h" -#include "common.h" - - -/***************************************** - * Basic types definitions - */ - -struct _stx_centry { - void *key; /* key for doing lookups */ - void *data; /* data in the cache */ - size_t weight; /* "weight" of this entry */ - struct _stx_centry *next; /* next entry */ - struct _stx_centry **pthis; - stx_clist_t lru_link; /* for putting this entry on LRU list */ - int ref_count; /* use count for this entry */ - int delete_pending; /* pending delete flag */ -}; - -struct _stx_cache { - size_t max_size; /* max size of cache */ - size_t cur_size; /* current size of cache */ - - size_t max_weight; /* cache capacity */ - size_t cur_weight; /* current total "weight" of all entries */ - - size_t hash_size; /* size of hash table */ - stx_cache_entry_t **table; /* hash table for this cache */ - - stx_clist_t lru_list; /* least-recently-used list */ - - /* Cache stats */ - unsigned long hits; /* num cache hits */ - unsigned long lookups; /* num cache lookups */ - unsigned long inserts; /* num inserts */ - unsigned long deletes; /* num deletes */ - - /* Functions */ - unsigned long (*key_hash_fn)(const void *); - long (*key_cmp_fn)(const void *, const void *); - void (*cleanup_fn)(void *, void *); -}; - - -#define STX_CACHE_ENTRY_PTR(_qp) \ - ((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link))) - - -/***************************************** - * Cache methods - */ - -stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight, - size_t hash_size, - unsigned long (*key_hash_fn)(const void *key), - long (*key_cmp_fn)(const void *key1, - const void *key2), - void (*cleanup_fn)(void *key, void *data)) -{ - stx_cache_t *newcache; - - newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t)); - if (newcache == NULL) - return NULL; - newcache->table = (stx_cache_entry_t **)calloc(hash_size, - sizeof(stx_cache_entry_t *)); - if (newcache->table == NULL) { - free(newcache); - return NULL; - } - - newcache->max_size = max_size; - newcache->max_weight = max_weight; - newcache->hash_size = hash_size; - STX_CLIST_INIT_CLIST(&(newcache->lru_list)); - newcache->key_hash_fn = key_hash_fn; - newcache->key_cmp_fn = key_cmp_fn; - newcache->cleanup_fn = cleanup_fn; - - return newcache; -} - - -void stx_cache_empty(stx_cache_t *cache) -{ - size_t i; - stx_cache_entry_t *entry, *next_entry; - - for (i = 0; i < cache->hash_size; i++) { - entry = cache->table[i]; - while (entry) { - next_entry = entry->next; - stx_cache_entry_delete(cache, entry); - entry = next_entry; - } - } -} - - -void stx_cache_traverse(stx_cache_t *cache, - void (*callback)(void *key, void *data)) -{ - size_t i; - stx_cache_entry_t *entry; - - for (i = 0; i < cache->hash_size; i++) { - for (entry = cache->table[i]; entry; entry = entry->next) { - if (!entry->delete_pending) - (*callback)(entry->key, entry->data); - } - } -} - - -void stx_cache_traverse_lru(stx_cache_t *cache, - void (*callback)(void *key, void *data), - unsigned int n) -{ - stx_clist_t *q; - stx_cache_entry_t *entry; - - for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n; - q = q->next, n--) { - entry = STX_CACHE_ENTRY_PTR(q); - (*callback)(entry->key, entry->data); - } -} - - -void stx_cache_traverse_mru(stx_cache_t *cache, - void (*callback)(void *key, void *data), - unsigned int n) -{ - stx_clist_t *q; - stx_cache_entry_t *entry; - - for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n; - q = q->prev, n--) { - entry = STX_CACHE_ENTRY_PTR(q); - (*callback)(entry->key, entry->data); - } -} - - -size_t stx_cache_getsize(stx_cache_t *cache) -{ - return cache->cur_size; -} - - -size_t stx_cache_getweight(stx_cache_t *cache) -{ - return cache->cur_weight; -} - - -void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info) -{ - info->max_size = cache->max_size; - info->max_weight = cache->max_weight; - info->hash_size = cache->hash_size; - info->cur_size = cache->cur_size; - info->cur_weight = cache->cur_weight; - info->hits = cache->hits; - info->lookups = cache->lookups; - info->inserts = cache->inserts; - info->deletes = cache->deletes; -} - - -/***************************************** - * Cache entry methods - */ - -stx_cache_entry_t *stx_cache_entry_create(void *key, void *data, - size_t weight) -{ - stx_cache_entry_t *newentry; - - newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t)); - if (newentry == NULL) - return NULL; - - newentry->key = key; - newentry->data = data; - newentry->weight = weight; - - return newentry; -} - - -void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry) -{ - entry->delete_pending = 1; - - if (entry->ref_count > 0) - return; - - if (entry->pthis) { - *entry->pthis = entry->next; - if (entry->next) - entry->next->pthis = entry->pthis; - - cache->cur_size--; - cache->cur_weight -= entry->weight; - cache->deletes++; - STX_CLIST_REMOVE_LINK(&(entry->lru_link)); - } - - if (cache->cleanup_fn) - cache->cleanup_fn(entry->key, entry->data); - - entry->pthis = NULL; - entry->key = NULL; - entry->data = NULL; - free(entry); -} - - -stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key) -{ - unsigned long bucket; - stx_cache_entry_t *entry; - - cache->lookups++; - bucket = cache->key_hash_fn(key) % cache->hash_size; - for (entry = cache->table[bucket]; entry; entry = entry->next) { - if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0) - break; - } - if (entry) { - cache->hits++; - if (entry->ref_count == 0) - STX_CLIST_REMOVE_LINK(&(entry->lru_link)); - entry->ref_count++; - } - - return entry; -} - - -void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry) -{ - if (entry->ref_count == 0) - return; - - entry->ref_count--; - - if (entry->ref_count == 0) { - STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list)); - if (entry->delete_pending) - stx_cache_entry_delete(cache, entry); - } -} - - -int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry) -{ - stx_cache_entry_t *old_entry; - unsigned long bucket; - - /* - * If cache capacity is exceeded, try to remove LRU entries till there is - * enough room or LRU list is empty. - */ - while (cache->cur_weight + entry->weight > cache->max_weight) { - old_entry = stx_cache_entry_getlru(cache); - if (!old_entry) { - /* cache capacity is exceeded and all entries are in use */ - return -1; - } - stx_cache_entry_delete(cache, old_entry); - } - - /* If cache size is exceeded, remove LRU entry */ - if (cache->cur_size >= cache->max_size) { - old_entry = stx_cache_entry_getlru(cache); - if (!old_entry) { - /* cache size is exceeded and all entries are in use */ - return -1; - } - stx_cache_entry_delete(cache, old_entry); - } - - /* Don't add duplicate entries in the cache */ - bucket = cache->key_hash_fn(entry->key) % cache->hash_size; - for (old_entry = cache->table[bucket]; old_entry; - old_entry = old_entry->next) { - if (!old_entry->delete_pending && - cache->key_cmp_fn(entry->key, old_entry->key) == 0) - break; - } - if (old_entry) - stx_cache_entry_delete(cache, old_entry); - - /* Insert in the hash table */ - entry->next = cache->table[bucket]; - cache->table[bucket] = entry; - entry->pthis = &cache->table[bucket]; - if (entry->next) - entry->next->pthis = &entry->next; - entry->ref_count++; - - cache->inserts++; - cache->cur_size++; - cache->cur_weight += entry->weight; - - return 0; -} - - -stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache) -{ - if (STX_CLIST_IS_EMPTY(&(cache->lru_list))) - return NULL; - - return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list))); -} - - -int stx_cache_entry_sizeof(void) -{ - return (int)sizeof(stx_cache_entry_t); -} - - -void *stx_cache_entry_getdata(stx_cache_entry_t *entry) -{ - return entry->data; -} - - -void *stx_cache_entry_getkey(stx_cache_entry_t *entry) -{ - return entry->key; -} - - -size_t stx_cache_entry_getweight(stx_cache_entry_t *entry) -{ - return entry->weight; -} - diff --git a/trunk/3rdparty/st-srs/extensions/print_stk.patch b/trunk/3rdparty/st-srs/extensions/print_stk.patch deleted file mode 100644 index f7451c7b0..000000000 --- a/trunk/3rdparty/st-srs/extensions/print_stk.patch +++ /dev/null @@ -1,367 +0,0 @@ -Michael Abd-El-Malek contributed this patch. He wrote: ----------------------------------------- -Hello, - -This is a patch that enables programmatically dumping the stack of -every thread. This has been useful in debugging deadlocks, etc... -Our usage model is that the SIGUSR2 handler calls the new -_st_print_thread_stacks function, which dumps the stack for all -threads. A convenient feature is that for thread stacks that are the -same (which is common for application with a lot of worker threads -waiting for work), only one stack trace is printed, along with a -count of how many threads have that same stack. - -I use the glibc backtrace function to get the backtrace, and then use -popen to execute addr2line and convert memory addresses to file -names, function names, and line numbers. If glibc isn't available, -_st_print_thread_stacks just prints a warning. And this feature is -only available if DEBUG is turned on. - -We've found this feature extremely helpful when debugging. - -The patch can be a bit more robust (it assumes addr2line exists). -But I didn't want to go through the hassle of doing this, if the -StateThreads community doesn't want to use this patch. (In our -environment, addr2line will always be there.) - -Cheers, -Mike ----------------------------------------- -Invoking complex functions from a signal handler is not recommended, -plus this patch changes the behavior of existing API hooks. It will -not become part of State Threads proper but you may find it useful -nonetheless. This patch applies to st-1.5.2. - -diff -Nur Makefile.1.5.2 Makefile ---- Makefile.1.5.2 Wed Sep 7 14:19:50 2005 -+++ Makefile Wed Sep 7 14:33:08 2005 -@@ -255,7 +255,8 @@ - $(TARGETDIR)/stk.o \ - $(TARGETDIR)/sync.o \ - $(TARGETDIR)/key.o \ -- $(TARGETDIR)/io.o -+ $(TARGETDIR)/io.o \ -+ $(TARGETDIR)/backtrace.o - OBJS += $(EXTRA_OBJS) - HEADER = $(TARGETDIR)/st.h - SLIBRARY = $(TARGETDIR)/libst.a -diff -Nur backtrace.c.1.5.2 backtrace.c ---- backtrace.c.1.5.2 Wed Dec 31 16:00:00 1969 -+++ backtrace.c Wed Sep 7 13:40:21 2005 -@@ -0,0 +1,211 @@ -+/* -+ * The contents of this file are subject to the Mozilla Public -+ * License Version 1.1 (the "License"); you may not use this file -+ * except in compliance with the License. You may obtain a copy of -+ * the License at http://www.mozilla.org/MPL/ -+ * -+ * Software distributed under the License is distributed on an "AS -+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -+ * implied. See the License for the specific language governing -+ * rights and limitations under the License. -+ * -+ * Contributor(s): Michael Abd-El-Malek (mabdelmalek@cmu.edu) -+ * Carnegie Mellon University -+ * -+ * Alternatively, the contents of this file may be used under the -+ * terms of the GNU General Public License Version 2 or later (the -+ * "GPL"), in which case the provisions of the GPL are applicable -+ * instead of those above. If you wish to allow use of your -+ * version of this file only under the terms of the GPL and not to -+ * allow others to use your version of this file under the MPL, -+ * indicate your decision by deleting the provisions above and -+ * replace them with the notice and other provisions required by -+ * the GPL. If you do not delete the provisions above, a recipient -+ * may use your version of this file under either the MPL or the -+ * GPL. -+ */ -+ -+ -+ -+/* -+ * This file contains routines for printing a stack trace of all threads. -+ * Only works when DEBUG is defined and where glibc is available, since it -+ * provides the backtrace() function. -+ */ -+ -+#define _GNU_SOURCE /* to get program_invocation_name */ -+ -+#include -+#include -+ -+ -+#if defined(DEBUG) && defined(__GLIBC__) -+ -+#include -+#include "common.h" -+#include -+#include -+#include -+ -+ -+/* The maximum number of frames to get a stack trace for. If a thread has more -+ * frames than this, then we only show the latest X frames. */ -+#define MAX_NUM_FRAMES 64 -+ -+ -+typedef struct thread_stack_s { -+ uint32_t num_frames; -+ void* addresses[MAX_NUM_FRAMES]; /* frame pointers */ -+ char* locations[MAX_NUM_FRAMES]; /* file/function/line numbers */ -+ uint32_t num_matches; -+ -+ struct thread_stack_s* next; -+} thread_stack_t; -+ -+static thread_stack_t* stacks = NULL; -+ -+ -+/* Converts the function's memory addresses to function names, file names, and -+ * line numbers. Calls binutil's addr2line program. */ -+static void get_symbol_names(thread_stack_t *stack) -+{ -+ char program_to_run[1024], function[256], filename_lineno[256], temp[19]; -+ FILE* output; -+ int num_bytes_left; -+ uint32_t i; -+ -+ /* Construct the arguments to addr2line */ -+ num_bytes_left = sizeof(program_to_run); -+ num_bytes_left -= snprintf(program_to_run, sizeof(program_to_run), -+ "addr2line -fCe %s", program_invocation_name); -+ for (i = 0; i < stack->num_frames && num_bytes_left > 0; ++i) { -+ num_bytes_left -= snprintf(temp, sizeof(temp), " %p", stack->addresses[i]); -+ strncat(program_to_run, temp, num_bytes_left); -+ } -+ -+ /* Use popen to execute addr2line and read its ouput */ -+ output = popen(program_to_run, "r"); -+ for (i = 0; i < stack->num_frames; ++i) { -+ char* function_listing = (char*) malloc(512); -+ fscanf(output, "%255s\n", function); -+ fscanf(output, "%255s\n", filename_lineno); -+ snprintf(function_listing, 512, "%s at %s", function, filename_lineno); -+ stack->locations[i] = function_listing; -+ } -+ pclose(output); -+} -+ -+ -+static void print_stack(thread_stack_t* stack) -+{ -+ int skip_offset = 0, cmp_len; -+ uint32_t i; -+ -+ /* Get the function names/filenames/line numbers */ -+ get_symbol_names(stack); -+ -+ cmp_len = strlen("_st_iterate_threads_helper"); -+ -+ /* Print the backtrace */ -+ for (i = 0; i < stack->num_frames; ++i) { -+ /* Skip frames we don't have location info for */ -+ if (!strncmp(stack->locations[i], "??", 2)) { -+ continue; -+ } -+ -+ /* Skip the frames that are used for printing the stack trace */ -+ if (skip_offset) { -+ printf("\t#%2d %s %p\n", i - skip_offset, stack->locations[i], -+ stack->addresses[i]); -+ } else if (!strncmp(stack->locations[i], "_st_iterate_threads_helper", -+ cmp_len)) { -+ skip_offset = i + 1; -+ } -+ } -+} -+ -+ -+static void add_current_thread_stack(void) -+{ -+ thread_stack_t *new_stack = malloc(sizeof(thread_stack_t)); -+ thread_stack_t *search; -+ -+ /* Call glibc function to get the backtrace */ -+ new_stack->num_frames = backtrace(new_stack->addresses, MAX_NUM_FRAMES); -+ -+ /* Check if we have another stacks that is equivalent. If so, then coaelsce -+ * two stacks into one, to minimize output to user. */ -+ search = stacks; -+ while (search) { -+ if (search->num_frames == new_stack->num_frames && -+ !memcmp(search->addresses, new_stack->addresses, -+ search->num_frames * sizeof(void*))) { -+ /* Found an existing stack that is the same as this thread's stack */ -+ ++search->num_matches; -+ free(new_stack); -+ return; -+ } else { -+ search = search->next; -+ } -+ } -+ -+ /* This is a new stack. Add it to the list of stacks. */ -+ new_stack->num_matches = 1; -+ new_stack->next = stacks; -+ stacks = new_stack; -+} -+ -+static void print_stack_frames(void) -+{ -+ while (stacks) { -+ printf("\n%u thread(s) with this backtrace:\n", stacks->num_matches); -+ print_stack(stacks); -+ stacks = stacks->next; -+ } -+ printf("\n"); -+} -+ -+static void free_stacks(void) -+{ -+ uint32_t i; -+ while (stacks) { -+ thread_stack_t *next = stacks->next; -+ for (i = 0; i < stacks->num_frames; ++i) { -+ free(stacks->locations[i]); -+ } -+ free(stacks); -+ stacks = next; -+ } -+ stacks = NULL; -+} -+ -+ -+static void st_print_thread_stack(_st_thread_t *thread, int start_flag, -+ int end_flag) -+{ -+ if (end_flag == 0) { -+ add_current_thread_stack(); -+ } else { -+ print_stack_frames(); -+ } -+} -+ -+ -+void _st_print_thread_stacks(int ignore) -+{ -+ _st_iterate_threads_flag = 1; -+ _st_iterate_threads_helper(st_print_thread_stack); -+ _st_iterate_threads_flag = 0; -+ -+ /* Deallocate memory */ -+ free_stacks(); -+} -+ -+#else /* defined(DEBUG) && defined(__GLIBC__) */ -+ -+void _st_print_thread_stacks(int ignore) -+{ -+ printf("%s: need DEBUG mode and glibc-specific functions to read stack.\n", -+ __FUNCTION__); -+} -+#endif /* defined(DEBUG) && defined(__GLIBC__) */ -diff -Nur common.h.1.5.2 common.h ---- common.h.1.5.2 Wed Sep 7 14:18:37 2005 -+++ common.h Wed Sep 7 14:35:36 2005 -@@ -371,8 +371,18 @@ - */ - - #ifdef DEBUG --void _st_iterate_threads(void); --#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() -+typedef void(*_st_func_ptr_t)(_st_thread_t *thread, -+ int start_flag, -+ int end_flag); -+/* Pointer to function that will be called on thread switch */ -+extern _st_func_ptr_t _st_iterate_func_ptr; -+extern int _st_iterate_threads_flag; -+/* Thread iteration function that will call an arbitrary function */ -+extern void _st_iterate_threads_helper(_st_func_ptr_t func); -+#define ST_DEBUG_ITERATE_THREADS() \ -+ if (_st_iterate_func_ptr) { \ -+ _st_iterate_threads_helper(_st_iterate_func_ptr); \ -+ } - #else - #define ST_DEBUG_ITERATE_THREADS() - #endif -diff -Nur public.h.1.5.2 public.h ---- public.h.1.5.2 Wed Sep 7 11:46:58 2005 -+++ public.h Wed Sep 7 13:38:46 2005 -@@ -171,8 +171,10 @@ - extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); - - #ifdef DEBUG --extern void _st_show_thread_stack(st_thread_t thread, const char *messg); -+extern void _st_show_thread_stack(st_thread_t thread, int start_flag, -+ int end_flag); - extern void _st_iterate_threads(void); -+extern void _st_print_thread_stacks(int ignore); - #endif - - #ifdef __cplusplus -diff -Nur sched.c.1.5.2 sched.c ---- sched.c.1.5.2 Wed Sep 7 10:48:05 2005 -+++ sched.c Wed Sep 7 13:38:46 2005 -@@ -919,16 +919,13 @@ - - - #ifdef DEBUG --/* ARGSUSED */ --void _st_show_thread_stack(_st_thread_t *thread, const char *messg) --{ -- --} -- - /* To be set from debugger */ - int _st_iterate_threads_flag = 0; -+/* Thread iteration function that will call an arbitrary function */ -+_st_func_ptr_t _st_iterate_func_ptr = NULL; - --void _st_iterate_threads(void) -+/* This function iterates over all threads, calling "func" for each thread. */ -+void _st_iterate_threads_helper(_st_func_ptr_t func) - { - static _st_thread_t *thread = NULL; - static jmp_buf orig_jb, save_jb; -@@ -944,16 +941,20 @@ - - if (thread) { - memcpy(thread->context, save_jb, sizeof(jmp_buf)); -- _st_show_thread_stack(thread, NULL); -+ func(thread, 0, 0); - } else { - if (MD_SETJMP(orig_jb)) { - _st_iterate_threads_flag = 0; -+ _st_iterate_func_ptr = NULL; - thread = NULL; -- _st_show_thread_stack(thread, "Iteration completed"); -+ /* Last thread to iterate through */ -+ func(thread, 0, 1); - return; - } -+ /* First thread to iterate through */ - thread = _ST_CURRENT_THREAD(); -- _st_show_thread_stack(thread, "Iteration started"); -+ _st_iterate_func_ptr = func; -+ func(thread, 1, 0); - } - - q = thread->tlink.next; -@@ -966,5 +967,17 @@ - memcpy(save_jb, thread->context, sizeof(jmp_buf)); - MD_LONGJMP(thread->context, 1); - } -+ -+/* ARGSUSED */ -+void _st_show_thread_stack(_st_thread_t *thread, int start_flag, int end_flag) -+{ -+} -+ -+/* Iterate over threads inside debugger; see st/README */ -+void _st_iterate_threads(void) -+{ -+ _st_iterate_threads_helper(_st_show_thread_stack); -+} -+ - #endif /* DEBUG */ - diff --git a/trunk/3rdparty/st-srs/extensions/stx.h b/trunk/3rdparty/st-srs/extensions/stx.h deleted file mode 100644 index 8371e0d93..000000000 --- a/trunk/3rdparty/st-srs/extensions/stx.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef _STX_H_ -#define _STX_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "st.h" - - -#ifdef __cplusplus -extern "C" { -#endif - - -/***************************************** - * Basic types definitions - */ - -typedef struct _stx_centry stx_cache_entry_t; -typedef struct _stx_cache stx_cache_t; - -/* This is public type */ -typedef struct _stx_cache_info { - size_t max_size; - size_t max_weight; - size_t hash_size; - size_t cur_size; - size_t cur_weight; - unsigned long hits; - unsigned long lookups; - unsigned long inserts; - unsigned long deletes; -} stx_cache_info_t; - - -/***************************************** - * Cache and cache entry methods - */ - -stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight, - size_t hash_size, - unsigned long (*key_hash_fn)(const void *key), - long (*key_cmp_fn)(const void *key1, - const void *key2), - void (*cleanup_fn)(void *key, void *data)); -void stx_cache_empty(stx_cache_t *cache); -void stx_cache_traverse(stx_cache_t *cache, - void (*callback)(void *key, void *data)); -void stx_cache_traverse_lru(stx_cache_t *, void (*)(void *, void *), - unsigned int); -void stx_cache_traverse_mru(stx_cache_t *, void (*)(void *, void *), - unsigned int); -void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info); -size_t stx_cache_getsize(stx_cache_t *cache); -size_t stx_cache_getweight(stx_cache_t *cache); - - -stx_cache_entry_t *stx_cache_entry_create(void *key, void *data, - size_t weight); -void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry); -stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key); -void stx_cache_entry_release(stx_cache_t *, stx_cache_entry_t *); -int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry); -stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache); -int stx_cache_entry_sizeof(void); -void *stx_cache_entry_getdata(stx_cache_entry_t *entry); -void *stx_cache_entry_getkey(stx_cache_entry_t *entry); -size_t stx_cache_entry_getweight(stx_cache_entry_t *entry); - - -int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size); -void stx_dns_cache_getinfo(stx_cache_info_t *info); -int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, - int *num_addrs, st_utime_t timeout); -int stx_dns_getaddr(const char *hostname, struct in_addr *addr, - st_utime_t timeout); - -#ifdef __cplusplus -} -#endif - -#endif /* !_STX_H_ */ - diff --git a/trunk/3rdparty/st-srs/extensions/stx_fileio.c b/trunk/3rdparty/st-srs/extensions/stx_fileio.c deleted file mode 100644 index cb24346e8..000000000 --- a/trunk/3rdparty/st-srs/extensions/stx_fileio.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * File I/O extension to the State Threads Library. - */ - -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the file I/O extension to the State Threads Library. - * - * The Initial Developer of the Original Code is Jeff - * . Portions created by the Initial - * Developer are Copyright (C) 2002 the Initial Developer. All Rights - * Reserved. - * - * Contributor(s): (none) - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#include - -#include "stx_fileio.h" - -#define STX_FILEIO_SIGNUM SIGUSR2 - -typedef struct { - st_netfd_t data_fd; - st_netfd_t control_fd; - pid_t pid; -} fileio_data_t; - -#define FILEREADER_MAX_READ 1024 - -typedef struct { - off_t offset; - ssize_t nbytes; -} file_reader_cb_t; - -/** - * Fork a process to read a file and return its pid. Receives - * offset/length commands from control stream and sends corresponding data - * to out stream. A zero length on the control stream signals an end. - * - * @param fd stream from which to read - * @param control_out receives the file descriptor to which control commands can be sent - * @param fd_out receives the file descriptor from which the output of the command can be read. - * @return PID of the process created to execute the command - */ -pid_t -file_reader(int fd, int *fd_control, int *fd_out) -{ - pid_t pid; - int control_pipe[2], out_pipe[2]; - - if (pipe(control_pipe) < 0 || pipe(out_pipe) < 0) - return (pid_t)-1; - - pid = fork(); - if (pid == (pid_t) -1) - { - close(control_pipe[0]); - close(control_pipe[1]); - close(out_pipe[0]); - close(out_pipe[1]); - return pid; - } - else if (pid == (pid_t) 0) - { - // child - off_t pos = 0; - file_reader_cb_t cb; - char buf[FILEREADER_MAX_READ]; - if (fd == -1) - _exit(EXIT_FAILURE); - - while (sizeof(cb) == read(control_pipe[0], &cb, sizeof(cb))) { - ssize_t nb; - if (0 >= cb.nbytes) - goto clean_exit; - if (pos != cb.offset) { - pos = lseek(fd, cb.offset, SEEK_SET); - if (pos == (off_t)-1) - break; - } - nb = read(fd, buf, cb.nbytes); - if (nb == (ssize_t)-1) - break; - pos += nb; - write(out_pipe[1], (char *)&nb, sizeof(nb)); - write(out_pipe[1], buf, nb); - } - perror("ERROR: file_reader: "); - clean_exit: - close(control_pipe[0]); - close(control_pipe[1]); - close(out_pipe[0]); - close(out_pipe[1]); - _exit(EXIT_SUCCESS); - } - - // parent - close(out_pipe[1]); - close(control_pipe[0]); - *fd_out = out_pipe[0]; - *fd_control = control_pipe[1]; - return pid; -} - -/** - * fileio_data_t destructor callback - */ -static void -fileio_data_destructor(void *dat_in) -{ - if (dat_in) { - fileio_data_t *dat = (fileio_data_t *)dat_in; - file_reader_cb_t cb; - cb.offset = 0; - cb.nbytes = 0; - st_write(dat->control_fd, (char *)&cb, sizeof(cb), - ST_UTIME_NO_TIMEOUT); - waitpid(dat->pid, NULL, 0); - st_netfd_close(dat->control_fd); - st_netfd_close(dat->data_fd); - free(dat_in); - } -} - -/** - * Retrieve fileio_data_t struct from an st descriptor. Create and store - * a new one if needed. - */ -static fileio_data_t *get_fileio_data(st_netfd_t fd) -{ - fileio_data_t *dat = (fileio_data_t *)st_netfd_getspecific(fd); - if (!dat) { - int fd_control, fd_out; - pid_t pid = file_reader(st_netfd_fileno(fd), &fd_control, &fd_out); - if (pid != (pid_t)-1) { - dat = (fileio_data_t *)calloc(1, sizeof(fileio_data_t)); - dat->control_fd = st_netfd_open(fd_control); - dat->data_fd = st_netfd_open(fd_out); - dat->pid = pid; - st_netfd_setspecific(fd, dat, fileio_data_destructor); - } - } - return dat; -} - -/** - * Read data from the specified section of a file. Uses a forked - * file_reader process to do the actual reading so as to avoid causing all - * State Threads to block. - * - * @param fd must refer to a seekable file. - * @param offset absolute offset within the file - * @param buf output buffer - * @param nbytes size of the output buffer - * @param timeout - */ -ssize_t -stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout) -{ - fileio_data_t *dat = get_fileio_data(fd); - if (dat) { - file_reader_cb_t cb; - ssize_t ret = (ssize_t)-1; - cb.offset = offset; - cb.nbytes = nbytes; - st_write(dat->control_fd, (char *)&cb, sizeof(cb), timeout); - if (sizeof(ret) == st_read(dat->data_fd, (char *)&ret, sizeof(ret), timeout) && 0 < ret && ret <= nbytes) { - return st_read(dat->data_fd, buf, ret, timeout); - } else { - return ret; - } - } - - return (ssize_t)-1; -} diff --git a/trunk/3rdparty/st-srs/extensions/stx_fileio.h b/trunk/3rdparty/st-srs/extensions/stx_fileio.h deleted file mode 100644 index b6bec190b..000000000 --- a/trunk/3rdparty/st-srs/extensions/stx_fileio.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * File I/O extension to the State Threads Library. - */ - -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the file I/O extension to the State Threads Library. - * - * The Initial Developer of the Original Code is Jeff - * . Portions created by the Initial - * Developer are Copyright (C) 2002 the Initial Developer. All Rights - * Reserved. - * - * Contributor(s): (none) - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#ifndef __STX_FILEIO_H__ -#define __STX_FILEIO_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -extern ssize_t stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout); - -#ifdef __cplusplus -} -#endif -#endif /* !__STX_FILEIO_H__ */ diff --git a/trunk/3rdparty/st-srs/extensions/testdns.c b/trunk/3rdparty/st-srs/extensions/testdns.c deleted file mode 100644 index aa896b25e..000000000 --- a/trunk/3rdparty/st-srs/extensions/testdns.c +++ /dev/null @@ -1,112 +0,0 @@ -#include "stx.h" -#include -#include - - -#define MAX_ADDRS 128 -#define TIMEOUT (4*1000000LL) - -static void do_resolve(const char *host) -{ - struct in_addr addrs[MAX_ADDRS]; - int i, n = MAX_ADDRS; - - if (stx_dns_getaddrlist(host, addrs, &n, TIMEOUT) < 0) { - fprintf(stderr, "stx_dns_getaddrlist: can't resolve %s: ", host); - if (h_errno == NETDB_INTERNAL) - perror(""); - else - herror(""); - } else { - if (n > 0) - printf("%-40s %s\n", (char *)host, inet_ntoa(addrs[0])); - for (i = 1; i < n; i++) - printf("%-40s %s\n", "", inet_ntoa(addrs[i])); - } -} - -static void show_info(void) -{ - stx_cache_info_t info; - - stx_dns_cache_getinfo(&info); - printf("DNS cache info:\n\n"); - printf("max_size: %8d\n", (int)info.max_size); - printf("capacity: %8d bytes\n", (int)info.max_weight); - printf("hash_size: %8d\n", (int)info.hash_size); - printf("cur_size: %8d\n" - "cur_mem: %8d bytes\n" - "hits: %8d\n" - "lookups: %8d\n" - "inserts: %8d\n" - "deletes: %8d\n", - (int)info.cur_size, (int)info.cur_weight, (int)info.hits, - (int)info.lookups, (int)info.inserts, (int)info.deletes); -} - -extern stx_cache_t *_stx_dns_cache; - -static void printhost(void *host, void *data) -{ - printf("%s\n", (char *)host); -} - -static void show_lru(void) -{ - printf("LRU hosts:\n\n"); - stx_cache_traverse_lru(_stx_dns_cache, printhost, 10); -} - -static void show_mru(void) -{ - printf("MRU hosts:\n\n"); - stx_cache_traverse_mru(_stx_dns_cache, printhost, 10); -} - -static void flush_cache(void) -{ - stx_cache_empty(_stx_dns_cache); - printf("DNS cache is empty\n"); -} - - -int main() -{ - char line[256]; - char str[sizeof(line)]; - - st_init(); - stx_dns_cache_init(100, 10000, 101); - - for ( ; ; ) { - fputs("> ", stdout); - fflush(stdout); - if (!fgets(line, sizeof(line), stdin)) - break; - if (sscanf(line, "%s", str) != 1) - continue; - if (strcmp(str, "exit") == 0 || strcmp(str, "quit") == 0) - break; - if (strcmp(str, "info") == 0) { - show_info(); - continue; - } - if (strcmp(str, "lru") == 0) { - show_lru(); - continue; - } - if (strcmp(str, "mru") == 0) { - show_mru(); - continue; - } - if (strcmp(str, "flush") == 0) { - flush_cache(); - continue; - } - - do_resolve(str); - } - - return 0; -} - diff --git a/trunk/3rdparty/st-srs/io.c b/trunk/3rdparty/st-srs/io.c deleted file mode 100644 index 912de0b28..000000000 --- a/trunk/3rdparty/st-srs/io.c +++ /dev/null @@ -1,769 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "common.h" - - -#if EAGAIN != EWOULDBLOCK - #define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK)) -#else - #define _IO_NOT_READY_ERROR (errno == EAGAIN) -#endif - -#define _LOCAL_MAXIOV 16 - -/* File descriptor object free list */ -static _st_netfd_t *_st_netfd_freelist = NULL; -/* Maximum number of file descriptors that the process can open */ -static int _st_osfd_limit = -1; - -static void _st_netfd_free_aux_data(_st_netfd_t *fd); - -int _st_io_init(void) -{ - struct sigaction sigact; - struct rlimit rlim; - int fdlim; - - /* Ignore SIGPIPE */ - sigact.sa_handler = SIG_IGN; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - if (sigaction(SIGPIPE, &sigact, NULL) < 0) - return -1; - - /* Set maximum number of open file descriptors */ - if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) - return -1; - - fdlim = (*_st_eventsys->fd_getlimit)(); - if (fdlim > 0 && rlim.rlim_max > (rlim_t) fdlim) { - rlim.rlim_max = fdlim; - } - - /** - * by SRS, for osx. - * when rlimit max is negative, for example, osx, use cur directly. - * @see https://github.com/winlinvip/simple-rtmp-server/issues/336 - */ - if ((int)rlim.rlim_max < 0) { - _st_osfd_limit = (int)(fdlim > 0? fdlim : rlim.rlim_cur); - return 0; - } - - rlim.rlim_cur = rlim.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) - return -1; - _st_osfd_limit = (int) rlim.rlim_max; - - return 0; -} - - -int st_getfdlimit(void) -{ - return _st_osfd_limit; -} - - -void st_netfd_free(_st_netfd_t *fd) -{ - if (!fd->inuse) - return; - - fd->inuse = 0; - if (fd->aux_data) - _st_netfd_free_aux_data(fd); - if (fd->private_data && fd->destructor) - (*(fd->destructor))(fd->private_data); - fd->private_data = NULL; - fd->destructor = NULL; - fd->next = _st_netfd_freelist; - _st_netfd_freelist = fd; -} - - -static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket) -{ - _st_netfd_t *fd; - int flags = 1; - - if ((*_st_eventsys->fd_new)(osfd) < 0) - return NULL; - - if (_st_netfd_freelist) { - fd = _st_netfd_freelist; - _st_netfd_freelist = _st_netfd_freelist->next; - } else { - fd = calloc(1, sizeof(_st_netfd_t)); - if (!fd) - return NULL; - } - - fd->osfd = osfd; - fd->inuse = 1; - fd->next = NULL; - - if (nonblock) { - /* Use just one system call */ - if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) - return fd; - /* Do it the Posix way */ - if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || - fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) { - st_netfd_free(fd); - return NULL; - } - } - - return fd; -} - - -_st_netfd_t *st_netfd_open(int osfd) -{ - return _st_netfd_new(osfd, 1, 0); -} - - -_st_netfd_t *st_netfd_open_socket(int osfd) -{ - return _st_netfd_new(osfd, 1, 1); -} - - -int st_netfd_close(_st_netfd_t *fd) -{ - if ((*_st_eventsys->fd_close)(fd->osfd) < 0) - return -1; - - st_netfd_free(fd); - return close(fd->osfd); -} - - -int st_netfd_fileno(_st_netfd_t *fd) -{ - return (fd->osfd); -} - - -void st_netfd_setspecific(_st_netfd_t *fd, void *value, _st_destructor_t destructor) -{ - if (value != fd->private_data) { - /* Free up previously set non-NULL data value */ - if (fd->private_data && fd->destructor) - (*(fd->destructor))(fd->private_data); - } - fd->private_data = value; - fd->destructor = destructor; -} - - -void *st_netfd_getspecific(_st_netfd_t *fd) -{ - return (fd->private_data); -} - - -/* - * Wait for I/O on a single descriptor. - */ -int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeout) -{ - struct pollfd pd; - int n; - - pd.fd = fd->osfd; - pd.events = (short) how; - pd.revents = 0; - - if ((n = st_poll(&pd, 1, timeout)) < 0) - return -1; - if (n == 0) { - /* Timed out */ - errno = ETIME; - return -1; - } - if (pd.revents & POLLNVAL) { - errno = EBADF; - return -1; - } - - return 0; -} - - -#ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT -/* No-op */ -int st_netfd_serialize_accept(_st_netfd_t *fd) -{ - fd->aux_data = NULL; - return 0; -} - -/* No-op */ -static void _st_netfd_free_aux_data(_st_netfd_t *fd) -{ - fd->aux_data = NULL; -} - -_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) -{ - int osfd, err; - _st_netfd_t *newfd; - - while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return NULL; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return NULL; - } - - /* On some platforms the new socket created by accept() inherits */ - /* the nonblocking attribute of the listening socket */ -#if defined (MD_ACCEPT_NB_INHERITED) - newfd = _st_netfd_new(osfd, 0, 1); -#elif defined (MD_ACCEPT_NB_NOT_INHERITED) - newfd = _st_netfd_new(osfd, 1, 1); -#else - #error Unknown OS -#endif - - if (!newfd) { - err = errno; - close(osfd); - errno = err; - } - - return newfd; -} - -#else /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ -/* - * On some platforms accept() calls from different processes - * on the same listen socket must be serialized. - * The following code serializes accept()'s without process blocking. - * A pipe is used as an inter-process semaphore. - */ -int st_netfd_serialize_accept(_st_netfd_t *fd) -{ - _st_netfd_t **p; - int osfd[2], err; - - if (fd->aux_data) { - errno = EINVAL; - return -1; - } - if ((p = (_st_netfd_t **)calloc(2, sizeof(_st_netfd_t *))) == NULL) - return -1; - if (pipe(osfd) < 0) { - free(p); - return -1; - } - if ((p[0] = st_netfd_open(osfd[0])) != NULL && (p[1] = st_netfd_open(osfd[1])) != NULL && write(osfd[1], " ", 1) == 1) { - fd->aux_data = p; - return 0; - } - /* Error */ - err = errno; - if (p[0]) - st_netfd_free(p[0]); - if (p[1]) - st_netfd_free(p[1]); - close(osfd[0]); - close(osfd[1]); - free(p); - errno = err; - - return -1; -} - -static void _st_netfd_free_aux_data(_st_netfd_t *fd) -{ - _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; - - st_netfd_close(p[0]); - st_netfd_close(p[1]); - free(p); - fd->aux_data = NULL; -} - -_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) -{ - int osfd, err; - _st_netfd_t *newfd; - _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; - ssize_t n; - char c; - - for ( ; ; ) { - if (p == NULL) { - osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); - } else { - /* Get the lock */ - n = st_read(p[0], &c, 1, timeout); - if (n < 0) - return NULL; - ST_ASSERT(n == 1); - /* Got the lock */ - osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); - /* Unlock */ - err = errno; - n = st_write(p[1], &c, 1, timeout); - ST_ASSERT(n == 1); - errno = err; - } - if (osfd >= 0) - break; - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return NULL; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return NULL; - } - - /* On some platforms the new socket created by accept() inherits */ - /* the nonblocking attribute of the listening socket */ -#if defined (MD_ACCEPT_NB_INHERITED) - newfd = _st_netfd_new(osfd, 0, 1); -#elif defined (MD_ACCEPT_NB_NOT_INHERITED) - newfd = _st_netfd_new(osfd, 1, 1); -#else -#error Unknown OS -#endif - - if (!newfd) { - err = errno; - close(osfd); - errno = err; - } - - return newfd; -} -#endif /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ - - -int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout) -{ - int n, err = 0; - - while (connect(fd->osfd, addr, addrlen) < 0) { - if (errno != EINTR) { - /* - * On some platforms, if connect() is interrupted (errno == EINTR) - * after the kernel binds the socket, a subsequent connect() - * attempt will fail with errno == EADDRINUSE. Ignore EADDRINUSE - * iff connect() was previously interrupted. See Rich Stevens' - * "UNIX Network Programming," Vol. 1, 2nd edition, p. 413 - * ("Interrupted connect"). - */ - if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) - return -1; - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - return -1; - /* Try to find out whether the connection setup succeeded or failed */ - n = sizeof(int); - if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, (socklen_t *)&n) < 0) - return -1; - if (err) { - errno = err; - return -1; - } - break; - } - err = 1; - } - - return 0; -} - - -ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) -{ - ssize_t n; - - while ((n = read(fd->osfd, buf, nbyte)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return n; -} - - -int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout) -{ - struct iovec iov, *riov; - int riov_size, rv; - - iov.iov_base = buf; - iov.iov_len = *resid; - riov = &iov; - riov_size = 1; - rv = st_readv_resid(fd, &riov, &riov_size, timeout); - *resid = iov.iov_len; - return rv; -} - - -ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) -{ - ssize_t n; - - while ((n = readv(fd->osfd, iov, iov_size)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return n; -} - -int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) -{ - ssize_t n; - - while (*iov_size > 0) { - if (*iov_size == 1) - n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); - else - n = readv(fd->osfd, *iov, *iov_size); - if (n < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - } else if (n == 0) - break; - else { - while ((size_t) n >= (*iov)->iov_len) { - n -= (*iov)->iov_len; - (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; - (*iov)->iov_len = 0; - (*iov)++; - (*iov_size)--; - if (n == 0) - break; - } - if (*iov_size == 0) - break; - (*iov)->iov_base = (char *) (*iov)->iov_base + n; - (*iov)->iov_len -= n; - } - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return 0; -} - - -ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) -{ - size_t resid = nbyte; - return st_read_resid(fd, buf, &resid, timeout) == 0 ? - (ssize_t) (nbyte - resid) : -1; -} - - -int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, st_utime_t timeout) -{ - struct iovec iov, *riov; - int riov_size, rv; - - iov.iov_base = (void *) buf; /* we promise not to modify buf */ - iov.iov_len = *resid; - riov = &iov; - riov_size = 1; - rv = st_writev_resid(fd, &riov, &riov_size, timeout); - *resid = iov.iov_len; - return rv; -} - - -ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout) -{ - size_t resid = nbyte; - return st_write_resid(fd, buf, &resid, timeout) == 0 ? - (ssize_t) (nbyte - resid) : -1; -} - - -ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) -{ - ssize_t n, rv; - size_t nleft, nbyte; - int index, iov_cnt; - struct iovec *tmp_iov; - struct iovec local_iov[_LOCAL_MAXIOV]; - - /* Calculate the total number of bytes to be sent */ - nbyte = 0; - for (index = 0; index < iov_size; index++) - nbyte += iov[index].iov_len; - - rv = (ssize_t)nbyte; - nleft = nbyte; - tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ - iov_cnt = iov_size; - - while (nleft > 0) { - if (iov_cnt == 1) { - if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) - rv = -1; - break; - } - if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) { - rv = -1; - break; - } - } else { - if ((size_t) n == nleft) - break; - nleft -= n; - /* Find the next unwritten vector */ - n = (ssize_t)(nbyte - nleft); - for (index = 0; (size_t) n >= iov[index].iov_len; index++) - n -= iov[index].iov_len; - - if (tmp_iov == iov) { - /* Must copy iov's around */ - if (iov_size - index <= _LOCAL_MAXIOV) { - tmp_iov = local_iov; - } else { - tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec)); - if (tmp_iov == NULL) - return -1; - } - } - - /* Fill in the first partial read */ - tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[n]); - tmp_iov[0].iov_len = iov[index].iov_len - n; - index++; - /* Copy the remaining vectors */ - for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) { - tmp_iov[iov_cnt].iov_base = iov[index].iov_base; - tmp_iov[iov_cnt].iov_len = iov[index].iov_len; - } - } - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { - rv = -1; - break; - } - } - - if (tmp_iov != iov && tmp_iov != local_iov) - free(tmp_iov); - - return rv; -} - - -int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) -{ - ssize_t n; - - while (*iov_size > 0) { - if (*iov_size == 1) - n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); - else - n = writev(fd->osfd, *iov, *iov_size); - if (n < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - } else { - while ((size_t) n >= (*iov)->iov_len) { - n -= (*iov)->iov_len; - (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; - (*iov)->iov_len = 0; - (*iov)++; - (*iov_size)--; - if (n == 0) - break; - } - if (*iov_size == 0) - break; - (*iov)->iov_base = (char *) (*iov)->iov_base + n; - (*iov)->iov_len -= n; - } - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - return -1; - } - - return 0; -} - - -/* - * Simple I/O functions for UDP. - */ -int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout) -{ - int n; - - while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return n; -} - - -int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout) -{ - int n; - - while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - return -1; - } - - return n; -} - - -int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeout) -{ - int n; - - while ((n = recvmsg(fd->osfd, msg, flags)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) - return -1; - } - - return n; -} - - -int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, st_utime_t timeout) -{ - int n; - - while ((n = sendmsg(fd->osfd, msg, flags)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - return -1; - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - return -1; - } - - return n; -} - - - -/* - * To open FIFOs or other special files. - */ -_st_netfd_t *st_open(const char *path, int oflags, mode_t mode) -{ - int osfd, err; - _st_netfd_t *newfd; - - while ((osfd = open(path, oflags | O_NONBLOCK, mode)) < 0) { - if (errno != EINTR) - return NULL; - } - - newfd = _st_netfd_new(osfd, 0, 0); - if (!newfd) { - err = errno; - close(osfd); - errno = err; - } - - return newfd; -} - diff --git a/trunk/3rdparty/st-srs/key.c b/trunk/3rdparty/st-srs/key.c deleted file mode 100644 index 5e64022c0..000000000 --- a/trunk/3rdparty/st-srs/key.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include "common.h" - - -/* - * Destructor table for per-thread private data - */ -static _st_destructor_t _st_destructors[ST_KEYS_MAX]; -static int key_max = 0; - - -/* - * Return a key to be used for thread specific data - */ -int st_key_create(int *keyp, _st_destructor_t destructor) -{ - if (key_max >= ST_KEYS_MAX) { - errno = EAGAIN; - return -1; - } - - *keyp = key_max++; - _st_destructors[*keyp] = destructor; - - return 0; -} - - -int st_key_getlimit(void) -{ - return ST_KEYS_MAX; -} - - -int st_thread_setspecific(int key, void *value) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - - if (key < 0 || key >= key_max) { - errno = EINVAL; - return -1; - } - - if (value != me->private_data[key]) { - /* free up previously set non-NULL data value */ - if (me->private_data[key] && _st_destructors[key]) { - (*_st_destructors[key])(me->private_data[key]); - } - me->private_data[key] = value; - } - - return 0; -} - - -void *st_thread_getspecific(int key) -{ - if (key < 0 || key >= key_max) - return NULL; - - return ((_ST_CURRENT_THREAD())->private_data[key]); -} - - -/* - * Free up all per-thread private data - */ -void _st_thread_cleanup(_st_thread_t *thread) -{ - int key; - - for (key = 0; key < key_max; key++) { - if (thread->private_data[key] && _st_destructors[key]) { - (*_st_destructors[key])(thread->private_data[key]); - thread->private_data[key] = NULL; - } - } -} - diff --git a/trunk/3rdparty/st-srs/libst.def b/trunk/3rdparty/st-srs/libst.def deleted file mode 100644 index 6eaf149a9..000000000 --- a/trunk/3rdparty/st-srs/libst.def +++ /dev/null @@ -1,51 +0,0 @@ -EXPORTS - st_accept @62 - st_cond_broadcast @63 - st_cond_destroy @64 - st_cond_new @65 - st_cond_signal @66 - st_cond_timedwait @67 - st_cond_wait @68 - st_connect @69 - st_getfdlimit @70 - st_init @71 - st_key_create @72 - st_key_getlimit @73 - st_mutex_destroy @74 - st_mutex_lock @75 - st_mutex_new @76 - st_mutex_trylock @77 - st_mutex_unlock @78 - st_netfd_close @79 - st_netfd_fileno @80 - st_netfd_free @81 - st_netfd_getspecific @82 - st_netfd_open @83 - st_netfd_open_socket @84 - st_netfd_poll @85 - st_netfd_serialize_accept @86 - st_netfd_setspecific @87 - st_open @88 - st_poll @89 - st_randomize_stacks @90 - st_read @91 - st_read_fully @92 - st_read_resid @93 - st_recvfrom @94 - st_sendto @95 - st_sleep @96 - st_thread_create @97 - st_thread_exit @98 - st_thread_getspecific @99 - st_thread_interrupt @100 - st_thread_join @101 - st_thread_self @102 - st_thread_setspecific @103 - st_time @104 - st_timecache_set @105 - st_usleep @106 - st_utime @107 - st_utime_last_clock @108 - st_write @109 - st_write_resid @110 - st_writev @111 diff --git a/trunk/3rdparty/st-srs/md.S b/trunk/3rdparty/st-srs/md.S deleted file mode 100644 index 2ef9c41f7..000000000 --- a/trunk/3rdparty/st-srs/md.S +++ /dev/null @@ -1,644 +0,0 @@ - -/* If user disable the ASM, such as avoiding bugs in ASM, donot compile it. */ -#if !defined(MD_ST_NO_ASM) - -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - */ - -#if defined(__ia64__) - - /****************************************************************/ - - /* - * The internal __jmp_buf layout is different from one used - * by setjmp()/longjmp(). - * - * Offset Description - * ------ ----------- - * 0x000 stack pointer (r12) - * 0x008 gp (r1) - * 0x010 caller's unat - * 0x018 fpsr - * 0x020 r4 - * 0x028 r5 - * 0x030 r6 - * 0x038 r7 - * 0x040 rp (b0) - * 0x048 b1 - * 0x050 b2 - * 0x058 b3 - * 0x060 b4 - * 0x068 b5 - * 0x070 ar.pfs - * 0x078 ar.lc - * 0x080 pr - * 0x088 ar.bsp - * 0x090 ar.unat - * 0x098 &__jmp_buf - * 0x0a0 ar.rsc - * 0x0a8 ar.rnat - * 0x0b0 f2 - * 0x0c0 f3 - * 0x0d0 f4 - * 0x0e0 f5 - * 0x0f0 f16 - * 0x100 f17 - * 0x110 f18 - * 0x120 f19 - * 0x130 f20 - * 0x130 f21 - * 0x140 f22 - * 0x150 f23 - * 0x160 f24 - * 0x170 f25 - * 0x180 f26 - * 0x190 f27 - * 0x1a0 f28 - * 0x1b0 f29 - * 0x1c0 f30 - * 0x1d0 f31 - * - * Note that the address of __jmp_buf is saved but not used: we assume - * that the jmp_buf data structure is never moved around in memory. - */ - - /* - * Implemented according to "IA-64 Software Conventions and Runtime - * Architecture Guide", Chapter 10: "Context Management". - */ - - .text - .psr abi64 - .psr lsb - .lsb - - /* _st_md_cxt_save(__jmp_buf env) */ - .align 32 - .global _st_md_cxt_save - .proc _st_md_cxt_save - _st_md_cxt_save: - alloc r14 = ar.pfs,1,0,0,0 - mov r16 = ar.unat - ;; - mov r17 = ar.fpsr - mov r2 = in0 - add r3 = 8,in0 - ;; - st8.spill.nta [r2] = sp,16 // r12 (sp) - ;; - st8.spill.nta [r3] = gp,16 // r1 (gp) - ;; - st8.nta [r2] = r16,16 // save caller's unat - st8.nta [r3] = r17,16 // save fpsr - add r8 = 0xb0,in0 - ;; - st8.spill.nta [r2] = r4,16 // r4 - ;; - st8.spill.nta [r3] = r5,16 // r5 - add r9 = 0xc0,in0 - ;; - stf.spill.nta [r8] = f2,32 - stf.spill.nta [r9] = f3,32 - mov r15 = rp - ;; - stf.spill.nta [r8] = f4,32 - stf.spill.nta [r9] = f5,32 - mov r17 = b1 - ;; - stf.spill.nta [r8] = f16,32 - stf.spill.nta [r9] = f17,32 - mov r18 = b2 - ;; - stf.spill.nta [r8] = f18,32 - stf.spill.nta [r9] = f19,32 - mov r19 = b3 - ;; - stf.spill.nta [r8] = f20,32 - stf.spill.nta [r9] = f21,32 - mov r20 = b4 - ;; - stf.spill.nta [r8] = f22,32 - stf.spill.nta [r9] = f23,32 - mov r21 = b5 - ;; - stf.spill.nta [r8] = f24,32 - stf.spill.nta [r9] = f25,32 - mov r22 = ar.lc - ;; - stf.spill.nta [r8] = f26,32 - stf.spill.nta [r9] = f27,32 - mov r24 = pr - ;; - stf.spill.nta [r8] = f28,32 - stf.spill.nta [r9] = f29,32 - ;; - stf.spill.nta [r8] = f30 - stf.spill.nta [r9] = f31 - - st8.spill.nta [r2] = r6,16 // r6 - ;; - st8.spill.nta [r3] = r7,16 // r7 - ;; - mov r23 = ar.bsp - mov r25 = ar.unat - - st8.nta [r2] = r15,16 // b0 - st8.nta [r3] = r17,16 // b1 - ;; - st8.nta [r2] = r18,16 // b2 - st8.nta [r3] = r19,16 // b3 - mov r26 = ar.rsc - ;; - st8.nta [r2] = r20,16 // b4 - st8.nta [r3] = r21,16 // b5 - ;; - st8.nta [r2] = r14,16 // ar.pfs - st8.nta [r3] = r22,16 // ar.lc - ;; - st8.nta [r2] = r24,16 // pr - st8.nta [r3] = r23,16 // ar.bsp - ;; - st8.nta [r2] = r25,16 // ar.unat - st8.nta [r3] = in0,16 // &__jmp_buf (just in case) - ;; - st8.nta [r2] = r26 // ar.rsc - ;; - flushrs // flush dirty regs to backing store - ;; - and r27 = ~0x3,r26 // clear ar.rsc.mode - ;; - mov ar.rsc = r27 // put RSE in enforced lazy mode - ;; - mov r28 = ar.rnat - ;; - st8.nta [r3] = r28 // ar.rnat - mov ar.rsc = r26 // restore ar.rsc - ;; - mov r8 = 0 - br.ret.sptk.few b0 - .endp _st_md_cxt_save - - - /****************************************************************/ - - /* _st_md_cxt_restore(__jmp_buf env, int val) */ - .global _st_md_cxt_restore - .proc _st_md_cxt_restore - _st_md_cxt_restore: - alloc r8 = ar.pfs,2,0,0,0 - add r2 = 0x88,in0 // r2 <- &jmpbuf.ar_bsp - mov r16 = ar.rsc - ;; - flushrs // flush dirty regs to backing store - ;; - and r17 = ~0x3,r16 // clear ar.rsc.mode - ;; - mov ar.rsc = r17 // put RSE in enforced lazy mode - ;; - invala // invalidate the ALAT - ;; - ld8 r23 = [r2],8 // r23 <- jmpbuf.ar_bsp - ;; - mov ar.bspstore = r23 // write BSPSTORE - ld8 r25 = [r2],24 // r25 <- jmpbuf.ar_unat - ;; - ld8 r26 = [r2],-8 // r26 <- jmpbuf.ar_rnat - ;; - mov ar.rnat = r26 // write RNAT - ld8 r27 = [r2] // r27 <- jmpbuf.ar_rsc - ;; - mov ar.rsc = r27 // write RSE control - mov r2 = in0 - ;; - mov ar.unat = r25 // write ar.unat - add r3 = 8,in0 - ;; - ld8.fill.nta sp = [r2],16 // r12 (sp) - ld8.fill.nta gp = [r3],16 // r1 (gp) - ;; - ld8.nta r16 = [r2],16 // caller's unat - ld8.nta r17 = [r3],16 // fpsr - ;; - ld8.fill.nta r4 = [r2],16 // r4 - ld8.fill.nta r5 = [r3],16 // r5 - ;; - ld8.fill.nta r6 = [r2],16 // r6 - ld8.fill.nta r7 = [r3],16 // r7 - ;; - mov ar.unat = r16 // restore caller's unat - mov ar.fpsr = r17 // restore fpsr - ;; - ld8.nta r16 = [r2],16 // b0 - ld8.nta r17 = [r3],16 // b1 - ;; - ld8.nta r18 = [r2],16 // b2 - ld8.nta r19 = [r3],16 // b3 - ;; - ld8.nta r20 = [r2],16 // b4 - ld8.nta r21 = [r3],16 // b5 - ;; - ld8.nta r11 = [r2],16 // ar.pfs - ld8.nta r22 = [r3],72 // ar.lc - ;; - ld8.nta r24 = [r2],48 // pr - mov b0 = r16 - ;; - ldf.fill.nta f2 = [r2],32 - ldf.fill.nta f3 = [r3],32 - mov b1 = r17 - ;; - ldf.fill.nta f4 = [r2],32 - ldf.fill.nta f5 = [r3],32 - mov b2 = r18 - ;; - ldf.fill.nta f16 = [r2],32 - ldf.fill.nta f17 = [r3],32 - mov b3 = r19 - ;; - ldf.fill.nta f18 = [r2],32 - ldf.fill.nta f19 = [r3],32 - mov b4 = r20 - ;; - ldf.fill.nta f20 = [r2],32 - ldf.fill.nta f21 = [r3],32 - mov b5 = r21 - ;; - ldf.fill.nta f22 = [r2],32 - ldf.fill.nta f23 = [r3],32 - mov ar.lc = r22 - ;; - ldf.fill.nta f24 = [r2],32 - ldf.fill.nta f25 = [r3],32 - cmp.eq p6,p7 = 0,in1 - ;; - ldf.fill.nta f26 = [r2],32 - ldf.fill.nta f27 = [r3],32 - mov ar.pfs = r11 - ;; - ldf.fill.nta f28 = [r2],32 - ldf.fill.nta f29 = [r3],32 - ;; - ldf.fill.nta f30 = [r2] - ldf.fill.nta f31 = [r3] - (p6) mov r8 = 1 - (p7) mov r8 = in1 - - mov pr = r24,-1 - br.ret.sptk.few b0 - .endp _st_md_cxt_restore - - /****************************************************************/ - - - - - - - - - -#elif defined(__i386__) - - /****************************************************************/ - - /* - * Internal __jmp_buf layout - */ - #define JB_BX 0 - #define JB_SI 1 - #define JB_DI 2 - #define JB_BP 3 - #define JB_SP 4 - #define JB_PC 5 - - .file "md.S" - .text - - /* _st_md_cxt_save(__jmp_buf env) */ - .globl _st_md_cxt_save - .type _st_md_cxt_save, @function - .align 16 - _st_md_cxt_save: - movl 4(%esp), %eax - - /* - * Save registers. - */ - movl %ebx, (JB_BX*4)(%eax) - movl %esi, (JB_SI*4)(%eax) - movl %edi, (JB_DI*4)(%eax) - /* Save SP */ - leal 4(%esp), %ecx - movl %ecx, (JB_SP*4)(%eax) - /* Save PC we are returning to */ - movl 0(%esp), %ecx - movl %ecx, (JB_PC*4)(%eax) - /* Save caller frame pointer */ - movl %ebp, (JB_BP*4)(%eax) - xorl %eax, %eax - ret - .size _st_md_cxt_save, .-_st_md_cxt_save - - - /****************************************************************/ - - /* _st_md_cxt_restore(__jmp_buf env, int val) */ - .globl _st_md_cxt_restore - .type _st_md_cxt_restore, @function - .align 16 - _st_md_cxt_restore: - /* First argument is jmp_buf */ - movl 4(%esp), %ecx - /* Second argument is return value */ - movl 8(%esp), %eax - /* Set the return address */ - movl (JB_PC*4)(%ecx), %edx - /* - * Restore registers. - */ - movl (JB_BX*4)(%ecx), %ebx - movl (JB_SI*4)(%ecx), %esi - movl (JB_DI*4)(%ecx), %edi - movl (JB_BP*4)(%ecx), %ebp - movl (JB_SP*4)(%ecx), %esp - testl %eax, %eax - jnz 1f - incl %eax - /* Jump to saved PC */ - 1: jmp *%edx - .size _st_md_cxt_restore, .-_st_md_cxt_restore - - /****************************************************************/ - - - - - - - - - - -#elif defined(__amd64__) || defined(__x86_64__) - - /****************************************************************/ - - /* - * Internal __jmp_buf layout - */ - #define JB_RBX 0 - #define JB_RBP 1 - #define JB_R12 2 - #define JB_R13 3 - #define JB_R14 4 - #define JB_R15 5 - #define JB_RSP 6 - #define JB_PC 7 - - .file "md.S" - .text - - /* _st_md_cxt_save(__jmp_buf env) */ - .globl _st_md_cxt_save - .type _st_md_cxt_save, @function - .align 16 - _st_md_cxt_save: - /* - * Save registers. - */ - movq %rbx, (JB_RBX*8)(%rdi) - movq %rbp, (JB_RBP*8)(%rdi) - movq %r12, (JB_R12*8)(%rdi) - movq %r13, (JB_R13*8)(%rdi) - movq %r14, (JB_R14*8)(%rdi) - movq %r15, (JB_R15*8)(%rdi) - /* Save SP */ - leaq 8(%rsp), %rdx - movq %rdx, (JB_RSP*8)(%rdi) - /* Save PC we are returning to */ - movq (%rsp), %rax - movq %rax, (JB_PC*8)(%rdi) - xorq %rax, %rax - ret - .size _st_md_cxt_save, .-_st_md_cxt_save - - - /****************************************************************/ - - /* _st_md_cxt_restore(__jmp_buf env, int val) */ - .globl _st_md_cxt_restore - .type _st_md_cxt_restore, @function - .align 16 - _st_md_cxt_restore: - /* - * Restore registers. - */ - movq (JB_RBX*8)(%rdi), %rbx - movq (JB_RBP*8)(%rdi), %rbp - movq (JB_R12*8)(%rdi), %r12 - movq (JB_R13*8)(%rdi), %r13 - movq (JB_R14*8)(%rdi), %r14 - movq (JB_R15*8)(%rdi), %r15 - /* Set return value */ - test %esi, %esi - mov $01, %eax - cmove %eax, %esi - mov %esi, %eax - movq (JB_PC*8)(%rdi), %rdx - movq (JB_RSP*8)(%rdi), %rsp - /* Jump to saved PC */ - jmpq *%rdx - .size _st_md_cxt_restore, .-_st_md_cxt_restore - - /****************************************************************/ - - - - - - - - - - -#elif defined(__aarch64__) - - /****************************************************************/ - /* https://github.com/ossrs/srs/issues/1282#issuecomment-445539513 */ - - #define JB_X19 0 - #define JB_X20 1 - #define JB_X21 2 - #define JB_X22 3 - #define JB_X23 4 - #define JB_X24 5 - #define JB_X25 6 - #define JB_X26 7 - #define JB_X27 8 - #define JB_X28 9 - #define JB_X29 10 - #define JB_LR 11 - #define JB_SP 13 - - #define JB_D8 14 - #define JB_D9 15 - #define JB_D10 16 - #define JB_D11 17 - #define JB_D12 18 - #define JB_D13 19 - #define JB_D14 20 - #define JB_D15 21 - - .file "md.S" - .text - - /* _st_md_cxt_save(__jmp_buf env) */ - .globl _st_md_cxt_save - .type _st_md_cxt_save, %function - .align 4 - _st_md_cxt_save: - stp x19, x20, [x0, #JB_X19<<3] - stp x21, x22, [x0, #JB_X21<<3] - stp x23, x24, [x0, #JB_X23<<3] - stp x25, x26, [x0, #JB_X25<<3] - stp x27, x28, [x0, #JB_X27<<3] - stp x29, x30, [x0, #JB_X29<<3] - - stp d8, d9, [x0, #JB_D8<<3] - stp d10, d11, [x0, #JB_D10<<3] - stp d12, d13, [x0, #JB_D12<<3] - stp d14, d15, [x0, #JB_D14<<3] - mov x2, sp - str x2, [x0, #JB_SP<<3] - - mov x0, #0 - ret - .size _st_md_cxt_save, .-_st_md_cxt_save - - /****************************************************************/ - - /* _st_md_cxt_restore(__jmp_buf env, int val) */ - .globl _st_md_cxt_restore - .type _st_md_cxt_restore, %function - .align 4 - _st_md_cxt_restore: - ldp x19, x20, [x0, #JB_X19<<3] - ldp x21, x22, [x0, #JB_X21<<3] - ldp x23, x24, [x0, #JB_X23<<3] - ldp x25, x26, [x0, #JB_X25<<3] - ldp x27, x28, [x0, #JB_X27<<3] - - ldp x29, x30, [x0, #JB_X29<<3] - - ldp d8, d9, [x0, #JB_D8<<3] - ldp d10, d11, [x0, #JB_D10<<3] - ldp d12, d13, [x0, #JB_D12<<3] - ldp d14, d15, [x0, #JB_D14<<3] - - ldr x5, [x0, #JB_SP<<3] - mov sp, x5 - - cmp x1, #0 - mov x0, #1 - csel x0, x1, x0, ne - /* Use br instead of ret because ret is guaranteed to mispredict */ - br x30 - .size _st_md_cxt_restore, .-_st_md_cxt_restore - - /****************************************************************/ - - - - - - - - - - -#elif defined(__arm__) - - /****************************************************************/ - /* https://github.com/ossrs/srs/issues/1282#issuecomment-445539513 */ - - /* Register list for a ldm/stm instruction to load/store - the general registers from a __jmp_buf. */ - # define JMP_BUF_REGLIST {v1-v6, sl, fp, sp, lr} - - .file "md.S" - .text - - /* _st_md_cxt_save(__jmp_buf env) */ - .globl _st_md_cxt_save - .type _st_md_cxt_save, %function - .align 2 - _st_md_cxt_save: - mov ip, r0 - - /* Save registers */ - stmia ip!, JMP_BUF_REGLIST - - #ifdef __VFP_FP__ - /* Store the VFP registers. */ - /* Following instruction is vstmia ip!, {d8-d15}. */ - stc p11, cr8, [ip], #64 - #endif - - #ifdef __IWMMXT__ - /* Save the call-preserved iWMMXt registers. */ - /* Following instructions are wstrd wr10, [ip], #8 (etc.) */ - stcl p1, cr10, [r12], #8 - stcl p1, cr11, [r12], #8 - stcl p1, cr12, [r12], #8 - stcl p1, cr13, [r12], #8 - stcl p1, cr14, [r12], #8 - stcl p1, cr15, [r12], #8 - #endif - - mov r0, #0 - bx lr - - .size _st_md_cxt_save, .-_st_md_cxt_save - - /****************************************************************/ - - /* _st_md_cxt_restore(__jmp_buf env, int val) */ - .globl _st_md_cxt_restore - .type _st_md_cxt_restore, %function - .align 2 - _st_md_cxt_restore: - mov ip, r0 - - /* Restore registers */ - ldmia ip!, JMP_BUF_REGLIST - - #ifdef __VFP_FP__ - /* Restore the VFP registers. */ - /* Following instruction is vldmia ip!, {d8-d15}. */ - ldc p11, cr8, [r12], #64 - #endif - - #ifdef __IWMMXT__ - /* Restore the call-preserved iWMMXt registers. */ - /* Following instructions are wldrd wr10, [ip], #8 (etc.) */ - ldcl p1, cr10, [r12], #8 - ldcl p1, cr11, [r12], #8 - ldcl p1, cr12, [r12], #8 - ldcl p1, cr13, [r12], #8 - ldcl p1, cr14, [r12], #8 - ldcl p1, cr15, [r12], #8 - #endif - - movs r0, r1 /* get the return value in place */ - moveq r0, #1 /* can't let setjmp() return zero! */ - bx lr - - .size _st_md_cxt_restore, .-_st_md_cxt_restore - - /****************************************************************/ - -#endif - -#endif diff --git a/trunk/3rdparty/st-srs/md.h b/trunk/3rdparty/st-srs/md.h deleted file mode 100644 index dc4ef54c9..000000000 --- a/trunk/3rdparty/st-srs/md.h +++ /dev/null @@ -1,641 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#ifndef __ST_MD_H__ -#define __ST_MD_H__ - -#if defined(ETIMEDOUT) && !defined(ETIME) - #define ETIME ETIMEDOUT -#endif - -#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON) - #define MAP_ANON MAP_ANONYMOUS -#endif - -#ifndef MAP_FAILED - #define MAP_FAILED -1 -#endif - -/***************************************** - * Platform specifics - */ - -#if defined (AIX) - - #define MD_STACK_GROWS_DOWN - #define MD_USE_SYSV_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #ifndef MD_HAVE_SOCKLEN_T - #define MD_HAVE_SOCKLEN_T - #define socklen_t unsigned long - #endif - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[3] = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - timebasestruct_t rt; \ - (void) read_real_time(&rt, TIMEBASE_SZ); \ - (void) time_base_to_time(&rt, TIMEBASE_SZ); \ - return (rt.tb_high * 1000000LL + rt.tb_low / 1000) - -#elif defined (CYGWIN) - - #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_NOT_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #define MD_SETJMP(env) setjmp(env) - #define MD_LONGJMP(env, val) longjmp(env, val) - - #define MD_JB_SP 7 - - #define MD_GET_SP(_t) (_t)->context[MD_JB_SP] - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - MD_GET_SP(_thread) = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (DARWIN) - - #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - #define MD_HAVE_SOCKLEN_T - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #if defined(__ppc__) - #define MD_JB_SP 0 - #elif defined(__i386__) - #define MD_JB_SP 9 - #elif defined(__x86_64__) - #define MD_JB_SP 4 - #else - #error Unknown CPU architecture - #endif - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - *((long *)&((_thread)->context[MD_JB_SP])) = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (FREEBSD) - - #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #if defined(__i386__) - #define MD_JB_SP 2 - #elif defined(__alpha__) - #define MD_JB_SP 34 - #elif defined(__amd64__) - #define MD_JB_SP 2 - #else - #error Unknown CPU architecture - #endif - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[0]._jb[MD_JB_SP] = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (HPUX) - - #define MD_STACK_GROWS_UP - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #ifndef __LP64__ - /* 32-bit mode (ILP32 data model) */ - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - ((long *)((_thread)->context))[1] = (long) (_sp); \ - ST_END_MACRO - #else - /* 64-bit mode (LP64 data model) */ - #define MD_STACK_PAD_SIZE 256 - /* Last stack frame must be preserved */ - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - memcpy((char *)(_sp) - MD_STACK_PAD_SIZE, \ - ((char **)((_thread)->context))[1] - MD_STACK_PAD_SIZE, \ - MD_STACK_PAD_SIZE); \ - ((long *)((_thread)->context))[1] = (long) (_sp); \ - ST_END_MACRO - #endif /* !__LP64__ */ - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (IRIX) - - #include - - #define MD_STACK_GROWS_DOWN - #define MD_USE_SYSV_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #define MD_SETJMP(env) setjmp(env) - #define MD_LONGJMP(env, val) longjmp(env, val) - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - (void) MD_SETJMP((_thread)->context); \ - (_thread)->context[JB_SP] = (long) (_sp); \ - (_thread)->context[JB_PC] = (long) _main; \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - static int inited = 0; \ - static clockid_t clock_id = CLOCK_SGI_CYCLE; \ - struct timespec ts; \ - if (!inited) { \ - if (syssgi(SGI_CYCLECNTR_SIZE) < 64) \ - clock_id = CLOCK_REALTIME; \ - inited = 1; \ - } \ - (void) clock_gettime(clock_id, &ts); \ - return (ts.tv_sec * 1000000LL + ts.tv_nsec / 1000) - - /* - * Cap the stack by zeroing out the saved return address register - * value. This allows libexc, used by SpeedShop, to know when to stop - * backtracing since it won't find main, start, or any other known - * stack root function in a state thread's stack. Without this libexc - * traces right off the stack and crashes. - * The function preamble stores ra at 8(sp), this stores zero there. - * N.B. This macro is compiler/ABI dependent. It must change if ANY more - * automatic variables are added to the _st_thread_main() routine, because - * the address where ra is stored will change. - */ - #if !defined(__GNUC__) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32 - #define MD_CAP_STACK(var_addr) \ - (((volatile __uint64_t *)(var_addr))[1] = 0) - #endif - -#elif defined (LINUX) - - /* - * These are properties of the linux kernel and are the same on every - * flavor and architecture. - */ - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_NOT_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - /* - * Modern GNU/Linux is Posix.1g compliant. - */ - #define MD_HAVE_SOCKLEN_T - - /* - * All architectures and flavors of linux have the gettimeofday - * function but if you know of a faster way, use it. - */ - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - - #if defined(__ia64__) - #define MD_STACK_GROWS_DOWN - - /* - * IA-64 architecture. Besides traditional memory call stack, IA-64 - * uses general register stack. Thus each thread needs a backing store - * for register stack in addition to memory stack. Standard - * setjmp()/longjmp() cannot be used for thread context switching - * because their implementation implicitly assumes that only one - * register stack exists. - */ - #ifdef USE_LIBC_SETJMP - #undef USE_LIBC_SETJMP - #endif - #define MD_USE_BUILTIN_SETJMP - - #define MD_STACK_PAD_SIZE 128 - /* Last register stack frame must be preserved */ - #define MD_INIT_CONTEXT(_thread, _sp, _bsp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - memcpy((char *)(_bsp) - MD_STACK_PAD_SIZE, \ - (char *)(_thread)->context[0].__jmpbuf[17] - MD_STACK_PAD_SIZE, \ - MD_STACK_PAD_SIZE); \ - (_thread)->context[0].__jmpbuf[0] = (long) (_sp); \ - (_thread)->context[0].__jmpbuf[17] = (long) (_bsp); \ - ST_END_MACRO - - #elif defined(__mips__) - #define MD_STACK_GROWS_DOWN - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - MD_SETJMP((_thread)->context); \ - _thread->context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \ - _thread->context[0].__jmpbuf[0].__sp = _sp; \ - ST_END_MACRO - - #else /* Not IA-64 or mips */ - - /* - * On linux, there are a few styles of jmpbuf format. These vary based - * on architecture/glibc combination. - * - * Most of the glibc based toggles were lifted from: - * mozilla/nsprpub/pr/include/md/_linux.h - */ - - /* - * Starting with glibc 2.4, JB_SP definitions are not public anymore. - * They, however, can still be found in glibc source tree in - * architecture-specific "jmpbuf-offsets.h" files. - * Most importantly, the content of jmp_buf is mangled by setjmp to make - * it completely opaque (the mangling can be disabled by setting the - * LD_POINTER_GUARD environment variable before application execution). - * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore - * functions as a setjmp/longjmp replacement wherever they are available - * unless USE_LIBC_SETJMP is defined. - */ - - #if defined(__powerpc__) - #define MD_STACK_GROWS_DOWN - - #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) - #ifndef JB_GPR1 - #define JB_GPR1 0 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_GPR1] - #else - /* not an error but certainly cause for caution */ - #error "Untested use of old glibc on powerpc" - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__misc[0] - #endif /* glibc 2.1 or later */ - - #elif defined(__alpha) - #define MD_STACK_GROWS_DOWN - - #if defined(__GLIBC__) && __GLIBC__ >= 2 - #ifndef JB_SP - #define JB_SP 8 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] - #else - /* not an error but certainly cause for caution */ - #error "Untested use of old glibc on alpha" - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp - #endif - - #elif defined(__mc68000__) - #define MD_STACK_GROWS_DOWN - - /* m68k still uses old style sigjmp_buf */ - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp - - #elif defined(__sparc__) - #define MD_STACK_GROWS_DOWN - - #if defined(__GLIBC__) && __GLIBC__ >= 2 - #ifndef JB_SP - #define JB_SP 0 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] - #else - /* not an error but certainly cause for caution */ - #error "Untested use of old glic on sparc -- also using odd mozilla derived __fp" - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__fp - #endif - - #elif defined(__i386__) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BUILTIN_SETJMP - - #if defined(__GLIBC__) && __GLIBC__ >= 2 - #ifndef JB_SP - #define JB_SP 4 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] - #else - /* not an error but certainly cause for caution */ - #error "Untested use of old glibc on i386" - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp - #endif - - #elif defined(__amd64__) || defined(__x86_64__) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BUILTIN_SETJMP - - #ifndef JB_RSP - #define JB_RSP 6 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP] - - #elif defined(__aarch64__) - /* https://github.com/ossrs/state-threads/issues/9 */ - #define MD_STACK_GROWS_DOWN - #define MD_USE_BUILTIN_SETJMP - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[13] - - #elif defined(__arm__) - #define MD_STACK_GROWS_DOWN - /* https://github.com/ossrs/state-threads/issues/1#issuecomment-244648573 */ - #define MD_USE_BUILTIN_SETJMP - - /* force to use glibc solution, hack the guard jmpbuf from michaeltalyansky */ - #ifdef USE_LIBC_SETJMP - #undef MD_USE_BUILTIN_SETJMP - #endif - - #if defined(__GLIBC__) && __GLIBC__ >= 2 - /* Merge from https://github.com/michaeltalyansky/state-threads/commit/56554a5c425aee8e7a73782eae23d74d83c4120a */ - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8] - #else - #error "ARM/Linux pre-glibc2 not supported yet" - #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ - - #elif defined(__s390__) - #define MD_STACK_GROWS_DOWN - - /* There is no JB_SP in glibc at this time. (glibc 2.2.5) - */ - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__gregs[9] - - #elif defined(__hppa__) - #define MD_STACK_GROWS_UP - - /* yes, this is gross, unfortunately at the moment (2002/08/01) there is - * a bug in hppa's glibc header definition for JB_SP, so we can't - * use that... - */ - #define MD_GET_SP(_t) (*(long *)(((char *)&(_t)->context[0].__jmpbuf[0]) + 76)) - - #else - #error "Unknown CPU architecture" - #endif /* Cases with common MD_INIT_CONTEXT and different SP locations */ - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - MD_GET_SP(_thread) = (long) (_sp); \ - ST_END_MACRO - - #endif /* Cases with different MD_INIT_CONTEXT */ - - #if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP) - #define MD_SETJMP(env) _st_md_cxt_save(env) - #define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) - - extern int _st_md_cxt_save(jmp_buf env); - extern void _st_md_cxt_restore(jmp_buf env, int val); - #else - #define MD_SETJMP(env) setjmp(env) - #define MD_LONGJMP(env, val) longjmp(env, val) - #endif - -#elif defined (NETBSD) - - #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - #define MD_HAVE_SOCKLEN_T - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #if defined(__i386__) - #define MD_JB_SP 2 - #elif defined(__alpha__) - #define MD_JB_SP 34 - #elif defined(__sparc__) - #define MD_JB_SP 0 - #elif defined(__vax__) - #define MD_JB_SP 2 - #else - #error Unknown CPU architecture - #endif - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[MD_JB_SP] = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (OPENBSD) - - #define MD_STACK_GROWS_DOWN - #define MD_USE_BSD_ANON_MMAP - #define MD_ACCEPT_NB_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #if defined(__i386__) - #define MD_JB_SP 2 - #elif defined(__alpha__) - #define MD_JB_SP 34 - #elif defined(__sparc__) - #define MD_JB_SP 0 - #elif defined(__amd64__) - #define MD_JB_SP 6 - #else - #error Unknown CPU architecture - #endif - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[MD_JB_SP] = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (OSF1) - - #include - - #define MD_STACK_GROWS_DOWN - #define MD_USE_SYSV_ANON_MMAP - #define MD_ACCEPT_NB_NOT_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT - - #define MD_SETJMP(env) _setjmp(env) - #define MD_LONGJMP(env, val) _longjmp(env, val) - - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - ((struct sigcontext *)((_thread)->context))->sc_sp = (long) (_sp); \ - ST_END_MACRO - - #define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#elif defined (SOLARIS) - - #include - extern int getpagesize(void); - - #define MD_STACK_GROWS_DOWN - #define MD_USE_SYSV_ANON_MMAP - #define MD_ACCEPT_NB_NOT_INHERITED - - #define MD_SETJMP(env) setjmp(env) - #define MD_LONGJMP(env, val) longjmp(env, val) - - #if defined(sparc) || defined(__sparc) - #ifdef _LP64 - #define MD_STACK_PAD_SIZE 4095 - #endif - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - (void) MD_SETJMP((_thread)->context); \ - (_thread)->context[1] = (long) (_sp); \ - (_thread)->context[2] = (long) _main; \ - ST_END_MACRO - #elif defined(i386) || defined(__i386) - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - (void) MD_SETJMP((_thread)->context); \ - (_thread)->context[4] = (long) (_sp); \ - (_thread)->context[5] = (long) _main; \ - ST_END_MACRO - #elif defined(__amd64__) - #define MD_INIT_CONTEXT(_thread, _sp, _main) \ - ST_BEGIN_MACRO \ - if (MD_SETJMP((_thread)->context)) \ - _main(); \ - (_thread)->context[6] = (long) (_sp); \ - ST_END_MACRO - #else - #error Unknown CPU architecture - #endif - - #define MD_GET_UTIME() \ - return (gethrtime() / 1000) - -#else - #error Unknown OS -#endif /* OS */ - -#if !defined(MD_HAVE_POLL) && !defined(MD_DONT_HAVE_POLL) - #define MD_HAVE_POLL -#endif - -#ifndef MD_STACK_PAD_SIZE - #define MD_STACK_PAD_SIZE 128 -#endif - -#if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t) - #define socklen_t int -#endif - -#ifndef MD_CAP_STACK - #define MD_CAP_STACK(var_addr) -#endif - -#endif /* !__ST_MD_H__ */ - diff --git a/trunk/3rdparty/st-srs/osguess.sh b/trunk/3rdparty/st-srs/osguess.sh deleted file mode 100644 index 531681efe..000000000 --- a/trunk/3rdparty/st-srs/osguess.sh +++ /dev/null @@ -1,45 +0,0 @@ -# -# This script can be used to automatically guess target OS. -# It requires the config.guess utility which is a part of GNU Autoconf. -# GNU Autoconf can be downloaded from ftp://ftp.gnu.org/gnu/autoconf/ -# -# Use "default" as a make target for automatic builds. -# - - -# Specify path to the config.guess utility (unless set via environment) -#CONFIG_GUESS_PATH= - - -if [ x"$CONFIG_GUESS_PATH" = x ]; then - echo "Error: CONFIG_GUESS_PATH variable is not set" - exit 1 -fi - -if [ ! -f "$CONFIG_GUESS_PATH/config.guess" ]; then - echo "Can't find $CONFIG_GUESS_PATH/config.guess utility. Wrong path?" - exit 1 -fi - -sys_info=`/bin/sh $CONFIG_GUESS_PATH/config.guess` - -echo "Building for $sys_info" - -case "$sys_info" in - *-ibm-aix4* ) OS=AIX ;; - *-freebsd* ) OS=FREEBSD ;; - hppa*-hp-hpux11*) OS=HPUX ;; - *-sgi-irix6* ) OS=IRIX ;; - *-linux* ) OS=LINUX ;; - *-netbsd* ) OS=NETBSD ;; - *-openbsd* ) OS=OPENBSD ;; - *-dec-osf* ) OS=OSF1 ;; - *-solaris2* ) OS=SOLARIS ;; - *-darwin* ) OS=DARWIN ;; - * ) OS= - echo "Sorry, unsupported OS" - exit 1 ;; -esac - -echo "Making with OS=$OS" - diff --git a/trunk/3rdparty/st-srs/public.h b/trunk/3rdparty/st-srs/public.h deleted file mode 100644 index 20b09407b..000000000 --- a/trunk/3rdparty/st-srs/public.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#ifndef __ST_THREAD_H__ -#define __ST_THREAD_H__ - -#include -#include -#include -#include -#include -#include -#include - -#define ST_VERSION "1.9" -#define ST_VERSION_MAJOR 1 -#define ST_VERSION_MINOR 9 - -/* Undefine this to remove the context switch callback feature. */ -#define ST_SWITCH_CB - -#ifndef ETIME - #define ETIME ETIMEDOUT -#endif - -#ifndef ST_UTIME_NO_TIMEOUT - #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) -#endif - -#ifndef ST_UTIME_NO_WAIT - #define ST_UTIME_NO_WAIT 0 -#endif - -#define ST_EVENTSYS_DEFAULT 0 -#define ST_EVENTSYS_SELECT 1 -#define ST_EVENTSYS_POLL 2 -#define ST_EVENTSYS_ALT 3 - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned long long st_utime_t; -typedef struct _st_thread * st_thread_t; -typedef struct _st_cond * st_cond_t; -typedef struct _st_mutex * st_mutex_t; -typedef struct _st_netfd * st_netfd_t; -#ifdef ST_SWITCH_CB -typedef void (*st_switch_cb_t)(void); -#endif - -extern int st_init(void); -extern int st_getfdlimit(void); - -extern int st_set_eventsys(int eventsys); -extern int st_get_eventsys(void); -extern const char *st_get_eventsys_name(void); - -#ifdef ST_SWITCH_CB -extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb); -extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb); -#endif - -extern st_thread_t st_thread_self(void); -extern void st_thread_exit(void *retval); -extern int st_thread_join(st_thread_t thread, void **retvalp); -extern void st_thread_interrupt(st_thread_t thread); -extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stack_size); -extern int st_randomize_stacks(int on); -extern int st_set_utime_function(st_utime_t (*func)(void)); - -extern st_utime_t st_utime(void); -extern st_utime_t st_utime_last_clock(void); -extern int st_timecache_set(int on); -extern time_t st_time(void); -extern int st_usleep(st_utime_t usecs); -extern int st_sleep(int secs); -extern st_cond_t st_cond_new(void); -extern int st_cond_destroy(st_cond_t cvar); -extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); -extern int st_cond_wait(st_cond_t cvar); -extern int st_cond_signal(st_cond_t cvar); -extern int st_cond_broadcast(st_cond_t cvar); -extern st_mutex_t st_mutex_new(void); -extern int st_mutex_destroy(st_mutex_t lock); -extern int st_mutex_lock(st_mutex_t lock); -extern int st_mutex_unlock(st_mutex_t lock); -extern int st_mutex_trylock(st_mutex_t lock); - -extern int st_key_create(int *keyp, void (*destructor)(void *)); -extern int st_key_getlimit(void); -extern int st_thread_setspecific(int key, void *value); -extern void *st_thread_getspecific(int key); - -extern st_netfd_t st_netfd_open(int osfd); -extern st_netfd_t st_netfd_open_socket(int osfd); -extern void st_netfd_free(st_netfd_t fd); -extern int st_netfd_close(st_netfd_t fd); -extern int st_netfd_fileno(st_netfd_t fd); -extern void st_netfd_setspecific(st_netfd_t fd, void *value, void (*destructor)(void *)); -extern void *st_netfd_getspecific(st_netfd_t fd); -extern int st_netfd_serialize_accept(st_netfd_t fd); -extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); - -extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); -extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout); -extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout); -extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); -extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); -extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, st_utime_t timeout); -extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); -extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); -extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, st_utime_t timeout); -extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, st_utime_t timeout); -extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); -extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); -extern int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout); -extern int st_sendto(st_netfd_t fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout); -extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, st_utime_t timeout); -extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, st_utime_t timeout); -extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); - -#ifdef DEBUG -extern void _st_show_thread_stack(st_thread_t thread, const char *messg); -extern void _st_iterate_threads(void); -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* !__ST_THREAD_H__ */ - diff --git a/trunk/3rdparty/st-srs/sched.c b/trunk/3rdparty/st-srs/sched.c deleted file mode 100644 index 87515827e..000000000 --- a/trunk/3rdparty/st-srs/sched.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include -#include -#include -#include -#include "common.h" - -/* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ -#ifndef NVALGRIND -#include -#endif - - -/* Global data */ -_st_vp_t _st_this_vp; /* This VP */ -_st_thread_t *_st_this_thread; /* Current thread */ -int _st_active_count = 0; /* Active thread count */ - -time_t _st_curr_time = 0; /* Current time as returned by time(2) */ -st_utime_t _st_last_tset; /* Last time it was fetched */ - - -int st_poll(struct pollfd *pds, int npds, st_utime_t timeout) -{ - struct pollfd *pd; - struct pollfd *epd = pds + npds; - _st_pollq_t pq; - _st_thread_t *me = _ST_CURRENT_THREAD(); - int n; - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - if ((*_st_eventsys->pollset_add)(pds, npds) < 0) - return -1; - - pq.pds = pds; - pq.npds = npds; - pq.thread = me; - pq.on_ioq = 1; - _ST_ADD_IOQ(pq); - if (timeout != ST_UTIME_NO_TIMEOUT) - _ST_ADD_SLEEPQ(me, timeout); - me->state = _ST_ST_IO_WAIT; - - _ST_SWITCH_CONTEXT(me); - - n = 0; - if (pq.on_ioq) { - /* If we timed out, the pollq might still be on the ioq. Remove it */ - _ST_DEL_IOQ(pq); - (*_st_eventsys->pollset_del)(pds, npds); - } else { - /* Count the number of ready descriptors */ - for (pd = pds; pd < epd; pd++) { - if (pd->revents) - n++; - } - } - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - return n; -} - - -void _st_vp_schedule(void) -{ - _st_thread_t *thread; - - if (_ST_RUNQ.next != &_ST_RUNQ) { - /* Pull thread off of the run queue */ - thread = _ST_THREAD_PTR(_ST_RUNQ.next); - _ST_DEL_RUNQ(thread); - } else { - /* If there are no threads to run, switch to the idle thread */ - thread = _st_this_vp.idle_thread; - } - ST_ASSERT(thread->state == _ST_ST_RUNNABLE); - - /* Resume the thread */ - thread->state = _ST_ST_RUNNING; - _ST_RESTORE_CONTEXT(thread); -} - - -/* - * Initialize this Virtual Processor - */ -int st_init(void) -{ - _st_thread_t *thread; - - if (_st_active_count) { - /* Already initialized */ - return 0; - } - - /* We can ignore return value here */ - st_set_eventsys(ST_EVENTSYS_DEFAULT); - - if (_st_io_init() < 0) - return -1; - - memset(&_st_this_vp, 0, sizeof(_st_vp_t)); - - ST_INIT_CLIST(&_ST_RUNQ); - ST_INIT_CLIST(&_ST_IOQ); - ST_INIT_CLIST(&_ST_ZOMBIEQ); -#ifdef DEBUG - ST_INIT_CLIST(&_ST_THREADQ); -#endif - - if ((*_st_eventsys->init)() < 0) - return -1; - - _st_this_vp.pagesize = getpagesize(); - _st_this_vp.last_clock = st_utime(); - - /* - * Create idle thread - */ - _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, NULL, 0, 0); - if (!_st_this_vp.idle_thread) - return -1; - _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD; - _st_active_count--; - _ST_DEL_RUNQ(_st_this_vp.idle_thread); - - /* - * Initialize primordial thread - */ - thread = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) + (ST_KEYS_MAX * sizeof(void *))); - if (!thread) - return -1; - thread->private_data = (void **) (thread + 1); - thread->state = _ST_ST_RUNNING; - thread->flags = _ST_FL_PRIMORDIAL; - _ST_SET_CURRENT_THREAD(thread); - _st_active_count++; -#ifdef DEBUG - _ST_ADD_THREADQ(thread); -#endif - - return 0; -} - - -#ifdef ST_SWITCH_CB -st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb) -{ - st_switch_cb_t ocb = _st_this_vp.switch_in_cb; - _st_this_vp.switch_in_cb = cb; - return ocb; -} - -st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb) -{ - st_switch_cb_t ocb = _st_this_vp.switch_out_cb; - _st_this_vp.switch_out_cb = cb; - return ocb; -} -#endif - - -/* - * Start function for the idle thread - */ -/* ARGSUSED */ -void *_st_idle_thread_start(void *arg) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - - while (_st_active_count > 0) { - /* Idle vp till I/O is ready or the smallest timeout expired */ - _ST_VP_IDLE(); - - /* Check sleep queue for expired threads */ - _st_vp_check_clock(); - - me->state = _ST_ST_RUNNABLE; - _ST_SWITCH_CONTEXT(me); - } - - /* No more threads */ - exit(0); - - /* NOTREACHED */ - return NULL; -} - - -void st_thread_exit(void *retval) -{ - _st_thread_t *thread = _ST_CURRENT_THREAD(); - - thread->retval = retval; - _st_thread_cleanup(thread); - _st_active_count--; - if (thread->term) { - /* Put thread on the zombie queue */ - thread->state = _ST_ST_ZOMBIE; - _ST_ADD_ZOMBIEQ(thread); - - /* Notify on our termination condition variable */ - st_cond_signal(thread->term); - - /* Switch context and come back later */ - _ST_SWITCH_CONTEXT(thread); - - /* Continue the cleanup */ - st_cond_destroy(thread->term); - thread->term = NULL; - } - -#ifdef DEBUG - _ST_DEL_THREADQ(thread); -#endif - - /* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ -#ifndef NVALGRIND - if (!(thread->flags & _ST_FL_PRIMORDIAL)) { - VALGRIND_STACK_DEREGISTER(thread->stack->valgrind_stack_id); - } -#endif - - if (!(thread->flags & _ST_FL_PRIMORDIAL)) - _st_stack_free(thread->stack); - - /* Find another thread to run */ - _ST_SWITCH_CONTEXT(thread); - /* Not going to land here */ -} - - -int st_thread_join(_st_thread_t *thread, void **retvalp) -{ - _st_cond_t *term = thread->term; - - /* Can't join a non-joinable thread */ - if (term == NULL) { - errno = EINVAL; - return -1; - } - if (_ST_CURRENT_THREAD() == thread) { - errno = EDEADLK; - return -1; - } - - /* Multiple threads can't wait on the same joinable thread */ - if (term->wait_q.next != &term->wait_q) { - errno = EINVAL; - return -1; - } - - while (thread->state != _ST_ST_ZOMBIE) { - if (st_cond_timedwait(term, ST_UTIME_NO_TIMEOUT) != 0) - return -1; - } - - if (retvalp) - *retvalp = thread->retval; - - /* - * Remove target thread from the zombie queue and make it runnable. - * When it gets scheduled later, it will do the clean up. - */ - thread->state = _ST_ST_RUNNABLE; - _ST_DEL_ZOMBIEQ(thread); - _ST_ADD_RUNQ(thread); - - return 0; -} - - -void _st_thread_main(void) -{ - _st_thread_t *thread = _ST_CURRENT_THREAD(); - - /* - * Cap the stack by zeroing out the saved return address register - * value. This allows some debugging/profiling tools to know when - * to stop unwinding the stack. It's a no-op on most platforms. - */ - MD_CAP_STACK(&thread); - - /* Run thread main */ - thread->retval = (*thread->start)(thread->arg); - - /* All done, time to go away */ - st_thread_exit(thread->retval); -} - - -/* - * Insert "thread" into the timeout heap, in the position - * specified by thread->heap_index. See docs/timeout_heap.txt - * for details about the timeout heap. - */ -static _st_thread_t **heap_insert(_st_thread_t *thread) { - int target = thread->heap_index; - int s = target; - _st_thread_t **p = &_ST_SLEEPQ; - int bits = 0; - int bit; - int index = 1; - - while (s) { - s >>= 1; - bits++; - } - for (bit = bits - 2; bit >= 0; bit--) { - if (thread->due < (*p)->due) { - _st_thread_t *t = *p; - thread->left = t->left; - thread->right = t->right; - *p = thread; - thread->heap_index = index; - thread = t; - } - index <<= 1; - if (target & (1 << bit)) { - p = &((*p)->right); - index |= 1; - } else { - p = &((*p)->left); - } - } - thread->heap_index = index; - *p = thread; - thread->left = thread->right = NULL; - return p; -} - - -/* - * Delete "thread" from the timeout heap. - */ -static void heap_delete(_st_thread_t *thread) { - _st_thread_t *t, **p; - int bits = 0; - int s, bit; - - /* First find and unlink the last heap element */ - p = &_ST_SLEEPQ; - s = _ST_SLEEPQ_SIZE; - while (s) { - s >>= 1; - bits++; - } - for (bit = bits - 2; bit >= 0; bit--) { - if (_ST_SLEEPQ_SIZE & (1 << bit)) { - p = &((*p)->right); - } else { - p = &((*p)->left); - } - } - t = *p; - *p = NULL; - --_ST_SLEEPQ_SIZE; - if (t != thread) { - /* - * Insert the unlinked last element in place of the element we are deleting - */ - t->heap_index = thread->heap_index; - p = heap_insert(t); - t = *p; - t->left = thread->left; - t->right = thread->right; - - /* - * Reestablish the heap invariant. - */ - for (;;) { - _st_thread_t *y; /* The younger child */ - int index_tmp; - if (t->left == NULL) - break; - else if (t->right == NULL) - y = t->left; - else if (t->left->due < t->right->due) - y = t->left; - else - y = t->right; - if (t->due > y->due) { - _st_thread_t *tl = y->left; - _st_thread_t *tr = y->right; - *p = y; - if (y == t->left) { - y->left = t; - y->right = t->right; - p = &y->left; - } else { - y->left = t->left; - y->right = t; - p = &y->right; - } - t->left = tl; - t->right = tr; - index_tmp = t->heap_index; - t->heap_index = y->heap_index; - y->heap_index = index_tmp; - } else { - break; - } - } - } - thread->left = thread->right = NULL; -} - - -void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout) -{ - thread->due = _ST_LAST_CLOCK + timeout; - thread->flags |= _ST_FL_ON_SLEEPQ; - thread->heap_index = ++_ST_SLEEPQ_SIZE; - heap_insert(thread); -} - - -void _st_del_sleep_q(_st_thread_t *thread) -{ - heap_delete(thread); - thread->flags &= ~_ST_FL_ON_SLEEPQ; -} - - -void _st_vp_check_clock(void) -{ - _st_thread_t *thread; - st_utime_t elapsed, now; - - now = st_utime(); - elapsed = now - _ST_LAST_CLOCK; - _ST_LAST_CLOCK = now; - - if (_st_curr_time && now - _st_last_tset > 999000) { - _st_curr_time = time(NULL); - _st_last_tset = now; - } - - while (_ST_SLEEPQ != NULL) { - thread = _ST_SLEEPQ; - ST_ASSERT(thread->flags & _ST_FL_ON_SLEEPQ); - if (thread->due > now) - break; - _ST_DEL_SLEEPQ(thread); - - /* If thread is waiting on condition variable, set the time out flag */ - if (thread->state == _ST_ST_COND_WAIT) - thread->flags |= _ST_FL_TIMEDOUT; - - /* Make thread runnable */ - ST_ASSERT(!(thread->flags & _ST_FL_IDLE_THREAD)); - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); - } -} - - -void st_thread_interrupt(_st_thread_t *thread) -{ - /* If thread is already dead */ - if (thread->state == _ST_ST_ZOMBIE) - return; - - thread->flags |= _ST_FL_INTERRUPT; - - if (thread->state == _ST_ST_RUNNING || thread->state == _ST_ST_RUNNABLE) - return; - - if (thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(thread); - - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); -} - - -/* Merge from https://github.com/michaeltalyansky/state-threads/commit/cce736426c2320ffec7c9820df49ee7a18ae638c */ -#if defined(__arm__) && !defined(MD_USE_BUILTIN_SETJMP) && __GLIBC_MINOR__ >= 19 - extern unsigned long __pointer_chk_guard; - #define PTR_MANGLE(var) \ - (var) = (__typeof (var)) ((unsigned long) (var) ^ __pointer_chk_guard) - #define PTR_DEMANGLE(var) PTR_MANGLE (var) -#endif - - -_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size) -{ - _st_thread_t *thread; - _st_stack_t *stack; - void **ptds; - char *sp; -#ifdef __ia64__ - char *bsp; -#endif - - /* Adjust stack size */ - if (stk_size == 0) - stk_size = ST_DEFAULT_STACK_SIZE; - stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE; - stack = _st_stack_new(stk_size); - if (!stack) - return NULL; - - /* Allocate thread object and per-thread data off the stack */ -#if defined (MD_STACK_GROWS_DOWN) - sp = stack->stk_top; -#ifdef __ia64__ - /* - * The stack segment is split in the middle. The upper half is used - * as backing store for the register stack which grows upward. - * The lower half is used for the traditional memory stack which - * grows downward. Both stacks start in the middle and grow outward - * from each other. - */ - sp -= (stk_size >> 1); - bsp = sp; - /* Make register stack 64-byte aligned */ - if ((unsigned long)bsp & 0x3f) - bsp = bsp + (0x40 - ((unsigned long)bsp & 0x3f)); - stack->bsp = bsp + _ST_STACK_PAD_SIZE; -#endif - sp = sp - (ST_KEYS_MAX * sizeof(void *)); - ptds = (void **) sp; - sp = sp - sizeof(_st_thread_t); - thread = (_st_thread_t *) sp; - - /* Make stack 64-byte aligned */ - if ((unsigned long)sp & 0x3f) - sp = sp - ((unsigned long)sp & 0x3f); - stack->sp = sp - _ST_STACK_PAD_SIZE; -#elif defined (MD_STACK_GROWS_UP) - sp = stack->stk_bottom; - thread = (_st_thread_t *) sp; - sp = sp + sizeof(_st_thread_t); - ptds = (void **) sp; - sp = sp + (ST_KEYS_MAX * sizeof(void *)); - - /* Make stack 64-byte aligned */ - if ((unsigned long)sp & 0x3f) - sp = sp + (0x40 - ((unsigned long)sp & 0x3f)); - stack->sp = sp + _ST_STACK_PAD_SIZE; -#else -#error Unknown OS -#endif - - memset(thread, 0, sizeof(_st_thread_t)); - memset(ptds, 0, ST_KEYS_MAX * sizeof(void *)); - - /* Initialize thread */ - thread->private_data = ptds; - thread->stack = stack; - thread->start = start; - thread->arg = arg; - -#ifndef __ia64__ - /* Merge from https://github.com/michaeltalyansky/state-threads/commit/cce736426c2320ffec7c9820df49ee7a18ae638c */ - #if defined(__arm__) && !defined(MD_USE_BUILTIN_SETJMP) && __GLIBC_MINOR__ >= 19 - volatile void * lsp = PTR_MANGLE(stack->sp); - if (_setjmp ((thread)->context)) - _st_thread_main(); - (thread)->context[0].__jmpbuf[8] = (long) (lsp); - #else - _ST_INIT_CONTEXT(thread, stack->sp, _st_thread_main); - #endif -#else - _ST_INIT_CONTEXT(thread, stack->sp, stack->bsp, _st_thread_main); -#endif - - /* If thread is joinable, allocate a termination condition variable */ - if (joinable) { - thread->term = st_cond_new(); - if (thread->term == NULL) { - _st_stack_free(thread->stack); - return NULL; - } - } - - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _st_active_count++; - _ST_ADD_RUNQ(thread); -#ifdef DEBUG - _ST_ADD_THREADQ(thread); -#endif - - /* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ -#ifndef NVALGRIND - if (!(thread->flags & _ST_FL_PRIMORDIAL)) { - thread->stack->valgrind_stack_id = VALGRIND_STACK_REGISTER(thread->stack->stk_top, thread->stack->stk_bottom); - } -#endif - - return thread; -} - - -_st_thread_t *st_thread_self(void) -{ - return _ST_CURRENT_THREAD(); -} - - -#ifdef DEBUG -/* ARGSUSED */ -void _st_show_thread_stack(_st_thread_t *thread, const char *messg) -{ - -} - -/* To be set from debugger */ -int _st_iterate_threads_flag = 0; - -void _st_iterate_threads(void) -{ - static _st_thread_t *thread = NULL; - static jmp_buf orig_jb, save_jb; - _st_clist_t *q; - - if (!_st_iterate_threads_flag) { - if (thread) { - memcpy(thread->context, save_jb, sizeof(jmp_buf)); - MD_LONGJMP(orig_jb, 1); - } - return; - } - - if (thread) { - memcpy(thread->context, save_jb, sizeof(jmp_buf)); - _st_show_thread_stack(thread, NULL); - } else { - if (MD_SETJMP(orig_jb)) { - _st_iterate_threads_flag = 0; - thread = NULL; - _st_show_thread_stack(thread, "Iteration completed"); - return; - } - thread = _ST_CURRENT_THREAD(); - _st_show_thread_stack(thread, "Iteration started"); - } - - q = thread->tlink.next; - if (q == &_ST_THREADQ) - q = q->next; - ST_ASSERT(q != &_ST_THREADQ); - thread = _ST_THREAD_THREADQ_PTR(q); - if (thread == _ST_CURRENT_THREAD()) - MD_LONGJMP(orig_jb, 1); - memcpy(save_jb, thread->context, sizeof(jmp_buf)); - MD_LONGJMP(thread->context, 1); -} -#endif /* DEBUG */ - diff --git a/trunk/3rdparty/st-srs/st.pc.in b/trunk/3rdparty/st-srs/st.pc.in deleted file mode 100644 index 46c39ec52..000000000 --- a/trunk/3rdparty/st-srs/st.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=${prefix} -libdir=${exec_prefix}/lib -includedir=${prefix}/include - -Name: libst -Description: State Thread Library -Version: @VERSION@ -Libs: -L${libdir} -lst -Cflags: -I${includedir} diff --git a/trunk/3rdparty/st-srs/st.spec b/trunk/3rdparty/st-srs/st.spec deleted file mode 100644 index 4914aa196..000000000 --- a/trunk/3rdparty/st-srs/st.spec +++ /dev/null @@ -1,79 +0,0 @@ -Summary: State Threads Library -Name: st -Version: 1.9 -Release: 1 -Copyright: MPL 1.2 or GPL 2+ -Packager: Wesley W. Terpstra -Source: http://prdownloads.sourceforge.net/state-threads/st-%{version}.tar.gz -Prefix: /usr -BuildRoot: /tmp/%{name}-%{version}-build -Group: Development/Libraries - -%description -The State Threads library has an interface similar to POSIX threads. - -However, the threads are actually all run in-process. This type of -threading allows for controlled schedualing points. It is highly useful -for designing robust and extremely scalable internet applications since -there is no resource contention and locking is generally unnecessary. - -It can be combined with traditional threading or multiple process -parallelism to take advantage of multiple processors. - -See: for further -information about how state threads improve performance. - -%package -n libst-devel -Summary: State Threads Library - Development Files -Group: Development/Libraries -Requires: libst1 - -%description -n libst-devel -Development headers and documentation for libst - -%package -n libst1 -Summary: State Threads Library - Shared Libs Major 1 -Group: System/Libraries - -%description -n libst1 -Shared libraries for running applications linked against api version 1. - -%prep -%setup -q - -%build -make CONFIG_GUESS_PATH=/usr/share/automake default-optimized - -%install -if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi - -mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig -mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/include -mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel -cp -a obj/libst.* ${RPM_BUILD_ROOT}/%{prefix}/lib -cp -a obj/st.h ${RPM_BUILD_ROOT}/%{prefix}/include -sed "s*@prefix@*%{prefix}*g" ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig/st.pc -cp -a docs/* ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/ -cp -a examples ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/ - -%post -n libst1 -/sbin/ldconfig %{prefix}/lib - -%files -n libst1 -%defattr(-,root,root) -%{prefix}/lib/lib*.so.* - -%files -n libst-devel -%defattr(-,root,root) -%{prefix}/include/* -%{prefix}/lib/lib*.a -%{prefix}/lib/lib*.so -%{prefix}/lib/pkgconfig/st.pc -%{prefix}/share/doc/libst-devel/* - -%clean -if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi - -%changelog -* Wed Dec 26 2001 Wesley W. Terpstra -- first rpms for libst-1.3.tar.gz diff --git a/trunk/3rdparty/st-srs/stk.c b/trunk/3rdparty/st-srs/stk.c deleted file mode 100644 index 3e681e595..000000000 --- a/trunk/3rdparty/st-srs/stk.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include -#include -#include -#include "common.h" - - -/* How much space to leave between the stacks, at each end */ -#define REDZONE _ST_PAGE_SIZE - -_st_clist_t _st_free_stacks = ST_INIT_STATIC_CLIST(&_st_free_stacks); -int _st_num_free_stacks = 0; -int _st_randomize_stacks = 0; - -static char *_st_new_stk_segment(int size); - -_st_stack_t *_st_stack_new(int stack_size) -{ - _st_clist_t *qp; - _st_stack_t *ts; - int extra; - - for (qp = _st_free_stacks.next; qp != &_st_free_stacks; qp = qp->next) { - ts = _ST_THREAD_STACK_PTR(qp); - if (ts->stk_size >= stack_size) { - /* Found a stack that is big enough */ - ST_REMOVE_LINK(&ts->links); - _st_num_free_stacks--; - ts->links.next = NULL; - ts->links.prev = NULL; - return ts; - } - } - - /* Make a new thread stack object. */ - if ((ts = (_st_stack_t *)calloc(1, sizeof(_st_stack_t))) == NULL) - return NULL; - extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0; - ts->vaddr_size = stack_size + 2*REDZONE + extra; - ts->vaddr = _st_new_stk_segment(ts->vaddr_size); - if (!ts->vaddr) { - free(ts); - return NULL; - } - ts->stk_size = stack_size; - ts->stk_bottom = ts->vaddr + REDZONE; - ts->stk_top = ts->stk_bottom + stack_size; - -#ifdef DEBUG - mprotect(ts->vaddr, REDZONE, PROT_NONE); - mprotect(ts->stk_top + extra, REDZONE, PROT_NONE); -#endif - - if (extra) { - long offset = (random() % extra) & ~0xf; - - ts->stk_bottom += offset; - ts->stk_top += offset; - } - - return ts; -} - - -/* - * Free the stack for the current thread - */ -void _st_stack_free(_st_stack_t *ts) -{ - if (!ts) - return; - - /* Put the stack on the free list */ - ST_APPEND_LINK(&ts->links, _st_free_stacks.prev); - _st_num_free_stacks++; -} - - -static char *_st_new_stk_segment(int size) -{ -#ifdef MALLOC_STACK - void *vaddr = malloc(size); -#else - static int zero_fd = -1; - int mmap_flags = MAP_PRIVATE; - void *vaddr; - -#if defined (MD_USE_SYSV_ANON_MMAP) - if (zero_fd < 0) { - if ((zero_fd = open("/dev/zero", O_RDWR, 0)) < 0) - return NULL; - fcntl(zero_fd, F_SETFD, FD_CLOEXEC); - } -#elif defined (MD_USE_BSD_ANON_MMAP) - mmap_flags |= MAP_ANON; -#else -#error Unknown OS -#endif - - vaddr = mmap(NULL, size, PROT_READ | PROT_WRITE, mmap_flags, zero_fd, 0); - if (vaddr == (void *)MAP_FAILED) - return NULL; - -#endif /* MALLOC_STACK */ - - return (char *)vaddr; -} - - -/* Not used */ -#if 0 -void _st_delete_stk_segment(char *vaddr, int size) -{ -#ifdef MALLOC_STACK - free(vaddr); -#else - (void) munmap(vaddr, size); -#endif -} -#endif - -int st_randomize_stacks(int on) -{ - int wason = _st_randomize_stacks; - - _st_randomize_stacks = on; - if (on) - srandom((unsigned int) st_utime()); - - return wason; -} diff --git a/trunk/3rdparty/st-srs/sync.c b/trunk/3rdparty/st-srs/sync.c deleted file mode 100644 index 907fdfac3..000000000 --- a/trunk/3rdparty/st-srs/sync.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include -#include "common.h" - - -extern time_t _st_curr_time; -extern st_utime_t _st_last_tset; -extern int _st_active_count; - -static st_utime_t (*_st_utime)(void) = NULL; - - -/***************************************** - * Time functions - */ - -st_utime_t st_utime(void) -{ - if (_st_utime == NULL) { -#ifdef MD_GET_UTIME - MD_GET_UTIME(); -#else -#error Unknown OS -#endif - } - - return (*_st_utime)(); -} - - -int st_set_utime_function(st_utime_t (*func)(void)) -{ - if (_st_active_count) { - errno = EINVAL; - return -1; - } - - _st_utime = func; - - return 0; -} - - -st_utime_t st_utime_last_clock(void) -{ - return _ST_LAST_CLOCK; -} - - -int st_timecache_set(int on) -{ - int wason = (_st_curr_time) ? 1 : 0; - - if (on) { - _st_curr_time = time(NULL); - _st_last_tset = st_utime(); - } else - _st_curr_time = 0; - - return wason; -} - - -time_t st_time(void) -{ - if (_st_curr_time) - return _st_curr_time; - - return time(NULL); -} - - -int st_usleep(st_utime_t usecs) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - if (usecs != ST_UTIME_NO_TIMEOUT) { - me->state = _ST_ST_SLEEPING; - _ST_ADD_SLEEPQ(me, usecs); - } else - me->state = _ST_ST_SUSPENDED; - - _ST_SWITCH_CONTEXT(me); - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - return 0; -} - - -int st_sleep(int secs) -{ - return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : ST_UTIME_NO_TIMEOUT); -} - - -/***************************************** - * Condition variable functions - */ - -_st_cond_t *st_cond_new(void) -{ - _st_cond_t *cvar; - - cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t)); - if (cvar) { - ST_INIT_CLIST(&cvar->wait_q); - } - - return cvar; -} - - -int st_cond_destroy(_st_cond_t *cvar) -{ - if (cvar->wait_q.next != &cvar->wait_q) { - errno = EBUSY; - return -1; - } - - free(cvar); - - return 0; -} - - -int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - int rv; - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - /* Put caller thread on the condition variable's wait queue */ - me->state = _ST_ST_COND_WAIT; - ST_APPEND_LINK(&me->wait_links, &cvar->wait_q); - - if (timeout != ST_UTIME_NO_TIMEOUT) - _ST_ADD_SLEEPQ(me, timeout); - - _ST_SWITCH_CONTEXT(me); - - ST_REMOVE_LINK(&me->wait_links); - rv = 0; - - if (me->flags & _ST_FL_TIMEDOUT) { - me->flags &= ~_ST_FL_TIMEDOUT; - errno = ETIME; - rv = -1; - } - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - rv = -1; - } - - return rv; -} - - -int st_cond_wait(_st_cond_t *cvar) -{ - return st_cond_timedwait(cvar, ST_UTIME_NO_TIMEOUT); -} - - -static int _st_cond_signal(_st_cond_t *cvar, int broadcast) -{ - _st_thread_t *thread; - _st_clist_t *q; - - for (q = cvar->wait_q.next; q != &cvar->wait_q; q = q->next) { - thread = _ST_THREAD_WAITQ_PTR(q); - if (thread->state == _ST_ST_COND_WAIT) { - if (thread->flags & _ST_FL_ON_SLEEPQ) - _ST_DEL_SLEEPQ(thread); - - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); - if (!broadcast) - break; - } - } - - return 0; -} - - -int st_cond_signal(_st_cond_t *cvar) -{ - return _st_cond_signal(cvar, 0); -} - - -int st_cond_broadcast(_st_cond_t *cvar) -{ - return _st_cond_signal(cvar, 1); -} - - -/***************************************** - * Mutex functions - */ - -_st_mutex_t *st_mutex_new(void) -{ - _st_mutex_t *lock; - - lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t)); - if (lock) { - ST_INIT_CLIST(&lock->wait_q); - lock->owner = NULL; - } - - return lock; -} - - -int st_mutex_destroy(_st_mutex_t *lock) -{ - if (lock->owner != NULL || lock->wait_q.next != &lock->wait_q) { - errno = EBUSY; - return -1; - } - - free(lock); - - return 0; -} - - -int st_mutex_lock(_st_mutex_t *lock) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - if (lock->owner == NULL) { - /* Got the mutex */ - lock->owner = me; - return 0; - } - - if (lock->owner == me) { - errno = EDEADLK; - return -1; - } - - /* Put caller thread on the mutex's wait queue */ - me->state = _ST_ST_LOCK_WAIT; - ST_APPEND_LINK(&me->wait_links, &lock->wait_q); - - _ST_SWITCH_CONTEXT(me); - - ST_REMOVE_LINK(&me->wait_links); - - if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - return 0; -} - - -int st_mutex_unlock(_st_mutex_t *lock) -{ - _st_thread_t *thread; - _st_clist_t *q; - - if (lock->owner != _ST_CURRENT_THREAD()) { - errno = EPERM; - return -1; - } - - for (q = lock->wait_q.next; q != &lock->wait_q; q = q->next) { - thread = _ST_THREAD_WAITQ_PTR(q); - if (thread->state == _ST_ST_LOCK_WAIT) { - lock->owner = thread; - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); - return 0; - } - } - - /* No threads waiting on this mutex */ - lock->owner = NULL; - - return 0; -} - - -int st_mutex_trylock(_st_mutex_t *lock) -{ - if (lock->owner != NULL) { - errno = EBUSY; - return -1; - } - - /* Got the mutex */ - lock->owner = _ST_CURRENT_THREAD(); - - return 0; -} - diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index a4c8730df..843439357 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -212,34 +212,23 @@ if [[ $OS_IS_UBUNTU = NO && $OS_IS_CENTOS = NO && $SRS_EXPORT_LIBRTMP_PROJECT = fi ##################################################################################### -# state-threads +# libco ##################################################################################### if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then - # check the cross build flag file, if flag changed, need to rebuild the st. - _ST_MAKE=linux-debug && _ST_EXTRA_CFLAGS="-DMD_HAVE_EPOLL" - if [[ $SRS_VALGRIND == YES ]]; then - _ST_EXTRA_CFLAGS="$_ST_EXTRA_CFLAGS -DMD_VALGRIND" - fi - # Pass the global extra flags. - if [[ $SRS_EXTRA_FLAGS != '' ]]; then - _ST_EXTRA_CFLAGS="$_ST_EXTRA_CFLAGS $SRS_EXTRA_FLAGS" - fi - # Patched ST from https://github.com/ossrs/state-threads/tree/srs - if [[ -f ${SRS_OBJS}/st/libst.a ]]; then - echo "The state-threads is ok."; + if [[ -f ${SRS_OBJS}/co/libcolib.a ]]; then + echo "The libco is ok."; else - echo "Building state-threads."; + echo "Building libco."; ( - rm -rf ${SRS_OBJS}/st-srs && cd ${SRS_OBJS} && - ln -sf ../3rdparty/st-srs && cd st-srs && - make clean && make ${_ST_MAKE} EXTRA_CFLAGS="${_ST_EXTRA_CFLAGS}" \ - CC=${SRS_TOOL_CC} AR=${SRS_TOOL_AR} LD=${SRS_TOOL_LD} RANDLIB=${SRS_TOOL_RANDLIB} && - cd .. && rm -f st && ln -sf st-srs/obj st + rm -rf ${SRS_OBJS}/co && cd ${SRS_OBJS} && + ln -sf ../3rdparty/libco && cd libco && + make clean && make colib && + cd .. && rm -f co && ln -sf libco/ co ) fi # check status - ret=$?; if [[ $ret -ne 0 ]]; then echo "Build state-threads failed, ret=$ret"; exit $ret; fi - if [ ! -f ${SRS_OBJS}/st/libst.a ]; then echo "Build state-threads static lib failed."; exit -1; fi + ret=$?; if [[ $ret -ne 0 ]]; then echo "Build libco failed, ret=$ret"; exit $ret; fi + if [ ! -f ${SRS_OBJS}/co/lib/libcolib.a ]; then echo "Build libco static lib failed."; exit -1; fi fi ##################################################################################### diff --git a/trunk/configure b/trunk/configure index d233f66db..e67644a20 100755 --- a/trunk/configure +++ b/trunk/configure @@ -142,12 +142,12 @@ END ##################################################################################### # Libraries, external library to build in srs, -# header(.h): add to ModuleLibIncs if need the specified library. for example, LibSTRoot -# library(.a): add to ModuleLibFiles if binary need the specifeid library. for example, LibSTfile +# header(.h): add to ModuleLibIncs if need the specified library. for example, LibCoRoot +# library(.a): add to ModuleLibFiles if binary need the specifeid library. for example, LibCofile # -# st(state-threads) the basic network library for SRS. -LibSTRoot="${SRS_OBJS_DIR}/st"; LibSTfile="${LibSTRoot}/libst.a" -if [[ $SRS_SHARED_ST == YES ]]; then LibSTfile="-lst"; fi +# libco the basic network library for SRS. +LibcoRoot="${SRS_OBJS_DIR}/co"; Libcofile="${LibcoRoot}/lib/libcolib.a -lpthread" +if [[ $SRS_SHARED_LIBCO == YES ]]; then Libcofile="-lcolib -lpthread"; fi # openssl-1.1.0e, for the RTMP complex handshake. LibSSLRoot="";LibSSLfile="" if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL == NO ]]; then @@ -233,7 +233,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="SERVICE" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) + ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_service_log" "srs_service_st" "srs_service_http_client" "srs_service_http_conn" "srs_service_rtmp_conn" "srs_service_utility" "srs_service_conn") @@ -246,7 +246,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="APP" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) + ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config" @@ -284,7 +284,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then if [[ $SRS_SRT == YES ]]; then MODULE_DEPENDS+=("SRT") fi - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) if [[ $SRS_SRT == YES ]]; then ModuleLibIncs+=("${LibSRTRoot[*]}") fi @@ -297,7 +297,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="MAIN" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) MODULE_FILES=() DEFINES="" # add each modules for main @@ -324,24 +324,24 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then done # # all depends libraries - ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile}) + ModuleLibFiles=(${Libcofile} ${LibSSLfile} ${LibGperfFile}) if [[ $SRS_SRT == YES ]]; then ModuleLibFiles+=("${LibSRTfile[*]}") fi # all depends objects MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${APP_OBJS[@]} ${SERVER_OBJS[@]}" - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) if [[ $SRS_SRT == YES ]]; then MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}" fi LINK_OPTIONS="${SrsLinkOptions}${SrsGprofLink}${SrsGperfLink}" # - # srs: srs(simple rtmp server) over st(state-threads) + # srs: srs(simple rtmp server) over co(libco) BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" . auto/apps.sh # # For modules, without the app module. MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${MAIN_OBJS[@]}" - ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile}) + ModuleLibFiles=(${Libcofile} ${LibSSLfile} ${LibGperfFile}) # for SRS_MODULE in ${SRS_MODULES[*]}; do . $SRS_MODULE/config @@ -361,11 +361,11 @@ if [ $SRS_UTEST = YES ]; then MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_protocol" "srs_utest_kernel" "srs_utest_core" "srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload" "srs_utest_mp4" "srs_utest_service" "srs_utest_app") - ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot}) + ModuleLibIncs=(${SRS_OBJS_DIR} ${LibcoRoot} ${LibSSLRoot}) if [[ $SRS_SRT == YES ]]; then ModuleLibIncs+=("${LibSRTRoot[*]}") fi - ModuleLibFiles=(${LibSTfile} ${LibSSLfile}) + ModuleLibFiles=(${Libcofile} ${LibSSLfile}) if [[ $SRS_SRT == YES ]]; then ModuleLibFiles+=("${LibSRTfile[*]}") fi @@ -413,7 +413,7 @@ help: @echo "Usage: make |||||||" @echo " help display this help menu" @echo " clean cleanup project" - @echo " server build the srs(simple rtmp server) over st(state-threads)" + @echo " server build the srs(simple rtmp server) over co(libco)" @echo " librtmp build the client publish/play library, and samples" @echo " utest build the utest for srs" @echo " install install srs to the prefix path" @@ -451,7 +451,7 @@ END else cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE} server: _prepare_dir - @echo "Build the srs(simple rtmp server) over ST(state-threads)" + @echo "Build the srs(simple rtmp server) over co(libco)" \$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} srs END diff --git a/trunk/research/st/Makefile b/trunk/research/st/Makefile deleted file mode 100755 index 0b24bb80c..000000000 --- a/trunk/research/st/Makefile +++ /dev/null @@ -1,100 +0,0 @@ -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Original Code is the Netscape Portable Runtime library. -# -# The Initial Developer of the Original Code is Netscape -# Communications Corporation. Portions created by Netscape are -# Copyright (C) 1994-2000 Netscape Communications Corporation. All -# Rights Reserved. -# -# Contributor(s): Silicon Graphics, Inc. -# -# Portions created by SGI are Copyright (C) 2000-2001 Silicon -# Graphics, Inc. All Rights Reserved. -# -# Alternatively, the contents of this file may be used under the -# terms of the GNU General Public License Version 2 or later (the -# "GPL"), in which case the provisions of the GPL are applicable -# instead of those above. If you wish to allow use of your -# version of this file only under the terms of the GPL and not to -# allow others to use your version of this file under the MPL, -# indicate your decision by deleting the provisions above and -# replace them with the notice and other provisions required by -# the GPL. If you do not delete the provisions above, a recipient -# may use your version of this file under either the MPL or the -# GPL. - -########################## -# Target dir and cc: -CC = cc -TARGETDIR = objs - -########################## -# Supported OSes: -OS = LINUX - -ifneq ($(shell test -f /usr/include/sys/epoll.h && echo yes), yes) -default: - @echo "epoll not found" - @exit 1 -endif - -EXTRA_OBJS = $(TARGETDIR)/md.o - -CFLAGS = -OTHER_FLAGS += -Wall -g -O0 -DEFINES = -D$(OS) -DDEBUG -DMD_HAVE_EPOLL -DMALLOC_STACK - -########################## -# Other possible defines: -# To use malloc(3) instead of mmap(2) for stack allocation: -# DEFINES += -DMALLOC_STACK -# -# To provision more than the default 16 thread-specific-data keys -# (but not too many!): -# DEFINES += -DST_KEYS_MAX= -# -# Note that you can also add these defines by specifying them as -# make/gmake arguments (without editing this Makefile). For example: -# -# make EXTRA_CFLAGS=-UMD_HAVE_EPOLL -# -########################## - -CFLAGS += $(DEFINES) $(OTHER_FLAGS) $(EXTRA_CFLAGS) - -OBJS = $(TARGETDIR)/sched.o \ - $(TARGETDIR)/stk.o \ - $(TARGETDIR)/sync.o \ - $(TARGETDIR)/key.o \ - $(TARGETDIR)/io.o \ - $(TARGETDIR)/event.o \ - $(TARGETDIR)/srs.o -OBJS += $(EXTRA_OBJS) -SRS = $(TARGETDIR)/srs - -linux-debug: all -all: $(TARGETDIR) $(SRS) - -$(TARGETDIR): - if [ ! -d $(TARGETDIR) ]; then mkdir $(TARGETDIR); fi - -$(SRS): $(OBJS) - $(CC) $(CFLAGS) -o $@ $(OBJS) - -$(TARGETDIR)/md.o: md.S - $(CC) $(CFLAGS) -c $< -o $@ - -$(TARGETDIR)/%.o: %.c common.h md.h Makefile - $(CC) $(CFLAGS) -c $< -o $@ - -clean: - rm -rf $(TARGETDIR) diff --git a/trunk/research/st/common.h b/trunk/research/st/common.h deleted file mode 100644 index b83e575ca..000000000 --- a/trunk/research/st/common.h +++ /dev/null @@ -1,445 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#ifndef __ST_COMMON_H__ -#define __ST_COMMON_H__ - -#include -#include -#include -#include -#include - -/* Enable assertions only if DEBUG is defined */ -#ifndef DEBUG - #define NDEBUG -#endif -#include -#define ST_ASSERT(expr) assert(expr) - -#define ST_BEGIN_MACRO { -#define ST_END_MACRO } - -#ifdef DEBUG - #define ST_HIDDEN /*nothing*/ -#else - #define ST_HIDDEN static -#endif - -#include "public.h" -#include "md.h" - -/***************************************** - * Circular linked list definitions - */ - -typedef struct _st_clist { - struct _st_clist *next; - struct _st_clist *prev; -} _st_clist_t; - -/* Insert element "_e" into the list, before "_l" */ -#define ST_INSERT_BEFORE(_e,_l) \ - ST_BEGIN_MACRO \ - (_e)->next = (_l); \ - (_e)->prev = (_l)->prev; \ - (_l)->prev->next = (_e); \ - (_l)->prev = (_e); \ - ST_END_MACRO - -/* Insert element "_e" into the list, after "_l" */ -#define ST_INSERT_AFTER(_e,_l) \ - ST_BEGIN_MACRO \ - (_e)->next = (_l)->next; \ - (_e)->prev = (_l); \ - (_l)->next->prev = (_e); \ - (_l)->next = (_e); \ - ST_END_MACRO - -/* Return the element following element "_e" */ -#define ST_NEXT_LINK(_e) ((_e)->next) - -/* Append an element "_e" to the end of the list "_l" */ -#define ST_APPEND_LINK(_e,_l) ST_INSERT_BEFORE(_e,_l) - -/* Insert an element "_e" at the head of the list "_l" */ -#define ST_INSERT_LINK(_e,_l) ST_INSERT_AFTER(_e,_l) - -/* Return the head/tail of the list */ -#define ST_LIST_HEAD(_l) (_l)->next -#define ST_LIST_TAIL(_l) (_l)->prev - -/* Remove the element "_e" from it's circular list */ -#define ST_REMOVE_LINK(_e) \ - ST_BEGIN_MACRO \ - (_e)->prev->next = (_e)->next; \ - (_e)->next->prev = (_e)->prev; \ - ST_END_MACRO - -/* Return non-zero if the given circular list "_l" is empty, */ -/* zero if the circular list is not empty */ -#define ST_CLIST_IS_EMPTY(_l) \ - ((_l)->next == (_l)) - -/* Initialize a circular list */ -#define ST_INIT_CLIST(_l) \ - ST_BEGIN_MACRO \ - (_l)->next = (_l); \ - (_l)->prev = (_l); \ - ST_END_MACRO - -#define ST_INIT_STATIC_CLIST(_l) \ - {(_l), (_l)} - - -/***************************************** - * Basic types definitions - */ - -typedef void (*_st_destructor_t)(void *); - -typedef struct _st_stack { - _st_clist_t links; - char *vaddr; /* Base of stack's allocated memory */ - int vaddr_size; /* Size of stack's allocated memory */ - int stk_size; /* Size of usable portion of the stack */ - char *stk_bottom; /* Lowest address of stack's usable portion */ - char *stk_top; /* Highest address of stack's usable portion */ - void *sp; /* Stack pointer from C's point of view */ -} _st_stack_t; - - -typedef struct _st_cond { - _st_clist_t wait_q; /* Condition variable wait queue */ -} _st_cond_t; - - -typedef struct _st_thread _st_thread_t; - -struct _st_thread { - int state; /* Thread's state */ - int flags; /* Thread's flags */ - - void *(*start)(void *arg); /* The start function of the thread */ - void *arg; /* Argument of the start function */ - void *retval; /* Return value of the start function */ - - _st_stack_t *stack; /* Info about thread's stack */ - - _st_clist_t links; /* For putting on run/sleep/zombie queue */ - _st_clist_t wait_links; /* For putting on mutex/condvar wait queue */ - #ifdef DEBUG - _st_clist_t tlink; /* For putting on thread queue */ - #endif - - st_utime_t due; /* Wakeup time when thread is sleeping */ - _st_thread_t *left; /* For putting in timeout heap */ - _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ - int heap_index; - - void **private_data; /* Per thread private data */ - - _st_cond_t *term; /* Termination condition variable for join */ - - jmp_buf context; /* Thread's context */ -}; - - -typedef struct _st_mutex { - _st_thread_t *owner; /* Current mutex owner */ - _st_clist_t wait_q; /* Mutex wait queue */ -} _st_mutex_t; - - -typedef struct _st_pollq { - _st_clist_t links; /* For putting on io queue */ - _st_thread_t *thread; /* Polling thread */ - struct pollfd *pds; /* Array of poll descriptors */ - int npds; /* Length of the array */ - int on_ioq; /* Is it on ioq? */ -} _st_pollq_t; - - -typedef struct _st_eventsys_ops { - const char *name; /* Name of this event system */ - int val; /* Type of this event system */ - int (*init)(void); /* Initialization */ - void (*dispatch)(void); /* Dispatch function */ - int (*pollset_add)(struct pollfd *, int); /* Add descriptor set */ - void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */ - int (*fd_new)(int); /* New descriptor allocated */ - int (*fd_close)(int); /* Descriptor closed */ - int (*fd_getlimit)(void); /* Descriptor hard limit */ -} _st_eventsys_t; - - -typedef struct _st_vp { - _st_thread_t *idle_thread; /* Idle thread for this vp */ - st_utime_t last_clock; /* The last time we went into vp_check_clock() */ - - _st_clist_t run_q; /* run queue for this vp */ - _st_clist_t io_q; /* io queue for this vp */ - _st_clist_t zombie_q; /* zombie queue for this vp */ - #ifdef DEBUG - _st_clist_t thread_q; /* all threads of this vp */ - #endif - int pagesize; - - _st_thread_t *sleep_q; /* sleep queue for this vp */ - int sleepq_size; /* number of threads on sleep queue */ - - #ifdef ST_SWITCH_CB - st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ - st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ - #endif -} _st_vp_t; - - -typedef struct _st_netfd { - int osfd; /* Underlying OS file descriptor */ - int inuse; /* In-use flag */ - void *private_data; /* Per descriptor private data */ - _st_destructor_t destructor; /* Private data destructor function */ - void *aux_data; /* Auxiliary data for internal use */ - struct _st_netfd *next; /* For putting on the free list */ -} _st_netfd_t; - - -/***************************************** - * Current vp, thread, and event system - */ - -extern _st_vp_t _st_this_vp; -extern _st_thread_t *_st_this_thread; -extern _st_eventsys_t *_st_eventsys; - -#define _ST_CURRENT_THREAD() (_st_this_thread) -#define _ST_SET_CURRENT_THREAD(_thread) (_st_this_thread = (_thread)) - -#define _ST_LAST_CLOCK (_st_this_vp.last_clock) - -#define _ST_RUNQ (_st_this_vp.run_q) -#define _ST_IOQ (_st_this_vp.io_q) -#define _ST_ZOMBIEQ (_st_this_vp.zombie_q) -#ifdef DEBUG - #define _ST_THREADQ (_st_this_vp.thread_q) -#endif - -#define _ST_PAGE_SIZE (_st_this_vp.pagesize) - -#define _ST_SLEEPQ (_st_this_vp.sleep_q) -#define _ST_SLEEPQ_SIZE (_st_this_vp.sleepq_size) - -#define _ST_VP_IDLE() (*_st_eventsys->dispatch)() - - -/***************************************** - * vp queues operations - */ - -#define _ST_ADD_IOQ(_pq) ST_APPEND_LINK(&_pq.links, &_ST_IOQ) -#define _ST_DEL_IOQ(_pq) ST_REMOVE_LINK(&_pq.links) - -#define _ST_ADD_RUNQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_RUNQ) -#define _ST_DEL_RUNQ(_thr) ST_REMOVE_LINK(&(_thr)->links) - -#define _ST_ADD_SLEEPQ(_thr, _timeout) _st_add_sleep_q(_thr, _timeout) -#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr) - -#define _ST_ADD_ZOMBIEQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ) -#define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links) - -#ifdef DEBUG - #define _ST_ADD_THREADQ(_thr) ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ) - #define _ST_DEL_THREADQ(_thr) ST_REMOVE_LINK(&(_thr)->tlink) -#endif - - -/***************************************** - * Thread states and flags - */ - -#define _ST_ST_RUNNING 0 -#define _ST_ST_RUNNABLE 1 -#define _ST_ST_IO_WAIT 2 -#define _ST_ST_LOCK_WAIT 3 -#define _ST_ST_COND_WAIT 4 -#define _ST_ST_SLEEPING 5 -#define _ST_ST_ZOMBIE 6 -#define _ST_ST_SUSPENDED 7 - -#define _ST_FL_PRIMORDIAL 0x01 -#define _ST_FL_IDLE_THREAD 0x02 -#define _ST_FL_ON_SLEEPQ 0x04 -#define _ST_FL_INTERRUPT 0x08 -#define _ST_FL_TIMEDOUT 0x10 - -/***************************************** - * Pointer conversion - */ - -#ifndef offsetof - #define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) -#endif - -#define _ST_THREAD_PTR(_qp) \ - ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, links))) - -#define _ST_THREAD_WAITQ_PTR(_qp) \ - ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, wait_links))) - -#define _ST_THREAD_STACK_PTR(_qp) \ - ((_st_stack_t *)((char*)(_qp) - offsetof(_st_stack_t, links))) - -#define _ST_POLLQUEUE_PTR(_qp) \ - ((_st_pollq_t *)((char *)(_qp) - offsetof(_st_pollq_t, links))) - -#ifdef DEBUG - #define _ST_THREAD_THREADQ_PTR(_qp) \ - ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink))) -#endif - - -/***************************************** - * Constants - */ - -#ifndef ST_UTIME_NO_TIMEOUT - #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) -#endif - -#define ST_DEFAULT_STACK_SIZE (64*1024) - -#ifndef ST_KEYS_MAX - #define ST_KEYS_MAX 16 -#endif - -#ifndef ST_MIN_POLLFDS_SIZE - #define ST_MIN_POLLFDS_SIZE 64 -#endif - - -/***************************************** - * Threads context switching - */ - -#ifdef DEBUG - void _st_iterate_threads(void); - #define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() -#else - #define ST_DEBUG_ITERATE_THREADS() -#endif - -#ifdef ST_SWITCH_CB - #define ST_SWITCH_OUT_CB(_thread) \ - if (_st_this_vp.switch_out_cb != NULL && \ - _thread != _st_this_vp.idle_thread && \ - _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_out_cb(); \ - } - #define ST_SWITCH_IN_CB(_thread) \ - if (_st_this_vp.switch_in_cb != NULL && \ - _thread != _st_this_vp.idle_thread && \ - _thread->state != _ST_ST_ZOMBIE) { \ - _st_this_vp.switch_in_cb(); \ - } -#else - #define ST_SWITCH_OUT_CB(_thread) - #define ST_SWITCH_IN_CB(_thread) -#endif - -/* - * Switch away from the current thread context by saving its state and - * calling the thread scheduler - */ -#define _ST_SWITCH_CONTEXT(_thread) \ - ST_BEGIN_MACRO \ - ST_SWITCH_OUT_CB(_thread); \ - if (!MD_SETJMP((_thread)->context)) { \ - _st_vp_schedule(); \ - } \ - ST_DEBUG_ITERATE_THREADS(); \ - ST_SWITCH_IN_CB(_thread); \ - ST_END_MACRO - -/* - * Restore a thread context that was saved by _ST_SWITCH_CONTEXT or - * initialized by _ST_INIT_CONTEXT - */ -#define _ST_RESTORE_CONTEXT(_thread) \ - ST_BEGIN_MACRO \ - _ST_SET_CURRENT_THREAD(_thread); \ - MD_LONGJMP((_thread)->context, 1); \ - ST_END_MACRO - -/* - * Number of bytes reserved under the stack "bottom" - */ -#define _ST_STACK_PAD_SIZE MD_STACK_PAD_SIZE - - -/***************************************** - * Forward declarations - */ - -void _st_vp_schedule(void); -void _st_vp_check_clock(void); -void *_st_idle_thread_start(void *arg); -void _st_thread_main(void); -void _st_thread_cleanup(_st_thread_t *thread); -void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout); -void _st_del_sleep_q(_st_thread_t *thread); -_st_stack_t *_st_stack_new(int stack_size); -void _st_stack_free(_st_stack_t *ts); -int _st_io_init(void); - -st_utime_t st_utime(void); -_st_cond_t *st_cond_new(void); -int st_cond_destroy(_st_cond_t *cvar); -int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout); -int st_cond_signal(_st_cond_t *cvar); -ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout); -ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout); -int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); -_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size); - -#endif /* !__ST_COMMON_H__ */ - diff --git a/trunk/research/st/event.c b/trunk/research/st/event.c deleted file mode 100644 index 5704f520a..000000000 --- a/trunk/research/st/event.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * Yahoo! Inc. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#include -#include -#include -#include -#include -#include -#include "common.h" - -#ifdef USE_POLL - #error "Not support USE_POLL" -#endif -#ifdef MD_HAVE_KQUEUE - #error "Not support MD_HAVE_KQUEUE" -#endif -#ifdef MD_HAVE_POLL - #error "Not support MD_HAVE_POLL" -#endif -#ifndef MD_HAVE_EPOLL - #error "Only support MD_HAVE_EPOLL" -#endif - -#include - -typedef struct _epoll_fd_data { - int rd_ref_cnt; - int wr_ref_cnt; - int ex_ref_cnt; - int revents; -} _epoll_fd_data_t; - -static struct _st_epolldata { - _epoll_fd_data_t *fd_data; - struct epoll_event *evtlist; - int fd_data_size; - int evtlist_size; - int evtlist_cnt; - int fd_hint; - int epfd; - pid_t pid; -} *_st_epoll_data; - -#ifndef ST_EPOLL_EVTLIST_SIZE -/* Not a limit, just a hint */ -#define ST_EPOLL_EVTLIST_SIZE 4096 -#endif - -#define _ST_EPOLL_READ_CNT(fd) (_st_epoll_data->fd_data[fd].rd_ref_cnt) -#define _ST_EPOLL_WRITE_CNT(fd) (_st_epoll_data->fd_data[fd].wr_ref_cnt) -#define _ST_EPOLL_EXCEP_CNT(fd) (_st_epoll_data->fd_data[fd].ex_ref_cnt) -#define _ST_EPOLL_REVENTS(fd) (_st_epoll_data->fd_data[fd].revents) - -#define _ST_EPOLL_READ_BIT(fd) (_ST_EPOLL_READ_CNT(fd) ? EPOLLIN : 0) -#define _ST_EPOLL_WRITE_BIT(fd) (_ST_EPOLL_WRITE_CNT(fd) ? EPOLLOUT : 0) -#define _ST_EPOLL_EXCEP_BIT(fd) (_ST_EPOLL_EXCEP_CNT(fd) ? EPOLLPRI : 0) -#define _ST_EPOLL_EVENTS(fd) \ - (_ST_EPOLL_READ_BIT(fd)|_ST_EPOLL_WRITE_BIT(fd)|_ST_EPOLL_EXCEP_BIT(fd)) - -_st_eventsys_t *_st_eventsys = NULL; - -/***************************************** - * epoll event system - */ - -ST_HIDDEN int _st_epoll_init(void) -{ - int fdlim; - int err = 0; - int rv = 0; - - _st_epoll_data = (struct _st_epolldata *) calloc(1, sizeof(*_st_epoll_data)); - if (!_st_epoll_data) { - return -1; - } - - fdlim = st_getfdlimit(); - _st_epoll_data->fd_hint = (fdlim > 0 && fdlim < ST_EPOLL_EVTLIST_SIZE) ? fdlim : ST_EPOLL_EVTLIST_SIZE; - - if ((_st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint)) < 0) { - err = errno; - rv = -1; - goto cleanup_epoll; - } - fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC); - _st_epoll_data->pid = getpid(); - - /* Allocate file descriptor data array */ - _st_epoll_data->fd_data_size = _st_epoll_data->fd_hint; - _st_epoll_data->fd_data = (_epoll_fd_data_t *)calloc(_st_epoll_data->fd_data_size, sizeof(_epoll_fd_data_t)); - if (!_st_epoll_data->fd_data) { - err = errno; - rv = -1; - goto cleanup_epoll; - } - - /* Allocate event lists */ - _st_epoll_data->evtlist_size = _st_epoll_data->fd_hint; - _st_epoll_data->evtlist = (struct epoll_event *)malloc(_st_epoll_data->evtlist_size * sizeof(struct epoll_event)); - if (!_st_epoll_data->evtlist) { - err = errno; - rv = -1; - } - - cleanup_epoll: - if (rv < 0) { - if (_st_epoll_data->epfd >= 0) { - close(_st_epoll_data->epfd); - } - free(_st_epoll_data->fd_data); - free(_st_epoll_data->evtlist); - free(_st_epoll_data); - _st_epoll_data = NULL; - errno = err; - } - - return rv; -} - -ST_HIDDEN int _st_epoll_fd_data_expand(int maxfd) -{ - _epoll_fd_data_t *ptr; - int n = _st_epoll_data->fd_data_size; - - while (maxfd >= n) { - n <<= 1; - } - - ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, n * sizeof(_epoll_fd_data_t)); - if (!ptr) { - return -1; - } - - memset(ptr + _st_epoll_data->fd_data_size, 0, (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t)); - - _st_epoll_data->fd_data = ptr; - _st_epoll_data->fd_data_size = n; - - return 0; -} - -ST_HIDDEN void _st_epoll_evtlist_expand(void) -{ - struct epoll_event *ptr; - int n = _st_epoll_data->evtlist_size; - - while (_st_epoll_data->evtlist_cnt > n) { - n <<= 1; - } - - ptr = (struct epoll_event *)realloc(_st_epoll_data->evtlist, n * sizeof(struct epoll_event)); - if (ptr) { - _st_epoll_data->evtlist = ptr; - _st_epoll_data->evtlist_size = n; - } -} - -ST_HIDDEN void _st_epoll_pollset_del(struct pollfd *pds, int npds) -{ - struct epoll_event ev; - struct pollfd *pd; - struct pollfd *epd = pds + npds; - int old_events, events, op; - - /* - * It's more or less OK if deleting fails because a descriptor - * will either be closed or deleted in dispatch function after - * it fires. - */ - for (pd = pds; pd < epd; pd++) { - old_events = _ST_EPOLL_EVENTS(pd->fd); - - if (pd->events & POLLIN) { - _ST_EPOLL_READ_CNT(pd->fd)--; - } - if (pd->events & POLLOUT) { - _ST_EPOLL_WRITE_CNT(pd->fd)--; - } - if (pd->events & POLLPRI) { - _ST_EPOLL_EXCEP_CNT(pd->fd)--; - } - - events = _ST_EPOLL_EVENTS(pd->fd); - /* - * The _ST_EPOLL_REVENTS check below is needed so we can use - * this function inside dispatch(). Outside of dispatch() - * _ST_EPOLL_REVENTS is always zero for all descriptors. - */ - if (events != old_events && _ST_EPOLL_REVENTS(pd->fd) == 0) { - op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; - ev.events = events; - ev.data.fd = pd->fd; - if (epoll_ctl(_st_epoll_data->epfd, op, pd->fd, &ev) == 0 && op == EPOLL_CTL_DEL) { - _st_epoll_data->evtlist_cnt--; - } - } - } -} - -ST_HIDDEN int _st_epoll_pollset_add(struct pollfd *pds, int npds) -{ - struct epoll_event ev; - int i, fd; - int old_events, events, op; - - /* Do as many checks as possible up front */ - for (i = 0; i < npds; i++) { - fd = pds[i].fd; - if (fd < 0 || !pds[i].events || (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) { - errno = EINVAL; - return -1; - } - if (fd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(fd) < 0) { - return -1; - } - } - - for (i = 0; i < npds; i++) { - fd = pds[i].fd; - old_events = _ST_EPOLL_EVENTS(fd); - - if (pds[i].events & POLLIN) { - _ST_EPOLL_READ_CNT(fd)++; - } - if (pds[i].events & POLLOUT) { - _ST_EPOLL_WRITE_CNT(fd)++; - } - if (pds[i].events & POLLPRI) { - _ST_EPOLL_EXCEP_CNT(fd)++; - } - - events = _ST_EPOLL_EVENTS(fd); - if (events != old_events) { - op = old_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; - ev.events = events; - ev.data.fd = fd; - if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 && (op != EPOLL_CTL_ADD || errno != EEXIST)) { - break; - } - if (op == EPOLL_CTL_ADD) { - _st_epoll_data->evtlist_cnt++; - if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size) { - _st_epoll_evtlist_expand(); - } - } - } - } - - if (i < npds) { - /* Error */ - int err = errno; - /* Unroll the state */ - _st_epoll_pollset_del(pds, i + 1); - errno = err; - return -1; - } - - return 0; -} - -ST_HIDDEN void _st_epoll_dispatch(void) -{ - st_utime_t min_timeout; - _st_clist_t *q; - _st_pollq_t *pq; - struct pollfd *pds, *epds; - struct epoll_event ev; - int timeout, nfd, i, osfd, notify; - int events, op; - short revents; - - if (_ST_SLEEPQ == NULL) { - timeout = -1; - } else { - min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); - timeout = (int) (min_timeout / 1000); - } - - if (_st_epoll_data->pid != getpid()) { - // WINLIN: remove it for bug introduced. - // @see: https://github.com/ossrs/srs/issues/193 - exit(-1); - } - - /* Check for I/O operations */ - nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, _st_epoll_data->evtlist_size, timeout); - - if (nfd > 0) { - for (i = 0; i < nfd; i++) { - osfd = _st_epoll_data->evtlist[i].data.fd; - _ST_EPOLL_REVENTS(osfd) = _st_epoll_data->evtlist[i].events; - if (_ST_EPOLL_REVENTS(osfd) & (EPOLLERR | EPOLLHUP)) { - /* Also set I/O bits on error */ - _ST_EPOLL_REVENTS(osfd) |= _ST_EPOLL_EVENTS(osfd); - } - } - - for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { - pq = _ST_POLLQUEUE_PTR(q); - notify = 0; - epds = pq->pds + pq->npds; - - for (pds = pq->pds; pds < epds; pds++) { - if (_ST_EPOLL_REVENTS(pds->fd) == 0) { - pds->revents = 0; - continue; - } - osfd = pds->fd; - events = pds->events; - revents = 0; - if ((events & POLLIN) && (_ST_EPOLL_REVENTS(osfd) & EPOLLIN)) { - revents |= POLLIN; - } - if ((events & POLLOUT) && (_ST_EPOLL_REVENTS(osfd) & EPOLLOUT)) { - revents |= POLLOUT; - } - if ((events & POLLPRI) && (_ST_EPOLL_REVENTS(osfd) & EPOLLPRI)) { - revents |= POLLPRI; - } - if (_ST_EPOLL_REVENTS(osfd) & EPOLLERR) { - revents |= POLLERR; - } - if (_ST_EPOLL_REVENTS(osfd) & EPOLLHUP) { - revents |= POLLHUP; - } - - pds->revents = revents; - if (revents) { - notify = 1; - } - } - if (notify) { - ST_REMOVE_LINK(&pq->links); - pq->on_ioq = 0; - /* - * Here we will only delete/modify descriptors that - * didn't fire (see comments in _st_epoll_pollset_del()). - */ - _st_epoll_pollset_del(pq->pds, pq->npds); - - if (pq->thread->flags & _ST_FL_ON_SLEEPQ) { - _ST_DEL_SLEEPQ(pq->thread); - } - pq->thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(pq->thread); - } - } - - for (i = 0; i < nfd; i++) { - /* Delete/modify descriptors that fired */ - osfd = _st_epoll_data->evtlist[i].data.fd; - _ST_EPOLL_REVENTS(osfd) = 0; - events = _ST_EPOLL_EVENTS(osfd); - op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; - ev.events = events; - ev.data.fd = osfd; - if (epoll_ctl(_st_epoll_data->epfd, op, osfd, &ev) == 0 && op == EPOLL_CTL_DEL) { - _st_epoll_data->evtlist_cnt--; - } - } - } -} - -ST_HIDDEN int _st_epoll_fd_new(int osfd) -{ - if (osfd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(osfd) < 0) { - return -1; - } - - return 0; -} - -ST_HIDDEN int _st_epoll_fd_close(int osfd) -{ - if (_ST_EPOLL_READ_CNT(osfd) || _ST_EPOLL_WRITE_CNT(osfd) || _ST_EPOLL_EXCEP_CNT(osfd)) { - errno = EBUSY; - return -1; - } - - return 0; -} - -ST_HIDDEN int _st_epoll_fd_getlimit(void) -{ - /* zero means no specific limit */ - return 0; -} - -/* - * Check if epoll functions are just stubs. - */ -ST_HIDDEN int _st_epoll_is_supported(void) -{ - struct epoll_event ev; - - ev.events = EPOLLIN; - ev.data.ptr = NULL; - /* Guaranteed to fail */ - epoll_ctl(-1, EPOLL_CTL_ADD, -1, &ev); - - return (errno != ENOSYS); -} - -static _st_eventsys_t _st_epoll_eventsys = { - "epoll", - ST_EVENTSYS_ALT, - _st_epoll_init, - _st_epoll_dispatch, - _st_epoll_pollset_add, - _st_epoll_pollset_del, - _st_epoll_fd_new, - _st_epoll_fd_close, - _st_epoll_fd_getlimit -}; - -/***************************************** - * Public functions - */ - -int st_set_eventsys(int eventsys) -{ - if (_st_eventsys) { - errno = EBUSY; - return -1; - } - - switch (eventsys) { - case ST_EVENTSYS_DEFAULT: - case ST_EVENTSYS_ALT: - default: - if (_st_epoll_is_supported()) { - _st_eventsys = &_st_epoll_eventsys; - break; - } - errno = EINVAL; - return -1; - } - - return 0; -} - -int st_get_eventsys(void) -{ - return _st_eventsys ? _st_eventsys->val : -1; -} - -const char *st_get_eventsys_name(void) -{ - return _st_eventsys ? _st_eventsys->name : ""; -} - diff --git a/trunk/research/st/io.c b/trunk/research/st/io.c deleted file mode 100644 index bc77dc8e1..000000000 --- a/trunk/research/st/io.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "common.h" - -#if EAGAIN != EWOULDBLOCK - #define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK)) -#else - #define _IO_NOT_READY_ERROR (errno == EAGAIN) -#endif - -#define _LOCAL_MAXIOV 16 - -/* File descriptor object free list */ -static _st_netfd_t *_st_netfd_freelist = NULL; -/* Maximum number of file descriptors that the process can open */ -static int _st_osfd_limit = -1; - -static void _st_netfd_free_aux_data(_st_netfd_t *fd); - -int _st_io_init(void) -{ - struct sigaction sigact; - struct rlimit rlim; - int fdlim; - - /* Ignore SIGPIPE */ - sigact.sa_handler = SIG_IGN; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - if (sigaction(SIGPIPE, &sigact, NULL) < 0) { - return -1; - } - - /* Set maximum number of open file descriptors */ - if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { - return -1; - } - - fdlim = (*_st_eventsys->fd_getlimit)(); - if (fdlim > 0 && rlim.rlim_max > (rlim_t) fdlim) { - rlim.rlim_max = fdlim; - } - rlim.rlim_cur = rlim.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { - return -1; - } - _st_osfd_limit = (int) rlim.rlim_max; - - return 0; -} - -int st_getfdlimit(void) -{ - return _st_osfd_limit; -} - -void st_netfd_free(_st_netfd_t *fd) -{ - if (!fd->inuse) { - return; - } - - fd->inuse = 0; - if (fd->aux_data) { - _st_netfd_free_aux_data(fd); - } - if (fd->private_data && fd->destructor) { - (*(fd->destructor))(fd->private_data); - } - fd->private_data = NULL; - fd->destructor = NULL; - fd->next = _st_netfd_freelist; - _st_netfd_freelist = fd; -} - -static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket) -{ - _st_netfd_t *fd; - int flags = 1; - - if ((*_st_eventsys->fd_new)(osfd) < 0) { - return NULL; - } - - if (_st_netfd_freelist) { - fd = _st_netfd_freelist; - _st_netfd_freelist = _st_netfd_freelist->next; - } else { - fd = calloc(1, sizeof(_st_netfd_t)); - if (!fd) { - return NULL; - } - } - - fd->osfd = osfd; - fd->inuse = 1; - fd->next = NULL; - - if (nonblock) { - /* Use just one system call */ - if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) { - return fd; - } - /* Do it the Posix way */ - if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) { - st_netfd_free(fd); - return NULL; - } - } - - return fd; -} - -_st_netfd_t *st_netfd_open(int osfd) -{ - return _st_netfd_new(osfd, 1, 0); -} - -_st_netfd_t *st_netfd_open_socket(int osfd) -{ - return _st_netfd_new(osfd, 1, 1); -} - -int st_netfd_close(_st_netfd_t *fd) -{ - if ((*_st_eventsys->fd_close)(fd->osfd) < 0) { - return -1; - } - - st_netfd_free(fd); - return close(fd->osfd); -} - -int st_netfd_fileno(_st_netfd_t *fd) -{ - return (fd->osfd); -} - -void st_netfd_setspecific(_st_netfd_t *fd, void *value, _st_destructor_t destructor) -{ - if (value != fd->private_data) { - /* Free up previously set non-NULL data value */ - if (fd->private_data && fd->destructor) { - (*(fd->destructor))(fd->private_data); - } - } - fd->private_data = value; - fd->destructor = destructor; -} - -void *st_netfd_getspecific(_st_netfd_t *fd) -{ - return (fd->private_data); -} - -/* - * Wait for I/O on a single descriptor. - */ -int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeout) -{ - struct pollfd pd; - int n; - - pd.fd = fd->osfd; - pd.events = (short) how; - pd.revents = 0; - - if ((n = st_poll(&pd, 1, timeout)) < 0) { - return -1; - } - if (n == 0) { - /* Timed out */ - errno = ETIME; - return -1; - } - if (pd.revents & POLLNVAL) { - errno = EBADF; - return -1; - } - - return 0; -} - -#ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT -/* No-op */ -int st_netfd_serialize_accept(_st_netfd_t *fd) -{ - fd->aux_data = NULL; - return 0; -} - -/* No-op */ -static void _st_netfd_free_aux_data(_st_netfd_t *fd) -{ - fd->aux_data = NULL; -} - -_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) -{ - int osfd, err; - _st_netfd_t *newfd; - - while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return NULL; - } - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) { - return NULL; - } - } - - /* On some platforms the new socket created by accept() inherits */ - /* the nonblocking attribute of the listening socket */ -#if defined (MD_ACCEPT_NB_INHERITED) - newfd = _st_netfd_new(osfd, 0, 1); -#elif defined (MD_ACCEPT_NB_NOT_INHERITED) - newfd = _st_netfd_new(osfd, 1, 1); -#else - #error Unknown OS -#endif - - if (!newfd) { - err = errno; - close(osfd); - errno = err; - } - - return newfd; -} - -#else /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ -/* - * On some platforms accept() calls from different processes - * on the same listen socket must be serialized. - * The following code serializes accept()'s without process blocking. - * A pipe is used as an inter-process semaphore. - */ -int st_netfd_serialize_accept(_st_netfd_t *fd) -{ - _st_netfd_t **p; - int osfd[2], err; - - if (fd->aux_data) { - errno = EINVAL; - return -1; - } - if ((p = (_st_netfd_t **)calloc(2, sizeof(_st_netfd_t *))) == NULL) { - return -1; - } - if (pipe(osfd) < 0) { - free(p); - return -1; - } - if ((p[0] = st_netfd_open(osfd[0])) != NULL && (p[1] = st_netfd_open(osfd[1])) != NULL && write(osfd[1], " ", 1) == 1) { - fd->aux_data = p; - return 0; - } - /* Error */ - err = errno; - if (p[0]) { - st_netfd_free(p[0]); - } - if (p[1]) { - st_netfd_free(p[1]); - } - close(osfd[0]); - close(osfd[1]); - free(p); - errno = err; - - return -1; -} - -static void _st_netfd_free_aux_data(_st_netfd_t *fd) -{ - _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; - - st_netfd_close(p[0]); - st_netfd_close(p[1]); - free(p); - fd->aux_data = NULL; -} - -_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) -{ - int osfd, err; - _st_netfd_t *newfd; - _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; - ssize_t n; - char c; - - for ( ; ; ) { - if (p == NULL) { - osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); - } else { - /* Get the lock */ - n = st_read(p[0], &c, 1, timeout); - if (n < 0) { - return NULL; - } - ST_ASSERT(n == 1); - /* Got the lock */ - osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); - /* Unlock */ - err = errno; - n = st_write(p[1], &c, 1, timeout); - ST_ASSERT(n == 1); - errno = err; - } - if (osfd >= 0) { - break; - } - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return NULL; - } - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) { - return NULL; - } - } - - /* On some platforms the new socket created by accept() inherits */ - /* the nonblocking attribute of the listening socket */ -#if defined (MD_ACCEPT_NB_INHERITED) - newfd = _st_netfd_new(osfd, 0, 1); -#elif defined (MD_ACCEPT_NB_NOT_INHERITED) - newfd = _st_netfd_new(osfd, 1, 1); -#else - #error Unknown OS -#endif - - if (!newfd) { - err = errno; - close(osfd); - errno = err; - } - - return newfd; -} -#endif /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ - -int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout) -{ - int n, err = 0; - - while (connect(fd->osfd, addr, addrlen) < 0) { - if (errno != EINTR) { - /* - * On some platforms, if connect() is interrupted (errno == EINTR) - * after the kernel binds the socket, a subsequent connect() - * attempt will fail with errno == EADDRINUSE. Ignore EADDRINUSE - * iff connect() was previously interrupted. See Rich Stevens' - * "UNIX Network Programming," Vol. 1, 2nd edition, p. 413 - * ("Interrupted connect"). - */ - if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) { - return -1; - } - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { - return -1; - } - /* Try to find out whether the connection setup succeeded or failed */ - n = sizeof(int); - if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, (socklen_t *)&n) < 0) { - return -1; - } - if (err) { - errno = err; - return -1; - } - break; - } - err = 1; - } - - return 0; -} - -ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) -{ - ssize_t n; - - while ((n = read(fd->osfd, buf, nbyte)) < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return -1; - } - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) { - return -1; - } - } - - return n; -} - -int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout) -{ - struct iovec iov, *riov; - int riov_size, rv; - - iov.iov_base = buf; - iov.iov_len = *resid; - riov = &iov; - riov_size = 1; - rv = st_readv_resid(fd, &riov, &riov_size, timeout); - *resid = iov.iov_len; - return rv; -} - -ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) -{ - ssize_t n; - - while ((n = readv(fd->osfd, iov, iov_size)) < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return -1; - } - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) { - return -1; - } - } - - return n; -} - -int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) -{ - ssize_t n; - - while (*iov_size > 0) { - if (*iov_size == 1) { - n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); - } else { - n = readv(fd->osfd, *iov, *iov_size); - } - if (n < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return -1; - } - } else if (n == 0) { - break; - } else { - while ((size_t) n >= (*iov)->iov_len) { - n -= (*iov)->iov_len; - (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; - (*iov)->iov_len = 0; - (*iov)++; - (*iov_size)--; - if (n == 0) { - break; - } - } - if (*iov_size == 0) { - break; - } - (*iov)->iov_base = (char *) (*iov)->iov_base + n; - (*iov)->iov_len -= n; - } - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) { - return -1; - } - } - - return 0; -} - -ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) -{ - size_t resid = nbyte; - return st_read_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1; -} - -int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, st_utime_t timeout) -{ - struct iovec iov, *riov; - int riov_size, rv; - - iov.iov_base = (void *) buf; /* we promise not to modify buf */ - iov.iov_len = *resid; - riov = &iov; - riov_size = 1; - rv = st_writev_resid(fd, &riov, &riov_size, timeout); - *resid = iov.iov_len; - return rv; -} - -ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout) -{ - size_t resid = nbyte; - return st_write_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1; -} - -ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) -{ - ssize_t n, rv; - size_t nleft, nbyte; - int index, iov_cnt; - struct iovec *tmp_iov; - struct iovec local_iov[_LOCAL_MAXIOV]; - - /* Calculate the total number of bytes to be sent */ - nbyte = 0; - for (index = 0; index < iov_size; index++) { - nbyte += iov[index].iov_len; - } - - rv = (ssize_t)nbyte; - nleft = nbyte; - tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ - iov_cnt = iov_size; - - while (nleft > 0) { - if (iov_cnt == 1) { - if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) { - rv = -1; - } - break; - } - if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - rv = -1; - break; - } - } else { - if ((size_t) n == nleft) { - break; - } - nleft -= n; - /* Find the next unwritten vector */ - n = (ssize_t)(nbyte - nleft); - for (index = 0; (size_t) n >= iov[index].iov_len; index++) { - n -= iov[index].iov_len; - } - - if (tmp_iov == iov) { - /* Must copy iov's around */ - if (iov_size - index <= _LOCAL_MAXIOV) { - tmp_iov = local_iov; - } else { - tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec)); - if (tmp_iov == NULL) { - return -1; - } - } - } - - /* Fill in the first partial read */ - tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[n]); - tmp_iov[0].iov_len = iov[index].iov_len - n; - index++; - /* Copy the remaining vectors */ - for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) { - tmp_iov[iov_cnt].iov_base = iov[index].iov_base; - tmp_iov[iov_cnt].iov_len = iov[index].iov_len; - } - } - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { - rv = -1; - break; - } - } - - if (tmp_iov != iov && tmp_iov != local_iov) { - free(tmp_iov); - } - - return rv; -} - -int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) -{ - ssize_t n; - - while (*iov_size > 0) { - if (*iov_size == 1) { - n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); - } else { - n = writev(fd->osfd, *iov, *iov_size); - } - if (n < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return -1; - } - } else { - while ((size_t) n >= (*iov)->iov_len) { - n -= (*iov)->iov_len; - (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; - (*iov)->iov_len = 0; - (*iov)++; - (*iov_size)--; - if (n == 0) { - break; - } - } - if (*iov_size == 0) { - break; - } - (*iov)->iov_base = (char *) (*iov)->iov_base + n; - (*iov)->iov_len -= n; - } - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { - return -1; - } - } - - return 0; -} - -/* - * Simple I/O functions for UDP. - */ -int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout) -{ - int n; - - while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return -1; - } - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) { - return -1; - } - } - - return n; -} - -int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout) -{ - int n; - - while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return -1; - } - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { - return -1; - } - } - - return n; -} - -int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeout) -{ - int n; - - while ((n = recvmsg(fd->osfd, msg, flags)) < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return -1; - } - /* Wait until the socket becomes readable */ - if (st_netfd_poll(fd, POLLIN, timeout) < 0) { - return -1; - } - } - - return n; -} - -int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, st_utime_t timeout) -{ - int n; - - while ((n = sendmsg(fd->osfd, msg, flags)) < 0) { - if (errno == EINTR) { - continue; - } - if (!_IO_NOT_READY_ERROR) { - return -1; - } - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { - return -1; - } - } - - return n; -} - -/* - * To open FIFOs or other special files. - */ -_st_netfd_t *st_open(const char *path, int oflags, mode_t mode) -{ - int osfd, err; - _st_netfd_t *newfd; - - while ((osfd = open(path, oflags | O_NONBLOCK, mode)) < 0) { - if (errno != EINTR) { - return NULL; - } - } - - newfd = _st_netfd_new(osfd, 0, 0); - if (!newfd) { - err = errno; - close(osfd); - errno = err; - } - - return newfd; -} - diff --git a/trunk/research/st/key.c b/trunk/research/st/key.c deleted file mode 100644 index 49d778a18..000000000 --- a/trunk/research/st/key.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include "common.h" - -/* - * Destructor table for per-thread private data - */ -static _st_destructor_t _st_destructors[ST_KEYS_MAX]; -static int key_max = 0; - -/* - * Return a key to be used for thread specific data - */ -int st_key_create(int *keyp, _st_destructor_t destructor) -{ - if (key_max >= ST_KEYS_MAX) { - errno = EAGAIN; - return -1; - } - - *keyp = key_max++; - _st_destructors[*keyp] = destructor; - - return 0; -} - -int st_key_getlimit(void) -{ - return ST_KEYS_MAX; -} - -int st_thread_setspecific(int key, void *value) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - - if (key < 0 || key >= key_max) { - errno = EINVAL; - return -1; - } - - if (value != me->private_data[key]) { - /* free up previously set non-NULL data value */ - if (me->private_data[key] && _st_destructors[key]) { - (*_st_destructors[key])(me->private_data[key]); - } - me->private_data[key] = value; - } - - return 0; -} - -void *st_thread_getspecific(int key) -{ - if (key < 0 || key >= key_max) { - return NULL; - } - - return ((_ST_CURRENT_THREAD())->private_data[key]); -} - -/* - * Free up all per-thread private data - */ -void _st_thread_cleanup(_st_thread_t *thread) -{ - int key; - - for (key = 0; key < key_max; key++) { - if (thread->private_data[key] && _st_destructors[key]) { - (*_st_destructors[key])(thread->private_data[key]); - thread->private_data[key] = NULL; - } - } -} - diff --git a/trunk/research/st/md.S b/trunk/research/st/md.S deleted file mode 100755 index 883da302b..000000000 --- a/trunk/research/st/md.S +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. - * All Rights Reserved. - */ - -/****************************************************************/ - -#if defined(__i386__) - -/* - * Internal __jmp_buf layout - */ -#define JB_BX 0 -#define JB_SI 1 -#define JB_DI 2 -#define JB_BP 3 -#define JB_SP 4 -#define JB_PC 5 - - .file "md.S" - .text - - /* _st_md_cxt_save(__jmp_buf env) */ -.globl _st_md_cxt_save - .type _st_md_cxt_save, @function - .align 16 -_st_md_cxt_save: - movl 4(%esp), %eax - - /* - * Save registers. - */ - movl %ebx, (JB_BX*4)(%eax) - movl %esi, (JB_SI*4)(%eax) - movl %edi, (JB_DI*4)(%eax) - /* Save SP */ - leal 4(%esp), %ecx - movl %ecx, (JB_SP*4)(%eax) - /* Save PC we are returning to */ - movl 0(%esp), %ecx - movl %ecx, (JB_PC*4)(%eax) - /* Save caller frame pointer */ - movl %ebp, (JB_BP*4)(%eax) - xorl %eax, %eax - ret - .size _st_md_cxt_save, .-_st_md_cxt_save - - -/****************************************************************/ - - /* _st_md_cxt_restore(__jmp_buf env, int val) */ -.globl _st_md_cxt_restore - .type _st_md_cxt_restore, @function - .align 16 -_st_md_cxt_restore: - /* First argument is jmp_buf */ - movl 4(%esp), %ecx - /* Second argument is return value */ - movl 8(%esp), %eax - /* Set the return address */ - movl (JB_PC*4)(%ecx), %edx - /* - * Restore registers. - */ - movl (JB_BX*4)(%ecx), %ebx - movl (JB_SI*4)(%ecx), %esi - movl (JB_DI*4)(%ecx), %edi - movl (JB_BP*4)(%ecx), %ebp - movl (JB_SP*4)(%ecx), %esp - testl %eax, %eax - jnz 1f - incl %eax - /* Jump to saved PC */ -1: jmp *%edx - .size _st_md_cxt_restore, .-_st_md_cxt_restore - -/****************************************************************/ - -#elif defined(__amd64__) || defined(__x86_64__) - -/* - * Internal __jmp_buf layout - */ -#define JB_RBX 0 -#define JB_RBP 1 -#define JB_R12 2 -#define JB_R13 3 -#define JB_R14 4 -#define JB_R15 5 -#define JB_RSP 6 -#define JB_PC 7 - - .file "md.S" - .text - - /* _st_md_cxt_save(__jmp_buf env) */ -.globl _st_md_cxt_save - .type _st_md_cxt_save, @function - .align 16 -_st_md_cxt_save: - /* - * Save registers. - */ - movq %rbx, (JB_RBX*8)(%rdi) - movq %rbp, (JB_RBP*8)(%rdi) - movq %r12, (JB_R12*8)(%rdi) - movq %r13, (JB_R13*8)(%rdi) - movq %r14, (JB_R14*8)(%rdi) - movq %r15, (JB_R15*8)(%rdi) - /* Save SP */ - leaq 8(%rsp), %rdx - movq %rdx, (JB_RSP*8)(%rdi) - /* Save PC we are returning to */ - movq (%rsp), %rax - movq %rax, (JB_PC*8)(%rdi) - xorq %rax, %rax - ret - .size _st_md_cxt_save, .-_st_md_cxt_save - - -/****************************************************************/ - - /* _st_md_cxt_restore(__jmp_buf env, int val) */ -.globl _st_md_cxt_restore - .type _st_md_cxt_restore, @function - .align 16 -_st_md_cxt_restore: - /* - * Restore registers. - */ - movq (JB_RBX*8)(%rdi), %rbx - movq (JB_RBP*8)(%rdi), %rbp - movq (JB_R12*8)(%rdi), %r12 - movq (JB_R13*8)(%rdi), %r13 - movq (JB_R14*8)(%rdi), %r14 - movq (JB_R15*8)(%rdi), %r15 - /* Set return value */ - test %esi, %esi - mov $01, %eax - cmove %eax, %esi - mov %esi, %eax - movq (JB_PC*8)(%rdi), %rdx - movq (JB_RSP*8)(%rdi), %rsp - /* Jump to saved PC */ - jmpq *%rdx - .size _st_md_cxt_restore, .-_st_md_cxt_restore - -/****************************************************************/ - -#endif - diff --git a/trunk/research/st/md.h b/trunk/research/st/md.h deleted file mode 100644 index bf82f4d55..000000000 --- a/trunk/research/st/md.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#ifndef __ST_MD_H__ -#define __ST_MD_H__ - -#if defined(ETIMEDOUT) && !defined(ETIME) - #define ETIME ETIMEDOUT -#endif - -#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON) - #define MAP_ANON MAP_ANONYMOUS -#endif - -#ifndef MAP_FAILED - #define MAP_FAILED -1 -#endif - -/***************************************** - * Platform specifics - */ -#if defined (LINUX) - /* linux ok, defined bellow */ -#elif defined (AIX) - #error "AIX not supported" -#elif defined (CYGWIN) - #error "CYGWIN not supported" -#elif defined (DARWIN) - #error "DARWIN not supported" -#elif defined (FREEBSD) - #error "FREEBSD not supported" -#elif defined (HPUX) - #error "HPUX not supported" -#elif defined (IRIX) - #error "IRIX not supported" -#elif defined (NETBSD) - #error "NETBSD not supported" -#elif defined (OPENBSD) - #error "OPENBSD not supported" -#elif defined (OSF1) - #error "OSF1 not supported" -#elif defined (SOLARIS) - #error "SOLARIS not supported" -#else - #error "Unknown OS" -#endif /* OS */ - -/* linux only, defined bellow */ -/* - * These are properties of the linux kernel and are the same on every - * flavor and architecture. - */ -#define MD_USE_BSD_ANON_MMAP -#define MD_ACCEPT_NB_NOT_INHERITED -#define MD_ALWAYS_UNSERIALIZED_ACCEPT -/* - * Modern GNU/Linux is Posix.1g compliant. - */ -#define MD_HAVE_SOCKLEN_T - -/* - * All architectures and flavors of linux have the gettimeofday - * function but if you know of a faster way, use it. - */ -#define MD_GET_UTIME() \ - struct timeval tv; \ - (void) gettimeofday(&tv, NULL); \ - return (tv.tv_sec * 1000000LL + tv.tv_usec) - -#if defined(__mips__) - #define MD_STACK_GROWS_DOWN -#else /* Not or mips */ - /* - * On linux, there are a few styles of jmpbuf format. These vary based - * on architecture/glibc combination. - * - * Most of the glibc based toggles were lifted from: - * mozilla/nsprpub/pr/include/md/_linux.h - */ - /* - * Starting with glibc 2.4, JB_SP definitions are not public anymore. - * They, however, can still be found in glibc source tree in - * architecture-specific "jmpbuf-offsets.h" files. - * Most importantly, the content of jmp_buf is mangled by setjmp to make - * it completely opaque (the mangling can be disabled by setting the - * LD_POINTER_GUARD environment variable before application execution). - * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore - * functions as a setjmp/longjmp replacement wherever they are available - * unless USE_LIBC_SETJMP is defined. - */ - #if defined(__i386__) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BUILTIN_SETJMP - - #if defined(__GLIBC__) && __GLIBC__ >= 2 - #ifndef JB_SP - #define JB_SP 4 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] - #else - /* not an error but certainly cause for caution */ - #error "Untested use of old glibc on i386" - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp - #endif - #elif defined(__amd64__) || defined(__x86_64__) - #define MD_STACK_GROWS_DOWN - #define MD_USE_BUILTIN_SETJMP - - #ifndef JB_RSP - #define JB_RSP 6 - #endif - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP] - #elif defined(__arm__) - #define MD_STACK_GROWS_DOWN - - #if defined(__GLIBC__) && __GLIBC__ >= 2 - #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8] - #else - #error "ARM/Linux pre-glibc2 not supported yet" - #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ - #else - #error "Unknown CPU architecture" - #endif /* Cases with common MD_INIT_CONTEXT and different SP locations */ -#endif /* Cases with different MD_INIT_CONTEXT */ - -#if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP) - /* i386/x86_64 */ - #define MD_SETJMP(env) _st_md_cxt_save(env) - #define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) - - extern int _st_md_cxt_save(jmp_buf env); - extern void _st_md_cxt_restore(jmp_buf env, int val); -#else - /* arm/mips */ - #define MD_SETJMP(env) setjmp(env) - #define MD_LONGJMP(env, val) longjmp(env, val) -#endif - -/***************************************** - * Other defines - */ -#ifndef MD_STACK_PAD_SIZE - #define MD_STACK_PAD_SIZE 128 -#endif - -#if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t) - #define socklen_t int -#endif - -#ifndef MD_CAP_STACK - #define MD_CAP_STACK(var_addr) -#endif - -#endif /* !__ST_MD_H__ */ - diff --git a/trunk/research/st/public.h b/trunk/research/st/public.h deleted file mode 100644 index 3275191bc..000000000 --- a/trunk/research/st/public.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -#ifndef __ST_THREAD_H__ -#define __ST_THREAD_H__ - -#include -#include -#include -#include -#include -#include -#include - -#define ST_VERSION "1.9" -#define ST_VERSION_MAJOR 1 -#define ST_VERSION_MINOR 9 - -/* Undefine this to remove the context switch callback feature. */ -#define ST_SWITCH_CB - -#ifndef ETIME - #define ETIME ETIMEDOUT -#endif - -#ifndef ST_UTIME_NO_TIMEOUT - #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) -#endif - -#ifndef ST_UTIME_NO_WAIT - #define ST_UTIME_NO_WAIT 0 -#endif - -#define ST_EVENTSYS_DEFAULT 0 -#define ST_EVENTSYS_SELECT 1 -#define ST_EVENTSYS_POLL 2 -#define ST_EVENTSYS_ALT 3 - -#ifdef __cplusplus -extern "C" { -#endif - typedef unsigned long long st_utime_t; - typedef struct _st_thread * st_thread_t; - typedef struct _st_cond * st_cond_t; - typedef struct _st_mutex * st_mutex_t; - typedef struct _st_netfd * st_netfd_t; - #ifdef ST_SWITCH_CB - typedef void (*st_switch_cb_t)(void); - #endif - - extern int st_init(void); - extern int st_getfdlimit(void); - - extern int st_set_eventsys(int eventsys); - extern int st_get_eventsys(void); - extern const char *st_get_eventsys_name(void); - - #ifdef ST_SWITCH_CB - extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb); - extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb); - #endif - - extern st_thread_t st_thread_self(void); - extern void st_thread_exit(void *retval); - extern int st_thread_join(st_thread_t trd, void **retvalp); - extern void st_thread_interrupt(st_thread_t trd); - extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stack_size); - extern int st_randomize_stacks(int on); - extern int st_set_utime_function(st_utime_t (*func)(void)); - - extern st_utime_t st_utime(void); - extern st_utime_t st_utime_last_clock(void); - extern int st_timecache_set(int on); - extern time_t st_time(void); - extern int st_usleep(st_utime_t usecs); - extern int st_sleep(int secs); - extern st_cond_t st_cond_new(void); - extern int st_cond_destroy(st_cond_t cvar); - extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); - extern int st_cond_wait(st_cond_t cvar); - extern int st_cond_signal(st_cond_t cvar); - extern int st_cond_broadcast(st_cond_t cvar); - extern st_mutex_t st_mutex_new(void); - extern int st_mutex_destroy(st_mutex_t lock); - extern int st_mutex_lock(st_mutex_t lock); - extern int st_mutex_unlock(st_mutex_t lock); - extern int st_mutex_trylock(st_mutex_t lock); - - extern int st_key_create(int *keyp, void (*destructor)(void *)); - extern int st_key_getlimit(void); - extern int st_thread_setspecific(int key, void *value); - extern void *st_thread_getspecific(int key); - - extern st_netfd_t st_netfd_open(int osfd); - extern st_netfd_t st_netfd_open_socket(int osfd); - extern void st_netfd_free(st_netfd_t fd); - extern int st_netfd_close(st_netfd_t fd); - extern int st_netfd_fileno(st_netfd_t fd); - extern void st_netfd_setspecific(st_netfd_t fd, void *value, void (*destructor)(void *)); - extern void *st_netfd_getspecific(st_netfd_t fd); - extern int st_netfd_serialize_accept(st_netfd_t fd); - extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); - - extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); - extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout); - extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout); - extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); - extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); - extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, st_utime_t timeout); - extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); - extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); - extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, st_utime_t timeout); - extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, st_utime_t timeout); - extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); - extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); - extern int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout); - extern int st_sendto(st_netfd_t fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout); - extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, st_utime_t timeout); - extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, st_utime_t timeout); - extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); - - #ifdef DEBUG - extern void _st_show_thread_stack(st_thread_t thread, const char *messg); - extern void _st_iterate_threads(void); - #endif -#ifdef __cplusplus -} -#endif - -#endif /* !__ST_THREAD_H__ */ - diff --git a/trunk/research/st/sched.c b/trunk/research/st/sched.c deleted file mode 100755 index 66095cfd1..000000000 --- a/trunk/research/st/sched.c +++ /dev/null @@ -1,680 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include -#include -#include -#include -#include "common.h" - -/* Global data */ -_st_vp_t _st_this_vp; /* This VP */ -_st_thread_t *_st_this_thread; /* Current thread */ -int _st_active_count = 0; /* Active thread count */ - -time_t _st_curr_time = 0; /* Current time as returned by time(2) */ -st_utime_t _st_last_tset; /* Last time it was fetched */ - -int st_poll(struct pollfd *pds, int npds, st_utime_t timeout) -{ - struct pollfd *pd; - struct pollfd *epd = pds + npds; - _st_pollq_t pq; - _st_thread_t *me = _ST_CURRENT_THREAD(); - int n; - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - if ((*_st_eventsys->pollset_add)(pds, npds) < 0) { - return -1; - } - - pq.pds = pds; - pq.npds = npds; - pq.thread = me; - pq.on_ioq = 1; - _ST_ADD_IOQ(pq); - if (timeout != ST_UTIME_NO_TIMEOUT) { - _ST_ADD_SLEEPQ(me, timeout); - } - me->state = _ST_ST_IO_WAIT; - - _ST_SWITCH_CONTEXT(me); - - n = 0; - if (pq.on_ioq) { - /* If we timed out, the pollq might still be on the ioq. Remove it */ - _ST_DEL_IOQ(pq); - (*_st_eventsys->pollset_del)(pds, npds); - } else { - /* Count the number of ready descriptors */ - for (pd = pds; pd < epd; pd++) { - if (pd->revents) { - n++; - } - } - } - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - return n; -} - -void _st_vp_schedule(void) -{ - _st_thread_t *trd; - - if (_ST_RUNQ.next != &_ST_RUNQ) { - /* Pull thread off of the run queue */ - trd = _ST_THREAD_PTR(_ST_RUNQ.next); - _ST_DEL_RUNQ(trd); - } else { - /* If there are no threads to run, switch to the idle thread */ - trd = _st_this_vp.idle_thread; - } - ST_ASSERT(trd->state == _ST_ST_RUNNABLE); - - /* Resume the thread */ - trd->state = _ST_ST_RUNNING; - _ST_RESTORE_CONTEXT(trd); -} - -/* - * Initialize this Virtual Processor - */ -int st_init(void) -{ - _st_thread_t *trd; - - if (_st_active_count) { - /* Already initialized */ - return 0; - } - - /* We can ignore return value here */ - st_set_eventsys(ST_EVENTSYS_DEFAULT); - - if (_st_io_init() < 0) { - return -1; - } - - memset(&_st_this_vp, 0, sizeof(_st_vp_t)); - - ST_INIT_CLIST(&_ST_RUNQ); - ST_INIT_CLIST(&_ST_IOQ); - ST_INIT_CLIST(&_ST_ZOMBIEQ); -#ifdef DEBUG - ST_INIT_CLIST(&_ST_THREADQ); -#endif - - if ((*_st_eventsys->init)() < 0) { - return -1; - } - - _st_this_vp.pagesize = getpagesize(); - _st_this_vp.last_clock = st_utime(); - - /* - * Create idle thread - */ - _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, NULL, 0, 0); - if (!_st_this_vp.idle_thread) { - return -1; - } - _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD; - _st_active_count--; - _ST_DEL_RUNQ(_st_this_vp.idle_thread); - - /* - * Initialize primordial thread - */ - trd = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) + - (ST_KEYS_MAX * sizeof(void *))); - if (!trd) { - return -1; - } - trd->private_data = (void **) (trd + 1); - trd->state = _ST_ST_RUNNING; - trd->flags = _ST_FL_PRIMORDIAL; - _ST_SET_CURRENT_THREAD(trd); - _st_active_count++; -#ifdef DEBUG - _ST_ADD_THREADQ(trd); -#endif - - return 0; -} - -#ifdef ST_SWITCH_CB -st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb) -{ - st_switch_cb_t ocb = _st_this_vp.switch_in_cb; - _st_this_vp.switch_in_cb = cb; - return ocb; -} - -st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb) -{ - st_switch_cb_t ocb = _st_this_vp.switch_out_cb; - _st_this_vp.switch_out_cb = cb; - return ocb; -} -#endif - -/* - * Start function for the idle thread - */ -/* ARGSUSED */ -void *_st_idle_thread_start(void *arg) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - - while (_st_active_count > 0) { - /* Idle vp till I/O is ready or the smallest timeout expired */ - _ST_VP_IDLE(); - - /* Check sleep queue for expired threads */ - _st_vp_check_clock(); - - me->state = _ST_ST_RUNNABLE; - _ST_SWITCH_CONTEXT(me); - } - - /* No more threads */ - exit(0); - - /* NOTREACHED */ - return NULL; -} - -void st_thread_exit(void *retval) -{ - _st_thread_t *trd = _ST_CURRENT_THREAD(); - - trd->retval = retval; - _st_thread_cleanup(trd); - _st_active_count--; - if (trd->term) { - /* Put thread on the zombie queue */ - trd->state = _ST_ST_ZOMBIE; - _ST_ADD_ZOMBIEQ(trd); - - /* Notify on our termination condition variable */ - st_cond_signal(trd->term); - - /* Switch context and come back later */ - _ST_SWITCH_CONTEXT(trd); - - /* Continue the cleanup */ - st_cond_destroy(trd->term); - trd->term = NULL; - } - -#ifdef DEBUG - _ST_DEL_THREADQ(trd); -#endif - - if (!(trd->flags & _ST_FL_PRIMORDIAL)) { - _st_stack_free(trd->stack); - } - - /* Find another thread to run */ - _ST_SWITCH_CONTEXT(trd); - /* Not going to land here */ -} - -int st_thread_join(_st_thread_t *trd, void **retvalp) -{ - _st_cond_t *term = trd->term; - - /* Can't join a non-joinable thread */ - if (term == NULL) { - errno = EINVAL; - return -1; - } - if (_ST_CURRENT_THREAD() == trd) { - errno = EDEADLK; - return -1; - } - - /* Multiple threads can't wait on the same joinable thread */ - if (term->wait_q.next != &term->wait_q) { - errno = EINVAL; - return -1; - } - - while (trd->state != _ST_ST_ZOMBIE) { - if (st_cond_timedwait(term, ST_UTIME_NO_TIMEOUT) != 0) { - return -1; - } - } - - if (retvalp) { - *retvalp = trd->retval; - } - - /* - * Remove target thread from the zombie queue and make it runnable. - * When it gets scheduled later, it will do the clean up. - */ - trd->state = _ST_ST_RUNNABLE; - _ST_DEL_ZOMBIEQ(trd); - _ST_ADD_RUNQ(trd); - - return 0; -} - -void _st_thread_main(void) -{ - _st_thread_t *trd = _ST_CURRENT_THREAD(); - - /* - * Cap the stack by zeroing out the saved return address register - * value. This allows some debugging/profiling tools to know when - * to stop unwinding the stack. It's a no-op on most platforms. - */ - MD_CAP_STACK(&trd); - - /* Run thread main */ - trd->retval = (*trd->start)(trd->arg); - - /* All done, time to go away */ - st_thread_exit(trd->retval); -} - -/* - * Insert "thread" into the timeout heap, in the position - * specified by thread->heap_index. See docs/timeout_heap.txt - * for details about the timeout heap. - */ -static _st_thread_t **heap_insert(_st_thread_t *trd) -{ - int target = trd->heap_index; - int s = target; - _st_thread_t **p = &_ST_SLEEPQ; - int bits = 0; - int bit; - int index = 1; - - while (s) { - s >>= 1; - bits++; - } - - for (bit = bits - 2; bit >= 0; bit--) { - if (trd->due < (*p)->due) { - _st_thread_t *t = *p; - trd->left = t->left; - trd->right = t->right; - *p = trd; - trd->heap_index = index; - trd = t; - } - index <<= 1; - if (target & (1 << bit)) { - p = &((*p)->right); - index |= 1; - } else { - p = &((*p)->left); - } - } - - trd->heap_index = index; - *p = trd; - trd->left = trd->right = NULL; - - return p; -} - -/* - * Delete "thread" from the timeout heap. - */ -static void heap_delete(_st_thread_t *trd) -{ - _st_thread_t *t, **p; - int bits = 0; - int s, bit; - - /* First find and unlink the last heap element */ - p = &_ST_SLEEPQ; - s = _ST_SLEEPQ_SIZE; - while (s) { - s >>= 1; - bits++; - } - - for (bit = bits - 2; bit >= 0; bit--) { - if (_ST_SLEEPQ_SIZE & (1 << bit)) { - p = &((*p)->right); - } else { - p = &((*p)->left); - } - } - - t = *p; - *p = NULL; - --_ST_SLEEPQ_SIZE; - if (t != trd) { - /* - * Insert the unlinked last element in place of the element we are deleting - */ - t->heap_index = trd->heap_index; - p = heap_insert(t); - t = *p; - t->left = trd->left; - t->right = trd->right; - - /* - * Reestablish the heap invariant. - */ - for (;;) { - _st_thread_t *y; /* The younger child */ - int index_tmp; - - if (t->left == NULL) { - break; - } else if (t->right == NULL) { - y = t->left; - } else if (t->left->due < t->right->due) { - y = t->left; - } else { - y = t->right; - } - - if (t->due > y->due) { - _st_thread_t *tl = y->left; - _st_thread_t *tr = y->right; - *p = y; - if (y == t->left) { - y->left = t; - y->right = t->right; - p = &y->left; - } else { - y->left = t->left; - y->right = t; - p = &y->right; - } - t->left = tl; - t->right = tr; - index_tmp = t->heap_index; - t->heap_index = y->heap_index; - y->heap_index = index_tmp; - } else { - break; - } - } - } - - trd->left = trd->right = NULL; -} - -void _st_add_sleep_q(_st_thread_t *trd, st_utime_t timeout) -{ - trd->due = _ST_LAST_CLOCK + timeout; - trd->flags |= _ST_FL_ON_SLEEPQ; - trd->heap_index = ++_ST_SLEEPQ_SIZE; - heap_insert(trd); -} - -void _st_del_sleep_q(_st_thread_t *trd) -{ - heap_delete(trd); - trd->flags &= ~_ST_FL_ON_SLEEPQ; -} - -void _st_vp_check_clock(void) -{ - _st_thread_t *trd; - st_utime_t elapsed, now; - - now = st_utime(); - elapsed = now - _ST_LAST_CLOCK; - _ST_LAST_CLOCK = now; - - if (_st_curr_time && now - _st_last_tset > 999000) { - _st_curr_time = time(NULL); - _st_last_tset = now; - } - - while (_ST_SLEEPQ != NULL) { - trd = _ST_SLEEPQ; - ST_ASSERT(trd->flags & _ST_FL_ON_SLEEPQ); - if (trd->due > now) { - break; - } - _ST_DEL_SLEEPQ(trd); - - /* If thread is waiting on condition variable, set the time out flag */ - if (trd->state == _ST_ST_COND_WAIT) { - trd->flags |= _ST_FL_TIMEDOUT; - } - - /* Make thread runnable */ - ST_ASSERT(!(trd->flags & _ST_FL_IDLE_THREAD)); - trd->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(trd); - } -} - -void st_thread_interrupt(_st_thread_t* trd) -{ - /* If thread is already dead */ - if (trd->state == _ST_ST_ZOMBIE) { - return; - } - - trd->flags |= _ST_FL_INTERRUPT; - - if (trd->state == _ST_ST_RUNNING || trd->state == _ST_ST_RUNNABLE) { - return; - } - - if (trd->flags & _ST_FL_ON_SLEEPQ) { - _ST_DEL_SLEEPQ(trd); - } - - /* Make thread runnable */ - trd->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(trd); -} - -_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size) -{ - _st_thread_t *trd; - _st_stack_t *stack; - void **ptds; - char *sp; - - /* Adjust stack size */ - if (stk_size == 0) { - stk_size = ST_DEFAULT_STACK_SIZE; - } - stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE; - stack = _st_stack_new(stk_size); - if (!stack) { - return NULL; - } - - /* Allocate thread object and per-thread data off the stack */ -#if defined (MD_STACK_GROWS_DOWN) - sp = stack->stk_top; - /* - * The stack segment is split in the middle. The upper half is used - * as backing store for the register stack which grows upward. - * The lower half is used for the traditional memory stack which - * grows downward. Both stacks start in the middle and grow outward - * from each other. - */ - /** - The below comments is by winlin: - The Stack public structure: - +--------------------------------------------------------------+ - | stack | - +--------------------------------------------------------------+ - bottom top - The code bellow use the stack as: - +-----------------+-----------------+-------------+------------+ - | stack of thread |pad+align(128B+) |thread(336B) | keys(128B) | - +-----------------+-----------------+-------------+------------+ - bottom sp trd ptds top - (context[0].__jmpbuf.sp) (private_data) - */ - sp = sp - (ST_KEYS_MAX * sizeof(void *)); - ptds = (void **) sp; - sp = sp - sizeof(_st_thread_t); - trd = (_st_thread_t *) sp; - - /* Make stack 64-byte aligned */ - if ((unsigned long)sp & 0x3f) { - sp = sp - ((unsigned long)sp & 0x3f); - } - stack->sp = sp - _ST_STACK_PAD_SIZE; -#else - #error "Only Supports Stack Grown Down" -#endif - - memset(trd, 0, sizeof(_st_thread_t)); - memset(ptds, 0, ST_KEYS_MAX * sizeof(void *)); - - /* Initialize thread */ - trd->private_data = ptds; - trd->stack = stack; - trd->start = start; - trd->arg = arg; - -// by winlin, expand macro MD_INIT_CONTEXT -#if defined(__mips__) - MD_SETJMP((trd)->context); - trd->context[0].__jmpbuf[0].__pc = (__ptr_t) _st_thread_main; - trd->context[0].__jmpbuf[0].__sp = stack->sp; -#else - if (MD_SETJMP((trd)->context)) { - _st_thread_main(); - } - MD_GET_SP(trd) = (long) (stack->sp); -#endif - - /* If thread is joinable, allocate a termination condition variable */ - if (joinable) { - trd->term = st_cond_new(); - if (trd->term == NULL) { - _st_stack_free(trd->stack); - return NULL; - } - } - - /* Make thread runnable */ - trd->state = _ST_ST_RUNNABLE; - _st_active_count++; - _ST_ADD_RUNQ(trd); -#ifdef DEBUG - _ST_ADD_THREADQ(trd); -#endif - - return trd; -} - -_st_thread_t *st_thread_self(void) -{ - return _ST_CURRENT_THREAD(); -} - -#ifdef DEBUG -/* ARGSUSED */ -void _st_show_thread_stack(_st_thread_t *trd, const char *messg) -{ -} - -/* To be set from debugger */ -int _st_iterate_threads_flag = 0; - -void _st_iterate_threads(void) -{ - static _st_thread_t *trd = NULL; - static jmp_buf orig_jb, save_jb; - _st_clist_t *q; - - if (!_st_iterate_threads_flag) { - if (trd) { - memcpy(trd->context, save_jb, sizeof(jmp_buf)); - MD_LONGJMP(orig_jb, 1); - } - return; - } - - if (trd) { - memcpy(trd->context, save_jb, sizeof(jmp_buf)); - _st_show_thread_stack(trd, NULL); - } else { - if (MD_SETJMP(orig_jb)) { - _st_iterate_threads_flag = 0; - trd = NULL; - _st_show_thread_stack(trd, "Iteration completed"); - return; - } - trd = _ST_CURRENT_THREAD(); - _st_show_thread_stack(trd, "Iteration started"); - } - - q = trd->tlink.next; - if (q == &_ST_THREADQ) { - q = q->next; - } - ST_ASSERT(q != &_ST_THREADQ); - trd = _ST_THREAD_THREADQ_PTR(q); - if (trd == _ST_CURRENT_THREAD()) { - MD_LONGJMP(orig_jb, 1); - } - memcpy(save_jb, trd->context, sizeof(jmp_buf)); - MD_LONGJMP(trd->context, 1); -} -#endif /* DEBUG */ - diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c deleted file mode 100644 index 09bc5eacc..000000000 --- a/trunk/research/st/srs.c +++ /dev/null @@ -1,497 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "public.h" - -#define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") - -int io_port = 1990; -int sleep_ms = 100; - -void stack_print(long int previous_sp, int level) -{ - if (level <= 0) { - return; - } - - register long int rsp asm("sp"); - char buf[level * 1024]; - - stack_print(rsp, level - 1); - - srs_trace("%d. psp=%#lx, sp=%#lx, size=%dB(%dB+%dKB)", - level, previous_sp, rsp, (int)(previous_sp - rsp), - (int)(previous_sp - rsp - sizeof(buf)), (int)(sizeof(buf) / 1024)); -} - -int huge_stack_test() -{ - srs_trace("==================================================="); - srs_trace("huge_stack test: start"); - - register long int rsp asm("sp"); - stack_print(rsp, 10); - - srs_trace("huge_stack test: end"); - - return 0; -} - -int sleep_test() -{ - srs_trace("==================================================="); - srs_trace("sleep test: start"); - - srs_trace("1. sleep..."); - st_utime_t start = st_utime(); - st_usleep(sleep_ms * 1000); - st_utime_t end = st_utime(); - - srs_trace("2. sleep ok, sleep=%dus, deviation=%dus", - (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000)); - - srs_trace("sleep test: end"); - - return 0; -} - -void* sleep2_func0(void* arg) -{ - int sleep_ms = 100; - st_utime_t start = st_utime(); - st_usleep(sleep_ms * 1000); - st_utime_t end = st_utime(); - - srs_trace("sleep ok, sleep=%dus, deviation=%dus", - (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000)); - - return NULL; -} - -void* sleep2_func1(void* arg) -{ - int sleep_ms = 250; - st_utime_t start = st_utime(); - st_usleep(sleep_ms * 1000); - st_utime_t end = st_utime(); - - srs_trace("sleep ok, sleep=%dus, deviation=%dus", - (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000)); - - return NULL; -} - -int sleep2_test() -{ - srs_trace("==================================================="); - srs_trace("sleep2 test: start"); - - st_thread_t trd0 = st_thread_create(sleep2_func0, NULL, 1, 0); - st_thread_t trd1 = st_thread_create(sleep2_func1, NULL, 1, 0); - st_thread_join(trd0, NULL); - st_thread_join(trd1, NULL); - - srs_trace("sleep test: end"); - - return 0; -} - -st_mutex_t sleep_work_cond = NULL; -void* sleep_deviation_func(void* arg) -{ - st_mutex_lock(sleep_work_cond); - srs_trace("2. work thread start."); - - int64_t i; - for (i = 0; i < 3000000000ULL; i++) { - } - - st_mutex_unlock(sleep_work_cond); - srs_trace("3. work thread end."); - - return NULL; -} - -int sleep_deviation_test() -{ - srs_trace("==================================================="); - srs_trace("sleep deviation test: start"); - - sleep_work_cond = st_mutex_new(); - - st_thread_create(sleep_deviation_func, NULL, 0, 0); - st_mutex_lock(sleep_work_cond); - - srs_trace("1. sleep..."); - st_utime_t start = st_utime(); - - // other thread to do some complex work. - st_mutex_unlock(sleep_work_cond); - st_usleep(1000 * 1000); - - st_utime_t end = st_utime(); - - srs_trace("4. sleep ok, sleep=%dus, deviation=%dus", - (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000)); - - st_mutex_lock(sleep_work_cond); - srs_trace("sleep deviation test: end"); - - st_mutex_destroy(sleep_work_cond); - - return 0; -} - -void* thread_func(void* arg) -{ - srs_trace("1. thread run"); - st_usleep(sleep_ms * 1000); - srs_trace("2. thread completed"); - return NULL; -} - -int thread_test() -{ - srs_trace("==================================================="); - srs_trace("thread test: start"); - - st_thread_t trd = st_thread_create(thread_func, NULL, 1, 0); - if (trd == NULL) { - srs_trace("st_thread_create failed"); - return -1; - } - - st_thread_join(trd, NULL); - srs_trace("3. thread joined"); - - srs_trace("thread test: end"); - - return 0; -} - -st_mutex_t sync_start = NULL; -st_cond_t sync_cond = NULL; -st_mutex_t sync_mutex = NULL; -st_cond_t sync_end = NULL; - -void* sync_master(void* arg) -{ - // wait for main to sync_start this thread. - st_mutex_lock(sync_start); - st_mutex_unlock(sync_start); - - st_usleep(sleep_ms * 1000); - st_cond_signal(sync_cond); - - st_mutex_lock(sync_mutex); - srs_trace("2. st mutex is ok"); - st_mutex_unlock(sync_mutex); - - st_usleep(sleep_ms * 1000); - srs_trace("3. st thread is ok"); - st_cond_signal(sync_cond); - - return NULL; -} - -void* sync_slave(void* arg) -{ - // lock mutex to control thread. - st_mutex_lock(sync_mutex); - - // wait for main to sync_start this thread. - st_mutex_lock(sync_start); - st_mutex_unlock(sync_start); - - // wait thread to ready. - st_cond_wait(sync_cond); - srs_trace("1. st cond is ok"); - - // release mutex to control thread - st_usleep(sleep_ms * 1000); - st_mutex_unlock(sync_mutex); - - // wait thread to exit. - st_cond_wait(sync_cond); - srs_trace("4. st is ok"); - - st_cond_signal(sync_end); - - return NULL; -} - -int sync_test() -{ - srs_trace("==================================================="); - srs_trace("sync test: start"); - - if ((sync_start = st_mutex_new()) == NULL) { - srs_trace("st_mutex_new sync_start failed"); - return -1; - } - st_mutex_lock(sync_start); - - if ((sync_cond = st_cond_new()) == NULL) { - srs_trace("st_cond_new cond failed"); - return -1; - } - - if ((sync_end = st_cond_new()) == NULL) { - srs_trace("st_cond_new end failed"); - return -1; - } - - if ((sync_mutex = st_mutex_new()) == NULL) { - srs_trace("st_mutex_new mutex failed"); - return -1; - } - - if (!st_thread_create(sync_master, NULL, 0, 0)) { - srs_trace("st_thread_create failed"); - return -1; - } - - if (!st_thread_create(sync_slave, NULL, 0, 0)) { - srs_trace("st_thread_create failed"); - return -1; - } - - // run all threads. - st_mutex_unlock(sync_start); - - st_cond_wait(sync_end); - srs_trace("sync test: end"); - - return 0; -} - -void* io_client(void* arg) -{ - - int fd; - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - srs_trace("create linux socket error."); - return NULL; - } - srs_trace("6. client create linux socket success. fd=%d", fd); - - st_netfd_t stfd; - if ((stfd = st_netfd_open_socket(fd)) == NULL){ - srs_trace("st_netfd_open_socket open socket failed."); - return NULL; - } - srs_trace("7. client st open socket success. fd=%d", fd); - - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(io_port); - addr.sin_addr.s_addr = INADDR_ANY; - if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1) { - srs_trace("bind socket error."); - return NULL; - } - - char buf[1024]; - if (st_read_fully(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { - srs_trace("st_read_fully failed"); - return NULL; - } - if (st_write(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { - srs_trace("st_write failed"); - return NULL; - } - - st_netfd_close(stfd); - - return NULL; -} - -int io_test() -{ - srs_trace("==================================================="); - srs_trace("io test: start, port=%d", io_port); - - int fd; - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - srs_trace("create linux socket error."); - return -1; - } - srs_trace("1. server create linux socket success. fd=%d", fd); - - int reuse_socket = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) { - srs_trace("setsockopt reuse-addr error."); - return -1; - } - srs_trace("2. server setsockopt reuse-addr success. fd=%d", fd); - - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(io_port); - addr.sin_addr.s_addr = INADDR_ANY; - if (bind(fd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) { - srs_trace("bind socket error."); - return -1; - } - srs_trace("3. server bind socket success. fd=%d", fd); - - if (listen(fd, 10) == -1) { - srs_trace("listen socket error."); - return -1; - } - srs_trace("4. server listen socket success. fd=%d", fd); - - st_netfd_t stfd; - if ((stfd = st_netfd_open_socket(fd)) == NULL){ - srs_trace("st_netfd_open_socket open socket failed."); - return -1; - } - srs_trace("5. server st open socket success. fd=%d", fd); - - if (!st_thread_create(io_client, NULL, 0, 0)) { - srs_trace("st_thread_create failed"); - return -1; - } - - st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT); - srs_trace("8. server get a client. fd=%d", st_netfd_fileno(client_stfd)); - - char buf[1024]; - if (st_write(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { - srs_trace("st_write failed"); - return -1; - } - if (st_read_fully(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { - srs_trace("st_read_fully failed"); - return -1; - } - srs_trace("9. server io completed."); - - st_netfd_close(stfd); - st_netfd_close(client_stfd); - - srs_trace("io test: end"); - return 0; -} - -int pipe_test() -{ - srs_trace("==================================================="); - srs_trace("pipe test: start"); - - int fds[2]; - if (pipe(fds) < 0) { - srs_trace("pipe failed"); - return -1; - } - srs_trace("1. pipe ok, %d=>%d", fds[1], fds[0]); - - st_netfd_t fdw; - if ((fdw = st_netfd_open_socket(fds[1])) == NULL) { - srs_trace("st_netfd_open_socket open socket failed."); - return -1; - } - srs_trace("2. open write fd ok"); - - st_netfd_t fdr; - if ((fdr = st_netfd_open_socket(fds[0])) == NULL) { - srs_trace("st_netfd_open_socket open socket failed."); - return -1; - } - srs_trace("3. open read fd ok"); - - char buf[1024]; - if (st_write(fdw, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) { - srs_trace("st_write socket failed."); - return -1; - } - srs_trace("4. write to pipe ok"); - - if (st_read(fdr, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) { - srs_trace("st_read socket failed."); - return -1; - } - srs_trace("5. read from pipe ok"); - - st_netfd_close(fdw); - st_netfd_close(fdr); - - srs_trace("pipe test: end"); - return 0; -} - -int main(int argc, char** argv) -{ - srs_trace("ETIME=%d", ETIME); - - if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) { - srs_trace("st_set_eventsys failed"); - return -1; - } - - if (st_init() < 0) { - srs_trace("st_init failed"); - return -1; - } - - if (sleep2_test() < 0) { - srs_trace("sleep2_test failed"); - return -1; - } - - if (sleep_test() < 0) { - srs_trace("sleep_test failed"); - return -1; - } - - if (sleep_deviation_test() < 0) { - srs_trace("sleep_deviation_test failed"); - return -1; - } - - if (huge_stack_test() < 0) { - srs_trace("huge_stack_test failed"); - return -1; - } - - if (thread_test() < 0) { - srs_trace("thread_test failed"); - return -1; - } - - if (sync_test() < 0) { - srs_trace("sync_test failed"); - return -1; - } - - if (io_test() < 0) { - srs_trace("io_test failed"); - return -1; - } - - if (pipe_test() < 0) { - srs_trace("pipe_test failed"); - return -1; - } - - // cleanup. - srs_trace("wait for all thread completed"); - st_thread_exit(NULL); - // the following never enter, - // the above code will exit when all thread exit, - // current is a primordial st-thread, when all thread exit, - // the st idle thread will exit(0), see _st_idle_thread_start() - srs_trace("all thread completed"); - - return 0; -} - diff --git a/trunk/research/st/st/init b/trunk/research/st/st/init deleted file mode 100644 index 61604b75f..000000000 --- a/trunk/research/st/st/init +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef _st_icpp_init_stub -#define _st_icpp_init_stub -#endif diff --git a/trunk/research/st/st/st.upp b/trunk/research/st/st/st.upp deleted file mode 100755 index dab6d4958..000000000 --- a/trunk/research/st/st/st.upp +++ /dev/null @@ -1,18 +0,0 @@ -file - main readonly separator, - ..\srs.c, - st readonly separator, - ..\common.h, - ..\event.c, - ..\io.c, - ..\key.c, - ..\md.h, - ..\md.S, - ..\public.h, - ..\sched.c, - ..\stk.c, - ..\sync.c; - -mainconfig - "" = "MAIN"; - diff --git a/trunk/research/st/stk.c b/trunk/research/st/stk.c deleted file mode 100644 index c26223ba5..000000000 --- a/trunk/research/st/stk.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include -#include -#include -#include "common.h" - -/* How much space to leave between the stacks, at each end */ -#define REDZONE _ST_PAGE_SIZE - -_st_clist_t _st_free_stacks = ST_INIT_STATIC_CLIST(&_st_free_stacks); -int _st_num_free_stacks = 0; -int _st_randomize_stacks = 0; - -static char *_st_new_stk_segment(int size); - -/** -The below comments is by winlin: -The stack memory struct: - | REDZONE | stack | extra | REDZONE | - +---------+------------------------+---------+---------+ - | 4k | | 4k/0 | 4k | - +---------+------------------------+---------+---------+ - vaddr bottom top -When _st_randomize_stacks is on, by st_randomize_stacks(), -the bottom and top will random movided in the extra: - long offset = (random() % extra) & ~0xf; - ts->stk_bottom += offset; - ts->stk_top += offset; -Both REDZONE are protected by mprotect when DEBUG is on. -*/ -_st_stack_t *_st_stack_new(int stack_size) -{ - _st_clist_t *qp; - _st_stack_t *ts; - int extra; - - // TODO: WINLIN: remove the stack reuse. - for (qp = _st_free_stacks.next; qp != &_st_free_stacks; qp = qp->next) { - ts = _ST_THREAD_STACK_PTR(qp); - if (ts->stk_size >= stack_size) { - /* Found a stack that is big enough */ - ST_REMOVE_LINK(&ts->links); - _st_num_free_stacks--; - ts->links.next = NULL; - ts->links.prev = NULL; - return ts; - } - } - - /* Make a new thread stack object. */ - if ((ts = (_st_stack_t *)calloc(1, sizeof(_st_stack_t))) == NULL) { - return NULL; - } - extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0; - ts->vaddr_size = stack_size + 2*REDZONE + extra; - ts->vaddr = _st_new_stk_segment(ts->vaddr_size); - if (!ts->vaddr) { - free(ts); - return NULL; - } - ts->stk_size = stack_size; - ts->stk_bottom = ts->vaddr + REDZONE; - ts->stk_top = ts->stk_bottom + stack_size; - -#ifdef DEBUG - mprotect(ts->vaddr, REDZONE, PROT_NONE); - mprotect(ts->stk_top + extra, REDZONE, PROT_NONE); -#endif - - if (extra) { - long offset = (random() % extra) & ~0xf; - - ts->stk_bottom += offset; - ts->stk_top += offset; - } - - return ts; -} - -/* - * Free the stack for the current thread - */ -void _st_stack_free(_st_stack_t *ts) -{ - if (!ts) { - return; - } - - /* Put the stack on the free list */ - ST_APPEND_LINK(&ts->links, _st_free_stacks.prev); - _st_num_free_stacks++; -} - -static char *_st_new_stk_segment(int size) -{ -#ifdef MALLOC_STACK - void *vaddr = malloc(size); -#else - #error "Only Supports Malloc Stack" -#endif - - return (char *)vaddr; -} - -/* Not used */ -#if 0 -void _st_delete_stk_segment(char *vaddr, int size) -{ -#ifdef MALLOC_STACK - free(vaddr); -#else - #error Unknown Stack Malloc -#endif -} -#endif - -int st_randomize_stacks(int on) -{ - int wason = _st_randomize_stacks; - - _st_randomize_stacks = on; - if (on) { - srandom((unsigned int) st_utime()); - } - - return wason; -} diff --git a/trunk/research/st/sync.c b/trunk/research/st/sync.c deleted file mode 100644 index 3e5324084..000000000 --- a/trunk/research/st/sync.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code is the Netscape Portable Runtime library. - * - * The Initial Developer of the Original Code is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1994-2000 Netscape Communications Corporation. All - * Rights Reserved. - * - * Contributor(s): Silicon Graphics, Inc. - * - * Portions created by SGI are Copyright (C) 2000-2001 Silicon - * Graphics, Inc. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License Version 2 or later (the - * "GPL"), in which case the provisions of the GPL are applicable - * instead of those above. If you wish to allow use of your - * version of this file only under the terms of the GPL and not to - * allow others to use your version of this file under the MPL, - * indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by - * the GPL. If you do not delete the provisions above, a recipient - * may use your version of this file under either the MPL or the - * GPL. - */ - -/* - * This file is derived directly from Netscape Communications Corporation, - * and consists of extensive modifications made during the year(s) 1999-2000. - */ - -#include -#include -#include -#include "common.h" - -extern time_t _st_curr_time; -extern st_utime_t _st_last_tset; -extern int _st_active_count; - -static st_utime_t (*_st_utime)(void) = NULL; - -/***************************************** - * Time functions - */ - -st_utime_t st_utime(void) -{ - if (_st_utime == NULL) { -#ifdef MD_GET_UTIME - MD_GET_UTIME(); -#else - #error Unknown OS -#endif - } - - return (*_st_utime)(); -} - -int st_set_utime_function(st_utime_t (*func)(void)) -{ - if (_st_active_count) { - errno = EINVAL; - return -1; - } - - _st_utime = func; - - return 0; -} - -st_utime_t st_utime_last_clock(void) -{ - return _ST_LAST_CLOCK; -} - -int st_timecache_set(int on) -{ - int wason = (_st_curr_time) ? 1 : 0; - - if (on) { - _st_curr_time = time(NULL); - _st_last_tset = st_utime(); - } else { - _st_curr_time = 0; - } - - return wason; -} - -time_t st_time(void) -{ - if (_st_curr_time) { - return _st_curr_time; - } - - return time(NULL); -} - -int st_usleep(st_utime_t usecs) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - if (usecs != ST_UTIME_NO_TIMEOUT) { - me->state = _ST_ST_SLEEPING; - _ST_ADD_SLEEPQ(me, usecs); - } else { - me->state = _ST_ST_SUSPENDED; - } - - _ST_SWITCH_CONTEXT(me); - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - return 0; -} - -int st_sleep(int secs) -{ - return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : ST_UTIME_NO_TIMEOUT); -} - -/***************************************** - * Condition variable functions - */ -_st_cond_t *st_cond_new(void) -{ - _st_cond_t *cvar; - - cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t)); - if (cvar) { - ST_INIT_CLIST(&cvar->wait_q); - } - - return cvar; -} - -int st_cond_destroy(_st_cond_t *cvar) -{ - if (cvar->wait_q.next != &cvar->wait_q) { - errno = EBUSY; - return -1; - } - - free(cvar); - - return 0; -} - -int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - int rv; - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - /* Put caller thread on the condition variable's wait queue */ - me->state = _ST_ST_COND_WAIT; - ST_APPEND_LINK(&me->wait_links, &cvar->wait_q); - - if (timeout != ST_UTIME_NO_TIMEOUT) { - _ST_ADD_SLEEPQ(me, timeout); - } - - _ST_SWITCH_CONTEXT(me); - - ST_REMOVE_LINK(&me->wait_links); - rv = 0; - - if (me->flags & _ST_FL_TIMEDOUT) { - me->flags &= ~_ST_FL_TIMEDOUT; - errno = ETIME; - rv = -1; - } - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - rv = -1; - } - - return rv; -} - -int st_cond_wait(_st_cond_t *cvar) -{ - return st_cond_timedwait(cvar, ST_UTIME_NO_TIMEOUT); -} - -static int _st_cond_signal(_st_cond_t *cvar, int broadcast) -{ - _st_thread_t *thread; - _st_clist_t *q; - - for (q = cvar->wait_q.next; q != &cvar->wait_q; q = q->next) { - thread = _ST_THREAD_WAITQ_PTR(q); - if (thread->state == _ST_ST_COND_WAIT) { - if (thread->flags & _ST_FL_ON_SLEEPQ) { - _ST_DEL_SLEEPQ(thread); - } - - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); - if (!broadcast) { - break; - } - } - } - - return 0; -} - -int st_cond_signal(_st_cond_t *cvar) -{ - return _st_cond_signal(cvar, 0); -} - -int st_cond_broadcast(_st_cond_t *cvar) -{ - return _st_cond_signal(cvar, 1); -} - -/***************************************** - * Mutex functions - */ -_st_mutex_t *st_mutex_new(void) -{ - _st_mutex_t *lock; - - lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t)); - if (lock) { - ST_INIT_CLIST(&lock->wait_q); - lock->owner = NULL; - } - - return lock; -} - -int st_mutex_destroy(_st_mutex_t *lock) -{ - if (lock->owner != NULL || lock->wait_q.next != &lock->wait_q) { - errno = EBUSY; - return -1; - } - - free(lock); - - return 0; -} - -int st_mutex_lock(_st_mutex_t *lock) -{ - _st_thread_t *me = _ST_CURRENT_THREAD(); - - if (me->flags & _ST_FL_INTERRUPT) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - if (lock->owner == NULL) { - /* Got the mutex */ - lock->owner = me; - return 0; - } - - if (lock->owner == me) { - errno = EDEADLK; - return -1; - } - - /* Put caller thread on the mutex's wait queue */ - me->state = _ST_ST_LOCK_WAIT; - ST_APPEND_LINK(&me->wait_links, &lock->wait_q); - - _ST_SWITCH_CONTEXT(me); - - ST_REMOVE_LINK(&me->wait_links); - - if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) { - me->flags &= ~_ST_FL_INTERRUPT; - errno = EINTR; - return -1; - } - - return 0; -} - -int st_mutex_unlock(_st_mutex_t *lock) -{ - _st_thread_t *thread; - _st_clist_t *q; - - if (lock->owner != _ST_CURRENT_THREAD()) { - errno = EPERM; - return -1; - } - - for (q = lock->wait_q.next; q != &lock->wait_q; q = q->next) { - thread = _ST_THREAD_WAITQ_PTR(q); - if (thread->state == _ST_ST_LOCK_WAIT) { - lock->owner = thread; - /* Make thread runnable */ - thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); - return 0; - } - } - - /* No threads waiting on this mutex */ - lock->owner = NULL; - - return 0; -} - -int st_mutex_trylock(_st_mutex_t *lock) -{ - if (lock->owner != NULL) { - errno = EBUSY; - return -1; - } - - /* Got the mutex */ - lock->owner = _ST_CURRENT_THREAD(); - - return 0; -} - diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index b33b4f6f8..7ebb10b14 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -72,7 +72,7 @@ SrsUdpListener::SrsUdpListener(ISrsUdpHandler* h, string i, int p) handler = h; ip = i; port = p; - lfd = NULL; + lfd = -1; nb_buf = SRS_UDP_MAX_PACKET_SIZE; buf = new char[nb_buf]; @@ -148,7 +148,7 @@ SrsTcpListener::SrsTcpListener(ISrsTcpHandler* h, string i, int p) ip = i; port = p; - lfd = NULL; + lfd = -1; trd = new SrsDummyCoroutine(); } @@ -161,7 +161,7 @@ SrsTcpListener::~SrsTcpListener() int SrsTcpListener::fd() { - return srs_netfd_fileno(lfd);; + return srs_netfd_fileno(lfd); } srs_error_t SrsTcpListener::listen() @@ -191,10 +191,10 @@ srs_error_t SrsTcpListener::cycle() } srs_netfd_t fd = srs_accept(lfd, NULL, NULL, SRS_UTIME_NO_TIMEOUT); - if(fd == NULL){ + if(fd < 0){ return srs_error_new(ERROR_SOCKET_ACCEPT, "accept at fd=%d", srs_netfd_fileno(lfd)); } - + if ((err = srs_fd_closeexec(srs_netfd_fileno(fd))) != srs_success) { return srs_error_wrap(err, "set closeexec"); } diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 61eeb2309..e42f8314b 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -32,6 +32,8 @@ #include using namespace std; +#include + #include #include #include @@ -344,7 +346,7 @@ SrsSignalManager::SrsSignalManager(SrsServer* s) server = s; sig_pipe[0] = sig_pipe[1] = -1; trd = new SrsSTCoroutine("signal", this); - signal_read_stfd = NULL; + signal_read_stfd = -1; } SrsSignalManager::~SrsSignalManager() @@ -368,7 +370,7 @@ srs_error_t SrsSignalManager::initialize() return srs_error_new(ERROR_SYSTEM_CREATE_PIPE, "create pipe"); } - if ((signal_read_stfd = srs_netfd_open(sig_pipe[0])) == NULL) { + if ((signal_read_stfd = srs_netfd_open(sig_pipe[0])) < 0) { return srs_error_new(ERROR_SYSTEM_CREATE_PIPE, "open pipe"); } @@ -794,7 +796,7 @@ srs_error_t SrsServer::ingest() srs_error_t SrsServer::cycle() { srs_error_t err = do_cycle(); - + #ifdef SRS_AUTO_GPERF_MC destroy(); @@ -885,6 +887,10 @@ srs_error_t SrsServer::do_cycle() // the daemon thread, update the time cache // TODO: FIXME: use SrsHourGlass. + + // FIXME: libco will take over user's event loop + co_eventloop(co_get_epoll_ct(), NULL, NULL); + while (true) { if (handler && (err = handler->on_cycle()) != srs_success) { return srs_error_wrap(err, "handle callback"); diff --git a/trunk/src/app/srs_app_st.cpp b/trunk/src/app/srs_app_st.cpp index 831a0613a..5c4c7b87b 100755 --- a/trunk/src/app/srs_app_st.cpp +++ b/trunk/src/app/srs_app_st.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include using namespace std; @@ -79,13 +79,24 @@ int SrsDummyCoroutine::cid() return 0; } -_ST_THREAD_CREATE_PFN _pfn_st_thread_create = (_ST_THREAD_CREATE_PFN)st_thread_create; +void* co_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stack_size) { + (void)joinable; + (void)stack_size; + + stCoRoutine_t *co = NULL; + co_create(&co, NULL, start, arg); + co_resume(co); + return co; +} + +_ST_THREAD_CREATE_PFN _pfn_st_thread_create = (_ST_THREAD_CREATE_PFN)co_thread_create; SrsSTCoroutine::SrsSTCoroutine(string n, ISrsCoroutineHandler* h, int cid) { name = n; handler = h; context = cid; + term = co_cond_alloc(); trd = NULL; trd_err = srs_success; started = interrupted = disposed = cycle_done = false; @@ -94,7 +105,8 @@ SrsSTCoroutine::SrsSTCoroutine(string n, ISrsCoroutineHandler* h, int cid) SrsSTCoroutine::~SrsSTCoroutine() { stop(); - + + co_cond_free(term); srs_freep(trd_err); } @@ -142,8 +154,8 @@ void SrsSTCoroutine::stop() // When not started, the rd is NULL. if (trd) { void* res = NULL; - int r0 = st_thread_join((st_thread_t)trd, &res); - srs_assert(!r0); + + co_cond_timedwait(term, -1); srs_error_t err_res = (srs_error_t)res; if (err_res != srs_success) { @@ -171,8 +183,6 @@ void SrsSTCoroutine::interrupt() if (trd_err == srs_success) { trd_err = srs_error_new(ERROR_THREAD_INTERRUPED, "interrupted"); } - - st_thread_interrupt((st_thread_t)trd); } srs_error_t SrsSTCoroutine::pull() @@ -220,6 +230,9 @@ void* SrsSTCoroutine::pfn(void* arg) p->trd_err = err; } + // FIXME: + //co_cond_signal(term); + return (void*)err; } diff --git a/trunk/src/app/srs_app_st.hpp b/trunk/src/app/srs_app_st.hpp index 956e00b42..f158d5ba6 100644 --- a/trunk/src/app/srs_app_st.hpp +++ b/trunk/src/app/srs_app_st.hpp @@ -102,6 +102,8 @@ public: typedef void* (*_ST_THREAD_CREATE_PFN)(void *(*start)(void *arg), void *arg, int joinable, int stack_size); extern _ST_THREAD_CREATE_PFN _pfn_st_thread_create; +struct stCoCond_t; + // A ST-coroutine is a lightweight thread, just like the goroutine. // But the goroutine maybe run on different thread, while ST-coroutine only // run in single thread, because it use setjmp and longjmp, so it may cause @@ -120,6 +122,7 @@ private: std::string name; ISrsCoroutineHandler* handler; private: + stCoCond_t* term; srs_thread_t trd; int context; srs_error_t trd_err; diff --git a/trunk/src/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp index f63cd4279..dba7adbe8 100644 --- a/trunk/src/service/srs_service_st.cpp +++ b/trunk/src/service/srs_service_st.cpp @@ -23,10 +23,11 @@ #include -#include +#include #include #include #include +#include using namespace std; #include @@ -41,6 +42,17 @@ using namespace std; #ifdef __linux__ #include +static int set_fd_nonblock(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL, 0); + flags |= O_NONBLOCK; + flags |= O_NDELAY; + int ret = fcntl(fd, F_SETFL, flags); + return ret; +} + bool srs_st_epoll_is_supported(void) { struct epoll_event ev; @@ -56,37 +68,12 @@ bool srs_st_epoll_is_supported(void) srs_error_t srs_st_init() { -#ifdef __linux__ - // check epoll, some old linux donot support epoll. - // @see https://github.com/ossrs/srs/issues/162 - if (!srs_st_epoll_is_supported()) { - return srs_error_new(ERROR_ST_SET_EPOLL, "linux epoll disabled"); - } -#endif - - // Select the best event system available on the OS. In Linux this is - // epoll(). On BSD it will be kqueue. - if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) { - return srs_error_new(ERROR_ST_SET_EPOLL, "st enable st failed, current is %s", st_get_eventsys_name()); - } - - int r0 = 0; - if((r0 = st_init()) != 0){ - return srs_error_new(ERROR_ST_INITIALIZE, "st initialize failed, r0=%d", r0); - } - srs_trace("st_init success, use %s", st_get_eventsys_name()); - return srs_success; } void srs_close_stfd(srs_netfd_t& stfd) { - if (stfd) { - // we must ensure the close is ok. - int err = st_netfd_close((st_netfd_t)stfd); - srs_assert(err != -1); - stfd = NULL; - } + ::close(stfd); } srs_error_t srs_fd_closeexec(int fd) @@ -144,18 +131,17 @@ srs_error_t srs_fd_keepalive(int fd) srs_thread_t srs_thread_self() { - return (srs_thread_t)st_thread_self(); + return (srs_thread_t)co_self(); } srs_error_t srs_tcp_connect(string server, int port, srs_utime_t tm, srs_netfd_t* pstfd) { - st_utime_t timeout = ST_UTIME_NO_TIMEOUT; + srs_utime_t timeout = SRS_UTIME_NO_TIMEOUT; if (tm != SRS_UTIME_NO_TIMEOUT) { timeout = tm; } - - *pstfd = NULL; - srs_netfd_t stfd = NULL; + + (void)timeout; char sport[8]; snprintf(sport, sizeof(sport), "%d", port); @@ -175,20 +161,15 @@ srs_error_t srs_tcp_connect(string server, int port, srs_utime_t tm, srs_netfd_t if(sock == -1){ return srs_error_new(ERROR_SOCKET_CREATE, "create socket"); } + + *pstfd = sock; - srs_assert(!stfd); - stfd = st_netfd_open_socket(sock); - if(stfd == NULL){ - ::close(sock); - return srs_error_new(ERROR_ST_OPEN_SOCKET, "open socket"); - } - - if (st_connect((st_netfd_t)stfd, r->ai_addr, r->ai_addrlen, timeout) == -1){ - srs_close_stfd(stfd); + // TODO: timeout + if (connect(sock, r->ai_addr, r->ai_addrlen) == -1) { + srs_close_stfd(sock); return srs_error_new(ERROR_ST_CONNECT, "connect to %s:%d", server.c_str(), port); } - *pstfd = stfd; return srs_success; } @@ -214,7 +195,7 @@ srs_error_t do_srs_tcp_listen(int fd, addrinfo* r, srs_netfd_t* pfd) return srs_error_wrap(err, "set reuseport"); } - if (bind(fd, r->ai_addr, r->ai_addrlen) == -1) { + if (::bind(fd, r->ai_addr, r->ai_addrlen) == -1) { return srs_error_new(ERROR_SOCKET_BIND, "bind"); } @@ -222,10 +203,6 @@ srs_error_t do_srs_tcp_listen(int fd, addrinfo* r, srs_netfd_t* pfd) return srs_error_new(ERROR_SOCKET_LISTEN, "listen"); } - if ((*pfd = srs_netfd_open_socket(fd)) == NULL){ - return srs_error_new(ERROR_ST_OPEN_SOCKET, "st open"); - } - return err; } @@ -255,11 +232,15 @@ srs_error_t srs_tcp_listen(std::string ip, int port, srs_netfd_t* pfd) r->ai_family, r->ai_socktype, r->ai_protocol); } + set_fd_nonblock(fd); + if ((err = do_srs_tcp_listen(fd, r, pfd)) != srs_success) { ::close(fd); return srs_error_wrap(err, "fd=%d", fd); } + *pfd = fd; + return err; } @@ -283,10 +264,6 @@ srs_error_t do_srs_udp_listen(int fd, addrinfo* r, srs_netfd_t* pfd) return srs_error_new(ERROR_SOCKET_BIND, "bind"); } - if ((*pfd = srs_netfd_open_socket(fd)) == NULL){ - return srs_error_new(ERROR_ST_OPEN_SOCKET, "st open"); - } - return err; } @@ -326,85 +303,121 @@ srs_error_t srs_udp_listen(std::string ip, int port, srs_netfd_t* pfd) srs_cond_t srs_cond_new() { - return (srs_cond_t)st_cond_new(); + return (srs_cond_t)co_cond_alloc(); } int srs_cond_destroy(srs_cond_t cond) { - return st_cond_destroy((st_cond_t)cond); + return co_cond_free((stCoCond_t*)cond); } int srs_cond_wait(srs_cond_t cond) { - return st_cond_wait((st_cond_t)cond); + return co_cond_timedwait((stCoCond_t*)cond, -1); } int srs_cond_timedwait(srs_cond_t cond, srs_utime_t timeout) { - return st_cond_timedwait((st_cond_t)cond, (st_utime_t)timeout); + return co_cond_timedwait((stCoCond_t*)cond, timeout); } int srs_cond_signal(srs_cond_t cond) { - return st_cond_signal((st_cond_t)cond); + return co_cond_signal((stCoCond_t*)cond); } srs_mutex_t srs_mutex_new() { - return (srs_mutex_t)st_mutex_new(); + return NULL; } int srs_mutex_destroy(srs_mutex_t mutex) { - if (!mutex) { - return 0; - } - return st_mutex_destroy((st_mutex_t)mutex); + return 0; } int srs_mutex_lock(srs_mutex_t mutex) { - return st_mutex_lock((st_mutex_t)mutex); + return 0; } int srs_mutex_unlock(srs_mutex_t mutex) { - return st_mutex_unlock((st_mutex_t)mutex); + return 0; } int srs_netfd_fileno(srs_netfd_t stfd) { - return st_netfd_fileno((st_netfd_t)stfd); + return stfd; } int srs_usleep(srs_utime_t usecs) { - return st_usleep((st_utime_t)usecs); + // XXX: libco has no API like co_sleep, use co_cond_timedwait instead + stCoCond_t* cond = co_cond_alloc(); + co_cond_timedwait(cond, usecs/1000.0); + + return 0; } srs_netfd_t srs_netfd_open_socket(int osfd) { - return (srs_netfd_t)st_netfd_open_socket(osfd); + set_fd_nonblock(osfd); + return osfd; } srs_netfd_t srs_netfd_open(int osfd) { - return (srs_netfd_t)st_netfd_open(osfd); + set_fd_nonblock(osfd); + return osfd; } int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, int *fromlen, srs_utime_t timeout) { - return st_recvfrom((st_netfd_t)stfd, buf, len, from, fromlen, (st_utime_t)timeout); + // TODO: timeout + return recvfrom(stfd, buf, len, 0, from, (socklen_t*)fromlen); } srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout) { - return (srs_netfd_t)st_accept((st_netfd_t)stfd, addr, addrlen, (st_utime_t)timeout); + struct pollfd pf = { 0 }; + pf.fd = stfd; + pf.events = (POLLIN | POLLERR | POLLHUP); + + srs_utime_t atm = timeout; + if (atm != SRS_UTIME_NO_TIMEOUT) + atm /= 1000; + + int client_fd; + while ((client_fd = accept(stfd, addr, (socklen_t*)addrlen)) < 0) { + if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { + return -1; + } + + co_poll(co_get_epoll_ct(), &pf, 1, atm); + } + + set_fd_nonblock(client_fd); + + return client_fd; } ssize_t srs_read(srs_netfd_t stfd, void *buf, size_t nbyte, srs_utime_t timeout) { - return st_read((st_netfd_t)stfd, buf, nbyte, (st_utime_t)timeout); + struct pollfd pf = { 0 }; + pf.fd = stfd; + pf.events = (POLLIN | POLLERR | POLLHUP); + + int n; + while ((n = ::read(stfd, buf, nbyte)) < 0) { + if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { + return -1; + } + + co_poll(co_get_epoll_ct(), &pf, 1, timeout); + } + + return n; } bool srs_is_never_timeout(srs_utime_t tm) @@ -414,7 +427,6 @@ bool srs_is_never_timeout(srs_utime_t tm) SrsStSocket::SrsStSocket() { - stfd = NULL; stm = rtm = SRS_UTIME_NO_TIMEOUT; rbytes = sbytes = 0; } @@ -465,15 +477,15 @@ srs_error_t SrsStSocket::read(void* buf, size_t size, ssize_t* nread) ssize_t nb_read; if (rtm == SRS_UTIME_NO_TIMEOUT) { - nb_read = st_read((st_netfd_t)stfd, buf, size, ST_UTIME_NO_TIMEOUT); + nb_read = srs_read(stfd, buf, size, -1); } else { - nb_read = st_read((st_netfd_t)stfd, buf, size, rtm); + nb_read = srs_read(stfd, buf, size, rtm / 1000); } if (nread) { *nread = nb_read; } - + // On success a non-negative integer indicating the number of bytes actually read is returned // (a value of 0 means the network connection is closed or end of file is reached). // Otherwise, a value of -1 is returned and errno is set to indicate the error. @@ -499,13 +511,25 @@ srs_error_t SrsStSocket::read_fully(void* buf, size_t size, ssize_t* nread) { srs_error_t err = srs_success; - ssize_t nb_read; - if (rtm == SRS_UTIME_NO_TIMEOUT) { - nb_read = st_read_fully((st_netfd_t)stfd, buf, size, ST_UTIME_NO_TIMEOUT); - } else { - nb_read = st_read_fully((st_netfd_t)stfd, buf, size, rtm); + ssize_t nb_read = 0; + + int wait_read_bytes = size; + while (wait_read_bytes > 0) { + int bytes = ::read(stfd, buf, wait_read_bytes); + if (bytes > 0) { + nb_read += bytes; + wait_read_bytes -= bytes; + if (nb_read == (ssize_t)size) { + break; + } + } else if (bytes == 0) { + break; + } else { + if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) + break; + } } - + if (nread) { *nread = nb_read; } @@ -535,11 +559,30 @@ srs_error_t SrsStSocket::write(void* buf, size_t size, ssize_t* nwrite) { srs_error_t err = srs_success; - ssize_t nb_write; - if (stm == SRS_UTIME_NO_TIMEOUT) { - nb_write = st_write((st_netfd_t)stfd, buf, size, ST_UTIME_NO_TIMEOUT); - } else { - nb_write = st_write((st_netfd_t)stfd, buf, size, stm); + ssize_t nb_write = 0; + + struct pollfd pf = { 0 }; + pf.fd = stfd; + pf.events = (POLLOUT | POLLERR | POLLHUP); + + srs_utime_t wtm = stm; + if (wtm != SRS_UTIME_NO_TIMEOUT) + wtm = stm / 1000; + + int wait_write_bytes = size; + while (wait_write_bytes > 0) { + int n = 0; + if ((n = ::write(stfd, buf, size)) < 0) { + if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { + break; + } + + co_poll(co_get_epoll_ct(), &pf, 1, wtm); + continue; + } + + wait_write_bytes -= n; + nb_write += n; } if (nwrite) { @@ -566,11 +609,44 @@ srs_error_t SrsStSocket::writev(const iovec *iov, int iov_size, ssize_t* nwrite) { srs_error_t err = srs_success; - ssize_t nb_write; - if (stm == SRS_UTIME_NO_TIMEOUT) { - nb_write = st_writev((st_netfd_t)stfd, iov, iov_size, ST_UTIME_NO_TIMEOUT); - } else { - nb_write = st_writev((st_netfd_t)stfd, iov, iov_size, stm); + srs_utime_t tm = stm; + if (tm != SRS_UTIME_NO_TIMEOUT) + tm = stm / 1000; + + int wait_write_bytes = 0; + for (int i = 0; i < iov_size; ++i) + wait_write_bytes += iov[i].iov_len; + + ssize_t nb_write = 0; + iovec* cur_iov = (iovec*)iov; + int cur_iov_size = iov_size; + + while (wait_write_bytes > 0) { + int n = 0; + if ((n = ::writev(stfd, cur_iov, cur_iov_size)) < 0) { + if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { + break; + } + + struct pollfd pf = {0}; + pf.fd = stfd; + pf.events = (POLLOUT | POLLERR | POLLHUP); + + co_poll(co_get_epoll_ct(), &pf, 1, tm); + continue; + } + + wait_write_bytes -= n; + nb_write += n; + + while (n >= (int)cur_iov->iov_len) { + n -= cur_iov->iov_len; + --cur_iov_size; + ++cur_iov; + } + // FIXME: no modify iov + (*cur_iov).iov_base = (void*)((char*)(*cur_iov).iov_base + n); + (*cur_iov).iov_len -= n; } if (nwrite) { @@ -595,7 +671,7 @@ srs_error_t SrsStSocket::writev(const iovec *iov, int iov_size, ssize_t* nwrite) SrsTcpClient::SrsTcpClient(string h, int p, srs_utime_t tm) { - stfd = NULL; + stfd = -1; io = new SrsStSocket(); host = h; @@ -616,7 +692,7 @@ srs_error_t SrsTcpClient::connect() close(); - srs_assert(stfd == NULL); + srs_assert(stfd == -1); if ((err = srs_tcp_connect(host, port, timeout, &stfd)) != srs_success) { return srs_error_wrap(err, "tcp: connect %s:%d to=%dms", host.c_str(), port, srsu2msi(timeout)); } diff --git a/trunk/src/service/srs_service_st.hpp b/trunk/src/service/srs_service_st.hpp index 510b9ba8a..ca6da3f7c 100644 --- a/trunk/src/service/srs_service_st.hpp +++ b/trunk/src/service/srs_service_st.hpp @@ -31,7 +31,7 @@ #include // Wrap for coroutine. -typedef void* srs_netfd_t; +typedef int srs_netfd_t; typedef void* srs_thread_t; typedef void* srs_cond_t; typedef void* srs_mutex_t; From 1c74083de84315a105f5a4e535d657e0fbdbb6df Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Mon, 17 Feb 2020 15:30:20 +0800 Subject: [PATCH 02/21] Revert "use libco instead of state-thread(st), still have some bug" This reverts commit 7c8a35aea9b6108bffdc39390cdb00db525be2ac. --- trunk/3rdparty/libco/.gitignore | 1 - trunk/3rdparty/libco/CMakeLists.txt | 55 - trunk/3rdparty/libco/LICENSE.txt | Bin 18962 -> 0 bytes trunk/3rdparty/libco/Makefile | 84 - trunk/3rdparty/libco/co.mk | 85 - trunk/3rdparty/libco/co_closure.h | 96 - trunk/3rdparty/libco/co_epoll.cpp | 318 -- trunk/3rdparty/libco/co_epoll.h | 93 - trunk/3rdparty/libco/co_hook_sys_call.cpp | 1003 ------ trunk/3rdparty/libco/co_routine.cpp | 1196 ------- trunk/3rdparty/libco/co_routine.h | 93 - trunk/3rdparty/libco/co_routine_inner.h | 111 - trunk/3rdparty/libco/co_routine_specific.h | 86 - trunk/3rdparty/libco/coctx.cpp | 132 - trunk/3rdparty/libco/coctx.h | 42 - trunk/3rdparty/libco/coctx_swap.S | 83 - trunk/3rdparty/libco/example_closure.cpp | 91 - trunk/3rdparty/libco/example_cond.cpp | 83 - trunk/3rdparty/libco/example_copystack.cpp | 61 - trunk/3rdparty/libco/example_echocli.cpp | 218 -- trunk/3rdparty/libco/example_echosvr.cpp | 255 -- trunk/3rdparty/libco/example_poll.cpp | 211 -- trunk/3rdparty/libco/example_setenv.cpp | 89 - trunk/3rdparty/libco/example_specific.cpp | 61 - trunk/3rdparty/libco/example_thread.cpp | 56 - trunk/3rdparty/st-srs/.gitignore | 4 + trunk/3rdparty/st-srs/Makefile | 474 +++ trunk/3rdparty/st-srs/README | 394 +++ trunk/3rdparty/st-srs/README.md | 88 + trunk/3rdparty/st-srs/common.h | 479 +++ trunk/3rdparty/st-srs/docs/fig.gif | Bin 0 -> 5374 bytes trunk/3rdparty/st-srs/docs/notes.html | 434 +++ trunk/3rdparty/st-srs/docs/reference.html | 3120 +++++++++++++++++ trunk/3rdparty/st-srs/docs/st.html | 504 +++ trunk/3rdparty/st-srs/docs/timeout_heap.txt | 60 + trunk/3rdparty/st-srs/event.c | 1413 ++++++++ trunk/3rdparty/st-srs/examples/Makefile | 115 + trunk/3rdparty/st-srs/examples/README | 98 + trunk/3rdparty/st-srs/examples/error.c | 168 + trunk/3rdparty/st-srs/examples/lookupdns.c | 103 + trunk/3rdparty/st-srs/examples/proxy.c | 541 +++ trunk/3rdparty/st-srs/examples/res.c | 305 ++ trunk/3rdparty/st-srs/examples/server.c | 1025 ++++++ trunk/3rdparty/st-srs/extensions/Makefile | 91 + trunk/3rdparty/st-srs/extensions/README | 42 + trunk/3rdparty/st-srs/extensions/common.h | 77 + trunk/3rdparty/st-srs/extensions/dnscache.c | 190 + trunk/3rdparty/st-srs/extensions/dnsres.c | 305 ++ trunk/3rdparty/st-srs/extensions/lrucache.c | 343 ++ .../st-srs/extensions/print_stk.patch | 367 ++ trunk/3rdparty/st-srs/extensions/stx.h | 91 + trunk/3rdparty/st-srs/extensions/stx_fileio.c | 197 ++ trunk/3rdparty/st-srs/extensions/stx_fileio.h | 52 + trunk/3rdparty/st-srs/extensions/testdns.c | 112 + trunk/3rdparty/st-srs/io.c | 769 ++++ trunk/3rdparty/st-srs/key.c | 121 + trunk/3rdparty/st-srs/libst.def | 51 + trunk/3rdparty/st-srs/md.S | 644 ++++ trunk/3rdparty/st-srs/md.h | 641 ++++ trunk/3rdparty/st-srs/osguess.sh | 45 + trunk/3rdparty/st-srs/public.h | 166 + trunk/3rdparty/st-srs/sched.c | 705 ++++ trunk/3rdparty/st-srs/st.pc.in | 10 + trunk/3rdparty/st-srs/st.spec | 79 + trunk/3rdparty/st-srs/stk.c | 173 + trunk/3rdparty/st-srs/sync.c | 368 ++ trunk/auto/depends.sh | 31 +- trunk/configure | 34 +- trunk/research/st/Makefile | 100 + trunk/research/st/common.h | 445 +++ trunk/research/st/event.c | 483 +++ trunk/research/st/io.c | 792 +++++ trunk/research/st/key.c | 116 + trunk/research/st/md.S | 151 + trunk/research/st/md.h | 193 + trunk/research/st/public.h | 164 + trunk/research/st/sched.c | 680 ++++ trunk/research/st/srs.c | 497 +++ trunk/research/st/st/init | 3 + trunk/research/st/st/st.upp | 18 + trunk/research/st/stk.c | 169 + trunk/research/st/sync.c | 352 ++ trunk/src/app/srs_app_listener.cpp | 10 +- trunk/src/app/srs_app_server.cpp | 12 +- trunk/src/app/srs_app_st.cpp | 27 +- trunk/src/app/srs_app_st.hpp | 3 - trunk/src/service/srs_service_st.cpp | 260 +- trunk/src/service/srs_service_st.hpp | 2 +- 88 files changed, 19273 insertions(+), 4836 deletions(-) delete mode 100644 trunk/3rdparty/libco/.gitignore delete mode 100644 trunk/3rdparty/libco/CMakeLists.txt delete mode 100644 trunk/3rdparty/libco/LICENSE.txt delete mode 100644 trunk/3rdparty/libco/Makefile delete mode 100644 trunk/3rdparty/libco/co.mk delete mode 100644 trunk/3rdparty/libco/co_closure.h delete mode 100644 trunk/3rdparty/libco/co_epoll.cpp delete mode 100644 trunk/3rdparty/libco/co_epoll.h delete mode 100644 trunk/3rdparty/libco/co_hook_sys_call.cpp delete mode 100644 trunk/3rdparty/libco/co_routine.cpp delete mode 100644 trunk/3rdparty/libco/co_routine.h delete mode 100644 trunk/3rdparty/libco/co_routine_inner.h delete mode 100644 trunk/3rdparty/libco/co_routine_specific.h delete mode 100644 trunk/3rdparty/libco/coctx.cpp delete mode 100644 trunk/3rdparty/libco/coctx.h delete mode 100644 trunk/3rdparty/libco/coctx_swap.S delete mode 100644 trunk/3rdparty/libco/example_closure.cpp delete mode 100644 trunk/3rdparty/libco/example_cond.cpp delete mode 100644 trunk/3rdparty/libco/example_copystack.cpp delete mode 100644 trunk/3rdparty/libco/example_echocli.cpp delete mode 100644 trunk/3rdparty/libco/example_echosvr.cpp delete mode 100644 trunk/3rdparty/libco/example_poll.cpp delete mode 100644 trunk/3rdparty/libco/example_setenv.cpp delete mode 100644 trunk/3rdparty/libco/example_specific.cpp delete mode 100644 trunk/3rdparty/libco/example_thread.cpp create mode 100644 trunk/3rdparty/st-srs/.gitignore create mode 100644 trunk/3rdparty/st-srs/Makefile create mode 100644 trunk/3rdparty/st-srs/README create mode 100644 trunk/3rdparty/st-srs/README.md create mode 100644 trunk/3rdparty/st-srs/common.h create mode 100644 trunk/3rdparty/st-srs/docs/fig.gif create mode 100644 trunk/3rdparty/st-srs/docs/notes.html create mode 100644 trunk/3rdparty/st-srs/docs/reference.html create mode 100644 trunk/3rdparty/st-srs/docs/st.html create mode 100644 trunk/3rdparty/st-srs/docs/timeout_heap.txt create mode 100644 trunk/3rdparty/st-srs/event.c create mode 100644 trunk/3rdparty/st-srs/examples/Makefile create mode 100644 trunk/3rdparty/st-srs/examples/README create mode 100644 trunk/3rdparty/st-srs/examples/error.c create mode 100644 trunk/3rdparty/st-srs/examples/lookupdns.c create mode 100644 trunk/3rdparty/st-srs/examples/proxy.c create mode 100644 trunk/3rdparty/st-srs/examples/res.c create mode 100644 trunk/3rdparty/st-srs/examples/server.c create mode 100644 trunk/3rdparty/st-srs/extensions/Makefile create mode 100644 trunk/3rdparty/st-srs/extensions/README create mode 100644 trunk/3rdparty/st-srs/extensions/common.h create mode 100644 trunk/3rdparty/st-srs/extensions/dnscache.c create mode 100644 trunk/3rdparty/st-srs/extensions/dnsres.c create mode 100644 trunk/3rdparty/st-srs/extensions/lrucache.c create mode 100644 trunk/3rdparty/st-srs/extensions/print_stk.patch create mode 100644 trunk/3rdparty/st-srs/extensions/stx.h create mode 100644 trunk/3rdparty/st-srs/extensions/stx_fileio.c create mode 100644 trunk/3rdparty/st-srs/extensions/stx_fileio.h create mode 100644 trunk/3rdparty/st-srs/extensions/testdns.c create mode 100644 trunk/3rdparty/st-srs/io.c create mode 100644 trunk/3rdparty/st-srs/key.c create mode 100644 trunk/3rdparty/st-srs/libst.def create mode 100644 trunk/3rdparty/st-srs/md.S create mode 100644 trunk/3rdparty/st-srs/md.h create mode 100644 trunk/3rdparty/st-srs/osguess.sh create mode 100644 trunk/3rdparty/st-srs/public.h create mode 100644 trunk/3rdparty/st-srs/sched.c create mode 100644 trunk/3rdparty/st-srs/st.pc.in create mode 100644 trunk/3rdparty/st-srs/st.spec create mode 100644 trunk/3rdparty/st-srs/stk.c create mode 100644 trunk/3rdparty/st-srs/sync.c create mode 100755 trunk/research/st/Makefile create mode 100644 trunk/research/st/common.h create mode 100644 trunk/research/st/event.c create mode 100644 trunk/research/st/io.c create mode 100644 trunk/research/st/key.c create mode 100755 trunk/research/st/md.S create mode 100644 trunk/research/st/md.h create mode 100644 trunk/research/st/public.h create mode 100755 trunk/research/st/sched.c create mode 100644 trunk/research/st/srs.c create mode 100644 trunk/research/st/st/init create mode 100755 trunk/research/st/st/st.upp create mode 100644 trunk/research/st/stk.c create mode 100644 trunk/research/st/sync.c diff --git a/trunk/3rdparty/libco/.gitignore b/trunk/3rdparty/libco/.gitignore deleted file mode 100644 index 42061c01a..000000000 --- a/trunk/3rdparty/libco/.gitignore +++ /dev/null @@ -1 +0,0 @@ -README.md \ No newline at end of file diff --git a/trunk/3rdparty/libco/CMakeLists.txt b/trunk/3rdparty/libco/CMakeLists.txt deleted file mode 100644 index 04399a55a..000000000 --- a/trunk/3rdparty/libco/CMakeLists.txt +++ /dev/null @@ -1,55 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -project(libco) - -# This for mac osx only -set(CMAKE_MACOSX_RPATH 0) - -# Set lib version -set(LIBCO_VERSION 0.5) - -# Set cflags -set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -g -fno-strict-aliasing -O2 -Wall -export-dynamic -Wall -pipe -D_GNU_SOURCE -D_REENTRANT -fPIC -Wno-deprecated -m64) - -# Use c and asm -enable_language(C ASM) - -# Add source files -set(SOURCE_FILES - co_epoll.cpp - co_hook_sys_call.cpp - co_routine.cpp - coctx.cpp - coctx_swap.S) - -# Add static and shared library target -add_library(colib_static STATIC ${SOURCE_FILES}) -add_library(colib_shared SHARED ${SOURCE_FILES}) - -# Set library output name -set_target_properties(colib_static PROPERTIES OUTPUT_NAME colib) -set_target_properties(colib_shared PROPERTIES OUTPUT_NAME colib) - -set_target_properties(colib_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) -set_target_properties(colib_shared PROPERTIES CLEAN_DIRECT_OUTPUT 1) - -# Set shared library version, will generate libcolib.${LIBCO_VERSION}.so and a symbol link named libcolib.so -# For mac osx, the extension name will be .dylib -set_target_properties(colib_shared PROPERTIES VERSION ${LIBCO_VERSION} SOVERSION ${LIBCO_VERSION}) - - - -# Macro for add example target -macro(add_example_target EXAMPLE_TARGET) - add_executable("example_${EXAMPLE_TARGET}" "example_${EXAMPLE_TARGET}.cpp") - target_link_libraries("example_${EXAMPLE_TARGET}" colib_static pthread dl) -endmacro(add_example_target) - -add_example_target(closure) -add_example_target(cond) -add_example_target(copystack) -add_example_target(echocli) -add_example_target(echosvr) -add_example_target(poll) -add_example_target(setenv) -add_example_target(specific) -add_example_target(thread) diff --git a/trunk/3rdparty/libco/LICENSE.txt b/trunk/3rdparty/libco/LICENSE.txt deleted file mode 100644 index bff49d4bc3a55637e1ed4bd7eeb9834a2b16dbd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18962 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 diff --git a/trunk/3rdparty/libco/Makefile b/trunk/3rdparty/libco/Makefile deleted file mode 100644 index 7db374946..000000000 --- a/trunk/3rdparty/libco/Makefile +++ /dev/null @@ -1,84 +0,0 @@ -# -# Tencent is pleased to support the open source community by making Libco available. -# -# Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - - -COMM_MAKE = 1 -COMM_ECHO = 1 -version=0.5 -v=debug -include co.mk - -########## options ########## -CFLAGS += -g -fno-strict-aliasing -O2 -Wall -export-dynamic \ - -Wall -pipe -D_GNU_SOURCE -D_REENTRANT -fPIC -Wno-deprecated -m64 - -UNAME := $(shell uname -s) - -ifeq ($(UNAME), FreeBSD) -LINKS += -g -L./lib -lcolib -lpthread -else -LINKS += -g -L./lib -lcolib -lpthread -ldl -endif - -COLIB_OBJS=co_epoll.o co_routine.o co_hook_sys_call.o coctx_swap.o coctx.o -#co_swapcontext.o - -PROGS = colib example_poll example_echosvr example_echocli example_thread example_cond example_specific example_copystack example_closure - -all:$(PROGS) - -colib:libcolib.a libcolib.so - -libcolib.a: $(COLIB_OBJS) - $(ARSTATICLIB) -libcolib.so: $(COLIB_OBJS) - $(BUILDSHARELIB) - -example_echosvr:example_echosvr.o - $(BUILDEXE) -example_echocli:example_echocli.o - $(BUILDEXE) -example_thread:example_thread.o - $(BUILDEXE) -example_poll:example_poll.o - $(BUILDEXE) -example_exit:example_exit.o - $(BUILDEXE) -example_cond:example_cond.o - $(BUILDEXE) -example_specific:example_specific.o - $(BUILDEXE) -example_copystack:example_copystack.o - $(BUILDEXE) -example_setenv:example_setenv.o - $(BUILDEXE) -example_closure:example_closure.o - $(BUILDEXE) - -dist: clean libco-$(version).src.tar.gz - -libco-$(version).src.tar.gz: - @find . -type f | grep -v CVS | grep -v .svn | sed s:^./:libco-$(version)/: > MANIFEST - @(cd ..; ln -s libco_pub libco-$(version)) - (cd ..; tar cvf - `cat libco_pub/MANIFEST` | gzip > libco_pub/libco-$(version).src.tar.gz) - @(cd ..; rm libco-$(version)) - -clean: - $(CLEAN) *.o $(PROGS) - rm -fr MANIFEST lib solib libco-$(version).src.tar.gz libco-$(version) - diff --git a/trunk/3rdparty/libco/co.mk b/trunk/3rdparty/libco/co.mk deleted file mode 100644 index 29658b511..000000000 --- a/trunk/3rdparty/libco/co.mk +++ /dev/null @@ -1,85 +0,0 @@ -# -# Tencent is pleased to support the open source community by making Libco available. -# -# Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - - - -##### Makefile Rules ########## -MAIL_ROOT=. -SRCROOT=. - -##define the compliers -CPP = $(CXX) -AR = ar -rc -RANLIB = ranlib - -CPPSHARE = $(CPP) -fPIC -shared -O2 -pipe -L$(SRCROOT)/solib/ -o -CSHARE = $(CC) -fPIC -shared -O2 -pipe -L$(SRCROOT)/solib/ -o - -ifeq ($v,release) -CFLAGS= -O2 $(INCLS) -fPIC -DLINUX -pipe -Wno-deprecated -c -else -CFLAGS= -g $(INCLS) -fPIC -DLINUX -pipe -c -fno-inline -endif - -ifneq ($v,release) -BFLAGS= -g -endif - -STATICLIBPATH=$(SRCROOT)/lib -DYNAMICLIBPATH=$(SRCROOT)/solib - -INCLS += -I$(SRCROOT) - -## default links -ifeq ($(LINKS_DYNAMIC), 1) -LINKS += -L$(DYNAMICLIBPATH) -L$(STATICLIBPATH) -else -LINKS += -L$(STATICLIBPATH) -endif - -CPPSRCS = $(wildcard *.cpp) -CSRCS = $(wildcard *.c) -CPPOBJS = $(patsubst %.cpp,%.o,$(CPPSRCS)) -COBJS = $(patsubst %.c,%.o,$(CSRCS)) - -SRCS = $(CPPSRCS) $(CSRCS) -OBJS = $(CPPOBJS) $(COBJS) - -CPPCOMPI=$(CPP) $(CFLAGS) -Wno-deprecated -CCCOMPI=$(CC) $(CFLAGS) - -BUILDEXE = $(CPP) $(BFLAGS) -o $@ $^ $(LINKS) -CLEAN = rm -f *.o - -CPPCOMPILE = $(CPPCOMPI) $< $(FLAGS) $(INCLS) $(MTOOL_INCL) -o $@ -CCCOMPILE = $(CCCOMPI) $< $(FLAGS) $(INCLS) $(MTOOL_INCL) -o $@ - -ARSTATICLIB = $(AR) $@.tmp $^ $(AR_FLAGS); \ - if [ $$? -ne 0 ]; then exit 1; fi; \ - test -d $(STATICLIBPATH) || mkdir -p $(STATICLIBPATH); \ - mv -f $@.tmp $(STATICLIBPATH)/$@; - -BUILDSHARELIB = $(CPPSHARE) $@.tmp $^ $(BS_FLAGS); \ - if [ $$? -ne 0 ]; then exit 1; fi; \ - test -d $(DYNAMICLIBPATH) || mkdir -p $(DYNAMICLIBPATH); \ - mv -f $@.tmp $(DYNAMICLIBPATH)/$@; - -.cpp.o: - $(CPPCOMPILE) -.c.o: - $(CCCOMPILE) diff --git a/trunk/3rdparty/libco/co_closure.h b/trunk/3rdparty/libco/co_closure.h deleted file mode 100644 index 87d5c6891..000000000 --- a/trunk/3rdparty/libco/co_closure.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#ifndef __CO_CLOSURE_H__ -#define __CO_CLOSURE_H__ -struct stCoClosure_t -{ -public: - virtual void exec() = 0; -}; - -//1.base -//-- 1.1 comac_argc - -#define comac_get_args_cnt( ... ) comac_arg_n( __VA_ARGS__ ) -#define comac_arg_n( _0,_1,_2,_3,_4,_5,_6,_7,N,...) N -#define comac_args_seqs() 7,6,5,4,3,2,1,0 -#define comac_join_1( x,y ) x##y - -#define comac_argc( ... ) comac_get_args_cnt( 0,##__VA_ARGS__,comac_args_seqs() ) -#define comac_join( x,y) comac_join_1( x,y ) - -//-- 1.2 repeat -#define repeat_0( fun,a,... ) -#define repeat_1( fun,a,... ) fun( 1,a,__VA_ARGS__ ) repeat_0( fun,__VA_ARGS__ ) -#define repeat_2( fun,a,... ) fun( 2,a,__VA_ARGS__ ) repeat_1( fun,__VA_ARGS__ ) -#define repeat_3( fun,a,... ) fun( 3,a,__VA_ARGS__ ) repeat_2( fun,__VA_ARGS__ ) -#define repeat_4( fun,a,... ) fun( 4,a,__VA_ARGS__ ) repeat_3( fun,__VA_ARGS__ ) -#define repeat_5( fun,a,... ) fun( 5,a,__VA_ARGS__ ) repeat_4( fun,__VA_ARGS__ ) -#define repeat_6( fun,a,... ) fun( 6,a,__VA_ARGS__ ) repeat_5( fun,__VA_ARGS__ ) - -#define repeat( n,fun,... ) comac_join( repeat_,n )( fun,__VA_ARGS__) - -//2.implement -#if __cplusplus <= 199711L -#define decl_typeof( i,a,... ) typedef typeof( a ) typeof_##a; -#else -#define decl_typeof( i,a,... ) typedef decltype( a ) typeof_##a; -#endif -#define impl_typeof( i,a,... ) typeof_##a & a; -#define impl_typeof_cpy( i,a,... ) typeof_##a a; -#define con_param_typeof( i,a,... ) typeof_##a & a##r, -#define param_init_typeof( i,a,... ) a(a##r), - - -//2.1 reference - -#define co_ref( name,... )\ -repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )\ -class type_##name\ -{\ -public:\ - repeat( comac_argc(__VA_ARGS__) ,impl_typeof,__VA_ARGS__ )\ - int _member_cnt;\ - type_##name( \ - repeat( comac_argc(__VA_ARGS__),con_param_typeof,__VA_ARGS__ ) ... ): \ - repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__)) \ - {}\ -} name( __VA_ARGS__ ) ; - - -//2.2 function - -#define co_func(name,...)\ -repeat( comac_argc(__VA_ARGS__) ,decl_typeof,__VA_ARGS__ )\ -class name:public stCoClosure_t\ -{\ -public:\ - repeat( comac_argc(__VA_ARGS__) ,impl_typeof_cpy,__VA_ARGS__ )\ - int _member_cnt;\ -public:\ - name( repeat( comac_argc(__VA_ARGS__),con_param_typeof,__VA_ARGS__ ) ... ): \ - repeat( comac_argc(__VA_ARGS__),param_init_typeof,__VA_ARGS__ ) _member_cnt(comac_argc(__VA_ARGS__))\ - {}\ - void exec() - -#define co_func_end } - - -#endif - diff --git a/trunk/3rdparty/libco/co_epoll.cpp b/trunk/3rdparty/libco/co_epoll.cpp deleted file mode 100644 index 12726218d..000000000 --- a/trunk/3rdparty/libco/co_epoll.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "co_epoll.h" -#include -#include -#include -#include - -#if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) - -int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ) -{ - return epoll_wait( epfd,events->events,maxevents,timeout ); -} -int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ev ) -{ - return epoll_ctl( epfd,op,fd,ev ); -} -int co_epoll_create( int size ) -{ - return epoll_create( size ); -} - -struct co_epoll_res *co_epoll_res_alloc( int n ) -{ - struct co_epoll_res * ptr = - (struct co_epoll_res *)malloc( sizeof( struct co_epoll_res ) ); - - ptr->size = n; - ptr->events = (struct epoll_event*)calloc( 1,n * sizeof( struct epoll_event ) ); - - return ptr; - -} -void co_epoll_res_free( struct co_epoll_res * ptr ) -{ - if( !ptr ) return; - if( ptr->events ) free( ptr->events ); - free( ptr ); -} - -#else -class clsFdMap // million of fd , 1024 * 1024 -{ -private: - static const int row_size = 1024; - static const int col_size = 1024; - - void **m_pp[ 1024 ]; -public: - clsFdMap() - { - memset( m_pp,0,sizeof(m_pp) ); - } - ~clsFdMap() - { - for(int i=0;i= sizeof(m_pp)/sizeof(m_pp[0]) ) - { - assert( __LINE__ == 0 ); - return -__LINE__; - } - if( !m_pp[ idx ] ) - { - m_pp[ idx ] = (void**)calloc( 1,sizeof(void*) * col_size ); - } - m_pp[ idx ][ fd % col_size ] = (void*)ptr; - return 0; - } - inline void *get( int fd ) - { - int idx = fd / row_size; - if( idx < 0 || idx >= sizeof(m_pp)/sizeof(m_pp[0]) ) - { - return NULL; - } - void **lp = m_pp[ idx ]; - if( !lp ) return NULL; - - return lp[ fd % col_size ]; - } -}; - -__thread clsFdMap *s_fd_map = NULL; - -static inline clsFdMap *get_fd_map() -{ - if( !s_fd_map ) - { - s_fd_map = new clsFdMap(); - } - return s_fd_map; -} - -struct kevent_pair_t -{ - int fire_idx; - int events; - uint64_t u64; -}; -int co_epoll_create( int size ) -{ - return kqueue(); -} -int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ) -{ - struct timespec t = { 0 }; - if( timeout > 0 ) - { - t.tv_sec = timeout; - } - int ret = kevent( epfd, - NULL, 0, //register null - events->eventlist, maxevents,//just retrival - ( -1 == timeout ) ? NULL : &t ); - int j = 0; - for(int i=0;ieventlist[i]; - struct kevent_pair_t *ptr = (struct kevent_pair_t*)kev.udata; - struct epoll_event *ev = events->events + i; - if( 0 == ptr->fire_idx ) - { - ptr->fire_idx = i + 1; - memset( ev,0,sizeof(*ev) ); - ++j; - } - else - { - ev = events->events + ptr->fire_idx - 1; - } - if( EVFILT_READ == kev.filter ) - { - ev->events |= EPOLLIN; - } - else if( EVFILT_WRITE == kev.filter ) - { - ev->events |= EPOLLOUT; - } - ev->data.u64 = ptr->u64; - } - for(int i=0;ieventlist[i].udata) )->fire_idx = 0; - } - return j; -} -int co_epoll_del( int epfd,int fd ) -{ - - struct timespec t = { 0 }; - struct kevent_pair_t *ptr = ( struct kevent_pair_t* )get_fd_map()->get( fd ); - if( !ptr ) return 0; - if( EPOLLIN & ptr->events ) - { - struct kevent kev = { 0 }; - kev.ident = fd; - kev.filter = EVFILT_READ; - kev.flags = EV_DELETE; - kevent( epfd,&kev,1, NULL,0,&t ); - } - if( EPOLLOUT & ptr->events ) - { - struct kevent kev = { 0 }; - kev.ident = fd; - kev.filter = EVFILT_WRITE; - kev.flags = EV_DELETE; - kevent( epfd,&kev,1, NULL,0,&t ); - } - get_fd_map()->clear( fd ); - free( ptr ); - return 0; -} -int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ev ) -{ - if( EPOLL_CTL_DEL == op ) - { - return co_epoll_del( epfd,fd ); - } - - const int flags = ( EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP ); - if( ev->events & ~flags ) - { - return -1; - } - - if( EPOLL_CTL_ADD == op && get_fd_map()->get( fd ) ) - { - errno = EEXIST; - return -1; - } - else if( EPOLL_CTL_MOD == op && !get_fd_map()->get( fd ) ) - { - errno = ENOENT; - return -1; - } - - struct kevent_pair_t *ptr = (struct kevent_pair_t*)get_fd_map()->get( fd ); - if( !ptr ) - { - ptr = (kevent_pair_t*)calloc(1,sizeof(kevent_pair_t)); - get_fd_map()->set( fd,ptr ); - } - - int ret = 0; - struct timespec t = { 0 }; - - // printf("ptr->events 0x%X\n",ptr->events); - - if( EPOLL_CTL_MOD == op ) - { - //1.delete if exists - if( ptr->events & EPOLLIN ) - { - struct kevent kev = { 0 }; - EV_SET( &kev,fd,EVFILT_READ,EV_DELETE,0,0,NULL ); - kevent( epfd, &kev,1, NULL,0, &t ); - } - //1.delete if exists - if( ptr->events & EPOLLOUT ) - { - struct kevent kev = { 0 }; - EV_SET( &kev,fd,EVFILT_WRITE,EV_DELETE,0,0,NULL ); - ret = kevent( epfd, &kev,1, NULL,0, &t ); - // printf("delete write ret %d\n",ret ); - } - } - - do - { - if( ev->events & EPOLLIN ) - { - - //2.add - struct kevent kev = { 0 }; - EV_SET( &kev,fd,EVFILT_READ,EV_ADD,0,0,ptr ); - ret = kevent( epfd, &kev,1, NULL,0, &t ); - if( ret ) break; - } - if( ev->events & EPOLLOUT ) - { - //2.add - struct kevent kev = { 0 }; - EV_SET( &kev,fd,EVFILT_WRITE,EV_ADD,0,0,ptr ); - ret = kevent( epfd, &kev,1, NULL,0, &t ); - if( ret ) break; - } - } while( 0 ); - - if( ret ) - { - get_fd_map()->clear( fd ); - free( ptr ); - return ret; - } - - ptr->events = ev->events; - ptr->u64 = ev->data.u64; - - - return ret; -} - -struct co_epoll_res *co_epoll_res_alloc( int n ) -{ - struct co_epoll_res * ptr = - (struct co_epoll_res *)malloc( sizeof( struct co_epoll_res ) ); - - ptr->size = n; - ptr->events = (struct epoll_event*)calloc( 1,n * sizeof( struct epoll_event ) ); - ptr->eventlist = (struct kevent*)calloc( 1,n * sizeof( struct kevent) ); - - return ptr; -} - -void co_epoll_res_free( struct co_epoll_res * ptr ) -{ - if( !ptr ) return; - if( ptr->events ) free( ptr->events ); - if( ptr->eventlist ) free( ptr->eventlist ); - free( ptr ); -} - -#endif - - diff --git a/trunk/3rdparty/libco/co_epoll.h b/trunk/3rdparty/libco/co_epoll.h deleted file mode 100644 index a1b0e4a7d..000000000 --- a/trunk/3rdparty/libco/co_epoll.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#ifndef __CO_EPOLL_H__ -#define __CO_EPOLL_H__ -#include -#include -#include -#include -#include -#include -#include - -#if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) - -#include - -struct co_epoll_res -{ - int size; - struct epoll_event *events; - struct kevent *eventlist; -}; -int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ); -int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ); -int co_epoll_create( int size ); -struct co_epoll_res *co_epoll_res_alloc( int n ); -void co_epoll_res_free( struct co_epoll_res * ); - -#else - -#include -enum EPOLL_EVENTS -{ - EPOLLIN = 0X001, - EPOLLPRI = 0X002, - EPOLLOUT = 0X004, - - EPOLLERR = 0X008, - EPOLLHUP = 0X010, - - EPOLLRDNORM = 0x40, - EPOLLWRNORM = 0x004, -}; -#define EPOLL_CTL_ADD 1 -#define EPOLL_CTL_DEL 2 -#define EPOLL_CTL_MOD 3 -typedef union epoll_data -{ - void *ptr; - int fd; - uint32_t u32; - uint64_t u64; - -} epoll_data_t; - -struct epoll_event -{ - uint32_t events; - epoll_data_t data; -}; - -struct co_epoll_res -{ - int size; - struct epoll_event *events; - struct kevent *eventlist; -}; -int co_epoll_wait( int epfd,struct co_epoll_res *events,int maxevents,int timeout ); -int co_epoll_ctl( int epfd,int op,int fd,struct epoll_event * ); -int co_epoll_create( int size ); -struct co_epoll_res *co_epoll_res_alloc( int n ); -void co_epoll_res_free( struct co_epoll_res * ); - -#endif -#endif - - diff --git a/trunk/3rdparty/libco/co_hook_sys_call.cpp b/trunk/3rdparty/libco/co_hook_sys_call.cpp deleted file mode 100644 index 7bb0c417b..000000000 --- a/trunk/3rdparty/libco/co_hook_sys_call.cpp +++ /dev/null @@ -1,1003 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include "co_routine.h" -#include "co_routine_inner.h" -#include "co_routine_specific.h" - -typedef long long ll64_t; - -struct rpchook_t -{ - int user_flag; - struct sockaddr_in dest; //maybe sockaddr_un; - int domain; //AF_LOCAL , AF_INET - - struct timeval read_timeout; - struct timeval write_timeout; -}; -static inline pid_t GetPid() -{ - char **p = (char**)pthread_self(); - return p ? *(pid_t*)(p + 18) : getpid(); -} -static rpchook_t *g_rpchook_socket_fd[ 102400 ] = { 0 }; - -typedef int (*socket_pfn_t)(int domain, int type, int protocol); -typedef int (*connect_pfn_t)(int socket, const struct sockaddr *address, socklen_t address_len); -typedef int (*close_pfn_t)(int fd); - -typedef ssize_t (*read_pfn_t)(int fildes, void *buf, size_t nbyte); -typedef ssize_t (*write_pfn_t)(int fildes, const void *buf, size_t nbyte); - -typedef ssize_t (*sendto_pfn_t)(int socket, const void *message, size_t length, - int flags, const struct sockaddr *dest_addr, - socklen_t dest_len); - -typedef ssize_t (*recvfrom_pfn_t)(int socket, void *buffer, size_t length, - int flags, struct sockaddr *address, - socklen_t *address_len); - -typedef size_t (*send_pfn_t)(int socket, const void *buffer, size_t length, int flags); -typedef ssize_t (*recv_pfn_t)(int socket, void *buffer, size_t length, int flags); - -typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); -typedef int (*setsockopt_pfn_t)(int socket, int level, int option_name, - const void *option_value, socklen_t option_len); - -typedef int (*fcntl_pfn_t)(int fildes, int cmd, ...); -typedef struct tm *(*localtime_r_pfn_t)( const time_t *timep, struct tm *result ); - -typedef void *(*pthread_getspecific_pfn_t)(pthread_key_t key); -typedef int (*pthread_setspecific_pfn_t)(pthread_key_t key, const void *value); - -typedef int (*setenv_pfn_t)(const char *name, const char *value, int overwrite); -typedef int (*unsetenv_pfn_t)(const char *name); -typedef char *(*getenv_pfn_t)(const char *name); -typedef hostent* (*gethostbyname_pfn_t)(const char *name); -typedef res_state (*__res_state_pfn_t)(); -typedef int (*__poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); - -static socket_pfn_t g_sys_socket_func = (socket_pfn_t)dlsym(RTLD_NEXT,"socket"); -static connect_pfn_t g_sys_connect_func = (connect_pfn_t)dlsym(RTLD_NEXT,"connect"); -static close_pfn_t g_sys_close_func = (close_pfn_t)dlsym(RTLD_NEXT,"close"); - -static read_pfn_t g_sys_read_func = (read_pfn_t)dlsym(RTLD_NEXT,"read"); -static write_pfn_t g_sys_write_func = (write_pfn_t)dlsym(RTLD_NEXT,"write"); - -static sendto_pfn_t g_sys_sendto_func = (sendto_pfn_t)dlsym(RTLD_NEXT,"sendto"); -static recvfrom_pfn_t g_sys_recvfrom_func = (recvfrom_pfn_t)dlsym(RTLD_NEXT,"recvfrom"); - -static send_pfn_t g_sys_send_func = (send_pfn_t)dlsym(RTLD_NEXT,"send"); -static recv_pfn_t g_sys_recv_func = (recv_pfn_t)dlsym(RTLD_NEXT,"recv"); - -static poll_pfn_t g_sys_poll_func = (poll_pfn_t)dlsym(RTLD_NEXT,"poll"); - -static setsockopt_pfn_t g_sys_setsockopt_func - = (setsockopt_pfn_t)dlsym(RTLD_NEXT,"setsockopt"); -static fcntl_pfn_t g_sys_fcntl_func = (fcntl_pfn_t)dlsym(RTLD_NEXT,"fcntl"); - -static setenv_pfn_t g_sys_setenv_func = (setenv_pfn_t)dlsym(RTLD_NEXT,"setenv"); -static unsetenv_pfn_t g_sys_unsetenv_func = (unsetenv_pfn_t)dlsym(RTLD_NEXT,"unsetenv"); -static getenv_pfn_t g_sys_getenv_func = (getenv_pfn_t)dlsym(RTLD_NEXT,"getenv"); -static __res_state_pfn_t g_sys___res_state_func = (__res_state_pfn_t)dlsym(RTLD_NEXT,"__res_state"); - -static gethostbyname_pfn_t g_sys_gethostbyname_func = (gethostbyname_pfn_t)dlsym(RTLD_NEXT, "gethostbyname"); - -static __poll_pfn_t g_sys___poll_func = (__poll_pfn_t)dlsym(RTLD_NEXT, "__poll"); - - -/* -static pthread_getspecific_pfn_t g_sys_pthread_getspecific_func - = (pthread_getspecific_pfn_t)dlsym(RTLD_NEXT,"pthread_getspecific"); - -static pthread_setspecific_pfn_t g_sys_pthread_setspecific_func - = (pthread_setspecific_pfn_t)dlsym(RTLD_NEXT,"pthread_setspecific"); - -static pthread_rwlock_rdlock_pfn_t g_sys_pthread_rwlock_rdlock_func - = (pthread_rwlock_rdlock_pfn_t)dlsym(RTLD_NEXT,"pthread_rwlock_rdlock"); - -static pthread_rwlock_wrlock_pfn_t g_sys_pthread_rwlock_wrlock_func - = (pthread_rwlock_wrlock_pfn_t)dlsym(RTLD_NEXT,"pthread_rwlock_wrlock"); - -static pthread_rwlock_unlock_pfn_t g_sys_pthread_rwlock_unlock_func - = (pthread_rwlock_unlock_pfn_t)dlsym(RTLD_NEXT,"pthread_rwlock_unlock"); -*/ - - - -static inline unsigned long long get_tick_count() -{ - uint32_t lo, hi; - __asm__ __volatile__ ( - "rdtscp" : "=a"(lo), "=d"(hi) - ); - return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); -} - -struct rpchook_connagent_head_t -{ - unsigned char bVersion; - struct in_addr iIP; - unsigned short hPort; - unsigned int iBodyLen; - unsigned int iOssAttrID; - unsigned char bIsRespNotExist; - unsigned char sReserved[6]; -}__attribute__((packed)); - - -#define HOOK_SYS_FUNC(name) if( !g_sys_##name##_func ) { g_sys_##name##_func = (name##_pfn_t)dlsym(RTLD_NEXT,#name); } - -static inline ll64_t diff_ms(struct timeval &begin,struct timeval &end) -{ - ll64_t u = (end.tv_sec - begin.tv_sec) ; - u *= 1000 * 10; - u += ( end.tv_usec - begin.tv_usec ) / ( 100 ); - return u; -} - - - -static inline rpchook_t * get_by_fd( int fd ) -{ - if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) - { - return g_rpchook_socket_fd[ fd ]; - } - return NULL; -} -static inline rpchook_t * alloc_by_fd( int fd ) -{ - if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) - { - rpchook_t *lp = (rpchook_t*)calloc( 1,sizeof(rpchook_t) ); - lp->read_timeout.tv_sec = 1; - lp->write_timeout.tv_sec = 1; - g_rpchook_socket_fd[ fd ] = lp; - return lp; - } - return NULL; -} -static inline void free_by_fd( int fd ) -{ - if( fd > -1 && fd < (int)sizeof(g_rpchook_socket_fd) / (int)sizeof(g_rpchook_socket_fd[0]) ) - { - rpchook_t *lp = g_rpchook_socket_fd[ fd ]; - if( lp ) - { - g_rpchook_socket_fd[ fd ] = NULL; - free(lp); - } - } - return; - -} -int socket(int domain, int type, int protocol) -{ - HOOK_SYS_FUNC( socket ); - - if( !co_is_enable_sys_hook() ) - { - return g_sys_socket_func( domain,type,protocol ); - } - int fd = g_sys_socket_func(domain,type,protocol); - if( fd < 0 ) - { - return fd; - } - - rpchook_t *lp = alloc_by_fd( fd ); - lp->domain = domain; - - fcntl( fd, F_SETFL, g_sys_fcntl_func(fd, F_GETFL,0 ) ); - - return fd; -} - -int co_accept( int fd, struct sockaddr *addr, socklen_t *len ) -{ - int cli = accept( fd,addr,len ); - if( cli < 0 ) - { - return cli; - } - alloc_by_fd( cli ); - return cli; -} -int connect(int fd, const struct sockaddr *address, socklen_t address_len) -{ - HOOK_SYS_FUNC( connect ); - - if( !co_is_enable_sys_hook() ) - { - return g_sys_connect_func(fd,address,address_len); - } - - //1.sys call - int ret = g_sys_connect_func( fd,address,address_len ); - - rpchook_t *lp = get_by_fd( fd ); - if( !lp ) return ret; - - if( sizeof(lp->dest) >= address_len ) - { - memcpy( &(lp->dest),address,(int)address_len ); - } - if( O_NONBLOCK & lp->user_flag ) - { - return ret; - } - - if (!(ret < 0 && errno == EINPROGRESS)) - { - return ret; - } - - //2.wait - int pollret = 0; - struct pollfd pf = { 0 }; - - for(int i=0;i<3;i++) //25s * 3 = 75s - { - memset( &pf,0,sizeof(pf) ); - pf.fd = fd; - pf.events = ( POLLOUT | POLLERR | POLLHUP ); - - pollret = poll( &pf,1,25000 ); - - if( 1 == pollret ) - { - break; - } - } - - if( pf.revents & POLLOUT ) //connect succ - { - // 3.check getsockopt ret - int err = 0; - socklen_t errlen = sizeof(err); - ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen); - if (ret < 0) { - return ret; - } else if (err != 0) { - errno = err; - return -1; - } - errno = 0; - return 0; - } - - errno = ETIMEDOUT; - return ret; -} - - -int close(int fd) -{ - HOOK_SYS_FUNC( close ); - - if( !co_is_enable_sys_hook() ) - { - return g_sys_close_func( fd ); - } - - free_by_fd( fd ); - int ret = g_sys_close_func(fd); - - return ret; -} -ssize_t read( int fd, void *buf, size_t nbyte ) -{ - HOOK_SYS_FUNC( read ); - - if( !co_is_enable_sys_hook() ) - { - return g_sys_read_func( fd,buf,nbyte ); - } - rpchook_t *lp = get_by_fd( fd ); - - if( !lp || ( O_NONBLOCK & lp->user_flag ) ) - { - ssize_t ret = g_sys_read_func( fd,buf,nbyte ); - return ret; - } - int timeout = ( lp->read_timeout.tv_sec * 1000 ) - + ( lp->read_timeout.tv_usec / 1000 ); - - struct pollfd pf = { 0 }; - pf.fd = fd; - pf.events = ( POLLIN | POLLERR | POLLHUP ); - - int pollret = poll( &pf,1,timeout ); - - ssize_t readret = g_sys_read_func( fd,(char*)buf ,nbyte ); - - if( readret < 0 ) - { - co_log_err("CO_ERR: read fd %d ret %ld errno %d poll ret %d timeout %d", - fd,readret,errno,pollret,timeout); - } - - return readret; - -} -ssize_t write( int fd, const void *buf, size_t nbyte ) -{ - HOOK_SYS_FUNC( write ); - - if( !co_is_enable_sys_hook() ) - { - return g_sys_write_func( fd,buf,nbyte ); - } - rpchook_t *lp = get_by_fd( fd ); - - if( !lp || ( O_NONBLOCK & lp->user_flag ) ) - { - ssize_t ret = g_sys_write_func( fd,buf,nbyte ); - return ret; - } - size_t wrotelen = 0; - int timeout = ( lp->write_timeout.tv_sec * 1000 ) - + ( lp->write_timeout.tv_usec / 1000 ); - - ssize_t writeret = g_sys_write_func( fd,(const char*)buf + wrotelen,nbyte - wrotelen ); - - if (writeret == 0) - { - return writeret; - } - - if( writeret > 0 ) - { - wrotelen += writeret; - } - while( wrotelen < nbyte ) - { - - struct pollfd pf = { 0 }; - pf.fd = fd; - pf.events = ( POLLOUT | POLLERR | POLLHUP ); - poll( &pf,1,timeout ); - - writeret = g_sys_write_func( fd,(const char*)buf + wrotelen,nbyte - wrotelen ); - - if( writeret <= 0 ) - { - break; - } - wrotelen += writeret ; - } - if (writeret <= 0 && wrotelen == 0) - { - return writeret; - } - return wrotelen; -} - -ssize_t sendto(int socket, const void *message, size_t length, - int flags, const struct sockaddr *dest_addr, - socklen_t dest_len) -{ - /* - 1.no enable sys call ? sys - 2.( !lp || lp is non block ) ? sys - 3.try - 4.wait - 5.try - */ - HOOK_SYS_FUNC( sendto ); - if( !co_is_enable_sys_hook() ) - { - return g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); - } - - rpchook_t *lp = get_by_fd( socket ); - if( !lp || ( O_NONBLOCK & lp->user_flag ) ) - { - return g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); - } - - ssize_t ret = g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); - if( ret < 0 && EAGAIN == errno ) - { - int timeout = ( lp->write_timeout.tv_sec * 1000 ) - + ( lp->write_timeout.tv_usec / 1000 ); - - - struct pollfd pf = { 0 }; - pf.fd = socket; - pf.events = ( POLLOUT | POLLERR | POLLHUP ); - poll( &pf,1,timeout ); - - ret = g_sys_sendto_func( socket,message,length,flags,dest_addr,dest_len ); - - } - return ret; -} - -ssize_t recvfrom(int socket, void *buffer, size_t length, - int flags, struct sockaddr *address, - socklen_t *address_len) -{ - HOOK_SYS_FUNC( recvfrom ); - if( !co_is_enable_sys_hook() ) - { - return g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); - } - - rpchook_t *lp = get_by_fd( socket ); - if( !lp || ( O_NONBLOCK & lp->user_flag ) ) - { - return g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); - } - - int timeout = ( lp->read_timeout.tv_sec * 1000 ) - + ( lp->read_timeout.tv_usec / 1000 ); - - - struct pollfd pf = { 0 }; - pf.fd = socket; - pf.events = ( POLLIN | POLLERR | POLLHUP ); - poll( &pf,1,timeout ); - - ssize_t ret = g_sys_recvfrom_func( socket,buffer,length,flags,address,address_len ); - return ret; -} - -ssize_t send(int socket, const void *buffer, size_t length, int flags) -{ - HOOK_SYS_FUNC( send ); - - if( !co_is_enable_sys_hook() ) - { - return g_sys_send_func( socket,buffer,length,flags ); - } - rpchook_t *lp = get_by_fd( socket ); - - if( !lp || ( O_NONBLOCK & lp->user_flag ) ) - { - return g_sys_send_func( socket,buffer,length,flags ); - } - size_t wrotelen = 0; - int timeout = ( lp->write_timeout.tv_sec * 1000 ) - + ( lp->write_timeout.tv_usec / 1000 ); - - ssize_t writeret = g_sys_send_func( socket,buffer,length,flags ); - if (writeret == 0) - { - return writeret; - } - - if( writeret > 0 ) - { - wrotelen += writeret; - } - while( wrotelen < length ) - { - - struct pollfd pf = { 0 }; - pf.fd = socket; - pf.events = ( POLLOUT | POLLERR | POLLHUP ); - poll( &pf,1,timeout ); - - writeret = g_sys_send_func( socket,(const char*)buffer + wrotelen,length - wrotelen,flags ); - - if( writeret <= 0 ) - { - break; - } - wrotelen += writeret ; - } - if (writeret <= 0 && wrotelen == 0) - { - return writeret; - } - return wrotelen; -} - -ssize_t recv( int socket, void *buffer, size_t length, int flags ) -{ - HOOK_SYS_FUNC( recv ); - - if( !co_is_enable_sys_hook() ) - { - return g_sys_recv_func( socket,buffer,length,flags ); - } - rpchook_t *lp = get_by_fd( socket ); - - if( !lp || ( O_NONBLOCK & lp->user_flag ) ) - { - return g_sys_recv_func( socket,buffer,length,flags ); - } - int timeout = ( lp->read_timeout.tv_sec * 1000 ) - + ( lp->read_timeout.tv_usec / 1000 ); - - struct pollfd pf = { 0 }; - pf.fd = socket; - pf.events = ( POLLIN | POLLERR | POLLHUP ); - - int pollret = poll( &pf,1,timeout ); - - ssize_t readret = g_sys_recv_func( socket,buffer,length,flags ); - - if( readret < 0 ) - { - co_log_err("CO_ERR: read fd %d ret %ld errno %d poll ret %d timeout %d", - socket,readret,errno,pollret,timeout); - } - - return readret; - -} - -extern int co_poll_inner( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout, poll_pfn_t pollfunc); - -int poll(struct pollfd fds[], nfds_t nfds, int timeout) -{ - HOOK_SYS_FUNC( poll ); - - if (!co_is_enable_sys_hook() || timeout == 0) { - return g_sys_poll_func(fds, nfds, timeout); - } - pollfd *fds_merge = NULL; - nfds_t nfds_merge = 0; - std::map m; // fd --> idx - std::map::iterator it; - if (nfds > 1) { - fds_merge = (pollfd *)malloc(sizeof(pollfd) * nfds); - for (size_t i = 0; i < nfds; i++) { - if ((it = m.find(fds[i].fd)) == m.end()) { - fds_merge[nfds_merge] = fds[i]; - m[fds[i].fd] = nfds_merge; - nfds_merge++; - } else { - int j = it->second; - fds_merge[j].events |= fds[i].events; // merge in j slot - } - } - } - - int ret = 0; - if (nfds_merge == nfds || nfds == 1) { - ret = co_poll_inner(co_get_epoll_ct(), fds, nfds, timeout, g_sys_poll_func); - } else { - ret = co_poll_inner(co_get_epoll_ct(), fds_merge, nfds_merge, timeout, - g_sys_poll_func); - if (ret > 0) { - for (size_t i = 0; i < nfds; i++) { - it = m.find(fds[i].fd); - if (it != m.end()) { - int j = it->second; - fds[i].revents = fds_merge[j].revents & fds[i].events; - } - } - } - } - free(fds_merge); - return ret; - - -} -int setsockopt(int fd, int level, int option_name, - const void *option_value, socklen_t option_len) -{ - HOOK_SYS_FUNC( setsockopt ); - - if( !co_is_enable_sys_hook() ) - { - return g_sys_setsockopt_func( fd,level,option_name,option_value,option_len ); - } - rpchook_t *lp = get_by_fd( fd ); - - if( lp && SOL_SOCKET == level ) - { - struct timeval *val = (struct timeval*)option_value; - if( SO_RCVTIMEO == option_name ) - { - memcpy( &lp->read_timeout,val,sizeof(*val) ); - } - else if( SO_SNDTIMEO == option_name ) - { - memcpy( &lp->write_timeout,val,sizeof(*val) ); - } - } - return g_sys_setsockopt_func( fd,level,option_name,option_value,option_len ); -} - - -int fcntl(int fildes, int cmd, ...) -{ - HOOK_SYS_FUNC( fcntl ); - - if( fildes < 0 ) - { - return __LINE__; - } - - va_list arg_list; - va_start( arg_list,cmd ); - - int ret = -1; - rpchook_t *lp = get_by_fd( fildes ); - switch( cmd ) - { - case F_DUPFD: - { - int param = va_arg(arg_list,int); - ret = g_sys_fcntl_func( fildes,cmd,param ); - break; - } - case F_GETFD: - { - ret = g_sys_fcntl_func( fildes,cmd ); - if (lp && !(lp->user_flag & O_NONBLOCK)) { - ret = ret & (~O_NONBLOCK); - } - break; - } - case F_SETFD: - { - int param = va_arg(arg_list,int); - ret = g_sys_fcntl_func( fildes,cmd,param ); - break; - } - case F_GETFL: - { - ret = g_sys_fcntl_func( fildes,cmd ); - break; - } - case F_SETFL: - { - int param = va_arg(arg_list,int); - int flag = param; - if( co_is_enable_sys_hook() && lp ) - { - flag |= O_NONBLOCK; - } - ret = g_sys_fcntl_func( fildes,cmd,flag ); - if( 0 == ret && lp ) - { - lp->user_flag = param; - } - break; - } - case F_GETOWN: - { - ret = g_sys_fcntl_func( fildes,cmd ); - break; - } - case F_SETOWN: - { - int param = va_arg(arg_list,int); - ret = g_sys_fcntl_func( fildes,cmd,param ); - break; - } - case F_GETLK: - { - struct flock *param = va_arg(arg_list,struct flock *); - ret = g_sys_fcntl_func( fildes,cmd,param ); - break; - } - case F_SETLK: - { - struct flock *param = va_arg(arg_list,struct flock *); - ret = g_sys_fcntl_func( fildes,cmd,param ); - break; - } - case F_SETLKW: - { - struct flock *param = va_arg(arg_list,struct flock *); - ret = g_sys_fcntl_func( fildes,cmd,param ); - break; - } - } - - va_end( arg_list ); - - return ret; -} - -struct stCoSysEnv_t -{ - char *name; - char *value; -}; -struct stCoSysEnvArr_t -{ - stCoSysEnv_t *data; - size_t cnt; -}; -static stCoSysEnvArr_t *dup_co_sysenv_arr( stCoSysEnvArr_t * arr ) -{ - stCoSysEnvArr_t *lp = (stCoSysEnvArr_t*)calloc( sizeof(stCoSysEnvArr_t),1 ); - if( arr->cnt ) - { - lp->data = (stCoSysEnv_t*)calloc( sizeof(stCoSysEnv_t) * arr->cnt,1 ); - lp->cnt = arr->cnt; - memcpy( lp->data,arr->data,sizeof( stCoSysEnv_t ) * arr->cnt ); - } - return lp; -} - -static int co_sysenv_comp(const void *a, const void *b) -{ - return strcmp(((stCoSysEnv_t*)a)->name, ((stCoSysEnv_t*)b)->name); -} -static stCoSysEnvArr_t g_co_sysenv = { 0 }; - - - -void co_set_env_list( const char *name[],size_t cnt) -{ - if( g_co_sysenv.data ) - { - return ; - } - g_co_sysenv.data = (stCoSysEnv_t*)calloc( 1,sizeof(stCoSysEnv_t) * cnt ); - - for(size_t i=0;i 1 ) - { - qsort( g_co_sysenv.data,g_co_sysenv.cnt,sizeof(stCoSysEnv_t),co_sysenv_comp ); - stCoSysEnv_t *lp = g_co_sysenv.data; - stCoSysEnv_t *lq = g_co_sysenv.data + 1; - for(size_t i=1;iname,lq->name ) ) - { - ++lp; - if( lq != lp ) - { - *lp = *lq; - } - } - ++lq; - } - g_co_sysenv.cnt = lp - g_co_sysenv.data + 1; - } - -} - -int setenv(const char *n, const char *value, int overwrite) -{ - HOOK_SYS_FUNC( setenv ) - if( co_is_enable_sys_hook() && g_co_sysenv.data ) - { - stCoRoutine_t *self = co_self(); - if( self ) - { - if( !self->pvEnv ) - { - self->pvEnv = dup_co_sysenv_arr( &g_co_sysenv ); - } - stCoSysEnvArr_t *arr = (stCoSysEnvArr_t*)(self->pvEnv); - - stCoSysEnv_t name = { (char*)n,0 }; - - stCoSysEnv_t *e = (stCoSysEnv_t*)bsearch( &name,arr->data,arr->cnt,sizeof(name),co_sysenv_comp ); - - if( e ) - { - if( overwrite || !e->value ) - { - if( e->value ) free( e->value ); - e->value = ( value ? strdup( value ) : 0 ); - } - return 0; - } - } - - } - return g_sys_setenv_func( n,value,overwrite ); -} -int unsetenv(const char *n) -{ - HOOK_SYS_FUNC( unsetenv ) - if( co_is_enable_sys_hook() && g_co_sysenv.data ) - { - stCoRoutine_t *self = co_self(); - if( self ) - { - if( !self->pvEnv ) - { - self->pvEnv = dup_co_sysenv_arr( &g_co_sysenv ); - } - stCoSysEnvArr_t *arr = (stCoSysEnvArr_t*)(self->pvEnv); - - stCoSysEnv_t name = { (char*)n,0 }; - - stCoSysEnv_t *e = (stCoSysEnv_t*)bsearch( &name,arr->data,arr->cnt,sizeof(name),co_sysenv_comp ); - - if( e ) - { - if( e->value ) - { - free( e->value ); - e->value = 0; - } - return 0; - } - } - - } - return g_sys_unsetenv_func( n ); -} -char *getenv( const char *n ) -{ - HOOK_SYS_FUNC( getenv ) - if( co_is_enable_sys_hook() && g_co_sysenv.data ) - { - stCoRoutine_t *self = co_self(); - - stCoSysEnv_t name = { (char*)n,0 }; - - if( !self->pvEnv ) - { - self->pvEnv = dup_co_sysenv_arr( &g_co_sysenv ); - } - stCoSysEnvArr_t *arr = (stCoSysEnvArr_t*)(self->pvEnv); - - stCoSysEnv_t *e = (stCoSysEnv_t*)bsearch( &name,arr->data,arr->cnt,sizeof(name),co_sysenv_comp ); - - if( e ) - { - return e->value; - } - - } - return g_sys_getenv_func( n ); - -} -struct hostent* co_gethostbyname(const char *name); - -struct hostent *gethostbyname(const char *name) -{ - HOOK_SYS_FUNC( gethostbyname ); - -#if defined( __APPLE__ ) || defined( __FreeBSD__ ) - return g_sys_gethostbyname_func( name ); -#else - if (!co_is_enable_sys_hook()) - { - return g_sys_gethostbyname_func(name); - } - return co_gethostbyname(name); -#endif - -} - - -struct res_state_wrap -{ - struct __res_state state; -}; -CO_ROUTINE_SPECIFIC(res_state_wrap, __co_state_wrap); - -extern "C" -{ - res_state __res_state() - { - HOOK_SYS_FUNC(__res_state); - - if (!co_is_enable_sys_hook()) - { - return g_sys___res_state_func(); - } - - return &(__co_state_wrap->state); - } - int __poll(struct pollfd fds[], nfds_t nfds, int timeout) - { - return poll(fds, nfds, timeout); - } -} - -struct hostbuf_wrap -{ - struct hostent host; - char* buffer; - size_t iBufferSize; - int host_errno; -}; - -CO_ROUTINE_SPECIFIC(hostbuf_wrap, __co_hostbuf_wrap); - -#if !defined( __APPLE__ ) && !defined( __FreeBSD__ ) -struct hostent *co_gethostbyname(const char *name) -{ - if (!name) - { - return NULL; - } - - if (__co_hostbuf_wrap->buffer && __co_hostbuf_wrap->iBufferSize > 1024) - { - free(__co_hostbuf_wrap->buffer); - __co_hostbuf_wrap->buffer = NULL; - } - if (!__co_hostbuf_wrap->buffer) - { - __co_hostbuf_wrap->buffer = (char*)malloc(1024); - __co_hostbuf_wrap->iBufferSize = 1024; - } - - struct hostent *host = &__co_hostbuf_wrap->host; - struct hostent *result = NULL; - int *h_errnop = &(__co_hostbuf_wrap->host_errno); - - int ret = -1; - while (ret = gethostbyname_r(name, host, __co_hostbuf_wrap->buffer, - __co_hostbuf_wrap->iBufferSize, &result, h_errnop) == ERANGE && - *h_errnop == NETDB_INTERNAL ) - { - free(__co_hostbuf_wrap->buffer); - __co_hostbuf_wrap->iBufferSize = __co_hostbuf_wrap->iBufferSize * 2; - __co_hostbuf_wrap->buffer = (char*)malloc(__co_hostbuf_wrap->iBufferSize); - } - - if (ret == 0 && (host == result)) - { - return host; - } - return NULL; -} -#endif - - -void co_enable_hook_sys() //这函数必须在这里,否则本文件会被忽略!!! -{ - stCoRoutine_t *co = GetCurrThreadCo(); - if( co ) - { - co->cEnableSysHook = 1; - } -} - diff --git a/trunk/3rdparty/libco/co_routine.cpp b/trunk/3rdparty/libco/co_routine.cpp deleted file mode 100644 index 352ae7ea0..000000000 --- a/trunk/3rdparty/libco/co_routine.cpp +++ /dev/null @@ -1,1196 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "co_routine.h" -#include "co_routine_inner.h" -#include "co_epoll.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -extern "C" -{ - extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap"); -}; -using namespace std; -stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env ); -struct stCoEpoll_t; - -struct stCoRoutineEnv_t -{ - stCoRoutine_t *pCallStack[ 128 ]; - int iCallStackSize; - stCoEpoll_t *pEpoll; - - //for copy stack log lastco and nextco - stCoRoutine_t* pending_co; - stCoRoutine_t* occupy_co; -}; -//int socket(int domain, int type, int protocol); -void co_log_err( const char *fmt,... ) -{ -} - - -#if defined( __LIBCO_RDTSCP__) -static unsigned long long counter(void) -{ - register uint32_t lo, hi; - register unsigned long long o; - __asm__ __volatile__ ( - "rdtscp" : "=a"(lo), "=d"(hi)::"%rcx" - ); - o = hi; - o <<= 32; - return (o | lo); - -} -static unsigned long long getCpuKhz() -{ - FILE *fp = fopen("/proc/cpuinfo","r"); - if(!fp) return 1; - char buf[4096] = {0}; - fread(buf,1,sizeof(buf),fp); - fclose(fp); - - char *lp = strstr(buf,"cpu MHz"); - if(!lp) return 1; - lp += strlen("cpu MHz"); - while(*lp == ' ' || *lp == '\t' || *lp == ':') - { - ++lp; - } - - double mhz = atof(lp); - unsigned long long u = (unsigned long long)(mhz * 1000); - return u; -} -#endif - -static unsigned long long GetTickMS() -{ -#if defined( __LIBCO_RDTSCP__) - static uint32_t khz = getCpuKhz(); - return counter() / khz; -#else - struct timeval now = { 0 }; - gettimeofday( &now,NULL ); - unsigned long long u = now.tv_sec; - u *= 1000; - u += now.tv_usec / 1000; - return u; -#endif -} - -/* no longer use -static pid_t GetPid() -{ - static __thread pid_t pid = 0; - static __thread pid_t tid = 0; - if( !pid || !tid || pid != getpid() ) - { - pid = getpid(); -#if defined( __APPLE__ ) - tid = syscall( SYS_gettid ); - if( -1 == (long)tid ) - { - tid = pid; - } -#elif defined( __FreeBSD__ ) - syscall(SYS_thr_self, &tid); - if( tid < 0 ) - { - tid = pid; - } -#else - tid = syscall( __NR_gettid ); -#endif - - } - return tid; - -} -static pid_t GetPid() -{ - char **p = (char**)pthread_self(); - return p ? *(pid_t*)(p + 18) : getpid(); -} -*/ -template -void RemoveFromLink(T *ap) -{ - TLink *lst = ap->pLink; - if(!lst) return ; - assert( lst->head && lst->tail ); - - if( ap == lst->head ) - { - lst->head = ap->pNext; - if(lst->head) - { - lst->head->pPrev = NULL; - } - } - else - { - if(ap->pPrev) - { - ap->pPrev->pNext = ap->pNext; - } - } - - if( ap == lst->tail ) - { - lst->tail = ap->pPrev; - if(lst->tail) - { - lst->tail->pNext = NULL; - } - } - else - { - ap->pNext->pPrev = ap->pPrev; - } - - ap->pPrev = ap->pNext = NULL; - ap->pLink = NULL; -} - -template -void inline AddTail(TLink*apLink,TNode *ap) -{ - if( ap->pLink ) - { - return ; - } - if(apLink->tail) - { - apLink->tail->pNext = (TNode*)ap; - ap->pNext = NULL; - ap->pPrev = apLink->tail; - apLink->tail = ap; - } - else - { - apLink->head = apLink->tail = ap; - ap->pNext = ap->pPrev = NULL; - } - ap->pLink = apLink; -} -template -void inline PopHead( TLink*apLink ) -{ - if( !apLink->head ) - { - return ; - } - TNode *lp = apLink->head; - if( apLink->head == apLink->tail ) - { - apLink->head = apLink->tail = NULL; - } - else - { - apLink->head = apLink->head->pNext; - } - - lp->pPrev = lp->pNext = NULL; - lp->pLink = NULL; - - if( apLink->head ) - { - apLink->head->pPrev = NULL; - } -} - -template -void inline Join( TLink*apLink,TLink *apOther ) -{ - //printf("apOther %p\n",apOther); - if( !apOther->head ) - { - return ; - } - TNode *lp = apOther->head; - while( lp ) - { - lp->pLink = apLink; - lp = lp->pNext; - } - lp = apOther->head; - if(apLink->tail) - { - apLink->tail->pNext = (TNode*)lp; - lp->pPrev = apLink->tail; - apLink->tail = apOther->tail; - } - else - { - apLink->head = apOther->head; - apLink->tail = apOther->tail; - } - - apOther->head = apOther->tail = NULL; -} - -/////////////////for copy stack ////////////////////////// -stStackMem_t* co_alloc_stackmem(unsigned int stack_size) -{ - stStackMem_t* stack_mem = (stStackMem_t*)malloc(sizeof(stStackMem_t)); - stack_mem->occupy_co= NULL; - stack_mem->stack_size = stack_size; - stack_mem->stack_buffer = (char*)malloc(stack_size); - stack_mem->stack_bp = stack_mem->stack_buffer + stack_size; - return stack_mem; -} - -stShareStack_t* co_alloc_sharestack(int count, int stack_size) -{ - stShareStack_t* share_stack = (stShareStack_t*)malloc(sizeof(stShareStack_t)); - share_stack->alloc_idx = 0; - share_stack->stack_size = stack_size; - - //alloc stack array - share_stack->count = count; - stStackMem_t** stack_array = (stStackMem_t**)calloc(count, sizeof(stStackMem_t*)); - for (int i = 0; i < count; i++) - { - stack_array[i] = co_alloc_stackmem(stack_size); - } - share_stack->stack_array = stack_array; - return share_stack; -} - -static stStackMem_t* co_get_stackmem(stShareStack_t* share_stack) -{ - if (!share_stack) - { - return NULL; - } - int idx = share_stack->alloc_idx % share_stack->count; - share_stack->alloc_idx++; - - return share_stack->stack_array[idx]; -} - - -// ---------------------------------------------------------------------------- -struct stTimeoutItemLink_t; -struct stTimeoutItem_t; -struct stCoEpoll_t -{ - int iEpollFd; - static const int _EPOLL_SIZE = 1024 * 10; - - struct stTimeout_t *pTimeout; - - struct stTimeoutItemLink_t *pstTimeoutList; - - struct stTimeoutItemLink_t *pstActiveList; - - co_epoll_res *result; - -}; -typedef void (*OnPreparePfn_t)( stTimeoutItem_t *,struct epoll_event &ev, stTimeoutItemLink_t *active ); -typedef void (*OnProcessPfn_t)( stTimeoutItem_t *); -struct stTimeoutItem_t -{ - - enum - { - eMaxTimeout = 40 * 1000 //40s - }; - stTimeoutItem_t *pPrev; - stTimeoutItem_t *pNext; - stTimeoutItemLink_t *pLink; - - unsigned long long ullExpireTime; - - OnPreparePfn_t pfnPrepare; - OnProcessPfn_t pfnProcess; - - void *pArg; // routine - bool bTimeout; -}; -struct stTimeoutItemLink_t -{ - stTimeoutItem_t *head; - stTimeoutItem_t *tail; - -}; -struct stTimeout_t -{ - stTimeoutItemLink_t *pItems; - int iItemSize; - - unsigned long long ullStart; - long long llStartIdx; -}; -stTimeout_t *AllocTimeout( int iSize ) -{ - stTimeout_t *lp = (stTimeout_t*)calloc( 1,sizeof(stTimeout_t) ); - - lp->iItemSize = iSize; - lp->pItems = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) * lp->iItemSize ); - - lp->ullStart = GetTickMS(); - lp->llStartIdx = 0; - - return lp; -} -void FreeTimeout( stTimeout_t *apTimeout ) -{ - free( apTimeout->pItems ); - free ( apTimeout ); -} -int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,unsigned long long allNow ) -{ - if( apTimeout->ullStart == 0 ) - { - apTimeout->ullStart = allNow; - apTimeout->llStartIdx = 0; - } - if( allNow < apTimeout->ullStart ) - { - co_log_err("CO_ERR: AddTimeout line %d allNow %llu apTimeout->ullStart %llu", - __LINE__,allNow,apTimeout->ullStart); - - return __LINE__; - } - if( apItem->ullExpireTime < allNow ) - { - co_log_err("CO_ERR: AddTimeout line %d apItem->ullExpireTime %llu allNow %llu apTimeout->ullStart %llu", - __LINE__,apItem->ullExpireTime,allNow,apTimeout->ullStart); - - return __LINE__; - } - unsigned long long diff = apItem->ullExpireTime - apTimeout->ullStart; - - if( diff >= (unsigned long long)apTimeout->iItemSize ) - { - diff = apTimeout->iItemSize - 1; - co_log_err("CO_ERR: AddTimeout line %d diff %d", - __LINE__,diff); - - //return __LINE__; - } - AddTail( apTimeout->pItems + ( apTimeout->llStartIdx + diff ) % apTimeout->iItemSize , apItem ); - - return 0; -} -inline void TakeAllTimeout( stTimeout_t *apTimeout,unsigned long long allNow,stTimeoutItemLink_t *apResult ) -{ - if( apTimeout->ullStart == 0 ) - { - apTimeout->ullStart = allNow; - apTimeout->llStartIdx = 0; - } - - if( allNow < apTimeout->ullStart ) - { - return ; - } - int cnt = allNow - apTimeout->ullStart + 1; - if( cnt > apTimeout->iItemSize ) - { - cnt = apTimeout->iItemSize; - } - if( cnt < 0 ) - { - return; - } - for( int i = 0;illStartIdx + i) % apTimeout->iItemSize; - Join( apResult,apTimeout->pItems + idx ); - } - apTimeout->ullStart = allNow; - apTimeout->llStartIdx += cnt - 1; - - -} -static int CoRoutineFunc( stCoRoutine_t *co,void * ) -{ - if( co->pfn ) - { - co->pfn( co->arg ); - } - co->cEnd = 1; - - stCoRoutineEnv_t *env = co->env; - - co_yield_env( env ); - - return 0; -} - - - -struct stCoRoutine_t *co_create_env( stCoRoutineEnv_t * env, const stCoRoutineAttr_t* attr, - pfn_co_routine_t pfn,void *arg ) -{ - - stCoRoutineAttr_t at; - if( attr ) - { - memcpy( &at,attr,sizeof(at) ); - } - if( at.stack_size <= 0 ) - { - at.stack_size = 128 * 1024; - } - else if( at.stack_size > 1024 * 1024 * 8 ) - { - at.stack_size = 1024 * 1024 * 8; - } - - if( at.stack_size & 0xFFF ) - { - at.stack_size &= ~0xFFF; - at.stack_size += 0x1000; - } - - stCoRoutine_t *lp = (stCoRoutine_t*)malloc( sizeof(stCoRoutine_t) ); - - memset( lp,0,(long)(sizeof(stCoRoutine_t))); - - - lp->env = env; - lp->pfn = pfn; - lp->arg = arg; - - stStackMem_t* stack_mem = NULL; - if( at.share_stack ) - { - stack_mem = co_get_stackmem( at.share_stack); - at.stack_size = at.share_stack->stack_size; - } - else - { - stack_mem = co_alloc_stackmem(at.stack_size); - } - lp->stack_mem = stack_mem; - - lp->ctx.ss_sp = stack_mem->stack_buffer; - lp->ctx.ss_size = at.stack_size; - - lp->cStart = 0; - lp->cEnd = 0; - lp->cIsMain = 0; - lp->cEnableSysHook = 0; - lp->cIsShareStack = at.share_stack != NULL; - - lp->save_size = 0; - lp->save_buffer = NULL; - - return lp; -} - -int co_create( stCoRoutine_t **ppco,const stCoRoutineAttr_t *attr,pfn_co_routine_t pfn,void *arg ) -{ - if( !co_get_curr_thread_env() ) - { - co_init_curr_thread_env(); - } - stCoRoutine_t *co = co_create_env( co_get_curr_thread_env(), attr, pfn,arg ); - *ppco = co; - return 0; -} -void co_free( stCoRoutine_t *co ) -{ - if (!co->cIsShareStack) - { - free(co->stack_mem->stack_buffer); - free(co->stack_mem); - } - //walkerdu fix at 2018-01-20 - //瀛樺湪鍐呭瓨娉勬紡 - else - { - if(co->save_buffer) - free(co->save_buffer); - - if(co->stack_mem->occupy_co == co) - co->stack_mem->occupy_co = NULL; - } - - free( co ); -} -void co_release( stCoRoutine_t *co ) -{ - co_free( co ); -} - -void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co); - -void co_resume( stCoRoutine_t *co ) -{ - stCoRoutineEnv_t *env = co->env; - stCoRoutine_t *lpCurrRoutine = env->pCallStack[ env->iCallStackSize - 1 ]; - if( !co->cStart ) - { - coctx_make( &co->ctx,(coctx_pfn_t)CoRoutineFunc,co,0 ); - co->cStart = 1; - } - env->pCallStack[ env->iCallStackSize++ ] = co; - co_swap( lpCurrRoutine, co ); - - -} - - -// walkerdu 2018-01-14 -// 鐢ㄤ簬reset瓒呮椂鏃犳硶閲嶅浣跨敤鐨勫崗绋 -void co_reset(stCoRoutine_t * co) -{ - if(!co->cStart || co->cIsMain) - return; - - co->cStart = 0; - co->cEnd = 0; - - // 濡傛灉褰撳墠鍗忕▼鏈夊叡浜爤琚垏鍑虹殑buff锛岃杩涜閲婃斁 - if(co->save_buffer) - { - free(co->save_buffer); - co->save_buffer = NULL; - co->save_size = 0; - } - - // 濡傛灉鍏变韩鏍堣褰撳墠鍗忕▼鍗犵敤锛岃閲婃斁鍗犵敤鏍囧織锛屽惁鍒欒鍒囨崲锛屼細鎵цsave_stack_buffer() - if(co->stack_mem->occupy_co == co) - co->stack_mem->occupy_co = NULL; -} - -void co_yield_env( stCoRoutineEnv_t *env ) -{ - - stCoRoutine_t *last = env->pCallStack[ env->iCallStackSize - 2 ]; - stCoRoutine_t *curr = env->pCallStack[ env->iCallStackSize - 1 ]; - - env->iCallStackSize--; - - co_swap( curr, last); -} - -void co_yield_ct() -{ - - co_yield_env( co_get_curr_thread_env() ); -} -void co_yield( stCoRoutine_t *co ) -{ - co_yield_env( co->env ); -} - -void save_stack_buffer(stCoRoutine_t* occupy_co) -{ - ///copy out - stStackMem_t* stack_mem = occupy_co->stack_mem; - int len = stack_mem->stack_bp - occupy_co->stack_sp; - - if (occupy_co->save_buffer) - { - free(occupy_co->save_buffer), occupy_co->save_buffer = NULL; - } - - occupy_co->save_buffer = (char*)malloc(len); //malloc buf; - occupy_co->save_size = len; - - memcpy(occupy_co->save_buffer, occupy_co->stack_sp, len); -} - -void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co) -{ - stCoRoutineEnv_t* env = co_get_curr_thread_env(); - - //get curr stack sp - char c; - curr->stack_sp= &c; - - if (!pending_co->cIsShareStack) - { - env->pending_co = NULL; - env->occupy_co = NULL; - } - else - { - env->pending_co = pending_co; - //get last occupy co on the same stack mem - stCoRoutine_t* occupy_co = pending_co->stack_mem->occupy_co; - //set pending co to occupy thest stack mem; - pending_co->stack_mem->occupy_co = pending_co; - - env->occupy_co = occupy_co; - if (occupy_co && occupy_co != pending_co) - { - save_stack_buffer(occupy_co); - } - } - - //swap context - coctx_swap(&(curr->ctx),&(pending_co->ctx) ); - - //stack buffer may be overwrite, so get again; - stCoRoutineEnv_t* curr_env = co_get_curr_thread_env(); - stCoRoutine_t* update_occupy_co = curr_env->occupy_co; - stCoRoutine_t* update_pending_co = curr_env->pending_co; - - if (update_occupy_co && update_pending_co && update_occupy_co != update_pending_co) - { - //resume stack buffer - if (update_pending_co->save_buffer && update_pending_co->save_size > 0) - { - memcpy(update_pending_co->stack_sp, update_pending_co->save_buffer, update_pending_co->save_size); - } - } -} - - - -//int poll(struct pollfd fds[], nfds_t nfds, int timeout); -// { fd,events,revents } -struct stPollItem_t ; -struct stPoll_t : public stTimeoutItem_t -{ - struct pollfd *fds; - nfds_t nfds; // typedef unsigned long int nfds_t; - - stPollItem_t *pPollItems; - - int iAllEventDetach; - - int iEpollFd; - - int iRaiseCnt; - - -}; -struct stPollItem_t : public stTimeoutItem_t -{ - struct pollfd *pSelf; - stPoll_t *pPoll; - - struct epoll_event stEvent; -}; -/* - * EPOLLPRI POLLPRI // There is urgent data to read. - * EPOLLMSG POLLMSG - * - * POLLREMOVE - * POLLRDHUP - * POLLNVAL - * - * */ -static uint32_t PollEvent2Epoll( short events ) -{ - uint32_t e = 0; - if( events & POLLIN ) e |= EPOLLIN; - if( events & POLLOUT ) e |= EPOLLOUT; - if( events & POLLHUP ) e |= EPOLLHUP; - if( events & POLLERR ) e |= EPOLLERR; - if( events & POLLRDNORM ) e |= EPOLLRDNORM; - if( events & POLLWRNORM ) e |= EPOLLWRNORM; - return e; -} -static short EpollEvent2Poll( uint32_t events ) -{ - short e = 0; - if( events & EPOLLIN ) e |= POLLIN; - if( events & EPOLLOUT ) e |= POLLOUT; - if( events & EPOLLHUP ) e |= POLLHUP; - if( events & EPOLLERR ) e |= POLLERR; - if( events & EPOLLRDNORM ) e |= POLLRDNORM; - if( events & EPOLLWRNORM ) e |= POLLWRNORM; - return e; -} - -static __thread stCoRoutineEnv_t* gCoEnvPerThread = NULL; - -void co_init_curr_thread_env() -{ - gCoEnvPerThread = (stCoRoutineEnv_t*)calloc( 1, sizeof(stCoRoutineEnv_t) ); - stCoRoutineEnv_t *env = gCoEnvPerThread; - - env->iCallStackSize = 0; - struct stCoRoutine_t *self = co_create_env( env, NULL, NULL,NULL ); - self->cIsMain = 1; - - env->pending_co = NULL; - env->occupy_co = NULL; - - coctx_init( &self->ctx ); - - env->pCallStack[ env->iCallStackSize++ ] = self; - - stCoEpoll_t *ev = AllocEpoll(); - SetEpoll( env,ev ); -} -stCoRoutineEnv_t *co_get_curr_thread_env() -{ - return gCoEnvPerThread; -} - -void OnPollProcessEvent( stTimeoutItem_t * ap ) -{ - stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; - co_resume( co ); -} - -void OnPollPreparePfn( stTimeoutItem_t * ap,struct epoll_event &e,stTimeoutItemLink_t *active ) -{ - stPollItem_t *lp = (stPollItem_t *)ap; - lp->pSelf->revents = EpollEvent2Poll( e.events ); - - - stPoll_t *pPoll = lp->pPoll; - pPoll->iRaiseCnt++; - - if( !pPoll->iAllEventDetach ) - { - pPoll->iAllEventDetach = 1; - - RemoveFromLink( pPoll ); - - AddTail( active,pPoll ); - - } -} - - -void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg ) -{ - if( !ctx->result ) - { - ctx->result = co_epoll_res_alloc( stCoEpoll_t::_EPOLL_SIZE ); - } - co_epoll_res *result = ctx->result; - - - for(;;) - { - int ret = co_epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 ); - - stTimeoutItemLink_t *active = (ctx->pstActiveList); - stTimeoutItemLink_t *timeout = (ctx->pstTimeoutList); - - memset( timeout,0,sizeof(stTimeoutItemLink_t) ); - - for(int i=0;ievents[i].data.ptr; - if( item->pfnPrepare ) - { - item->pfnPrepare( item,result->events[i],active ); - } - else - { - AddTail( active,item ); - } - } - - - unsigned long long now = GetTickMS(); - TakeAllTimeout( ctx->pTimeout,now,timeout ); - - stTimeoutItem_t *lp = timeout->head; - while( lp ) - { - //printf("raise timeout %p\n",lp); - lp->bTimeout = true; - lp = lp->pNext; - } - - Join( active,timeout ); - - lp = active->head; - while( lp ) - { - - PopHead( active ); - if (lp->bTimeout && now < lp->ullExpireTime) - { - int ret = AddTimeout(ctx->pTimeout, lp, now); - if (!ret) - { - lp->bTimeout = false; - lp = active->head; - continue; - } - } - if( lp->pfnProcess ) - { - lp->pfnProcess( lp ); - } - - lp = active->head; - } - if( pfn ) - { - if( -1 == pfn( arg ) ) - { - break; - } - } - - } -} -void OnCoroutineEvent( stTimeoutItem_t * ap ) -{ - stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; - co_resume( co ); -} - - -stCoEpoll_t *AllocEpoll() -{ - stCoEpoll_t *ctx = (stCoEpoll_t*)calloc( 1,sizeof(stCoEpoll_t) ); - - ctx->iEpollFd = co_epoll_create( stCoEpoll_t::_EPOLL_SIZE ); - ctx->pTimeout = AllocTimeout( 60 * 1000 ); - - ctx->pstActiveList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) ); - ctx->pstTimeoutList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) ); - - - return ctx; -} - -void FreeEpoll( stCoEpoll_t *ctx ) -{ - if( ctx ) - { - free( ctx->pstActiveList ); - free( ctx->pstTimeoutList ); - FreeTimeout( ctx->pTimeout ); - co_epoll_res_free( ctx->result ); - } - free( ctx ); -} - -stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env ) -{ - return env->pCallStack[ env->iCallStackSize - 1 ]; -} -stCoRoutine_t *GetCurrThreadCo( ) -{ - stCoRoutineEnv_t *env = co_get_curr_thread_env(); - if( !env ) return 0; - return GetCurrCo(env); -} - - - -typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout); -int co_poll_inner( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout, poll_pfn_t pollfunc) -{ - if (timeout == 0) - { - return pollfunc(fds, nfds, timeout); - } - if (timeout < 0) - { - timeout = INT_MAX; - } - int epfd = ctx->iEpollFd; - stCoRoutine_t* self = co_self(); - - //1.struct change - stPoll_t& arg = *((stPoll_t*)malloc(sizeof(stPoll_t))); - memset( &arg,0,sizeof(arg) ); - - arg.iEpollFd = epfd; - arg.fds = (pollfd*)calloc(nfds, sizeof(pollfd)); - arg.nfds = nfds; - - stPollItem_t arr[2]; - if( nfds < sizeof(arr) / sizeof(arr[0]) && !self->cIsShareStack) - { - arg.pPollItems = arr; - } - else - { - arg.pPollItems = (stPollItem_t*)malloc( nfds * sizeof( stPollItem_t ) ); - } - memset( arg.pPollItems,0,nfds * sizeof(stPollItem_t) ); - - arg.pfnProcess = OnPollProcessEvent; - arg.pArg = GetCurrCo( co_get_curr_thread_env() ); - - - //2. add epoll - for(nfds_t i=0;i -1 ) - { - ev.data.ptr = arg.pPollItems + i; - ev.events = PollEvent2Epoll( fds[i].events ); - - int ret = co_epoll_ctl( epfd,EPOLL_CTL_ADD, fds[i].fd, &ev ); - if (ret < 0 && errno == EPERM && nfds == 1 && pollfunc != NULL) - { - if( arg.pPollItems != arr ) - { - free( arg.pPollItems ); - arg.pPollItems = NULL; - } - free(arg.fds); - free(&arg); - return pollfunc(fds, nfds, timeout); - } - } - //if fail,the timeout would work - } - - //3.add timeout - - unsigned long long now = GetTickMS(); - arg.ullExpireTime = now + timeout; - int ret = AddTimeout( ctx->pTimeout,&arg,now ); - int iRaiseCnt = 0; - if( ret != 0 ) - { - co_log_err("CO_ERR: AddTimeout ret %d now %lld timeout %d arg.ullExpireTime %lld", - ret,now,timeout,arg.ullExpireTime); - errno = EINVAL; - iRaiseCnt = -1; - - } - else - { - co_yield_env( co_get_curr_thread_env() ); - iRaiseCnt = arg.iRaiseCnt; - } - - { - //clear epoll status and memory - RemoveFromLink( &arg ); - for(nfds_t i = 0;i < nfds;i++) - { - int fd = fds[i].fd; - if( fd > -1 ) - { - co_epoll_ctl( epfd,EPOLL_CTL_DEL,fd,&arg.pPollItems[i].stEvent ); - } - fds[i].revents = arg.fds[i].revents; - } - - - if( arg.pPollItems != arr ) - { - free( arg.pPollItems ); - arg.pPollItems = NULL; - } - - free(arg.fds); - free(&arg); - } - - return iRaiseCnt; -} - -int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms ) -{ - return co_poll_inner(ctx, fds, nfds, timeout_ms, NULL); -} - -void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev ) -{ - env->pEpoll = ev; -} -stCoEpoll_t *co_get_epoll_ct() -{ - if( !co_get_curr_thread_env() ) - { - co_init_curr_thread_env(); - } - return co_get_curr_thread_env()->pEpoll; -} -struct stHookPThreadSpec_t -{ - stCoRoutine_t *co; - void *value; - - enum - { - size = 1024 - }; -}; -void *co_getspecific(pthread_key_t key) -{ - stCoRoutine_t *co = GetCurrThreadCo(); - if( !co || co->cIsMain ) - { - return pthread_getspecific( key ); - } - return co->aSpec[ key ].value; -} -int co_setspecific(pthread_key_t key, const void *value) -{ - stCoRoutine_t *co = GetCurrThreadCo(); - if( !co || co->cIsMain ) - { - return pthread_setspecific( key,value ); - } - co->aSpec[ key ].value = (void*)value; - return 0; -} - - - -void co_disable_hook_sys() -{ - stCoRoutine_t *co = GetCurrThreadCo(); - if( co ) - { - co->cEnableSysHook = 0; - } -} -bool co_is_enable_sys_hook() -{ - stCoRoutine_t *co = GetCurrThreadCo(); - return ( co && co->cEnableSysHook ); -} - -stCoRoutine_t *co_self() -{ - return GetCurrThreadCo(); -} - -//co cond -struct stCoCond_t; -struct stCoCondItem_t -{ - stCoCondItem_t *pPrev; - stCoCondItem_t *pNext; - stCoCond_t *pLink; - - stTimeoutItem_t timeout; -}; -struct stCoCond_t -{ - stCoCondItem_t *head; - stCoCondItem_t *tail; -}; -static void OnSignalProcessEvent( stTimeoutItem_t * ap ) -{ - stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg; - co_resume( co ); -} - -stCoCondItem_t *co_cond_pop( stCoCond_t *link ); -int co_cond_signal( stCoCond_t *si ) -{ - stCoCondItem_t * sp = co_cond_pop( si ); - if( !sp ) - { - return 0; - } - RemoveFromLink( &sp->timeout ); - - AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout ); - - return 0; -} -int co_cond_broadcast( stCoCond_t *si ) -{ - for(;;) - { - stCoCondItem_t * sp = co_cond_pop( si ); - if( !sp ) return 0; - - RemoveFromLink( &sp->timeout ); - - AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout ); - } - - return 0; -} - - -int co_cond_timedwait( stCoCond_t *link,int ms ) -{ - stCoCondItem_t* psi = (stCoCondItem_t*)calloc(1, sizeof(stCoCondItem_t)); - psi->timeout.pArg = GetCurrThreadCo(); - psi->timeout.pfnProcess = OnSignalProcessEvent; - - if( ms > 0 ) - { - unsigned long long now = GetTickMS(); - psi->timeout.ullExpireTime = now + ms; - - int ret = AddTimeout( co_get_curr_thread_env()->pEpoll->pTimeout,&psi->timeout,now ); - if( ret != 0 ) - { - free(psi); - return ret; - } - } - AddTail( link, psi); - - co_yield_ct(); - - - RemoveFromLink( psi ); - free(psi); - - return 0; -} -stCoCond_t *co_cond_alloc() -{ - return (stCoCond_t*)calloc( 1,sizeof(stCoCond_t) ); -} -int co_cond_free( stCoCond_t * cc ) -{ - free( cc ); - return 0; -} - - -stCoCondItem_t *co_cond_pop( stCoCond_t *link ) -{ - stCoCondItem_t *p = link->head; - if( p ) - { - PopHead( link ); - } - return p; -} diff --git a/trunk/3rdparty/libco/co_routine.h b/trunk/3rdparty/libco/co_routine.h deleted file mode 100644 index d6f478928..000000000 --- a/trunk/3rdparty/libco/co_routine.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#ifndef __CO_ROUTINE_H__ -#define __CO_ROUTINE_H__ - -#include -#include -#include - -//1.struct - -struct stCoRoutine_t; -struct stShareStack_t; - -struct stCoRoutineAttr_t -{ - int stack_size; - stShareStack_t* share_stack; - stCoRoutineAttr_t() - { - stack_size = 128 * 1024; - share_stack = NULL; - } -}__attribute__ ((packed)); - -struct stCoEpoll_t; -typedef int (*pfn_co_eventloop_t)(void *); -typedef void *(*pfn_co_routine_t)( void * ); - -//2.co_routine - -int co_create( stCoRoutine_t **co,const stCoRoutineAttr_t *attr,void *(*routine)(void*),void *arg ); -void co_resume( stCoRoutine_t *co ); -void co_yield( stCoRoutine_t *co ); -void co_yield_ct(); //ct = current thread -void co_release( stCoRoutine_t *co ); -void co_reset(stCoRoutine_t * co); - -stCoRoutine_t *co_self(); - -int co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms ); -void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg ); - -//3.specific - -int co_setspecific( pthread_key_t key, const void *value ); -void * co_getspecific( pthread_key_t key ); - -//4.event - -stCoEpoll_t * co_get_epoll_ct(); //ct = current thread - -//5.hook syscall ( poll/read/write/recv/send/recvfrom/sendto ) - -void co_enable_hook_sys(); -void co_disable_hook_sys(); -bool co_is_enable_sys_hook(); - -//6.sync -struct stCoCond_t; - -stCoCond_t *co_cond_alloc(); -int co_cond_free( stCoCond_t * cc ); - -int co_cond_signal( stCoCond_t * ); -int co_cond_broadcast( stCoCond_t * ); -int co_cond_timedwait( stCoCond_t *,int timeout_ms ); - -//7.share stack -stShareStack_t* co_alloc_sharestack(int iCount, int iStackSize); - -//8.init envlist for hook get/set env -void co_set_env_list( const char *name[],size_t cnt); - -void co_log_err( const char *fmt,... ); -#endif - diff --git a/trunk/3rdparty/libco/co_routine_inner.h b/trunk/3rdparty/libco/co_routine_inner.h deleted file mode 100644 index 9d0e092de..000000000 --- a/trunk/3rdparty/libco/co_routine_inner.h +++ /dev/null @@ -1,111 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - - -#ifndef __CO_ROUTINE_INNER_H__ - -#include "co_routine.h" -#include "coctx.h" -struct stCoRoutineEnv_t; -struct stCoSpec_t -{ - void *value; -}; - -struct stStackMem_t -{ - stCoRoutine_t* occupy_co; - int stack_size; - char* stack_bp; //stack_buffer + stack_size - char* stack_buffer; - -}; - -struct stShareStack_t -{ - unsigned int alloc_idx; - int stack_size; - int count; - stStackMem_t** stack_array; -}; - - - -struct stCoRoutine_t -{ - stCoRoutineEnv_t *env; - pfn_co_routine_t pfn; - void *arg; - coctx_t ctx; - - char cStart; - char cEnd; - char cIsMain; - char cEnableSysHook; - char cIsShareStack; - - void *pvEnv; - - //char sRunStack[ 1024 * 128 ]; - stStackMem_t* stack_mem; - - - //save satck buffer while confilct on same stack_buffer; - char* stack_sp; - unsigned int save_size; - char* save_buffer; - - stCoSpec_t aSpec[1024]; - -}; - - - -//1.env -void co_init_curr_thread_env(); -stCoRoutineEnv_t * co_get_curr_thread_env(); - -//2.coroutine -void co_free( stCoRoutine_t * co ); -void co_yield_env( stCoRoutineEnv_t *env ); - -//3.func - - - -//----------------------------------------------------------------------------------------------- - -struct stTimeout_t; -struct stTimeoutItem_t ; - -stTimeout_t *AllocTimeout( int iSize ); -void FreeTimeout( stTimeout_t *apTimeout ); -int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,uint64_t allNow ); - -struct stCoEpoll_t; -stCoEpoll_t * AllocEpoll(); -void FreeEpoll( stCoEpoll_t *ctx ); - -stCoRoutine_t * GetCurrThreadCo(); -void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev ); - -typedef void (*pfnCoRoutineFunc_t)(); - -#endif - -#define __CO_ROUTINE_INNER_H__ diff --git a/trunk/3rdparty/libco/co_routine_specific.h b/trunk/3rdparty/libco/co_routine_specific.h deleted file mode 100644 index 1b451caa5..000000000 --- a/trunk/3rdparty/libco/co_routine_specific.h +++ /dev/null @@ -1,86 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#pragma once -#include -#include - -/* -invoke only once in the whole program -CoRoutineSetSpecificCallBack(CoRoutineGetSpecificFunc_t pfnGet,CoRoutineSetSpecificFunc_t pfnSet) - -struct MyData_t -{ - int iValue; - char szValue[100]; -}; -CO_ROUTINE_SPECIFIC( MyData_t,__routine ); - -int main() -{ - CoRoutineSetSpecificCallBack( co_getspecific,co_setspecific ); - - __routine->iValue = 10; - strcpy( __routine->szValue,"hello world" ); - - return 0; -} -*/ -extern int co_setspecific( pthread_key_t key, const void *value ); -extern void * co_getspecific( pthread_key_t key ); - -#define CO_ROUTINE_SPECIFIC( name,y ) \ -\ -static pthread_once_t _routine_once_##name = PTHREAD_ONCE_INIT; \ -static pthread_key_t _routine_key_##name;\ -static int _routine_init_##name = 0;\ -static void _routine_make_key_##name() \ -{\ - (void) pthread_key_create(&_routine_key_##name, NULL); \ -}\ -template \ -class clsRoutineData_routine_##name\ -{\ -public:\ - inline T *operator->()\ - {\ - if( !_routine_init_##name ) \ - {\ - pthread_once( &_routine_once_##name,_routine_make_key_##name );\ - _routine_init_##name = 1;\ - }\ - T* p = (T*)co_getspecific( _routine_key_##name );\ - if( !p )\ - {\ - p = (T*)calloc(1,sizeof( T ));\ - int ret = co_setspecific( _routine_key_##name,p) ;\ - if ( ret )\ - {\ - if ( p )\ - {\ - free(p);\ - p = NULL;\ - }\ - }\ - }\ - return p;\ - }\ -};\ -\ -static clsRoutineData_routine_##name y; - diff --git a/trunk/3rdparty/libco/coctx.cpp b/trunk/3rdparty/libco/coctx.cpp deleted file mode 100644 index d5eeed148..000000000 --- a/trunk/3rdparty/libco/coctx.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco -available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "coctx.h" -#include -#include - -#define ESP 0 -#define EIP 1 -#define EAX 2 -#define ECX 3 -// ----------- -#define RSP 0 -#define RIP 1 -#define RBX 2 -#define RDI 3 -#define RSI 4 - -#define RBP 5 -#define R12 6 -#define R13 7 -#define R14 8 -#define R15 9 -#define RDX 10 -#define RCX 11 -#define R8 12 -#define R9 13 - -//----- -------- -// 32 bit -// | regs[0]: ret | -// | regs[1]: ebx | -// | regs[2]: ecx | -// | regs[3]: edx | -// | regs[4]: edi | -// | regs[5]: esi | -// | regs[6]: ebp | -// | regs[7]: eax | = esp -enum { - kEIP = 0, - kEBP = 6, - kESP = 7, -}; - -//------------- -// 64 bit -// low | regs[0]: r15 | -// | regs[1]: r14 | -// | regs[2]: r13 | -// | regs[3]: r12 | -// | regs[4]: r9 | -// | regs[5]: r8 | -// | regs[6]: rbp | -// | regs[7]: rdi | -// | regs[8]: rsi | -// | regs[9]: ret | //ret func addr -// | regs[10]: rdx | -// | regs[11]: rcx | -// | regs[12]: rbx | -// hig | regs[13]: rsp | -enum { - kRDI = 7, - kRSI = 8, - kRETAddr = 9, - kRSP = 13, -}; - -// 64 bit -extern "C" { -extern void coctx_swap(coctx_t*, coctx_t*) asm("coctx_swap"); -}; -#if defined(__i386__) -int coctx_init(coctx_t* ctx) { - memset(ctx, 0, sizeof(*ctx)); - return 0; -} -int coctx_make(coctx_t* ctx, coctx_pfn_t pfn, const void* s, const void* s1) { - // make room for coctx_param - char* sp = ctx->ss_sp + ctx->ss_size - sizeof(coctx_param_t); - sp = (char*)((unsigned long)sp & -16L); - - coctx_param_t* param = (coctx_param_t*)sp; - void** ret_addr = (void**)(sp - sizeof(void*) * 2); - *ret_addr = (void*)pfn; - param->s1 = s; - param->s2 = s1; - - memset(ctx->regs, 0, sizeof(ctx->regs)); - - ctx->regs[kESP] = (char*)(sp) - sizeof(void*) * 2; - return 0; -} -#elif defined(__x86_64__) -int coctx_make(coctx_t* ctx, coctx_pfn_t pfn, const void* s, const void* s1) { - char* sp = ctx->ss_sp + ctx->ss_size - sizeof(void*); - sp = (char*)((unsigned long)sp & -16LL); - - memset(ctx->regs, 0, sizeof(ctx->regs)); - void** ret_addr = (void**)(sp); - *ret_addr = (void*)pfn; - - ctx->regs[kRSP] = sp; - - ctx->regs[kRETAddr] = (char*)pfn; - - ctx->regs[kRDI] = (char*)s; - ctx->regs[kRSI] = (char*)s1; - return 0; -} - -int coctx_init(coctx_t* ctx) { - memset(ctx, 0, sizeof(*ctx)); - return 0; -} - -#endif diff --git a/trunk/3rdparty/libco/coctx.h b/trunk/3rdparty/libco/coctx.h deleted file mode 100644 index c1fdfa9da..000000000 --- a/trunk/3rdparty/libco/coctx.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#ifndef __CO_CTX_H__ -#define __CO_CTX_H__ -#include -typedef void* (*coctx_pfn_t)( void* s, void* s2 ); -struct coctx_param_t -{ - const void *s1; - const void *s2; -}; -struct coctx_t -{ -#if defined(__i386__) - void *regs[ 8 ]; -#else - void *regs[ 14 ]; -#endif - size_t ss_size; - char *ss_sp; - -}; - -int coctx_init( coctx_t *ctx ); -int coctx_make( coctx_t *ctx,coctx_pfn_t pfn,const void *s,const void *s1 ); -#endif diff --git a/trunk/3rdparty/libco/coctx_swap.S b/trunk/3rdparty/libco/coctx_swap.S deleted file mode 100644 index 0e4ce1c92..000000000 --- a/trunk/3rdparty/libco/coctx_swap.S +++ /dev/null @@ -1,83 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -.globl coctx_swap -#if !defined( __APPLE__ ) -.type coctx_swap, @function -#endif -coctx_swap: - -#if defined(__i386__) - movl 4(%esp), %eax - movl %esp, 28(%eax) - movl %ebp, 24(%eax) - movl %esi, 20(%eax) - movl %edi, 16(%eax) - movl %edx, 12(%eax) - movl %ecx, 8(%eax) - movl %ebx, 4(%eax) - - - movl 8(%esp), %eax - movl 4(%eax), %ebx - movl 8(%eax), %ecx - movl 12(%eax), %edx - movl 16(%eax), %edi - movl 20(%eax), %esi - movl 24(%eax), %ebp - movl 28(%eax), %esp - - ret - -#elif defined(__x86_64__) - leaq (%rsp),%rax - movq %rax, 104(%rdi) - movq %rbx, 96(%rdi) - movq %rcx, 88(%rdi) - movq %rdx, 80(%rdi) - movq 0(%rax), %rax - movq %rax, 72(%rdi) - movq %rsi, 64(%rdi) - movq %rdi, 56(%rdi) - movq %rbp, 48(%rdi) - movq %r8, 40(%rdi) - movq %r9, 32(%rdi) - movq %r12, 24(%rdi) - movq %r13, 16(%rdi) - movq %r14, 8(%rdi) - movq %r15, (%rdi) - xorq %rax, %rax - - movq 48(%rsi), %rbp - movq 104(%rsi), %rsp - movq (%rsi), %r15 - movq 8(%rsi), %r14 - movq 16(%rsi), %r13 - movq 24(%rsi), %r12 - movq 32(%rsi), %r9 - movq 40(%rsi), %r8 - movq 56(%rsi), %rdi - movq 80(%rsi), %rdx - movq 88(%rsi), %rcx - movq 96(%rsi), %rbx - leaq 8(%rsp), %rsp - pushq 72(%rsi) - - movq 64(%rsi), %rsi - ret -#endif diff --git a/trunk/3rdparty/libco/example_closure.cpp b/trunk/3rdparty/libco/example_closure.cpp deleted file mode 100644 index c5bae1cc4..000000000 --- a/trunk/3rdparty/libco/example_closure.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "co_closure.h" -#include -#include -#include -#include -#include -using namespace std; - -static void *thread_func( void * arg ) -{ - stCoClosure_t *p = (stCoClosure_t*) arg; - p->exec(); - return 0; -} -static void batch_exec( vector &v ) -{ - vector ths; - for( size_t i=0;i v; - - pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; - - int total = 100; - vector v2; - co_ref( ref,total,v2,m); - for(int i=0;i<10;i++) - { - co_func( f,ref,i ) - { - printf("ref.total %d i %d\n",ref.total,i ); - //lock - pthread_mutex_lock(&ref.m); - ref.v2.push_back( i ); - pthread_mutex_unlock(&ref.m); - //unlock - } - co_func_end; - v.push_back( new f( ref,i ) ); - } - for(int i=0;i<2;i++) - { - co_func( f2,i ) - { - printf("i: %d\n",i); - for(int j=0;j<2;j++) - { - usleep( 1000 ); - printf("i %d j %d\n",i,j); - } - } - co_func_end; - v.push_back( new f2( i ) ); - } - - batch_exec( v ); - printf("done\n"); - - return 0; -} - - diff --git a/trunk/3rdparty/libco/example_cond.cpp b/trunk/3rdparty/libco/example_cond.cpp deleted file mode 100644 index 526256807..000000000 --- a/trunk/3rdparty/libco/example_cond.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include -#include -#include -#include -#include "co_routine.h" -using namespace std; -struct stTask_t -{ - int id; -}; -struct stEnv_t -{ - stCoCond_t* cond; - queue task_queue; -}; -void* Producer(void* args) -{ - co_enable_hook_sys(); - stEnv_t* env= (stEnv_t*)args; - int id = 0; - while (true) - { - stTask_t* task = (stTask_t*)calloc(1, sizeof(stTask_t)); - task->id = id++; - env->task_queue.push(task); - printf("%s:%d produce task %d\n", __func__, __LINE__, task->id); - co_cond_signal(env->cond); - poll(NULL, 0, 1000); - } - return NULL; -} -void* Consumer(void* args) -{ - co_enable_hook_sys(); - stEnv_t* env = (stEnv_t*)args; - while (true) - { - if (env->task_queue.empty()) - { - co_cond_timedwait(env->cond, -1); - continue; - } - stTask_t* task = env->task_queue.front(); - env->task_queue.pop(); - printf("%s:%d consume task %d\n", __func__, __LINE__, task->id); - free(task); - } - return NULL; -} -int main() -{ - stEnv_t* env = new stEnv_t; - env->cond = co_cond_alloc(); - - stCoRoutine_t* consumer_routine; - co_create(&consumer_routine, NULL, Consumer, env); - co_resume(consumer_routine); - - stCoRoutine_t* producer_routine; - co_create(&producer_routine, NULL, Producer, env); - co_resume(producer_routine); - - co_eventloop(co_get_epoll_ct(), NULL, NULL); - return 0; -} diff --git a/trunk/3rdparty/libco/example_copystack.cpp b/trunk/3rdparty/libco/example_copystack.cpp deleted file mode 100644 index 92062a6ea..000000000 --- a/trunk/3rdparty/libco/example_copystack.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include -#include -#include -#include -#include -#include -#include "coctx.h" -#include "co_routine.h" -#include "co_routine_inner.h" - -void* RoutineFunc(void* args) -{ - co_enable_hook_sys(); - int* routineid = (int*)args; - while (true) - { - char sBuff[128]; - sprintf(sBuff, "from routineid %d stack addr %p\n", *routineid, sBuff); - - printf("%s", sBuff); - poll(NULL, 0, 1000); //sleep 1s - } - return NULL; -} - -int main() -{ - stShareStack_t* share_stack= co_alloc_sharestack(1, 1024 * 128); - stCoRoutineAttr_t attr; - attr.stack_size = 0; - attr.share_stack = share_stack; - - stCoRoutine_t* co[2]; - int routineid[2]; - for (int i = 0; i < 2; i++) - { - routineid[i] = i; - co_create(&co[i], &attr, RoutineFunc, routineid + i); - co_resume(co[i]); - } - co_eventloop(co_get_epoll_ct(), NULL, NULL); - return 0; -} diff --git a/trunk/3rdparty/libco/example_echocli.cpp b/trunk/3rdparty/libco/example_echocli.cpp deleted file mode 100644 index 083c1e700..000000000 --- a/trunk/3rdparty/libco/example_echocli.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "co_routine.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -struct stEndPoint -{ - char *ip; - unsigned short int port; -}; - -static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) -{ - bzero(&addr,sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(shPort); - int nIP = 0; - if( !pszIP || '\0' == *pszIP - || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") - || 0 == strcmp(pszIP,"*") - ) - { - nIP = htonl(INADDR_ANY); - } - else - { - nIP = inet_addr(pszIP); - } - addr.sin_addr.s_addr = nIP; - -} - -static int iSuccCnt = 0; -static int iFailCnt = 0; -static int iTime = 0; - -void AddSuccCnt() -{ - int now = time(NULL); - if (now >iTime) - { - printf("time %d Succ Cnt %d Fail Cnt %d\n", iTime, iSuccCnt, iFailCnt); - iTime = now; - iSuccCnt = 0; - iFailCnt = 0; - } - else - { - iSuccCnt++; - } -} -void AddFailCnt() -{ - int now = time(NULL); - if (now >iTime) - { - printf("time %d Succ Cnt %d Fail Cnt %d\n", iTime, iSuccCnt, iFailCnt); - iTime = now; - iSuccCnt = 0; - iFailCnt = 0; - } - else - { - iFailCnt++; - } -} - -static void *readwrite_routine( void *arg ) -{ - - co_enable_hook_sys(); - - stEndPoint *endpoint = (stEndPoint *)arg; - char str[8]="sarlmol"; - char buf[ 1024 * 16 ]; - int fd = -1; - int ret = 0; - for(;;) - { - if ( fd < 0 ) - { - fd = socket(PF_INET, SOCK_STREAM, 0); - struct sockaddr_in addr; - SetAddr(endpoint->ip, endpoint->port, addr); - ret = connect(fd,(struct sockaddr*)&addr,sizeof(addr)); - - if ( errno == EALREADY || errno == EINPROGRESS ) - { - struct pollfd pf = { 0 }; - pf.fd = fd; - pf.events = (POLLOUT|POLLERR|POLLHUP); - co_poll( co_get_epoll_ct(),&pf,1,200); - //check connect - int error = 0; - uint32_t socklen = sizeof(error); - errno = 0; - ret = getsockopt(fd, SOL_SOCKET, SO_ERROR,(void *)&error, &socklen); - if ( ret == -1 ) - { - //printf("getsockopt ERROR ret %d %d:%s\n", ret, errno, strerror(errno)); - close(fd); - fd = -1; - AddFailCnt(); - continue; - } - if ( error ) - { - errno = error; - //printf("connect ERROR ret %d %d:%s\n", error, errno, strerror(errno)); - close(fd); - fd = -1; - AddFailCnt(); - continue; - } - } - - } - - ret = write( fd,str, 8); - if ( ret > 0 ) - { - ret = read( fd,buf, sizeof(buf) ); - if ( ret <= 0 ) - { - //printf("co %p read ret %d errno %d (%s)\n", - // co_self(), ret,errno,strerror(errno)); - close(fd); - fd = -1; - AddFailCnt(); - } - else - { - //printf("echo %s fd %d\n", buf,fd); - AddSuccCnt(); - } - } - else - { - //printf("co %p write ret %d errno %d (%s)\n", - // co_self(), ret,errno,strerror(errno)); - close(fd); - fd = -1; - AddFailCnt(); - } - } - return 0; -} - -int main(int argc,char *argv[]) -{ - stEndPoint endpoint; - endpoint.ip = argv[1]; - endpoint.port = atoi(argv[2]); - int cnt = atoi( argv[3] ); - int proccnt = atoi( argv[4] ); - - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigaction( SIGPIPE, &sa, NULL ); - - for(int k=0;k 0 ) - { - continue; - } - else if( pid < 0 ) - { - break; - } - for(int i=0;i -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __FreeBSD__ -#include -#include -#include -#endif - -using namespace std; -struct task_t -{ - stCoRoutine_t *co; - int fd; -}; - -static stack g_readwrite; -static int g_listen_fd = -1; -static int SetNonBlock(int iSock) -{ - int iFlags; - - iFlags = fcntl(iSock, F_GETFL, 0); - iFlags |= O_NONBLOCK; - iFlags |= O_NDELAY; - int ret = fcntl(iSock, F_SETFL, iFlags); - return ret; -} - -static void *readwrite_routine( void *arg ) -{ - - co_enable_hook_sys(); - - task_t *co = (task_t*)arg; - char buf[ 1024 * 16 ]; - for(;;) - { - if( -1 == co->fd ) - { - g_readwrite.push( co ); - co_yield_ct(); - continue; - } - - int fd = co->fd; - co->fd = -1; - - for(;;) - { - struct pollfd pf = { 0 }; - pf.fd = fd; - pf.events = (POLLIN|POLLERR|POLLHUP); - co_poll( co_get_epoll_ct(),&pf,1,1000); - - int ret = read( fd,buf,sizeof(buf) ); - if( ret > 0 ) - { - ret = write( fd,buf,ret ); - } - if( ret > 0 || ( -1 == ret && EAGAIN == errno ) ) - { - continue; - } - close( fd ); - break; - } - - } - return 0; -} -int co_accept(int fd, struct sockaddr *addr, socklen_t *len ); -static void *accept_routine( void * ) -{ - co_enable_hook_sys(); - printf("accept_routine\n"); - fflush(stdout); - for(;;) - { - //printf("pid %ld g_readwrite.size %ld\n",getpid(),g_readwrite.size()); - if( g_readwrite.empty() ) - { - printf("empty\n"); //sleep - struct pollfd pf = { 0 }; - pf.fd = -1; - poll( &pf,1,1000); - - continue; - - } - struct sockaddr_in addr; //maybe sockaddr_un; - memset( &addr,0,sizeof(addr) ); - socklen_t len = sizeof(addr); - - int fd = co_accept(g_listen_fd, (struct sockaddr *)&addr, &len); - if( fd < 0 ) - { - struct pollfd pf = { 0 }; - pf.fd = g_listen_fd; - pf.events = (POLLIN|POLLERR|POLLHUP); - co_poll( co_get_epoll_ct(),&pf,1,1000 ); - continue; - } - if( g_readwrite.empty() ) - { - close( fd ); - continue; - } - SetNonBlock( fd ); - task_t *co = g_readwrite.top(); - co->fd = fd; - g_readwrite.pop(); - co_resume( co->co ); - } - return 0; -} - -static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) -{ - bzero(&addr,sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(shPort); - int nIP = 0; - if( !pszIP || '\0' == *pszIP - || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") - || 0 == strcmp(pszIP,"*") - ) - { - nIP = htonl(INADDR_ANY); - } - else - { - nIP = inet_addr(pszIP); - } - addr.sin_addr.s_addr = nIP; - -} - -static int CreateTcpSocket(const unsigned short shPort /* = 0 */,const char *pszIP /* = "*" */,bool bReuse /* = false */) -{ - int fd = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP); - if( fd >= 0 ) - { - if(shPort != 0) - { - if(bReuse) - { - int nReuseAddr = 1; - setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&nReuseAddr,sizeof(nReuseAddr)); - } - struct sockaddr_in addr ; - SetAddr(pszIP,shPort,addr); - int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); - if( ret != 0) - { - close(fd); - return -1; - } - } - } - return fd; -} - - -int main(int argc,char *argv[]) -{ - if(argc<5){ - printf("Usage:\n" - "example_echosvr [IP] [PORT] [TASK_COUNT] [PROCESS_COUNT]\n" - "example_echosvr [IP] [PORT] [TASK_COUNT] [PROCESS_COUNT] -d # daemonize mode\n"); - return -1; - } - const char *ip = argv[1]; - int port = atoi( argv[2] ); - int cnt = atoi( argv[3] ); - int proccnt = atoi( argv[4] ); - bool deamonize = argc >= 6 && strcmp(argv[5], "-d") == 0; - - g_listen_fd = CreateTcpSocket( port,ip,true ); - listen( g_listen_fd,1024 ); - if(g_listen_fd==-1){ - printf("Port %d is in use\n", port); - return -1; - } - printf("listen %d %s:%d\n",g_listen_fd,ip,port); - - SetNonBlock( g_listen_fd ); - - for(int k=0;k 0 ) - { - continue; - } - else if( pid < 0 ) - { - break; - } - for(int i=0;ifd = -1; - - co_create( &(task->co),NULL,readwrite_routine,task ); - co_resume( task->co ); - - } - stCoRoutine_t *accept_co = NULL; - co_create( &accept_co,NULL,accept_routine,0 ); - co_resume( accept_co ); - - co_eventloop( co_get_epoll_ct(),0,0 ); - - exit(0); - } - if(!deamonize) wait(NULL); - return 0; -} - diff --git a/trunk/3rdparty/libco/example_poll.cpp b/trunk/3rdparty/libco/example_poll.cpp deleted file mode 100644 index eb92f219e..000000000 --- a/trunk/3rdparty/libco/example_poll.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "co_routine.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __FreeBSD__ -#include -#endif - -using namespace std; - -struct task_t -{ - stCoRoutine_t *co; - int fd; - struct sockaddr_in addr; -}; - -static int SetNonBlock(int iSock) -{ - int iFlags; - - iFlags = fcntl(iSock, F_GETFL, 0); - iFlags |= O_NONBLOCK; - iFlags |= O_NDELAY; - int ret = fcntl(iSock, F_SETFL, iFlags); - return ret; -} - - - -static void SetAddr(const char *pszIP,const unsigned short shPort,struct sockaddr_in &addr) -{ - bzero(&addr,sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(shPort); - int nIP = 0; - if( !pszIP || '\0' == *pszIP - || 0 == strcmp(pszIP,"0") || 0 == strcmp(pszIP,"0.0.0.0") - || 0 == strcmp(pszIP,"*") - ) - { - nIP = htonl(INADDR_ANY); - } - else - { - nIP = inet_addr(pszIP); - } - addr.sin_addr.s_addr = nIP; - -} - -static int CreateTcpSocket(const unsigned short shPort = 0 ,const char *pszIP = "*" ,bool bReuse = false ) -{ - int fd = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP); - if( fd >= 0 ) - { - if(shPort != 0) - { - if(bReuse) - { - int nReuseAddr = 1; - setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&nReuseAddr,sizeof(nReuseAddr)); - } - struct sockaddr_in addr ; - SetAddr(pszIP,shPort,addr); - int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); - if( ret != 0) - { - close(fd); - return -1; - } - } - } - return fd; -} - -static void *poll_routine( void *arg ) -{ - co_enable_hook_sys(); - - vector &v = *(vector*)arg; - for(size_t i=0;i setRaiseFds; - size_t iWaitCnt = v.size(); - for(;;) - { - int ret = poll( pf,iWaitCnt,1000 ); - printf("co %p poll wait %ld ret %d\n", - co_self(),iWaitCnt,ret); - for(int i=0;i<(int)iWaitCnt;i++) - { - printf("co %p fire fd %d revents 0x%X POLLOUT 0x%X POLLERR 0x%X POLLHUP 0x%X\n", - co_self(), - pf[i].fd, - pf[i].revents, - POLLOUT, - POLLERR, - POLLHUP - ); - setRaiseFds.insert( pf[i].fd ); - } - if( setRaiseFds.size() == v.size()) - { - break; - } - if( ret <= 0 ) - { - break; - } - - iWaitCnt = 0; - for(size_t i=0;i v; - for(int i=1;i v2 = v; - poll_routine( &v2 ); - printf("--------------------- routine -------------------\n"); - - for(int i=0;i<10;i++) - { - stCoRoutine_t *co = 0; - vector *v2 = new vector(); - *v2 = v; - co_create( &co,NULL,poll_routine,v2 ); - printf("routine i %d\n",i); - co_resume( co ); - } - - co_eventloop( co_get_epoll_ct(),0,0 ); - - return 0; -} -//./example_poll 127.0.0.1 12365 127.0.0.1 12222 192.168.1.1 1000 192.168.1.2 1111 - diff --git a/trunk/3rdparty/libco/example_setenv.cpp b/trunk/3rdparty/libco/example_setenv.cpp deleted file mode 100644 index 520cfb633..000000000 --- a/trunk/3rdparty/libco/example_setenv.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include -#include -#include -#include -#include -#include -#include "co_routine.h" - -const char* CGI_ENV_HOOK_LIST [] = -{ - "CGINAME", -}; -struct stRoutineArgs_t -{ - int iRoutineID; -}; -void SetAndGetEnv(int iRoutineID) -{ - printf("routineid %d begin\n", iRoutineID); - - //use poll as sleep - poll(NULL, 0, 500); - - char sBuf[128]; - sprintf(sBuf, "cgi_routine_%d", iRoutineID); - int ret = setenv("CGINAME", sBuf, 1); - if (ret) - { - printf("%s:%d set env err ret %d errno %d %s\n", __func__, __LINE__, - ret, errno, strerror(errno)); - return; - } - printf("routineid %d set env CGINAME %s\n", iRoutineID, sBuf); - - poll(NULL, 0, 500); - - char* env = getenv("CGINAME"); - if (!env) - { - printf("%s:%d get env err errno %d %s\n", __func__, __LINE__, - errno, strerror(errno)); - return; - } - printf("routineid %d get env CGINAME %s\n", iRoutineID, env); -} - -void* RoutineFunc(void* args) -{ - co_enable_hook_sys(); - - stRoutineArgs_t* g = (stRoutineArgs_t*)args; - - SetAndGetEnv(g->iRoutineID); - return NULL; -} - -int main(int argc, char* argv[]) -{ - co_set_env_list(CGI_ENV_HOOK_LIST, sizeof(CGI_ENV_HOOK_LIST) / sizeof(char*)); - stRoutineArgs_t args[3]; - for (int i = 0; i < 3; i++) - { - stCoRoutine_t* co = NULL; - args[i].iRoutineID = i; - co_create(&co, NULL, RoutineFunc, &args[i]); - co_resume(co); - } - co_eventloop(co_get_epoll_ct(), NULL, NULL); - return 0; -} - diff --git a/trunk/3rdparty/libco/example_specific.cpp b/trunk/3rdparty/libco/example_specific.cpp deleted file mode 100644 index 5d20a8b1a..000000000 --- a/trunk/3rdparty/libco/example_specific.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "co_routine_specific.h" -#include "co_routine.h" -#include -#include -#include -#include -using namespace std; -struct stRoutineArgs_t -{ - stCoRoutine_t* co; - int routine_id; -}; -struct stRoutineSpecificData_t -{ - int idx; -}; - -CO_ROUTINE_SPECIFIC(stRoutineSpecificData_t, __routine); - -void* RoutineFunc(void* args) -{ - co_enable_hook_sys(); - stRoutineArgs_t* routine_args = (stRoutineArgs_t*)args; - __routine->idx = routine_args->routine_id; - while (true) - { - printf("%s:%d routine specific data idx %d\n", __func__, __LINE__, __routine->idx); - poll(NULL, 0, 1000); - } - return NULL; -} -int main() -{ - stRoutineArgs_t args[10]; - for (int i = 0; i < 10; i++) - { - args[i].routine_id = i; - co_create(&args[i].co, NULL, RoutineFunc, (void*)&args[i]); - co_resume(args[i].co); - } - co_eventloop(co_get_epoll_ct(), NULL, NULL); - return 0; -} diff --git a/trunk/3rdparty/libco/example_thread.cpp b/trunk/3rdparty/libco/example_thread.cpp deleted file mode 100644 index 401771071..000000000 --- a/trunk/3rdparty/libco/example_thread.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* -* Tencent is pleased to support the open source community by making Libco available. - -* Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - - - -#include "co_routine.h" -#include "co_routine_inner.h" - -#include -#include -#include -#include -#include - -int loop(void *) -{ - return 0; -} -static void *routine_func( void * ) -{ - stCoEpoll_t * ev = co_get_epoll_ct(); //ct = current thread - co_eventloop( ev,loop,0 ); - return 0; -} -int main(int argc,char *argv[]) -{ - int cnt = atoi( argv[1] ); - - pthread_t tid[ cnt ]; - for(int i=0;i +# +# To start with more than the default 64 initial pollfd slots +# (but the table grows dynamically anyway): +# DEFINES += -DST_MIN_POLLFDS_SIZE= +# +# Note that you can also add these defines by specifying them as +# make/gmake arguments (without editing this Makefile). For example: +# +# make EXTRA_CFLAGS=-DUSE_POLL +# +# (replace make with gmake if needed). +# +# You can also modify the default selection of an alternative event +# notification mechanism. E.g., to enable kqueue(2) support (if it's not +# enabled by default): +# +# gmake EXTRA_CFLAGS=-DMD_HAVE_KQUEUE +# +# or to disable default epoll(4) support: +# +# make EXTRA_CFLAGS=-UMD_HAVE_EPOLL +# +########################## + +CFLAGS += $(DEFINES) $(OTHER_FLAGS) $(EXTRA_CFLAGS) + +OBJS = $(TARGETDIR)/sched.o \ + $(TARGETDIR)/stk.o \ + $(TARGETDIR)/sync.o \ + $(TARGETDIR)/key.o \ + $(TARGETDIR)/io.o \ + $(TARGETDIR)/event.o +OBJS += $(EXTRA_OBJS) +HEADER = $(TARGETDIR)/st.h +SLIBRARY = $(TARGETDIR)/libst.a +DLIBRARY = $(TARGETDIR)/libst.$(DSO_SUFFIX).$(VERSION) +EXAMPLES = examples + +LINKNAME = libst.$(DSO_SUFFIX) +SONAME = libst.$(DSO_SUFFIX).$(MAJOR) +FULLNAME = libst.$(DSO_SUFFIX).$(VERSION) + +ifeq ($(OS), CYGWIN) +SONAME = cygst.$(DSO_SUFFIX) +SLIBRARY = $(TARGETDIR)/libst.dll.a +DLIBRARY = $(TARGETDIR)/$(SONAME) +LINKNAME = +# examples directory does not compile under cygwin +EXAMPLES = +endif + +# for SRS +# disable examples for ubuntu crossbuild failed. +# @see https://github.com/winlinvip/simple-rtmp-server/issues/308 +EXAMPLES = + +ifeq ($(OS), DARWIN) +LINKNAME = libst.$(DSO_SUFFIX) +SONAME = libst.$(MAJOR).$(DSO_SUFFIX) +FULLNAME = libst.$(VERSION).$(DSO_SUFFIX) +endif + +ifeq ($(STATIC_ONLY), yes) +LIBRARIES = $(SLIBRARY) +else +LIBRARIES = $(SLIBRARY) $(DLIBRARY) +endif + +ifeq ($(OS),) +ST_ALL = unknown +else +ST_ALL = $(TARGETDIR) $(LIBRARIES) $(HEADER) $(EXAMPLES) $(DESC) +endif + +all: $(ST_ALL) + +unknown: + @echo + @echo "Please specify one of the following targets:" + @echo + @for target in $(TARGETS); do echo $$target; done + @echo + +st.pc: st.pc.in + sed "s/@VERSION@/${VERSION}/g" < $< > $@ + +$(TARGETDIR): + if [ ! -d $(TARGETDIR) ]; then mkdir $(TARGETDIR); fi + +$(SLIBRARY): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + $(RANLIB) $@ + rm -f obj; $(LN) $(LNFLAGS) $(TARGETDIR) obj + +$(DLIBRARY): $(OBJS:%.o=%-pic.o) + $(LD) $(LDFLAGS) $^ -o $@ + if test "$(LINKNAME)"; then \ + cd $(TARGETDIR); \ + rm -f $(SONAME) $(LINKNAME); \ + $(LN) $(LNFLAGS) $(FULLNAME) $(SONAME); \ + $(LN) $(LNFLAGS) $(FULLNAME) $(LINKNAME); \ + fi + +$(HEADER): public.h + rm -f $@ + cp public.h $@ + +$(TARGETDIR)/md.o: md.S + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGETDIR)/%.o: %.c common.h md.h + $(CC) $(CFLAGS) -c $< -o $@ + +examples:: + @echo Making $@ + @cd $@; $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" OS="$(OS)" TARGETDIR="$(TARGETDIR)" + +clean: + rm -rf *_OPT *_DBG obj st.pc + +########################## +# Pattern rules: + +ifneq ($(SFLAGS),) +# Compile with shared library options if it's a C file +$(TARGETDIR)/%-pic.o: %.c common.h md.h + $(CC) $(CFLAGS) $(SFLAGS) -c $< -o $@ +endif + +# Compile assembly as normal or C as normal if no SFLAGS +%-pic.o: %.o + rm -f $@; $(LN) $(LNFLAGS) $(. Install them with: + # rpm -i libst*.rpm +Requires GNU automake and rpm 3.0.3 or later. + +Debian users: + If you run potato, please upgrade to woody. + If you run woody, "apt-get install libst-dev" will get you v1.3. + If you run testing/unstable, you will get the newest available version. + If you *must* have the newest libst in woody, you may follow these + not-recommended instructions: + 1. Add "deb-src unstable main" to your + /etc/apt/sources.list + 2. apt-get update + 3. apt-get source st + 4. cd st-1.4 (or whatever version you got) + 5. debuild + 6. dpkg -i ../*.deb + +If your application uses autoconf to search for dependencies and you +want to search for a given version of libst, you can simply add + PKG_CHECK_MODULES(MYAPP, st >= 1.3 mumble >= 0.2.23) +to your configure.ac/in. This will define @MYAPP_LIBS@ and +@MYAPP_CFLAGS@ which you may then use in your Makefile.am/in files to +link against mumble and st. + + +LICENSE + +The State Threads library is a derivative of the Netscape Portable +Runtime library (NSPR). All source code in this directory is +distributed under the terms of the Mozilla Public License (MPL) version +1.1 or the GNU General Public License (GPL) version 2 or later. For +more information about these licenses please see +http://www.mozilla.org/MPL/ and http://www.gnu.org/copyleft/. + +All source code in the "examples" directory is distributed under the BSD +style license. + + +PLATFORMS + +Please see the "docs/notes.html" file for the list of currently +supported platforms. + + +DEBUGGER SUPPORT + +It's almost impossible to print SP and PC in a portable way. The only +way to see thread's stack platform-independently is to actually jump to +the saved context. That's what the _st_iterate_threads() function does. +Do the following to iterate over all threads: + +- set the _st_iterate_threads_flag to 1 in debugger +- set breakpoint at the _st_show_thread_stack() function + (which does nothing) +- call the _st_iterate_threads() function which jumps to the + next thread +- at each break you can explore thread's stack +- continue +- when iteration is complete, you return to the original + point (you can see thread id and a message as arguments of + the _st_show_thread_stack() function). + +You can call _st_iterate_threads() in three ways: + +- Insert it into your source code at the point you want to + go over threads. +- Just run application and this function will be called at + the first context switch. +- Call it directly from the debugger at any point. + +This works with gdb and dbx. + +Example using gdb: + +(gdb) set _st_iterate_threads_flag = 1 +(gdb) b _st_show_thread_stack +... +(gdb) call _st_iterate_threads() +... +(gdb) bt +... +(gdb) c +... +(gdb) bt +... +(gdb) c +... +and so on... + +_st_iterate_threads_flag will be set to 0 automatically +after iteration is over or you can set it to 0 at any time +to stop iteration. + +Sometimes gdb complains about SIGSEGV when you call a function +directly at gdb command-line. It can be ignored -- just call the +same function right away again, it works just fine. For example: + +(gdb) set _st_iterate_threads_flag = 1 +(gdb) b _st_show_thread_stack +Breakpoint 1 at 0x809bbbb: file sched.c, line 856. +(gdb) call _st_iterate_threads() +Program received signal SIGSEGV, Segmentation fault. +.... +(gdb) # just call the function again: +(gdb) call _st_iterate_threads() +Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 +"Iteration started") at sched.c:856 +856 } +.... + +You can use simple gdb command-line scripting to display +all threads and their stack traces at once: + +(gdb) while _st_iterate_threads_flag + >bt + >c + >end +.... + +Another script to stop at the thread with the specific thread id +(e.g., 0x40252ee4): + +(gdb) # set the flag again: +(gdb) set _st_iterate_threads_flag = 1 +(gdb) call _st_iterate_threads() +Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 +"Iteration started") at sched.c:856 +856 } +.... +(gdb) while thread != 0x40252ee4 + >c + >end +.... +.... +Breakpoint 1, _st_show_thread_stack (thread=0x40252ee4, messg=0x0) at +sched.c:856 +856 } +(gdb) bt +.... +(gdb) # don't want to continue iteration, unset the flag: +(gdb) set _st_iterate_threads_flag = 0 +(gdb) c +Continuing. +Breakpoint 1, _st_show_thread_stack (thread=0x0, messg=0x80ae78e "Iteration +completed") + at sched.c:856 +856 } +(gdb) c +Continuing. +(gdb) return +Make selected stack frame return now? (y or n) y +#0 0x4011254e in __select () + from /lib/libc.so.6 +(gdb) detach + + +CHANGE LOG + +Changes from 1.8 to 1.9. +------------------------ +o Support 32-bit and 64-bit Intel Macs. + +o Added ST_VERSION string, and ST_VERSION_MAJOR and ST_VERSION_MINOR + [bug 1796801]. + +o Fixed some compiler warnings, based on a patch from Brian Wellington + [bug 1932741]. + + +Changes from 1.7 to 1.8. +-------------------------- +o Added support for kqueue and epoll on platforms that support them. + Added ability to choose the event notification system at program + startup. + +o Long-overdue public definitions of ST_UTIME_NO_TIMEOUT (-1ULL) and + ST_UTIME_NO_WAIT (0) [bug 1514436]. + +o Documentation patch for st_utime() [bug 1514484]. + +o Documentation patch for st_timecache_set() [bug 1514486]. + +o Documentation patch for st_netfd_serialize_accept() [bug 1514494]. + +o Added st_writev_resid() [rfe 1538344]. + +o Added st_readv_resid() [rfe 1538768] and, for symmetry, st_readv(). + + +Changes from 1.6 to 1.7. +------------------------ +o Support glibc 2.4, which breaks programs that manipulate jump buffers. + Replaced Linux IA64 special cases with new md.S that covers all + Linux. + + +Changes from 1.5.2 to 1.6. +-------------------------- +none + + +Changes from 1.5.1 to 1.5.2. +---------------------------- +o Alfred Perlstein's context switch callback feature. + +o Claus Assmann's st_recvmsg/st_sendmsg wrappers. + +o Extra stack padding for platforms that need it. + +o Ron Arts's timeout clarifications in the reference manual. + +o Raymond Bero and Anton Berezin's AMD64 FreeBSD port. + +o Claus Assmann's AMD64 SunOS 5.10 port. + +o Claus Assmann's AMD64 OpenBSD port. + +o Michael Abd-El-Malek's Mac OS X port. + +o Michael Abd-El-Malek's stack printing patch. + + +Changes from 1.5.0 to 1.5.1. +---------------------------- +o Andreas Gustafsson's USE_POLL fix. + +o Gene's st_set_utime_function() enhancement. + + +Changes from 1.4 to 1.5.0. +-------------------------- +o Andreas Gustafsson's performance patch. + +o New extensions: Improved DNS resolver, generic LRU cache, in-process + DNS cache, and a program to test the resolver and cache. + +o Support for AMD Opteron 64-bit CPUs under Linux. + +o Support for SPARC-64 under Solaris. + +o Andreas Gustafsson's support for VAX under NetBSD. + +o Changed unportable #warning directives in md.h to #error. + + +Changes from 1.3 to 1.4. +------------------------ +o Andreas Gustafsson's NetBSD port. + +o Wesley W. Terpstra's Darwin (MacOS X) port. + +o Support for many CPU architectures under Linux and *BSD. + +o Renamed private typedefs so they don't conflict with public ones any + more. + +o common.h now includes public.h for strict prototyping. + +o Joshua Levy's recommendation to make st_connect() and st_sendto() + accept const struct sockaddr pointers, as the originals do. + +o Clarified the documentation regarding blocking vs. non-blocking I/O. + +o Cygwin support. + +o Created the extensions directory. + +o Fixed warnings from ia64asm.S. + + +Changes from 1.2 to 1.3. +------------------------ +o Added st_read_resid() and st_write_resid() to allow the caller to know + how much data was transferred before an error occurred. Updated + documentation. + +o Updated project link, copyrights, and documentation regarding + timeouts. Added comment to st_connect(). + +o Optimized the _st_add_sleep_q() function in sched.c. Now we walk the + sleep queue *backward* when inserting a thread into it. When you + have lots (hundreds) of threads and several timeout values, it takes + a while to insert a thread at the appropriate point in the sleep + queue. The idea is that often this appropriate point is closer to + the end of the queue rather than the beginning. Measurements show + performance improves with this change. In any case this change + should do no harm. + +o Added a hint of when to define USE_POLL and when not to, to the + Makefile. + +o Added debugging support (files common.h and sched.c). See above. + +o Decreased the number of reallocations of _ST_POLLFDS in sched.c. + Inspired by Lev Walkin. + +o Fixed st_usleep(-1) and st_sleep(-1), and added a warning to the + documentation about too-large timeouts. + +o Linux/*BSD Alpha port. + +o Wesley W. Terpstra modernized the build process: + - properly build relocatable libraries under bsd and linux + - use library versioning + - added rpm spec file + - added debian/ files + See above for build instructions. + + +Changes from 1.1 to 1.2. +------------------------ +o Added st_randomize_stacks(). + +o Added a patch contributed by Sascha Schumann. + + +Changes from 1.0 to 1.1. +------------------------ +o Relicensed under dual MPL-GPL. + +o OpenBSD port. + +o Compile-time option to use poll() instead of select() for + event polling (see Makefile). + This is useful if you want to support a large number of open + file descriptors (larger than FD_SETSIZE) within a single + process. + +o Linux IA-64 port. + Two issues make IA-64 different from other platforms: + + - Besides the traditional call stack in memory, IA-64 uses the + general register stack. Thus each thread needs a backing store + for the register stack in addition to the memory stack. + + - Current implementation of setjmp()/longjmp() can not be used + for thread context-switching since it assumes that only one + register stack exists. Using special assembly functions for + context-switching is unavoidable. + +o Thread stack capping on IRIX. + This allows some profiling tools (such as SpeedShop) to know when + to stop unwinding the stack. Without this libexc, used by SpeedShop, + traces right off the stack and crashes. + +o Miscellaneous documentation additions. + + +COPYRIGHTS + +Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. +All Rights Reserved. diff --git a/trunk/3rdparty/st-srs/README.md b/trunk/3rdparty/st-srs/README.md new file mode 100644 index 000000000..60fc1a651 --- /dev/null +++ b/trunk/3rdparty/st-srs/README.md @@ -0,0 +1,88 @@ +# state-threads + +![](http://ossrs.net:8000/gif/v1/sls.gif?site=github.com&path=/srs/srsst) +[![](https://cloud.githubusercontent.com/assets/2777660/22814959/c51cbe72-ef92-11e6-81cc-32b657b285d5.png)](https://github.com/ossrs/srs/wiki/v1_CN_Contact#wechat) + +Fork from http://sourceforge.net/projects/state-threads, patched for [SRS](https://github.com/ossrs/srs/tree/2.0release). + +> See: https://github.com/ossrs/state-threads/blob/srs/README + +For original ST without any changes, checkout the [ST master branch](https://github.com/ossrs/state-threads/tree/master). + +## Branch SRS + +The branch [srs](https://github.com/ossrs/state-threads/tree/srs) will be patched the following patches: + +- [x] Patch [st.arm.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/1.st.arm.patch), for ARM. +- [x] Patch [st.osx.kqueue.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/3.st.osx.kqueue.patch), for osx. +- [x] Patch [st.disable.examples.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/4.st.disable.examples.patch), for ubuntu. +- [x] [Refine TAB of code](https://github.com/ossrs/state-threads/compare/c2001d30ca58f55d72a6cc6b9b6c70391eaf14db...d2101b26988b0e0db0aabc53ddf452068c1e2cbc). +- [x] Merge from [michaeltalyansky](https://github.com/michaeltalyansky/state-threads) and [xzh3836598](https://github.com/ossrs/state-threads/commit/9a17dec8f9c2814d93761665df7c5575a4d2d8a3), support [ARM](https://github.com/ossrs/state-threads/issues/1). +- [x] Merge from [toffaletti](https://github.com/toffaletti/state-threads), support [valgrind](https://github.com/ossrs/state-threads/issues/2) for ST. +- [x] Patch [st.osx10.14.build.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/6.st.osx10.14.build.patch), for osx 10.14 build. +- [x] Support macro `MD_ST_NO_ASM` to disable ASM, [#8](https://github.com/ossrs/state-threads/issues/8). +- [x] Merge patch [srs#1282](https://github.com/ossrs/srs/issues/1282#issuecomment-445539513) to support aarch64, [#9](https://github.com/ossrs/state-threads/issues/9). + +## Docs + +* Introduction: http://ossrs.github.io/state-threads/docs/st.html +* API reference: http://ossrs.github.io/state-threads/docs/reference.html +* Programming notes: http://ossrs.github.io/state-threads/docs/notes.html + +## Usage + +Get code: + +``` +git clone https://github.com/ossrs/state-threads.git st-1.9 && +git checkout -b srs origin/srs +``` + +For Linux: + +``` +make linux-debug EXTRA_CFLAGS="-DMD_HAVE_EPOLL" +``` + +For OSX: + +``` +make darwin-debug EXTRA_CFLAGS="-DMD_HAVE_KQUEUE" +``` + +Linux with valgrind: + +``` +make linux-debug EXTRA_CFLAGS="-DMD_VALGRIND" +``` + +> Remark: User must install valgrind, for instance, in centos6 `sudo yum install -y valgrind valgrind-devel`. + +Linux with valgrind and epoll: + +``` +make linux-debug EXTRA_CFLAGS="-DMD_HAVE_EPOLL -DMD_VALGRIND" +``` + +For OSX, user must specifies the valgrind header files: + +``` +make darwin-debug EXTRA_CFLAGS="-DMD_HAVE_KQUEUE -DMD_VALGRIND -I/usr/local/include" +``` + +> Remark: Latest OSX does not support ST, please use docker to run ST. + +## Valgrind + +How to debug with gdb under valgrind, read [valgrind manual](http://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.gdbserver-simple). + +About startup parameters, read [valgrind cli](http://valgrind.org/docs/manual/mc-manual.html#mc-manual.options). + +Important cli options: + +1. `--undef-value-errors= [default: yes]`, Controls whether Memcheck reports uses of undefined value errors. Set this to no if you don't want to see undefined value errors. It also has the side effect of speeding up Memcheck somewhat. +1. `--leak-check= [default: summary]`, When enabled, search for memory leaks when the client program finishes. If set to summary, it says how many leaks occurred. If set to full or yes, each individual leak will be shown in detail and/or counted as an error, as specified by the options `--show-leak-kinds` and `--errors-for-leak-kinds`. +1. `--track-origins= [default: no]`, Controls whether Memcheck tracks the origin of uninitialised values. By default, it does not, which means that although it can tell you that an uninitialised value is being used in a dangerous way, it cannot tell you where the uninitialised value came from. This often makes it difficult to track down the root problem. +1. `--show-reachable= , --show-possibly-lost=`, to show the using memory. + +Winlin 2016 diff --git a/trunk/3rdparty/st-srs/common.h b/trunk/3rdparty/st-srs/common.h new file mode 100644 index 000000000..0c0685b9a --- /dev/null +++ b/trunk/3rdparty/st-srs/common.h @@ -0,0 +1,479 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#ifndef __ST_COMMON_H__ +#define __ST_COMMON_H__ + +#include +#include +#include +#include +#include + +/* Enable assertions only if DEBUG is defined */ +#ifndef DEBUG + #define NDEBUG +#endif +#include +#define ST_ASSERT(expr) assert(expr) + +#define ST_BEGIN_MACRO { +#define ST_END_MACRO } + +#ifdef DEBUG + #define ST_HIDDEN /*nothing*/ +#else + #define ST_HIDDEN static +#endif + +#include "public.h" +#include "md.h" + +/* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ +#ifndef MD_VALGRIND + #ifndef NVALGRIND + #define NVALGRIND + #endif +#else + #undef NVALGRIND +#endif + + +/***************************************** + * Circular linked list definitions + */ + +typedef struct _st_clist { + struct _st_clist *next; + struct _st_clist *prev; +} _st_clist_t; + +/* Insert element "_e" into the list, before "_l" */ +#define ST_INSERT_BEFORE(_e,_l) \ + ST_BEGIN_MACRO \ + (_e)->next = (_l); \ + (_e)->prev = (_l)->prev; \ + (_l)->prev->next = (_e); \ + (_l)->prev = (_e); \ + ST_END_MACRO + +/* Insert element "_e" into the list, after "_l" */ +#define ST_INSERT_AFTER(_e,_l) \ + ST_BEGIN_MACRO \ + (_e)->next = (_l)->next; \ + (_e)->prev = (_l); \ + (_l)->next->prev = (_e); \ + (_l)->next = (_e); \ + ST_END_MACRO + +/* Return the element following element "_e" */ +#define ST_NEXT_LINK(_e) ((_e)->next) + +/* Append an element "_e" to the end of the list "_l" */ +#define ST_APPEND_LINK(_e,_l) ST_INSERT_BEFORE(_e,_l) + +/* Insert an element "_e" at the head of the list "_l" */ +#define ST_INSERT_LINK(_e,_l) ST_INSERT_AFTER(_e,_l) + +/* Return the head/tail of the list */ +#define ST_LIST_HEAD(_l) (_l)->next +#define ST_LIST_TAIL(_l) (_l)->prev + +/* Remove the element "_e" from it's circular list */ +#define ST_REMOVE_LINK(_e) \ + ST_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + ST_END_MACRO + +/* Return non-zero if the given circular list "_l" is empty, */ +/* zero if the circular list is not empty */ +#define ST_CLIST_IS_EMPTY(_l) \ + ((_l)->next == (_l)) + +/* Initialize a circular list */ +#define ST_INIT_CLIST(_l) \ + ST_BEGIN_MACRO \ + (_l)->next = (_l); \ + (_l)->prev = (_l); \ + ST_END_MACRO + +#define ST_INIT_STATIC_CLIST(_l) \ + {(_l), (_l)} + + +/***************************************** + * Basic types definitions + */ + +typedef void (*_st_destructor_t)(void *); + + +typedef struct _st_stack { + _st_clist_t links; + char *vaddr; /* Base of stack's allocated memory */ + int vaddr_size; /* Size of stack's allocated memory */ + int stk_size; /* Size of usable portion of the stack */ + char *stk_bottom; /* Lowest address of stack's usable portion */ + char *stk_top; /* Highest address of stack's usable portion */ + void *sp; /* Stack pointer from C's point of view */ +#ifdef __ia64__ + void *bsp; /* Register stack backing store pointer */ +#endif + /* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ +#ifndef NVALGRIND + /* id returned by VALGRIND_STACK_REGISTER */ + /* http://valgrind.org/docs/manual/manual-core-adv.html */ + unsigned long valgrind_stack_id; +#endif +} _st_stack_t; + + +typedef struct _st_cond { + _st_clist_t wait_q; /* Condition variable wait queue */ +} _st_cond_t; + + +typedef struct _st_thread _st_thread_t; + +struct _st_thread { + int state; /* Thread's state */ + int flags; /* Thread's flags */ + + void *(*start)(void *arg); /* The start function of the thread */ + void *arg; /* Argument of the start function */ + void *retval; /* Return value of the start function */ + + _st_stack_t *stack; /* Info about thread's stack */ + + _st_clist_t links; /* For putting on run/sleep/zombie queue */ + _st_clist_t wait_links; /* For putting on mutex/condvar wait queue */ +#ifdef DEBUG + _st_clist_t tlink; /* For putting on thread queue */ +#endif + + st_utime_t due; /* Wakeup time when thread is sleeping */ + _st_thread_t *left; /* For putting in timeout heap */ + _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ + int heap_index; + + void **private_data; /* Per thread private data */ + + _st_cond_t *term; /* Termination condition variable for join */ + + jmp_buf context; /* Thread's context */ +}; + + +typedef struct _st_mutex { + _st_thread_t *owner; /* Current mutex owner */ + _st_clist_t wait_q; /* Mutex wait queue */ +} _st_mutex_t; + + +typedef struct _st_pollq { + _st_clist_t links; /* For putting on io queue */ + _st_thread_t *thread; /* Polling thread */ + struct pollfd *pds; /* Array of poll descriptors */ + int npds; /* Length of the array */ + int on_ioq; /* Is it on ioq? */ +} _st_pollq_t; + + +typedef struct _st_eventsys_ops { + const char *name; /* Name of this event system */ + int val; /* Type of this event system */ + int (*init)(void); /* Initialization */ + void (*dispatch)(void); /* Dispatch function */ + int (*pollset_add)(struct pollfd *, int); /* Add descriptor set */ + void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */ + int (*fd_new)(int); /* New descriptor allocated */ + int (*fd_close)(int); /* Descriptor closed */ + int (*fd_getlimit)(void); /* Descriptor hard limit */ +} _st_eventsys_t; + + +typedef struct _st_vp { + _st_thread_t *idle_thread; /* Idle thread for this vp */ + st_utime_t last_clock; /* The last time we went into vp_check_clock() */ + + _st_clist_t run_q; /* run queue for this vp */ + _st_clist_t io_q; /* io queue for this vp */ + _st_clist_t zombie_q; /* zombie queue for this vp */ +#ifdef DEBUG + _st_clist_t thread_q; /* all threads of this vp */ +#endif + int pagesize; + + _st_thread_t *sleep_q; /* sleep queue for this vp */ + int sleepq_size; /* number of threads on sleep queue */ + +#ifdef ST_SWITCH_CB + st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ + st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ +#endif +} _st_vp_t; + + +typedef struct _st_netfd { + int osfd; /* Underlying OS file descriptor */ + int inuse; /* In-use flag */ + void *private_data; /* Per descriptor private data */ + _st_destructor_t destructor; /* Private data destructor function */ + void *aux_data; /* Auxiliary data for internal use */ + struct _st_netfd *next; /* For putting on the free list */ +} _st_netfd_t; + + +/***************************************** + * Current vp, thread, and event system + */ + +extern _st_vp_t _st_this_vp; +extern _st_thread_t *_st_this_thread; +extern _st_eventsys_t *_st_eventsys; + +#define _ST_CURRENT_THREAD() (_st_this_thread) +#define _ST_SET_CURRENT_THREAD(_thread) (_st_this_thread = (_thread)) + +#define _ST_LAST_CLOCK (_st_this_vp.last_clock) + +#define _ST_RUNQ (_st_this_vp.run_q) +#define _ST_IOQ (_st_this_vp.io_q) +#define _ST_ZOMBIEQ (_st_this_vp.zombie_q) +#ifdef DEBUG + #define _ST_THREADQ (_st_this_vp.thread_q) +#endif + +#define _ST_PAGE_SIZE (_st_this_vp.pagesize) + +#define _ST_SLEEPQ (_st_this_vp.sleep_q) +#define _ST_SLEEPQ_SIZE (_st_this_vp.sleepq_size) + +#define _ST_VP_IDLE() (*_st_eventsys->dispatch)() + + +/***************************************** + * vp queues operations + */ + +#define _ST_ADD_IOQ(_pq) ST_APPEND_LINK(&_pq.links, &_ST_IOQ) +#define _ST_DEL_IOQ(_pq) ST_REMOVE_LINK(&_pq.links) + +#define _ST_ADD_RUNQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_RUNQ) +#define _ST_DEL_RUNQ(_thr) ST_REMOVE_LINK(&(_thr)->links) + +#define _ST_ADD_SLEEPQ(_thr, _timeout) _st_add_sleep_q(_thr, _timeout) +#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr) + +#define _ST_ADD_ZOMBIEQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ) +#define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links) + +#ifdef DEBUG + #define _ST_ADD_THREADQ(_thr) ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ) + #define _ST_DEL_THREADQ(_thr) ST_REMOVE_LINK(&(_thr)->tlink) +#endif + + +/***************************************** + * Thread states and flags + */ + +#define _ST_ST_RUNNING 0 +#define _ST_ST_RUNNABLE 1 +#define _ST_ST_IO_WAIT 2 +#define _ST_ST_LOCK_WAIT 3 +#define _ST_ST_COND_WAIT 4 +#define _ST_ST_SLEEPING 5 +#define _ST_ST_ZOMBIE 6 +#define _ST_ST_SUSPENDED 7 + +#define _ST_FL_PRIMORDIAL 0x01 +#define _ST_FL_IDLE_THREAD 0x02 +#define _ST_FL_ON_SLEEPQ 0x04 +#define _ST_FL_INTERRUPT 0x08 +#define _ST_FL_TIMEDOUT 0x10 + + +/***************************************** + * Pointer conversion + */ + +#ifndef offsetof + #define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) +#endif + +#define _ST_THREAD_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, links))) + +#define _ST_THREAD_WAITQ_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, wait_links))) + +#define _ST_THREAD_STACK_PTR(_qp) \ + ((_st_stack_t *)((char*)(_qp) - offsetof(_st_stack_t, links))) + +#define _ST_POLLQUEUE_PTR(_qp) \ + ((_st_pollq_t *)((char *)(_qp) - offsetof(_st_pollq_t, links))) + +#ifdef DEBUG + #define _ST_THREAD_THREADQ_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink))) +#endif + + +/***************************************** + * Constants + */ + +#ifndef ST_UTIME_NO_TIMEOUT + #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) +#endif + +#ifndef __ia64__ + #define ST_DEFAULT_STACK_SIZE (64*1024) +#else + #define ST_DEFAULT_STACK_SIZE (128*1024) /* Includes register stack size */ +#endif + +#ifndef ST_KEYS_MAX + #define ST_KEYS_MAX 16 +#endif + +#ifndef ST_MIN_POLLFDS_SIZE + #define ST_MIN_POLLFDS_SIZE 64 +#endif + + +/***************************************** + * Threads context switching + */ + +#ifdef DEBUG + void _st_iterate_threads(void); + #define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() +#else + #define ST_DEBUG_ITERATE_THREADS() +#endif + +#ifdef ST_SWITCH_CB + #define ST_SWITCH_OUT_CB(_thread) \ + if (_st_this_vp.switch_out_cb != NULL && \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_out_cb(); \ + } + #define ST_SWITCH_IN_CB(_thread) \ + if (_st_this_vp.switch_in_cb != NULL && \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_in_cb(); \ + } +#else + #define ST_SWITCH_OUT_CB(_thread) + #define ST_SWITCH_IN_CB(_thread) +#endif + +/* + * Switch away from the current thread context by saving its state and + * calling the thread scheduler + */ +#define _ST_SWITCH_CONTEXT(_thread) \ + ST_BEGIN_MACRO \ + ST_SWITCH_OUT_CB(_thread); \ + if (!MD_SETJMP((_thread)->context)) { \ + _st_vp_schedule(); \ + } \ + ST_DEBUG_ITERATE_THREADS(); \ + ST_SWITCH_IN_CB(_thread); \ + ST_END_MACRO + +/* + * Restore a thread context that was saved by _ST_SWITCH_CONTEXT or + * initialized by _ST_INIT_CONTEXT + */ +#define _ST_RESTORE_CONTEXT(_thread) \ + ST_BEGIN_MACRO \ + _ST_SET_CURRENT_THREAD(_thread); \ + MD_LONGJMP((_thread)->context, 1); \ + ST_END_MACRO + +/* + * Initialize the thread context preparing it to execute _main + */ +#ifdef MD_INIT_CONTEXT + #define _ST_INIT_CONTEXT MD_INIT_CONTEXT +#else + #error Unknown OS +#endif + +/* + * Number of bytes reserved under the stack "bottom" + */ +#define _ST_STACK_PAD_SIZE MD_STACK_PAD_SIZE + + +/***************************************** + * Forward declarations + */ + +void _st_vp_schedule(void); +void _st_vp_check_clock(void); +void *_st_idle_thread_start(void *arg); +void _st_thread_main(void); +void _st_thread_cleanup(_st_thread_t *thread); +void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout); +void _st_del_sleep_q(_st_thread_t *thread); +_st_stack_t *_st_stack_new(int stack_size); +void _st_stack_free(_st_stack_t *ts); +int _st_io_init(void); + +st_utime_t st_utime(void); +_st_cond_t *st_cond_new(void); +int st_cond_destroy(_st_cond_t *cvar); +int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout); +int st_cond_signal(_st_cond_t *cvar); +ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout); +ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout); +int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); +_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size); + +#endif /* !__ST_COMMON_H__ */ + diff --git a/trunk/3rdparty/st-srs/docs/fig.gif b/trunk/3rdparty/st-srs/docs/fig.gif new file mode 100644 index 0000000000000000000000000000000000000000..7265a05db4f516b44fcf37c3949922ff4f62999d GIT binary patch literal 5374 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 literal 0 HcmV?d00001 diff --git a/trunk/3rdparty/st-srs/docs/notes.html b/trunk/3rdparty/st-srs/docs/notes.html new file mode 100644 index 000000000..5a24369e2 --- /dev/null +++ b/trunk/3rdparty/st-srs/docs/notes.html @@ -0,0 +1,434 @@ + + +State Threads Library Programming Notes + + +

Programming Notes

+

+ +

+ +

+


+

+ +

Porting

+The State Threads library uses OS concepts that are available in some +form on most UNIX platforms, making the library very portable across +many flavors of UNIX. However, there are several parts of the library +that rely on platform-specific features. Here is the list of such parts: +

+

    +
  • Thread context initialization: Two ingredients of the +jmp_buf +data structure (the program counter and the stack pointer) have to be +manually set in the thread creation routine. The jmp_buf data +structure is defined in the setjmp.h header file and differs from +platform to platform. Usually the program counter is a structure member +with PC in the name and the stack pointer is a structure member +with SP in the name. One can also look in the +Netscape's NSPR library source +which already has this code for many UNIX-like platforms +(mozilla/nsprpub/pr/include/md/*.h files). +

    +Note that on some BSD-derived platforms _setjmp(3)/_longjmp(3) +calls should be used instead of setjmp(3)/longjmp(3) (that is +the calls that manipulate only the stack and registers and do not +save and restore the process's signal mask).

  • +

    +Starting with glibc 2.4 on Linux the opacity of the jmp_buf data +structure is enforced by setjmp(3)/longjmp(3) so the +jmp_buf ingredients cannot be accessed directly anymore (unless +special environmental variable LD_POINTER_GUARD is set before application +execution). To avoid dependency on custom environment, the State Threads +library provides setjmp/longjmp replacement functions for +all Intel CPU architectures. Other CPU architectures can also be easily +supported (the setjmp/longjmp source code is widely available for +many CPU architectures). +

    +

  • High resolution time function: Some platforms (IRIX, Solaris) +provide a high resolution time function based on the free running hardware +counter. This function returns the time counted since some arbitrary +moment in the past (usually machine power up time). It is not correlated in +any way to the time of day, and thus is not subject to resetting, +drifting, etc. This type of time is ideal for tasks where cheap, accurate +interval timing is required. If such a function is not available on a +particular platform, the gettimeofday(3) function can be used +(though on some platforms it involves a system call). +

    +

  • The stack growth direction: The library needs to know whether the +stack grows toward lower (down) or higher (up) memory addresses. +One can write a simple test program that detects the stack growth direction +on a particular platform.
  • +

    +

  • Non-blocking attribute inheritance: On some platforms (e.g. IRIX) +the socket created as a result of the accept(2) call inherits the +non-blocking attribute of the listening socket. One needs to consult the manual +pages or write a simple test program to see if this applies to a specific +platform.
  • +

    +

  • Anonymous memory mapping: The library allocates memory segments +for thread stacks by doing anonymous memory mapping (mmap(2)). This +mapping is somewhat different on SVR4 and BSD4.3 derived platforms. +

    +The memory mapping can be avoided altogether by using malloc(3) for +stack allocation. In this case the MALLOC_STACK macro should be +defined.

  • +
+

+All machine-dependent feature test macros should be defined in the +md.h header file. The assembly code for setjmp/longjmp +replacement functions for all CPU architectures should be placed in +the md.S file. +

+The current version of the library is ported to: +

    +
  • IRIX 6.x (both 32 and 64 bit)
  • +
  • Linux (kernel 2.x and glibc 2.x) on x86, Alpha, MIPS and MIPSEL, + SPARC, ARM, PowerPC, 68k, HPPA, S390, IA-64, and Opteron (AMD-64)
  • +
  • Solaris 2.x (SunOS 5.x) on x86, AMD64, SPARC, and SPARC-64
  • +
  • AIX 4.x
  • +
  • HP-UX 11 (both 32 and 64 bit)
  • +
  • Tru64/OSF1
  • +
  • FreeBSD on x86, AMD64, and Alpha
  • +
  • OpenBSD on x86, AMD64, Alpha, and SPARC
  • +
  • NetBSD on x86, Alpha, SPARC, and VAX
  • +
  • MacOS X (Darwin) on PowerPC (32 bit) and Intel (both 32 and 64 bit) [universal]
  • +
  • Cygwin
  • +
+

+ + +

Signals

+Signal handling in an application using State Threads should be treated the +same way as in a classical UNIX process application. There is no such +thing as per-thread signal mask, all threads share the same signal handlers, +and only asynchronous-safe functions can be used in signal handlers. +However, there is a way to process signals synchronously by converting a +signal event to an I/O event: a signal catching function does a write to +a pipe which will be processed synchronously by a dedicated signal handling +thread. The following code demonstrates this technique (error handling is +omitted for clarity): +
+
+/* Per-process pipe which is used as a signal queue. */
+/* Up to PIPE_BUF/sizeof(int) signals can be queued up. */
+int sig_pipe[2];
+
+/* Signal catching function. */
+/* Converts signal event to I/O event. */
+void sig_catcher(int signo)
+{
+  int err;
+
+  /* Save errno to restore it after the write() */
+  err = errno;
+  /* write() is reentrant/async-safe */
+  write(sig_pipe[1], &signo, sizeof(int));
+  errno = err;
+}
+
+/* Signal processing function. */
+/* This is the "main" function of the signal processing thread. */
+void *sig_process(void *arg)
+{
+  st_netfd_t nfd;
+  int signo;
+
+  nfd = st_netfd_open(sig_pipe[0]);
+
+  for ( ; ; ) {
+    /* Read the next signal from the pipe */
+    st_read(nfd, &signo, sizeof(int), ST_UTIME_NO_TIMEOUT);
+
+    /* Process signal synchronously */
+    switch (signo) {
+    case SIGHUP:
+      /* do something here - reread config files, etc. */
+      break;
+    case SIGTERM:
+      /* do something here - cleanup, etc. */
+      break;
+      /*      .
+              .
+         Other signals
+              .
+              .
+      */
+    }
+  }
+
+  return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+  struct sigaction sa;
+        .
+        .
+        .
+
+  /* Create signal pipe */
+  pipe(sig_pipe);
+
+  /* Create signal processing thread */
+  st_thread_create(sig_process, NULL, 0, 0);
+
+  /* Install sig_catcher() as a signal handler */
+  sa.sa_handler = sig_catcher;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  sigaction(SIGHUP, &sa, NULL);
+
+  sa.sa_handler = sig_catcher;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  sigaction(SIGTERM, &sa, NULL);
+
+        .
+        .
+        .
+      
+}
+
+
+

+Note that if multiple processes are used (see below), the signal pipe should +be initialized after the fork(2) call so that each process has its +own private pipe. +

+ + +

Intra-Process Synchronization

+Due to the event-driven nature of the library scheduler, the thread context +switch (process state change) can only happen in a well-known set of +library functions. This set includes functions in which a thread may +"block": I/O functions (st_read(), st_write(), etc.), +sleep functions (st_sleep(), etc.), and thread synchronization +functions (st_thread_join(), st_cond_wait(), etc.). As a result, +process-specific global data need not to be protected by locks since a thread +cannot be rescheduled while in a critical section (and only one thread at a +time can access the same memory location). By the same token, +non thread-safe functions (in a traditional sense) can be safely used with +the State Threads. The library's mutex facilities are practically useless +for a correctly written application (no blocking functions in critical +section) and are provided mostly for completeness. This absence of locking +greatly simplifies an application design and provides a foundation for +scalability. +

+ + +

Inter-Process Synchronization

+The State Threads library makes it possible to multiplex a large number +of simultaneous connections onto a much smaller number of separate +processes, where each process uses a many-to-one user-level threading +implementation (N of M:1 mappings rather than one M:N +mapping used in native threading libraries on some platforms). This design +is key to the application's scalability. One can think about it as if a +set of all threads is partitioned into separate groups (processes) where +each group has a separate pool of resources (virtual address space, file +descriptors, etc.). An application designer has full control of how many +groups (processes) an application creates and what resources, if any, +are shared among different groups via standard UNIX inter-process +communication (IPC) facilities.

+There are several reasons for creating multiple processes: +

+

    +
  • To take advantage of multiple hardware entities (CPUs, disks, etc.) +available in the system (hardware parallelism).
  • +

    +

  • To reduce risk of losing a large number of user connections when one of +the processes crashes. For example, if C user connections (threads) +are multiplexed onto P processes and one of the processes crashes, +only a fraction (C/P) of all connections will be lost.
  • +

    +

  • To overcome per-process resource limitations imposed by the OS. For +example, if select(2) is used for event polling, the number of +simultaneous connections (threads) per process is +limited by the FD_SETSIZE parameter (see select(2)). +If FD_SETSIZE is equal to 1024 and each connection needs one file +descriptor, then an application should create 10 processes to support 10,000 +simultaneous connections.
  • +
+

+Ideally all user sessions are completely independent, so there is no need for +inter-process communication. It is always better to have several separate +smaller process-specific resources (e.g., data caches) than to have one large +resource shared (and modified) by all processes. Sometimes, however, there +is a need to share a common resource among different processes. In that case, +standard UNIX IPC facilities can be used. In addition to that, there is a way +to synchronize different processes so that only the thread accessing the +shared resource will be suspended (but not the entire process) if that resource +is unavailable. In the following code fragment a pipe is used as a counting +semaphore for inter-process synchronization: +

+#ifndef PIPE_BUF
+#define PIPE_BUF 512  /* POSIX */
+#endif
+
+/* Semaphore data structure */
+typedef struct ipc_sem {
+  st_netfd_t rdfd;  /* read descriptor */
+  st_netfd_t wrfd;  /* write descriptor */
+} ipc_sem_t;
+
+/* Create and initialize the semaphore. Should be called before fork(2). */
+/* 'value' must be less than PIPE_BUF. */
+/* If 'value' is 1, the semaphore works as mutex. */
+ipc_sem_t *ipc_sem_create(int value)
+{
+  ipc_sem_t *sem;
+  int p[2];
+  char b[PIPE_BUF];
+
+  /* Error checking is omitted for clarity */
+  sem = malloc(sizeof(ipc_sem_t));
+
+  /* Create the pipe */
+  pipe(p);
+  sem->rdfd = st_netfd_open(p[0]);
+  sem->wrfd = st_netfd_open(p[1]);
+
+  /* Initialize the semaphore: put 'value' bytes into the pipe */
+  write(p[1], b, value);
+
+  return sem;
+}
+
+/* Try to decrement the "value" of the semaphore. */
+/* If "value" is 0, the calling thread blocks on the semaphore. */
+int ipc_sem_wait(ipc_sem_t *sem)
+{
+  char c;
+
+  /* Read one byte from the pipe */
+  if (st_read(sem->rdfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
+    return -1;
+
+  return 0;
+}
+
+/* Increment the "value" of the semaphore. */
+int ipc_sem_post(ipc_sem_t *sem)
+{
+  char c;
+
+  if (st_write(sem->wrfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
+    return -1;
+
+  return 0;
+}
+
+
+

+ +Generally, the following steps should be followed when writing an application +using the State Threads library: +

+

    +
  1. Initialize the library (st_init()).
  2. +

    +

  3. Create resources that will be shared among different processes: + create and bind listening sockets, create shared memory segments, IPC + channels, synchronization primitives, etc.
  4. +

    +

  5. Create several processes (fork(2)). The parent process should + either exit or become a "watchdog" (e.g., it starts a new process when + an existing one crashes, does a cleanup upon application termination, + etc.).
  6. +

    +

  7. In each child process create a pool of threads + (st_thread_create()) to handle user connections.
  8. +
+

+ + +

Non-Network I/O

+ +The State Threads architecture uses non-blocking I/O on +st_netfd_t objects for concurrent processing of multiple user +connections. This architecture has a drawback: the entire process and +all its threads may block for the duration of a disk or other +non-network I/O operation, whether through State Threads I/O functions, +direct system calls, or standard I/O functions. (This is applicable +mostly to disk reads; disk writes are usually performed +asynchronously -- data goes to the buffer cache to be written to disk +later.) Fortunately, disk I/O (unlike network I/O) usually takes a +finite and predictable amount of time, but this may not be true for +special devices or user input devices (including stdin). Nevertheless, +such I/O reduces throughput of the system and increases response times. +There are several ways to design an application to overcome this +drawback: + +

+

+

+ + +

Timeouts

+ +The timeout parameter to st_cond_timedwait() and the +I/O functions, and the arguments to st_sleep() and +st_usleep() specify a maximum time to wait since the last +context switch not since the beginning of the function call. + +

The State Threads' time resolution is actually the time interval +between context switches. That time interval may be large in some +situations, for example, when a single thread does a lot of work +continuously. Note that a steady, uninterrupted stream of network I/O +qualifies for this description; a context switch occurs only when a +thread blocks. + +

If a specified I/O timeout is less than the time interval between +context switches the function may return with a timeout error before +that amount of time has elapsed since the beginning of the function +call. For example, if eight milliseconds have passed since the last +context switch and an I/O function with a timeout of 10 milliseconds +blocks, causing a switch, the call may return with a timeout error as +little as two milliseconds after it was called. (On Linux, +select()'s timeout is an upper bound on the amount of +time elapsed before select returns.) Similarly, if 12 ms have passed +already, the function may return immediately. + +

In almost all cases I/O timeouts should be used only for detecting a +broken network connection or for preventing a peer from holding an idle +connection for too long. Therefore for most applications realistic I/O +timeouts should be on the order of seconds. Furthermore, there's +probably no point in retrying operations that time out. Rather than +retrying simply use a larger timeout in the first place. + +

The largest valid timeout value is platform-dependent and may be +significantly less than INT_MAX seconds for select() +or INT_MAX milliseconds for poll(). Generally, you +should not use timeouts exceeding several hours. Use +ST_UTIME_NO_TIMEOUT (-1) as a special value to +indicate infinite timeout or indefinite sleep. Use +ST_UTIME_NO_WAIT (0) to indicate no waiting at all. + +

+


+

+ + + diff --git a/trunk/3rdparty/st-srs/docs/reference.html b/trunk/3rdparty/st-srs/docs/reference.html new file mode 100644 index 000000000..3c9c7bd78 --- /dev/null +++ b/trunk/3rdparty/st-srs/docs/reference.html @@ -0,0 +1,3120 @@ + + +State Threads Library Reference + + + +

State Threads Library Reference

+ +
+
Types
+
st_thread_t
+
st_cond_t
+
st_mutex_t
+
st_utime_t
+
st_netfd_t
+
st_switch_cb_t
+

+

Error Handling
+

+

Library Initialization
+

+

st_init()
+
st_getfdlimit()
+
st_set_eventsys()
+
st_get_eventsys()
+
st_get_eventsys_name()
+
st_set_utime_function()
+
st_timecache_set()
+
st_randomize_stacks()
+

+

st_switch_cb_t type
+
st_set_switch_in_cb()
+
st_set_switch_out_cb()
+

+

Thread Control and Identification
+

+

st_thread_t type
+
st_thread_create()
+
st_thread_exit()
+
st_thread_join()
+
st_thread_self()
+
st_thread_interrupt()
+
st_sleep()
+
st_usleep()
+
st_randomize_stacks()
+

+

Per-Thread Private Data
+

+

st_key_create()
+
st_key_getlimit()
+
st_thread_setspecific()
+
st_thread_getspecific()
+

+

Synchronization
+

+

st_cond_t type
+
st_cond_new()
+
st_cond_destroy()
+
st_cond_wait()
+
st_cond_timedwait()
+
st_cond_signal()
+
st_cond_broadcast()
+

+

st_mutex_t type
+
st_mutex_new()
+
st_mutex_destroy()
+
st_mutex_lock()
+
st_mutex_trylock()
+
st_mutex_unlock()
+

+

Timing
+

+

st_utime_t type
+
st_utime()
+
st_set_utime_function()
+
st_timecache_set()
+
st_time()
+

+

I/O Functions
+

+

st_netfd_t type
+
st_netfd_open()
+
st_netfd_open_socket()
+
st_netfd_free()
+
st_netfd_close()
+
st_netfd_fileno()
+
st_netfd_setspecific()
+
st_netfd_getspecific()
+
st_netfd_serialize_accept()
+
+
st_netfd_poll()
+

+

st_accept()
+
st_connect()
+
st_read()
+
st_read_fully()
+
st_read_resid()
+
st_readv()
+
st_readv_resid()
+
st_write()
+
st_write_resid()
+
st_writev()
+
st_writev_resid()
+
st_recvfrom()
+
st_sendto()
+
st_recvmsg()
+
st_sendmsg()
+

+

st_open()
+
st_poll()
+

+

Program Structure
+

+

List of Blocking Functions
+

+

+

+


+

+ + + +

Types

+ +The State Thread library defines the following types in the st.h +header file: +

+

+
st_thread_t
+
st_cond_t
+
st_mutex_t
+
st_utime_t
+
st_netfd_t
+
+

+


+

+ + +

st_thread_t

+ +Thread type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void *  st_thread_t;
+
+

+

Description
+ +A thread is represented and identified by a pointer to an opaque data +structure. This pointer is a required parameter for most of the functions +that operate on threads. +

+The thread identifier remains valid until the thread returns from its root +function and, if the thread was created joinable, is joined. +

+


+

+ + +

st_cond_t

+ +Condition variable type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void *  st_cond_t;
+
+

+

Description
+ +A condition variable is an opaque object identified by a pointer. +Condition variables provide synchronization primitives to wait for or wake +up threads waiting for certain conditions to be satisfied. +

+In the State Threads library there is no need to lock a mutex before +waiting on a condition variable. +

+


+

+ + +

st_mutex_t

+ +Mutex type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void *  st_mutex_t;
+
+

+

Description
+ +A mutex is an opaque object identified by a pointer. +Mutual exclusion locks (mutexes) are used to serialize the execution of +threads through critical sections of code. +

+If application using the State Threads library is written with no +I/O or control yielding in critical sections (that is no +blocking functions in critical sections), then there is +no need for mutexes.

+These mutexes can only be used for intra-process thread synchronization. +They cannot be used for inter-process synchronization. +

+


+

+ + +

st_utime_t

+ +High resolution time type ("u" stands for "micro"). +

+

Syntax
+ +
+#include <st.h>
+
+typedef unsigned long long  st_utime_t;
+
+

+

Description
+ +This datatype (unsigned 64-bit integer) represents high-resolution real time +expressed in microseconds since some arbitrary time in the past. It is not +correlated in any way to the time of day. +

+


+

+ + +

st_netfd_t

+ +File descriptor type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void *  st_netfd_t;
+
+

+

Description
+ +This datatype typically represents any open end point of network +communication (socket, end point of a pipe, FIFO, etc.) but can +encapsulate any open file descriptor. Objects of this type are +identified by a pointer to an opaque data structure. + +

+


+

+ + +

st_switch_cb_t

+ +Context switch callback function type. +

+

Syntax
+ +
+#include <st.h>
+
+typedef void (*st_switch_cb_t)(void);
+
+

+

Description
+ +This datatype is a convenience type for describing a pointer +to a function that will be called when a thread is set to stop +or set to run. +This feature is available only when ST_SWITCH_CB is defined +in <st.h>. + +

+


+

+ + +

Error Handling

+ + +All State Threads library non-void functions return on success either a +non-negative integer or a pointer to a newly created object (constructor-type +functions). On failure they return either -1 or a NULL +pointer respectively and set global errno to indicate the error. +It is safe to use errno because it is set right before the function +return and only one thread at a time can modify its value.

+The perror(3) function can be used to produce an error message on the +standard error output. +

+


+

+ + +

Library Initialization

+ +

+

+
st_init()
+
st_getfdlimit()
+
st_set_eventsys()
+
st_get_eventsys()
+
st_get_eventsys_name()
+

+These functions operate on a callback function of type +st_switch_cb_t: +

st_set_switch_in_cb()
+
st_set_switch_out_cb()
+
+

+


+

+ + +

st_init()

+ +Initializes the runtime. +

+

Syntax
+ +
+#include <st.h>
+
+int st_init(void);
+
+

+

Parameters
+None. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. +

+

Description
+This function initializes the library runtime. It should be called near +the beginning of the application's main() function before any other +State Threads library function is called.

+Among other things, this function limits the number of open file descriptors +to the OS imposed per-process maximum number or, if select(2) is +used, to FD_SETSIZE, whichever is less (getrlimit(2)). +This limit can be +retrieved by st_getfdlimit(). It also sets the +disposition of the SIGPIPE signal to SIG_IGN (to be ignored) +(signal(5)). +

+Unlike POSIX threads, a new process created by the fork(2) system +call is an exact copy of the calling process and all state threads +which are running in the parent do exist in the child. That means that +st_init() may be called either before or after multiple processes +are created by fork(2). +

+If the library runtime is not properly initialized (e.g., st_init() +is accidentally omitted), then the process will receive either an arithmetic +exception (SIGFPE or SIGTRAP) or segmentation fault (SIGSEGV) signal upon +new thread creation or the first context switch, respectively. +

+


+

+ +

st_getfdlimit()

+ +Returns the maximum number of file descriptors that the calling process +can open. +

+

Syntax
+ +
+#include <st.h>
+
+int st_getfdlimit(void);
+
+

+

Parameters
+None. +

+

Returns
+The maximum number of file descriptors that the calling process can open. +If this function is called before the library is successfully initialized by +st_init(), a value of -1 is returned. +

+

Description
+This function returns the limit on the number of open file descriptors which +is set by the st_init() function. +

+


+

+ + +

st_set_eventsys()

+ +Sets event notification mechanism. +

+

Syntax
+ +
+#include <st.h>
+
+int st_set_eventsys(int eventsys);
+
+

+

Parameters
+st_set_eventsys() has the following parameter:

+eventsys

+An integer value identifying selected event notification mechanism. The +following values are defined in the st.h header file: +

+ + + + + + + + + + + + + + + +
ST_EVENTSYS_DEFAULTUse default event notification mechanism. Usually it's select(2) +but if the library was compiled with the USE_POLL macro defined +then the default is poll(2).
ST_EVENTSYS_SELECTUse select(2) as an event notification mechanism.
ST_EVENTSYS_POLLUse poll(2) as an event notification mechanism.
ST_EVENTSYS_ALTUse an alternative event notification mechanism. The actual +mechanism selected depends on OS support. For example, epoll(4) +will be used on Linux if supported and kqueue(2) will be used +on FreeBSD/OpenBSD. If the OS supports no alternative event +notification mechanism, setting ST_EVENTSYS_ALT has no effect +and the ST_EVENTSYS_DEFAULT mechanism will be used.
+

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + + + +
EINVAL +The supplied eventsys parameter has an invalid value. +
EBUSY +The event notification mechanism has already been set. +
+

+

Description
+This function sets the event notification mechanism that will be used by +the State Threads library. To have any effect, it must be called +before the st_init() function which performs +the actual initialization. If st_set_eventsys() is not called, +st_init() will set the ST_EVENTSYS_DEFAULT +mechanism. The mechanism cannot be changed once set. +

+There are no strict rules for selecting an event notification +mechanism. The "best" one depends on how your application behaves. +Try a few to see which one works best for you. As a rule of +thumb, you should use the ST_EVENTSYS_ALT mechanism if your +application deals with a very large number of network connections of +which only a few are active at once. +

+


+

+ +

st_get_eventsys()

+ +Returns the integer value identifying the event notification mechanism +being used by the State Threads library. +

+

Syntax
+ +
+#include <st.h>
+
+int st_get_eventsys(void);
+
+

+

Parameters
+None. +

+

Returns
+The integer value identifying the current event notification mechanism. +This value can be one of the following (see st_set_eventsys()): +ST_EVENTSYS_SELECT, ST_EVENTSYS_POLL, or +ST_EVENTSYS_ALT. Future versions of the library may return other +values. If a mechanism hasn't been set yet, a value of -1 is returned. +

+

Description
+This function returns the integer value identifying the event notification +mechanism which is actually being used by the State Threads library. +

+


+

+ +

st_get_eventsys_name()

+ +Returns the name of the event notification mechanism being used by the +State Threads library. +

+

Syntax
+ +
+#include <st.h>
+
+const char *st_get_eventsys_name(void);
+
+

+

Parameters
+None. +

+

Returns
+The string identifying the current event notification mechanism. If a +mechanism hasn't been set yet (see st_set_eventsys()), an empty string is +returned. Possible return values are "select", +"poll", "kqueue", or "epoll". Future versions +of the library may return other values. +

+

Description
+This function returns the string identifying the event notification +mechanism which is actually being used by the State Threads library. +

+


+

+ + +

st_set_switch_in_cb()

+ + +

st_set_switch_out_cb()

+
+Set the optional callback function for thread switches. +

+

Syntax
+ +
+#include <st.h>
+
+st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb);
+st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb);
+
+

+

Parameters
+st_set_switch_in_cb() and st_set_switch_out_cb() have the +following parameter:

+cb

+A function to be called when a thread is resumed and stopped respectively.

+

Returns
+The previous callback function pointer. +

+

Description
+These functions set the callback for when a thread is resumed and stopped +respectively. After being called any thread switch will call the callback. +Use a NULL pointer to disable the callback (this is the default). +Use st_thread_self() or thread +specific data to differentiate between threads.

+These functions can be called at any time.

+This feature is available only when ST_SWITCH_CB is defined +in <st.h>. +

+


+

+ + +

Thread Control and Identification

+ +

+These functions operate on a thread object of type +st_thread_t. +

+

+
st_thread_create()
+
st_thread_exit()
+
st_thread_join()
+
st_thread_self()
+
st_thread_interrupt()
+
st_sleep()
+
st_usleep()
+
st_randomize_stacks()
+
+

+


+

+ +

st_thread_create()

+ +Creates a new thread. +

+

Syntax
+ +
+#include <st.h>
+
+st_thread_t st_thread_create(void *(*start)(void *arg), void *arg,
+                             int joinable, int stack_size);
+
+
+

+

Parameters
+st_thread_create() has the following parameters:

+start

+A pointer to the thread's start function, which is called as the root of the +new thread. Return from this function terminates a thread.

+arg

+A pointer to the root function's only parameter.

+joinable

+Specifies whether the thread is joinable or unjoinable. If this parameter +is zero, the thread is unjoinable. Otherwise, it is joinable. +See also st_thread_join().

+stack_size

+Specifies your preference for the size of the stack, in bytes, associated +with the newly created thread. If you pass zero in this parameter, the +default stack size will be used. The default stack size is 128 KB on IA-64 +and 64 KB on all other platforms. On IA-64 only a half of stack_size +bytes is used for the memory stack. The other half is used for the register +stack backing store. +

+

Returns
+Upon successful completion, a new thread identifier is returned (this +identifier remains valid until the thread returns from its start function). +Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new thread. Note that the total number of threads +created by the application is limited by the amount of swap space available. +Upon thread creation, stack_size bytes are reserved on the swap +space. The stack pages are not actually used (valid) until touched by the +application. +

+


+

+ +

st_thread_exit()

+ +Terminates the calling thread. +

+

Syntax
+ +
+#include <st.h>
+
+void st_thread_exit(void *retval);
+
+

+

Parameters
+st_thread_exit() has the following parameters:

+retval

+If the thread is joinable, then the value retval may be retrieved +by st_thread_join(). If a thread returns from its +start function, it acts as if it had called st_thread_exit() with +retval as the value returned. +

+

Returns
+Nothing. +

+

Description
+This function terminates the calling thread. When a thread exits, per-thread +private data is destroyed by invoking the destructor function for any +non-NULL thread specific values associated with active keys (see +st_key_create()). This function is implicitly called +when a thread returns from its start function.

+When the last thread terminates the process exits with a zero status value. +

+


+

+ +

st_thread_join()

+ +Blocks the calling thread until a specified thread terminates. +

+

Syntax
+ +
+#include <st.h>
+
+int st_thread_join(st_thread_t thread, void **retvalp);
+
+

+

Parameters
+st_thread_join() has the following parameters:

+thread

+A valid identifier for the thread that is to be joined.

+retvalp

+If this parameter is not NULL, then the exit value of the +thread will be placed in the location referenced by this parameter +(see st_thread_exit()). +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + + + +
EINVALTarget thread is unjoinable.
EINVALOther thread already waits on the same +joinable thread.
EDEADLKTarget thread is the same as the +calling thread.
EINTRCurrent thread was interrupted by +st_thread_interrupt().
+

+

Description
+This function is used to synchronize the termination of a thread and possibly +retrieve its exit value. Several threads cannot wait for the same thread +to complete - one of the calling threads operates successfully, and the others +terminate with the error. The calling thread is not blocked if the target +thread has already terminated. +

+


+

+ +

st_thread_self()

+ +Identifies the calling thread. +

+

Syntax
+ +
+#include <st.h>
+
+st_thread_t st_thread_self(void);
+
+

+

Parameters
+None. +

+

Returns
+Always returns a valid reference to the calling thread - a self-identity. +

+

Description
+This function identifies the calling thread. This is the same identifier +that the creating thread obtains from +st_thread_create(). +

+


+

+ +

st_thread_interrupt()

+ +Interrupts a target thread. +

+

Syntax
+ +
+#include <st.h>
+
+void st_thread_interrupt(st_thread_t thread);
+
+

+

Parameters
+st_thread_interrupt() has the following parameters:

+thread

+A valid identifier for the thread being interrupted. +

+

Returns
+Nothing. +

+

Description
+This function interrupts (unblocks) a target thread that is blocked in one +of the blocking functions. A function that was interrupted +returns an error and sets errno to EINTR. It is up to +the target thread to act upon an interrupt (e.g., it may exit or just +abort the current transaction).

+Note: State Threads library functions are never interrupted by a +caught signal. A blocking library function returns an error and sets +errno to EINTR only if the current thread was +interrupted via st_thread_interrupt(). +

+If a target thread is already runnable or running (e.g., it is a newly +created thread or calling thread itself), this function will prevent it +from subsequent blocking. In other words, the interrupt will be "delivered" +only when a target thread is about to block. +

+


+

+ +

st_sleep(), st_usleep()

+ +Suspends current thread for a specified amount of time. +

+

Syntax
+ +
+#include <st.h>
+
+int st_sleep(int secs);
+
+int st_usleep(st_utime_t usecs);
+
+

+

Parameters
+st_sleep() has the following parameters:

+secs

+The number of seconds you want the thread to sleep for. +

+st_usleep() has the following parameters:

+usecs

+The number of microseconds you want the thread to sleep for. This parameter +is a variable of type st_utime_t. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
+

+

Description
+These functions suspend the calling thread from execution for a specified +number of seconds (st_sleep()) or microseconds (st_usleep()). +

+ +If zero is passed as a parameter to st_sleep(), or +ST_UTIME_NO_WAIT (0) is passed to +st_usleep(), the calling thread yields, thus potentially +allowing another thread to run. + +

+ +If -1 is passed as a parameter to st_sleep(), or +ST_UTIME_NO_TIMEOUT (-1) is passed to +st_usleep(), the calling thread will be suspended permanently. +It can be resumed again by interrupting it via st_thread_interrupt(). + +

+


+

+ +

st_randomize_stacks()

+ +Turns stack base address randomization on or off. +

+

Syntax
+ +
+#include <st.h>
+
+int st_randomize_stacks(int on);
+
+

+

Parameters
+st_randomize_stacks() has the following parameters:

+on

+If this parameter has a non-zero value, the State Threads library +randomizes the base addresses of stacks allocated for threads created +after this call. Otherwise new threads' stacks are typically page +aligned. +

+

Returns
+The previous state of stack randomization (a value of 0 if it +was off and a non-zero value otherwise). +

+

Description
+Randomizing state threads' stack bases may improve cache performance on +some systems when large numbers of state threads all perform roughly the +same work, as when they all start from the same root function. On many +modern systems the performance increase is negligible. You should +compare your application's performance with this feature on and off to +see if you really need it. +

+When randomization is enabled, new stacks are allocated one page larger +to accomodate the randomization. +

+This call affects only threads created afterward. It has no effect on +existing threads. +

+


+

+ + +

Per-Thread Private Data

+ +These functions allow to associate private data with each of the threads in +a process. +

+

+
st_key_create()
+
st_key_getlimit()
+
st_thread_setspecific()
+
st_thread_getspecific()
+
+

+


+

+ +

st_key_create()

+ +Creates a key (non-negative integer) that can be used by all +threads in the process to get and set thread-specific data. +

+

Syntax
+ +
+#include <st.h>
+
+int st_key_create(int *keyp, void (*destructor)(void *));
+
+

+

Parameters
+st_key_create() has the following parameters:

+keyp

+The newly created key is returned in the memory pointed to by this parameter. +The new key can be used with +st_thread_setspecific() and +st_thread_getspecific().

+destructor

+Specifies an optional destructor function for the private data associated +with the key. This function can be specified as NULL. +Upon thread exit (see st_thread_exit()), if a key +has a non-NULL destructor and has a non-NULL value +associated with that key, then the destructor function will be +called with the associated value. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EAGAINThe limit on the total number of keys per +process has been exceeded (see st_key_getlimit()). +
+

+

Description
+If this function is successful, every thread in the same process is capable +of associating private data with the new key. After a new key is created, all +active threads have the value NULL associated with that key. +After a new thread is created, the value NULL is associated with +all keys for that thread. If a non-NULL destructor function is +registered with a new key, it will be called at one of two times, as long as +the private data is not NULL: + +

+The key maintains independent data values for each binding thread. A thread +can get access only to its own thread-specific data. There is no way to +deallocate a private data key once it is allocated. +

+


+

+ +

st_key_getlimit()

+ +Returns the key limit. +

+

Syntax
+ +
+#include <st.h>
+
+int st_key_getlimit(void);
+
+

+

Parameters
+None. +

+

Returns
+The limit on the total number of keys per process. +

+

Description
+This function can be used to obtain the limit on the total number of keys +per process (see st_key_create()). +

+


+

+ +

st_thread_setspecific()

+ +Sets per-thread private data. +

+

Syntax
+ +
+#include <st.h>
+
+int st_thread_setspecific(int key, void *value);
+
+

+

Parameters
+st_thread_setspecific() has the following parameters:

+key

+This parameter represents a key with which thread-specific data is associated. +

+value

+The per-thread private data, or more likely, a pointer to the data which is +associated with key. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EINVALThe specified key is invalid.
+

+

Description
+This function associates a thread-specific value with key. +Different threads may bind different values to the same key.

+If the thread already has non-NULL private data associated with +key, and if the destructor function for that key is not +NULL, this destructor function will be called before setting the +new data value. +

+


+

+ +

st_thread_getspecific()

+ +Retrieves the per-thread private data for the current thread. +

+

Syntax
+ +
+#include <st.h>
+
+void *st_thread_getspecific(int key);
+
+

+

Parameters
+st_thread_getspecific() has the following parameters:

+key

+This parameter represents a key with which thread-specific data is associated. +

+

Returns
+The thread-specific data associated with key. If no data is +associated with key, then NULL is returned. +

+

Description
+This function returns the calling thread's value that is bound to the +specified key (see +st_thread_setspecific()). +

+


+

+ + +

Synchronization

+ +

+These functions operate on condition variables +and mutual exclusion locks (mutexes).

+Functions are provided to wait on a condition variable and to wake up +(signal) threads that are waiting on the condition variable. +

+

+
st_cond_new()
+
st_cond_destroy()
+
st_cond_wait()
+
st_cond_timedwait()
+
st_cond_signal()
+
st_cond_broadcast()
+

+

st_mutex_new()
+
st_mutex_destroy()
+
st_mutex_lock()
+
st_mutex_trylock()
+
st_mutex_unlock()
+
+

+


+

+ +

st_cond_new()

+ +Creates a new condition variable. +

+

Syntax
+ +
+#include <st.h>
+
+st_cond_t st_cond_new(void);
+
+

+

Parameters
+None. +

+

Returns
+Upon successful completion, a new condition variable identifier is returned. +Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new condition variable. +

+


+

+ +

st_cond_destroy()

+ +Destroys a condition variable. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_destroy(st_cond_t cvar);
+
+

+

Parameters
+st_cond_destroy() has the following parameters:

+cvar

+An identifier of the condition variable object to be destroyed. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EBUSYThe condition variable is currently being +used by one or more threads.
+

+

Description
+This function destroys a condition variable. The caller is responsible for +ensuring that the condition variable is no longer in use. +

+


+

+ +

st_cond_wait()

+ +Waits on a condition. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_wait(st_cond_t cvar);
+
+

+

Parameters
+st_cond_wait() has the following parameters:

+cvar

+The condition variable on which to wait. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
+

+

Description
+This function is used to block on a condition variable. A return from this +function does not guarantee that the condition or event for which the caller +was waiting actually occurred. It is the responsibility of the caller +to recheck the condition wait predicate before proceeding.

+Note: The State Threads library scheduling guarantees that the +condition cannot change between the checking and blocking, therefore there +is no need for mutex protection. You must not call any +blocking functions between the condition checking and +the st_cond_wait() call. +

+


+

+ +

st_cond_timedwait()

+ +Waits on a condition. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout);
+
+

+

Parameters
+st_cond_timedwait() has the following parameters:

+cvar

+The condition variable on which to wait.

+timeout

+If the number of microseconds specified by this parameter passes before the +waiting thread is signalled, an error is returned. This parameter is a +variable of type st_utime_t. Note that this +time value is a time delta; it is not an absolute time. +Also note that timeouts are measured since +the last context switch. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred before the thread was +awakened by st_cond_signal() or +st_cond_broadcast().
+

+

Description
+This function works the same way as st_cond_wait(), +except that an error is returned if the number of microseconds specified by +timeout passes before the waiting thread is signalled. +

+


+

+ +

st_cond_signal()

+ +Unblocks a thread waiting on a condition variable. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_signal(st_cond_t cvar);
+
+

+

Parameters
+st_cond_signal() has the following parameters:

+cvar

+The condition variable to signal. +

+

Returns
+Always zero. +

+

Description
+This function unblocks (signals) one of the threads that are blocked on +cvar at the time of the call. If no thread is waiting on the +condition variable, the signal operation is a no-op. +

+


+

+ +

st_cond_broadcast()

+ +Unblocks all threads waiting on a condition variable. +

+

Syntax
+ +
+#include <st.h>
+
+int st_cond_broadcast(st_cond_t cvar);
+
+

+

Parameters
+st_cond_broadcast() has the following parameters:

+cvar

+The condition variable to broadcast. +

+

Returns
+Always zero. +

+

Description
+This function unblocks all threads blocked on the specified condition +variable at the time of the call. If no threads are waiting, this operation +is a no-op. +

+


+

+ + +

st_mutex_new()

+ +Creates a new mutual exclusion lock (mutex). +

+

Syntax
+ +
+#include <st.h>
+
+st_mutex_t st_mutex_new(void);
+
+

+

Parameters
+None. +

+

Returns
+Upon successful completion, a new mutex identifier is returned. +Otherwise, NULL is returned and errno is set to +indicate the error. +

+

Description
+This function creates a new opaque mutual exclusion lock (see +st_mutex_t). +

+


+

+ +

st_mutex_destroy()

+ +Destroys a specified mutex object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_mutex_destroy(st_mutex_t lock);
+
+

+

Parameters
+st_mutex_destroy() has the following parameters:

+lock

+An identifier of the mutex object to be destroyed. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EBUSYThe mutex is currently being used by +other threads.
+

+

Description
+This function destroys a mutex. The caller is responsible for ensuring +that the mutex is no longer in use. +

+


+

+ +

st_mutex_lock()

+ +Locks a specified mutex object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_mutex_lock(st_mutex_t lock);
+
+

+

Parameters
+st_mutex_lock() has the following parameters:

+lock

+An identifier of the mutex object to be locked. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + + +
EDEADLKThe current thread already owns the mutex. +
EINTRThe current thread was interrupted by +st_thread_interrupt().
+

+

Description
+A thread that calls this function will block until it can gain exclusive +ownership of a mutex, and retains ownership until it calls +st_mutex_unlock(). +

+


+

+ +

st_mutex_trylock()

+ +Attempts to acquire a mutex. +

+

Syntax
+ +
+#include <st.h>
+
+int st_mutex_trylock(st_mutex_t lock);
+
+

+

Parameters
+st_mutex_trylock() has the following parameters:

+lock

+An identifier of the mutex object to be locked. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EBUSYThe mutex is currently held by another +thread.
+

+

Description
+This function attempts to acquire a mutex. If the mutex object is locked +(by any thread, including the current thread), the call returns immediately +with an error. +

+


+

+ +

st_mutex_unlock()

+ +Releases a specified mutex object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_mutex_unlock(st_mutex_t lock);
+
+

+

Parameters
+st_mutex_unlock() has the following parameters:

+lock

+An identifier of the mutex object to be unlocked. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EPERMThe current thread does not own the mutex. +
+

+

Description
+This function releases a specified mutex object previously acquired by +st_mutex_lock() or +st_mutex_trylock(). Only the thread that locked +a mutex should unlock it. +

+


+

+ + +

Timing

+ +

+

+
st_utime()
+
st_set_utime_function()
+
st_timecache_set()
+
st_time()
+
+

+


+

+ +

st_utime()

+ +Returns current high-resolution time. +

+

Syntax
+ +
+#include <st.h>
+
+st_utime_t st_utime(void);
+
+

+

Parameters
+None. +

+

Returns
+Current high-resolution time value of type +st_utime_t. +

+

Description
+This function returns the current high-resolution time. Time is +expressed as microseconds since some arbitrary time in the past. It is +not correlated in any way to the time of day. See also st_utime_t and st_time(). +

+


+

+ +

st_set_utime_function()

+ +Set high-resolution time function. +

+

Syntax
+ +
+#include <st.h>
+
+int st_set_utime_function(st_utime_t (*func)(void));
+
+

+

Parameters
+st_set_utime_function() has the following parameters:

+func

+This function will be called to get high-resolution time instead of the +default st_utime() function. It must return +number of microseconds since some arbitrary time in the past. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to EINVAL to indicate the error. +

+

Description
+This function may be called to replace the default implementation of the +st_utime() function. It must be called before the ST +library has been initialized (see st_init()). +The user-provided function func will be invoked whenever +st_utime() is called to obtain current high-resolution time. +Replacing default implementation may be useful, for example, for taking +advantage of high performance CPU cycle counters. +

+


+

+ +

st_timecache_set()

+ +Turns the time caching on or off. +

+

Syntax
+ +
+#include <st.h>
+
+int st_timecache_set(int on);
+
+

+

Parameters
+st_timecache_set() has the following parameters:

+on

+If this parameter has a non-zero value, the time caching is turned on +(enabled). Otherwise, the time caching is turned off (disabled). +By default time caching is disabled. +

+

Returns
+The previous state of time caching (a value of 0 if it was off and +a value of 1 otherwise). +

+

Description
+The State Threads library has the ability to "cache" the time value that is +reported by the time(2) system call. If the time caching is enabled +by calling this function with a non-zero argument, then the result value +of time(2) will be stored and updated at most once per second. The +cached time can be retrieved by st_time(). +By default time caching is disabled. +You may enable or disable time caching at any time but generally +you enable it once (if desired) during program initialization.

+Note: There are some pathological cases (e.g., very heavy loads during +application benchmarking) when a single thread runs for a long time without +giving up control and the cached time value is not updated properly. If you +always need "real-time" time values, don't enable the time caching. +

+


+

+ +

st_time()

+ +Returns the value of time in seconds since 00:00:00 UTC, January 1, 1970. +

+

Syntax
+ +
+#include <st.h>
+
+time_t st_time(void);
+
+

+

Parameters
+None. +

+

Returns
+The value of time in seconds since 00:00:00 UTC, January 1, 1970 as reported +by the time(2) system call. +

+

Description
+If the time caching was enabled by +st_timecache_set(), then this function returns +the cached result. Otherwise, it just calls time(2). +

+


+

+ + +

I/O Functions

+ +

+Most State Threads library I/O functions look like corresponding C library +functions with two exceptions: +

    +
  • They operate on file descriptor objects of type +st_netfd_t.
  • +
  • They take an additional argument of type +st_utime_t which represents an inactivity +timeout: if no I/O is possible during this amount of time, I/O functions +return an error code and set errno to ETIME. + +The boundary values ST_UTIME_NO_WAIT (0) and +ST_UTIME_NO_TIMEOUT (-1) for this argument indicate +that the thread should wait no time (function returns immediately) or +wait forever (never time out), respectively. + +Note that timeouts are measured since the +last context switch. +
  • +
+

+

+
st_netfd_open()
+
st_netfd_open_socket()
+
st_netfd_free()
+
st_netfd_close()
+
st_netfd_fileno()
+
st_netfd_setspecific()
+
st_netfd_getspecific()
+
st_netfd_serialize_accept()
+
st_netfd_poll()
+

+

st_accept()
+
st_connect()
+
st_read()
+
st_read_fully()
+
st_read_resid()
+
st_readv()
+
st_read_resid()
+
st_write()
+
st_write_resid()
+
st_writev()
+
st_writev_resid()
+
st_recvfrom()
+
st_sendto()
+
st_recvmsg()
+
st_sendmsg()
+
st_open()
+
st_poll()
+
+

+


+

+ +

st_netfd_open()

+ +Creates a new file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+st_netfd_t st_netfd_open(int osfd);
+
+

+

Parameters
+st_netfd_open() has the following parameters:

+osfd

+ +Any open OS file descriptor; can be obtained from calls to +functions including, but not restricted to, pipe(2), socket(3), +socketpair(3), fcntl(2), dup(2), etc. + + +

+

Returns
+Upon successful completion, a new file descriptor object identifier is +returned. Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new file descriptor object of type +st_netfd_t.

+ +Note: Among other things, this function sets a non-blocking +flag on the underlying OS file descriptor. You should not modify this +flag directly. Also, once an st_netfd_t +has been created with a given file descriptor, you should avoid +passing that descriptor to normal I/O or stdio functions. Since the +O_NONBLOCK flag is shared across dup(2), this applies to +dup()'ed file descriptors as well - for instance, if you pass +standard output or standard input to st_netfd_open(), then +you should use st_write() instead of write +or fprintf when writing to standard error as well - since all +three descriptors could point to the same terminal. If necessary, you +can still use write directly if you remember to check +errno for EAGAIN, but fprintf and other +stdio functions should be avoided completely because, at least on +Linux, the stdio library cannot be made to work reliably with +non-blocking files. (This only applies to file descriptors which are +passed to st_netfd_open() or st_netfd_open_socket(), or which are +related to such descriptors through dup(); other file +descriptors are untouched by State Threads.) +

+


+

+ +

st_netfd_open_socket()

+ +Creates a new file descriptor object from a socket. +

+

Syntax
+ +
+#include <st.h>
+
+st_netfd_t st_netfd_open_socket(int osfd);
+
+

+

Parameters
+st_netfd_open_socket() has the following parameters:

+osfd

+An open OS file descriptor which is a socket initially obtained from a +socket(3) or socketpair(3) call. +

+

Returns
+Upon successful completion, a new file descriptor object identifier is +returned. Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new file descriptor object of type +st_netfd_t which represents an open end +point of network communication.

+Unlike the st_netfd_open() function which may be used +on OS file descriptors of any origin, st_netfd_open_socket() must +be used only on sockets. It is slightly more efficient than +st_netfd_open().

+Note: Among other things, this function sets a non-blocking flag +on the underlying OS socket. You should not modify this flag directly. +See st_netfd_open(). +

+


+

+ +

st_netfd_free()

+ +Frees a file descriptor object without closing the underlying OS file +descriptor. +

+

Syntax
+ +
+#include <st.h>
+
+void st_netfd_free(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_free() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t). +

+

Returns
+Nothing. +

+

Description
+This function frees the memory and other resources identified by the +fd parameter without closing the underlying OS file descriptor. +Any non-NULL descriptor-specific data is destroyed by invoking +the specified destructor function (see st_netfd_setspecific()).

A thread should +not free file descriptor objects that are in use by other threads +because it may lead to unpredictable results (e.g., a freed file +descriptor may be reused without other threads knowing that). +

+


+

+ +

st_netfd_close()

+ +Closes a file descriptor. +

+

Syntax
+ +
+#include <st.h>
+
+int st_netfd_close(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_close() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t). +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. +

+

Description
+This function closes the underlying OS file descriptor, frees the memory and +other resources identified by the fd parameter. Any non-NULL +descriptor-specific data is destroyed by invoking the specified destructor +function (see st_netfd_setspecific()).

+A thread should not close file descriptor objects that are in use by other +threads because it may lead to unpredictable results (e.g., a closed +file descriptor may be reused without other threads knowing that). +

+


+

+ +

st_netfd_fileno()

+ +Returns an underlying OS file descriptor. +

+

Syntax
+ +
+#include <st.h>
+
+int st_netfd_fileno(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_fileno() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t). +

+

Returns
+An underlying OS file descriptor. +

+

Description
+This function returns the integer OS file descriptor associated with the named +file descriptor object. +

+


+

+ +

st_netfd_setspecific()

+ +Sets per-descriptor private data. +

+

Syntax
+ +
+#include <st.h>
+
+void st_netfd_setspecific(st_netfd_t fd, void *value,
+                          void (*destructor)(void *));
+
+

+

Parameters
+st_netfd_setspecific() has the following parameters:

+fd

+A valid file descriptor object identifier (see +st_netfd_t). +

+value

+The per-descriptor private data, or more likely, a pointer to the data which +is being associated with the named file descriptor object. +

+destructor

+Specifies an optional destructor function for the private data associated +with fd. This function can be specified as NULL. +If value is not NULL, then this destructor function will +be called with value as an argument upon freeing the file descriptor +object (see st_netfd_free() and +st_netfd_close()). +

+

Returns
+Nothing. +

+

Description
+This function allows to associate any data with the specified file +descriptor object (network connection). If a non-NULL destructor +function is registered, it will be called at one of two times, as long as +the associated data is not NULL: +
    +
  • when private data is replaced by calling +st_netfd_setspecific() again +
  • upon freeing the file descriptor object (see +st_netfd_free() and +st_netfd_close()) +
+

+


+

+ +

st_netfd_getspecific()

+ +Retrieves the per-descriptor private data. +

+

Syntax
+ +
+#include <st.h>
+
+void *st_netfd_getspecific(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_getspecific() has the following parameters:

+fd

+A valid file descriptor object identifier (see +st_netfd_t). +

+

Returns
+The data associated with the named file descriptor object. If no data is +associated with fd, then NULL is returned. +

+

Description
+This function allows to retrieve the data that was associated with the +specified file descriptor object (see +st_netfd_setspecific()). +

+


+

+ +

st_netfd_serialize_accept()

+ +Serializes all subsequent accept(3) calls on a specified file +descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_netfd_serialize_accept(st_netfd_t fd);
+
+

+

Parameters
+st_netfd_serialize_accept() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) which has been successfully created +from a valid listening socket by st_netfd_open() or +st_netfd_open_socket(). +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. +

+

Description
+On some platforms (e.g., Solaris 2.5 and possibly other SVR4 implementations) +accept(3) calls from different processes on +the same listening socket (see bind(3), listen(3)) must be +serialized. This function causes all subsequent accept(3) calls +made by st_accept() on the specified file descriptor +object to be serialized. +

+st_netfd_serialize_accept() must be called before +creating multiple server processes via fork(2). If the application +does not create multiple processes to accept network connections on +the same listening socket, there is no need to call this function. +

+Deciding whether or not to serialize accepts is tricky. On some +platforms (IRIX, Linux) it's not needed at all and +st_netfd_serialize_accept() is a no-op. On other platforms +it depends on the version of the OS (Solaris 2.6 doesn't need it but +earlier versions do). Serializing accepts does incur a slight +performance penalty so you want to enable it only if necessary. Read +your system's manual pages for accept(2) and select(2) +to see if accept serialization is necessary on your system. +

+st_netfd_serialize_accept() allocates resources that are +freed upon freeing of the specified file descriptor object (see +st_netfd_free() and +st_netfd_close()). +

+


+

+ +

st_netfd_poll()

+ +Waits for I/O on a single file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout);
+
+

+

Parameters
+st_netfd_poll() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t). +

+how

+Specifies I/O events of interest. This parameter can be constructed by +OR-ing any combination of the following event flags which are defined +in the poll.h header file:

+ + + + + +
POLLINfd is readable.
POLLOUTfd is is writable.
POLLPRIfd has an exception condition.
+

+timeout

+Amount of time in microseconds the call will block waiting for I/O +to become ready. This parameter is a variable of type +st_utime_t. If this time expires without any +I/O becoming ready, st_netfd_poll() returns an error and sets +errno to ETIME. +Note that timeouts are measured since the +last context switch. +

+

Returns
+If the named file descriptor object is ready for I/O within the specified +amount of time, a value of 0 is returned. Otherwise, a value +of -1 is returned and errno is set to indicate the error: +

+ + + + +
EBADFThe underlying OS file descriptor is invalid. +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred without any I/O +becoming ready.
+

+

Description
+This function returns as soon as I/O is ready on the named file +descriptor object or the specified amount of time expires. The +how parameter should be set to the I/O events (readable, +writable, exception, or some combination) that the caller is interested +in. If the value of timeout is ST_UTIME_NO_TIMEOUT +(-1), this function blocks until a requested I/O event occurs +or until the call is interrupted by st_thread_interrupt().

+Despite having an interface like poll(2), this function uses +the same event notification mechanism as the rest of the library. For +instance if an alternative event nofication mechanism was set using st_set_eventsys(), this function uses that +mechanism to check for events.

+Note: if kqueue(2) is used as an alternative event +notification mechanism (see st_set_eventsys()), the POLLPRI +event flag is not supported and st_netfd_poll() will return an error +if it's set (errno will be set to EINVAL). +

+


+

+ +

st_accept()

+ +Accepts a connection on a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen,
+                     st_utime_t timeout);
+
+

+

Parameters
+st_accept() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing the rendezvous socket +on which the caller is willing to accept new connections. This object has been +created from a valid listening socket by +st_netfd_open() or +st_netfd_open_socket().

+addr

+If this value is non-zero, it is a result parameter that is filled +in with the address of the connecting entity, as known to the communications +layer (see accept(3)).

+addrlen

+This parameter should initially contain the amount of space pointed to by +addr; on return it will contain the actual length (in bytes) of the +address returned (see accept(3)).

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the accept operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+Upon successful completion, a new file descriptor object identifier +representing the newly accepted connection is returned. Otherwise, +NULL is returned and errno is set to indicate the error. +Possible errno values are the same as set by the accept(3) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no pending +connection was accepted.
+

+

Description
+This function accepts the first connection from the queue of pending +connections and creates a new file descriptor object for the newly +accepted connection. The rendezvous socket can still be used to accept +more connections.

+st_accept() blocks the calling thread until either a new connection +is successfully accepted or an error occurs. If no pending connection can +be accepted before the time limit, this function returns NULL +and sets errno to ETIME. +

+


+

+ +

st_connect()

+ +Initiates a connection on a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_connect(st_netfd_t fd, struct sockaddr *addr, int addrlen,
+               st_utime_t timeout);
+
+

+

Parameters
+st_connect() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a socket.

+addr

+A pointer to the address of the peer to which the socket is to be connected. +

+addrlen

+This parameter specifies the amount of space pointed to by addr. +

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the connect operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+Upon successful completion, a value of 0 is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. Possible errno values are the same as set +by the connect(3) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and connection setup +was not completed.
+

+

Description
+This function is usually invoked on a file descriptor object representing +a TCP socket. Upon completion it establishes a TCP connection to the peer. +If the underlying OS socket is not bound, it will be bound to an arbitrary +local address (see connect(3)).

+st_connect() blocks the calling thread until either the connection +is successfully established or an error occurs. If the connection setup +cannot complete before the specified time limit, this function fails with +errno set to ETIME. +

+


+

+ +

st_read()

+ +Reads data from a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout);
+
+

+

Parameters
+st_read() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to a buffer to hold the data read in. On output the buffer +contains the data.

+nbyte

+The size of buf in bytes.

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the read operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes actually +read is returned (a value of 0 means the network connection is +closed or end of file is reached). Otherwise, a value of -1 is +returned and errno is set to indicate the error. +Possible errno values are the same as set by the read(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was read. +
+

+

Description
+This function blocks the calling thread until it encounters an end-of-stream +indication, some positive number of bytes (but no more than nbyte +bytes) are read in, a timeout occurs, or an error occurs. +

+


+

+ +

st_read_fully()

+ +Reads the specified amount of data in full from a file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte,
+                      st_utime_t timeout);
+
+

+

Parameters
+st_read_fully() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to a buffer to hold the data read in. On output the buffer +contains the data.

+nbyte

+The amount of data to be read in full (in bytes). It must not exceed the +size of buf.

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes actually +read is returned (a value less than nbyte means the network +connection is closed or end of file is reached). Otherwise, a value of +-1 is returned and errno is set to indicate the error. +Possible errno values are the same as set by the read(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until the specified amount of data +is read in full, it encounters an end-of-stream indication, a timeout occurs, +or an error occurs. +

+


+

+ +

st_read_resid()

+ +Reads the specified amount of data in full from a file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_read_resid(st_netfd_t fd, void *buf, size_t *resid,
+		  st_utime_t timeout);
+
+

+

Parameters
+st_read_resid() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to a buffer to hold the data read in. On output the buffer +contains the data.

+resid

+A pointer to a number of bytes. +On entry, the amount of data to be read in full. +It must not exceed the size of buf. +On return, the amount of data remaining to be read. +(A non-zero returned value means some but not all of the data was read.)

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success, zero is returned. *resid may be zero, indicating +a complete read, or non-zero, indicating the network +connection is closed or end of file is reached. +

+Otherwise, a value of -1 is returned, *resid is non-zero, +and errno is set to indicate the error. +Possible errno values are the same as set by the read(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until the specified amount of data +is read in full, it encounters an end-of-stream indication, a timeout occurs, +or an error occurs. It differs from st_read_fully() only in that +it allows the caller to know how many bytes were transferred before an error +occurred. +

+


+

+ +

st_readv()

+ +Reads data from a specified file descriptor object into multiple buffers. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size,
+		 st_utime_t timeout);
+
+

+

Parameters
+st_readv() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+iov

+An array of iovec structures that identify the buffers for holding +the data read in. +On return the buffers contain the data.

+iov_size

+The number of iovec structures in the iov array.

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the read operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes actually +read is returned (a value of 0 means the network connection is +closed or end of file is reached). Otherwise, a value of -1 is +returned and errno is set to indicate the error. +Possible errno values are the same as set by the readv(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was read. +
+

+

Description
+This function blocks the calling thread until it encounters an end-of-stream +indication, some positive number of bytes (but no more than fit in the buffers) +are read in, a timeout occurs, or an error occurs. +

+


+

+ +

st_readv_resid()

+ +Reads the specified amount of data in full from a file descriptor object +into multiple buffers. +

+

Syntax
+ +
+#include <st.h>
+
+int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
+		   st_utime_t timeout);
+
+

+

Parameters
+st_readv_resid() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+iov

+A pointer to an array of iovec structures. +On entry, the iovecs identify the buffers for holding the data read in. +On return, the incomplete iovecs. +This function modifies both the pointer and the array to which it points.

+iov_size

+A pointer to a number of iovec structures. +On entry, the number of iovec structures pointed to by *iov. +On return, the number of incomplete or unused iovec structures. +(A non-zero returned value means some but not all of the data was read.)

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success, zero is returned. *iov_size may be zero, indicating +a complete read, or non-zero, indicating the network connection is +closed or end of file is reached. *iov points to the first +iovec after the end of the original array on a complete read, or to the +first incomplete iovec on an incomplete read. +

+Otherwise, a value of -1 is returned, *iov_size is non-zero, +and errno is set to indicate the error. *iov points to the +first unused iovec. +Possible errno values are the same as set by the readv(2) +call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

All of the iovecs before *iov are modified such that +iov_base points to the end of the original buffer and +iov_len is zero. +

+

Description
+This function blocks the calling thread until the specified amount of data +is read in full, it encounters an end-of-stream indication, a timeout occurs, +or an error occurs. Like st_read_resid() it blocks the thread until +all of the requested data is read or an error occurs. Use +st_readv() to read up to the requested amount of data. +

+


+

+ +

st_write()

+ +Writes a buffer of data to a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte,
+                 st_utime_t timeout);
+
+

+

Parameters
+st_write() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to the buffer holding the data to be written.

+nbyte

+The amount of data in bytes to be written from the buffer.

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer equal to nbyte is returned. +Otherwise, a value of -1 is returned and errno is set +to indicate the error. Possible errno values are the same as set +by the write(2) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until all the data is written, +a timeout occurs, or the write operation fails. The return value is equal to +either nbyte (on success) or -1 (on failure). Note that if +st_write() returns -1, some data (less than nbyte +bytes) may have been written before an error occurred. +

+


+

+ +

st_write_resid()

+ +Writes a buffer of data to a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid,
+                   st_utime_t timeout);
+
+

+

Parameters
+st_write_resid() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+buf

+A pointer to the buffer holding the data to be written.

+resid

+A pointer to a number of bytes. +On entry, the amount of data to be written from the buffer. +On return, the amount of data remaining to be written. +(A non-zero returned value means some but not all of the data was written.)

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success, zero is returned and *resid is zero. +Otherwise, a value of -1 is returned, *resid is non-zero, +and errno is set +to indicate the error. Possible errno values are the same as set +by the write(2) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until all the data is written, +a timeout occurs, or the write operation fails. It differs from +st_write() only in that it allows the caller to know how many bytes +were transferred before an error occurred. +

+


+

+ +

st_writev()

+ +Writes data to a specified file descriptor object from multiple buffers. +

+

Syntax
+ +
+#include <st.h>
+
+ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size,
+                  st_utime_t timeout);
+
+

+

Parameters
+st_writev() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+iov

+An array of iovec structures that describe the buffers to write +from (see writev(2)).

+iov_size

+Number of iovec structures in the iov array.

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer equal to the sum of all the buffer lengths +is returned. Otherwise, a value of -1 is returned and errno +is set to indicate the error. Possible errno values are the same as +set by the writev(2) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+

Description
+This function blocks the calling thread until all the data is written, +a timeout occurs, or the write operation fails. The return value is equal to +either the sum of all the buffer lengths (on success) or -1 (on +failure). Note that if st_writev() returns -1, part of the +data may have been written before an error occurred. +

+


+

+ +

st_writev_resid()

+ +Writes multiple buffers of data to a specified file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
+		    st_utime_t timeout);
+
+

+

Parameters
+st_writev_resid() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t).

+iov

+A pointer to an array of iovec structures. +On entry, the iovecs identify the buffers holding the data to write. +On return, the incomplete iovecs. +This function modifies both the pointer and the array to which it points.

+iov_size

+A pointer to a number of iovec structures. +On entry, the number of iovec structures pointed to by *iov. +On return, the number of incomplete or unused iovec structures. +(A non-zero returned value means some but not all of the data was written.)

+timeout

+A value of type st_utime_t specifying the +inactivity timeout (in microseconds). +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success, zero is returned, *iov_size is zero, and *iov +points to the first iovec after the end of the original array. +Otherwise, a value of -1 is returned, *iov_size is non-zero, +*iov points to the first incomplete iovec, and errno is set +to indicate the error. Possible errno values are the same as set +by the writev(2) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred. +
+

+All of the iovecs before *iov are modified such that +iov_base points to the end of the original buffer and +iov_len is zero. +

+

Description
+This function blocks the calling thread until all the data is written, +a timeout occurs, or the write operation fails. It differs from +st_writev() only in that it allows the caller to know how many bytes +were transferred before an error occurred. +

+


+

+ +

st_recvfrom()

+ +Receives bytes from a file descriptor object and stores the sending peer's +address. +

+

Syntax
+ +
+#include <st.h>
+
+int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from,
+                int *fromlen, st_utime_t timeout);
+
+

+

Parameters
+st_recvfrom() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a UDP socket.

+buf

+A pointer to a buffer to hold the data received.

+len

+The size of buf in bytes.

+from

+If this parameter is not a NULL pointer, the source address of the +message is filled in (see recvfrom(3)).

+fromlen

+This is a value-result parameter, initialized to the size of the buffer +associated with from, and modified on return to indicate the actual +size of the address stored there.

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the receive operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the length of the received +message in bytes is returned. Otherwise, a value of -1 is returned +and errno is set to indicate the error. Possible errno +values are the same as set by the recvfrom(3) call with two +exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was received. +
+

+

Description
+This function receives up to a specified number of bytes from the specified +file descriptor object representing a UDP socket.

+st_recvfrom() blocks the calling thread until one or more bytes are +transferred, a timeout has occurred, or there is an error. No more than +len bytes will be transferred. +

+


+

+ +

st_sendto()

+ +Sends bytes to a specified destination. +

+

Syntax
+ +
+#include <st.h>
+
+int st_sendto(st_netfd_t fd, const void *msg, int len, struct sockaddr *to,
+              int tolen, st_utime_t timeout);
+
+

+

Parameters
+st_sendto() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a UDP socket.

+msg

+A pointer to a buffer containing the message to be sent.

+len

+The length of the message to be sent (in bytes).

+to

+A pointer to the address of the destination (see sendto(3)).

+tolen

+This parameter specifies the size of the destination address.

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the send operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes sent is +returned. Otherwise, a value of -1 is returned and errno is +set to indicate the error. Possible errno values are the same as +set by the sendto(3) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was sent. +
+

+

Description
+This function sends a specified number of bytes from a file descriptor +object representing a UDP socket to the specified destination address. +If no buffer space is available at the underlying OS socket to hold the +message to be transmitted, then st_sendto() blocks the calling +thread until the space becomes available, a timeout occurs, or an error +occurs. +

+


+

+ +

st_recvmsg()

+ +Receives a message from a file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags,
+               st_utime_t timeout);
+
+

+

Parameters
+st_recvmsg() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a UDP socket.

+msg

+A pointer to a msghdr structure to describe the data received.

+flags

+Control flags for recvmsg(3).

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the receive operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes received +is returned. Otherwise, a value of -1 is returned +and errno is set to indicate the error. Possible errno +values are the same as set by the recvmsg(3) call with two +exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was received. +
+

+

Description
+This function receives bytes from the specified file descriptor object +representing a UDP socket. The operation is controlled by the in/out +msg parameter.

+st_recvmsg() blocks the calling thread until one or more bytes are +transferred, a timeout has occurred, or there is an error. +

+


+

+ +

st_sendmsg()

+ +Sends a message to a file descriptor object. +

+

Syntax
+ +
+#include <st.h>
+
+int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags,
+               st_utime_t timeout);
+
+

+

Parameters
+st_sendmsg() has the following parameters:

+fd

+A file descriptor object identifier (see +st_netfd_t) representing a UDP socket.

+msg

+A pointer to a msghdr structure describing the message to be sent.

+flags

+Control flags for sendmsg(3).

+timeout

+A value of type st_utime_t specifying the time +limit in microseconds for completion of the send operation. +Note that timeouts are measured since the +last context switch. +

+

Returns
+On success a non-negative integer indicating the number of bytes sent is +returned. Otherwise, a value of -1 is returned and errno is +set to indicate the error. Possible errno values are the same as +set by the sendmsg(3) call with two exceptions:

+ + + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
ETIMEThe timeout occurred and no data was sent. +
+

+

Description
+This function sends bytes to a file descriptor object representing a UDP +socket. The operation is controlled by the msg parameter. +If no buffer space is available at the underlying OS socket to hold the +message to be transmitted, then st_sendmsg() blocks the calling +thread until the space becomes available, a timeout occurs, or an error +occurs. +

+


+

+ +

st_open()

+ +Opens a file for reading, writing, or both. +

+

Syntax
+ +
+#include <st.h>
+
+st_netfd_t st_open(const char *path, int oflags, mode_t mode);
+
+

+

Parameters
+st_open() has the following parameters:

+path

+The pathname of the file to be opened.

+oflags

+File status flags. These are the same flags that are used by the +open(2) system call.

+mode

+Access permission bits of the file mode, if the file is created when +O_CREAT is set in oflags (see open(2)). +

+

Returns
+Upon successful completion, a new file descriptor object identifier is +returned. Otherwise, NULL is returned and errno is set +to indicate the error. +

+

Description
+This function creates a new file descriptor object of type +st_netfd_t for the file with the pathname +path. This object can be freed by +st_netfd_free() or +st_netfd_close().

+The primary purpose of this function is to open FIFOs (named pipes) or +other special files in order to create an end point of communication. +However, it can be used on regular files as well.

+Among other things, this function always sets a non-blocking flag on the +underlying OS file descriptor, so there is no need to include that flag in +oflags. +

+


+

+ +

st_poll()

+ +Detects when I/O is ready for a set of OS file descriptors. +

+

Syntax
+ +
+#include <st.h>
+
+int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);
+
+

+

Parameters
+st_poll() has the following parameters:

+pds

+A pointer to an array of pollfd structures (see poll(2)). +

+npds

+The number of elements in the pds array.

+timeout

+A value of type st_utime_t specifying the +amount of time in microseconds the call will block waiting for I/O +to become ready. If this time expires without any I/O becoming ready, +st_poll() returns zero. +Note that timeouts are measured since the +last context switch. +

+

Returns
+Upon successful completion, a non-negative value is returned. A positive +value indicates the total number of OS file descriptors in pds +that have events. A value of 0 indicates that the call timed out. +Upon failure, a value of -1 is returned and errno is set +to indicate the error:

+ + +
EINTRThe current thread was interrupted by +st_thread_interrupt().
+

+If an alternative event notification mechanism has been set by +st_set_eventsys(), other values of +errno could be set upon failure as well. The values +depend on the specific mechanism in use. +

+

Description
+This function returns as soon as I/O is ready on one or more of the specified +OS file descriptors. A count of the number of ready descriptors is returned +unless a timeout occurs, in which case zero is returned.

+The pollfd structure is defined in the poll.h header file +and contains the following members:

+

+    int fd;             /* OS file descriptor */
+    short events;       /* requested events   */
+    short revents;      /* returned events    */
+
+The events field should be set to the I/O events (readable, +writable, exception, or some combination) that the caller is interested in. +On return, the revents field is set to indicate what kind of I/O +is ready on the respective descriptor.

+The events and revents fields are constructed by OR-ing +any combination of the following event flags (defined in poll.h): +

+ + + + + + +
POLLINfd is readable.
POLLOUTfd is is writable.
POLLPRIfd has an exception condition.
POLLNVALfd is bad.
+

+The POLLNVAL flag is only valid in the revents field; +it is not used in the events field.

+Despite having an interface like poll(2), this function uses +the same event notification mechanism as the rest of the library. For +instance if an alternative event nofication mechanism was set using st_set_eventsys(), this function uses that +mechanism to check for events.

+Note that unlike the poll(2) call, this function has the +timeout parameter expressed in microseconds. If the value of +timeout is ST_UTIME_NO_TIMEOUT +(-1), this function blocks until a requested I/O +event occurs or until the call is interrupted by +st_thread_interrupt(). +

+Note: if kqueue(2) is used as an alternative event +notification mechanism (see st_set_eventsys()), the POLLPRI +event flag is not supported and st_poll() will return an error +if it's set (errno will be set to EINVAL). +

+


+

+ + +

Program Structure

+ +

+Generally, the following steps should be followed when writing an application +using the State Threads library: +

+

    +
  1. Configure the library by calling these pre-init functions, if desired. + +
  2. +

    +

  3. Initialize the library by calling st_init().
  4. +

    +

  5. Configure the library by calling these post-init functions, if desired. + +
  6. +

    +

  7. Create resources that will be shared among different processes: + create and bind listening sockets (see socket(3), + bind(3), listen(3), + st_netfd_open_socket(), and possibly + st_netfd_serialize_accept()), + create shared memory segments, inter-process communication (IPC) + channels and synchronization primitives (if any).
  8. +

    +

  9. Create several processes via fork(2). The parent process should + either exit or become a "watchdog" (e.g., it starts a new process when + an existing one crashes, does a cleanup upon application termination, + etc.).
  10. +

    +

  11. In each child process create a pool of threads (see + st_thread_create()) to handle user + connections. Each thread in the pool may accept client connections + (st_accept()), connect to other servers + (st_connect()), perform various network I/O + (st_read(), st_write(), etc.).
  12. +
+

+Note that only State Threads library I/O functions should +be used for a network I/O: any other I/O calls may block the calling process +indefinitely. For example, standard I/O functions (fgets(3), +fread(3), fwrite(3), fprintf(3), etc.) call +read(2) and write(2) directly and therefore should not be +used on sockets or pipes. +

+Also note that for short timeouts to work the program +should do context switches (for example by calling +st_usleep()) on a regular basis. +

+


+

+ + +

List of Blocking Functions

+ +

+The thread context switch (process state change) can only happen +in a well-known set of blocking functions. +Only the following functions can block the calling thread: +

+

+
st_thread_join()
+
st_sleep()
+
st_usleep()
+
st_cond_wait()
+
st_cond_timedwait()
+
st_mutex_lock()
+
st_netfd_poll()
+
st_accept()
+
st_connect()
+
st_read()
+
st_read_fully()
+
st_read_resid()
+
st_readv()
+
st_readv_resid()
+
st_write()
+
st_write_resid()
+
st_writev()
+
st_writev_resid()
+
st_recvfrom()
+
st_sendto()
+
st_recvmsg()
+
st_sendmsg()
+
st_poll()
+
+

+


+

+ + + + diff --git a/trunk/3rdparty/st-srs/docs/st.html b/trunk/3rdparty/st-srs/docs/st.html new file mode 100644 index 000000000..a6b932a81 --- /dev/null +++ b/trunk/3rdparty/st-srs/docs/st.html @@ -0,0 +1,504 @@ + + +State Threads for Internet Applications + + +

State Threads for Internet Applications

+

Introduction

+

+State Threads is an application library which provides a +foundation for writing fast and highly scalable Internet Applications +on UNIX-like platforms. It combines the simplicity of the multithreaded +programming paradigm, in which one thread supports each simultaneous +connection, with the performance and scalability of an event-driven +state machine architecture.

+ +

1. Definitions

+

+ +

1.1 Internet Applications

+ +

+An Internet Application (IA) is either a server or client network +application that accepts connections from clients and may or may not +connect to servers. In an IA the arrival or departure of network data +often controls processing (that is, IA is a data-driven application). +For each connection, an IA does some finite amount of work +involving data exchange with its peer, where its peer may be either +a client or a server. +The typical transaction steps of an IA are to accept a connection, +read a request, do some finite and predictable amount of work to +process the request, then write a response to the peer that sent the +request. One example of an IA is a Web server; +the most general example of an IA is a proxy server, because it both +accepts connections from clients and connects to other servers.

+

+We assume that the performance of an IA is constrained by available CPU +cycles rather than network bandwidth or disk I/O (that is, CPU +is a bottleneck resource). +

+ + +

1.2 Performance and Scalability

+ +

+The performance of an IA is usually evaluated as its +throughput measured in transactions per second or bytes per second (one +can be converted to the other, given the average transaction size). There are +several benchmarks that can be used to measure throughput of Web serving +applications for specific workloads (such as +SPECweb96, +WebStone, +WebBench). +Although there is no common definition for scalability, in general it +expresses the ability of an application to sustain its performance when some +external condition changes. For IAs this external condition is either the +number of clients (also known as "users," "simultaneous connections," or "load +generators") or the underlying hardware system size (number of CPUs, memory +size, and so on). Thus there are two types of scalability: load +scalability and system scalability, respectively. +

+The figure below shows how the throughput of an idealized IA changes with +the increasing number of clients (solid blue line). Initially the throughput +grows linearly (the slope represents the maximal throughput that one client +can provide). Within this initial range, the IA is underutilized and CPUs are +partially idle. Further increase in the number of clients leads to a system +saturation, and the throughput gradually stops growing as all CPUs become fully +utilized. After that point, the throughput stays flat because there are no +more CPU cycles available. +In the real world, however, each simultaneous connection +consumes some computational and memory resources, even when idle, and this +overhead grows with the number of clients. Therefore, the throughput of the +real world IA starts dropping after some point (dashed blue line in the figure +below). The rate at which the throughput drops depends, among other things, on +application design. +

+We say that an application has a good load scalability if it can +sustain its throughput over a wide range of loads. +Interestingly, the SPECweb99 +benchmark somewhat reflects the Web server's load scalability because it +measures the number of clients (load generators) given a mandatory minimal +throughput per client (that is, it measures the server's capacity). +This is unlike SPECweb96 and +other benchmarks that use the throughput as their main metric (see the figure +below). +

+

Figure: Throughput vs. Number of clients +
+

+System scalability is the ability of an application to sustain its +performance per hardware unit (such as a CPU) with the increasing number of +these units. In other words, good system scalability means that doubling the +number of processors will roughly double the application's throughput (dashed +green line). We assume here that the underlying operating system also scales +well. Good system scalability allows you to initially run an application on +the smallest system possible, while retaining the ability to move that +application to a larger system if necessary, without excessive effort or +expense. That is, an application need not be rewritten or even undergo a +major porting effort when changing system size. +

+Although scalability and performance are more important in the case of server +IAs, they should also be considered for some client applications (such as +benchmark load generators). +

+ + +

1.3 Concurrency

+ +

+Concurrency reflects the parallelism in a system. The two unrelated types +are virtual concurrency and real concurrency. +

    +
  • Virtual (or apparent) concurrency is the number of simultaneous +connections that a system supports. +

    +
  • Real concurrency is the number of hardware devices, including +CPUs, network cards, and disks, that actually allow a system to perform +tasks in parallel. +
+

+An IA must provide virtual concurrency in order to serve many users +simultaneously. +To achieve maximum performance and scalability in doing so, the number of +programming entities than an IA creates to be scheduled by the OS kernel +should be +kept close to (within an order of magnitude of) the real concurrency found on +the system. These programming entities scheduled by the kernel are known as +kernel execution vehicles. Examples of kernel execution vehicles +include Solaris lightweight processes and IRIX kernel threads. +In other words, the number of kernel execution vehicles should be dictated by +the system size and not by the number of simultaneous connections. +

+ +

2. Existing Architectures

+

+There are a few different architectures that are commonly used by IAs. +These include the Multi-Process, +Multi-Threaded, and Event-Driven State Machine +architectures. +

+ +

2.1 Multi-Process Architecture

+ +

+In the Multi-Process (MP) architecture, an individual process is +dedicated to each simultaneous connection. +A process performs all of a transaction's initialization steps +and services a connection completely before moving on to service +a new connection. +

+User sessions in IAs are relatively independent; therefore, no +synchronization between processes handling different connections is +necessary. Because each process has its own private address space, +this architecture is very robust. If a process serving one of the connections +crashes, the other sessions will not be affected. However, to serve many +concurrent connections, an equal number of processes must be employed. +Because processes are kernel entities (and are in fact the heaviest ones), +the number of kernel entities will be at least as large as the number of +concurrent sessions. On most systems, good performance will not be achieved +when more than a few hundred processes are created because of the high +context-switching overhead. In other words, MP applications have poor load +scalability. +

+On the other hand, MP applications have very good system scalability, because +no resources are shared among different processes and there is no +synchronization overhead. +

+The Apache Web Server 1.x ([Reference 1]) uses the MP +architecture on UNIX systems. +

+ +

2.2 Multi-Threaded Architecture

+ +

+In the Multi-Threaded (MT) architecture, multiple independent threads +of control are employed within a single shared address space. Like a +process in the MP architecture, each thread performs all of a +transaction's initialization steps and services a connection completely +before moving on to service a new connection. +

+Many modern UNIX operating systems implement a many-to-few model when +mapping user-level threads to kernel entities. In this model, an +arbitrarily large number of user-level threads is multiplexed onto a +lesser number of kernel execution vehicles. Kernel execution +vehicles are also known as virtual processors. Whenever a user-level +thread makes a blocking system call, the kernel execution vehicle it is using +will become blocked in the kernel. If there are no other non-blocked kernel +execution vehicles and there are other runnable user-level threads, a new +kernel execution vehicle will be created automatically. This prevents the +application from blocking when it can continue to make useful forward +progress. +

+Because IAs are by nature network I/O driven, all concurrent sessions block on +network I/O at various points. As a result, the number of virtual processors +created in the kernel grows close to the number of user-level threads +(or simultaneous connections). When this occurs, the many-to-few model +effectively degenerates to a one-to-one model. Again, like in +the MP architecture, the number of kernel execution vehicles is dictated by +the number of simultaneous connections rather than by number of CPUs. This +reduces an application's load scalability. However, because kernel threads +(lightweight processes) use fewer resources and are more light-weight than +traditional UNIX processes, an MT application should scale better with load +than an MP application. +

+Unexpectedly, the small number of virtual processors sharing the same address +space in the MT architecture destroys an application's system scalability +because of contention among the threads on various locks. Even if an +application itself is carefully +optimized to avoid lock contention around its own global data (a non-trivial +task), there are still standard library functions and system calls +that use common resources hidden from the application. For example, +on many platforms thread safety of memory allocation routines +(malloc(3), free(3), and so on) is achieved by using a single +global lock. Another example is a per-process file descriptor table. +This common resource table is shared by all kernel execution vehicles within +the same process and must be protected when one modifies it via +certain system calls (such as open(2), close(2), and so on). +In addition to that, maintaining the caches coherent +among CPUs on multiprocessor systems hurts performance when different threads +running on different CPUs modify data items on the same cache line. +

+In order to improve load scalability, some applications employ a different +type of MT architecture: they create one or more thread(s) per task +rather than one thread per connection. For example, one small group +of threads may be responsible for accepting client connections, another +for request processing, and yet another for serving responses. The main +advantage of this architecture is that it eliminates the tight coupling +between the number of threads and number of simultaneous connections. However, +in this architecture, different task-specific thread groups must share common +work queues that must be protected by mutual exclusion locks (a typical +producer-consumer problem). This adds synchronization overhead that causes an +application to perform badly on multiprocessor systems. In other words, in +this architecture, the application's system scalability is sacrificed for the +sake of load scalability. +

+Of course, the usual nightmares of threaded programming, including data +corruption, deadlocks, and race conditions, also make MT architecture (in any +form) non-simplistic to use. +

+ + +

2.3 Event-Driven State Machine Architecture

+ +

+In the Event-Driven State Machine (EDSM) architecture, a single process +is employed to concurrently process multiple connections. The basics of this +architecture are described in Comer and Stevens +[Reference 2]. +The EDSM architecture performs one basic data-driven step associated with +a particular connection at a time, thus multiplexing many concurrent +connections. The process operates as a state machine that receives an event +and then reacts to it. +

+In the idle state the EDSM calls select(2) or poll(2) to +wait for network I/O events. When a particular file descriptor is ready for +I/O, the EDSM completes the corresponding basic step (usually by invoking a +handler function) and starts the next one. This architecture uses +non-blocking system calls to perform asynchronous network I/O operations. +For more details on non-blocking I/O see Stevens +[Reference 3]. +

+To take advantage of hardware parallelism (real concurrency), multiple +identical processes may be created. This is called Symmetric Multi-Process +EDSM and is used, for example, in the Zeus Web Server +([Reference 4]). To more efficiently multiplex disk I/O, +special "helper" processes may be created. This is called Asymmetric +Multi-Process EDSM and was proposed for Web servers by Druschel +and others [Reference 5]. +

+EDSM is probably the most scalable architecture for IAs. +Because the number of simultaneous connections (virtual concurrency) is +completely decoupled from the number of kernel execution vehicles (processes), +this architecture has very good load scalability. It requires only minimal +user-level resources to create and maintain additional connection. +

+Like MP applications, Multi-Process EDSM has very good system scalability +because no resources are shared among different processes and there is no +synchronization overhead. +

+Unfortunately, the EDSM architecture is monolithic rather than based on the +concept of threads, so new applications generally need to be implemented from +the ground up. In effect, the EDSM architecture simulates threads and their +stacks the hard way. +

+ + +

3. State Threads Library

+ +

+The State Threads library combines the advantages of all of the above +architectures. The interface preserves the programming simplicity of thread +abstraction, allowing each simultaneous connection to be treated as a separate +thread of execution within a single process. The underlying implementation is +close to the EDSM architecture as the state of each particular concurrent +session is saved in a separate memory segment. +

+ +

3.1 State Changes and Scheduling

+

+The state of each concurrent session includes its stack environment +(stack pointer, program counter, CPU registers) and its stack. Conceptually, +a thread context switch can be viewed as a process changing its state. There +are no kernel entities involved other than processes. +Unlike other general-purpose threading libraries, the State Threads library +is fully deterministic. The thread context switch (process state change) can +only happen in a well-known set of functions (at I/O points or at explicit +synchronization points). As a result, process-specific global data does not +have to be protected by mutual exclusion locks in most cases. The entire +application is free to use all the static variables and non-reentrant library +functions it wants, greatly simplifying programming and debugging while +increasing performance. This is somewhat similar to a co-routine model +(co-operatively multitasked threads), except that no explicit yield is needed +-- +sooner or later, a thread performs a blocking I/O operation and thus surrenders +control. All threads of execution (simultaneous connections) have the +same priority, so scheduling is non-preemptive, like in the EDSM architecture. +Because IAs are data-driven (processing is limited by the size of network +buffers and data arrival rates), scheduling is non-time-slicing. +

+Only two types of external events are handled by the library's +scheduler, because only these events can be detected by +select(2) or poll(2): I/O events (a file descriptor is ready +for I/O) and time events +(some timeout has expired). However, other types of events (such as +a signal sent to a process) can also be handled by converting them to I/O +events. For example, a signal handling function can perform a write to a pipe +(write(2) is reentrant/asynchronous-safe), thus converting a signal +event to an I/O event. +

+To take advantage of hardware parallelism, as in the EDSM architecture, +multiple processes can be created in either a symmetric or asymmetric manner. +Process management is not in the library's scope but instead is left up to the +application. +

+There are several general-purpose threading libraries that implement a +many-to-one model (many user-level threads to one kernel execution +vehicle), using the same basic techniques as the State Threads library +(non-blocking I/O, event-driven scheduler, and so on). For an example, see GNU +Portable Threads ([Reference 6]). Because they are +general-purpose, these libraries have different objectives than the State +Threads library. The State Threads library is not a general-purpose +threading library, +but rather an application library that targets only certain types of +applications (IAs) in order to achieve the highest possible performance and +scalability for those applications. +

+ +

3.2 Scalability

+

+State threads are very lightweight user-level entities, and therefore creating +and maintaining user connections requires minimal resources. An application +using the State Threads library scales very well with the increasing number +of connections. +

+On multiprocessor systems an application should create multiple processes +to take advantage of hardware parallelism. Using multiple separate processes +is the only way to achieve the highest possible system scalability. +This is because duplicating per-process resources is the only way to avoid +significant synchronization overhead on multiprocessor systems. Creating +separate UNIX processes naturally offers resource duplication. Again, +as in the EDSM architecture, there is no connection between the number of +simultaneous connections (which may be very large and changes within a wide +range) and the number of kernel entities (which is usually small and constant). +In other words, the State Threads library makes it possible to multiplex a +large number of simultaneous connections onto a much smaller number of +separate processes, thus allowing an application to scale well with both +the load and system size. +

+ +

3.3 Performance

+

+Performance is one of the library's main objectives. The State Threads +library is implemented to minimize the number of system calls and +to make thread creation and context switching as fast as possible. +For example, per-thread signal mask does not exist (unlike +POSIX threads), so there is no need to save and restore a process's +signal mask on every thread context switch. This eliminates two system +calls per context switch. Signal events can be handled much more +efficiently by converting them to I/O events (see above). +

+ +

3.4 Portability

+

+The library uses the same general, underlying concepts as the EDSM +architecture, including non-blocking I/O, file descriptors, and +I/O multiplexing. These concepts are available in some form on most +UNIX platforms, making the library very portable across many +flavors of UNIX. There are only a few platform-dependent sections in the +source. +

+ +

3.5 State Threads and NSPR

+

+The State Threads library is a derivative of the Netscape Portable +Runtime library (NSPR) [Reference 7]. The primary goal of +NSPR is to provide a platform-independent layer for system facilities, +where system facilities include threads, thread synchronization, and I/O. +Performance and scalability are not the main concern of NSPR. The +State Threads library addresses performance and scalability while +remaining much smaller than NSPR. It is contained in 8 source files +as opposed to more than 400, but provides all the functionality that +is needed to write efficient IAs on UNIX-like platforms. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NSPRState Threads
Lines of code~150,000~3000
Dynamic library size  
(debug version)
IRIX~700 KB~60 KB
Linux~900 KB~70 KB
+

+ +

Conclusion

+

+State Threads is an application library which provides a foundation for +writing Internet Applications. To summarize, it has the +following advantages: +

+

    +
  • It allows the design of fast and highly scalable applications. An +application will scale well with both load and number of CPUs. +

    +

  • It greatly simplifies application programming and debugging because, as a +rule, no mutual exclusion locking is necessary and the entire application is +free to use static variables and non-reentrant library functions. +
+

+The library's main limitation: +

+

    +
  • All I/O operations on sockets must use the State Thread library's I/O +functions because only those functions perform thread scheduling and prevent +the application's processes from blocking. +
+

+ +

References

+
    + +
  1. Apache Software Foundation, +http://www.apache.org. + +
  2. Douglas E. Comer, David L. Stevens, Internetworking With TCP/IP, +Vol. III: Client-Server Programming And Applications, Second Edition, +Ch. 8, 12. + +
  3. W. Richard Stevens, UNIX Network Programming, Second Edition, +Vol. 1, Ch. 15. + +
  4. Zeus Technology Limited, +http://www.zeus.co.uk. + +
  5. Peter Druschel, Vivek S. Pai, Willy Zwaenepoel, + +Flash: An Efficient and Portable Web Server. In Proceedings of the +USENIX 1999 Annual Technical Conference, Monterey, CA, June 1999. + +
  6. GNU Portable Threads, +http://www.gnu.org/software/pth/. + +
  7. Netscape Portable Runtime, +http://www.mozilla.org/docs/refList/refNSPR/. +
+ +

Other resources covering various architectural issues in IAs

+
    +
  1. Dan Kegel, The C10K problem, +http://www.kegel.com/c10k.html. +
  2. +
  3. James C. Hu, Douglas C. Schmidt, Irfan Pyarali, JAWS: Understanding +High Performance Web Systems, +http://www.cs.wustl.edu/~jxh/research/research.html.
  4. +
+

+


+

+ +

Portions created by SGI are Copyright © 2000 +Silicon Graphics, Inc. All rights reserved.
+

+ + + + diff --git a/trunk/3rdparty/st-srs/docs/timeout_heap.txt b/trunk/3rdparty/st-srs/docs/timeout_heap.txt new file mode 100644 index 000000000..1582dc129 --- /dev/null +++ b/trunk/3rdparty/st-srs/docs/timeout_heap.txt @@ -0,0 +1,60 @@ +How the timeout heap works + +As of version 1.5, the State Threads Library represents the queue of +sleeping threads using a heap data structure rather than a sorted +linked list. This improves performance when there is a large number +of sleeping threads, since insertion into a heap takes O(log N) time +while insertion into a sorted list takes O(N) time. For example, in +one test 1000 threads were created, each thread called st_usleep() +with a random time interval, and then all the threads where +immediately interrupted and joined before the sleeps had a chance to +finish. The whole process was repeated 1000 times, for a total of a +million sleep queue insertions and removals. With the old list-based +sleep queue, this test took 100 seconds; now it takes only 12 seconds. + +Heap data structures are typically based on dynamically resized +arrays. However, since the existing ST code base was very nicely +structured around linking the thread objects into pointer-based lists +without the need for any auxiliary data structures, implementing the +heap using a similar nodes-and-pointers based approach seemed more +appropriate for ST than introducing a separate array. + +Thus, the new ST timeout heap works by organizing the existing +_st_thread_t objects in a balanced binary tree, just as they were +previously organized into a doubly-linked, sorted list. The global +_ST_SLEEPQ variable, formerly a linked list head, is now simply a +pointer to the root of this tree, and the root node of the tree is the +thread with the earliest timeout. Each thread object has two child +pointers, "left" and "right", pointing to threads with later timeouts. + +Each node in the tree is numbered with an integer index, corresponding +to the array index in an array-based heap, and the tree is kept fully +balanced and left-adjusted at all times. In other words, the tree +consists of any number of fully populated top levels, followed by a +single bottom level which may be partially populated, such that any +existing nodes form a contiguous block to the left and the spaces for +missing nodes form a contiguous block to the right. For example, if +there are nine threads waiting for a timeout, they are numbered and +arranged in a tree exactly as follows: + + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + / \ + 8 9 + +Each node has either no children, only a left child, or both a left +and a right child. Children always time out later than their parents +(this is called the "heap invariant"), but when a node has two +children, their mutual order is unspecified - the left child may time +out before or after the right child. If a node is numbered N, its +left child is numbered 2N, and its right child is numbered 2N+1. + +There is no pointer from a child to its parent; all pointers point +downward. Additions and deletions both work by starting at the root +and traversing the tree towards the leaves, going left or right +according to the binary digits forming the index of the destination +node. As nodes are added or deleted, existing nodes are rearranged to +maintain the heap invariant. diff --git a/trunk/3rdparty/st-srs/event.c b/trunk/3rdparty/st-srs/event.c new file mode 100644 index 000000000..142882a51 --- /dev/null +++ b/trunk/3rdparty/st-srs/event.c @@ -0,0 +1,1413 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * Yahoo! Inc. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include +#include +#include +#include +#include +#include +#include "common.h" + +#ifdef MD_HAVE_KQUEUE +#include +#endif +#ifdef MD_HAVE_EPOLL +#include +#endif + +#if defined(USE_POLL) && !defined(MD_HAVE_POLL) + /* Force poll usage if explicitly asked for it */ + #define MD_HAVE_POLL +#endif + + +static struct _st_seldata { + fd_set fd_read_set, fd_write_set, fd_exception_set; + int fd_ref_cnts[FD_SETSIZE][3]; + int maxfd; +} *_st_select_data; + +#define _ST_SELECT_MAX_OSFD (_st_select_data->maxfd) +#define _ST_SELECT_READ_SET (_st_select_data->fd_read_set) +#define _ST_SELECT_WRITE_SET (_st_select_data->fd_write_set) +#define _ST_SELECT_EXCEP_SET (_st_select_data->fd_exception_set) +#define _ST_SELECT_READ_CNT(fd) (_st_select_data->fd_ref_cnts[fd][0]) +#define _ST_SELECT_WRITE_CNT(fd) (_st_select_data->fd_ref_cnts[fd][1]) +#define _ST_SELECT_EXCEP_CNT(fd) (_st_select_data->fd_ref_cnts[fd][2]) + + +#ifdef MD_HAVE_POLL +static struct _st_polldata { + struct pollfd *pollfds; + int pollfds_size; + int fdcnt; +} *_st_poll_data; + +#define _ST_POLL_OSFD_CNT (_st_poll_data->fdcnt) +#define _ST_POLLFDS (_st_poll_data->pollfds) +#define _ST_POLLFDS_SIZE (_st_poll_data->pollfds_size) +#endif /* MD_HAVE_POLL */ + + +#ifdef MD_HAVE_KQUEUE +typedef struct _kq_fd_data { + int rd_ref_cnt; + int wr_ref_cnt; + int revents; +} _kq_fd_data_t; + +static struct _st_kqdata { + _kq_fd_data_t *fd_data; + struct kevent *evtlist; + struct kevent *addlist; + struct kevent *dellist; + int fd_data_size; + int evtlist_size; + int addlist_size; + int addlist_cnt; + int dellist_size; + int dellist_cnt; + int kq; + pid_t pid; +} *_st_kq_data; + +#ifndef ST_KQ_MIN_EVTLIST_SIZE +#define ST_KQ_MIN_EVTLIST_SIZE 64 +#endif + +#define _ST_KQ_READ_CNT(fd) (_st_kq_data->fd_data[fd].rd_ref_cnt) +#define _ST_KQ_WRITE_CNT(fd) (_st_kq_data->fd_data[fd].wr_ref_cnt) +#define _ST_KQ_REVENTS(fd) (_st_kq_data->fd_data[fd].revents) +#endif /* MD_HAVE_KQUEUE */ + + +#ifdef MD_HAVE_EPOLL +typedef struct _epoll_fd_data { + int rd_ref_cnt; + int wr_ref_cnt; + int ex_ref_cnt; + int revents; +} _epoll_fd_data_t; + +static struct _st_epolldata { + _epoll_fd_data_t *fd_data; + struct epoll_event *evtlist; + int fd_data_size; + int evtlist_size; + int evtlist_cnt; + int fd_hint; + int epfd; + pid_t pid; +} *_st_epoll_data; + +#ifndef ST_EPOLL_EVTLIST_SIZE + /* Not a limit, just a hint */ + #define ST_EPOLL_EVTLIST_SIZE 4096 +#endif + +#define _ST_EPOLL_READ_CNT(fd) (_st_epoll_data->fd_data[fd].rd_ref_cnt) +#define _ST_EPOLL_WRITE_CNT(fd) (_st_epoll_data->fd_data[fd].wr_ref_cnt) +#define _ST_EPOLL_EXCEP_CNT(fd) (_st_epoll_data->fd_data[fd].ex_ref_cnt) +#define _ST_EPOLL_REVENTS(fd) (_st_epoll_data->fd_data[fd].revents) + +#define _ST_EPOLL_READ_BIT(fd) (_ST_EPOLL_READ_CNT(fd) ? EPOLLIN : 0) +#define _ST_EPOLL_WRITE_BIT(fd) (_ST_EPOLL_WRITE_CNT(fd) ? EPOLLOUT : 0) +#define _ST_EPOLL_EXCEP_BIT(fd) (_ST_EPOLL_EXCEP_CNT(fd) ? EPOLLPRI : 0) +#define _ST_EPOLL_EVENTS(fd) \ + (_ST_EPOLL_READ_BIT(fd)|_ST_EPOLL_WRITE_BIT(fd)|_ST_EPOLL_EXCEP_BIT(fd)) + +#endif /* MD_HAVE_EPOLL */ + +_st_eventsys_t *_st_eventsys = NULL; + + +/***************************************** + * select event system + */ + +ST_HIDDEN int _st_select_init(void) +{ + _st_select_data = (struct _st_seldata *) malloc(sizeof(*_st_select_data)); + if (!_st_select_data) + return -1; + + memset(_st_select_data, 0, sizeof(*_st_select_data)); + _st_select_data->maxfd = -1; + + return 0; +} + +ST_HIDDEN int _st_select_pollset_add(struct pollfd *pds, int npds) +{ + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + /* Do checks up front */ + for (pd = pds; pd < epd; pd++) { + if (pd->fd < 0 || pd->fd >= FD_SETSIZE || !pd->events || + (pd->events & ~(POLLIN | POLLOUT | POLLPRI))) { + errno = EINVAL; + return -1; + } + } + + for (pd = pds; pd < epd; pd++) { + if (pd->events & POLLIN) { + FD_SET(pd->fd, &_ST_SELECT_READ_SET); + _ST_SELECT_READ_CNT(pd->fd)++; + } + if (pd->events & POLLOUT) { + FD_SET(pd->fd, &_ST_SELECT_WRITE_SET); + _ST_SELECT_WRITE_CNT(pd->fd)++; + } + if (pd->events & POLLPRI) { + FD_SET(pd->fd, &_ST_SELECT_EXCEP_SET); + _ST_SELECT_EXCEP_CNT(pd->fd)++; + } + if (_ST_SELECT_MAX_OSFD < pd->fd) + _ST_SELECT_MAX_OSFD = pd->fd; + } + + return 0; +} + +ST_HIDDEN void _st_select_pollset_del(struct pollfd *pds, int npds) +{ + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + for (pd = pds; pd < epd; pd++) { + if (pd->events & POLLIN) { + if (--_ST_SELECT_READ_CNT(pd->fd) == 0) + FD_CLR(pd->fd, &_ST_SELECT_READ_SET); + } + if (pd->events & POLLOUT) { + if (--_ST_SELECT_WRITE_CNT(pd->fd) == 0) + FD_CLR(pd->fd, &_ST_SELECT_WRITE_SET); + } + if (pd->events & POLLPRI) { + if (--_ST_SELECT_EXCEP_CNT(pd->fd) == 0) + FD_CLR(pd->fd, &_ST_SELECT_EXCEP_SET); + } + } +} + +ST_HIDDEN void _st_select_find_bad_fd(void) +{ + _st_clist_t *q; + _st_pollq_t *pq; + int notify; + struct pollfd *pds, *epds; + int pq_max_osfd, osfd; + short events; + + _ST_SELECT_MAX_OSFD = -1; + + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + notify = 0; + epds = pq->pds + pq->npds; + pq_max_osfd = -1; + + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + pds->revents = 0; + if (pds->events == 0) + continue; + if (fcntl(osfd, F_GETFL, 0) < 0) { + pds->revents = POLLNVAL; + notify = 1; + } + if (osfd > pq_max_osfd) { + pq_max_osfd = osfd; + } + } + + if (notify) { + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + /* + * Decrement the count of descriptors for each descriptor/event + * because this I/O request is being removed from the ioq + */ + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + if (events & POLLIN) { + if (--_ST_SELECT_READ_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_READ_SET); + } + } + if (events & POLLOUT) { + if (--_ST_SELECT_WRITE_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_WRITE_SET); + } + } + if (events & POLLPRI) { + if (--_ST_SELECT_EXCEP_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_EXCEP_SET); + } + } + } + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + } else { + if (_ST_SELECT_MAX_OSFD < pq_max_osfd) + _ST_SELECT_MAX_OSFD = pq_max_osfd; + } + } +} + +ST_HIDDEN void _st_select_dispatch(void) +{ + struct timeval timeout, *tvp; + fd_set r, w, e; + fd_set *rp, *wp, *ep; + int nfd, pq_max_osfd, osfd; + _st_clist_t *q; + st_utime_t min_timeout; + _st_pollq_t *pq; + int notify; + struct pollfd *pds, *epds; + short events, revents; + + /* + * Assignment of fd_sets + */ + r = _ST_SELECT_READ_SET; + w = _ST_SELECT_WRITE_SET; + e = _ST_SELECT_EXCEP_SET; + + rp = &r; + wp = &w; + ep = &e; + + if (_ST_SLEEPQ == NULL) { + tvp = NULL; + } else { + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : + (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + timeout.tv_sec = (int) (min_timeout / 1000000); + timeout.tv_usec = (int) (min_timeout % 1000000); + tvp = &timeout; + } + + /* Check for I/O operations */ + nfd = select(_ST_SELECT_MAX_OSFD + 1, rp, wp, ep, tvp); + + /* Notify threads that are associated with the selected descriptors */ + if (nfd > 0) { + _ST_SELECT_MAX_OSFD = -1; + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + notify = 0; + epds = pq->pds + pq->npds; + pq_max_osfd = -1; + + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + revents = 0; + if ((events & POLLIN) && FD_ISSET(osfd, rp)) { + revents |= POLLIN; + } + if ((events & POLLOUT) && FD_ISSET(osfd, wp)) { + revents |= POLLOUT; + } + if ((events & POLLPRI) && FD_ISSET(osfd, ep)) { + revents |= POLLPRI; + } + pds->revents = revents; + if (revents) { + notify = 1; + } + if (osfd > pq_max_osfd) { + pq_max_osfd = osfd; + } + } + if (notify) { + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + /* + * Decrement the count of descriptors for each descriptor/event + * because this I/O request is being removed from the ioq + */ + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + if (events & POLLIN) { + if (--_ST_SELECT_READ_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_READ_SET); + } + } + if (events & POLLOUT) { + if (--_ST_SELECT_WRITE_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_WRITE_SET); + } + } + if (events & POLLPRI) { + if (--_ST_SELECT_EXCEP_CNT(osfd) == 0) { + FD_CLR(osfd, &_ST_SELECT_EXCEP_SET); + } + } + } + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + } else { + if (_ST_SELECT_MAX_OSFD < pq_max_osfd) + _ST_SELECT_MAX_OSFD = pq_max_osfd; + } + } + } else if (nfd < 0) { + /* + * It can happen when a thread closes file descriptor + * that is being used by some other thread -- BAD! + */ + if (errno == EBADF) + _st_select_find_bad_fd(); + } +} + +ST_HIDDEN int _st_select_fd_new(int osfd) +{ + if (osfd >= FD_SETSIZE) { + errno = EMFILE; + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_select_fd_close(int osfd) +{ + if (_ST_SELECT_READ_CNT(osfd) || _ST_SELECT_WRITE_CNT(osfd) || + _ST_SELECT_EXCEP_CNT(osfd)) { + errno = EBUSY; + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_select_fd_getlimit(void) +{ + return FD_SETSIZE; +} + +static _st_eventsys_t _st_select_eventsys = { + "select", + ST_EVENTSYS_SELECT, + _st_select_init, + _st_select_dispatch, + _st_select_pollset_add, + _st_select_pollset_del, + _st_select_fd_new, + _st_select_fd_close, + _st_select_fd_getlimit +}; + + +#ifdef MD_HAVE_POLL +/***************************************** + * poll event system + */ + +ST_HIDDEN int _st_poll_init(void) +{ + _st_poll_data = (struct _st_polldata *) malloc(sizeof(*_st_poll_data)); + if (!_st_poll_data) + return -1; + + _ST_POLLFDS = (struct pollfd *) malloc(ST_MIN_POLLFDS_SIZE * + sizeof(struct pollfd)); + if (!_ST_POLLFDS) { + free(_st_poll_data); + _st_poll_data = NULL; + return -1; + } + _ST_POLLFDS_SIZE = ST_MIN_POLLFDS_SIZE; + _ST_POLL_OSFD_CNT = 0; + + return 0; +} + +ST_HIDDEN int _st_poll_pollset_add(struct pollfd *pds, int npds) +{ + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + for (pd = pds; pd < epd; pd++) { + if (pd->fd < 0 || !pd->events) { + errno = EINVAL; + return -1; + } + } + + _ST_POLL_OSFD_CNT += npds; + + return 0; +} + +/* ARGSUSED */ +ST_HIDDEN void _st_poll_pollset_del(struct pollfd *pds, int npds) +{ + _ST_POLL_OSFD_CNT -= npds; + ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); +} + +ST_HIDDEN void _st_poll_dispatch(void) +{ + int timeout, nfd; + _st_clist_t *q; + st_utime_t min_timeout; + _st_pollq_t *pq; + struct pollfd *pds, *epds, *pollfds; + + /* + * Build up the array of struct pollfd to wait on. + * If existing array is not big enough, release it and allocate a new one. + */ + ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); + if (_ST_POLL_OSFD_CNT > _ST_POLLFDS_SIZE) { + free(_ST_POLLFDS); + _ST_POLLFDS = (struct pollfd *) malloc((_ST_POLL_OSFD_CNT + 10) * + sizeof(struct pollfd)); + ST_ASSERT(_ST_POLLFDS != NULL); + _ST_POLLFDS_SIZE = _ST_POLL_OSFD_CNT + 10; + } + pollfds = _ST_POLLFDS; + + /* Gather all descriptors into one array */ + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + memcpy(pollfds, pq->pds, sizeof(struct pollfd) * pq->npds); + pollfds += pq->npds; + } + ST_ASSERT(pollfds <= _ST_POLLFDS + _ST_POLLFDS_SIZE); + + if (_ST_SLEEPQ == NULL) { + timeout = -1; + } else { + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : + (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + timeout = (int) (min_timeout / 1000); + } + + /* Check for I/O operations */ + nfd = poll(_ST_POLLFDS, _ST_POLL_OSFD_CNT, timeout); + + /* Notify threads that are associated with the selected descriptors */ + if (nfd > 0) { + pollfds = _ST_POLLFDS; + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + epds = pollfds + pq->npds; + for (pds = pollfds; pds < epds; pds++) { + if (pds->revents) + break; + } + if (pds < epds) { + memcpy(pq->pds, pollfds, sizeof(struct pollfd) * pq->npds); + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + + _ST_POLL_OSFD_CNT -= pq->npds; + ST_ASSERT(_ST_POLL_OSFD_CNT >= 0); + } + pollfds = epds; + } + } +} + +/* ARGSUSED */ +ST_HIDDEN int _st_poll_fd_new(int osfd) +{ + return 0; +} + +/* ARGSUSED */ +ST_HIDDEN int _st_poll_fd_close(int osfd) +{ + /* + * We don't maintain I/O counts for poll event system + * so nothing to check here. + */ + return 0; +} + +ST_HIDDEN int _st_poll_fd_getlimit(void) +{ + /* zero means no specific limit */ + return 0; +} + +static _st_eventsys_t _st_poll_eventsys = { + "poll", + ST_EVENTSYS_POLL, + _st_poll_init, + _st_poll_dispatch, + _st_poll_pollset_add, + _st_poll_pollset_del, + _st_poll_fd_new, + _st_poll_fd_close, + _st_poll_fd_getlimit +}; +#endif /* MD_HAVE_POLL */ + + +#ifdef MD_HAVE_KQUEUE +/***************************************** + * kqueue event system + */ + +ST_HIDDEN int _st_kq_init(void) +{ + int err = 0; + int rv = 0; + + _st_kq_data = (struct _st_kqdata *) calloc(1, sizeof(*_st_kq_data)); + if (!_st_kq_data) + return -1; + + if ((_st_kq_data->kq = kqueue()) < 0) { + err = errno; + rv = -1; + goto cleanup_kq; + } + fcntl(_st_kq_data->kq, F_SETFD, FD_CLOEXEC); + _st_kq_data->pid = getpid(); + + /* + * Allocate file descriptor data array. + * FD_SETSIZE looks like good initial size. + */ + _st_kq_data->fd_data_size = FD_SETSIZE; + _st_kq_data->fd_data = (_kq_fd_data_t *)calloc(_st_kq_data->fd_data_size, sizeof(_kq_fd_data_t)); + if (!_st_kq_data->fd_data) { + err = errno; + rv = -1; + goto cleanup_kq; + } + + /* Allocate event lists */ + _st_kq_data->evtlist_size = ST_KQ_MIN_EVTLIST_SIZE; + _st_kq_data->evtlist = (struct kevent *)malloc(_st_kq_data->evtlist_size * sizeof(struct kevent)); + _st_kq_data->addlist_size = ST_KQ_MIN_EVTLIST_SIZE; + _st_kq_data->addlist = (struct kevent *)malloc(_st_kq_data->addlist_size * sizeof(struct kevent)); + _st_kq_data->dellist_size = ST_KQ_MIN_EVTLIST_SIZE; + _st_kq_data->dellist = (struct kevent *)malloc(_st_kq_data->dellist_size * sizeof(struct kevent)); + if (!_st_kq_data->evtlist || !_st_kq_data->addlist || + !_st_kq_data->dellist) { + err = ENOMEM; + rv = -1; + } + + cleanup_kq: + if (rv < 0) { + if (_st_kq_data->kq >= 0) + close(_st_kq_data->kq); + free(_st_kq_data->fd_data); + free(_st_kq_data->evtlist); + free(_st_kq_data->addlist); + free(_st_kq_data->dellist); + free(_st_kq_data); + _st_kq_data = NULL; + errno = err; + } + + return rv; +} + +ST_HIDDEN int _st_kq_fd_data_expand(int maxfd) +{ + _kq_fd_data_t *ptr; + int n = _st_kq_data->fd_data_size; + + while (maxfd >= n) + n <<= 1; + + ptr = (_kq_fd_data_t *)realloc(_st_kq_data->fd_data, n * sizeof(_kq_fd_data_t)); + if (!ptr) + return -1; + + memset(ptr + _st_kq_data->fd_data_size, 0, (n - _st_kq_data->fd_data_size) * sizeof(_kq_fd_data_t)); + + _st_kq_data->fd_data = ptr; + _st_kq_data->fd_data_size = n; + + return 0; +} + +ST_HIDDEN int _st_kq_addlist_expand(int avail) +{ + struct kevent *ptr; + int n = _st_kq_data->addlist_size; + + while (avail > n - _st_kq_data->addlist_cnt) + n <<= 1; + + ptr = (struct kevent *)realloc(_st_kq_data->addlist, n * sizeof(struct kevent)); + if (!ptr) + return -1; + + _st_kq_data->addlist = ptr; + _st_kq_data->addlist_size = n; + + /* + * Try to expand the result event list too + * (although we don't have to do it). + */ + ptr = (struct kevent *)realloc(_st_kq_data->evtlist, n * sizeof(struct kevent)); + if (ptr) { + _st_kq_data->evtlist = ptr; + _st_kq_data->evtlist_size = n; + } + + return 0; +} + +ST_HIDDEN void _st_kq_addlist_add(const struct kevent *kev) +{ + ST_ASSERT(_st_kq_data->addlist_cnt < _st_kq_data->addlist_size); + memcpy(_st_kq_data->addlist + _st_kq_data->addlist_cnt, kev, sizeof(struct kevent)); + _st_kq_data->addlist_cnt++; +} + +ST_HIDDEN void _st_kq_dellist_add(const struct kevent *kev) +{ + int n = _st_kq_data->dellist_size; + + if (_st_kq_data->dellist_cnt >= n) { + struct kevent *ptr; + + n <<= 1; + ptr = (struct kevent *)realloc(_st_kq_data->dellist, n * sizeof(struct kevent)); + if (!ptr) { + /* See comment in _st_kq_pollset_del() */ + return; + } + + _st_kq_data->dellist = ptr; + _st_kq_data->dellist_size = n; + } + + memcpy(_st_kq_data->dellist + _st_kq_data->dellist_cnt, kev, sizeof(struct kevent)); + _st_kq_data->dellist_cnt++; +} + +ST_HIDDEN int _st_kq_pollset_add(struct pollfd *pds, int npds) +{ + struct kevent kev; + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + /* + * Pollset adding is "atomic". That is, either it succeeded for + * all descriptors in the set or it failed. It means that we + * need to do all the checks up front so we don't have to + * "unwind" if adding of one of the descriptors failed. + */ + for (pd = pds; pd < epd; pd++) { + /* POLLIN and/or POLLOUT must be set, but nothing else */ + if (pd->fd < 0 || !pd->events || (pd->events & ~(POLLIN | POLLOUT))) { + errno = EINVAL; + return -1; + } + if (pd->fd >= _st_kq_data->fd_data_size && + _st_kq_fd_data_expand(pd->fd) < 0) + return -1; + } + + /* + * Make sure we have enough room in the addlist for twice as many + * descriptors as in the pollset (for both READ and WRITE filters). + */ + npds <<= 1; + if (npds > _st_kq_data->addlist_size - _st_kq_data->addlist_cnt && _st_kq_addlist_expand(npds) < 0) + return -1; + + for (pd = pds; pd < epd; pd++) { + if ((pd->events & POLLIN) && (_ST_KQ_READ_CNT(pd->fd)++ == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = pd->fd; + kev.filter = EVFILT_READ; +#ifdef NOTE_EOF + /* Make it behave like select() and poll() */ + kev.fflags = NOTE_EOF; +#endif + kev.flags = (EV_ADD | EV_ONESHOT); + _st_kq_addlist_add(&kev); + } + if ((pd->events & POLLOUT) && (_ST_KQ_WRITE_CNT(pd->fd)++ == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = pd->fd; + kev.filter = EVFILT_WRITE; + kev.flags = (EV_ADD | EV_ONESHOT); + _st_kq_addlist_add(&kev); + } + } + + return 0; +} + +ST_HIDDEN void _st_kq_pollset_del(struct pollfd *pds, int npds) +{ + struct kevent kev; + struct pollfd *pd; + struct pollfd *epd = pds + npds; + + /* + * It's OK if deleting fails because a descriptor will either be + * closed or fire only once (we set EV_ONESHOT flag). + */ + _st_kq_data->dellist_cnt = 0; + for (pd = pds; pd < epd; pd++) { + if ((pd->events & POLLIN) && (--_ST_KQ_READ_CNT(pd->fd) == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = pd->fd; + kev.filter = EVFILT_READ; + kev.flags = EV_DELETE; + _st_kq_dellist_add(&kev); + } + if ((pd->events & POLLOUT) && (--_ST_KQ_WRITE_CNT(pd->fd) == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = pd->fd; + kev.filter = EVFILT_WRITE; + kev.flags = EV_DELETE; + _st_kq_dellist_add(&kev); + } + } + + if (_st_kq_data->dellist_cnt > 0) { + /* + * We do "synchronous" kqueue deletes to avoid deleting + * closed descriptors and other possible problems. + */ + int rv; + do { + /* This kevent() won't block since result list size is 0 */ + rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, _st_kq_data->dellist_cnt, NULL, 0, NULL); + } while (rv < 0 && errno == EINTR); + } +} + +ST_HIDDEN void _st_kq_dispatch(void) +{ + struct timespec timeout, *tsp; + struct kevent kev; + st_utime_t min_timeout; + _st_clist_t *q; + _st_pollq_t *pq; + struct pollfd *pds, *epds; + int nfd, i, osfd, notify, filter; + short events, revents; + + if (_ST_SLEEPQ == NULL) { + tsp = NULL; + } else { + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + timeout.tv_sec = (time_t) (min_timeout / 1000000); + timeout.tv_nsec = (long) ((min_timeout % 1000000) * 1000); + tsp = &timeout; + } + + retry_kevent: + /* Check for I/O operations */ + nfd = kevent(_st_kq_data->kq, + _st_kq_data->addlist, _st_kq_data->addlist_cnt, + _st_kq_data->evtlist, _st_kq_data->evtlist_size, tsp); + + _st_kq_data->addlist_cnt = 0; + + if (nfd > 0) { + for (i = 0; i < nfd; i++) { + osfd = _st_kq_data->evtlist[i].ident; + filter = _st_kq_data->evtlist[i].filter; + + if (filter == EVFILT_READ) { + _ST_KQ_REVENTS(osfd) |= POLLIN; + } else if (filter == EVFILT_WRITE) { + _ST_KQ_REVENTS(osfd) |= POLLOUT; + } + if (_st_kq_data->evtlist[i].flags & EV_ERROR) { + if (_st_kq_data->evtlist[i].data == EBADF) { + _ST_KQ_REVENTS(osfd) |= POLLNVAL; + } else { + _ST_KQ_REVENTS(osfd) |= POLLERR; + } + } + } + + _st_kq_data->dellist_cnt = 0; + + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + notify = 0; + epds = pq->pds + pq->npds; + + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + revents = (short)(_ST_KQ_REVENTS(osfd) & ~(POLLIN | POLLOUT)); + if ((events & POLLIN) && (_ST_KQ_REVENTS(osfd) & POLLIN)) { + revents |= POLLIN; + } + if ((events & POLLOUT) && (_ST_KQ_REVENTS(osfd) & POLLOUT)) { + revents |= POLLOUT; + } + pds->revents = revents; + if (revents) { + notify = 1; + } + } + if (notify) { + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + for (pds = pq->pds; pds < epds; pds++) { + osfd = pds->fd; + events = pds->events; + /* + * We set EV_ONESHOT flag so we only need to delete + * descriptor if it didn't fire. + */ + if ((events & POLLIN) && (--_ST_KQ_READ_CNT(osfd) == 0) && ((_ST_KQ_REVENTS(osfd) & POLLIN) == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = osfd; + kev.filter = EVFILT_READ; + kev.flags = EV_DELETE; + _st_kq_dellist_add(&kev); + } + if ((events & POLLOUT) && (--_ST_KQ_WRITE_CNT(osfd) == 0) && ((_ST_KQ_REVENTS(osfd) & POLLOUT) == 0)) { + memset(&kev, 0, sizeof(kev)); + kev.ident = osfd; + kev.filter = EVFILT_WRITE; + kev.flags = EV_DELETE; + _st_kq_dellist_add(&kev); + } + } + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + } + } + + if (_st_kq_data->dellist_cnt > 0) { + int rv; + do { + /* This kevent() won't block since result list size is 0 */ + rv = kevent(_st_kq_data->kq, _st_kq_data->dellist, _st_kq_data->dellist_cnt, NULL, 0, NULL); + } while (rv < 0 && errno == EINTR); + } + + for (i = 0; i < nfd; i++) { + osfd = _st_kq_data->evtlist[i].ident; + _ST_KQ_REVENTS(osfd) = 0; + } + + } else if (nfd < 0) { + if (errno == EBADF && _st_kq_data->pid != getpid()) { + /* We probably forked, reinitialize kqueue */ + if ((_st_kq_data->kq = kqueue()) < 0) { + /* There is nothing we can do here, will retry later */ + return; + } + fcntl(_st_kq_data->kq, F_SETFD, FD_CLOEXEC); + _st_kq_data->pid = getpid(); + /* Re-register all descriptors on ioq with new kqueue */ + memset(_st_kq_data->fd_data, 0, _st_kq_data->fd_data_size * sizeof(_kq_fd_data_t)); + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + _st_kq_pollset_add(pq->pds, pq->npds); + } + goto retry_kevent; + } + } +} + +ST_HIDDEN int _st_kq_fd_new(int osfd) +{ + if (osfd >= _st_kq_data->fd_data_size && _st_kq_fd_data_expand(osfd) < 0) + return -1; + + return 0; +} + +ST_HIDDEN int _st_kq_fd_close(int osfd) +{ + if (_ST_KQ_READ_CNT(osfd) || _ST_KQ_WRITE_CNT(osfd)) { + errno = EBUSY; + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_kq_fd_getlimit(void) +{ + /* zero means no specific limit */ + return 0; +} + +static _st_eventsys_t _st_kq_eventsys = { + "kqueue", + ST_EVENTSYS_ALT, + _st_kq_init, + _st_kq_dispatch, + _st_kq_pollset_add, + _st_kq_pollset_del, + _st_kq_fd_new, + _st_kq_fd_close, + _st_kq_fd_getlimit +}; +#endif /* MD_HAVE_KQUEUE */ + + +#ifdef MD_HAVE_EPOLL +/***************************************** + * epoll event system + */ + +ST_HIDDEN int _st_epoll_init(void) +{ + int fdlim; + int err = 0; + int rv = 0; + + _st_epoll_data = (struct _st_epolldata *) calloc(1, sizeof(*_st_epoll_data)); + if (!_st_epoll_data) + return -1; + + fdlim = st_getfdlimit(); + _st_epoll_data->fd_hint = (fdlim > 0 && fdlim < ST_EPOLL_EVTLIST_SIZE) ? fdlim : ST_EPOLL_EVTLIST_SIZE; + + if ((_st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint)) < 0) { + err = errno; + rv = -1; + goto cleanup_epoll; + } + fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC); + _st_epoll_data->pid = getpid(); + + /* Allocate file descriptor data array */ + _st_epoll_data->fd_data_size = _st_epoll_data->fd_hint; + _st_epoll_data->fd_data = (_epoll_fd_data_t *)calloc(_st_epoll_data->fd_data_size, sizeof(_epoll_fd_data_t)); + if (!_st_epoll_data->fd_data) { + err = errno; + rv = -1; + goto cleanup_epoll; + } + + /* Allocate event lists */ + _st_epoll_data->evtlist_size = _st_epoll_data->fd_hint; + _st_epoll_data->evtlist = (struct epoll_event *)malloc(_st_epoll_data->evtlist_size * sizeof(struct epoll_event)); + if (!_st_epoll_data->evtlist) { + err = errno; + rv = -1; + } + + cleanup_epoll: + if (rv < 0) { + if (_st_epoll_data->epfd >= 0) + close(_st_epoll_data->epfd); + free(_st_epoll_data->fd_data); + free(_st_epoll_data->evtlist); + free(_st_epoll_data); + _st_epoll_data = NULL; + errno = err; + } + + return rv; +} + +ST_HIDDEN int _st_epoll_fd_data_expand(int maxfd) +{ + _epoll_fd_data_t *ptr; + int n = _st_epoll_data->fd_data_size; + + while (maxfd >= n) + n <<= 1; + + ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, n * sizeof(_epoll_fd_data_t)); + if (!ptr) + return -1; + + memset(ptr + _st_epoll_data->fd_data_size, 0, (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t)); + + _st_epoll_data->fd_data = ptr; + _st_epoll_data->fd_data_size = n; + + return 0; +} + +ST_HIDDEN void _st_epoll_evtlist_expand(void) +{ + struct epoll_event *ptr; + int n = _st_epoll_data->evtlist_size; + + while (_st_epoll_data->evtlist_cnt > n) + n <<= 1; + + ptr = (struct epoll_event *)realloc(_st_epoll_data->evtlist, n * sizeof(struct epoll_event)); + if (ptr) { + _st_epoll_data->evtlist = ptr; + _st_epoll_data->evtlist_size = n; + } +} + +ST_HIDDEN void _st_epoll_pollset_del(struct pollfd *pds, int npds) +{ + struct epoll_event ev; + struct pollfd *pd; + struct pollfd *epd = pds + npds; + int old_events, events, op; + + /* + * It's more or less OK if deleting fails because a descriptor + * will either be closed or deleted in dispatch function after + * it fires. + */ + for (pd = pds; pd < epd; pd++) { + old_events = _ST_EPOLL_EVENTS(pd->fd); + + if (pd->events & POLLIN) + _ST_EPOLL_READ_CNT(pd->fd)--; + if (pd->events & POLLOUT) + _ST_EPOLL_WRITE_CNT(pd->fd)--; + if (pd->events & POLLPRI) + _ST_EPOLL_EXCEP_CNT(pd->fd)--; + + events = _ST_EPOLL_EVENTS(pd->fd); + /* + * The _ST_EPOLL_REVENTS check below is needed so we can use + * this function inside dispatch(). Outside of dispatch() + * _ST_EPOLL_REVENTS is always zero for all descriptors. + */ + if (events != old_events && _ST_EPOLL_REVENTS(pd->fd) == 0) { + op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; + ev.events = events; + ev.data.fd = pd->fd; + if (epoll_ctl(_st_epoll_data->epfd, op, pd->fd, &ev) == 0 && op == EPOLL_CTL_DEL) { + _st_epoll_data->evtlist_cnt--; + } + } + } +} + +ST_HIDDEN int _st_epoll_pollset_add(struct pollfd *pds, int npds) +{ + struct epoll_event ev; + int i, fd; + int old_events, events, op; + + /* Do as many checks as possible up front */ + for (i = 0; i < npds; i++) { + fd = pds[i].fd; + if (fd < 0 || !pds[i].events || + (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) { + errno = EINVAL; + return -1; + } + if (fd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(fd) < 0) + return -1; + } + + for (i = 0; i < npds; i++) { + fd = pds[i].fd; + old_events = _ST_EPOLL_EVENTS(fd); + + if (pds[i].events & POLLIN) + _ST_EPOLL_READ_CNT(fd)++; + if (pds[i].events & POLLOUT) + _ST_EPOLL_WRITE_CNT(fd)++; + if (pds[i].events & POLLPRI) + _ST_EPOLL_EXCEP_CNT(fd)++; + + events = _ST_EPOLL_EVENTS(fd); + if (events != old_events) { + op = old_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; + ev.events = events; + ev.data.fd = fd; + if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 && (op != EPOLL_CTL_ADD || errno != EEXIST)) + break; + if (op == EPOLL_CTL_ADD) { + _st_epoll_data->evtlist_cnt++; + if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size) + _st_epoll_evtlist_expand(); + } + } + } + + if (i < npds) { + /* Error */ + int err = errno; + /* Unroll the state */ + _st_epoll_pollset_del(pds, i + 1); + errno = err; + return -1; + } + + return 0; +} + +ST_HIDDEN void _st_epoll_dispatch(void) +{ + st_utime_t min_timeout; + _st_clist_t *q; + _st_pollq_t *pq; + struct pollfd *pds, *epds; + struct epoll_event ev; + int timeout, nfd, i, osfd, notify; + int events, op; + short revents; + + if (_ST_SLEEPQ == NULL) { + timeout = -1; + } else { + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + timeout = (int) (min_timeout / 1000); + } + + if (_st_epoll_data->pid != getpid()) { + /* We probably forked, reinitialize epoll set */ + close(_st_epoll_data->epfd); + _st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint); + if (_st_epoll_data->epfd < 0) { + /* There is nothing we can do here, will retry later */ + return; + } + fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC); + _st_epoll_data->pid = getpid(); + + /* Put all descriptors on ioq into new epoll set */ + memset(_st_epoll_data->fd_data, 0, _st_epoll_data->fd_data_size * sizeof(_epoll_fd_data_t)); + _st_epoll_data->evtlist_cnt = 0; + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + _st_epoll_pollset_add(pq->pds, pq->npds); + } + } + + /* Check for I/O operations */ + nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, _st_epoll_data->evtlist_size, timeout); + + if (nfd > 0) { + for (i = 0; i < nfd; i++) { + osfd = _st_epoll_data->evtlist[i].data.fd; + _ST_EPOLL_REVENTS(osfd) = _st_epoll_data->evtlist[i].events; + if (_ST_EPOLL_REVENTS(osfd) & (EPOLLERR | EPOLLHUP)) { + /* Also set I/O bits on error */ + _ST_EPOLL_REVENTS(osfd) |= _ST_EPOLL_EVENTS(osfd); + } + } + + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + notify = 0; + epds = pq->pds + pq->npds; + + for (pds = pq->pds; pds < epds; pds++) { + if (_ST_EPOLL_REVENTS(pds->fd) == 0) { + pds->revents = 0; + continue; + } + osfd = pds->fd; + events = pds->events; + revents = 0; + if ((events & POLLIN) && (_ST_EPOLL_REVENTS(osfd) & EPOLLIN)) + revents |= POLLIN; + if ((events & POLLOUT) && (_ST_EPOLL_REVENTS(osfd) & EPOLLOUT)) + revents |= POLLOUT; + if ((events & POLLPRI) && (_ST_EPOLL_REVENTS(osfd) & EPOLLPRI)) + revents |= POLLPRI; + if (_ST_EPOLL_REVENTS(osfd) & EPOLLERR) + revents |= POLLERR; + if (_ST_EPOLL_REVENTS(osfd) & EPOLLHUP) + revents |= POLLHUP; + + pds->revents = revents; + if (revents) { + notify = 1; + } + } + if (notify) { + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + /* + * Here we will only delete/modify descriptors that + * didn't fire (see comments in _st_epoll_pollset_del()). + */ + _st_epoll_pollset_del(pq->pds, pq->npds); + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(pq->thread); + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + } + } + + for (i = 0; i < nfd; i++) { + /* Delete/modify descriptors that fired */ + osfd = _st_epoll_data->evtlist[i].data.fd; + _ST_EPOLL_REVENTS(osfd) = 0; + events = _ST_EPOLL_EVENTS(osfd); + op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; + ev.events = events; + ev.data.fd = osfd; + if (epoll_ctl(_st_epoll_data->epfd, op, osfd, &ev) == 0 && op == EPOLL_CTL_DEL) { + _st_epoll_data->evtlist_cnt--; + } + } + } +} + +ST_HIDDEN int _st_epoll_fd_new(int osfd) +{ + if (osfd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(osfd) < 0) + return -1; + + return 0; +} + +ST_HIDDEN int _st_epoll_fd_close(int osfd) +{ + if (_ST_EPOLL_READ_CNT(osfd) || _ST_EPOLL_WRITE_CNT(osfd) || _ST_EPOLL_EXCEP_CNT(osfd)) { + errno = EBUSY; + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_epoll_fd_getlimit(void) +{ + /* zero means no specific limit */ + return 0; +} + +/* + * Check if epoll functions are just stubs. + */ +ST_HIDDEN int _st_epoll_is_supported(void) +{ + struct epoll_event ev; + + ev.events = EPOLLIN; + ev.data.ptr = NULL; + /* Guaranteed to fail */ + epoll_ctl(-1, EPOLL_CTL_ADD, -1, &ev); + + return (errno != ENOSYS); +} + +static _st_eventsys_t _st_epoll_eventsys = { + "epoll", + ST_EVENTSYS_ALT, + _st_epoll_init, + _st_epoll_dispatch, + _st_epoll_pollset_add, + _st_epoll_pollset_del, + _st_epoll_fd_new, + _st_epoll_fd_close, + _st_epoll_fd_getlimit +}; +#endif /* MD_HAVE_EPOLL */ + + +/***************************************** + * Public functions + */ + +int st_set_eventsys(int eventsys) +{ + if (_st_eventsys) { + errno = EBUSY; + return -1; + } + + switch (eventsys) { + case ST_EVENTSYS_DEFAULT: +#ifdef USE_POLL + _st_eventsys = &_st_poll_eventsys; +#else + _st_eventsys = &_st_select_eventsys; +#endif + break; + case ST_EVENTSYS_SELECT: + _st_eventsys = &_st_select_eventsys; + break; +#ifdef MD_HAVE_POLL + case ST_EVENTSYS_POLL: + _st_eventsys = &_st_poll_eventsys; + break; +#endif + case ST_EVENTSYS_ALT: +#if defined (MD_HAVE_KQUEUE) + _st_eventsys = &_st_kq_eventsys; +#elif defined (MD_HAVE_EPOLL) + if (_st_epoll_is_supported()) + _st_eventsys = &_st_epoll_eventsys; +#endif + break; + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +int st_get_eventsys(void) +{ + return _st_eventsys ? _st_eventsys->val : -1; +} + +const char *st_get_eventsys_name(void) +{ + return _st_eventsys ? _st_eventsys->name : ""; +} + diff --git a/trunk/3rdparty/st-srs/examples/Makefile b/trunk/3rdparty/st-srs/examples/Makefile new file mode 100644 index 000000000..31c0a6e24 --- /dev/null +++ b/trunk/3rdparty/st-srs/examples/Makefile @@ -0,0 +1,115 @@ +# +# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Silicon Graphics, Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +########################## +# Supported OSes: +# +# AIX +# FREEBSD +# HPUX +# HPUX_64 +# IRIX +# IRIX_64 +# LINUX +# LINUX_IA64 +# NETBSD +# OPENBSD +# OSF1 +# SOLARIS +# SOLARIS_64 + +########################## + +CC = cc + +SHELL = /bin/sh +ECHO = /bin/echo + +DEPTH = .. +BUILD = +TARGETDIR = + +DEFINES = +CFLAGS = +OTHER_FLAGS = + +OBJDIR = $(DEPTH)/$(TARGETDIR) +INCDIR = $(DEPTH)/$(TARGETDIR) +LIBST = $(OBJDIR)/libst.a +HEADER = $(INCDIR)/st.h + +LIBRESOLV = +EXTRALIBS = + +ifeq ($(OS),) +EXAMPLES = unknown +else +EXAMPLES = $(OBJDIR)/lookupdns $(OBJDIR)/proxy $(OBJDIR)/server +endif + + +########################## +# Platform section. +# + +ifeq (DARWIN, $(findstring DARWIN, $(OS))) +LIBRESOLV = -lresolv +endif + +ifeq (LINUX, $(findstring LINUX, $(OS))) +LIBRESOLV = -lresolv +endif + +ifeq (SOLARIS, $(findstring SOLARIS, $(OS))) +LIBRESOLV = -lresolv +EXTRALIBS = -lsocket -lnsl +endif + +# +# End of platform section. +########################## + + +all: $(EXAMPLES) + +$(OBJDIR)/lookupdns: lookupdns.c $(OBJDIR)/res.o $(LIBST) $(HEADER) + $(CC) $(CFLAGS) -I$(INCDIR) lookupdns.c $(OBJDIR)/res.o $(LIBST) $(LIBRESOLV) $(EXTRALIBS) -o $@ + +$(OBJDIR)/proxy: proxy.c $(LIBST) $(HEADER) + $(CC) $(CFLAGS) -I$(INCDIR) proxy.c $(LIBST) $(EXTRALIBS) -o $@ + +$(OBJDIR)/server: server.c $(OBJDIR)/error.o $(LIBST) $(HEADER) + $(CC) $(CFLAGS) -I$(INCDIR) server.c $(OBJDIR)/error.o $(LIBST) $(EXTRALIBS) -o $@ + +$(OBJDIR)/%.o: %.c + $(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@ + +.DEFAULT: + @cd $(DEPTH); $(MAKE) $@ + diff --git a/trunk/3rdparty/st-srs/examples/README b/trunk/3rdparty/st-srs/examples/README new file mode 100644 index 000000000..646d4f623 --- /dev/null +++ b/trunk/3rdparty/st-srs/examples/README @@ -0,0 +1,98 @@ +Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. +All Rights Reserved. + + +This directory contains three example programs. + + +--------------------------------------------------------------------------- + +PROGRAM + + lookupdns + +FILES + + lookupdns.c + res.c + +USAGE + + lookupdns [] ... + +DESCRIPTION + + This program performs asynchronous DNS host name resolution and reports + IP address for each specified as a command line argument. + One ST thread is created for each host name. All threads do host name + resolution concurrently. + + +--------------------------------------------------------------------------- + +PROGRAM + + proxy + +FILES + + proxy.c + +USAGE + + proxy -l -r [-p ] [-S] + + -l bind to local address specified as []: + -r connect to remote address specified as : + -p create specified number of processes + -S serialize accept() calls from different processes + on the same listening socket (if needed). + +DESCRIPTION + + This program acts as a generic gateway. It listens for connections to a + local address. Upon accepting a client connection, it connects to the + specified remote address and then just pumps the data through without any + modification. + + +--------------------------------------------------------------------------- + +PROGRAM + + server + +FILES + + server.c + error.c + +USAGE + + server -l [] + + -l open all log files in specified directory. + + Possible options: + + -b : bind to specified address (multiple addresses + are permitted) + -p create specified number of processes + -t : specify thread limits per listening socket + across all processes + -u change server's user id to specified value + -q set max length of pending connections queue + -a enable access logging + -i run in interactive mode (useful for debugging) + -S serialize accept() calls from different processes + on the same listening socket (if needed). + +DESCRIPTION + + This program is a general server example. It accepts a client connection + and outputs a short HTML page. It can be easily adapted to provide + other services. + + +--------------------------------------------------------------------------- + diff --git a/trunk/3rdparty/st-srs/examples/error.c b/trunk/3rdparty/st-srs/examples/error.c new file mode 100644 index 000000000..0b2e77287 --- /dev/null +++ b/trunk/3rdparty/st-srs/examples/error.c @@ -0,0 +1,168 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include "st.h" + +/* + * Simple error reporting functions. + * Suggested in W. Richard Stevens' "Advanced Programming in UNIX + * Environment". + */ + +#define MAXLINE 4096 /* max line length */ + +static void err_doit(int, int, const char *, va_list); + + +/* + * Nonfatal error related to a system call. + * Print a message and return. + */ +void err_sys_report(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 1, fmt, ap); + va_end(ap); +} + + +/* + * Fatal error related to a system call. + * Print a message and terminate. + */ +void err_sys_quit(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 1, fmt, ap); + va_end(ap); + exit(1); +} + + +/* + * Fatal error related to a system call. + * Print a message, dump core, and terminate. + */ +void err_sys_dump(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 1, fmt, ap); + va_end(ap); + abort(); /* dump core and terminate */ + exit(1); /* shouldn't get here */ +} + + +/* + * Nonfatal error unrelated to a system call. + * Print a message and return. + */ +void err_report(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 0, fmt, ap); + va_end(ap); +} + + +/* + * Fatal error unrelated to a system call. + * Print a message and terminate. + */ +void err_quit(int fd, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + err_doit(fd, 0, fmt, ap); + va_end(ap); + exit(1); +} + + +/* + * Return a pointer to a string containing current time. + */ +char *err_tstamp(void) +{ + static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + static char str[32]; + static time_t lastt = 0; + struct tm *tmp; + time_t currt = st_time(); + + if (currt == lastt) + return str; + + tmp = localtime(&currt); + sprintf(str, "[%02d/%s/%d:%02d:%02d:%02d] ", tmp->tm_mday, + months[tmp->tm_mon], 1900 + tmp->tm_year, tmp->tm_hour, + tmp->tm_min, tmp->tm_sec); + lastt = currt; + + return str; +} + + +/* + * Print a message and return to caller. + * Caller specifies "errnoflag". + */ +static void err_doit(int fd, int errnoflag, const char *fmt, va_list ap) +{ + int errno_save; + char buf[MAXLINE]; + + errno_save = errno; /* value caller might want printed */ + strcpy(buf, err_tstamp()); /* prepend a message with time stamp */ + vsprintf(buf + strlen(buf), fmt, ap); + if (errnoflag) + sprintf(buf + strlen(buf), ": %s\n", strerror(errno_save)); + else + strcat(buf, "\n"); + write(fd, buf, strlen(buf)); + errno = errno_save; +} + diff --git a/trunk/3rdparty/st-srs/examples/lookupdns.c b/trunk/3rdparty/st-srs/examples/lookupdns.c new file mode 100644 index 000000000..98f6ec5d8 --- /dev/null +++ b/trunk/3rdparty/st-srs/examples/lookupdns.c @@ -0,0 +1,103 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + +#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) +#define NETDB_INTERNAL h_NETDB_INTERNAL +#endif + +/* Resolution timeout (in microseconds) */ +#define TIMEOUT (2*1000000LL) + +/* External function defined in the res.c file */ +int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout); + + +void *do_resolve(void *host) +{ + struct in_addr addr; + + /* Use dns_getaddr() instead of gethostbyname(3) to get IP address */ + if (dns_getaddr(host, &addr, TIMEOUT) < 0) { + fprintf(stderr, "dns_getaddr: can't resolve %s: ", (char *)host); + if (h_errno == NETDB_INTERNAL) + perror(""); + else + herror(""); + } else + printf("%-40s %s\n", (char *)host, inet_ntoa(addr)); + + return NULL; +} + + +/* + * Asynchronous DNS host name resolution. This program creates one + * ST thread for each host name (specified as command line arguments). + * All threads do host name resolution concurrently. + */ +int main(int argc, char *argv[]) +{ + int i; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [] ...\n", argv[0]); + exit(1); + } + + if (st_init() < 0) { + perror("st_init"); + exit(1); + } + + for (i = 1; i < argc; i++) { + /* Create a separate thread for each host name */ + if (st_thread_create(do_resolve, argv[i], 0, 0) == NULL) { + perror("st_thread_create"); + exit(1); + } + } + + st_thread_exit(NULL); + + /* NOTREACHED */ + return 1; +} + diff --git a/trunk/3rdparty/st-srs/examples/proxy.c b/trunk/3rdparty/st-srs/examples/proxy.c new file mode 100644 index 000000000..2f4636d6b --- /dev/null +++ b/trunk/3rdparty/st-srs/examples/proxy.c @@ -0,0 +1,541 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + +#define IOBUFSIZE (16*1024) + +#define IOV_LEN 256 +#define IOV_COUNT (IOBUFSIZE / IOV_LEN) + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +static char *prog; /* Program name */ +static struct sockaddr_in rmt_addr; /* Remote address */ + +static unsigned long testing; +#define TESTING_VERBOSE 0x1 +#define TESTING_READV 0x2 +#define TESTING_READ_RESID 0x4 +#define TESTING_WRITEV 0x8 +#define TESTING_WRITE_RESID 0x10 + +static void read_address(const char *str, struct sockaddr_in *sin); +static void start_daemon(void); +static int cpu_count(void); +static void set_concurrency(int nproc); +static void *handle_request(void *arg); +static void print_sys_error(const char *msg); + + +/* + * This program acts as a generic gateway. It listens for connections + * to a local address ('-l' option). Upon accepting a client connection, + * it connects to the specified remote address ('-r' option) and then + * just pumps the data through without any modification. + */ +int main(int argc, char *argv[]) +{ + extern char *optarg; + int opt, sock, n; + int laddr, raddr, num_procs, alt_ev, one_process; + int serialize_accept = 0; + struct sockaddr_in lcl_addr, cli_addr; + st_netfd_t cli_nfd, srv_nfd; + + prog = argv[0]; + num_procs = laddr = raddr = alt_ev = one_process = 0; + + /* Parse arguments */ + while((opt = getopt(argc, argv, "l:r:p:Saht:X")) != EOF) { + switch (opt) { + case 'a': + alt_ev = 1; + break; + case 'l': + read_address(optarg, &lcl_addr); + laddr = 1; + break; + case 'r': + read_address(optarg, &rmt_addr); + if (rmt_addr.sin_addr.s_addr == INADDR_ANY) { + fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg); + exit(1); + } + raddr = 1; + break; + case 'p': + num_procs = atoi(optarg); + if (num_procs < 1) { + fprintf(stderr, "%s: invalid number of processes: %s\n", prog, optarg); + exit(1); + } + break; + case 'S': + /* + * Serialization decision is tricky on some platforms. For example, + * Solaris 2.6 and above has kernel sockets implementation, so supposedly + * there is no need for serialization. The ST library may be compiled + * on one OS version, but used on another, so the need for serialization + * should be determined at run time by the application. Since it's just + * an example, the serialization decision is left up to user. + * Only on platforms where the serialization is never needed on any OS + * version st_netfd_serialize_accept() is a no-op. + */ + serialize_accept = 1; + break; + case 't': + testing = strtoul(optarg, NULL, 0); + break; + case 'X': + one_process = 1; + break; + case 'h': + case '?': + fprintf(stderr, "Usage: %s [options] -l <[host]:port> -r \n", + prog); + fprintf(stderr, "options are:\n"); + fprintf(stderr, " -p number of parallel processes\n"); + fprintf(stderr, " -S serialize accepts\n"); + fprintf(stderr, " -a use alternate event system\n"); +#ifdef DEBUG + fprintf(stderr, " -t mask testing/debugging mode\n"); + fprintf(stderr, " -X one process, don't daemonize\n"); +#endif + exit(1); + } + } + if (!laddr) { + fprintf(stderr, "%s: local address required\n", prog); + exit(1); + } + if (!raddr) { + fprintf(stderr, "%s: remote address required\n", prog); + exit(1); + } + if (num_procs == 0) + num_procs = cpu_count(); + + fprintf(stderr, "%s: starting proxy daemon on %s:%d\n", prog, + inet_ntoa(lcl_addr.sin_addr), ntohs(lcl_addr.sin_port)); + + /* Start the daemon */ + if (one_process) + num_procs = 1; + else + start_daemon(); + + if (alt_ev) + st_set_eventsys(ST_EVENTSYS_ALT); + + /* Initialize the ST library */ + if (st_init() < 0) { + print_sys_error("st_init"); + exit(1); + } + + /* Create and bind listening socket */ + if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + print_sys_error("socket"); + exit(1); + } + n = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) { + print_sys_error("setsockopt"); + exit(1); + } + if (bind(sock, (struct sockaddr *)&lcl_addr, sizeof(lcl_addr)) < 0) { + print_sys_error("bind"); + exit(1); + } + listen(sock, 128); + if ((srv_nfd = st_netfd_open_socket(sock)) == NULL) { + print_sys_error("st_netfd_open"); + exit(1); + } + /* See the comment regarding serialization decision above */ + if (num_procs > 1 && serialize_accept && st_netfd_serialize_accept(srv_nfd) + < 0) { + print_sys_error("st_netfd_serialize_accept"); + exit(1); + } + + /* Start server processes */ + if (!one_process) + set_concurrency(num_procs); + + for ( ; ; ) { + n = sizeof(cli_addr); + cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&cli_addr, &n, + ST_UTIME_NO_TIMEOUT); + if (cli_nfd == NULL) { + print_sys_error("st_accept"); + exit(1); + } + if (st_thread_create(handle_request, cli_nfd, 0, 0) == NULL) { + print_sys_error("st_thread_create"); + exit(1); + } + } + + /* NOTREACHED */ + return 1; +} + + +static void read_address(const char *str, struct sockaddr_in *sin) +{ + char host[128], *p; + struct hostent *hp; + unsigned short port; + + strcpy(host, str); + if ((p = strchr(host, ':')) == NULL) { + fprintf(stderr, "%s: invalid address: %s\n", prog, host); + exit(1); + } + *p++ = '\0'; + port = (unsigned short) atoi(p); + if (port < 1) { + fprintf(stderr, "%s: invalid port: %s\n", prog, p); + exit(1); + } + + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + if (host[0] == '\0') { + sin->sin_addr.s_addr = INADDR_ANY; + return; + } + sin->sin_addr.s_addr = inet_addr(host); + if (sin->sin_addr.s_addr == INADDR_NONE) { + /* not dotted-decimal */ + if ((hp = gethostbyname(host)) == NULL) { + fprintf(stderr, "%s: can't resolve address: %s\n", prog, host); + exit(1); + } + memcpy(&sin->sin_addr, hp->h_addr, hp->h_length); + } +} + +#ifdef DEBUG +static void show_iov(const struct iovec *iov, int niov) +{ + int i; + size_t total; + + printf("iov %p has %d entries:\n", iov, niov); + total = 0; + for (i = 0; i < niov; i++) { + printf("iov[%3d] iov_base=%p iov_len=0x%lx(%lu)\n", + i, iov[i].iov_base, (unsigned long) iov[i].iov_len, + (unsigned long) iov[i].iov_len); + total += iov[i].iov_len; + } + printf("total 0x%lx(%ld)\n", (unsigned long) total, (unsigned long) total); +} + +/* + * This version is tricked out to test all the + * st_(read|write)v?(_resid)? variants. Use the non-DEBUG version for + * anything serious. st_(read|write) are all this function really + * needs. + */ +static int pass(st_netfd_t in, st_netfd_t out) +{ + char buf[IOBUFSIZE]; + struct iovec iov[IOV_COUNT]; + int ioviter, nw, nr; + + if (testing & TESTING_READV) { + for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) { + iov[ioviter].iov_base = &buf[ioviter * IOV_LEN]; + iov[ioviter].iov_len = IOV_LEN; + } + if (testing & TESTING_VERBOSE) { + printf("readv(%p)...\n", in); + show_iov(iov, IOV_COUNT); + } + if (testing & TESTING_READ_RESID) { + struct iovec *riov = iov; + int riov_cnt = IOV_COUNT; + if (st_readv_resid(in, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { + if (testing & TESTING_VERBOSE) { + printf("resid\n"); + show_iov(riov, riov_cnt); + printf("full\n"); + show_iov(iov, IOV_COUNT); + } + nr = 0; + for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) + nr += iov[ioviter].iov_len; + nr = IOBUFSIZE - nr; + } else + nr = -1; + } else + nr = (int) st_readv(in, iov, IOV_COUNT, ST_UTIME_NO_TIMEOUT); + } else { + if (testing & TESTING_READ_RESID) { + size_t resid = IOBUFSIZE; + if (st_read_resid(in, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) + nr = IOBUFSIZE - resid; + else + nr = -1; + } else + nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); + } + if (testing & TESTING_VERBOSE) + printf("got 0x%x(%d) E=%d\n", nr, nr, errno); + + if (nr <= 0) + return 0; + + if (testing & TESTING_WRITEV) { + for (nw = 0, ioviter = 0; nw < nr; + nw += iov[ioviter].iov_len, ioviter++) { + iov[ioviter].iov_base = &buf[nw]; + iov[ioviter].iov_len = nr - nw; + if (iov[ioviter].iov_len > IOV_LEN) + iov[ioviter].iov_len = IOV_LEN; + } + if (testing & TESTING_VERBOSE) { + printf("writev(%p)...\n", out); + show_iov(iov, ioviter); + } + if (testing & TESTING_WRITE_RESID) { + struct iovec *riov = iov; + int riov_cnt = ioviter; + if (st_writev_resid(out, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { + if (testing & TESTING_VERBOSE) { + printf("resid\n"); + show_iov(riov, riov_cnt); + printf("full\n"); + show_iov(iov, ioviter); + } + nw = 0; + while (--ioviter >= 0) + nw += iov[ioviter].iov_len; + nw = nr - nw; + } else + nw = -1; + } else + nw = st_writev(out, iov, ioviter, ST_UTIME_NO_TIMEOUT); + } else { + if (testing & TESTING_WRITE_RESID) { + size_t resid = nr; + if (st_write_resid(out, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) + nw = nr - resid; + else + nw = -1; + } else + nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); + } + if (testing & TESTING_VERBOSE) + printf("put 0x%x(%d) E=%d\n", nw, nw, errno); + + if (nw != nr) + return 0; + + return 1; +} +#else /* DEBUG */ +/* + * This version is the simple one suitable for serious use. + */ +static int pass(st_netfd_t in, st_netfd_t out) +{ + char buf[IOBUFSIZE]; + int nw, nr; + + nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); + if (nr <= 0) + return 0; + + nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); + if (nw != nr) + return 0; + + return 1; +} +#endif + +static void *handle_request(void *arg) +{ + struct pollfd pds[2]; + st_netfd_t cli_nfd, rmt_nfd; + int sock; + + cli_nfd = (st_netfd_t) arg; + pds[0].fd = st_netfd_fileno(cli_nfd); + pds[0].events = POLLIN; + + /* Connect to remote host */ + if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + print_sys_error("socket"); + goto done; + } + if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) { + print_sys_error("st_netfd_open_socket"); + close(sock); + goto done; + } + if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr, + sizeof(rmt_addr), ST_UTIME_NO_TIMEOUT) < 0) { + print_sys_error("st_connect"); + st_netfd_close(rmt_nfd); + goto done; + } + pds[1].fd = sock; + pds[1].events = POLLIN; + + /* + * Now just pump the data through. + * XXX This should use one thread for each direction for true full-duplex. + */ + for ( ; ; ) { + pds[0].revents = 0; + pds[1].revents = 0; + + if (st_poll(pds, 2, ST_UTIME_NO_TIMEOUT) <= 0) { + print_sys_error("st_poll"); + break; + } + + if (pds[0].revents & POLLIN) { + if (!pass(cli_nfd, rmt_nfd)) + break; + } + + if (pds[1].revents & POLLIN) { + if (!pass(rmt_nfd, cli_nfd)) + break; + } + } + st_netfd_close(rmt_nfd); + +done: + + st_netfd_close(cli_nfd); + + return NULL; +} + +static void start_daemon(void) +{ + pid_t pid; + + /* Start forking */ + if ((pid = fork()) < 0) { + print_sys_error("fork"); + exit(1); + } + if (pid > 0) + exit(0); /* parent */ + + /* First child process */ + setsid(); /* become session leader */ + + if ((pid = fork()) < 0) { + print_sys_error("fork"); + exit(1); + } + if (pid > 0) /* first child */ + exit(0); + + chdir("/"); + umask(022); +} + +/* + * Create separate processes ("virtual processors"). Since it's just an + * example, there is no watchdog - the parent just exits leaving children + * on their own. + */ +static void set_concurrency(int nproc) +{ + pid_t pid; + int i; + + if (nproc < 1) + nproc = 1; + + for (i = 0; i < nproc; i++) { + if ((pid = fork()) < 0) { + print_sys_error("fork"); + exit(1); + } + /* Child returns */ + if (pid == 0) + return; + } + + /* Parent just exits */ + exit(0); +} + +static int cpu_count(void) +{ + int n; + +#if defined (_SC_NPROCESSORS_ONLN) + n = (int) sysconf(_SC_NPROCESSORS_ONLN); +#elif defined (_SC_NPROC_ONLN) + n = (int) sysconf(_SC_NPROC_ONLN); +#elif defined (HPUX) +#include + n = mpctl(MPC_GETNUMSPUS, 0, 0); +#else + n = -1; + errno = ENOSYS; +#endif + + return n; +} + +static void print_sys_error(const char *msg) +{ + fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno)); +} + diff --git a/trunk/3rdparty/st-srs/examples/res.c b/trunk/3rdparty/st-srs/examples/res.c new file mode 100644 index 000000000..14ecd8c92 --- /dev/null +++ b/trunk/3rdparty/st-srs/examples/res.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 1985, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined (DARWIN) +#define BIND_8_COMPAT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + +#define MAXPACKET 1024 + +#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) +#define NETDB_INTERNAL h_NETDB_INTERNAL +#endif + +/* New in Solaris 7 */ +#if !defined(_getshort) && defined(ns_get16) +#define _getshort(cp) ns_get16(cp) +#endif + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf_t; + + +static int parse_answer(querybuf_t *ans, int len, struct in_addr *addr) +{ + char buf[MAXPACKET]; + HEADER *ahp; + u_char *cp, *eoa; + int type, n; + + ahp = &ans->hdr; + eoa = ans->buf + len; + cp = ans->buf + sizeof(HEADER); + + while (ahp->qdcount > 0) { + ahp->qdcount--; + cp += dn_skipname(cp, eoa) + QFIXEDSZ; + } + while (ahp->ancount > 0 && cp < eoa) { + ahp->ancount--; + if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) + break; + cp += n; + type = _getshort(cp); + cp += 8; + n = _getshort(cp); + cp += 2; + if (type == T_CNAME) { + cp += n; + continue; + } + memcpy(addr, cp, n); + return 0; + } + + h_errno = TRY_AGAIN; + return -1; +} + + +static int query_domain(st_netfd_t nfd, const char *name, struct in_addr *addr, + st_utime_t timeout) +{ + querybuf_t qbuf; + u_char *buf = qbuf.buf; + HEADER *hp = &qbuf.hdr; + int blen = sizeof(qbuf); + int i, len, id; + + for (i = 0; i < _res.nscount; i++) { + len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); + if (len <= 0) { + h_errno = NO_RECOVERY; + return -1; + } + id = hp->id; + + if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), + sizeof(struct sockaddr), timeout) != len) { + h_errno = NETDB_INTERNAL; + /* EINTR means interrupt by other thread, NOT by a caught signal */ + if (errno == EINTR) + return -1; + continue; + } + + /* Wait for reply */ + do { + len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); + if (len <= 0) + break; + } while (id != hp->id); + + if (len < HFIXEDSZ) { + h_errno = NETDB_INTERNAL; + if (len >= 0) + errno = EMSGSIZE; + else if (errno == EINTR) /* see the comment above */ + return -1; + continue; + } + + hp->ancount = ntohs(hp->ancount); + hp->qdcount = ntohs(hp->qdcount); + if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { + switch (hp->rcode) { + case NXDOMAIN: + h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + } + continue; + } + + if (parse_answer(&qbuf, len, addr) == 0) + return 0; + } + + return -1; +} + + +#define CLOSE_AND_RETURN(ret) \ + { \ + n = errno; \ + st_netfd_close(nfd); \ + errno = n; \ + return (ret); \ + } + + +int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout) +{ + char name[MAXDNAME], **domain; + const char *cp; + int s, n, maxlen, dots; + int trailing_dot, tried_as_is; + st_netfd_t nfd; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return -1; + } + if (_res.options & RES_USEVC) { + h_errno = NETDB_INTERNAL; + errno = ENOSYS; + return -1; + } + if (!host || *host == '\0') { + h_errno = HOST_NOT_FOUND; + return -1; + } + + /* Create UDP socket */ + if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + h_errno = NETDB_INTERNAL; + return -1; + } + if ((nfd = st_netfd_open_socket(s)) == NULL) { + h_errno = NETDB_INTERNAL; + n = errno; + close(s); + errno = n; + return -1; + } + + maxlen = sizeof(name) - 1; + n = 0; + dots = 0; + trailing_dot = 0; + tried_as_is = 0; + + for (cp = host; *cp && n < maxlen; cp++) { + dots += (*cp == '.'); + name[n++] = *cp; + } + if (name[n - 1] == '.') + trailing_dot = 1; + + /* + * If there are dots in the name already, let's just give it a try + * 'as is'. The threshold can be set with the "ndots" option. + */ + if (dots >= _res.ndots) { + if (query_domain(nfd, host, addr, timeout) == 0) + CLOSE_AND_RETURN(0); + if (h_errno == NETDB_INTERNAL && errno == EINTR) + CLOSE_AND_RETURN(-1); + tried_as_is = 1; + } + + /* + * We do at least one level of search if + * - there is no dot and RES_DEFNAME is set, or + * - there is at least one dot, there is no trailing dot, + * and RES_DNSRCH is set. + */ + if ((!dots && (_res.options & RES_DEFNAMES)) || + (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { + name[n++] = '.'; + for (domain = _res.dnsrch; *domain; domain++) { + strncpy(name + n, *domain, maxlen - n); + if (query_domain(nfd, name, addr, timeout) == 0) + CLOSE_AND_RETURN(0); + if (h_errno == NETDB_INTERNAL && errno == EINTR) + CLOSE_AND_RETURN(-1); + if (!(_res.options & RES_DNSRCH)) + break; + } + } + + /* + * If we have not already tried the name "as is", do that now. + * note that we do this regardless of how many dots were in the + * name or whether it ends with a dot. + */ + if (!tried_as_is) { + if (query_domain(nfd, host, addr, timeout) == 0) + CLOSE_AND_RETURN(0); + } + + CLOSE_AND_RETURN(-1); +} + diff --git a/trunk/3rdparty/st-srs/examples/server.c b/trunk/3rdparty/st-srs/examples/server.c new file mode 100644 index 000000000..5d5aa6d72 --- /dev/null +++ b/trunk/3rdparty/st-srs/examples/server.c @@ -0,0 +1,1025 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + + +/****************************************************************** + * Server configuration parameters + */ + +/* Log files */ +#define PID_FILE "pid" +#define ERRORS_FILE "errors" +#define ACCESS_FILE "access" + +/* Default server port */ +#define SERV_PORT_DEFAULT 8000 + +/* Socket listen queue size */ +#define LISTENQ_SIZE_DEFAULT 256 + +/* Max number of listening sockets ("hardware virtual servers") */ +#define MAX_BIND_ADDRS 16 + +/* Max number of "spare" threads per process per socket */ +#define MAX_WAIT_THREADS_DEFAULT 8 + +/* Number of file descriptors needed to handle one client session */ +#define FD_PER_THREAD 2 + +/* Access log buffer flushing interval (in seconds) */ +#define ACCLOG_FLUSH_INTERVAL 30 + +/* Request read timeout (in seconds) */ +#define REQUEST_TIMEOUT 30 + + +/****************************************************************** + * Global data + */ + +struct socket_info { + st_netfd_t nfd; /* Listening socket */ + char *addr; /* Bind address */ + unsigned int port; /* Port */ + int wait_threads; /* Number of threads waiting to accept */ + int busy_threads; /* Number of threads processing request */ + int rqst_count; /* Total number of processed requests */ +} srv_socket[MAX_BIND_ADDRS]; /* Array of listening sockets */ + +static int sk_count = 0; /* Number of listening sockets */ + +static int vp_count = 0; /* Number of server processes (VPs) */ +static pid_t *vp_pids; /* Array of VP pids */ + +static int my_index = -1; /* Current process index */ +static pid_t my_pid = -1; /* Current process pid */ + +static st_netfd_t sig_pipe[2]; /* Signal pipe */ + +/* + * Configuration flags/parameters + */ +static int interactive_mode = 0; +static int serialize_accept = 0; +static int log_access = 0; +static char *logdir = NULL; +static char *username = NULL; +static int listenq_size = LISTENQ_SIZE_DEFAULT; +static int errfd = STDERR_FILENO; + +/* + * Thread throttling parameters (all numbers are per listening socket). + * Zero values mean use default. + */ +static int max_threads = 0; /* Max number of threads */ +static int max_wait_threads = 0; /* Max number of "spare" threads */ +static int min_wait_threads = 2; /* Min number of "spare" threads */ + + +/****************************************************************** + * Useful macros + */ + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +#define SEC2USEC(s) ((s)*1000000LL) + +#define WAIT_THREADS(i) (srv_socket[i].wait_threads) +#define BUSY_THREADS(i) (srv_socket[i].busy_threads) +#define TOTAL_THREADS(i) (WAIT_THREADS(i) + BUSY_THREADS(i)) +#define RQST_COUNT(i) (srv_socket[i].rqst_count) + + +/****************************************************************** + * Forward declarations + */ + +static void usage(const char *progname); +static void parse_arguments(int argc, char *argv[]); +static void start_daemon(void); +static void set_thread_throttling(void); +static void create_listeners(void); +static void change_user(void); +static void open_log_files(void); +static void start_processes(void); +static void wdog_sighandler(int signo); +static void child_sighandler(int signo); +static void install_sighandlers(void); +static void start_threads(void); +static void *process_signals(void *arg); +static void *flush_acclog_buffer(void *arg); +static void *handle_connections(void *arg); +static void dump_server_info(void); + +static void Signal(int sig, void (*handler)(int)); +static int cpu_count(void); + +extern void handle_session(long srv_socket_index, st_netfd_t cli_nfd); +extern void load_configs(void); +extern void logbuf_open(void); +extern void logbuf_flush(void); +extern void logbuf_close(void); + +/* Error reporting functions defined in the error.c file */ +extern void err_sys_report(int fd, const char *fmt, ...); +extern void err_sys_quit(int fd, const char *fmt, ...); +extern void err_sys_dump(int fd, const char *fmt, ...); +extern void err_report(int fd, const char *fmt, ...); +extern void err_quit(int fd, const char *fmt, ...); + + +/* + * General server example: accept a client connection and do something. + * This program just outputs a short HTML page, but can be easily adapted + * to do other things. + * + * This server creates a constant number of processes ("virtual processors" + * or VPs) and replaces them when they die. Each virtual processor manages + * its own independent set of state threads (STs), the number of which varies + * with load against the server. Each state thread listens to exactly one + * listening socket. The initial process becomes the watchdog, waiting for + * children (VPs) to die or for a signal requesting termination or restart. + * Upon receiving a restart signal (SIGHUP), all VPs close and then reopen + * log files and reload configuration. All currently active connections remain + * active. It is assumed that new configuration affects only request + * processing and not the general server parameters such as number of VPs, + * thread limits, bind addresses, etc. Those are specified as command line + * arguments, so the server has to be stopped and then started again in order + * to change them. + * + * Each state thread loops processing connections from a single listening + * socket. Only one ST runs on a VP at a time, and VPs do not share memory, + * so no mutual exclusion locking is necessary on any data, and the entire + * server is free to use all the static variables and non-reentrant library + * functions it wants, greatly simplifying programming and debugging and + * increasing performance (for example, it is safe to ++ and -- all global + * counters or call inet_ntoa(3) without any mutexes). The current thread on + * each VP maintains equilibrium on that VP, starting a new thread or + * terminating itself if the number of spare threads exceeds the lower or + * upper limit. + * + * All I/O operations on sockets must use the State Thread library's I/O + * functions because only those functions prevent blocking of the entire VP + * process and perform state thread scheduling. + */ +int main(int argc, char *argv[]) +{ + /* Parse command-line options */ + parse_arguments(argc, argv); + + /* Allocate array of server pids */ + if ((vp_pids = calloc(vp_count, sizeof(pid_t))) == NULL) + err_sys_quit(errfd, "ERROR: calloc failed"); + + /* Start the daemon */ + if (!interactive_mode) + start_daemon(); + + /* Initialize the ST library */ + if (st_init() < 0) + err_sys_quit(errfd, "ERROR: initialization failed: st_init"); + + /* Set thread throttling parameters */ + set_thread_throttling(); + + /* Create listening sockets */ + create_listeners(); + + /* Change the user */ + if (username) + change_user(); + + /* Open log files */ + open_log_files(); + + /* Start server processes (VPs) */ + start_processes(); + + /* Turn time caching on */ + st_timecache_set(1); + + /* Install signal handlers */ + install_sighandlers(); + + /* Load configuration from config files */ + load_configs(); + + /* Start all threads */ + start_threads(); + + /* Become a signal processing thread */ + process_signals(NULL); + + /* NOTREACHED */ + return 1; +} + + +/******************************************************************/ + +static void usage(const char *progname) +{ + fprintf(stderr, "Usage: %s -l []\n\n" + "Possible options:\n\n" + "\t-b : Bind to specified address. Multiple" + " addresses\n" + "\t are permitted.\n" + "\t-p Create specified number of processes.\n" + "\t-t : Specify thread limits per listening" + " socket\n" + "\t across all processes.\n" + "\t-u Change server's user id to specified" + " value.\n" + "\t-q Set max length of pending connections" + " queue.\n" + "\t-a Enable access logging.\n" + "\t-i Run in interactive mode.\n" + "\t-S Serialize all accept() calls.\n" + "\t-h Print this message.\n", + progname); + exit(1); +} + + +/******************************************************************/ + +static void parse_arguments(int argc, char *argv[]) +{ + extern char *optarg; + int opt; + char *c; + + while ((opt = getopt(argc, argv, "b:p:l:t:u:q:aiSh")) != EOF) { + switch (opt) { + case 'b': + if (sk_count >= MAX_BIND_ADDRS) + err_quit(errfd, "ERROR: max number of bind addresses (%d) exceeded", + MAX_BIND_ADDRS); + if ((c = strdup(optarg)) == NULL) + err_sys_quit(errfd, "ERROR: strdup"); + srv_socket[sk_count++].addr = c; + break; + case 'p': + vp_count = atoi(optarg); + if (vp_count < 1) + err_quit(errfd, "ERROR: invalid number of processes: %s", optarg); + break; + case 'l': + logdir = optarg; + break; + case 't': + max_wait_threads = (int) strtol(optarg, &c, 10); + if (*c++ == ':') + max_threads = atoi(c); + if (max_wait_threads < 0 || max_threads < 0) + err_quit(errfd, "ERROR: invalid number of threads: %s", optarg); + break; + case 'u': + username = optarg; + break; + case 'q': + listenq_size = atoi(optarg); + if (listenq_size < 1) + err_quit(errfd, "ERROR: invalid listen queue size: %s", optarg); + break; + case 'a': + log_access = 1; + break; + case 'i': + interactive_mode = 1; + break; + case 'S': + /* + * Serialization decision is tricky on some platforms. For example, + * Solaris 2.6 and above has kernel sockets implementation, so supposedly + * there is no need for serialization. The ST library may be compiled + * on one OS version, but used on another, so the need for serialization + * should be determined at run time by the application. Since it's just + * an example, the serialization decision is left up to user. + * Only on platforms where the serialization is never needed on any OS + * version st_netfd_serialize_accept() is a no-op. + */ + serialize_accept = 1; + break; + case 'h': + case '?': + usage(argv[0]); + } + } + + if (logdir == NULL && !interactive_mode) { + err_report(errfd, "ERROR: logging directory is required\n"); + usage(argv[0]); + } + + if (getuid() == 0 && username == NULL) + err_report(errfd, "WARNING: running as super-user!"); + + if (vp_count == 0 && (vp_count = cpu_count()) < 1) + vp_count = 1; + + if (sk_count == 0) { + sk_count = 1; + srv_socket[0].addr = "0.0.0.0"; + } +} + + +/******************************************************************/ + +static void start_daemon(void) +{ + pid_t pid; + + /* Start forking */ + if ((pid = fork()) < 0) + err_sys_quit(errfd, "ERROR: fork"); + if (pid > 0) + exit(0); /* parent */ + + /* First child process */ + setsid(); /* become session leader */ + + if ((pid = fork()) < 0) + err_sys_quit(errfd, "ERROR: fork"); + if (pid > 0) /* first child */ + exit(0); + + umask(022); + + if (chdir(logdir) < 0) + err_sys_quit(errfd, "ERROR: can't change directory to %s: chdir", logdir); +} + + +/****************************************************************** + * For simplicity, the minimal size of thread pool is considered + * as a maximum number of spare threads (max_wait_threads) that + * will be created upon server startup. The pool size can grow up + * to the max_threads value. Note that this is a per listening + * socket limit. It is also possible to limit the total number of + * threads for all sockets rather than impose a per socket limit. + */ + +static void set_thread_throttling(void) +{ + /* + * Calculate total values across all processes. + * All numbers are per listening socket. + */ + if (max_wait_threads == 0) + max_wait_threads = MAX_WAIT_THREADS_DEFAULT * vp_count; + /* Assuming that each client session needs FD_PER_THREAD file descriptors */ + if (max_threads == 0) + max_threads = (st_getfdlimit() * vp_count) / FD_PER_THREAD / sk_count; + if (max_wait_threads > max_threads) + max_wait_threads = max_threads; + + /* + * Now calculate per-process values. + */ + if (max_wait_threads % vp_count) + max_wait_threads = max_wait_threads / vp_count + 1; + else + max_wait_threads = max_wait_threads / vp_count; + if (max_threads % vp_count) + max_threads = max_threads / vp_count + 1; + else + max_threads = max_threads / vp_count; + + if (min_wait_threads > max_wait_threads) + min_wait_threads = max_wait_threads; +} + + +/******************************************************************/ + +static void create_listeners(void) +{ + int i, n, sock; + char *c; + struct sockaddr_in serv_addr; + struct hostent *hp; + unsigned short port; + + for (i = 0; i < sk_count; i++) { + port = 0; + if ((c = strchr(srv_socket[i].addr, ':')) != NULL) { + *c++ = '\0'; + port = (unsigned short) atoi(c); + } + if (srv_socket[i].addr[0] == '\0') + srv_socket[i].addr = "0.0.0.0"; + if (port == 0) + port = SERV_PORT_DEFAULT; + + /* Create server socket */ + if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) + err_sys_quit(errfd, "ERROR: can't create socket: socket"); + n = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) + err_sys_quit(errfd, "ERROR: can't set SO_REUSEADDR: setsockopt"); + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(port); + serv_addr.sin_addr.s_addr = inet_addr(srv_socket[i].addr); + if (serv_addr.sin_addr.s_addr == INADDR_NONE) { + /* not dotted-decimal */ + if ((hp = gethostbyname(srv_socket[i].addr)) == NULL) + err_quit(errfd, "ERROR: can't resolve address: %s", + srv_socket[i].addr); + memcpy(&serv_addr.sin_addr, hp->h_addr, hp->h_length); + } + srv_socket[i].port = port; + + /* Do bind and listen */ + if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) + err_sys_quit(errfd, "ERROR: can't bind to address %s, port %hu", + srv_socket[i].addr, port); + if (listen(sock, listenq_size) < 0) + err_sys_quit(errfd, "ERROR: listen"); + + /* Create file descriptor object from OS socket */ + if ((srv_socket[i].nfd = st_netfd_open_socket(sock)) == NULL) + err_sys_quit(errfd, "ERROR: st_netfd_open_socket"); + /* + * On some platforms (e.g. IRIX, Linux) accept() serialization is never + * needed for any OS version. In that case st_netfd_serialize_accept() + * is just a no-op. Also see the comment above. + */ + if (serialize_accept && st_netfd_serialize_accept(srv_socket[i].nfd) < 0) + err_sys_quit(errfd, "ERROR: st_netfd_serialize_accept"); + } +} + + +/******************************************************************/ + +static void change_user(void) +{ + struct passwd *pw; + + if ((pw = getpwnam(username)) == NULL) + err_quit(errfd, "ERROR: can't find user '%s': getpwnam failed", username); + + if (setgid(pw->pw_gid) < 0) + err_sys_quit(errfd, "ERROR: can't change group id: setgid"); + if (setuid(pw->pw_uid) < 0) + err_sys_quit(errfd, "ERROR: can't change user id: setuid"); + + err_report(errfd, "INFO: changed process user id to '%s'", username); +} + + +/******************************************************************/ + +static void open_log_files(void) +{ + int fd; + char str[32]; + + if (interactive_mode) + return; + + /* Open access log */ + if (log_access) + logbuf_open(); + + /* Open and write pid to pid file */ + if ((fd = open(PID_FILE, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) + err_sys_quit(errfd, "ERROR: can't open pid file: open"); + sprintf(str, "%d\n", (int)getpid()); + if (write(fd, str, strlen(str)) != strlen(str)) + err_sys_quit(errfd, "ERROR: can't write to pid file: write"); + close(fd); + + /* Open error log file */ + if ((fd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) + err_sys_quit(errfd, "ERROR: can't open error log file: open"); + errfd = fd; + + err_report(errfd, "INFO: starting the server..."); +} + + +/******************************************************************/ + +static void start_processes(void) +{ + int i, status; + pid_t pid; + sigset_t mask, omask; + + if (interactive_mode) { + my_index = 0; + my_pid = getpid(); + return; + } + + for (i = 0; i < vp_count; i++) { + if ((pid = fork()) < 0) { + err_sys_report(errfd, "ERROR: can't create process: fork"); + if (i == 0) + exit(1); + err_report(errfd, "WARN: started only %d processes out of %d", i, + vp_count); + vp_count = i; + break; + } + if (pid == 0) { + my_index = i; + my_pid = getpid(); + /* Child returns to continue in main() */ + return; + } + vp_pids[i] = pid; + } + + /* + * Parent process becomes a "watchdog" and never returns to main(). + */ + + /* Install signal handlers */ + Signal(SIGTERM, wdog_sighandler); /* terminate */ + Signal(SIGHUP, wdog_sighandler); /* restart */ + Signal(SIGUSR1, wdog_sighandler); /* dump info */ + + /* Now go to sleep waiting for a child termination or a signal */ + for ( ; ; ) { + if ((pid = wait(&status)) < 0) { + if (errno == EINTR) + continue; + err_sys_quit(errfd, "ERROR: watchdog: wait"); + } + /* Find index of the exited child */ + for (i = 0; i < vp_count; i++) { + if (vp_pids[i] == pid) + break; + } + + /* Block signals while printing and forking */ + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGHUP); + sigaddset(&mask, SIGUSR1); + sigprocmask(SIG_BLOCK, &mask, &omask); + + if (WIFEXITED(status)) + err_report(errfd, "WARN: watchdog: process %d (pid %d) exited" + " with status %d", i, pid, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated" + " by signal %d", i, pid, WTERMSIG(status)); + else if (WIFSTOPPED(status)) + err_report(errfd, "WARN: watchdog: process %d (pid %d) stopped" + " by signal %d", i, pid, WSTOPSIG(status)); + else + err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated:" + " unknown termination reason", i, pid); + + /* Fork another VP */ + if ((pid = fork()) < 0) { + err_sys_report(errfd, "ERROR: watchdog: can't create process: fork"); + } else if (pid == 0) { + my_index = i; + my_pid = getpid(); + /* Child returns to continue in main() */ + return; + } + vp_pids[i] = pid; + + /* Restore the signal mask */ + sigprocmask(SIG_SETMASK, &omask, NULL); + } +} + + +/******************************************************************/ + +static void wdog_sighandler(int signo) +{ + int i, err; + + /* Save errno */ + err = errno; + /* Forward the signal to all children */ + for (i = 0; i < vp_count; i++) { + if (vp_pids[i] > 0) + kill(vp_pids[i], signo); + } + /* + * It is safe to do pretty much everything here because process is + * sleeping in wait() which is async-safe. + */ + switch (signo) { + case SIGHUP: + err_report(errfd, "INFO: watchdog: caught SIGHUP"); + /* Reopen log files - needed for log rotation */ + if (log_access) { + logbuf_close(); + logbuf_open(); + } + close(errfd); + if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) + err_sys_quit(STDERR_FILENO, "ERROR: watchdog: open"); + break; + case SIGTERM: + /* Non-graceful termination */ + err_report(errfd, "INFO: watchdog: caught SIGTERM, terminating"); + unlink(PID_FILE); + exit(0); + case SIGUSR1: + err_report(errfd, "INFO: watchdog: caught SIGUSR1"); + break; + default: + err_report(errfd, "INFO: watchdog: caught signal %d", signo); + } + /* Restore errno */ + errno = err; +} + + +/******************************************************************/ + +static void install_sighandlers(void) +{ + sigset_t mask; + int p[2]; + + /* Create signal pipe */ + if (pipe(p) < 0) + err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" + " signal pipe: pipe", my_index, my_pid); + if ((sig_pipe[0] = st_netfd_open(p[0])) == NULL || + (sig_pipe[1] = st_netfd_open(p[1])) == NULL) + err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" + " signal pipe: st_netfd_open", my_index, my_pid); + + /* Install signal handlers */ + Signal(SIGTERM, child_sighandler); /* terminate */ + Signal(SIGHUP, child_sighandler); /* restart */ + Signal(SIGUSR1, child_sighandler); /* dump info */ + + /* Unblock signals */ + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGHUP); + sigaddset(&mask, SIGUSR1); + sigprocmask(SIG_UNBLOCK, &mask, NULL); +} + + +/******************************************************************/ + +static void child_sighandler(int signo) +{ + int err, fd; + + err = errno; + fd = st_netfd_fileno(sig_pipe[1]); + + /* write() is async-safe */ + if (write(fd, &signo, sizeof(int)) != sizeof(int)) + err_sys_quit(errfd, "ERROR: process %d (pid %d): child's signal" + " handler: write", my_index, my_pid); + errno = err; +} + + +/****************************************************************** + * The "main" function of the signal processing thread. + */ + +/* ARGSUSED */ +static void *process_signals(void *arg) +{ + int signo; + + for ( ; ; ) { + /* Read the next signal from the signal pipe */ + if (st_read(sig_pipe[0], &signo, sizeof(int), + ST_UTIME_NO_TIMEOUT) != sizeof(int)) + err_sys_quit(errfd, "ERROR: process %d (pid %d): signal processor:" + " st_read", my_index, my_pid); + + switch (signo) { + case SIGHUP: + err_report(errfd, "INFO: process %d (pid %d): caught SIGHUP," + " reloading configuration", my_index, my_pid); + if (interactive_mode) { + load_configs(); + break; + } + /* Reopen log files - needed for log rotation */ + if (log_access) { + logbuf_flush(); + logbuf_close(); + logbuf_open(); + } + close(errfd); + if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) + err_sys_quit(STDERR_FILENO, "ERROR: process %d (pid %d): signal" + " processor: open", my_index, my_pid); + /* Reload configuration */ + load_configs(); + break; + case SIGTERM: + /* + * Terminate ungracefully since it is generally not known how long + * it will take to gracefully complete all client sessions. + */ + err_report(errfd, "INFO: process %d (pid %d): caught SIGTERM," + " terminating", my_index, my_pid); + if (log_access) + logbuf_flush(); + exit(0); + case SIGUSR1: + err_report(errfd, "INFO: process %d (pid %d): caught SIGUSR1", + my_index, my_pid); + /* Print server info to stderr */ + dump_server_info(); + break; + default: + err_report(errfd, "INFO: process %d (pid %d): caught signal %d", + my_index, my_pid, signo); + } + } + + /* NOTREACHED */ + return NULL; +} + + +/****************************************************************** + * The "main" function of the access log flushing thread. + */ + +/* ARGSUSED */ +static void *flush_acclog_buffer(void *arg) +{ + for ( ; ; ) { + st_sleep(ACCLOG_FLUSH_INTERVAL); + logbuf_flush(); + } + + /* NOTREACHED */ + return NULL; +} + + +/******************************************************************/ + +static void start_threads(void) +{ + long i, n; + + /* Create access log flushing thread */ + if (log_access && st_thread_create(flush_acclog_buffer, NULL, 0, 0) == NULL) + err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" + " log flushing thread", my_index, my_pid); + + /* Create connections handling threads */ + for (i = 0; i < sk_count; i++) { + err_report(errfd, "INFO: process %d (pid %d): starting %d threads" + " on %s:%u", my_index, my_pid, max_wait_threads, + srv_socket[i].addr, srv_socket[i].port); + WAIT_THREADS(i) = 0; + BUSY_THREADS(i) = 0; + RQST_COUNT(i) = 0; + for (n = 0; n < max_wait_threads; n++) { + if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) + WAIT_THREADS(i)++; + else + err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" + " thread", my_index, my_pid); + } + if (WAIT_THREADS(i) == 0) + exit(1); + } +} + + +/******************************************************************/ + +static void *handle_connections(void *arg) +{ + st_netfd_t srv_nfd, cli_nfd; + struct sockaddr_in from; + int fromlen; + long i = (long) arg; + + srv_nfd = srv_socket[i].nfd; + fromlen = sizeof(from); + + while (WAIT_THREADS(i) <= max_wait_threads) { + cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&from, &fromlen, + ST_UTIME_NO_TIMEOUT); + if (cli_nfd == NULL) { + err_sys_report(errfd, "ERROR: can't accept connection: st_accept"); + continue; + } + /* Save peer address, so we can retrieve it later */ + st_netfd_setspecific(cli_nfd, &from.sin_addr, NULL); + + WAIT_THREADS(i)--; + BUSY_THREADS(i)++; + if (WAIT_THREADS(i) < min_wait_threads && TOTAL_THREADS(i) < max_threads) { + /* Create another spare thread */ + if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) + WAIT_THREADS(i)++; + else + err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" + " thread", my_index, my_pid); + } + + handle_session(i, cli_nfd); + + st_netfd_close(cli_nfd); + WAIT_THREADS(i)++; + BUSY_THREADS(i)--; + } + + WAIT_THREADS(i)--; + return NULL; +} + + +/******************************************************************/ + +static void dump_server_info(void) +{ + char *buf; + int i, len; + + if ((buf = malloc(sk_count * 512)) == NULL) { + err_sys_report(errfd, "ERROR: malloc failed"); + return; + } + + len = sprintf(buf, "\n\nProcess #%d (pid %d):\n", my_index, (int)my_pid); + for (i = 0; i < sk_count; i++) { + len += sprintf(buf + len, "\nListening Socket #%d:\n" + "-------------------------\n" + "Address %s:%u\n" + "Thread limits (min/max) %d/%d\n" + "Waiting threads %d\n" + "Busy threads %d\n" + "Requests served %d\n", + i, srv_socket[i].addr, srv_socket[i].port, + max_wait_threads, max_threads, + WAIT_THREADS(i), BUSY_THREADS(i), RQST_COUNT(i)); + } + + write(STDERR_FILENO, buf, len); + free(buf); +} + + +/****************************************************************** + * Stubs + */ + +/* + * Session handling function stub. Just dumps small HTML page. + */ +void handle_session(long srv_socket_index, st_netfd_t cli_nfd) +{ + static char resp[] = "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n" + "Connection: close\r\n\r\n

It worked!

\n"; + char buf[512]; + int n = sizeof(resp) - 1; + struct in_addr *from = st_netfd_getspecific(cli_nfd); + + if (st_read(cli_nfd, buf, sizeof(buf), SEC2USEC(REQUEST_TIMEOUT)) < 0) { + err_sys_report(errfd, "WARN: can't read request from %s: st_read", + inet_ntoa(*from)); + return; + } + if (st_write(cli_nfd, resp, n, ST_UTIME_NO_TIMEOUT) != n) { + err_sys_report(errfd, "WARN: can't write response to %s: st_write", + inet_ntoa(*from)); + return; + } + + RQST_COUNT(srv_socket_index)++; +} + + +/* + * Configuration loading function stub. + */ +void load_configs(void) +{ + err_report(errfd, "INFO: process %d (pid %d): configuration loaded", + my_index, my_pid); +} + + +/* + * Buffered access logging methods. + * Note that stdio functions (fopen(3), fprintf(3), fflush(3), etc.) cannot + * be used if multiple VPs are created since these functions can flush buffer + * at any point and thus write only partial log record to disk. + * Also, it is completely safe for all threads of the same VP to write to + * the same log buffer without any mutex protection (one buffer per VP, of + * course). + */ +void logbuf_open(void) +{ + +} + + +void logbuf_flush(void) +{ + +} + + +void logbuf_close(void) +{ + +} + + +/****************************************************************** + * Small utility functions + */ + +static void Signal(int sig, void (*handler)(int)) +{ + struct sigaction sa; + + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(sig, &sa, NULL); +} + +static int cpu_count(void) +{ + int n; + +#if defined (_SC_NPROCESSORS_ONLN) + n = (int) sysconf(_SC_NPROCESSORS_ONLN); +#elif defined (_SC_NPROC_ONLN) + n = (int) sysconf(_SC_NPROC_ONLN); +#elif defined (HPUX) +#include + n = mpctl(MPC_GETNUMSPUS, 0, 0); +#else + n = -1; + errno = ENOSYS; +#endif + + return n; +} + +/******************************************************************/ + diff --git a/trunk/3rdparty/st-srs/extensions/Makefile b/trunk/3rdparty/st-srs/extensions/Makefile new file mode 100644 index 000000000..fc6634f93 --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/Makefile @@ -0,0 +1,91 @@ +# +# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Silicon Graphics, Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +CC = cc + +SHELL = /bin/sh +ECHO = /bin/echo + +DEPTH = .. +BUILD = +TARGETDIR = obj + +DEFINES = +OTHER_FLAGS = +CFLAGS = + +OBJDIR = $(DEPTH)/$(TARGETDIR) +INCDIR = $(DEPTH)/$(TARGETDIR) + +LIBRESOLV = +EXTRALIBS = + +SLIBRARY = $(OBJDIR)/libstx.a +OBJS = $(OBJDIR)/dnscache.o $(OBJDIR)/dnsres.o $(OBJDIR)/lrucache.o + + +CFLAGS += -Wall -I$(INCDIR) +AR = ar +ARFLAGS = rv +RANLIB = ranlib + + +########################## +# Platform section. +# + +ifeq (LINUX, $(findstring LINUX, $(OS))) +LIBRESOLV = -lresolv +endif + +ifeq ($(OS), SOLARIS) +LIBRESOLV = -lresolv +EXTRALIBS = -lsocket -lnsl +endif + +# +# End of platform section. +########################## + + +all: $(SLIBRARY) + +$(SLIBRARY): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + $(RANLIB) $@ + +$(OBJDIR)/%.o: %.c stx.h common.h + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -rf $(OBJS) $(SLIBRARY) + +#.DEFAULT: +# @cd $(DEPTH); $(MAKE) $@ + diff --git a/trunk/3rdparty/st-srs/extensions/README b/trunk/3rdparty/st-srs/extensions/README new file mode 100644 index 000000000..f768aa712 --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/README @@ -0,0 +1,42 @@ +This directory contains extensions to the core State Threads Library +that were contributed by users. All files hereunder are not part of the +State Threads Library itself. They are provided as-is, without warranty +or support, and under whatever license terms their authors provided. To +contribute your own extensions, just mail them to the project +administrators or to one of the project's mailing lists; see +state-threads.sourceforge.net. Please indicate the license terms under +which the project may distribute your contribution. + +======================================================================== + +stx_fileio +---------- +Contributed by Jeff , 4 Nov 2002. + +Provides non-blocking random access file reading capability for +programs using the State Threads library. There is one public function: + +ssize_t stx_file_read(st_netfd_t fd, off_t offset, + void *buf, size_t nbytes, st_utime_t timeout); + +The implementation is not optimal in that the data is copied at least once +more than should be necessary. Its usefulness is limited to cases where +random access to a file is required and where starvation of other threads +is unacceptable. + +The particular application which motivated this implementation was a UDP +file transfer protocol. Because the OS does very little buffering of UDP +traffic it is important that UDP transmission threads are not starved for +periods of time which are long relative to the interval required to +maintain a steady send rate. + +Licensed under the same dual MPL/GPL as core State Threads. + +======================================================================== + +stx_dns +------- + +Documentation coming. + +======================================================================== diff --git a/trunk/3rdparty/st-srs/extensions/common.h b/trunk/3rdparty/st-srs/extensions/common.h new file mode 100644 index 000000000..f6298ba09 --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/common.h @@ -0,0 +1,77 @@ +#ifndef _STX_COMMON_H_ +#define _STX_COMMON_H_ + +#include +#include + + +#define STX_BEGIN_MACRO { +#define STX_END_MACRO } + + +/***************************************** + * Circular linked list definitions + */ + +typedef struct _stx_clist { + struct _stx_clist *next; + struct _stx_clist *prev; +} stx_clist_t; + +/* Insert element "_e" into the list, before "_l" */ +#define STX_CLIST_INSERT_BEFORE(_e,_l) \ + STX_BEGIN_MACRO \ + (_e)->next = (_l); \ + (_e)->prev = (_l)->prev; \ + (_l)->prev->next = (_e); \ + (_l)->prev = (_e); \ + STX_END_MACRO + +/* Insert element "_e" into the list, after "_l" */ +#define STX_CLIST_INSERT_AFTER(_e,_l) \ + STX_BEGIN_MACRO \ + (_e)->next = (_l)->next; \ + (_e)->prev = (_l); \ + (_l)->next->prev = (_e); \ + (_l)->next = (_e); \ + STX_END_MACRO + +/* Append an element "_e" to the end of the list "_l" */ +#define STX_CLIST_APPEND_LINK(_e,_l) STX_CLIST_INSERT_BEFORE(_e,_l) + +/* Remove the element "_e" from it's circular list */ +#define STX_CLIST_REMOVE_LINK(_e) \ + STX_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + STX_END_MACRO + +/* Return the head/tail of the list */ +#define STX_CLIST_HEAD(_l) (_l)->next +#define STX_CLIST_TAIL(_l) (_l)->prev + +/* Return non-zero if the given circular list "_l" is empty, */ +/* zero if the circular list is not empty */ +#define STX_CLIST_IS_EMPTY(_l) \ + ((_l)->next == (_l)) + +/* Initialize a circular list */ +#define STX_CLIST_INIT_CLIST(_l) \ + STX_BEGIN_MACRO \ + (_l)->next = (_l); \ + (_l)->prev = (_l); \ + STX_END_MACRO + + +/***************************************** + * Useful macros + */ + +#ifndef offsetof +#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) +#endif + +#define STX_MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#endif /* !_STX_COMMON_H_ */ + diff --git a/trunk/3rdparty/st-srs/extensions/dnscache.c b/trunk/3rdparty/st-srs/extensions/dnscache.c new file mode 100644 index 000000000..ac49166a1 --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/dnscache.c @@ -0,0 +1,190 @@ +#include "stx.h" +#include "common.h" + + +/***************************************** + * Basic types definitions + */ + +typedef struct _stx_dns_data { + struct in_addr *addrs; + int num_addrs; + int cur; + time_t expires; +} stx_dns_data_t; + + +#define MAX_HOST_ADDRS 1024 + +static struct in_addr addr_list[MAX_HOST_ADDRS]; + +stx_cache_t *_stx_dns_cache = NULL; + +extern int _stx_dns_ttl; +extern int _stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout); + + +static unsigned long hash_hostname(const void *key) +{ + const char *name = (const char *)key; + unsigned long hash = 0; + + while (*name) + hash = (hash << 4) - hash + *name++; /* hash = hash * 15 + *name++ */ + + return hash; +} + +static void cleanup_entry(void *key, void *data) +{ + if (key) + free(key); + + if (data) { + if (((stx_dns_data_t *)data)->addrs) + free(((stx_dns_data_t *)data)->addrs); + free(data); + } +} + +static int lookup_entry(const char *host, struct in_addr *addrs, + int *num_addrs, int rotate) +{ + stx_cache_entry_t *entry; + stx_dns_data_t *data; + int n; + + entry = stx_cache_entry_lookup(_stx_dns_cache, host); + if (entry) { + data = (stx_dns_data_t *)stx_cache_entry_getdata(entry); + if (st_time() <= data->expires) { + if (*num_addrs == 1) { + if (rotate) { + *addrs = data->addrs[data->cur++]; + if (data->cur >= data->num_addrs) + data->cur = 0; + } else { + *addrs = data->addrs[0]; + } + } else { + n = STX_MIN(*num_addrs, data->num_addrs); + memcpy(addrs, data->addrs, n * sizeof(*addrs)); + *num_addrs = n; + } + + stx_cache_entry_release(_stx_dns_cache, entry); + return 1; + } + + /* + * Cache entry expired: decrement its refcount and purge it from cache. + */ + stx_cache_entry_release(_stx_dns_cache, entry); + stx_cache_entry_delete(_stx_dns_cache, entry); + } + + return 0; +} + +static void insert_entry(const char *host, struct in_addr *addrs, int count) +{ + stx_cache_entry_t *entry; + stx_dns_data_t *data; + char *key; + size_t n; + + if (_stx_dns_ttl > 0) { + key = strdup(host); + data = (stx_dns_data_t *)malloc(sizeof(stx_dns_data_t)); + n = count * sizeof(*addrs); + if (data) { + data->addrs = (struct in_addr *)malloc(n); + if (data->addrs) + memcpy(data->addrs, addrs, n); + data->num_addrs = count; + data->cur = 0; + data->expires = st_time() + _stx_dns_ttl; + } + entry = stx_cache_entry_create(key, data, strlen(host) + 1 + + sizeof(stx_dns_data_t) + n + + stx_cache_entry_sizeof()); + if (key && data && data->addrs && entry && + stx_cache_entry_insert(_stx_dns_cache, entry) == 0) { + stx_cache_entry_release(_stx_dns_cache, entry); + return; + } + + if (entry) + stx_cache_entry_delete(_stx_dns_cache, entry); + else + cleanup_entry(key, data); + } +} + + + +int _stx_dns_cache_getaddrlist(const char *hostname, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout, + int rotate) +{ + char host[128]; + int n, count; + + if (!_stx_dns_cache) + return _stx_dns_getaddrlist(hostname, addrs, num_addrs, timeout); + + for (n = 0; n < sizeof(host) - 1 && hostname[n]; n++) { + host[n] = tolower(hostname[n]); + } + host[n] = '\0'; + + if (lookup_entry(host, addrs, num_addrs, rotate)) + return 0; + + count = MAX_HOST_ADDRS; + if (_stx_dns_getaddrlist(host, addr_list, &count, timeout) < 0) + return -1; + n = STX_MIN(*num_addrs, count); + memcpy(addrs, addr_list, n * sizeof(*addrs)); + *num_addrs = n; + + insert_entry(host, addr_list, count); + return 0; +} + + +int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size) +{ + _stx_dns_cache = stx_cache_create(max_size, max_bytes, hash_size, + hash_hostname, + (long (*)(const void *, const void *))strcmp, + cleanup_entry); + if (!_stx_dns_cache) + return -1; + + return 0; +} + +void stx_dns_cache_getinfo(stx_cache_info_t *info) +{ + if (_stx_dns_cache) + stx_cache_getinfo(_stx_dns_cache, info); + else + memset(info, 0, sizeof(stx_cache_info_t)); +} + +int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout) +{ + return _stx_dns_cache_getaddrlist(hostname, addrs, num_addrs, timeout, 0); +} + +int stx_dns_getaddr(const char *hostname, struct in_addr *addr, + st_utime_t timeout) +{ + int n = 1; + + return _stx_dns_cache_getaddrlist(hostname, addr, &n, timeout, 1); +} + diff --git a/trunk/3rdparty/st-srs/extensions/dnsres.c b/trunk/3rdparty/st-srs/extensions/dnsres.c new file mode 100644 index 000000000..04a91ccaf --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/dnsres.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 1985, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Silicon Graphics, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stx.h" + +#define MAXPACKET 1024 + +#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) +#define NETDB_INTERNAL h_NETDB_INTERNAL +#endif + +/* New in Solaris 7 */ +#if !defined(_getshort) && defined(ns_get16) +#define _getshort(cp) ns_get16(cp) +#define _getlong(cp) ns_get32(cp) +#endif + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf_t; + +int _stx_dns_ttl; + + +static int parse_answer(querybuf_t *ans, int len, struct in_addr *addrs, + int *num_addrs) +{ + char buf[MAXPACKET]; + HEADER *ahp; + u_char *cp, *eoa; + int type, n, i; + + ahp = &ans->hdr; + eoa = ans->buf + len; + cp = ans->buf + sizeof(HEADER); + h_errno = TRY_AGAIN; + _stx_dns_ttl = -1; + i = 0; + + while (ahp->qdcount > 0) { + ahp->qdcount--; + cp += dn_skipname(cp, eoa) + QFIXEDSZ; + } + while (ahp->ancount > 0 && cp < eoa && i < *num_addrs) { + ahp->ancount--; + if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) + return -1; + cp += n; + if (cp + 4 + 4 + 2 >= eoa) + return -1; + type = _getshort(cp); + cp += 4; + if (type == T_A) + _stx_dns_ttl = _getlong(cp); + cp += 4; + n = _getshort(cp); + cp += 2; + if (type == T_A) { + if (n > sizeof(*addrs) || cp + n > eoa) + return -1; + memcpy(&addrs[i++], cp, n); + } + cp += n; + } + + *num_addrs = i; + return 0; +} + + +static int query_domain(st_netfd_t nfd, const char *name, + struct in_addr *addrs, int *num_addrs, + st_utime_t timeout) +{ + querybuf_t qbuf; + u_char *buf = qbuf.buf; + HEADER *hp = &qbuf.hdr; + int blen = sizeof(qbuf); + int i, len, id; + + for (i = 0; i < _res.nscount; i++) { + len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); + if (len <= 0) { + h_errno = NO_RECOVERY; + return -1; + } + id = hp->id; + + if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), + sizeof(struct sockaddr), timeout) != len) { + h_errno = NETDB_INTERNAL; + /* EINTR means interrupt by other thread, NOT by a caught signal */ + if (errno == EINTR) + return -1; + continue; + } + + /* Wait for reply */ + do { + len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); + if (len <= 0) + break; + } while (id != hp->id); + + if (len < HFIXEDSZ) { + h_errno = NETDB_INTERNAL; + if (len >= 0) + errno = EMSGSIZE; + else if (errno == EINTR) /* see the comment above */ + return -1; + continue; + } + + hp->ancount = ntohs(hp->ancount); + hp->qdcount = ntohs(hp->qdcount); + if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { + switch (hp->rcode) { + case NXDOMAIN: + h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + } + continue; + } + + if (parse_answer(&qbuf, len, addrs, num_addrs) == 0) + return 0; + } + + return -1; +} + + +#define CLOSE_AND_RETURN(ret) \ + { \ + n = errno; \ + st_netfd_close(nfd); \ + errno = n; \ + return (ret); \ + } + + +int _stx_dns_getaddrlist(const char *host, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout) +{ + char name[MAXDNAME], **domain; + const char *cp; + int s, n, maxlen, dots; + int trailing_dot, tried_as_is; + st_netfd_t nfd; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return -1; + } + if (_res.options & RES_USEVC) { + h_errno = NETDB_INTERNAL; + errno = ENOSYS; + return -1; + } + if (!host || *host == '\0') { + h_errno = HOST_NOT_FOUND; + return -1; + } + + /* Create UDP socket */ + if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + h_errno = NETDB_INTERNAL; + return -1; + } + if ((nfd = st_netfd_open_socket(s)) == NULL) { + h_errno = NETDB_INTERNAL; + n = errno; + close(s); + errno = n; + return -1; + } + + maxlen = sizeof(name) - 1; + n = 0; + dots = 0; + trailing_dot = 0; + tried_as_is = 0; + + for (cp = host; *cp && n < maxlen; cp++) { + dots += (*cp == '.'); + name[n++] = *cp; + } + if (name[n - 1] == '.') + trailing_dot = 1; + + /* + * If there are dots in the name already, let's just give it a try + * 'as is'. The threshold can be set with the "ndots" option. + */ + if (dots >= _res.ndots) { + if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) + CLOSE_AND_RETURN(0); + if (h_errno == NETDB_INTERNAL && errno == EINTR) + CLOSE_AND_RETURN(-1); + tried_as_is = 1; + } + + /* + * We do at least one level of search if + * - there is no dot and RES_DEFNAME is set, or + * - there is at least one dot, there is no trailing dot, + * and RES_DNSRCH is set. + */ + if ((!dots && (_res.options & RES_DEFNAMES)) || + (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { + name[n++] = '.'; + for (domain = _res.dnsrch; *domain; domain++) { + strncpy(name + n, *domain, maxlen - n); + if (query_domain(nfd, name, addrs, num_addrs, timeout) == 0) + CLOSE_AND_RETURN(0); + if (h_errno == NETDB_INTERNAL && errno == EINTR) + CLOSE_AND_RETURN(-1); + if (!(_res.options & RES_DNSRCH)) + break; + } + } + + /* + * If we have not already tried the name "as is", do that now. + * note that we do this regardless of how many dots were in the + * name or whether it ends with a dot. + */ + if (!tried_as_is) { + if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) + CLOSE_AND_RETURN(0); + } + + CLOSE_AND_RETURN(-1); +} + diff --git a/trunk/3rdparty/st-srs/extensions/lrucache.c b/trunk/3rdparty/st-srs/extensions/lrucache.c new file mode 100644 index 000000000..33494fee6 --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/lrucache.c @@ -0,0 +1,343 @@ +#include "stx.h" +#include "common.h" + + +/***************************************** + * Basic types definitions + */ + +struct _stx_centry { + void *key; /* key for doing lookups */ + void *data; /* data in the cache */ + size_t weight; /* "weight" of this entry */ + struct _stx_centry *next; /* next entry */ + struct _stx_centry **pthis; + stx_clist_t lru_link; /* for putting this entry on LRU list */ + int ref_count; /* use count for this entry */ + int delete_pending; /* pending delete flag */ +}; + +struct _stx_cache { + size_t max_size; /* max size of cache */ + size_t cur_size; /* current size of cache */ + + size_t max_weight; /* cache capacity */ + size_t cur_weight; /* current total "weight" of all entries */ + + size_t hash_size; /* size of hash table */ + stx_cache_entry_t **table; /* hash table for this cache */ + + stx_clist_t lru_list; /* least-recently-used list */ + + /* Cache stats */ + unsigned long hits; /* num cache hits */ + unsigned long lookups; /* num cache lookups */ + unsigned long inserts; /* num inserts */ + unsigned long deletes; /* num deletes */ + + /* Functions */ + unsigned long (*key_hash_fn)(const void *); + long (*key_cmp_fn)(const void *, const void *); + void (*cleanup_fn)(void *, void *); +}; + + +#define STX_CACHE_ENTRY_PTR(_qp) \ + ((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link))) + + +/***************************************** + * Cache methods + */ + +stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight, + size_t hash_size, + unsigned long (*key_hash_fn)(const void *key), + long (*key_cmp_fn)(const void *key1, + const void *key2), + void (*cleanup_fn)(void *key, void *data)) +{ + stx_cache_t *newcache; + + newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t)); + if (newcache == NULL) + return NULL; + newcache->table = (stx_cache_entry_t **)calloc(hash_size, + sizeof(stx_cache_entry_t *)); + if (newcache->table == NULL) { + free(newcache); + return NULL; + } + + newcache->max_size = max_size; + newcache->max_weight = max_weight; + newcache->hash_size = hash_size; + STX_CLIST_INIT_CLIST(&(newcache->lru_list)); + newcache->key_hash_fn = key_hash_fn; + newcache->key_cmp_fn = key_cmp_fn; + newcache->cleanup_fn = cleanup_fn; + + return newcache; +} + + +void stx_cache_empty(stx_cache_t *cache) +{ + size_t i; + stx_cache_entry_t *entry, *next_entry; + + for (i = 0; i < cache->hash_size; i++) { + entry = cache->table[i]; + while (entry) { + next_entry = entry->next; + stx_cache_entry_delete(cache, entry); + entry = next_entry; + } + } +} + + +void stx_cache_traverse(stx_cache_t *cache, + void (*callback)(void *key, void *data)) +{ + size_t i; + stx_cache_entry_t *entry; + + for (i = 0; i < cache->hash_size; i++) { + for (entry = cache->table[i]; entry; entry = entry->next) { + if (!entry->delete_pending) + (*callback)(entry->key, entry->data); + } + } +} + + +void stx_cache_traverse_lru(stx_cache_t *cache, + void (*callback)(void *key, void *data), + unsigned int n) +{ + stx_clist_t *q; + stx_cache_entry_t *entry; + + for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n; + q = q->next, n--) { + entry = STX_CACHE_ENTRY_PTR(q); + (*callback)(entry->key, entry->data); + } +} + + +void stx_cache_traverse_mru(stx_cache_t *cache, + void (*callback)(void *key, void *data), + unsigned int n) +{ + stx_clist_t *q; + stx_cache_entry_t *entry; + + for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n; + q = q->prev, n--) { + entry = STX_CACHE_ENTRY_PTR(q); + (*callback)(entry->key, entry->data); + } +} + + +size_t stx_cache_getsize(stx_cache_t *cache) +{ + return cache->cur_size; +} + + +size_t stx_cache_getweight(stx_cache_t *cache) +{ + return cache->cur_weight; +} + + +void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info) +{ + info->max_size = cache->max_size; + info->max_weight = cache->max_weight; + info->hash_size = cache->hash_size; + info->cur_size = cache->cur_size; + info->cur_weight = cache->cur_weight; + info->hits = cache->hits; + info->lookups = cache->lookups; + info->inserts = cache->inserts; + info->deletes = cache->deletes; +} + + +/***************************************** + * Cache entry methods + */ + +stx_cache_entry_t *stx_cache_entry_create(void *key, void *data, + size_t weight) +{ + stx_cache_entry_t *newentry; + + newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t)); + if (newentry == NULL) + return NULL; + + newentry->key = key; + newentry->data = data; + newentry->weight = weight; + + return newentry; +} + + +void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry) +{ + entry->delete_pending = 1; + + if (entry->ref_count > 0) + return; + + if (entry->pthis) { + *entry->pthis = entry->next; + if (entry->next) + entry->next->pthis = entry->pthis; + + cache->cur_size--; + cache->cur_weight -= entry->weight; + cache->deletes++; + STX_CLIST_REMOVE_LINK(&(entry->lru_link)); + } + + if (cache->cleanup_fn) + cache->cleanup_fn(entry->key, entry->data); + + entry->pthis = NULL; + entry->key = NULL; + entry->data = NULL; + free(entry); +} + + +stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key) +{ + unsigned long bucket; + stx_cache_entry_t *entry; + + cache->lookups++; + bucket = cache->key_hash_fn(key) % cache->hash_size; + for (entry = cache->table[bucket]; entry; entry = entry->next) { + if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0) + break; + } + if (entry) { + cache->hits++; + if (entry->ref_count == 0) + STX_CLIST_REMOVE_LINK(&(entry->lru_link)); + entry->ref_count++; + } + + return entry; +} + + +void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry) +{ + if (entry->ref_count == 0) + return; + + entry->ref_count--; + + if (entry->ref_count == 0) { + STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list)); + if (entry->delete_pending) + stx_cache_entry_delete(cache, entry); + } +} + + +int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry) +{ + stx_cache_entry_t *old_entry; + unsigned long bucket; + + /* + * If cache capacity is exceeded, try to remove LRU entries till there is + * enough room or LRU list is empty. + */ + while (cache->cur_weight + entry->weight > cache->max_weight) { + old_entry = stx_cache_entry_getlru(cache); + if (!old_entry) { + /* cache capacity is exceeded and all entries are in use */ + return -1; + } + stx_cache_entry_delete(cache, old_entry); + } + + /* If cache size is exceeded, remove LRU entry */ + if (cache->cur_size >= cache->max_size) { + old_entry = stx_cache_entry_getlru(cache); + if (!old_entry) { + /* cache size is exceeded and all entries are in use */ + return -1; + } + stx_cache_entry_delete(cache, old_entry); + } + + /* Don't add duplicate entries in the cache */ + bucket = cache->key_hash_fn(entry->key) % cache->hash_size; + for (old_entry = cache->table[bucket]; old_entry; + old_entry = old_entry->next) { + if (!old_entry->delete_pending && + cache->key_cmp_fn(entry->key, old_entry->key) == 0) + break; + } + if (old_entry) + stx_cache_entry_delete(cache, old_entry); + + /* Insert in the hash table */ + entry->next = cache->table[bucket]; + cache->table[bucket] = entry; + entry->pthis = &cache->table[bucket]; + if (entry->next) + entry->next->pthis = &entry->next; + entry->ref_count++; + + cache->inserts++; + cache->cur_size++; + cache->cur_weight += entry->weight; + + return 0; +} + + +stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache) +{ + if (STX_CLIST_IS_EMPTY(&(cache->lru_list))) + return NULL; + + return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list))); +} + + +int stx_cache_entry_sizeof(void) +{ + return (int)sizeof(stx_cache_entry_t); +} + + +void *stx_cache_entry_getdata(stx_cache_entry_t *entry) +{ + return entry->data; +} + + +void *stx_cache_entry_getkey(stx_cache_entry_t *entry) +{ + return entry->key; +} + + +size_t stx_cache_entry_getweight(stx_cache_entry_t *entry) +{ + return entry->weight; +} + diff --git a/trunk/3rdparty/st-srs/extensions/print_stk.patch b/trunk/3rdparty/st-srs/extensions/print_stk.patch new file mode 100644 index 000000000..f7451c7b0 --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/print_stk.patch @@ -0,0 +1,367 @@ +Michael Abd-El-Malek contributed this patch. He wrote: +---------------------------------------- +Hello, + +This is a patch that enables programmatically dumping the stack of +every thread. This has been useful in debugging deadlocks, etc... +Our usage model is that the SIGUSR2 handler calls the new +_st_print_thread_stacks function, which dumps the stack for all +threads. A convenient feature is that for thread stacks that are the +same (which is common for application with a lot of worker threads +waiting for work), only one stack trace is printed, along with a +count of how many threads have that same stack. + +I use the glibc backtrace function to get the backtrace, and then use +popen to execute addr2line and convert memory addresses to file +names, function names, and line numbers. If glibc isn't available, +_st_print_thread_stacks just prints a warning. And this feature is +only available if DEBUG is turned on. + +We've found this feature extremely helpful when debugging. + +The patch can be a bit more robust (it assumes addr2line exists). +But I didn't want to go through the hassle of doing this, if the +StateThreads community doesn't want to use this patch. (In our +environment, addr2line will always be there.) + +Cheers, +Mike +---------------------------------------- +Invoking complex functions from a signal handler is not recommended, +plus this patch changes the behavior of existing API hooks. It will +not become part of State Threads proper but you may find it useful +nonetheless. This patch applies to st-1.5.2. + +diff -Nur Makefile.1.5.2 Makefile +--- Makefile.1.5.2 Wed Sep 7 14:19:50 2005 ++++ Makefile Wed Sep 7 14:33:08 2005 +@@ -255,7 +255,8 @@ + $(TARGETDIR)/stk.o \ + $(TARGETDIR)/sync.o \ + $(TARGETDIR)/key.o \ +- $(TARGETDIR)/io.o ++ $(TARGETDIR)/io.o \ ++ $(TARGETDIR)/backtrace.o + OBJS += $(EXTRA_OBJS) + HEADER = $(TARGETDIR)/st.h + SLIBRARY = $(TARGETDIR)/libst.a +diff -Nur backtrace.c.1.5.2 backtrace.c +--- backtrace.c.1.5.2 Wed Dec 31 16:00:00 1969 ++++ backtrace.c Wed Sep 7 13:40:21 2005 +@@ -0,0 +1,211 @@ ++/* ++ * The contents of this file are subject to the Mozilla Public ++ * License Version 1.1 (the "License"); you may not use this file ++ * except in compliance with the License. You may obtain a copy of ++ * the License at http://www.mozilla.org/MPL/ ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Contributor(s): Michael Abd-El-Malek (mabdelmalek@cmu.edu) ++ * Carnegie Mellon University ++ * ++ * Alternatively, the contents of this file may be used under the ++ * terms of the GNU General Public License Version 2 or later (the ++ * "GPL"), in which case the provisions of the GPL are applicable ++ * instead of those above. If you wish to allow use of your ++ * version of this file only under the terms of the GPL and not to ++ * allow others to use your version of this file under the MPL, ++ * indicate your decision by deleting the provisions above and ++ * replace them with the notice and other provisions required by ++ * the GPL. If you do not delete the provisions above, a recipient ++ * may use your version of this file under either the MPL or the ++ * GPL. ++ */ ++ ++ ++ ++/* ++ * This file contains routines for printing a stack trace of all threads. ++ * Only works when DEBUG is defined and where glibc is available, since it ++ * provides the backtrace() function. ++ */ ++ ++#define _GNU_SOURCE /* to get program_invocation_name */ ++ ++#include ++#include ++ ++ ++#if defined(DEBUG) && defined(__GLIBC__) ++ ++#include ++#include "common.h" ++#include ++#include ++#include ++ ++ ++/* The maximum number of frames to get a stack trace for. If a thread has more ++ * frames than this, then we only show the latest X frames. */ ++#define MAX_NUM_FRAMES 64 ++ ++ ++typedef struct thread_stack_s { ++ uint32_t num_frames; ++ void* addresses[MAX_NUM_FRAMES]; /* frame pointers */ ++ char* locations[MAX_NUM_FRAMES]; /* file/function/line numbers */ ++ uint32_t num_matches; ++ ++ struct thread_stack_s* next; ++} thread_stack_t; ++ ++static thread_stack_t* stacks = NULL; ++ ++ ++/* Converts the function's memory addresses to function names, file names, and ++ * line numbers. Calls binutil's addr2line program. */ ++static void get_symbol_names(thread_stack_t *stack) ++{ ++ char program_to_run[1024], function[256], filename_lineno[256], temp[19]; ++ FILE* output; ++ int num_bytes_left; ++ uint32_t i; ++ ++ /* Construct the arguments to addr2line */ ++ num_bytes_left = sizeof(program_to_run); ++ num_bytes_left -= snprintf(program_to_run, sizeof(program_to_run), ++ "addr2line -fCe %s", program_invocation_name); ++ for (i = 0; i < stack->num_frames && num_bytes_left > 0; ++i) { ++ num_bytes_left -= snprintf(temp, sizeof(temp), " %p", stack->addresses[i]); ++ strncat(program_to_run, temp, num_bytes_left); ++ } ++ ++ /* Use popen to execute addr2line and read its ouput */ ++ output = popen(program_to_run, "r"); ++ for (i = 0; i < stack->num_frames; ++i) { ++ char* function_listing = (char*) malloc(512); ++ fscanf(output, "%255s\n", function); ++ fscanf(output, "%255s\n", filename_lineno); ++ snprintf(function_listing, 512, "%s at %s", function, filename_lineno); ++ stack->locations[i] = function_listing; ++ } ++ pclose(output); ++} ++ ++ ++static void print_stack(thread_stack_t* stack) ++{ ++ int skip_offset = 0, cmp_len; ++ uint32_t i; ++ ++ /* Get the function names/filenames/line numbers */ ++ get_symbol_names(stack); ++ ++ cmp_len = strlen("_st_iterate_threads_helper"); ++ ++ /* Print the backtrace */ ++ for (i = 0; i < stack->num_frames; ++i) { ++ /* Skip frames we don't have location info for */ ++ if (!strncmp(stack->locations[i], "??", 2)) { ++ continue; ++ } ++ ++ /* Skip the frames that are used for printing the stack trace */ ++ if (skip_offset) { ++ printf("\t#%2d %s %p\n", i - skip_offset, stack->locations[i], ++ stack->addresses[i]); ++ } else if (!strncmp(stack->locations[i], "_st_iterate_threads_helper", ++ cmp_len)) { ++ skip_offset = i + 1; ++ } ++ } ++} ++ ++ ++static void add_current_thread_stack(void) ++{ ++ thread_stack_t *new_stack = malloc(sizeof(thread_stack_t)); ++ thread_stack_t *search; ++ ++ /* Call glibc function to get the backtrace */ ++ new_stack->num_frames = backtrace(new_stack->addresses, MAX_NUM_FRAMES); ++ ++ /* Check if we have another stacks that is equivalent. If so, then coaelsce ++ * two stacks into one, to minimize output to user. */ ++ search = stacks; ++ while (search) { ++ if (search->num_frames == new_stack->num_frames && ++ !memcmp(search->addresses, new_stack->addresses, ++ search->num_frames * sizeof(void*))) { ++ /* Found an existing stack that is the same as this thread's stack */ ++ ++search->num_matches; ++ free(new_stack); ++ return; ++ } else { ++ search = search->next; ++ } ++ } ++ ++ /* This is a new stack. Add it to the list of stacks. */ ++ new_stack->num_matches = 1; ++ new_stack->next = stacks; ++ stacks = new_stack; ++} ++ ++static void print_stack_frames(void) ++{ ++ while (stacks) { ++ printf("\n%u thread(s) with this backtrace:\n", stacks->num_matches); ++ print_stack(stacks); ++ stacks = stacks->next; ++ } ++ printf("\n"); ++} ++ ++static void free_stacks(void) ++{ ++ uint32_t i; ++ while (stacks) { ++ thread_stack_t *next = stacks->next; ++ for (i = 0; i < stacks->num_frames; ++i) { ++ free(stacks->locations[i]); ++ } ++ free(stacks); ++ stacks = next; ++ } ++ stacks = NULL; ++} ++ ++ ++static void st_print_thread_stack(_st_thread_t *thread, int start_flag, ++ int end_flag) ++{ ++ if (end_flag == 0) { ++ add_current_thread_stack(); ++ } else { ++ print_stack_frames(); ++ } ++} ++ ++ ++void _st_print_thread_stacks(int ignore) ++{ ++ _st_iterate_threads_flag = 1; ++ _st_iterate_threads_helper(st_print_thread_stack); ++ _st_iterate_threads_flag = 0; ++ ++ /* Deallocate memory */ ++ free_stacks(); ++} ++ ++#else /* defined(DEBUG) && defined(__GLIBC__) */ ++ ++void _st_print_thread_stacks(int ignore) ++{ ++ printf("%s: need DEBUG mode and glibc-specific functions to read stack.\n", ++ __FUNCTION__); ++} ++#endif /* defined(DEBUG) && defined(__GLIBC__) */ +diff -Nur common.h.1.5.2 common.h +--- common.h.1.5.2 Wed Sep 7 14:18:37 2005 ++++ common.h Wed Sep 7 14:35:36 2005 +@@ -371,8 +371,18 @@ + */ + + #ifdef DEBUG +-void _st_iterate_threads(void); +-#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() ++typedef void(*_st_func_ptr_t)(_st_thread_t *thread, ++ int start_flag, ++ int end_flag); ++/* Pointer to function that will be called on thread switch */ ++extern _st_func_ptr_t _st_iterate_func_ptr; ++extern int _st_iterate_threads_flag; ++/* Thread iteration function that will call an arbitrary function */ ++extern void _st_iterate_threads_helper(_st_func_ptr_t func); ++#define ST_DEBUG_ITERATE_THREADS() \ ++ if (_st_iterate_func_ptr) { \ ++ _st_iterate_threads_helper(_st_iterate_func_ptr); \ ++ } + #else + #define ST_DEBUG_ITERATE_THREADS() + #endif +diff -Nur public.h.1.5.2 public.h +--- public.h.1.5.2 Wed Sep 7 11:46:58 2005 ++++ public.h Wed Sep 7 13:38:46 2005 +@@ -171,8 +171,10 @@ + extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); + + #ifdef DEBUG +-extern void _st_show_thread_stack(st_thread_t thread, const char *messg); ++extern void _st_show_thread_stack(st_thread_t thread, int start_flag, ++ int end_flag); + extern void _st_iterate_threads(void); ++extern void _st_print_thread_stacks(int ignore); + #endif + + #ifdef __cplusplus +diff -Nur sched.c.1.5.2 sched.c +--- sched.c.1.5.2 Wed Sep 7 10:48:05 2005 ++++ sched.c Wed Sep 7 13:38:46 2005 +@@ -919,16 +919,13 @@ + + + #ifdef DEBUG +-/* ARGSUSED */ +-void _st_show_thread_stack(_st_thread_t *thread, const char *messg) +-{ +- +-} +- + /* To be set from debugger */ + int _st_iterate_threads_flag = 0; ++/* Thread iteration function that will call an arbitrary function */ ++_st_func_ptr_t _st_iterate_func_ptr = NULL; + +-void _st_iterate_threads(void) ++/* This function iterates over all threads, calling "func" for each thread. */ ++void _st_iterate_threads_helper(_st_func_ptr_t func) + { + static _st_thread_t *thread = NULL; + static jmp_buf orig_jb, save_jb; +@@ -944,16 +941,20 @@ + + if (thread) { + memcpy(thread->context, save_jb, sizeof(jmp_buf)); +- _st_show_thread_stack(thread, NULL); ++ func(thread, 0, 0); + } else { + if (MD_SETJMP(orig_jb)) { + _st_iterate_threads_flag = 0; ++ _st_iterate_func_ptr = NULL; + thread = NULL; +- _st_show_thread_stack(thread, "Iteration completed"); ++ /* Last thread to iterate through */ ++ func(thread, 0, 1); + return; + } ++ /* First thread to iterate through */ + thread = _ST_CURRENT_THREAD(); +- _st_show_thread_stack(thread, "Iteration started"); ++ _st_iterate_func_ptr = func; ++ func(thread, 1, 0); + } + + q = thread->tlink.next; +@@ -966,5 +967,17 @@ + memcpy(save_jb, thread->context, sizeof(jmp_buf)); + MD_LONGJMP(thread->context, 1); + } ++ ++/* ARGSUSED */ ++void _st_show_thread_stack(_st_thread_t *thread, int start_flag, int end_flag) ++{ ++} ++ ++/* Iterate over threads inside debugger; see st/README */ ++void _st_iterate_threads(void) ++{ ++ _st_iterate_threads_helper(_st_show_thread_stack); ++} ++ + #endif /* DEBUG */ + diff --git a/trunk/3rdparty/st-srs/extensions/stx.h b/trunk/3rdparty/st-srs/extensions/stx.h new file mode 100644 index 000000000..8371e0d93 --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/stx.h @@ -0,0 +1,91 @@ +#ifndef _STX_H_ +#define _STX_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "st.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/***************************************** + * Basic types definitions + */ + +typedef struct _stx_centry stx_cache_entry_t; +typedef struct _stx_cache stx_cache_t; + +/* This is public type */ +typedef struct _stx_cache_info { + size_t max_size; + size_t max_weight; + size_t hash_size; + size_t cur_size; + size_t cur_weight; + unsigned long hits; + unsigned long lookups; + unsigned long inserts; + unsigned long deletes; +} stx_cache_info_t; + + +/***************************************** + * Cache and cache entry methods + */ + +stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight, + size_t hash_size, + unsigned long (*key_hash_fn)(const void *key), + long (*key_cmp_fn)(const void *key1, + const void *key2), + void (*cleanup_fn)(void *key, void *data)); +void stx_cache_empty(stx_cache_t *cache); +void stx_cache_traverse(stx_cache_t *cache, + void (*callback)(void *key, void *data)); +void stx_cache_traverse_lru(stx_cache_t *, void (*)(void *, void *), + unsigned int); +void stx_cache_traverse_mru(stx_cache_t *, void (*)(void *, void *), + unsigned int); +void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info); +size_t stx_cache_getsize(stx_cache_t *cache); +size_t stx_cache_getweight(stx_cache_t *cache); + + +stx_cache_entry_t *stx_cache_entry_create(void *key, void *data, + size_t weight); +void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry); +stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key); +void stx_cache_entry_release(stx_cache_t *, stx_cache_entry_t *); +int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry); +stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache); +int stx_cache_entry_sizeof(void); +void *stx_cache_entry_getdata(stx_cache_entry_t *entry); +void *stx_cache_entry_getkey(stx_cache_entry_t *entry); +size_t stx_cache_entry_getweight(stx_cache_entry_t *entry); + + +int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size); +void stx_dns_cache_getinfo(stx_cache_info_t *info); +int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, + int *num_addrs, st_utime_t timeout); +int stx_dns_getaddr(const char *hostname, struct in_addr *addr, + st_utime_t timeout); + +#ifdef __cplusplus +} +#endif + +#endif /* !_STX_H_ */ + diff --git a/trunk/3rdparty/st-srs/extensions/stx_fileio.c b/trunk/3rdparty/st-srs/extensions/stx_fileio.c new file mode 100644 index 000000000..cb24346e8 --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/stx_fileio.c @@ -0,0 +1,197 @@ +/* + * File I/O extension to the State Threads Library. + */ + +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the file I/O extension to the State Threads Library. + * + * The Initial Developer of the Original Code is Jeff + * . Portions created by the Initial + * Developer are Copyright (C) 2002 the Initial Developer. All Rights + * Reserved. + * + * Contributor(s): (none) + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include + +#include "stx_fileio.h" + +#define STX_FILEIO_SIGNUM SIGUSR2 + +typedef struct { + st_netfd_t data_fd; + st_netfd_t control_fd; + pid_t pid; +} fileio_data_t; + +#define FILEREADER_MAX_READ 1024 + +typedef struct { + off_t offset; + ssize_t nbytes; +} file_reader_cb_t; + +/** + * Fork a process to read a file and return its pid. Receives + * offset/length commands from control stream and sends corresponding data + * to out stream. A zero length on the control stream signals an end. + * + * @param fd stream from which to read + * @param control_out receives the file descriptor to which control commands can be sent + * @param fd_out receives the file descriptor from which the output of the command can be read. + * @return PID of the process created to execute the command + */ +pid_t +file_reader(int fd, int *fd_control, int *fd_out) +{ + pid_t pid; + int control_pipe[2], out_pipe[2]; + + if (pipe(control_pipe) < 0 || pipe(out_pipe) < 0) + return (pid_t)-1; + + pid = fork(); + if (pid == (pid_t) -1) + { + close(control_pipe[0]); + close(control_pipe[1]); + close(out_pipe[0]); + close(out_pipe[1]); + return pid; + } + else if (pid == (pid_t) 0) + { + // child + off_t pos = 0; + file_reader_cb_t cb; + char buf[FILEREADER_MAX_READ]; + if (fd == -1) + _exit(EXIT_FAILURE); + + while (sizeof(cb) == read(control_pipe[0], &cb, sizeof(cb))) { + ssize_t nb; + if (0 >= cb.nbytes) + goto clean_exit; + if (pos != cb.offset) { + pos = lseek(fd, cb.offset, SEEK_SET); + if (pos == (off_t)-1) + break; + } + nb = read(fd, buf, cb.nbytes); + if (nb == (ssize_t)-1) + break; + pos += nb; + write(out_pipe[1], (char *)&nb, sizeof(nb)); + write(out_pipe[1], buf, nb); + } + perror("ERROR: file_reader: "); + clean_exit: + close(control_pipe[0]); + close(control_pipe[1]); + close(out_pipe[0]); + close(out_pipe[1]); + _exit(EXIT_SUCCESS); + } + + // parent + close(out_pipe[1]); + close(control_pipe[0]); + *fd_out = out_pipe[0]; + *fd_control = control_pipe[1]; + return pid; +} + +/** + * fileio_data_t destructor callback + */ +static void +fileio_data_destructor(void *dat_in) +{ + if (dat_in) { + fileio_data_t *dat = (fileio_data_t *)dat_in; + file_reader_cb_t cb; + cb.offset = 0; + cb.nbytes = 0; + st_write(dat->control_fd, (char *)&cb, sizeof(cb), + ST_UTIME_NO_TIMEOUT); + waitpid(dat->pid, NULL, 0); + st_netfd_close(dat->control_fd); + st_netfd_close(dat->data_fd); + free(dat_in); + } +} + +/** + * Retrieve fileio_data_t struct from an st descriptor. Create and store + * a new one if needed. + */ +static fileio_data_t *get_fileio_data(st_netfd_t fd) +{ + fileio_data_t *dat = (fileio_data_t *)st_netfd_getspecific(fd); + if (!dat) { + int fd_control, fd_out; + pid_t pid = file_reader(st_netfd_fileno(fd), &fd_control, &fd_out); + if (pid != (pid_t)-1) { + dat = (fileio_data_t *)calloc(1, sizeof(fileio_data_t)); + dat->control_fd = st_netfd_open(fd_control); + dat->data_fd = st_netfd_open(fd_out); + dat->pid = pid; + st_netfd_setspecific(fd, dat, fileio_data_destructor); + } + } + return dat; +} + +/** + * Read data from the specified section of a file. Uses a forked + * file_reader process to do the actual reading so as to avoid causing all + * State Threads to block. + * + * @param fd must refer to a seekable file. + * @param offset absolute offset within the file + * @param buf output buffer + * @param nbytes size of the output buffer + * @param timeout + */ +ssize_t +stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout) +{ + fileio_data_t *dat = get_fileio_data(fd); + if (dat) { + file_reader_cb_t cb; + ssize_t ret = (ssize_t)-1; + cb.offset = offset; + cb.nbytes = nbytes; + st_write(dat->control_fd, (char *)&cb, sizeof(cb), timeout); + if (sizeof(ret) == st_read(dat->data_fd, (char *)&ret, sizeof(ret), timeout) && 0 < ret && ret <= nbytes) { + return st_read(dat->data_fd, buf, ret, timeout); + } else { + return ret; + } + } + + return (ssize_t)-1; +} diff --git a/trunk/3rdparty/st-srs/extensions/stx_fileio.h b/trunk/3rdparty/st-srs/extensions/stx_fileio.h new file mode 100644 index 000000000..b6bec190b --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/stx_fileio.h @@ -0,0 +1,52 @@ +/* + * File I/O extension to the State Threads Library. + */ + +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the file I/O extension to the State Threads Library. + * + * The Initial Developer of the Original Code is Jeff + * . Portions created by the Initial + * Developer are Copyright (C) 2002 the Initial Developer. All Rights + * Reserved. + * + * Contributor(s): (none) + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef __STX_FILEIO_H__ +#define __STX_FILEIO_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern ssize_t stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout); + +#ifdef __cplusplus +} +#endif +#endif /* !__STX_FILEIO_H__ */ diff --git a/trunk/3rdparty/st-srs/extensions/testdns.c b/trunk/3rdparty/st-srs/extensions/testdns.c new file mode 100644 index 000000000..aa896b25e --- /dev/null +++ b/trunk/3rdparty/st-srs/extensions/testdns.c @@ -0,0 +1,112 @@ +#include "stx.h" +#include +#include + + +#define MAX_ADDRS 128 +#define TIMEOUT (4*1000000LL) + +static void do_resolve(const char *host) +{ + struct in_addr addrs[MAX_ADDRS]; + int i, n = MAX_ADDRS; + + if (stx_dns_getaddrlist(host, addrs, &n, TIMEOUT) < 0) { + fprintf(stderr, "stx_dns_getaddrlist: can't resolve %s: ", host); + if (h_errno == NETDB_INTERNAL) + perror(""); + else + herror(""); + } else { + if (n > 0) + printf("%-40s %s\n", (char *)host, inet_ntoa(addrs[0])); + for (i = 1; i < n; i++) + printf("%-40s %s\n", "", inet_ntoa(addrs[i])); + } +} + +static void show_info(void) +{ + stx_cache_info_t info; + + stx_dns_cache_getinfo(&info); + printf("DNS cache info:\n\n"); + printf("max_size: %8d\n", (int)info.max_size); + printf("capacity: %8d bytes\n", (int)info.max_weight); + printf("hash_size: %8d\n", (int)info.hash_size); + printf("cur_size: %8d\n" + "cur_mem: %8d bytes\n" + "hits: %8d\n" + "lookups: %8d\n" + "inserts: %8d\n" + "deletes: %8d\n", + (int)info.cur_size, (int)info.cur_weight, (int)info.hits, + (int)info.lookups, (int)info.inserts, (int)info.deletes); +} + +extern stx_cache_t *_stx_dns_cache; + +static void printhost(void *host, void *data) +{ + printf("%s\n", (char *)host); +} + +static void show_lru(void) +{ + printf("LRU hosts:\n\n"); + stx_cache_traverse_lru(_stx_dns_cache, printhost, 10); +} + +static void show_mru(void) +{ + printf("MRU hosts:\n\n"); + stx_cache_traverse_mru(_stx_dns_cache, printhost, 10); +} + +static void flush_cache(void) +{ + stx_cache_empty(_stx_dns_cache); + printf("DNS cache is empty\n"); +} + + +int main() +{ + char line[256]; + char str[sizeof(line)]; + + st_init(); + stx_dns_cache_init(100, 10000, 101); + + for ( ; ; ) { + fputs("> ", stdout); + fflush(stdout); + if (!fgets(line, sizeof(line), stdin)) + break; + if (sscanf(line, "%s", str) != 1) + continue; + if (strcmp(str, "exit") == 0 || strcmp(str, "quit") == 0) + break; + if (strcmp(str, "info") == 0) { + show_info(); + continue; + } + if (strcmp(str, "lru") == 0) { + show_lru(); + continue; + } + if (strcmp(str, "mru") == 0) { + show_mru(); + continue; + } + if (strcmp(str, "flush") == 0) { + flush_cache(); + continue; + } + + do_resolve(str); + } + + return 0; +} + diff --git a/trunk/3rdparty/st-srs/io.c b/trunk/3rdparty/st-srs/io.c new file mode 100644 index 000000000..912de0b28 --- /dev/null +++ b/trunk/3rdparty/st-srs/io.c @@ -0,0 +1,769 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + + +#if EAGAIN != EWOULDBLOCK + #define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK)) +#else + #define _IO_NOT_READY_ERROR (errno == EAGAIN) +#endif + +#define _LOCAL_MAXIOV 16 + +/* File descriptor object free list */ +static _st_netfd_t *_st_netfd_freelist = NULL; +/* Maximum number of file descriptors that the process can open */ +static int _st_osfd_limit = -1; + +static void _st_netfd_free_aux_data(_st_netfd_t *fd); + +int _st_io_init(void) +{ + struct sigaction sigact; + struct rlimit rlim; + int fdlim; + + /* Ignore SIGPIPE */ + sigact.sa_handler = SIG_IGN; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + if (sigaction(SIGPIPE, &sigact, NULL) < 0) + return -1; + + /* Set maximum number of open file descriptors */ + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) + return -1; + + fdlim = (*_st_eventsys->fd_getlimit)(); + if (fdlim > 0 && rlim.rlim_max > (rlim_t) fdlim) { + rlim.rlim_max = fdlim; + } + + /** + * by SRS, for osx. + * when rlimit max is negative, for example, osx, use cur directly. + * @see https://github.com/winlinvip/simple-rtmp-server/issues/336 + */ + if ((int)rlim.rlim_max < 0) { + _st_osfd_limit = (int)(fdlim > 0? fdlim : rlim.rlim_cur); + return 0; + } + + rlim.rlim_cur = rlim.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) + return -1; + _st_osfd_limit = (int) rlim.rlim_max; + + return 0; +} + + +int st_getfdlimit(void) +{ + return _st_osfd_limit; +} + + +void st_netfd_free(_st_netfd_t *fd) +{ + if (!fd->inuse) + return; + + fd->inuse = 0; + if (fd->aux_data) + _st_netfd_free_aux_data(fd); + if (fd->private_data && fd->destructor) + (*(fd->destructor))(fd->private_data); + fd->private_data = NULL; + fd->destructor = NULL; + fd->next = _st_netfd_freelist; + _st_netfd_freelist = fd; +} + + +static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket) +{ + _st_netfd_t *fd; + int flags = 1; + + if ((*_st_eventsys->fd_new)(osfd) < 0) + return NULL; + + if (_st_netfd_freelist) { + fd = _st_netfd_freelist; + _st_netfd_freelist = _st_netfd_freelist->next; + } else { + fd = calloc(1, sizeof(_st_netfd_t)); + if (!fd) + return NULL; + } + + fd->osfd = osfd; + fd->inuse = 1; + fd->next = NULL; + + if (nonblock) { + /* Use just one system call */ + if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) + return fd; + /* Do it the Posix way */ + if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || + fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) { + st_netfd_free(fd); + return NULL; + } + } + + return fd; +} + + +_st_netfd_t *st_netfd_open(int osfd) +{ + return _st_netfd_new(osfd, 1, 0); +} + + +_st_netfd_t *st_netfd_open_socket(int osfd) +{ + return _st_netfd_new(osfd, 1, 1); +} + + +int st_netfd_close(_st_netfd_t *fd) +{ + if ((*_st_eventsys->fd_close)(fd->osfd) < 0) + return -1; + + st_netfd_free(fd); + return close(fd->osfd); +} + + +int st_netfd_fileno(_st_netfd_t *fd) +{ + return (fd->osfd); +} + + +void st_netfd_setspecific(_st_netfd_t *fd, void *value, _st_destructor_t destructor) +{ + if (value != fd->private_data) { + /* Free up previously set non-NULL data value */ + if (fd->private_data && fd->destructor) + (*(fd->destructor))(fd->private_data); + } + fd->private_data = value; + fd->destructor = destructor; +} + + +void *st_netfd_getspecific(_st_netfd_t *fd) +{ + return (fd->private_data); +} + + +/* + * Wait for I/O on a single descriptor. + */ +int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeout) +{ + struct pollfd pd; + int n; + + pd.fd = fd->osfd; + pd.events = (short) how; + pd.revents = 0; + + if ((n = st_poll(&pd, 1, timeout)) < 0) + return -1; + if (n == 0) { + /* Timed out */ + errno = ETIME; + return -1; + } + if (pd.revents & POLLNVAL) { + errno = EBADF; + return -1; + } + + return 0; +} + + +#ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT +/* No-op */ +int st_netfd_serialize_accept(_st_netfd_t *fd) +{ + fd->aux_data = NULL; + return 0; +} + +/* No-op */ +static void _st_netfd_free_aux_data(_st_netfd_t *fd) +{ + fd->aux_data = NULL; +} + +_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) +{ + int osfd, err; + _st_netfd_t *newfd; + + while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return NULL; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return NULL; + } + + /* On some platforms the new socket created by accept() inherits */ + /* the nonblocking attribute of the listening socket */ +#if defined (MD_ACCEPT_NB_INHERITED) + newfd = _st_netfd_new(osfd, 0, 1); +#elif defined (MD_ACCEPT_NB_NOT_INHERITED) + newfd = _st_netfd_new(osfd, 1, 1); +#else + #error Unknown OS +#endif + + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; +} + +#else /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ +/* + * On some platforms accept() calls from different processes + * on the same listen socket must be serialized. + * The following code serializes accept()'s without process blocking. + * A pipe is used as an inter-process semaphore. + */ +int st_netfd_serialize_accept(_st_netfd_t *fd) +{ + _st_netfd_t **p; + int osfd[2], err; + + if (fd->aux_data) { + errno = EINVAL; + return -1; + } + if ((p = (_st_netfd_t **)calloc(2, sizeof(_st_netfd_t *))) == NULL) + return -1; + if (pipe(osfd) < 0) { + free(p); + return -1; + } + if ((p[0] = st_netfd_open(osfd[0])) != NULL && (p[1] = st_netfd_open(osfd[1])) != NULL && write(osfd[1], " ", 1) == 1) { + fd->aux_data = p; + return 0; + } + /* Error */ + err = errno; + if (p[0]) + st_netfd_free(p[0]); + if (p[1]) + st_netfd_free(p[1]); + close(osfd[0]); + close(osfd[1]); + free(p); + errno = err; + + return -1; +} + +static void _st_netfd_free_aux_data(_st_netfd_t *fd) +{ + _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; + + st_netfd_close(p[0]); + st_netfd_close(p[1]); + free(p); + fd->aux_data = NULL; +} + +_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) +{ + int osfd, err; + _st_netfd_t *newfd; + _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; + ssize_t n; + char c; + + for ( ; ; ) { + if (p == NULL) { + osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); + } else { + /* Get the lock */ + n = st_read(p[0], &c, 1, timeout); + if (n < 0) + return NULL; + ST_ASSERT(n == 1); + /* Got the lock */ + osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); + /* Unlock */ + err = errno; + n = st_write(p[1], &c, 1, timeout); + ST_ASSERT(n == 1); + errno = err; + } + if (osfd >= 0) + break; + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return NULL; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return NULL; + } + + /* On some platforms the new socket created by accept() inherits */ + /* the nonblocking attribute of the listening socket */ +#if defined (MD_ACCEPT_NB_INHERITED) + newfd = _st_netfd_new(osfd, 0, 1); +#elif defined (MD_ACCEPT_NB_NOT_INHERITED) + newfd = _st_netfd_new(osfd, 1, 1); +#else +#error Unknown OS +#endif + + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; +} +#endif /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ + + +int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout) +{ + int n, err = 0; + + while (connect(fd->osfd, addr, addrlen) < 0) { + if (errno != EINTR) { + /* + * On some platforms, if connect() is interrupted (errno == EINTR) + * after the kernel binds the socket, a subsequent connect() + * attempt will fail with errno == EADDRINUSE. Ignore EADDRINUSE + * iff connect() was previously interrupted. See Rich Stevens' + * "UNIX Network Programming," Vol. 1, 2nd edition, p. 413 + * ("Interrupted connect"). + */ + if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) + return -1; + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) + return -1; + /* Try to find out whether the connection setup succeeded or failed */ + n = sizeof(int); + if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, (socklen_t *)&n) < 0) + return -1; + if (err) { + errno = err; + return -1; + } + break; + } + err = 1; + } + + return 0; +} + + +ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) +{ + ssize_t n; + + while ((n = read(fd->osfd, buf, nbyte)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return n; +} + + +int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout) +{ + struct iovec iov, *riov; + int riov_size, rv; + + iov.iov_base = buf; + iov.iov_len = *resid; + riov = &iov; + riov_size = 1; + rv = st_readv_resid(fd, &riov, &riov_size, timeout); + *resid = iov.iov_len; + return rv; +} + + +ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) +{ + ssize_t n; + + while ((n = readv(fd->osfd, iov, iov_size)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return n; +} + +int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) +{ + ssize_t n; + + while (*iov_size > 0) { + if (*iov_size == 1) + n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); + else + n = readv(fd->osfd, *iov, *iov_size); + if (n < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + } else if (n == 0) + break; + else { + while ((size_t) n >= (*iov)->iov_len) { + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) + break; + } + if (*iov_size == 0) + break; + (*iov)->iov_base = (char *) (*iov)->iov_base + n; + (*iov)->iov_len -= n; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return 0; +} + + +ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) +{ + size_t resid = nbyte; + return st_read_resid(fd, buf, &resid, timeout) == 0 ? + (ssize_t) (nbyte - resid) : -1; +} + + +int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, st_utime_t timeout) +{ + struct iovec iov, *riov; + int riov_size, rv; + + iov.iov_base = (void *) buf; /* we promise not to modify buf */ + iov.iov_len = *resid; + riov = &iov; + riov_size = 1; + rv = st_writev_resid(fd, &riov, &riov_size, timeout); + *resid = iov.iov_len; + return rv; +} + + +ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout) +{ + size_t resid = nbyte; + return st_write_resid(fd, buf, &resid, timeout) == 0 ? + (ssize_t) (nbyte - resid) : -1; +} + + +ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) +{ + ssize_t n, rv; + size_t nleft, nbyte; + int index, iov_cnt; + struct iovec *tmp_iov; + struct iovec local_iov[_LOCAL_MAXIOV]; + + /* Calculate the total number of bytes to be sent */ + nbyte = 0; + for (index = 0; index < iov_size; index++) + nbyte += iov[index].iov_len; + + rv = (ssize_t)nbyte; + nleft = nbyte; + tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ + iov_cnt = iov_size; + + while (nleft > 0) { + if (iov_cnt == 1) { + if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) + rv = -1; + break; + } + if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) { + rv = -1; + break; + } + } else { + if ((size_t) n == nleft) + break; + nleft -= n; + /* Find the next unwritten vector */ + n = (ssize_t)(nbyte - nleft); + for (index = 0; (size_t) n >= iov[index].iov_len; index++) + n -= iov[index].iov_len; + + if (tmp_iov == iov) { + /* Must copy iov's around */ + if (iov_size - index <= _LOCAL_MAXIOV) { + tmp_iov = local_iov; + } else { + tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec)); + if (tmp_iov == NULL) + return -1; + } + } + + /* Fill in the first partial read */ + tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[n]); + tmp_iov[0].iov_len = iov[index].iov_len - n; + index++; + /* Copy the remaining vectors */ + for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) { + tmp_iov[iov_cnt].iov_base = iov[index].iov_base; + tmp_iov[iov_cnt].iov_len = iov[index].iov_len; + } + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + rv = -1; + break; + } + } + + if (tmp_iov != iov && tmp_iov != local_iov) + free(tmp_iov); + + return rv; +} + + +int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) +{ + ssize_t n; + + while (*iov_size > 0) { + if (*iov_size == 1) + n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); + else + n = writev(fd->osfd, *iov, *iov_size); + if (n < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + } else { + while ((size_t) n >= (*iov)->iov_len) { + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) + break; + } + if (*iov_size == 0) + break; + (*iov)->iov_base = (char *) (*iov)->iov_base + n; + (*iov)->iov_len -= n; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) + return -1; + } + + return 0; +} + + +/* + * Simple I/O functions for UDP. + */ +int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout) +{ + int n; + + while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return n; +} + + +int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout) +{ + int n; + + while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) + return -1; + } + + return n; +} + + +int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeout) +{ + int n; + + while ((n = recvmsg(fd->osfd, msg, flags)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) + return -1; + } + + return n; +} + + +int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, st_utime_t timeout) +{ + int n; + + while ((n = sendmsg(fd->osfd, msg, flags)) < 0) { + if (errno == EINTR) + continue; + if (!_IO_NOT_READY_ERROR) + return -1; + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) + return -1; + } + + return n; +} + + + +/* + * To open FIFOs or other special files. + */ +_st_netfd_t *st_open(const char *path, int oflags, mode_t mode) +{ + int osfd, err; + _st_netfd_t *newfd; + + while ((osfd = open(path, oflags | O_NONBLOCK, mode)) < 0) { + if (errno != EINTR) + return NULL; + } + + newfd = _st_netfd_new(osfd, 0, 0); + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; +} + diff --git a/trunk/3rdparty/st-srs/key.c b/trunk/3rdparty/st-srs/key.c new file mode 100644 index 000000000..5e64022c0 --- /dev/null +++ b/trunk/3rdparty/st-srs/key.c @@ -0,0 +1,121 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include "common.h" + + +/* + * Destructor table for per-thread private data + */ +static _st_destructor_t _st_destructors[ST_KEYS_MAX]; +static int key_max = 0; + + +/* + * Return a key to be used for thread specific data + */ +int st_key_create(int *keyp, _st_destructor_t destructor) +{ + if (key_max >= ST_KEYS_MAX) { + errno = EAGAIN; + return -1; + } + + *keyp = key_max++; + _st_destructors[*keyp] = destructor; + + return 0; +} + + +int st_key_getlimit(void) +{ + return ST_KEYS_MAX; +} + + +int st_thread_setspecific(int key, void *value) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (key < 0 || key >= key_max) { + errno = EINVAL; + return -1; + } + + if (value != me->private_data[key]) { + /* free up previously set non-NULL data value */ + if (me->private_data[key] && _st_destructors[key]) { + (*_st_destructors[key])(me->private_data[key]); + } + me->private_data[key] = value; + } + + return 0; +} + + +void *st_thread_getspecific(int key) +{ + if (key < 0 || key >= key_max) + return NULL; + + return ((_ST_CURRENT_THREAD())->private_data[key]); +} + + +/* + * Free up all per-thread private data + */ +void _st_thread_cleanup(_st_thread_t *thread) +{ + int key; + + for (key = 0; key < key_max; key++) { + if (thread->private_data[key] && _st_destructors[key]) { + (*_st_destructors[key])(thread->private_data[key]); + thread->private_data[key] = NULL; + } + } +} + diff --git a/trunk/3rdparty/st-srs/libst.def b/trunk/3rdparty/st-srs/libst.def new file mode 100644 index 000000000..6eaf149a9 --- /dev/null +++ b/trunk/3rdparty/st-srs/libst.def @@ -0,0 +1,51 @@ +EXPORTS + st_accept @62 + st_cond_broadcast @63 + st_cond_destroy @64 + st_cond_new @65 + st_cond_signal @66 + st_cond_timedwait @67 + st_cond_wait @68 + st_connect @69 + st_getfdlimit @70 + st_init @71 + st_key_create @72 + st_key_getlimit @73 + st_mutex_destroy @74 + st_mutex_lock @75 + st_mutex_new @76 + st_mutex_trylock @77 + st_mutex_unlock @78 + st_netfd_close @79 + st_netfd_fileno @80 + st_netfd_free @81 + st_netfd_getspecific @82 + st_netfd_open @83 + st_netfd_open_socket @84 + st_netfd_poll @85 + st_netfd_serialize_accept @86 + st_netfd_setspecific @87 + st_open @88 + st_poll @89 + st_randomize_stacks @90 + st_read @91 + st_read_fully @92 + st_read_resid @93 + st_recvfrom @94 + st_sendto @95 + st_sleep @96 + st_thread_create @97 + st_thread_exit @98 + st_thread_getspecific @99 + st_thread_interrupt @100 + st_thread_join @101 + st_thread_self @102 + st_thread_setspecific @103 + st_time @104 + st_timecache_set @105 + st_usleep @106 + st_utime @107 + st_utime_last_clock @108 + st_write @109 + st_write_resid @110 + st_writev @111 diff --git a/trunk/3rdparty/st-srs/md.S b/trunk/3rdparty/st-srs/md.S new file mode 100644 index 000000000..2ef9c41f7 --- /dev/null +++ b/trunk/3rdparty/st-srs/md.S @@ -0,0 +1,644 @@ + +/* If user disable the ASM, such as avoiding bugs in ASM, donot compile it. */ +#if !defined(MD_ST_NO_ASM) + +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + */ + +#if defined(__ia64__) + + /****************************************************************/ + + /* + * The internal __jmp_buf layout is different from one used + * by setjmp()/longjmp(). + * + * Offset Description + * ------ ----------- + * 0x000 stack pointer (r12) + * 0x008 gp (r1) + * 0x010 caller's unat + * 0x018 fpsr + * 0x020 r4 + * 0x028 r5 + * 0x030 r6 + * 0x038 r7 + * 0x040 rp (b0) + * 0x048 b1 + * 0x050 b2 + * 0x058 b3 + * 0x060 b4 + * 0x068 b5 + * 0x070 ar.pfs + * 0x078 ar.lc + * 0x080 pr + * 0x088 ar.bsp + * 0x090 ar.unat + * 0x098 &__jmp_buf + * 0x0a0 ar.rsc + * 0x0a8 ar.rnat + * 0x0b0 f2 + * 0x0c0 f3 + * 0x0d0 f4 + * 0x0e0 f5 + * 0x0f0 f16 + * 0x100 f17 + * 0x110 f18 + * 0x120 f19 + * 0x130 f20 + * 0x130 f21 + * 0x140 f22 + * 0x150 f23 + * 0x160 f24 + * 0x170 f25 + * 0x180 f26 + * 0x190 f27 + * 0x1a0 f28 + * 0x1b0 f29 + * 0x1c0 f30 + * 0x1d0 f31 + * + * Note that the address of __jmp_buf is saved but not used: we assume + * that the jmp_buf data structure is never moved around in memory. + */ + + /* + * Implemented according to "IA-64 Software Conventions and Runtime + * Architecture Guide", Chapter 10: "Context Management". + */ + + .text + .psr abi64 + .psr lsb + .lsb + + /* _st_md_cxt_save(__jmp_buf env) */ + .align 32 + .global _st_md_cxt_save + .proc _st_md_cxt_save + _st_md_cxt_save: + alloc r14 = ar.pfs,1,0,0,0 + mov r16 = ar.unat + ;; + mov r17 = ar.fpsr + mov r2 = in0 + add r3 = 8,in0 + ;; + st8.spill.nta [r2] = sp,16 // r12 (sp) + ;; + st8.spill.nta [r3] = gp,16 // r1 (gp) + ;; + st8.nta [r2] = r16,16 // save caller's unat + st8.nta [r3] = r17,16 // save fpsr + add r8 = 0xb0,in0 + ;; + st8.spill.nta [r2] = r4,16 // r4 + ;; + st8.spill.nta [r3] = r5,16 // r5 + add r9 = 0xc0,in0 + ;; + stf.spill.nta [r8] = f2,32 + stf.spill.nta [r9] = f3,32 + mov r15 = rp + ;; + stf.spill.nta [r8] = f4,32 + stf.spill.nta [r9] = f5,32 + mov r17 = b1 + ;; + stf.spill.nta [r8] = f16,32 + stf.spill.nta [r9] = f17,32 + mov r18 = b2 + ;; + stf.spill.nta [r8] = f18,32 + stf.spill.nta [r9] = f19,32 + mov r19 = b3 + ;; + stf.spill.nta [r8] = f20,32 + stf.spill.nta [r9] = f21,32 + mov r20 = b4 + ;; + stf.spill.nta [r8] = f22,32 + stf.spill.nta [r9] = f23,32 + mov r21 = b5 + ;; + stf.spill.nta [r8] = f24,32 + stf.spill.nta [r9] = f25,32 + mov r22 = ar.lc + ;; + stf.spill.nta [r8] = f26,32 + stf.spill.nta [r9] = f27,32 + mov r24 = pr + ;; + stf.spill.nta [r8] = f28,32 + stf.spill.nta [r9] = f29,32 + ;; + stf.spill.nta [r8] = f30 + stf.spill.nta [r9] = f31 + + st8.spill.nta [r2] = r6,16 // r6 + ;; + st8.spill.nta [r3] = r7,16 // r7 + ;; + mov r23 = ar.bsp + mov r25 = ar.unat + + st8.nta [r2] = r15,16 // b0 + st8.nta [r3] = r17,16 // b1 + ;; + st8.nta [r2] = r18,16 // b2 + st8.nta [r3] = r19,16 // b3 + mov r26 = ar.rsc + ;; + st8.nta [r2] = r20,16 // b4 + st8.nta [r3] = r21,16 // b5 + ;; + st8.nta [r2] = r14,16 // ar.pfs + st8.nta [r3] = r22,16 // ar.lc + ;; + st8.nta [r2] = r24,16 // pr + st8.nta [r3] = r23,16 // ar.bsp + ;; + st8.nta [r2] = r25,16 // ar.unat + st8.nta [r3] = in0,16 // &__jmp_buf (just in case) + ;; + st8.nta [r2] = r26 // ar.rsc + ;; + flushrs // flush dirty regs to backing store + ;; + and r27 = ~0x3,r26 // clear ar.rsc.mode + ;; + mov ar.rsc = r27 // put RSE in enforced lazy mode + ;; + mov r28 = ar.rnat + ;; + st8.nta [r3] = r28 // ar.rnat + mov ar.rsc = r26 // restore ar.rsc + ;; + mov r8 = 0 + br.ret.sptk.few b0 + .endp _st_md_cxt_save + + + /****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ + .global _st_md_cxt_restore + .proc _st_md_cxt_restore + _st_md_cxt_restore: + alloc r8 = ar.pfs,2,0,0,0 + add r2 = 0x88,in0 // r2 <- &jmpbuf.ar_bsp + mov r16 = ar.rsc + ;; + flushrs // flush dirty regs to backing store + ;; + and r17 = ~0x3,r16 // clear ar.rsc.mode + ;; + mov ar.rsc = r17 // put RSE in enforced lazy mode + ;; + invala // invalidate the ALAT + ;; + ld8 r23 = [r2],8 // r23 <- jmpbuf.ar_bsp + ;; + mov ar.bspstore = r23 // write BSPSTORE + ld8 r25 = [r2],24 // r25 <- jmpbuf.ar_unat + ;; + ld8 r26 = [r2],-8 // r26 <- jmpbuf.ar_rnat + ;; + mov ar.rnat = r26 // write RNAT + ld8 r27 = [r2] // r27 <- jmpbuf.ar_rsc + ;; + mov ar.rsc = r27 // write RSE control + mov r2 = in0 + ;; + mov ar.unat = r25 // write ar.unat + add r3 = 8,in0 + ;; + ld8.fill.nta sp = [r2],16 // r12 (sp) + ld8.fill.nta gp = [r3],16 // r1 (gp) + ;; + ld8.nta r16 = [r2],16 // caller's unat + ld8.nta r17 = [r3],16 // fpsr + ;; + ld8.fill.nta r4 = [r2],16 // r4 + ld8.fill.nta r5 = [r3],16 // r5 + ;; + ld8.fill.nta r6 = [r2],16 // r6 + ld8.fill.nta r7 = [r3],16 // r7 + ;; + mov ar.unat = r16 // restore caller's unat + mov ar.fpsr = r17 // restore fpsr + ;; + ld8.nta r16 = [r2],16 // b0 + ld8.nta r17 = [r3],16 // b1 + ;; + ld8.nta r18 = [r2],16 // b2 + ld8.nta r19 = [r3],16 // b3 + ;; + ld8.nta r20 = [r2],16 // b4 + ld8.nta r21 = [r3],16 // b5 + ;; + ld8.nta r11 = [r2],16 // ar.pfs + ld8.nta r22 = [r3],72 // ar.lc + ;; + ld8.nta r24 = [r2],48 // pr + mov b0 = r16 + ;; + ldf.fill.nta f2 = [r2],32 + ldf.fill.nta f3 = [r3],32 + mov b1 = r17 + ;; + ldf.fill.nta f4 = [r2],32 + ldf.fill.nta f5 = [r3],32 + mov b2 = r18 + ;; + ldf.fill.nta f16 = [r2],32 + ldf.fill.nta f17 = [r3],32 + mov b3 = r19 + ;; + ldf.fill.nta f18 = [r2],32 + ldf.fill.nta f19 = [r3],32 + mov b4 = r20 + ;; + ldf.fill.nta f20 = [r2],32 + ldf.fill.nta f21 = [r3],32 + mov b5 = r21 + ;; + ldf.fill.nta f22 = [r2],32 + ldf.fill.nta f23 = [r3],32 + mov ar.lc = r22 + ;; + ldf.fill.nta f24 = [r2],32 + ldf.fill.nta f25 = [r3],32 + cmp.eq p6,p7 = 0,in1 + ;; + ldf.fill.nta f26 = [r2],32 + ldf.fill.nta f27 = [r3],32 + mov ar.pfs = r11 + ;; + ldf.fill.nta f28 = [r2],32 + ldf.fill.nta f29 = [r3],32 + ;; + ldf.fill.nta f30 = [r2] + ldf.fill.nta f31 = [r3] + (p6) mov r8 = 1 + (p7) mov r8 = in1 + + mov pr = r24,-1 + br.ret.sptk.few b0 + .endp _st_md_cxt_restore + + /****************************************************************/ + + + + + + + + + +#elif defined(__i386__) + + /****************************************************************/ + + /* + * Internal __jmp_buf layout + */ + #define JB_BX 0 + #define JB_SI 1 + #define JB_DI 2 + #define JB_BP 3 + #define JB_SP 4 + #define JB_PC 5 + + .file "md.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ + .globl _st_md_cxt_save + .type _st_md_cxt_save, @function + .align 16 + _st_md_cxt_save: + movl 4(%esp), %eax + + /* + * Save registers. + */ + movl %ebx, (JB_BX*4)(%eax) + movl %esi, (JB_SI*4)(%eax) + movl %edi, (JB_DI*4)(%eax) + /* Save SP */ + leal 4(%esp), %ecx + movl %ecx, (JB_SP*4)(%eax) + /* Save PC we are returning to */ + movl 0(%esp), %ecx + movl %ecx, (JB_PC*4)(%eax) + /* Save caller frame pointer */ + movl %ebp, (JB_BP*4)(%eax) + xorl %eax, %eax + ret + .size _st_md_cxt_save, .-_st_md_cxt_save + + + /****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ + .globl _st_md_cxt_restore + .type _st_md_cxt_restore, @function + .align 16 + _st_md_cxt_restore: + /* First argument is jmp_buf */ + movl 4(%esp), %ecx + /* Second argument is return value */ + movl 8(%esp), %eax + /* Set the return address */ + movl (JB_PC*4)(%ecx), %edx + /* + * Restore registers. + */ + movl (JB_BX*4)(%ecx), %ebx + movl (JB_SI*4)(%ecx), %esi + movl (JB_DI*4)(%ecx), %edi + movl (JB_BP*4)(%ecx), %ebp + movl (JB_SP*4)(%ecx), %esp + testl %eax, %eax + jnz 1f + incl %eax + /* Jump to saved PC */ + 1: jmp *%edx + .size _st_md_cxt_restore, .-_st_md_cxt_restore + + /****************************************************************/ + + + + + + + + + + +#elif defined(__amd64__) || defined(__x86_64__) + + /****************************************************************/ + + /* + * Internal __jmp_buf layout + */ + #define JB_RBX 0 + #define JB_RBP 1 + #define JB_R12 2 + #define JB_R13 3 + #define JB_R14 4 + #define JB_R15 5 + #define JB_RSP 6 + #define JB_PC 7 + + .file "md.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ + .globl _st_md_cxt_save + .type _st_md_cxt_save, @function + .align 16 + _st_md_cxt_save: + /* + * Save registers. + */ + movq %rbx, (JB_RBX*8)(%rdi) + movq %rbp, (JB_RBP*8)(%rdi) + movq %r12, (JB_R12*8)(%rdi) + movq %r13, (JB_R13*8)(%rdi) + movq %r14, (JB_R14*8)(%rdi) + movq %r15, (JB_R15*8)(%rdi) + /* Save SP */ + leaq 8(%rsp), %rdx + movq %rdx, (JB_RSP*8)(%rdi) + /* Save PC we are returning to */ + movq (%rsp), %rax + movq %rax, (JB_PC*8)(%rdi) + xorq %rax, %rax + ret + .size _st_md_cxt_save, .-_st_md_cxt_save + + + /****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ + .globl _st_md_cxt_restore + .type _st_md_cxt_restore, @function + .align 16 + _st_md_cxt_restore: + /* + * Restore registers. + */ + movq (JB_RBX*8)(%rdi), %rbx + movq (JB_RBP*8)(%rdi), %rbp + movq (JB_R12*8)(%rdi), %r12 + movq (JB_R13*8)(%rdi), %r13 + movq (JB_R14*8)(%rdi), %r14 + movq (JB_R15*8)(%rdi), %r15 + /* Set return value */ + test %esi, %esi + mov $01, %eax + cmove %eax, %esi + mov %esi, %eax + movq (JB_PC*8)(%rdi), %rdx + movq (JB_RSP*8)(%rdi), %rsp + /* Jump to saved PC */ + jmpq *%rdx + .size _st_md_cxt_restore, .-_st_md_cxt_restore + + /****************************************************************/ + + + + + + + + + + +#elif defined(__aarch64__) + + /****************************************************************/ + /* https://github.com/ossrs/srs/issues/1282#issuecomment-445539513 */ + + #define JB_X19 0 + #define JB_X20 1 + #define JB_X21 2 + #define JB_X22 3 + #define JB_X23 4 + #define JB_X24 5 + #define JB_X25 6 + #define JB_X26 7 + #define JB_X27 8 + #define JB_X28 9 + #define JB_X29 10 + #define JB_LR 11 + #define JB_SP 13 + + #define JB_D8 14 + #define JB_D9 15 + #define JB_D10 16 + #define JB_D11 17 + #define JB_D12 18 + #define JB_D13 19 + #define JB_D14 20 + #define JB_D15 21 + + .file "md.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ + .globl _st_md_cxt_save + .type _st_md_cxt_save, %function + .align 4 + _st_md_cxt_save: + stp x19, x20, [x0, #JB_X19<<3] + stp x21, x22, [x0, #JB_X21<<3] + stp x23, x24, [x0, #JB_X23<<3] + stp x25, x26, [x0, #JB_X25<<3] + stp x27, x28, [x0, #JB_X27<<3] + stp x29, x30, [x0, #JB_X29<<3] + + stp d8, d9, [x0, #JB_D8<<3] + stp d10, d11, [x0, #JB_D10<<3] + stp d12, d13, [x0, #JB_D12<<3] + stp d14, d15, [x0, #JB_D14<<3] + mov x2, sp + str x2, [x0, #JB_SP<<3] + + mov x0, #0 + ret + .size _st_md_cxt_save, .-_st_md_cxt_save + + /****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ + .globl _st_md_cxt_restore + .type _st_md_cxt_restore, %function + .align 4 + _st_md_cxt_restore: + ldp x19, x20, [x0, #JB_X19<<3] + ldp x21, x22, [x0, #JB_X21<<3] + ldp x23, x24, [x0, #JB_X23<<3] + ldp x25, x26, [x0, #JB_X25<<3] + ldp x27, x28, [x0, #JB_X27<<3] + + ldp x29, x30, [x0, #JB_X29<<3] + + ldp d8, d9, [x0, #JB_D8<<3] + ldp d10, d11, [x0, #JB_D10<<3] + ldp d12, d13, [x0, #JB_D12<<3] + ldp d14, d15, [x0, #JB_D14<<3] + + ldr x5, [x0, #JB_SP<<3] + mov sp, x5 + + cmp x1, #0 + mov x0, #1 + csel x0, x1, x0, ne + /* Use br instead of ret because ret is guaranteed to mispredict */ + br x30 + .size _st_md_cxt_restore, .-_st_md_cxt_restore + + /****************************************************************/ + + + + + + + + + + +#elif defined(__arm__) + + /****************************************************************/ + /* https://github.com/ossrs/srs/issues/1282#issuecomment-445539513 */ + + /* Register list for a ldm/stm instruction to load/store + the general registers from a __jmp_buf. */ + # define JMP_BUF_REGLIST {v1-v6, sl, fp, sp, lr} + + .file "md.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ + .globl _st_md_cxt_save + .type _st_md_cxt_save, %function + .align 2 + _st_md_cxt_save: + mov ip, r0 + + /* Save registers */ + stmia ip!, JMP_BUF_REGLIST + + #ifdef __VFP_FP__ + /* Store the VFP registers. */ + /* Following instruction is vstmia ip!, {d8-d15}. */ + stc p11, cr8, [ip], #64 + #endif + + #ifdef __IWMMXT__ + /* Save the call-preserved iWMMXt registers. */ + /* Following instructions are wstrd wr10, [ip], #8 (etc.) */ + stcl p1, cr10, [r12], #8 + stcl p1, cr11, [r12], #8 + stcl p1, cr12, [r12], #8 + stcl p1, cr13, [r12], #8 + stcl p1, cr14, [r12], #8 + stcl p1, cr15, [r12], #8 + #endif + + mov r0, #0 + bx lr + + .size _st_md_cxt_save, .-_st_md_cxt_save + + /****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ + .globl _st_md_cxt_restore + .type _st_md_cxt_restore, %function + .align 2 + _st_md_cxt_restore: + mov ip, r0 + + /* Restore registers */ + ldmia ip!, JMP_BUF_REGLIST + + #ifdef __VFP_FP__ + /* Restore the VFP registers. */ + /* Following instruction is vldmia ip!, {d8-d15}. */ + ldc p11, cr8, [r12], #64 + #endif + + #ifdef __IWMMXT__ + /* Restore the call-preserved iWMMXt registers. */ + /* Following instructions are wldrd wr10, [ip], #8 (etc.) */ + ldcl p1, cr10, [r12], #8 + ldcl p1, cr11, [r12], #8 + ldcl p1, cr12, [r12], #8 + ldcl p1, cr13, [r12], #8 + ldcl p1, cr14, [r12], #8 + ldcl p1, cr15, [r12], #8 + #endif + + movs r0, r1 /* get the return value in place */ + moveq r0, #1 /* can't let setjmp() return zero! */ + bx lr + + .size _st_md_cxt_restore, .-_st_md_cxt_restore + + /****************************************************************/ + +#endif + +#endif diff --git a/trunk/3rdparty/st-srs/md.h b/trunk/3rdparty/st-srs/md.h new file mode 100644 index 000000000..dc4ef54c9 --- /dev/null +++ b/trunk/3rdparty/st-srs/md.h @@ -0,0 +1,641 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#ifndef __ST_MD_H__ +#define __ST_MD_H__ + +#if defined(ETIMEDOUT) && !defined(ETIME) + #define ETIME ETIMEDOUT +#endif + +#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON) + #define MAP_ANON MAP_ANONYMOUS +#endif + +#ifndef MAP_FAILED + #define MAP_FAILED -1 +#endif + +/***************************************** + * Platform specifics + */ + +#if defined (AIX) + + #define MD_STACK_GROWS_DOWN + #define MD_USE_SYSV_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #ifndef MD_HAVE_SOCKLEN_T + #define MD_HAVE_SOCKLEN_T + #define socklen_t unsigned long + #endif + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[3] = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + timebasestruct_t rt; \ + (void) read_real_time(&rt, TIMEBASE_SZ); \ + (void) time_base_to_time(&rt, TIMEBASE_SZ); \ + return (rt.tb_high * 1000000LL + rt.tb_low / 1000) + +#elif defined (CYGWIN) + + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_NOT_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) setjmp(env) + #define MD_LONGJMP(env, val) longjmp(env, val) + + #define MD_JB_SP 7 + + #define MD_GET_SP(_t) (_t)->context[MD_JB_SP] + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + MD_GET_SP(_thread) = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (DARWIN) + + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + #define MD_HAVE_SOCKLEN_T + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #if defined(__ppc__) + #define MD_JB_SP 0 + #elif defined(__i386__) + #define MD_JB_SP 9 + #elif defined(__x86_64__) + #define MD_JB_SP 4 + #else + #error Unknown CPU architecture + #endif + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + *((long *)&((_thread)->context[MD_JB_SP])) = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (FREEBSD) + + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #if defined(__i386__) + #define MD_JB_SP 2 + #elif defined(__alpha__) + #define MD_JB_SP 34 + #elif defined(__amd64__) + #define MD_JB_SP 2 + #else + #error Unknown CPU architecture + #endif + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[0]._jb[MD_JB_SP] = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (HPUX) + + #define MD_STACK_GROWS_UP + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #ifndef __LP64__ + /* 32-bit mode (ILP32 data model) */ + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + ((long *)((_thread)->context))[1] = (long) (_sp); \ + ST_END_MACRO + #else + /* 64-bit mode (LP64 data model) */ + #define MD_STACK_PAD_SIZE 256 + /* Last stack frame must be preserved */ + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + memcpy((char *)(_sp) - MD_STACK_PAD_SIZE, \ + ((char **)((_thread)->context))[1] - MD_STACK_PAD_SIZE, \ + MD_STACK_PAD_SIZE); \ + ((long *)((_thread)->context))[1] = (long) (_sp); \ + ST_END_MACRO + #endif /* !__LP64__ */ + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (IRIX) + + #include + + #define MD_STACK_GROWS_DOWN + #define MD_USE_SYSV_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) setjmp(env) + #define MD_LONGJMP(env, val) longjmp(env, val) + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + (void) MD_SETJMP((_thread)->context); \ + (_thread)->context[JB_SP] = (long) (_sp); \ + (_thread)->context[JB_PC] = (long) _main; \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + static int inited = 0; \ + static clockid_t clock_id = CLOCK_SGI_CYCLE; \ + struct timespec ts; \ + if (!inited) { \ + if (syssgi(SGI_CYCLECNTR_SIZE) < 64) \ + clock_id = CLOCK_REALTIME; \ + inited = 1; \ + } \ + (void) clock_gettime(clock_id, &ts); \ + return (ts.tv_sec * 1000000LL + ts.tv_nsec / 1000) + + /* + * Cap the stack by zeroing out the saved return address register + * value. This allows libexc, used by SpeedShop, to know when to stop + * backtracing since it won't find main, start, or any other known + * stack root function in a state thread's stack. Without this libexc + * traces right off the stack and crashes. + * The function preamble stores ra at 8(sp), this stores zero there. + * N.B. This macro is compiler/ABI dependent. It must change if ANY more + * automatic variables are added to the _st_thread_main() routine, because + * the address where ra is stored will change. + */ + #if !defined(__GNUC__) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32 + #define MD_CAP_STACK(var_addr) \ + (((volatile __uint64_t *)(var_addr))[1] = 0) + #endif + +#elif defined (LINUX) + + /* + * These are properties of the linux kernel and are the same on every + * flavor and architecture. + */ + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_NOT_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + /* + * Modern GNU/Linux is Posix.1g compliant. + */ + #define MD_HAVE_SOCKLEN_T + + /* + * All architectures and flavors of linux have the gettimeofday + * function but if you know of a faster way, use it. + */ + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + + #if defined(__ia64__) + #define MD_STACK_GROWS_DOWN + + /* + * IA-64 architecture. Besides traditional memory call stack, IA-64 + * uses general register stack. Thus each thread needs a backing store + * for register stack in addition to memory stack. Standard + * setjmp()/longjmp() cannot be used for thread context switching + * because their implementation implicitly assumes that only one + * register stack exists. + */ + #ifdef USE_LIBC_SETJMP + #undef USE_LIBC_SETJMP + #endif + #define MD_USE_BUILTIN_SETJMP + + #define MD_STACK_PAD_SIZE 128 + /* Last register stack frame must be preserved */ + #define MD_INIT_CONTEXT(_thread, _sp, _bsp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + memcpy((char *)(_bsp) - MD_STACK_PAD_SIZE, \ + (char *)(_thread)->context[0].__jmpbuf[17] - MD_STACK_PAD_SIZE, \ + MD_STACK_PAD_SIZE); \ + (_thread)->context[0].__jmpbuf[0] = (long) (_sp); \ + (_thread)->context[0].__jmpbuf[17] = (long) (_bsp); \ + ST_END_MACRO + + #elif defined(__mips__) + #define MD_STACK_GROWS_DOWN + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + MD_SETJMP((_thread)->context); \ + _thread->context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \ + _thread->context[0].__jmpbuf[0].__sp = _sp; \ + ST_END_MACRO + + #else /* Not IA-64 or mips */ + + /* + * On linux, there are a few styles of jmpbuf format. These vary based + * on architecture/glibc combination. + * + * Most of the glibc based toggles were lifted from: + * mozilla/nsprpub/pr/include/md/_linux.h + */ + + /* + * Starting with glibc 2.4, JB_SP definitions are not public anymore. + * They, however, can still be found in glibc source tree in + * architecture-specific "jmpbuf-offsets.h" files. + * Most importantly, the content of jmp_buf is mangled by setjmp to make + * it completely opaque (the mangling can be disabled by setting the + * LD_POINTER_GUARD environment variable before application execution). + * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore + * functions as a setjmp/longjmp replacement wherever they are available + * unless USE_LIBC_SETJMP is defined. + */ + + #if defined(__powerpc__) + #define MD_STACK_GROWS_DOWN + + #if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) + #ifndef JB_GPR1 + #define JB_GPR1 0 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_GPR1] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on powerpc" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__misc[0] + #endif /* glibc 2.1 or later */ + + #elif defined(__alpha) + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 8 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on alpha" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + #endif + + #elif defined(__mc68000__) + #define MD_STACK_GROWS_DOWN + + /* m68k still uses old style sigjmp_buf */ + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + + #elif defined(__sparc__) + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 0 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glic on sparc -- also using odd mozilla derived __fp" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__fp + #endif + + #elif defined(__i386__) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BUILTIN_SETJMP + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 4 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on i386" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + #endif + + #elif defined(__amd64__) || defined(__x86_64__) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BUILTIN_SETJMP + + #ifndef JB_RSP + #define JB_RSP 6 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP] + + #elif defined(__aarch64__) + /* https://github.com/ossrs/state-threads/issues/9 */ + #define MD_STACK_GROWS_DOWN + #define MD_USE_BUILTIN_SETJMP + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[13] + + #elif defined(__arm__) + #define MD_STACK_GROWS_DOWN + /* https://github.com/ossrs/state-threads/issues/1#issuecomment-244648573 */ + #define MD_USE_BUILTIN_SETJMP + + /* force to use glibc solution, hack the guard jmpbuf from michaeltalyansky */ + #ifdef USE_LIBC_SETJMP + #undef MD_USE_BUILTIN_SETJMP + #endif + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + /* Merge from https://github.com/michaeltalyansky/state-threads/commit/56554a5c425aee8e7a73782eae23d74d83c4120a */ + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8] + #else + #error "ARM/Linux pre-glibc2 not supported yet" + #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + + #elif defined(__s390__) + #define MD_STACK_GROWS_DOWN + + /* There is no JB_SP in glibc at this time. (glibc 2.2.5) + */ + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__gregs[9] + + #elif defined(__hppa__) + #define MD_STACK_GROWS_UP + + /* yes, this is gross, unfortunately at the moment (2002/08/01) there is + * a bug in hppa's glibc header definition for JB_SP, so we can't + * use that... + */ + #define MD_GET_SP(_t) (*(long *)(((char *)&(_t)->context[0].__jmpbuf[0]) + 76)) + + #else + #error "Unknown CPU architecture" + #endif /* Cases with common MD_INIT_CONTEXT and different SP locations */ + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + MD_GET_SP(_thread) = (long) (_sp); \ + ST_END_MACRO + + #endif /* Cases with different MD_INIT_CONTEXT */ + + #if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP) + #define MD_SETJMP(env) _st_md_cxt_save(env) + #define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) + + extern int _st_md_cxt_save(jmp_buf env); + extern void _st_md_cxt_restore(jmp_buf env, int val); + #else + #define MD_SETJMP(env) setjmp(env) + #define MD_LONGJMP(env, val) longjmp(env, val) + #endif + +#elif defined (NETBSD) + + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + #define MD_HAVE_SOCKLEN_T + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #if defined(__i386__) + #define MD_JB_SP 2 + #elif defined(__alpha__) + #define MD_JB_SP 34 + #elif defined(__sparc__) + #define MD_JB_SP 0 + #elif defined(__vax__) + #define MD_JB_SP 2 + #else + #error Unknown CPU architecture + #endif + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[MD_JB_SP] = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (OPENBSD) + + #define MD_STACK_GROWS_DOWN + #define MD_USE_BSD_ANON_MMAP + #define MD_ACCEPT_NB_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #if defined(__i386__) + #define MD_JB_SP 2 + #elif defined(__alpha__) + #define MD_JB_SP 34 + #elif defined(__sparc__) + #define MD_JB_SP 0 + #elif defined(__amd64__) + #define MD_JB_SP 6 + #else + #error Unknown CPU architecture + #endif + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[MD_JB_SP] = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (OSF1) + + #include + + #define MD_STACK_GROWS_DOWN + #define MD_USE_SYSV_ANON_MMAP + #define MD_ACCEPT_NB_NOT_INHERITED + #define MD_ALWAYS_UNSERIALIZED_ACCEPT + + #define MD_SETJMP(env) _setjmp(env) + #define MD_LONGJMP(env, val) _longjmp(env, val) + + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + ((struct sigcontext *)((_thread)->context))->sc_sp = (long) (_sp); \ + ST_END_MACRO + + #define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#elif defined (SOLARIS) + + #include + extern int getpagesize(void); + + #define MD_STACK_GROWS_DOWN + #define MD_USE_SYSV_ANON_MMAP + #define MD_ACCEPT_NB_NOT_INHERITED + + #define MD_SETJMP(env) setjmp(env) + #define MD_LONGJMP(env, val) longjmp(env, val) + + #if defined(sparc) || defined(__sparc) + #ifdef _LP64 + #define MD_STACK_PAD_SIZE 4095 + #endif + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + (void) MD_SETJMP((_thread)->context); \ + (_thread)->context[1] = (long) (_sp); \ + (_thread)->context[2] = (long) _main; \ + ST_END_MACRO + #elif defined(i386) || defined(__i386) + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + (void) MD_SETJMP((_thread)->context); \ + (_thread)->context[4] = (long) (_sp); \ + (_thread)->context[5] = (long) _main; \ + ST_END_MACRO + #elif defined(__amd64__) + #define MD_INIT_CONTEXT(_thread, _sp, _main) \ + ST_BEGIN_MACRO \ + if (MD_SETJMP((_thread)->context)) \ + _main(); \ + (_thread)->context[6] = (long) (_sp); \ + ST_END_MACRO + #else + #error Unknown CPU architecture + #endif + + #define MD_GET_UTIME() \ + return (gethrtime() / 1000) + +#else + #error Unknown OS +#endif /* OS */ + +#if !defined(MD_HAVE_POLL) && !defined(MD_DONT_HAVE_POLL) + #define MD_HAVE_POLL +#endif + +#ifndef MD_STACK_PAD_SIZE + #define MD_STACK_PAD_SIZE 128 +#endif + +#if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t) + #define socklen_t int +#endif + +#ifndef MD_CAP_STACK + #define MD_CAP_STACK(var_addr) +#endif + +#endif /* !__ST_MD_H__ */ + diff --git a/trunk/3rdparty/st-srs/osguess.sh b/trunk/3rdparty/st-srs/osguess.sh new file mode 100644 index 000000000..531681efe --- /dev/null +++ b/trunk/3rdparty/st-srs/osguess.sh @@ -0,0 +1,45 @@ +# +# This script can be used to automatically guess target OS. +# It requires the config.guess utility which is a part of GNU Autoconf. +# GNU Autoconf can be downloaded from ftp://ftp.gnu.org/gnu/autoconf/ +# +# Use "default" as a make target for automatic builds. +# + + +# Specify path to the config.guess utility (unless set via environment) +#CONFIG_GUESS_PATH= + + +if [ x"$CONFIG_GUESS_PATH" = x ]; then + echo "Error: CONFIG_GUESS_PATH variable is not set" + exit 1 +fi + +if [ ! -f "$CONFIG_GUESS_PATH/config.guess" ]; then + echo "Can't find $CONFIG_GUESS_PATH/config.guess utility. Wrong path?" + exit 1 +fi + +sys_info=`/bin/sh $CONFIG_GUESS_PATH/config.guess` + +echo "Building for $sys_info" + +case "$sys_info" in + *-ibm-aix4* ) OS=AIX ;; + *-freebsd* ) OS=FREEBSD ;; + hppa*-hp-hpux11*) OS=HPUX ;; + *-sgi-irix6* ) OS=IRIX ;; + *-linux* ) OS=LINUX ;; + *-netbsd* ) OS=NETBSD ;; + *-openbsd* ) OS=OPENBSD ;; + *-dec-osf* ) OS=OSF1 ;; + *-solaris2* ) OS=SOLARIS ;; + *-darwin* ) OS=DARWIN ;; + * ) OS= + echo "Sorry, unsupported OS" + exit 1 ;; +esac + +echo "Making with OS=$OS" + diff --git a/trunk/3rdparty/st-srs/public.h b/trunk/3rdparty/st-srs/public.h new file mode 100644 index 000000000..20b09407b --- /dev/null +++ b/trunk/3rdparty/st-srs/public.h @@ -0,0 +1,166 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef __ST_THREAD_H__ +#define __ST_THREAD_H__ + +#include +#include +#include +#include +#include +#include +#include + +#define ST_VERSION "1.9" +#define ST_VERSION_MAJOR 1 +#define ST_VERSION_MINOR 9 + +/* Undefine this to remove the context switch callback feature. */ +#define ST_SWITCH_CB + +#ifndef ETIME + #define ETIME ETIMEDOUT +#endif + +#ifndef ST_UTIME_NO_TIMEOUT + #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) +#endif + +#ifndef ST_UTIME_NO_WAIT + #define ST_UTIME_NO_WAIT 0 +#endif + +#define ST_EVENTSYS_DEFAULT 0 +#define ST_EVENTSYS_SELECT 1 +#define ST_EVENTSYS_POLL 2 +#define ST_EVENTSYS_ALT 3 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long long st_utime_t; +typedef struct _st_thread * st_thread_t; +typedef struct _st_cond * st_cond_t; +typedef struct _st_mutex * st_mutex_t; +typedef struct _st_netfd * st_netfd_t; +#ifdef ST_SWITCH_CB +typedef void (*st_switch_cb_t)(void); +#endif + +extern int st_init(void); +extern int st_getfdlimit(void); + +extern int st_set_eventsys(int eventsys); +extern int st_get_eventsys(void); +extern const char *st_get_eventsys_name(void); + +#ifdef ST_SWITCH_CB +extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb); +extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb); +#endif + +extern st_thread_t st_thread_self(void); +extern void st_thread_exit(void *retval); +extern int st_thread_join(st_thread_t thread, void **retvalp); +extern void st_thread_interrupt(st_thread_t thread); +extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stack_size); +extern int st_randomize_stacks(int on); +extern int st_set_utime_function(st_utime_t (*func)(void)); + +extern st_utime_t st_utime(void); +extern st_utime_t st_utime_last_clock(void); +extern int st_timecache_set(int on); +extern time_t st_time(void); +extern int st_usleep(st_utime_t usecs); +extern int st_sleep(int secs); +extern st_cond_t st_cond_new(void); +extern int st_cond_destroy(st_cond_t cvar); +extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); +extern int st_cond_wait(st_cond_t cvar); +extern int st_cond_signal(st_cond_t cvar); +extern int st_cond_broadcast(st_cond_t cvar); +extern st_mutex_t st_mutex_new(void); +extern int st_mutex_destroy(st_mutex_t lock); +extern int st_mutex_lock(st_mutex_t lock); +extern int st_mutex_unlock(st_mutex_t lock); +extern int st_mutex_trylock(st_mutex_t lock); + +extern int st_key_create(int *keyp, void (*destructor)(void *)); +extern int st_key_getlimit(void); +extern int st_thread_setspecific(int key, void *value); +extern void *st_thread_getspecific(int key); + +extern st_netfd_t st_netfd_open(int osfd); +extern st_netfd_t st_netfd_open_socket(int osfd); +extern void st_netfd_free(st_netfd_t fd); +extern int st_netfd_close(st_netfd_t fd); +extern int st_netfd_fileno(st_netfd_t fd); +extern void st_netfd_setspecific(st_netfd_t fd, void *value, void (*destructor)(void *)); +extern void *st_netfd_getspecific(st_netfd_t fd); +extern int st_netfd_serialize_accept(st_netfd_t fd); +extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); + +extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); +extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout); +extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout); +extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); +extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); +extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, st_utime_t timeout); +extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); +extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); +extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, st_utime_t timeout); +extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, st_utime_t timeout); +extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); +extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); +extern int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout); +extern int st_sendto(st_netfd_t fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout); +extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, st_utime_t timeout); +extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, st_utime_t timeout); +extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); + +#ifdef DEBUG +extern void _st_show_thread_stack(st_thread_t thread, const char *messg); +extern void _st_iterate_threads(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !__ST_THREAD_H__ */ + diff --git a/trunk/3rdparty/st-srs/sched.c b/trunk/3rdparty/st-srs/sched.c new file mode 100644 index 000000000..87515827e --- /dev/null +++ b/trunk/3rdparty/st-srs/sched.c @@ -0,0 +1,705 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include +#include +#include +#include "common.h" + +/* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ +#ifndef NVALGRIND +#include +#endif + + +/* Global data */ +_st_vp_t _st_this_vp; /* This VP */ +_st_thread_t *_st_this_thread; /* Current thread */ +int _st_active_count = 0; /* Active thread count */ + +time_t _st_curr_time = 0; /* Current time as returned by time(2) */ +st_utime_t _st_last_tset; /* Last time it was fetched */ + + +int st_poll(struct pollfd *pds, int npds, st_utime_t timeout) +{ + struct pollfd *pd; + struct pollfd *epd = pds + npds; + _st_pollq_t pq; + _st_thread_t *me = _ST_CURRENT_THREAD(); + int n; + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if ((*_st_eventsys->pollset_add)(pds, npds) < 0) + return -1; + + pq.pds = pds; + pq.npds = npds; + pq.thread = me; + pq.on_ioq = 1; + _ST_ADD_IOQ(pq); + if (timeout != ST_UTIME_NO_TIMEOUT) + _ST_ADD_SLEEPQ(me, timeout); + me->state = _ST_ST_IO_WAIT; + + _ST_SWITCH_CONTEXT(me); + + n = 0; + if (pq.on_ioq) { + /* If we timed out, the pollq might still be on the ioq. Remove it */ + _ST_DEL_IOQ(pq); + (*_st_eventsys->pollset_del)(pds, npds); + } else { + /* Count the number of ready descriptors */ + for (pd = pds; pd < epd; pd++) { + if (pd->revents) + n++; + } + } + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return n; +} + + +void _st_vp_schedule(void) +{ + _st_thread_t *thread; + + if (_ST_RUNQ.next != &_ST_RUNQ) { + /* Pull thread off of the run queue */ + thread = _ST_THREAD_PTR(_ST_RUNQ.next); + _ST_DEL_RUNQ(thread); + } else { + /* If there are no threads to run, switch to the idle thread */ + thread = _st_this_vp.idle_thread; + } + ST_ASSERT(thread->state == _ST_ST_RUNNABLE); + + /* Resume the thread */ + thread->state = _ST_ST_RUNNING; + _ST_RESTORE_CONTEXT(thread); +} + + +/* + * Initialize this Virtual Processor + */ +int st_init(void) +{ + _st_thread_t *thread; + + if (_st_active_count) { + /* Already initialized */ + return 0; + } + + /* We can ignore return value here */ + st_set_eventsys(ST_EVENTSYS_DEFAULT); + + if (_st_io_init() < 0) + return -1; + + memset(&_st_this_vp, 0, sizeof(_st_vp_t)); + + ST_INIT_CLIST(&_ST_RUNQ); + ST_INIT_CLIST(&_ST_IOQ); + ST_INIT_CLIST(&_ST_ZOMBIEQ); +#ifdef DEBUG + ST_INIT_CLIST(&_ST_THREADQ); +#endif + + if ((*_st_eventsys->init)() < 0) + return -1; + + _st_this_vp.pagesize = getpagesize(); + _st_this_vp.last_clock = st_utime(); + + /* + * Create idle thread + */ + _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, NULL, 0, 0); + if (!_st_this_vp.idle_thread) + return -1; + _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD; + _st_active_count--; + _ST_DEL_RUNQ(_st_this_vp.idle_thread); + + /* + * Initialize primordial thread + */ + thread = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) + (ST_KEYS_MAX * sizeof(void *))); + if (!thread) + return -1; + thread->private_data = (void **) (thread + 1); + thread->state = _ST_ST_RUNNING; + thread->flags = _ST_FL_PRIMORDIAL; + _ST_SET_CURRENT_THREAD(thread); + _st_active_count++; +#ifdef DEBUG + _ST_ADD_THREADQ(thread); +#endif + + return 0; +} + + +#ifdef ST_SWITCH_CB +st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb) +{ + st_switch_cb_t ocb = _st_this_vp.switch_in_cb; + _st_this_vp.switch_in_cb = cb; + return ocb; +} + +st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb) +{ + st_switch_cb_t ocb = _st_this_vp.switch_out_cb; + _st_this_vp.switch_out_cb = cb; + return ocb; +} +#endif + + +/* + * Start function for the idle thread + */ +/* ARGSUSED */ +void *_st_idle_thread_start(void *arg) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + while (_st_active_count > 0) { + /* Idle vp till I/O is ready or the smallest timeout expired */ + _ST_VP_IDLE(); + + /* Check sleep queue for expired threads */ + _st_vp_check_clock(); + + me->state = _ST_ST_RUNNABLE; + _ST_SWITCH_CONTEXT(me); + } + + /* No more threads */ + exit(0); + + /* NOTREACHED */ + return NULL; +} + + +void st_thread_exit(void *retval) +{ + _st_thread_t *thread = _ST_CURRENT_THREAD(); + + thread->retval = retval; + _st_thread_cleanup(thread); + _st_active_count--; + if (thread->term) { + /* Put thread on the zombie queue */ + thread->state = _ST_ST_ZOMBIE; + _ST_ADD_ZOMBIEQ(thread); + + /* Notify on our termination condition variable */ + st_cond_signal(thread->term); + + /* Switch context and come back later */ + _ST_SWITCH_CONTEXT(thread); + + /* Continue the cleanup */ + st_cond_destroy(thread->term); + thread->term = NULL; + } + +#ifdef DEBUG + _ST_DEL_THREADQ(thread); +#endif + + /* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ +#ifndef NVALGRIND + if (!(thread->flags & _ST_FL_PRIMORDIAL)) { + VALGRIND_STACK_DEREGISTER(thread->stack->valgrind_stack_id); + } +#endif + + if (!(thread->flags & _ST_FL_PRIMORDIAL)) + _st_stack_free(thread->stack); + + /* Find another thread to run */ + _ST_SWITCH_CONTEXT(thread); + /* Not going to land here */ +} + + +int st_thread_join(_st_thread_t *thread, void **retvalp) +{ + _st_cond_t *term = thread->term; + + /* Can't join a non-joinable thread */ + if (term == NULL) { + errno = EINVAL; + return -1; + } + if (_ST_CURRENT_THREAD() == thread) { + errno = EDEADLK; + return -1; + } + + /* Multiple threads can't wait on the same joinable thread */ + if (term->wait_q.next != &term->wait_q) { + errno = EINVAL; + return -1; + } + + while (thread->state != _ST_ST_ZOMBIE) { + if (st_cond_timedwait(term, ST_UTIME_NO_TIMEOUT) != 0) + return -1; + } + + if (retvalp) + *retvalp = thread->retval; + + /* + * Remove target thread from the zombie queue and make it runnable. + * When it gets scheduled later, it will do the clean up. + */ + thread->state = _ST_ST_RUNNABLE; + _ST_DEL_ZOMBIEQ(thread); + _ST_ADD_RUNQ(thread); + + return 0; +} + + +void _st_thread_main(void) +{ + _st_thread_t *thread = _ST_CURRENT_THREAD(); + + /* + * Cap the stack by zeroing out the saved return address register + * value. This allows some debugging/profiling tools to know when + * to stop unwinding the stack. It's a no-op on most platforms. + */ + MD_CAP_STACK(&thread); + + /* Run thread main */ + thread->retval = (*thread->start)(thread->arg); + + /* All done, time to go away */ + st_thread_exit(thread->retval); +} + + +/* + * Insert "thread" into the timeout heap, in the position + * specified by thread->heap_index. See docs/timeout_heap.txt + * for details about the timeout heap. + */ +static _st_thread_t **heap_insert(_st_thread_t *thread) { + int target = thread->heap_index; + int s = target; + _st_thread_t **p = &_ST_SLEEPQ; + int bits = 0; + int bit; + int index = 1; + + while (s) { + s >>= 1; + bits++; + } + for (bit = bits - 2; bit >= 0; bit--) { + if (thread->due < (*p)->due) { + _st_thread_t *t = *p; + thread->left = t->left; + thread->right = t->right; + *p = thread; + thread->heap_index = index; + thread = t; + } + index <<= 1; + if (target & (1 << bit)) { + p = &((*p)->right); + index |= 1; + } else { + p = &((*p)->left); + } + } + thread->heap_index = index; + *p = thread; + thread->left = thread->right = NULL; + return p; +} + + +/* + * Delete "thread" from the timeout heap. + */ +static void heap_delete(_st_thread_t *thread) { + _st_thread_t *t, **p; + int bits = 0; + int s, bit; + + /* First find and unlink the last heap element */ + p = &_ST_SLEEPQ; + s = _ST_SLEEPQ_SIZE; + while (s) { + s >>= 1; + bits++; + } + for (bit = bits - 2; bit >= 0; bit--) { + if (_ST_SLEEPQ_SIZE & (1 << bit)) { + p = &((*p)->right); + } else { + p = &((*p)->left); + } + } + t = *p; + *p = NULL; + --_ST_SLEEPQ_SIZE; + if (t != thread) { + /* + * Insert the unlinked last element in place of the element we are deleting + */ + t->heap_index = thread->heap_index; + p = heap_insert(t); + t = *p; + t->left = thread->left; + t->right = thread->right; + + /* + * Reestablish the heap invariant. + */ + for (;;) { + _st_thread_t *y; /* The younger child */ + int index_tmp; + if (t->left == NULL) + break; + else if (t->right == NULL) + y = t->left; + else if (t->left->due < t->right->due) + y = t->left; + else + y = t->right; + if (t->due > y->due) { + _st_thread_t *tl = y->left; + _st_thread_t *tr = y->right; + *p = y; + if (y == t->left) { + y->left = t; + y->right = t->right; + p = &y->left; + } else { + y->left = t->left; + y->right = t; + p = &y->right; + } + t->left = tl; + t->right = tr; + index_tmp = t->heap_index; + t->heap_index = y->heap_index; + y->heap_index = index_tmp; + } else { + break; + } + } + } + thread->left = thread->right = NULL; +} + + +void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout) +{ + thread->due = _ST_LAST_CLOCK + timeout; + thread->flags |= _ST_FL_ON_SLEEPQ; + thread->heap_index = ++_ST_SLEEPQ_SIZE; + heap_insert(thread); +} + + +void _st_del_sleep_q(_st_thread_t *thread) +{ + heap_delete(thread); + thread->flags &= ~_ST_FL_ON_SLEEPQ; +} + + +void _st_vp_check_clock(void) +{ + _st_thread_t *thread; + st_utime_t elapsed, now; + + now = st_utime(); + elapsed = now - _ST_LAST_CLOCK; + _ST_LAST_CLOCK = now; + + if (_st_curr_time && now - _st_last_tset > 999000) { + _st_curr_time = time(NULL); + _st_last_tset = now; + } + + while (_ST_SLEEPQ != NULL) { + thread = _ST_SLEEPQ; + ST_ASSERT(thread->flags & _ST_FL_ON_SLEEPQ); + if (thread->due > now) + break; + _ST_DEL_SLEEPQ(thread); + + /* If thread is waiting on condition variable, set the time out flag */ + if (thread->state == _ST_ST_COND_WAIT) + thread->flags |= _ST_FL_TIMEDOUT; + + /* Make thread runnable */ + ST_ASSERT(!(thread->flags & _ST_FL_IDLE_THREAD)); + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + } +} + + +void st_thread_interrupt(_st_thread_t *thread) +{ + /* If thread is already dead */ + if (thread->state == _ST_ST_ZOMBIE) + return; + + thread->flags |= _ST_FL_INTERRUPT; + + if (thread->state == _ST_ST_RUNNING || thread->state == _ST_ST_RUNNABLE) + return; + + if (thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(thread); + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); +} + + +/* Merge from https://github.com/michaeltalyansky/state-threads/commit/cce736426c2320ffec7c9820df49ee7a18ae638c */ +#if defined(__arm__) && !defined(MD_USE_BUILTIN_SETJMP) && __GLIBC_MINOR__ >= 19 + extern unsigned long __pointer_chk_guard; + #define PTR_MANGLE(var) \ + (var) = (__typeof (var)) ((unsigned long) (var) ^ __pointer_chk_guard) + #define PTR_DEMANGLE(var) PTR_MANGLE (var) +#endif + + +_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size) +{ + _st_thread_t *thread; + _st_stack_t *stack; + void **ptds; + char *sp; +#ifdef __ia64__ + char *bsp; +#endif + + /* Adjust stack size */ + if (stk_size == 0) + stk_size = ST_DEFAULT_STACK_SIZE; + stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE; + stack = _st_stack_new(stk_size); + if (!stack) + return NULL; + + /* Allocate thread object and per-thread data off the stack */ +#if defined (MD_STACK_GROWS_DOWN) + sp = stack->stk_top; +#ifdef __ia64__ + /* + * The stack segment is split in the middle. The upper half is used + * as backing store for the register stack which grows upward. + * The lower half is used for the traditional memory stack which + * grows downward. Both stacks start in the middle and grow outward + * from each other. + */ + sp -= (stk_size >> 1); + bsp = sp; + /* Make register stack 64-byte aligned */ + if ((unsigned long)bsp & 0x3f) + bsp = bsp + (0x40 - ((unsigned long)bsp & 0x3f)); + stack->bsp = bsp + _ST_STACK_PAD_SIZE; +#endif + sp = sp - (ST_KEYS_MAX * sizeof(void *)); + ptds = (void **) sp; + sp = sp - sizeof(_st_thread_t); + thread = (_st_thread_t *) sp; + + /* Make stack 64-byte aligned */ + if ((unsigned long)sp & 0x3f) + sp = sp - ((unsigned long)sp & 0x3f); + stack->sp = sp - _ST_STACK_PAD_SIZE; +#elif defined (MD_STACK_GROWS_UP) + sp = stack->stk_bottom; + thread = (_st_thread_t *) sp; + sp = sp + sizeof(_st_thread_t); + ptds = (void **) sp; + sp = sp + (ST_KEYS_MAX * sizeof(void *)); + + /* Make stack 64-byte aligned */ + if ((unsigned long)sp & 0x3f) + sp = sp + (0x40 - ((unsigned long)sp & 0x3f)); + stack->sp = sp + _ST_STACK_PAD_SIZE; +#else +#error Unknown OS +#endif + + memset(thread, 0, sizeof(_st_thread_t)); + memset(ptds, 0, ST_KEYS_MAX * sizeof(void *)); + + /* Initialize thread */ + thread->private_data = ptds; + thread->stack = stack; + thread->start = start; + thread->arg = arg; + +#ifndef __ia64__ + /* Merge from https://github.com/michaeltalyansky/state-threads/commit/cce736426c2320ffec7c9820df49ee7a18ae638c */ + #if defined(__arm__) && !defined(MD_USE_BUILTIN_SETJMP) && __GLIBC_MINOR__ >= 19 + volatile void * lsp = PTR_MANGLE(stack->sp); + if (_setjmp ((thread)->context)) + _st_thread_main(); + (thread)->context[0].__jmpbuf[8] = (long) (lsp); + #else + _ST_INIT_CONTEXT(thread, stack->sp, _st_thread_main); + #endif +#else + _ST_INIT_CONTEXT(thread, stack->sp, stack->bsp, _st_thread_main); +#endif + + /* If thread is joinable, allocate a termination condition variable */ + if (joinable) { + thread->term = st_cond_new(); + if (thread->term == NULL) { + _st_stack_free(thread->stack); + return NULL; + } + } + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _st_active_count++; + _ST_ADD_RUNQ(thread); +#ifdef DEBUG + _ST_ADD_THREADQ(thread); +#endif + + /* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */ +#ifndef NVALGRIND + if (!(thread->flags & _ST_FL_PRIMORDIAL)) { + thread->stack->valgrind_stack_id = VALGRIND_STACK_REGISTER(thread->stack->stk_top, thread->stack->stk_bottom); + } +#endif + + return thread; +} + + +_st_thread_t *st_thread_self(void) +{ + return _ST_CURRENT_THREAD(); +} + + +#ifdef DEBUG +/* ARGSUSED */ +void _st_show_thread_stack(_st_thread_t *thread, const char *messg) +{ + +} + +/* To be set from debugger */ +int _st_iterate_threads_flag = 0; + +void _st_iterate_threads(void) +{ + static _st_thread_t *thread = NULL; + static jmp_buf orig_jb, save_jb; + _st_clist_t *q; + + if (!_st_iterate_threads_flag) { + if (thread) { + memcpy(thread->context, save_jb, sizeof(jmp_buf)); + MD_LONGJMP(orig_jb, 1); + } + return; + } + + if (thread) { + memcpy(thread->context, save_jb, sizeof(jmp_buf)); + _st_show_thread_stack(thread, NULL); + } else { + if (MD_SETJMP(orig_jb)) { + _st_iterate_threads_flag = 0; + thread = NULL; + _st_show_thread_stack(thread, "Iteration completed"); + return; + } + thread = _ST_CURRENT_THREAD(); + _st_show_thread_stack(thread, "Iteration started"); + } + + q = thread->tlink.next; + if (q == &_ST_THREADQ) + q = q->next; + ST_ASSERT(q != &_ST_THREADQ); + thread = _ST_THREAD_THREADQ_PTR(q); + if (thread == _ST_CURRENT_THREAD()) + MD_LONGJMP(orig_jb, 1); + memcpy(save_jb, thread->context, sizeof(jmp_buf)); + MD_LONGJMP(thread->context, 1); +} +#endif /* DEBUG */ + diff --git a/trunk/3rdparty/st-srs/st.pc.in b/trunk/3rdparty/st-srs/st.pc.in new file mode 100644 index 000000000..46c39ec52 --- /dev/null +++ b/trunk/3rdparty/st-srs/st.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libst +Description: State Thread Library +Version: @VERSION@ +Libs: -L${libdir} -lst +Cflags: -I${includedir} diff --git a/trunk/3rdparty/st-srs/st.spec b/trunk/3rdparty/st-srs/st.spec new file mode 100644 index 000000000..4914aa196 --- /dev/null +++ b/trunk/3rdparty/st-srs/st.spec @@ -0,0 +1,79 @@ +Summary: State Threads Library +Name: st +Version: 1.9 +Release: 1 +Copyright: MPL 1.2 or GPL 2+ +Packager: Wesley W. Terpstra +Source: http://prdownloads.sourceforge.net/state-threads/st-%{version}.tar.gz +Prefix: /usr +BuildRoot: /tmp/%{name}-%{version}-build +Group: Development/Libraries + +%description +The State Threads library has an interface similar to POSIX threads. + +However, the threads are actually all run in-process. This type of +threading allows for controlled schedualing points. It is highly useful +for designing robust and extremely scalable internet applications since +there is no resource contention and locking is generally unnecessary. + +It can be combined with traditional threading or multiple process +parallelism to take advantage of multiple processors. + +See: for further +information about how state threads improve performance. + +%package -n libst-devel +Summary: State Threads Library - Development Files +Group: Development/Libraries +Requires: libst1 + +%description -n libst-devel +Development headers and documentation for libst + +%package -n libst1 +Summary: State Threads Library - Shared Libs Major 1 +Group: System/Libraries + +%description -n libst1 +Shared libraries for running applications linked against api version 1. + +%prep +%setup -q + +%build +make CONFIG_GUESS_PATH=/usr/share/automake default-optimized + +%install +if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi + +mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig +mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/include +mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel +cp -a obj/libst.* ${RPM_BUILD_ROOT}/%{prefix}/lib +cp -a obj/st.h ${RPM_BUILD_ROOT}/%{prefix}/include +sed "s*@prefix@*%{prefix}*g" ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig/st.pc +cp -a docs/* ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/ +cp -a examples ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/ + +%post -n libst1 +/sbin/ldconfig %{prefix}/lib + +%files -n libst1 +%defattr(-,root,root) +%{prefix}/lib/lib*.so.* + +%files -n libst-devel +%defattr(-,root,root) +%{prefix}/include/* +%{prefix}/lib/lib*.a +%{prefix}/lib/lib*.so +%{prefix}/lib/pkgconfig/st.pc +%{prefix}/share/doc/libst-devel/* + +%clean +if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi + +%changelog +* Wed Dec 26 2001 Wesley W. Terpstra +- first rpms for libst-1.3.tar.gz diff --git a/trunk/3rdparty/st-srs/stk.c b/trunk/3rdparty/st-srs/stk.c new file mode 100644 index 000000000..3e681e595 --- /dev/null +++ b/trunk/3rdparty/st-srs/stk.c @@ -0,0 +1,173 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include +#include +#include "common.h" + + +/* How much space to leave between the stacks, at each end */ +#define REDZONE _ST_PAGE_SIZE + +_st_clist_t _st_free_stacks = ST_INIT_STATIC_CLIST(&_st_free_stacks); +int _st_num_free_stacks = 0; +int _st_randomize_stacks = 0; + +static char *_st_new_stk_segment(int size); + +_st_stack_t *_st_stack_new(int stack_size) +{ + _st_clist_t *qp; + _st_stack_t *ts; + int extra; + + for (qp = _st_free_stacks.next; qp != &_st_free_stacks; qp = qp->next) { + ts = _ST_THREAD_STACK_PTR(qp); + if (ts->stk_size >= stack_size) { + /* Found a stack that is big enough */ + ST_REMOVE_LINK(&ts->links); + _st_num_free_stacks--; + ts->links.next = NULL; + ts->links.prev = NULL; + return ts; + } + } + + /* Make a new thread stack object. */ + if ((ts = (_st_stack_t *)calloc(1, sizeof(_st_stack_t))) == NULL) + return NULL; + extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0; + ts->vaddr_size = stack_size + 2*REDZONE + extra; + ts->vaddr = _st_new_stk_segment(ts->vaddr_size); + if (!ts->vaddr) { + free(ts); + return NULL; + } + ts->stk_size = stack_size; + ts->stk_bottom = ts->vaddr + REDZONE; + ts->stk_top = ts->stk_bottom + stack_size; + +#ifdef DEBUG + mprotect(ts->vaddr, REDZONE, PROT_NONE); + mprotect(ts->stk_top + extra, REDZONE, PROT_NONE); +#endif + + if (extra) { + long offset = (random() % extra) & ~0xf; + + ts->stk_bottom += offset; + ts->stk_top += offset; + } + + return ts; +} + + +/* + * Free the stack for the current thread + */ +void _st_stack_free(_st_stack_t *ts) +{ + if (!ts) + return; + + /* Put the stack on the free list */ + ST_APPEND_LINK(&ts->links, _st_free_stacks.prev); + _st_num_free_stacks++; +} + + +static char *_st_new_stk_segment(int size) +{ +#ifdef MALLOC_STACK + void *vaddr = malloc(size); +#else + static int zero_fd = -1; + int mmap_flags = MAP_PRIVATE; + void *vaddr; + +#if defined (MD_USE_SYSV_ANON_MMAP) + if (zero_fd < 0) { + if ((zero_fd = open("/dev/zero", O_RDWR, 0)) < 0) + return NULL; + fcntl(zero_fd, F_SETFD, FD_CLOEXEC); + } +#elif defined (MD_USE_BSD_ANON_MMAP) + mmap_flags |= MAP_ANON; +#else +#error Unknown OS +#endif + + vaddr = mmap(NULL, size, PROT_READ | PROT_WRITE, mmap_flags, zero_fd, 0); + if (vaddr == (void *)MAP_FAILED) + return NULL; + +#endif /* MALLOC_STACK */ + + return (char *)vaddr; +} + + +/* Not used */ +#if 0 +void _st_delete_stk_segment(char *vaddr, int size) +{ +#ifdef MALLOC_STACK + free(vaddr); +#else + (void) munmap(vaddr, size); +#endif +} +#endif + +int st_randomize_stacks(int on) +{ + int wason = _st_randomize_stacks; + + _st_randomize_stacks = on; + if (on) + srandom((unsigned int) st_utime()); + + return wason; +} diff --git a/trunk/3rdparty/st-srs/sync.c b/trunk/3rdparty/st-srs/sync.c new file mode 100644 index 000000000..907fdfac3 --- /dev/null +++ b/trunk/3rdparty/st-srs/sync.c @@ -0,0 +1,368 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include "common.h" + + +extern time_t _st_curr_time; +extern st_utime_t _st_last_tset; +extern int _st_active_count; + +static st_utime_t (*_st_utime)(void) = NULL; + + +/***************************************** + * Time functions + */ + +st_utime_t st_utime(void) +{ + if (_st_utime == NULL) { +#ifdef MD_GET_UTIME + MD_GET_UTIME(); +#else +#error Unknown OS +#endif + } + + return (*_st_utime)(); +} + + +int st_set_utime_function(st_utime_t (*func)(void)) +{ + if (_st_active_count) { + errno = EINVAL; + return -1; + } + + _st_utime = func; + + return 0; +} + + +st_utime_t st_utime_last_clock(void) +{ + return _ST_LAST_CLOCK; +} + + +int st_timecache_set(int on) +{ + int wason = (_st_curr_time) ? 1 : 0; + + if (on) { + _st_curr_time = time(NULL); + _st_last_tset = st_utime(); + } else + _st_curr_time = 0; + + return wason; +} + + +time_t st_time(void) +{ + if (_st_curr_time) + return _st_curr_time; + + return time(NULL); +} + + +int st_usleep(st_utime_t usecs) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if (usecs != ST_UTIME_NO_TIMEOUT) { + me->state = _ST_ST_SLEEPING; + _ST_ADD_SLEEPQ(me, usecs); + } else + me->state = _ST_ST_SUSPENDED; + + _ST_SWITCH_CONTEXT(me); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return 0; +} + + +int st_sleep(int secs) +{ + return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : ST_UTIME_NO_TIMEOUT); +} + + +/***************************************** + * Condition variable functions + */ + +_st_cond_t *st_cond_new(void) +{ + _st_cond_t *cvar; + + cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t)); + if (cvar) { + ST_INIT_CLIST(&cvar->wait_q); + } + + return cvar; +} + + +int st_cond_destroy(_st_cond_t *cvar) +{ + if (cvar->wait_q.next != &cvar->wait_q) { + errno = EBUSY; + return -1; + } + + free(cvar); + + return 0; +} + + +int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + int rv; + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + /* Put caller thread on the condition variable's wait queue */ + me->state = _ST_ST_COND_WAIT; + ST_APPEND_LINK(&me->wait_links, &cvar->wait_q); + + if (timeout != ST_UTIME_NO_TIMEOUT) + _ST_ADD_SLEEPQ(me, timeout); + + _ST_SWITCH_CONTEXT(me); + + ST_REMOVE_LINK(&me->wait_links); + rv = 0; + + if (me->flags & _ST_FL_TIMEDOUT) { + me->flags &= ~_ST_FL_TIMEDOUT; + errno = ETIME; + rv = -1; + } + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + rv = -1; + } + + return rv; +} + + +int st_cond_wait(_st_cond_t *cvar) +{ + return st_cond_timedwait(cvar, ST_UTIME_NO_TIMEOUT); +} + + +static int _st_cond_signal(_st_cond_t *cvar, int broadcast) +{ + _st_thread_t *thread; + _st_clist_t *q; + + for (q = cvar->wait_q.next; q != &cvar->wait_q; q = q->next) { + thread = _ST_THREAD_WAITQ_PTR(q); + if (thread->state == _ST_ST_COND_WAIT) { + if (thread->flags & _ST_FL_ON_SLEEPQ) + _ST_DEL_SLEEPQ(thread); + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + if (!broadcast) + break; + } + } + + return 0; +} + + +int st_cond_signal(_st_cond_t *cvar) +{ + return _st_cond_signal(cvar, 0); +} + + +int st_cond_broadcast(_st_cond_t *cvar) +{ + return _st_cond_signal(cvar, 1); +} + + +/***************************************** + * Mutex functions + */ + +_st_mutex_t *st_mutex_new(void) +{ + _st_mutex_t *lock; + + lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t)); + if (lock) { + ST_INIT_CLIST(&lock->wait_q); + lock->owner = NULL; + } + + return lock; +} + + +int st_mutex_destroy(_st_mutex_t *lock) +{ + if (lock->owner != NULL || lock->wait_q.next != &lock->wait_q) { + errno = EBUSY; + return -1; + } + + free(lock); + + return 0; +} + + +int st_mutex_lock(_st_mutex_t *lock) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if (lock->owner == NULL) { + /* Got the mutex */ + lock->owner = me; + return 0; + } + + if (lock->owner == me) { + errno = EDEADLK; + return -1; + } + + /* Put caller thread on the mutex's wait queue */ + me->state = _ST_ST_LOCK_WAIT; + ST_APPEND_LINK(&me->wait_links, &lock->wait_q); + + _ST_SWITCH_CONTEXT(me); + + ST_REMOVE_LINK(&me->wait_links); + + if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return 0; +} + + +int st_mutex_unlock(_st_mutex_t *lock) +{ + _st_thread_t *thread; + _st_clist_t *q; + + if (lock->owner != _ST_CURRENT_THREAD()) { + errno = EPERM; + return -1; + } + + for (q = lock->wait_q.next; q != &lock->wait_q; q = q->next) { + thread = _ST_THREAD_WAITQ_PTR(q); + if (thread->state == _ST_ST_LOCK_WAIT) { + lock->owner = thread; + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + return 0; + } + } + + /* No threads waiting on this mutex */ + lock->owner = NULL; + + return 0; +} + + +int st_mutex_trylock(_st_mutex_t *lock) +{ + if (lock->owner != NULL) { + errno = EBUSY; + return -1; + } + + /* Got the mutex */ + lock->owner = _ST_CURRENT_THREAD(); + + return 0; +} + diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index 843439357..a4c8730df 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -212,23 +212,34 @@ if [[ $OS_IS_UBUNTU = NO && $OS_IS_CENTOS = NO && $SRS_EXPORT_LIBRTMP_PROJECT = fi ##################################################################################### -# libco +# state-threads ##################################################################################### if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then - if [[ -f ${SRS_OBJS}/co/libcolib.a ]]; then - echo "The libco is ok."; + # check the cross build flag file, if flag changed, need to rebuild the st. + _ST_MAKE=linux-debug && _ST_EXTRA_CFLAGS="-DMD_HAVE_EPOLL" + if [[ $SRS_VALGRIND == YES ]]; then + _ST_EXTRA_CFLAGS="$_ST_EXTRA_CFLAGS -DMD_VALGRIND" + fi + # Pass the global extra flags. + if [[ $SRS_EXTRA_FLAGS != '' ]]; then + _ST_EXTRA_CFLAGS="$_ST_EXTRA_CFLAGS $SRS_EXTRA_FLAGS" + fi + # Patched ST from https://github.com/ossrs/state-threads/tree/srs + if [[ -f ${SRS_OBJS}/st/libst.a ]]; then + echo "The state-threads is ok."; else - echo "Building libco."; + echo "Building state-threads."; ( - rm -rf ${SRS_OBJS}/co && cd ${SRS_OBJS} && - ln -sf ../3rdparty/libco && cd libco && - make clean && make colib && - cd .. && rm -f co && ln -sf libco/ co + rm -rf ${SRS_OBJS}/st-srs && cd ${SRS_OBJS} && + ln -sf ../3rdparty/st-srs && cd st-srs && + make clean && make ${_ST_MAKE} EXTRA_CFLAGS="${_ST_EXTRA_CFLAGS}" \ + CC=${SRS_TOOL_CC} AR=${SRS_TOOL_AR} LD=${SRS_TOOL_LD} RANDLIB=${SRS_TOOL_RANDLIB} && + cd .. && rm -f st && ln -sf st-srs/obj st ) fi # check status - ret=$?; if [[ $ret -ne 0 ]]; then echo "Build libco failed, ret=$ret"; exit $ret; fi - if [ ! -f ${SRS_OBJS}/co/lib/libcolib.a ]; then echo "Build libco static lib failed."; exit -1; fi + ret=$?; if [[ $ret -ne 0 ]]; then echo "Build state-threads failed, ret=$ret"; exit $ret; fi + if [ ! -f ${SRS_OBJS}/st/libst.a ]; then echo "Build state-threads static lib failed."; exit -1; fi fi ##################################################################################### diff --git a/trunk/configure b/trunk/configure index e67644a20..d233f66db 100755 --- a/trunk/configure +++ b/trunk/configure @@ -142,12 +142,12 @@ END ##################################################################################### # Libraries, external library to build in srs, -# header(.h): add to ModuleLibIncs if need the specified library. for example, LibCoRoot -# library(.a): add to ModuleLibFiles if binary need the specifeid library. for example, LibCofile +# header(.h): add to ModuleLibIncs if need the specified library. for example, LibSTRoot +# library(.a): add to ModuleLibFiles if binary need the specifeid library. for example, LibSTfile # -# libco the basic network library for SRS. -LibcoRoot="${SRS_OBJS_DIR}/co"; Libcofile="${LibcoRoot}/lib/libcolib.a -lpthread" -if [[ $SRS_SHARED_LIBCO == YES ]]; then Libcofile="-lcolib -lpthread"; fi +# st(state-threads) the basic network library for SRS. +LibSTRoot="${SRS_OBJS_DIR}/st"; LibSTfile="${LibSTRoot}/libst.a" +if [[ $SRS_SHARED_ST == YES ]]; then LibSTfile="-lst"; fi # openssl-1.1.0e, for the RTMP complex handshake. LibSSLRoot="";LibSSLfile="" if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL == NO ]]; then @@ -233,7 +233,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="SERVICE" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL") - ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_service_log" "srs_service_st" "srs_service_http_client" "srs_service_http_conn" "srs_service_rtmp_conn" "srs_service_utility" "srs_service_conn") @@ -246,7 +246,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="APP" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") - ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config" @@ -284,7 +284,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then if [[ $SRS_SRT == YES ]]; then MODULE_DEPENDS+=("SRT") fi - ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) if [[ $SRS_SRT == YES ]]; then ModuleLibIncs+=("${LibSRTRoot[*]}") fi @@ -297,7 +297,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="MAIN" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") - ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) MODULE_FILES=() DEFINES="" # add each modules for main @@ -324,24 +324,24 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then done # # all depends libraries - ModuleLibFiles=(${Libcofile} ${LibSSLfile} ${LibGperfFile}) + ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile}) if [[ $SRS_SRT == YES ]]; then ModuleLibFiles+=("${LibSRTfile[*]}") fi # all depends objects MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${APP_OBJS[@]} ${SERVER_OBJS[@]}" - ModuleLibIncs=(${LibcoRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) if [[ $SRS_SRT == YES ]]; then MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}" fi LINK_OPTIONS="${SrsLinkOptions}${SrsGprofLink}${SrsGperfLink}" # - # srs: srs(simple rtmp server) over co(libco) + # srs: srs(simple rtmp server) over st(state-threads) BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" . auto/apps.sh # # For modules, without the app module. MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${MAIN_OBJS[@]}" - ModuleLibFiles=(${Libcofile} ${LibSSLfile} ${LibGperfFile}) + ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile}) # for SRS_MODULE in ${SRS_MODULES[*]}; do . $SRS_MODULE/config @@ -361,11 +361,11 @@ if [ $SRS_UTEST = YES ]; then MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_protocol" "srs_utest_kernel" "srs_utest_core" "srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload" "srs_utest_mp4" "srs_utest_service" "srs_utest_app") - ModuleLibIncs=(${SRS_OBJS_DIR} ${LibcoRoot} ${LibSSLRoot}) + ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot}) if [[ $SRS_SRT == YES ]]; then ModuleLibIncs+=("${LibSRTRoot[*]}") fi - ModuleLibFiles=(${Libcofile} ${LibSSLfile}) + ModuleLibFiles=(${LibSTfile} ${LibSSLfile}) if [[ $SRS_SRT == YES ]]; then ModuleLibFiles+=("${LibSRTfile[*]}") fi @@ -413,7 +413,7 @@ help: @echo "Usage: make |||||||" @echo " help display this help menu" @echo " clean cleanup project" - @echo " server build the srs(simple rtmp server) over co(libco)" + @echo " server build the srs(simple rtmp server) over st(state-threads)" @echo " librtmp build the client publish/play library, and samples" @echo " utest build the utest for srs" @echo " install install srs to the prefix path" @@ -451,7 +451,7 @@ END else cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE} server: _prepare_dir - @echo "Build the srs(simple rtmp server) over co(libco)" + @echo "Build the srs(simple rtmp server) over ST(state-threads)" \$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} srs END diff --git a/trunk/research/st/Makefile b/trunk/research/st/Makefile new file mode 100755 index 000000000..0b24bb80c --- /dev/null +++ b/trunk/research/st/Makefile @@ -0,0 +1,100 @@ +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape Portable Runtime library. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): Silicon Graphics, Inc. +# +# Portions created by SGI are Copyright (C) 2000-2001 Silicon +# Graphics, Inc. All Rights Reserved. +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. + +########################## +# Target dir and cc: +CC = cc +TARGETDIR = objs + +########################## +# Supported OSes: +OS = LINUX + +ifneq ($(shell test -f /usr/include/sys/epoll.h && echo yes), yes) +default: + @echo "epoll not found" + @exit 1 +endif + +EXTRA_OBJS = $(TARGETDIR)/md.o + +CFLAGS = +OTHER_FLAGS += -Wall -g -O0 +DEFINES = -D$(OS) -DDEBUG -DMD_HAVE_EPOLL -DMALLOC_STACK + +########################## +# Other possible defines: +# To use malloc(3) instead of mmap(2) for stack allocation: +# DEFINES += -DMALLOC_STACK +# +# To provision more than the default 16 thread-specific-data keys +# (but not too many!): +# DEFINES += -DST_KEYS_MAX= +# +# Note that you can also add these defines by specifying them as +# make/gmake arguments (without editing this Makefile). For example: +# +# make EXTRA_CFLAGS=-UMD_HAVE_EPOLL +# +########################## + +CFLAGS += $(DEFINES) $(OTHER_FLAGS) $(EXTRA_CFLAGS) + +OBJS = $(TARGETDIR)/sched.o \ + $(TARGETDIR)/stk.o \ + $(TARGETDIR)/sync.o \ + $(TARGETDIR)/key.o \ + $(TARGETDIR)/io.o \ + $(TARGETDIR)/event.o \ + $(TARGETDIR)/srs.o +OBJS += $(EXTRA_OBJS) +SRS = $(TARGETDIR)/srs + +linux-debug: all +all: $(TARGETDIR) $(SRS) + +$(TARGETDIR): + if [ ! -d $(TARGETDIR) ]; then mkdir $(TARGETDIR); fi + +$(SRS): $(OBJS) + $(CC) $(CFLAGS) -o $@ $(OBJS) + +$(TARGETDIR)/md.o: md.S + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGETDIR)/%.o: %.c common.h md.h Makefile + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -rf $(TARGETDIR) diff --git a/trunk/research/st/common.h b/trunk/research/st/common.h new file mode 100644 index 000000000..b83e575ca --- /dev/null +++ b/trunk/research/st/common.h @@ -0,0 +1,445 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#ifndef __ST_COMMON_H__ +#define __ST_COMMON_H__ + +#include +#include +#include +#include +#include + +/* Enable assertions only if DEBUG is defined */ +#ifndef DEBUG + #define NDEBUG +#endif +#include +#define ST_ASSERT(expr) assert(expr) + +#define ST_BEGIN_MACRO { +#define ST_END_MACRO } + +#ifdef DEBUG + #define ST_HIDDEN /*nothing*/ +#else + #define ST_HIDDEN static +#endif + +#include "public.h" +#include "md.h" + +/***************************************** + * Circular linked list definitions + */ + +typedef struct _st_clist { + struct _st_clist *next; + struct _st_clist *prev; +} _st_clist_t; + +/* Insert element "_e" into the list, before "_l" */ +#define ST_INSERT_BEFORE(_e,_l) \ + ST_BEGIN_MACRO \ + (_e)->next = (_l); \ + (_e)->prev = (_l)->prev; \ + (_l)->prev->next = (_e); \ + (_l)->prev = (_e); \ + ST_END_MACRO + +/* Insert element "_e" into the list, after "_l" */ +#define ST_INSERT_AFTER(_e,_l) \ + ST_BEGIN_MACRO \ + (_e)->next = (_l)->next; \ + (_e)->prev = (_l); \ + (_l)->next->prev = (_e); \ + (_l)->next = (_e); \ + ST_END_MACRO + +/* Return the element following element "_e" */ +#define ST_NEXT_LINK(_e) ((_e)->next) + +/* Append an element "_e" to the end of the list "_l" */ +#define ST_APPEND_LINK(_e,_l) ST_INSERT_BEFORE(_e,_l) + +/* Insert an element "_e" at the head of the list "_l" */ +#define ST_INSERT_LINK(_e,_l) ST_INSERT_AFTER(_e,_l) + +/* Return the head/tail of the list */ +#define ST_LIST_HEAD(_l) (_l)->next +#define ST_LIST_TAIL(_l) (_l)->prev + +/* Remove the element "_e" from it's circular list */ +#define ST_REMOVE_LINK(_e) \ + ST_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + ST_END_MACRO + +/* Return non-zero if the given circular list "_l" is empty, */ +/* zero if the circular list is not empty */ +#define ST_CLIST_IS_EMPTY(_l) \ + ((_l)->next == (_l)) + +/* Initialize a circular list */ +#define ST_INIT_CLIST(_l) \ + ST_BEGIN_MACRO \ + (_l)->next = (_l); \ + (_l)->prev = (_l); \ + ST_END_MACRO + +#define ST_INIT_STATIC_CLIST(_l) \ + {(_l), (_l)} + + +/***************************************** + * Basic types definitions + */ + +typedef void (*_st_destructor_t)(void *); + +typedef struct _st_stack { + _st_clist_t links; + char *vaddr; /* Base of stack's allocated memory */ + int vaddr_size; /* Size of stack's allocated memory */ + int stk_size; /* Size of usable portion of the stack */ + char *stk_bottom; /* Lowest address of stack's usable portion */ + char *stk_top; /* Highest address of stack's usable portion */ + void *sp; /* Stack pointer from C's point of view */ +} _st_stack_t; + + +typedef struct _st_cond { + _st_clist_t wait_q; /* Condition variable wait queue */ +} _st_cond_t; + + +typedef struct _st_thread _st_thread_t; + +struct _st_thread { + int state; /* Thread's state */ + int flags; /* Thread's flags */ + + void *(*start)(void *arg); /* The start function of the thread */ + void *arg; /* Argument of the start function */ + void *retval; /* Return value of the start function */ + + _st_stack_t *stack; /* Info about thread's stack */ + + _st_clist_t links; /* For putting on run/sleep/zombie queue */ + _st_clist_t wait_links; /* For putting on mutex/condvar wait queue */ + #ifdef DEBUG + _st_clist_t tlink; /* For putting on thread queue */ + #endif + + st_utime_t due; /* Wakeup time when thread is sleeping */ + _st_thread_t *left; /* For putting in timeout heap */ + _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ + int heap_index; + + void **private_data; /* Per thread private data */ + + _st_cond_t *term; /* Termination condition variable for join */ + + jmp_buf context; /* Thread's context */ +}; + + +typedef struct _st_mutex { + _st_thread_t *owner; /* Current mutex owner */ + _st_clist_t wait_q; /* Mutex wait queue */ +} _st_mutex_t; + + +typedef struct _st_pollq { + _st_clist_t links; /* For putting on io queue */ + _st_thread_t *thread; /* Polling thread */ + struct pollfd *pds; /* Array of poll descriptors */ + int npds; /* Length of the array */ + int on_ioq; /* Is it on ioq? */ +} _st_pollq_t; + + +typedef struct _st_eventsys_ops { + const char *name; /* Name of this event system */ + int val; /* Type of this event system */ + int (*init)(void); /* Initialization */ + void (*dispatch)(void); /* Dispatch function */ + int (*pollset_add)(struct pollfd *, int); /* Add descriptor set */ + void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */ + int (*fd_new)(int); /* New descriptor allocated */ + int (*fd_close)(int); /* Descriptor closed */ + int (*fd_getlimit)(void); /* Descriptor hard limit */ +} _st_eventsys_t; + + +typedef struct _st_vp { + _st_thread_t *idle_thread; /* Idle thread for this vp */ + st_utime_t last_clock; /* The last time we went into vp_check_clock() */ + + _st_clist_t run_q; /* run queue for this vp */ + _st_clist_t io_q; /* io queue for this vp */ + _st_clist_t zombie_q; /* zombie queue for this vp */ + #ifdef DEBUG + _st_clist_t thread_q; /* all threads of this vp */ + #endif + int pagesize; + + _st_thread_t *sleep_q; /* sleep queue for this vp */ + int sleepq_size; /* number of threads on sleep queue */ + + #ifdef ST_SWITCH_CB + st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ + st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ + #endif +} _st_vp_t; + + +typedef struct _st_netfd { + int osfd; /* Underlying OS file descriptor */ + int inuse; /* In-use flag */ + void *private_data; /* Per descriptor private data */ + _st_destructor_t destructor; /* Private data destructor function */ + void *aux_data; /* Auxiliary data for internal use */ + struct _st_netfd *next; /* For putting on the free list */ +} _st_netfd_t; + + +/***************************************** + * Current vp, thread, and event system + */ + +extern _st_vp_t _st_this_vp; +extern _st_thread_t *_st_this_thread; +extern _st_eventsys_t *_st_eventsys; + +#define _ST_CURRENT_THREAD() (_st_this_thread) +#define _ST_SET_CURRENT_THREAD(_thread) (_st_this_thread = (_thread)) + +#define _ST_LAST_CLOCK (_st_this_vp.last_clock) + +#define _ST_RUNQ (_st_this_vp.run_q) +#define _ST_IOQ (_st_this_vp.io_q) +#define _ST_ZOMBIEQ (_st_this_vp.zombie_q) +#ifdef DEBUG + #define _ST_THREADQ (_st_this_vp.thread_q) +#endif + +#define _ST_PAGE_SIZE (_st_this_vp.pagesize) + +#define _ST_SLEEPQ (_st_this_vp.sleep_q) +#define _ST_SLEEPQ_SIZE (_st_this_vp.sleepq_size) + +#define _ST_VP_IDLE() (*_st_eventsys->dispatch)() + + +/***************************************** + * vp queues operations + */ + +#define _ST_ADD_IOQ(_pq) ST_APPEND_LINK(&_pq.links, &_ST_IOQ) +#define _ST_DEL_IOQ(_pq) ST_REMOVE_LINK(&_pq.links) + +#define _ST_ADD_RUNQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_RUNQ) +#define _ST_DEL_RUNQ(_thr) ST_REMOVE_LINK(&(_thr)->links) + +#define _ST_ADD_SLEEPQ(_thr, _timeout) _st_add_sleep_q(_thr, _timeout) +#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr) + +#define _ST_ADD_ZOMBIEQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ) +#define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links) + +#ifdef DEBUG + #define _ST_ADD_THREADQ(_thr) ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ) + #define _ST_DEL_THREADQ(_thr) ST_REMOVE_LINK(&(_thr)->tlink) +#endif + + +/***************************************** + * Thread states and flags + */ + +#define _ST_ST_RUNNING 0 +#define _ST_ST_RUNNABLE 1 +#define _ST_ST_IO_WAIT 2 +#define _ST_ST_LOCK_WAIT 3 +#define _ST_ST_COND_WAIT 4 +#define _ST_ST_SLEEPING 5 +#define _ST_ST_ZOMBIE 6 +#define _ST_ST_SUSPENDED 7 + +#define _ST_FL_PRIMORDIAL 0x01 +#define _ST_FL_IDLE_THREAD 0x02 +#define _ST_FL_ON_SLEEPQ 0x04 +#define _ST_FL_INTERRUPT 0x08 +#define _ST_FL_TIMEDOUT 0x10 + +/***************************************** + * Pointer conversion + */ + +#ifndef offsetof + #define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) +#endif + +#define _ST_THREAD_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, links))) + +#define _ST_THREAD_WAITQ_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, wait_links))) + +#define _ST_THREAD_STACK_PTR(_qp) \ + ((_st_stack_t *)((char*)(_qp) - offsetof(_st_stack_t, links))) + +#define _ST_POLLQUEUE_PTR(_qp) \ + ((_st_pollq_t *)((char *)(_qp) - offsetof(_st_pollq_t, links))) + +#ifdef DEBUG + #define _ST_THREAD_THREADQ_PTR(_qp) \ + ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink))) +#endif + + +/***************************************** + * Constants + */ + +#ifndef ST_UTIME_NO_TIMEOUT + #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) +#endif + +#define ST_DEFAULT_STACK_SIZE (64*1024) + +#ifndef ST_KEYS_MAX + #define ST_KEYS_MAX 16 +#endif + +#ifndef ST_MIN_POLLFDS_SIZE + #define ST_MIN_POLLFDS_SIZE 64 +#endif + + +/***************************************** + * Threads context switching + */ + +#ifdef DEBUG + void _st_iterate_threads(void); + #define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() +#else + #define ST_DEBUG_ITERATE_THREADS() +#endif + +#ifdef ST_SWITCH_CB + #define ST_SWITCH_OUT_CB(_thread) \ + if (_st_this_vp.switch_out_cb != NULL && \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_out_cb(); \ + } + #define ST_SWITCH_IN_CB(_thread) \ + if (_st_this_vp.switch_in_cb != NULL && \ + _thread != _st_this_vp.idle_thread && \ + _thread->state != _ST_ST_ZOMBIE) { \ + _st_this_vp.switch_in_cb(); \ + } +#else + #define ST_SWITCH_OUT_CB(_thread) + #define ST_SWITCH_IN_CB(_thread) +#endif + +/* + * Switch away from the current thread context by saving its state and + * calling the thread scheduler + */ +#define _ST_SWITCH_CONTEXT(_thread) \ + ST_BEGIN_MACRO \ + ST_SWITCH_OUT_CB(_thread); \ + if (!MD_SETJMP((_thread)->context)) { \ + _st_vp_schedule(); \ + } \ + ST_DEBUG_ITERATE_THREADS(); \ + ST_SWITCH_IN_CB(_thread); \ + ST_END_MACRO + +/* + * Restore a thread context that was saved by _ST_SWITCH_CONTEXT or + * initialized by _ST_INIT_CONTEXT + */ +#define _ST_RESTORE_CONTEXT(_thread) \ + ST_BEGIN_MACRO \ + _ST_SET_CURRENT_THREAD(_thread); \ + MD_LONGJMP((_thread)->context, 1); \ + ST_END_MACRO + +/* + * Number of bytes reserved under the stack "bottom" + */ +#define _ST_STACK_PAD_SIZE MD_STACK_PAD_SIZE + + +/***************************************** + * Forward declarations + */ + +void _st_vp_schedule(void); +void _st_vp_check_clock(void); +void *_st_idle_thread_start(void *arg); +void _st_thread_main(void); +void _st_thread_cleanup(_st_thread_t *thread); +void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout); +void _st_del_sleep_q(_st_thread_t *thread); +_st_stack_t *_st_stack_new(int stack_size); +void _st_stack_free(_st_stack_t *ts); +int _st_io_init(void); + +st_utime_t st_utime(void); +_st_cond_t *st_cond_new(void); +int st_cond_destroy(_st_cond_t *cvar); +int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout); +int st_cond_signal(_st_cond_t *cvar); +ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout); +ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout); +int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); +_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size); + +#endif /* !__ST_COMMON_H__ */ + diff --git a/trunk/research/st/event.c b/trunk/research/st/event.c new file mode 100644 index 000000000..5704f520a --- /dev/null +++ b/trunk/research/st/event.c @@ -0,0 +1,483 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * Yahoo! Inc. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include +#include +#include +#include +#include +#include +#include "common.h" + +#ifdef USE_POLL + #error "Not support USE_POLL" +#endif +#ifdef MD_HAVE_KQUEUE + #error "Not support MD_HAVE_KQUEUE" +#endif +#ifdef MD_HAVE_POLL + #error "Not support MD_HAVE_POLL" +#endif +#ifndef MD_HAVE_EPOLL + #error "Only support MD_HAVE_EPOLL" +#endif + +#include + +typedef struct _epoll_fd_data { + int rd_ref_cnt; + int wr_ref_cnt; + int ex_ref_cnt; + int revents; +} _epoll_fd_data_t; + +static struct _st_epolldata { + _epoll_fd_data_t *fd_data; + struct epoll_event *evtlist; + int fd_data_size; + int evtlist_size; + int evtlist_cnt; + int fd_hint; + int epfd; + pid_t pid; +} *_st_epoll_data; + +#ifndef ST_EPOLL_EVTLIST_SIZE +/* Not a limit, just a hint */ +#define ST_EPOLL_EVTLIST_SIZE 4096 +#endif + +#define _ST_EPOLL_READ_CNT(fd) (_st_epoll_data->fd_data[fd].rd_ref_cnt) +#define _ST_EPOLL_WRITE_CNT(fd) (_st_epoll_data->fd_data[fd].wr_ref_cnt) +#define _ST_EPOLL_EXCEP_CNT(fd) (_st_epoll_data->fd_data[fd].ex_ref_cnt) +#define _ST_EPOLL_REVENTS(fd) (_st_epoll_data->fd_data[fd].revents) + +#define _ST_EPOLL_READ_BIT(fd) (_ST_EPOLL_READ_CNT(fd) ? EPOLLIN : 0) +#define _ST_EPOLL_WRITE_BIT(fd) (_ST_EPOLL_WRITE_CNT(fd) ? EPOLLOUT : 0) +#define _ST_EPOLL_EXCEP_BIT(fd) (_ST_EPOLL_EXCEP_CNT(fd) ? EPOLLPRI : 0) +#define _ST_EPOLL_EVENTS(fd) \ + (_ST_EPOLL_READ_BIT(fd)|_ST_EPOLL_WRITE_BIT(fd)|_ST_EPOLL_EXCEP_BIT(fd)) + +_st_eventsys_t *_st_eventsys = NULL; + +/***************************************** + * epoll event system + */ + +ST_HIDDEN int _st_epoll_init(void) +{ + int fdlim; + int err = 0; + int rv = 0; + + _st_epoll_data = (struct _st_epolldata *) calloc(1, sizeof(*_st_epoll_data)); + if (!_st_epoll_data) { + return -1; + } + + fdlim = st_getfdlimit(); + _st_epoll_data->fd_hint = (fdlim > 0 && fdlim < ST_EPOLL_EVTLIST_SIZE) ? fdlim : ST_EPOLL_EVTLIST_SIZE; + + if ((_st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint)) < 0) { + err = errno; + rv = -1; + goto cleanup_epoll; + } + fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC); + _st_epoll_data->pid = getpid(); + + /* Allocate file descriptor data array */ + _st_epoll_data->fd_data_size = _st_epoll_data->fd_hint; + _st_epoll_data->fd_data = (_epoll_fd_data_t *)calloc(_st_epoll_data->fd_data_size, sizeof(_epoll_fd_data_t)); + if (!_st_epoll_data->fd_data) { + err = errno; + rv = -1; + goto cleanup_epoll; + } + + /* Allocate event lists */ + _st_epoll_data->evtlist_size = _st_epoll_data->fd_hint; + _st_epoll_data->evtlist = (struct epoll_event *)malloc(_st_epoll_data->evtlist_size * sizeof(struct epoll_event)); + if (!_st_epoll_data->evtlist) { + err = errno; + rv = -1; + } + + cleanup_epoll: + if (rv < 0) { + if (_st_epoll_data->epfd >= 0) { + close(_st_epoll_data->epfd); + } + free(_st_epoll_data->fd_data); + free(_st_epoll_data->evtlist); + free(_st_epoll_data); + _st_epoll_data = NULL; + errno = err; + } + + return rv; +} + +ST_HIDDEN int _st_epoll_fd_data_expand(int maxfd) +{ + _epoll_fd_data_t *ptr; + int n = _st_epoll_data->fd_data_size; + + while (maxfd >= n) { + n <<= 1; + } + + ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, n * sizeof(_epoll_fd_data_t)); + if (!ptr) { + return -1; + } + + memset(ptr + _st_epoll_data->fd_data_size, 0, (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t)); + + _st_epoll_data->fd_data = ptr; + _st_epoll_data->fd_data_size = n; + + return 0; +} + +ST_HIDDEN void _st_epoll_evtlist_expand(void) +{ + struct epoll_event *ptr; + int n = _st_epoll_data->evtlist_size; + + while (_st_epoll_data->evtlist_cnt > n) { + n <<= 1; + } + + ptr = (struct epoll_event *)realloc(_st_epoll_data->evtlist, n * sizeof(struct epoll_event)); + if (ptr) { + _st_epoll_data->evtlist = ptr; + _st_epoll_data->evtlist_size = n; + } +} + +ST_HIDDEN void _st_epoll_pollset_del(struct pollfd *pds, int npds) +{ + struct epoll_event ev; + struct pollfd *pd; + struct pollfd *epd = pds + npds; + int old_events, events, op; + + /* + * It's more or less OK if deleting fails because a descriptor + * will either be closed or deleted in dispatch function after + * it fires. + */ + for (pd = pds; pd < epd; pd++) { + old_events = _ST_EPOLL_EVENTS(pd->fd); + + if (pd->events & POLLIN) { + _ST_EPOLL_READ_CNT(pd->fd)--; + } + if (pd->events & POLLOUT) { + _ST_EPOLL_WRITE_CNT(pd->fd)--; + } + if (pd->events & POLLPRI) { + _ST_EPOLL_EXCEP_CNT(pd->fd)--; + } + + events = _ST_EPOLL_EVENTS(pd->fd); + /* + * The _ST_EPOLL_REVENTS check below is needed so we can use + * this function inside dispatch(). Outside of dispatch() + * _ST_EPOLL_REVENTS is always zero for all descriptors. + */ + if (events != old_events && _ST_EPOLL_REVENTS(pd->fd) == 0) { + op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; + ev.events = events; + ev.data.fd = pd->fd; + if (epoll_ctl(_st_epoll_data->epfd, op, pd->fd, &ev) == 0 && op == EPOLL_CTL_DEL) { + _st_epoll_data->evtlist_cnt--; + } + } + } +} + +ST_HIDDEN int _st_epoll_pollset_add(struct pollfd *pds, int npds) +{ + struct epoll_event ev; + int i, fd; + int old_events, events, op; + + /* Do as many checks as possible up front */ + for (i = 0; i < npds; i++) { + fd = pds[i].fd; + if (fd < 0 || !pds[i].events || (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) { + errno = EINVAL; + return -1; + } + if (fd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(fd) < 0) { + return -1; + } + } + + for (i = 0; i < npds; i++) { + fd = pds[i].fd; + old_events = _ST_EPOLL_EVENTS(fd); + + if (pds[i].events & POLLIN) { + _ST_EPOLL_READ_CNT(fd)++; + } + if (pds[i].events & POLLOUT) { + _ST_EPOLL_WRITE_CNT(fd)++; + } + if (pds[i].events & POLLPRI) { + _ST_EPOLL_EXCEP_CNT(fd)++; + } + + events = _ST_EPOLL_EVENTS(fd); + if (events != old_events) { + op = old_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; + ev.events = events; + ev.data.fd = fd; + if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 && (op != EPOLL_CTL_ADD || errno != EEXIST)) { + break; + } + if (op == EPOLL_CTL_ADD) { + _st_epoll_data->evtlist_cnt++; + if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size) { + _st_epoll_evtlist_expand(); + } + } + } + } + + if (i < npds) { + /* Error */ + int err = errno; + /* Unroll the state */ + _st_epoll_pollset_del(pds, i + 1); + errno = err; + return -1; + } + + return 0; +} + +ST_HIDDEN void _st_epoll_dispatch(void) +{ + st_utime_t min_timeout; + _st_clist_t *q; + _st_pollq_t *pq; + struct pollfd *pds, *epds; + struct epoll_event ev; + int timeout, nfd, i, osfd, notify; + int events, op; + short revents; + + if (_ST_SLEEPQ == NULL) { + timeout = -1; + } else { + min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + timeout = (int) (min_timeout / 1000); + } + + if (_st_epoll_data->pid != getpid()) { + // WINLIN: remove it for bug introduced. + // @see: https://github.com/ossrs/srs/issues/193 + exit(-1); + } + + /* Check for I/O operations */ + nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, _st_epoll_data->evtlist_size, timeout); + + if (nfd > 0) { + for (i = 0; i < nfd; i++) { + osfd = _st_epoll_data->evtlist[i].data.fd; + _ST_EPOLL_REVENTS(osfd) = _st_epoll_data->evtlist[i].events; + if (_ST_EPOLL_REVENTS(osfd) & (EPOLLERR | EPOLLHUP)) { + /* Also set I/O bits on error */ + _ST_EPOLL_REVENTS(osfd) |= _ST_EPOLL_EVENTS(osfd); + } + } + + for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) { + pq = _ST_POLLQUEUE_PTR(q); + notify = 0; + epds = pq->pds + pq->npds; + + for (pds = pq->pds; pds < epds; pds++) { + if (_ST_EPOLL_REVENTS(pds->fd) == 0) { + pds->revents = 0; + continue; + } + osfd = pds->fd; + events = pds->events; + revents = 0; + if ((events & POLLIN) && (_ST_EPOLL_REVENTS(osfd) & EPOLLIN)) { + revents |= POLLIN; + } + if ((events & POLLOUT) && (_ST_EPOLL_REVENTS(osfd) & EPOLLOUT)) { + revents |= POLLOUT; + } + if ((events & POLLPRI) && (_ST_EPOLL_REVENTS(osfd) & EPOLLPRI)) { + revents |= POLLPRI; + } + if (_ST_EPOLL_REVENTS(osfd) & EPOLLERR) { + revents |= POLLERR; + } + if (_ST_EPOLL_REVENTS(osfd) & EPOLLHUP) { + revents |= POLLHUP; + } + + pds->revents = revents; + if (revents) { + notify = 1; + } + } + if (notify) { + ST_REMOVE_LINK(&pq->links); + pq->on_ioq = 0; + /* + * Here we will only delete/modify descriptors that + * didn't fire (see comments in _st_epoll_pollset_del()). + */ + _st_epoll_pollset_del(pq->pds, pq->npds); + + if (pq->thread->flags & _ST_FL_ON_SLEEPQ) { + _ST_DEL_SLEEPQ(pq->thread); + } + pq->thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(pq->thread); + } + } + + for (i = 0; i < nfd; i++) { + /* Delete/modify descriptors that fired */ + osfd = _st_epoll_data->evtlist[i].data.fd; + _ST_EPOLL_REVENTS(osfd) = 0; + events = _ST_EPOLL_EVENTS(osfd); + op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL; + ev.events = events; + ev.data.fd = osfd; + if (epoll_ctl(_st_epoll_data->epfd, op, osfd, &ev) == 0 && op == EPOLL_CTL_DEL) { + _st_epoll_data->evtlist_cnt--; + } + } + } +} + +ST_HIDDEN int _st_epoll_fd_new(int osfd) +{ + if (osfd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(osfd) < 0) { + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_epoll_fd_close(int osfd) +{ + if (_ST_EPOLL_READ_CNT(osfd) || _ST_EPOLL_WRITE_CNT(osfd) || _ST_EPOLL_EXCEP_CNT(osfd)) { + errno = EBUSY; + return -1; + } + + return 0; +} + +ST_HIDDEN int _st_epoll_fd_getlimit(void) +{ + /* zero means no specific limit */ + return 0; +} + +/* + * Check if epoll functions are just stubs. + */ +ST_HIDDEN int _st_epoll_is_supported(void) +{ + struct epoll_event ev; + + ev.events = EPOLLIN; + ev.data.ptr = NULL; + /* Guaranteed to fail */ + epoll_ctl(-1, EPOLL_CTL_ADD, -1, &ev); + + return (errno != ENOSYS); +} + +static _st_eventsys_t _st_epoll_eventsys = { + "epoll", + ST_EVENTSYS_ALT, + _st_epoll_init, + _st_epoll_dispatch, + _st_epoll_pollset_add, + _st_epoll_pollset_del, + _st_epoll_fd_new, + _st_epoll_fd_close, + _st_epoll_fd_getlimit +}; + +/***************************************** + * Public functions + */ + +int st_set_eventsys(int eventsys) +{ + if (_st_eventsys) { + errno = EBUSY; + return -1; + } + + switch (eventsys) { + case ST_EVENTSYS_DEFAULT: + case ST_EVENTSYS_ALT: + default: + if (_st_epoll_is_supported()) { + _st_eventsys = &_st_epoll_eventsys; + break; + } + errno = EINVAL; + return -1; + } + + return 0; +} + +int st_get_eventsys(void) +{ + return _st_eventsys ? _st_eventsys->val : -1; +} + +const char *st_get_eventsys_name(void) +{ + return _st_eventsys ? _st_eventsys->name : ""; +} + diff --git a/trunk/research/st/io.c b/trunk/research/st/io.c new file mode 100644 index 000000000..bc77dc8e1 --- /dev/null +++ b/trunk/research/st/io.c @@ -0,0 +1,792 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +#if EAGAIN != EWOULDBLOCK + #define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK)) +#else + #define _IO_NOT_READY_ERROR (errno == EAGAIN) +#endif + +#define _LOCAL_MAXIOV 16 + +/* File descriptor object free list */ +static _st_netfd_t *_st_netfd_freelist = NULL; +/* Maximum number of file descriptors that the process can open */ +static int _st_osfd_limit = -1; + +static void _st_netfd_free_aux_data(_st_netfd_t *fd); + +int _st_io_init(void) +{ + struct sigaction sigact; + struct rlimit rlim; + int fdlim; + + /* Ignore SIGPIPE */ + sigact.sa_handler = SIG_IGN; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + if (sigaction(SIGPIPE, &sigact, NULL) < 0) { + return -1; + } + + /* Set maximum number of open file descriptors */ + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + return -1; + } + + fdlim = (*_st_eventsys->fd_getlimit)(); + if (fdlim > 0 && rlim.rlim_max > (rlim_t) fdlim) { + rlim.rlim_max = fdlim; + } + rlim.rlim_cur = rlim.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { + return -1; + } + _st_osfd_limit = (int) rlim.rlim_max; + + return 0; +} + +int st_getfdlimit(void) +{ + return _st_osfd_limit; +} + +void st_netfd_free(_st_netfd_t *fd) +{ + if (!fd->inuse) { + return; + } + + fd->inuse = 0; + if (fd->aux_data) { + _st_netfd_free_aux_data(fd); + } + if (fd->private_data && fd->destructor) { + (*(fd->destructor))(fd->private_data); + } + fd->private_data = NULL; + fd->destructor = NULL; + fd->next = _st_netfd_freelist; + _st_netfd_freelist = fd; +} + +static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket) +{ + _st_netfd_t *fd; + int flags = 1; + + if ((*_st_eventsys->fd_new)(osfd) < 0) { + return NULL; + } + + if (_st_netfd_freelist) { + fd = _st_netfd_freelist; + _st_netfd_freelist = _st_netfd_freelist->next; + } else { + fd = calloc(1, sizeof(_st_netfd_t)); + if (!fd) { + return NULL; + } + } + + fd->osfd = osfd; + fd->inuse = 1; + fd->next = NULL; + + if (nonblock) { + /* Use just one system call */ + if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) { + return fd; + } + /* Do it the Posix way */ + if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) { + st_netfd_free(fd); + return NULL; + } + } + + return fd; +} + +_st_netfd_t *st_netfd_open(int osfd) +{ + return _st_netfd_new(osfd, 1, 0); +} + +_st_netfd_t *st_netfd_open_socket(int osfd) +{ + return _st_netfd_new(osfd, 1, 1); +} + +int st_netfd_close(_st_netfd_t *fd) +{ + if ((*_st_eventsys->fd_close)(fd->osfd) < 0) { + return -1; + } + + st_netfd_free(fd); + return close(fd->osfd); +} + +int st_netfd_fileno(_st_netfd_t *fd) +{ + return (fd->osfd); +} + +void st_netfd_setspecific(_st_netfd_t *fd, void *value, _st_destructor_t destructor) +{ + if (value != fd->private_data) { + /* Free up previously set non-NULL data value */ + if (fd->private_data && fd->destructor) { + (*(fd->destructor))(fd->private_data); + } + } + fd->private_data = value; + fd->destructor = destructor; +} + +void *st_netfd_getspecific(_st_netfd_t *fd) +{ + return (fd->private_data); +} + +/* + * Wait for I/O on a single descriptor. + */ +int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeout) +{ + struct pollfd pd; + int n; + + pd.fd = fd->osfd; + pd.events = (short) how; + pd.revents = 0; + + if ((n = st_poll(&pd, 1, timeout)) < 0) { + return -1; + } + if (n == 0) { + /* Timed out */ + errno = ETIME; + return -1; + } + if (pd.revents & POLLNVAL) { + errno = EBADF; + return -1; + } + + return 0; +} + +#ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT +/* No-op */ +int st_netfd_serialize_accept(_st_netfd_t *fd) +{ + fd->aux_data = NULL; + return 0; +} + +/* No-op */ +static void _st_netfd_free_aux_data(_st_netfd_t *fd) +{ + fd->aux_data = NULL; +} + +_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) +{ + int osfd, err; + _st_netfd_t *newfd; + + while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return NULL; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return NULL; + } + } + + /* On some platforms the new socket created by accept() inherits */ + /* the nonblocking attribute of the listening socket */ +#if defined (MD_ACCEPT_NB_INHERITED) + newfd = _st_netfd_new(osfd, 0, 1); +#elif defined (MD_ACCEPT_NB_NOT_INHERITED) + newfd = _st_netfd_new(osfd, 1, 1); +#else + #error Unknown OS +#endif + + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; +} + +#else /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ +/* + * On some platforms accept() calls from different processes + * on the same listen socket must be serialized. + * The following code serializes accept()'s without process blocking. + * A pipe is used as an inter-process semaphore. + */ +int st_netfd_serialize_accept(_st_netfd_t *fd) +{ + _st_netfd_t **p; + int osfd[2], err; + + if (fd->aux_data) { + errno = EINVAL; + return -1; + } + if ((p = (_st_netfd_t **)calloc(2, sizeof(_st_netfd_t *))) == NULL) { + return -1; + } + if (pipe(osfd) < 0) { + free(p); + return -1; + } + if ((p[0] = st_netfd_open(osfd[0])) != NULL && (p[1] = st_netfd_open(osfd[1])) != NULL && write(osfd[1], " ", 1) == 1) { + fd->aux_data = p; + return 0; + } + /* Error */ + err = errno; + if (p[0]) { + st_netfd_free(p[0]); + } + if (p[1]) { + st_netfd_free(p[1]); + } + close(osfd[0]); + close(osfd[1]); + free(p); + errno = err; + + return -1; +} + +static void _st_netfd_free_aux_data(_st_netfd_t *fd) +{ + _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; + + st_netfd_close(p[0]); + st_netfd_close(p[1]); + free(p); + fd->aux_data = NULL; +} + +_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout) +{ + int osfd, err; + _st_netfd_t *newfd; + _st_netfd_t **p = (_st_netfd_t **) fd->aux_data; + ssize_t n; + char c; + + for ( ; ; ) { + if (p == NULL) { + osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); + } else { + /* Get the lock */ + n = st_read(p[0], &c, 1, timeout); + if (n < 0) { + return NULL; + } + ST_ASSERT(n == 1); + /* Got the lock */ + osfd = accept(fd->osfd, addr, (socklen_t *)addrlen); + /* Unlock */ + err = errno; + n = st_write(p[1], &c, 1, timeout); + ST_ASSERT(n == 1); + errno = err; + } + if (osfd >= 0) { + break; + } + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return NULL; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return NULL; + } + } + + /* On some platforms the new socket created by accept() inherits */ + /* the nonblocking attribute of the listening socket */ +#if defined (MD_ACCEPT_NB_INHERITED) + newfd = _st_netfd_new(osfd, 0, 1); +#elif defined (MD_ACCEPT_NB_NOT_INHERITED) + newfd = _st_netfd_new(osfd, 1, 1); +#else + #error Unknown OS +#endif + + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; +} +#endif /* MD_ALWAYS_UNSERIALIZED_ACCEPT */ + +int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout) +{ + int n, err = 0; + + while (connect(fd->osfd, addr, addrlen) < 0) { + if (errno != EINTR) { + /* + * On some platforms, if connect() is interrupted (errno == EINTR) + * after the kernel binds the socket, a subsequent connect() + * attempt will fail with errno == EADDRINUSE. Ignore EADDRINUSE + * iff connect() was previously interrupted. See Rich Stevens' + * "UNIX Network Programming," Vol. 1, 2nd edition, p. 413 + * ("Interrupted connect"). + */ + if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) { + return -1; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + return -1; + } + /* Try to find out whether the connection setup succeeded or failed */ + n = sizeof(int); + if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, (socklen_t *)&n) < 0) { + return -1; + } + if (err) { + errno = err; + return -1; + } + break; + } + err = 1; + } + + return 0; +} + +ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) +{ + ssize_t n; + + while ((n = read(fd->osfd, buf, nbyte)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } + } + + return n; +} + +int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout) +{ + struct iovec iov, *riov; + int riov_size, rv; + + iov.iov_base = buf; + iov.iov_len = *resid; + riov = &iov; + riov_size = 1; + rv = st_readv_resid(fd, &riov, &riov_size, timeout); + *resid = iov.iov_len; + return rv; +} + +ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) +{ + ssize_t n; + + while ((n = readv(fd->osfd, iov, iov_size)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } + } + + return n; +} + +int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) +{ + ssize_t n; + + while (*iov_size > 0) { + if (*iov_size == 1) { + n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); + } else { + n = readv(fd->osfd, *iov, *iov_size); + } + if (n < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + } else if (n == 0) { + break; + } else { + while ((size_t) n >= (*iov)->iov_len) { + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) { + break; + } + } + if (*iov_size == 0) { + break; + } + (*iov)->iov_base = (char *) (*iov)->iov_base + n; + (*iov)->iov_len -= n; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } + } + + return 0; +} + +ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) +{ + size_t resid = nbyte; + return st_read_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1; +} + +int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, st_utime_t timeout) +{ + struct iovec iov, *riov; + int riov_size, rv; + + iov.iov_base = (void *) buf; /* we promise not to modify buf */ + iov.iov_len = *resid; + riov = &iov; + riov_size = 1; + rv = st_writev_resid(fd, &riov, &riov_size, timeout); + *resid = iov.iov_len; + return rv; +} + +ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout) +{ + size_t resid = nbyte; + return st_write_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1; +} + +ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) +{ + ssize_t n, rv; + size_t nleft, nbyte; + int index, iov_cnt; + struct iovec *tmp_iov; + struct iovec local_iov[_LOCAL_MAXIOV]; + + /* Calculate the total number of bytes to be sent */ + nbyte = 0; + for (index = 0; index < iov_size; index++) { + nbyte += iov[index].iov_len; + } + + rv = (ssize_t)nbyte; + nleft = nbyte; + tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ + iov_cnt = iov_size; + + while (nleft > 0) { + if (iov_cnt == 1) { + if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) { + rv = -1; + } + break; + } + if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + rv = -1; + break; + } + } else { + if ((size_t) n == nleft) { + break; + } + nleft -= n; + /* Find the next unwritten vector */ + n = (ssize_t)(nbyte - nleft); + for (index = 0; (size_t) n >= iov[index].iov_len; index++) { + n -= iov[index].iov_len; + } + + if (tmp_iov == iov) { + /* Must copy iov's around */ + if (iov_size - index <= _LOCAL_MAXIOV) { + tmp_iov = local_iov; + } else { + tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec)); + if (tmp_iov == NULL) { + return -1; + } + } + } + + /* Fill in the first partial read */ + tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[n]); + tmp_iov[0].iov_len = iov[index].iov_len - n; + index++; + /* Copy the remaining vectors */ + for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) { + tmp_iov[iov_cnt].iov_base = iov[index].iov_base; + tmp_iov[iov_cnt].iov_len = iov[index].iov_len; + } + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + rv = -1; + break; + } + } + + if (tmp_iov != iov && tmp_iov != local_iov) { + free(tmp_iov); + } + + return rv; +} + +int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) +{ + ssize_t n; + + while (*iov_size > 0) { + if (*iov_size == 1) { + n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len); + } else { + n = writev(fd->osfd, *iov, *iov_size); + } + if (n < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + } else { + while ((size_t) n >= (*iov)->iov_len) { + n -= (*iov)->iov_len; + (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len; + (*iov)->iov_len = 0; + (*iov)++; + (*iov_size)--; + if (n == 0) { + break; + } + } + if (*iov_size == 0) { + break; + } + (*iov)->iov_base = (char *) (*iov)->iov_base + n; + (*iov)->iov_len -= n; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + return -1; + } + } + + return 0; +} + +/* + * Simple I/O functions for UDP. + */ +int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout) +{ + int n; + + while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } + } + + return n; +} + +int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout) +{ + int n; + + while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + return -1; + } + } + + return n; +} + +int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeout) +{ + int n; + + while ((n = recvmsg(fd->osfd, msg, flags)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes readable */ + if (st_netfd_poll(fd, POLLIN, timeout) < 0) { + return -1; + } + } + + return n; +} + +int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, st_utime_t timeout) +{ + int n; + + while ((n = sendmsg(fd->osfd, msg, flags)) < 0) { + if (errno == EINTR) { + continue; + } + if (!_IO_NOT_READY_ERROR) { + return -1; + } + /* Wait until the socket becomes writable */ + if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { + return -1; + } + } + + return n; +} + +/* + * To open FIFOs or other special files. + */ +_st_netfd_t *st_open(const char *path, int oflags, mode_t mode) +{ + int osfd, err; + _st_netfd_t *newfd; + + while ((osfd = open(path, oflags | O_NONBLOCK, mode)) < 0) { + if (errno != EINTR) { + return NULL; + } + } + + newfd = _st_netfd_new(osfd, 0, 0); + if (!newfd) { + err = errno; + close(osfd); + errno = err; + } + + return newfd; +} + diff --git a/trunk/research/st/key.c b/trunk/research/st/key.c new file mode 100644 index 000000000..49d778a18 --- /dev/null +++ b/trunk/research/st/key.c @@ -0,0 +1,116 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include "common.h" + +/* + * Destructor table for per-thread private data + */ +static _st_destructor_t _st_destructors[ST_KEYS_MAX]; +static int key_max = 0; + +/* + * Return a key to be used for thread specific data + */ +int st_key_create(int *keyp, _st_destructor_t destructor) +{ + if (key_max >= ST_KEYS_MAX) { + errno = EAGAIN; + return -1; + } + + *keyp = key_max++; + _st_destructors[*keyp] = destructor; + + return 0; +} + +int st_key_getlimit(void) +{ + return ST_KEYS_MAX; +} + +int st_thread_setspecific(int key, void *value) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (key < 0 || key >= key_max) { + errno = EINVAL; + return -1; + } + + if (value != me->private_data[key]) { + /* free up previously set non-NULL data value */ + if (me->private_data[key] && _st_destructors[key]) { + (*_st_destructors[key])(me->private_data[key]); + } + me->private_data[key] = value; + } + + return 0; +} + +void *st_thread_getspecific(int key) +{ + if (key < 0 || key >= key_max) { + return NULL; + } + + return ((_ST_CURRENT_THREAD())->private_data[key]); +} + +/* + * Free up all per-thread private data + */ +void _st_thread_cleanup(_st_thread_t *thread) +{ + int key; + + for (key = 0; key < key_max; key++) { + if (thread->private_data[key] && _st_destructors[key]) { + (*_st_destructors[key])(thread->private_data[key]); + thread->private_data[key] = NULL; + } + } +} + diff --git a/trunk/research/st/md.S b/trunk/research/st/md.S new file mode 100755 index 000000000..883da302b --- /dev/null +++ b/trunk/research/st/md.S @@ -0,0 +1,151 @@ +/* + * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. + * All Rights Reserved. + */ + +/****************************************************************/ + +#if defined(__i386__) + +/* + * Internal __jmp_buf layout + */ +#define JB_BX 0 +#define JB_SI 1 +#define JB_DI 2 +#define JB_BP 3 +#define JB_SP 4 +#define JB_PC 5 + + .file "md.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ +.globl _st_md_cxt_save + .type _st_md_cxt_save, @function + .align 16 +_st_md_cxt_save: + movl 4(%esp), %eax + + /* + * Save registers. + */ + movl %ebx, (JB_BX*4)(%eax) + movl %esi, (JB_SI*4)(%eax) + movl %edi, (JB_DI*4)(%eax) + /* Save SP */ + leal 4(%esp), %ecx + movl %ecx, (JB_SP*4)(%eax) + /* Save PC we are returning to */ + movl 0(%esp), %ecx + movl %ecx, (JB_PC*4)(%eax) + /* Save caller frame pointer */ + movl %ebp, (JB_BP*4)(%eax) + xorl %eax, %eax + ret + .size _st_md_cxt_save, .-_st_md_cxt_save + + +/****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ +.globl _st_md_cxt_restore + .type _st_md_cxt_restore, @function + .align 16 +_st_md_cxt_restore: + /* First argument is jmp_buf */ + movl 4(%esp), %ecx + /* Second argument is return value */ + movl 8(%esp), %eax + /* Set the return address */ + movl (JB_PC*4)(%ecx), %edx + /* + * Restore registers. + */ + movl (JB_BX*4)(%ecx), %ebx + movl (JB_SI*4)(%ecx), %esi + movl (JB_DI*4)(%ecx), %edi + movl (JB_BP*4)(%ecx), %ebp + movl (JB_SP*4)(%ecx), %esp + testl %eax, %eax + jnz 1f + incl %eax + /* Jump to saved PC */ +1: jmp *%edx + .size _st_md_cxt_restore, .-_st_md_cxt_restore + +/****************************************************************/ + +#elif defined(__amd64__) || defined(__x86_64__) + +/* + * Internal __jmp_buf layout + */ +#define JB_RBX 0 +#define JB_RBP 1 +#define JB_R12 2 +#define JB_R13 3 +#define JB_R14 4 +#define JB_R15 5 +#define JB_RSP 6 +#define JB_PC 7 + + .file "md.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ +.globl _st_md_cxt_save + .type _st_md_cxt_save, @function + .align 16 +_st_md_cxt_save: + /* + * Save registers. + */ + movq %rbx, (JB_RBX*8)(%rdi) + movq %rbp, (JB_RBP*8)(%rdi) + movq %r12, (JB_R12*8)(%rdi) + movq %r13, (JB_R13*8)(%rdi) + movq %r14, (JB_R14*8)(%rdi) + movq %r15, (JB_R15*8)(%rdi) + /* Save SP */ + leaq 8(%rsp), %rdx + movq %rdx, (JB_RSP*8)(%rdi) + /* Save PC we are returning to */ + movq (%rsp), %rax + movq %rax, (JB_PC*8)(%rdi) + xorq %rax, %rax + ret + .size _st_md_cxt_save, .-_st_md_cxt_save + + +/****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ +.globl _st_md_cxt_restore + .type _st_md_cxt_restore, @function + .align 16 +_st_md_cxt_restore: + /* + * Restore registers. + */ + movq (JB_RBX*8)(%rdi), %rbx + movq (JB_RBP*8)(%rdi), %rbp + movq (JB_R12*8)(%rdi), %r12 + movq (JB_R13*8)(%rdi), %r13 + movq (JB_R14*8)(%rdi), %r14 + movq (JB_R15*8)(%rdi), %r15 + /* Set return value */ + test %esi, %esi + mov $01, %eax + cmove %eax, %esi + mov %esi, %eax + movq (JB_PC*8)(%rdi), %rdx + movq (JB_RSP*8)(%rdi), %rsp + /* Jump to saved PC */ + jmpq *%rdx + .size _st_md_cxt_restore, .-_st_md_cxt_restore + +/****************************************************************/ + +#endif + diff --git a/trunk/research/st/md.h b/trunk/research/st/md.h new file mode 100644 index 000000000..bf82f4d55 --- /dev/null +++ b/trunk/research/st/md.h @@ -0,0 +1,193 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#ifndef __ST_MD_H__ +#define __ST_MD_H__ + +#if defined(ETIMEDOUT) && !defined(ETIME) + #define ETIME ETIMEDOUT +#endif + +#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON) + #define MAP_ANON MAP_ANONYMOUS +#endif + +#ifndef MAP_FAILED + #define MAP_FAILED -1 +#endif + +/***************************************** + * Platform specifics + */ +#if defined (LINUX) + /* linux ok, defined bellow */ +#elif defined (AIX) + #error "AIX not supported" +#elif defined (CYGWIN) + #error "CYGWIN not supported" +#elif defined (DARWIN) + #error "DARWIN not supported" +#elif defined (FREEBSD) + #error "FREEBSD not supported" +#elif defined (HPUX) + #error "HPUX not supported" +#elif defined (IRIX) + #error "IRIX not supported" +#elif defined (NETBSD) + #error "NETBSD not supported" +#elif defined (OPENBSD) + #error "OPENBSD not supported" +#elif defined (OSF1) + #error "OSF1 not supported" +#elif defined (SOLARIS) + #error "SOLARIS not supported" +#else + #error "Unknown OS" +#endif /* OS */ + +/* linux only, defined bellow */ +/* + * These are properties of the linux kernel and are the same on every + * flavor and architecture. + */ +#define MD_USE_BSD_ANON_MMAP +#define MD_ACCEPT_NB_NOT_INHERITED +#define MD_ALWAYS_UNSERIALIZED_ACCEPT +/* + * Modern GNU/Linux is Posix.1g compliant. + */ +#define MD_HAVE_SOCKLEN_T + +/* + * All architectures and flavors of linux have the gettimeofday + * function but if you know of a faster way, use it. + */ +#define MD_GET_UTIME() \ + struct timeval tv; \ + (void) gettimeofday(&tv, NULL); \ + return (tv.tv_sec * 1000000LL + tv.tv_usec) + +#if defined(__mips__) + #define MD_STACK_GROWS_DOWN +#else /* Not or mips */ + /* + * On linux, there are a few styles of jmpbuf format. These vary based + * on architecture/glibc combination. + * + * Most of the glibc based toggles were lifted from: + * mozilla/nsprpub/pr/include/md/_linux.h + */ + /* + * Starting with glibc 2.4, JB_SP definitions are not public anymore. + * They, however, can still be found in glibc source tree in + * architecture-specific "jmpbuf-offsets.h" files. + * Most importantly, the content of jmp_buf is mangled by setjmp to make + * it completely opaque (the mangling can be disabled by setting the + * LD_POINTER_GUARD environment variable before application execution). + * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore + * functions as a setjmp/longjmp replacement wherever they are available + * unless USE_LIBC_SETJMP is defined. + */ + #if defined(__i386__) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BUILTIN_SETJMP + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #ifndef JB_SP + #define JB_SP 4 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP] + #else + /* not an error but certainly cause for caution */ + #error "Untested use of old glibc on i386" + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp + #endif + #elif defined(__amd64__) || defined(__x86_64__) + #define MD_STACK_GROWS_DOWN + #define MD_USE_BUILTIN_SETJMP + + #ifndef JB_RSP + #define JB_RSP 6 + #endif + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP] + #elif defined(__arm__) + #define MD_STACK_GROWS_DOWN + + #if defined(__GLIBC__) && __GLIBC__ >= 2 + #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8] + #else + #error "ARM/Linux pre-glibc2 not supported yet" + #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + #else + #error "Unknown CPU architecture" + #endif /* Cases with common MD_INIT_CONTEXT and different SP locations */ +#endif /* Cases with different MD_INIT_CONTEXT */ + +#if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP) + /* i386/x86_64 */ + #define MD_SETJMP(env) _st_md_cxt_save(env) + #define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) + + extern int _st_md_cxt_save(jmp_buf env); + extern void _st_md_cxt_restore(jmp_buf env, int val); +#else + /* arm/mips */ + #define MD_SETJMP(env) setjmp(env) + #define MD_LONGJMP(env, val) longjmp(env, val) +#endif + +/***************************************** + * Other defines + */ +#ifndef MD_STACK_PAD_SIZE + #define MD_STACK_PAD_SIZE 128 +#endif + +#if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t) + #define socklen_t int +#endif + +#ifndef MD_CAP_STACK + #define MD_CAP_STACK(var_addr) +#endif + +#endif /* !__ST_MD_H__ */ + diff --git a/trunk/research/st/public.h b/trunk/research/st/public.h new file mode 100644 index 000000000..3275191bc --- /dev/null +++ b/trunk/research/st/public.h @@ -0,0 +1,164 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#ifndef __ST_THREAD_H__ +#define __ST_THREAD_H__ + +#include +#include +#include +#include +#include +#include +#include + +#define ST_VERSION "1.9" +#define ST_VERSION_MAJOR 1 +#define ST_VERSION_MINOR 9 + +/* Undefine this to remove the context switch callback feature. */ +#define ST_SWITCH_CB + +#ifndef ETIME + #define ETIME ETIMEDOUT +#endif + +#ifndef ST_UTIME_NO_TIMEOUT + #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) +#endif + +#ifndef ST_UTIME_NO_WAIT + #define ST_UTIME_NO_WAIT 0 +#endif + +#define ST_EVENTSYS_DEFAULT 0 +#define ST_EVENTSYS_SELECT 1 +#define ST_EVENTSYS_POLL 2 +#define ST_EVENTSYS_ALT 3 + +#ifdef __cplusplus +extern "C" { +#endif + typedef unsigned long long st_utime_t; + typedef struct _st_thread * st_thread_t; + typedef struct _st_cond * st_cond_t; + typedef struct _st_mutex * st_mutex_t; + typedef struct _st_netfd * st_netfd_t; + #ifdef ST_SWITCH_CB + typedef void (*st_switch_cb_t)(void); + #endif + + extern int st_init(void); + extern int st_getfdlimit(void); + + extern int st_set_eventsys(int eventsys); + extern int st_get_eventsys(void); + extern const char *st_get_eventsys_name(void); + + #ifdef ST_SWITCH_CB + extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb); + extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb); + #endif + + extern st_thread_t st_thread_self(void); + extern void st_thread_exit(void *retval); + extern int st_thread_join(st_thread_t trd, void **retvalp); + extern void st_thread_interrupt(st_thread_t trd); + extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stack_size); + extern int st_randomize_stacks(int on); + extern int st_set_utime_function(st_utime_t (*func)(void)); + + extern st_utime_t st_utime(void); + extern st_utime_t st_utime_last_clock(void); + extern int st_timecache_set(int on); + extern time_t st_time(void); + extern int st_usleep(st_utime_t usecs); + extern int st_sleep(int secs); + extern st_cond_t st_cond_new(void); + extern int st_cond_destroy(st_cond_t cvar); + extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); + extern int st_cond_wait(st_cond_t cvar); + extern int st_cond_signal(st_cond_t cvar); + extern int st_cond_broadcast(st_cond_t cvar); + extern st_mutex_t st_mutex_new(void); + extern int st_mutex_destroy(st_mutex_t lock); + extern int st_mutex_lock(st_mutex_t lock); + extern int st_mutex_unlock(st_mutex_t lock); + extern int st_mutex_trylock(st_mutex_t lock); + + extern int st_key_create(int *keyp, void (*destructor)(void *)); + extern int st_key_getlimit(void); + extern int st_thread_setspecific(int key, void *value); + extern void *st_thread_getspecific(int key); + + extern st_netfd_t st_netfd_open(int osfd); + extern st_netfd_t st_netfd_open_socket(int osfd); + extern void st_netfd_free(st_netfd_t fd); + extern int st_netfd_close(st_netfd_t fd); + extern int st_netfd_fileno(st_netfd_t fd); + extern void st_netfd_setspecific(st_netfd_t fd, void *value, void (*destructor)(void *)); + extern void *st_netfd_getspecific(st_netfd_t fd); + extern int st_netfd_serialize_accept(st_netfd_t fd); + extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout); + + extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout); + extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout); + extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout); + extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); + extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout); + extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, st_utime_t timeout); + extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); + extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); + extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, st_utime_t timeout); + extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, st_utime_t timeout); + extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout); + extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout); + extern int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout); + extern int st_sendto(st_netfd_t fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout); + extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, st_utime_t timeout); + extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, st_utime_t timeout); + extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); + + #ifdef DEBUG + extern void _st_show_thread_stack(st_thread_t thread, const char *messg); + extern void _st_iterate_threads(void); + #endif +#ifdef __cplusplus +} +#endif + +#endif /* !__ST_THREAD_H__ */ + diff --git a/trunk/research/st/sched.c b/trunk/research/st/sched.c new file mode 100755 index 000000000..66095cfd1 --- /dev/null +++ b/trunk/research/st/sched.c @@ -0,0 +1,680 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include +#include +#include +#include "common.h" + +/* Global data */ +_st_vp_t _st_this_vp; /* This VP */ +_st_thread_t *_st_this_thread; /* Current thread */ +int _st_active_count = 0; /* Active thread count */ + +time_t _st_curr_time = 0; /* Current time as returned by time(2) */ +st_utime_t _st_last_tset; /* Last time it was fetched */ + +int st_poll(struct pollfd *pds, int npds, st_utime_t timeout) +{ + struct pollfd *pd; + struct pollfd *epd = pds + npds; + _st_pollq_t pq; + _st_thread_t *me = _ST_CURRENT_THREAD(); + int n; + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if ((*_st_eventsys->pollset_add)(pds, npds) < 0) { + return -1; + } + + pq.pds = pds; + pq.npds = npds; + pq.thread = me; + pq.on_ioq = 1; + _ST_ADD_IOQ(pq); + if (timeout != ST_UTIME_NO_TIMEOUT) { + _ST_ADD_SLEEPQ(me, timeout); + } + me->state = _ST_ST_IO_WAIT; + + _ST_SWITCH_CONTEXT(me); + + n = 0; + if (pq.on_ioq) { + /* If we timed out, the pollq might still be on the ioq. Remove it */ + _ST_DEL_IOQ(pq); + (*_st_eventsys->pollset_del)(pds, npds); + } else { + /* Count the number of ready descriptors */ + for (pd = pds; pd < epd; pd++) { + if (pd->revents) { + n++; + } + } + } + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return n; +} + +void _st_vp_schedule(void) +{ + _st_thread_t *trd; + + if (_ST_RUNQ.next != &_ST_RUNQ) { + /* Pull thread off of the run queue */ + trd = _ST_THREAD_PTR(_ST_RUNQ.next); + _ST_DEL_RUNQ(trd); + } else { + /* If there are no threads to run, switch to the idle thread */ + trd = _st_this_vp.idle_thread; + } + ST_ASSERT(trd->state == _ST_ST_RUNNABLE); + + /* Resume the thread */ + trd->state = _ST_ST_RUNNING; + _ST_RESTORE_CONTEXT(trd); +} + +/* + * Initialize this Virtual Processor + */ +int st_init(void) +{ + _st_thread_t *trd; + + if (_st_active_count) { + /* Already initialized */ + return 0; + } + + /* We can ignore return value here */ + st_set_eventsys(ST_EVENTSYS_DEFAULT); + + if (_st_io_init() < 0) { + return -1; + } + + memset(&_st_this_vp, 0, sizeof(_st_vp_t)); + + ST_INIT_CLIST(&_ST_RUNQ); + ST_INIT_CLIST(&_ST_IOQ); + ST_INIT_CLIST(&_ST_ZOMBIEQ); +#ifdef DEBUG + ST_INIT_CLIST(&_ST_THREADQ); +#endif + + if ((*_st_eventsys->init)() < 0) { + return -1; + } + + _st_this_vp.pagesize = getpagesize(); + _st_this_vp.last_clock = st_utime(); + + /* + * Create idle thread + */ + _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, NULL, 0, 0); + if (!_st_this_vp.idle_thread) { + return -1; + } + _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD; + _st_active_count--; + _ST_DEL_RUNQ(_st_this_vp.idle_thread); + + /* + * Initialize primordial thread + */ + trd = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) + + (ST_KEYS_MAX * sizeof(void *))); + if (!trd) { + return -1; + } + trd->private_data = (void **) (trd + 1); + trd->state = _ST_ST_RUNNING; + trd->flags = _ST_FL_PRIMORDIAL; + _ST_SET_CURRENT_THREAD(trd); + _st_active_count++; +#ifdef DEBUG + _ST_ADD_THREADQ(trd); +#endif + + return 0; +} + +#ifdef ST_SWITCH_CB +st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb) +{ + st_switch_cb_t ocb = _st_this_vp.switch_in_cb; + _st_this_vp.switch_in_cb = cb; + return ocb; +} + +st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb) +{ + st_switch_cb_t ocb = _st_this_vp.switch_out_cb; + _st_this_vp.switch_out_cb = cb; + return ocb; +} +#endif + +/* + * Start function for the idle thread + */ +/* ARGSUSED */ +void *_st_idle_thread_start(void *arg) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + while (_st_active_count > 0) { + /* Idle vp till I/O is ready or the smallest timeout expired */ + _ST_VP_IDLE(); + + /* Check sleep queue for expired threads */ + _st_vp_check_clock(); + + me->state = _ST_ST_RUNNABLE; + _ST_SWITCH_CONTEXT(me); + } + + /* No more threads */ + exit(0); + + /* NOTREACHED */ + return NULL; +} + +void st_thread_exit(void *retval) +{ + _st_thread_t *trd = _ST_CURRENT_THREAD(); + + trd->retval = retval; + _st_thread_cleanup(trd); + _st_active_count--; + if (trd->term) { + /* Put thread on the zombie queue */ + trd->state = _ST_ST_ZOMBIE; + _ST_ADD_ZOMBIEQ(trd); + + /* Notify on our termination condition variable */ + st_cond_signal(trd->term); + + /* Switch context and come back later */ + _ST_SWITCH_CONTEXT(trd); + + /* Continue the cleanup */ + st_cond_destroy(trd->term); + trd->term = NULL; + } + +#ifdef DEBUG + _ST_DEL_THREADQ(trd); +#endif + + if (!(trd->flags & _ST_FL_PRIMORDIAL)) { + _st_stack_free(trd->stack); + } + + /* Find another thread to run */ + _ST_SWITCH_CONTEXT(trd); + /* Not going to land here */ +} + +int st_thread_join(_st_thread_t *trd, void **retvalp) +{ + _st_cond_t *term = trd->term; + + /* Can't join a non-joinable thread */ + if (term == NULL) { + errno = EINVAL; + return -1; + } + if (_ST_CURRENT_THREAD() == trd) { + errno = EDEADLK; + return -1; + } + + /* Multiple threads can't wait on the same joinable thread */ + if (term->wait_q.next != &term->wait_q) { + errno = EINVAL; + return -1; + } + + while (trd->state != _ST_ST_ZOMBIE) { + if (st_cond_timedwait(term, ST_UTIME_NO_TIMEOUT) != 0) { + return -1; + } + } + + if (retvalp) { + *retvalp = trd->retval; + } + + /* + * Remove target thread from the zombie queue and make it runnable. + * When it gets scheduled later, it will do the clean up. + */ + trd->state = _ST_ST_RUNNABLE; + _ST_DEL_ZOMBIEQ(trd); + _ST_ADD_RUNQ(trd); + + return 0; +} + +void _st_thread_main(void) +{ + _st_thread_t *trd = _ST_CURRENT_THREAD(); + + /* + * Cap the stack by zeroing out the saved return address register + * value. This allows some debugging/profiling tools to know when + * to stop unwinding the stack. It's a no-op on most platforms. + */ + MD_CAP_STACK(&trd); + + /* Run thread main */ + trd->retval = (*trd->start)(trd->arg); + + /* All done, time to go away */ + st_thread_exit(trd->retval); +} + +/* + * Insert "thread" into the timeout heap, in the position + * specified by thread->heap_index. See docs/timeout_heap.txt + * for details about the timeout heap. + */ +static _st_thread_t **heap_insert(_st_thread_t *trd) +{ + int target = trd->heap_index; + int s = target; + _st_thread_t **p = &_ST_SLEEPQ; + int bits = 0; + int bit; + int index = 1; + + while (s) { + s >>= 1; + bits++; + } + + for (bit = bits - 2; bit >= 0; bit--) { + if (trd->due < (*p)->due) { + _st_thread_t *t = *p; + trd->left = t->left; + trd->right = t->right; + *p = trd; + trd->heap_index = index; + trd = t; + } + index <<= 1; + if (target & (1 << bit)) { + p = &((*p)->right); + index |= 1; + } else { + p = &((*p)->left); + } + } + + trd->heap_index = index; + *p = trd; + trd->left = trd->right = NULL; + + return p; +} + +/* + * Delete "thread" from the timeout heap. + */ +static void heap_delete(_st_thread_t *trd) +{ + _st_thread_t *t, **p; + int bits = 0; + int s, bit; + + /* First find and unlink the last heap element */ + p = &_ST_SLEEPQ; + s = _ST_SLEEPQ_SIZE; + while (s) { + s >>= 1; + bits++; + } + + for (bit = bits - 2; bit >= 0; bit--) { + if (_ST_SLEEPQ_SIZE & (1 << bit)) { + p = &((*p)->right); + } else { + p = &((*p)->left); + } + } + + t = *p; + *p = NULL; + --_ST_SLEEPQ_SIZE; + if (t != trd) { + /* + * Insert the unlinked last element in place of the element we are deleting + */ + t->heap_index = trd->heap_index; + p = heap_insert(t); + t = *p; + t->left = trd->left; + t->right = trd->right; + + /* + * Reestablish the heap invariant. + */ + for (;;) { + _st_thread_t *y; /* The younger child */ + int index_tmp; + + if (t->left == NULL) { + break; + } else if (t->right == NULL) { + y = t->left; + } else if (t->left->due < t->right->due) { + y = t->left; + } else { + y = t->right; + } + + if (t->due > y->due) { + _st_thread_t *tl = y->left; + _st_thread_t *tr = y->right; + *p = y; + if (y == t->left) { + y->left = t; + y->right = t->right; + p = &y->left; + } else { + y->left = t->left; + y->right = t; + p = &y->right; + } + t->left = tl; + t->right = tr; + index_tmp = t->heap_index; + t->heap_index = y->heap_index; + y->heap_index = index_tmp; + } else { + break; + } + } + } + + trd->left = trd->right = NULL; +} + +void _st_add_sleep_q(_st_thread_t *trd, st_utime_t timeout) +{ + trd->due = _ST_LAST_CLOCK + timeout; + trd->flags |= _ST_FL_ON_SLEEPQ; + trd->heap_index = ++_ST_SLEEPQ_SIZE; + heap_insert(trd); +} + +void _st_del_sleep_q(_st_thread_t *trd) +{ + heap_delete(trd); + trd->flags &= ~_ST_FL_ON_SLEEPQ; +} + +void _st_vp_check_clock(void) +{ + _st_thread_t *trd; + st_utime_t elapsed, now; + + now = st_utime(); + elapsed = now - _ST_LAST_CLOCK; + _ST_LAST_CLOCK = now; + + if (_st_curr_time && now - _st_last_tset > 999000) { + _st_curr_time = time(NULL); + _st_last_tset = now; + } + + while (_ST_SLEEPQ != NULL) { + trd = _ST_SLEEPQ; + ST_ASSERT(trd->flags & _ST_FL_ON_SLEEPQ); + if (trd->due > now) { + break; + } + _ST_DEL_SLEEPQ(trd); + + /* If thread is waiting on condition variable, set the time out flag */ + if (trd->state == _ST_ST_COND_WAIT) { + trd->flags |= _ST_FL_TIMEDOUT; + } + + /* Make thread runnable */ + ST_ASSERT(!(trd->flags & _ST_FL_IDLE_THREAD)); + trd->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(trd); + } +} + +void st_thread_interrupt(_st_thread_t* trd) +{ + /* If thread is already dead */ + if (trd->state == _ST_ST_ZOMBIE) { + return; + } + + trd->flags |= _ST_FL_INTERRUPT; + + if (trd->state == _ST_ST_RUNNING || trd->state == _ST_ST_RUNNABLE) { + return; + } + + if (trd->flags & _ST_FL_ON_SLEEPQ) { + _ST_DEL_SLEEPQ(trd); + } + + /* Make thread runnable */ + trd->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(trd); +} + +_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size) +{ + _st_thread_t *trd; + _st_stack_t *stack; + void **ptds; + char *sp; + + /* Adjust stack size */ + if (stk_size == 0) { + stk_size = ST_DEFAULT_STACK_SIZE; + } + stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE; + stack = _st_stack_new(stk_size); + if (!stack) { + return NULL; + } + + /* Allocate thread object and per-thread data off the stack */ +#if defined (MD_STACK_GROWS_DOWN) + sp = stack->stk_top; + /* + * The stack segment is split in the middle. The upper half is used + * as backing store for the register stack which grows upward. + * The lower half is used for the traditional memory stack which + * grows downward. Both stacks start in the middle and grow outward + * from each other. + */ + /** + The below comments is by winlin: + The Stack public structure: + +--------------------------------------------------------------+ + | stack | + +--------------------------------------------------------------+ + bottom top + The code bellow use the stack as: + +-----------------+-----------------+-------------+------------+ + | stack of thread |pad+align(128B+) |thread(336B) | keys(128B) | + +-----------------+-----------------+-------------+------------+ + bottom sp trd ptds top + (context[0].__jmpbuf.sp) (private_data) + */ + sp = sp - (ST_KEYS_MAX * sizeof(void *)); + ptds = (void **) sp; + sp = sp - sizeof(_st_thread_t); + trd = (_st_thread_t *) sp; + + /* Make stack 64-byte aligned */ + if ((unsigned long)sp & 0x3f) { + sp = sp - ((unsigned long)sp & 0x3f); + } + stack->sp = sp - _ST_STACK_PAD_SIZE; +#else + #error "Only Supports Stack Grown Down" +#endif + + memset(trd, 0, sizeof(_st_thread_t)); + memset(ptds, 0, ST_KEYS_MAX * sizeof(void *)); + + /* Initialize thread */ + trd->private_data = ptds; + trd->stack = stack; + trd->start = start; + trd->arg = arg; + +// by winlin, expand macro MD_INIT_CONTEXT +#if defined(__mips__) + MD_SETJMP((trd)->context); + trd->context[0].__jmpbuf[0].__pc = (__ptr_t) _st_thread_main; + trd->context[0].__jmpbuf[0].__sp = stack->sp; +#else + if (MD_SETJMP((trd)->context)) { + _st_thread_main(); + } + MD_GET_SP(trd) = (long) (stack->sp); +#endif + + /* If thread is joinable, allocate a termination condition variable */ + if (joinable) { + trd->term = st_cond_new(); + if (trd->term == NULL) { + _st_stack_free(trd->stack); + return NULL; + } + } + + /* Make thread runnable */ + trd->state = _ST_ST_RUNNABLE; + _st_active_count++; + _ST_ADD_RUNQ(trd); +#ifdef DEBUG + _ST_ADD_THREADQ(trd); +#endif + + return trd; +} + +_st_thread_t *st_thread_self(void) +{ + return _ST_CURRENT_THREAD(); +} + +#ifdef DEBUG +/* ARGSUSED */ +void _st_show_thread_stack(_st_thread_t *trd, const char *messg) +{ +} + +/* To be set from debugger */ +int _st_iterate_threads_flag = 0; + +void _st_iterate_threads(void) +{ + static _st_thread_t *trd = NULL; + static jmp_buf orig_jb, save_jb; + _st_clist_t *q; + + if (!_st_iterate_threads_flag) { + if (trd) { + memcpy(trd->context, save_jb, sizeof(jmp_buf)); + MD_LONGJMP(orig_jb, 1); + } + return; + } + + if (trd) { + memcpy(trd->context, save_jb, sizeof(jmp_buf)); + _st_show_thread_stack(trd, NULL); + } else { + if (MD_SETJMP(orig_jb)) { + _st_iterate_threads_flag = 0; + trd = NULL; + _st_show_thread_stack(trd, "Iteration completed"); + return; + } + trd = _ST_CURRENT_THREAD(); + _st_show_thread_stack(trd, "Iteration started"); + } + + q = trd->tlink.next; + if (q == &_ST_THREADQ) { + q = q->next; + } + ST_ASSERT(q != &_ST_THREADQ); + trd = _ST_THREAD_THREADQ_PTR(q); + if (trd == _ST_CURRENT_THREAD()) { + MD_LONGJMP(orig_jb, 1); + } + memcpy(save_jb, trd->context, sizeof(jmp_buf)); + MD_LONGJMP(trd->context, 1); +} +#endif /* DEBUG */ + diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c new file mode 100644 index 000000000..09bc5eacc --- /dev/null +++ b/trunk/research/st/srs.c @@ -0,0 +1,497 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "public.h" + +#define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n") + +int io_port = 1990; +int sleep_ms = 100; + +void stack_print(long int previous_sp, int level) +{ + if (level <= 0) { + return; + } + + register long int rsp asm("sp"); + char buf[level * 1024]; + + stack_print(rsp, level - 1); + + srs_trace("%d. psp=%#lx, sp=%#lx, size=%dB(%dB+%dKB)", + level, previous_sp, rsp, (int)(previous_sp - rsp), + (int)(previous_sp - rsp - sizeof(buf)), (int)(sizeof(buf) / 1024)); +} + +int huge_stack_test() +{ + srs_trace("==================================================="); + srs_trace("huge_stack test: start"); + + register long int rsp asm("sp"); + stack_print(rsp, 10); + + srs_trace("huge_stack test: end"); + + return 0; +} + +int sleep_test() +{ + srs_trace("==================================================="); + srs_trace("sleep test: start"); + + srs_trace("1. sleep..."); + st_utime_t start = st_utime(); + st_usleep(sleep_ms * 1000); + st_utime_t end = st_utime(); + + srs_trace("2. sleep ok, sleep=%dus, deviation=%dus", + (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000)); + + srs_trace("sleep test: end"); + + return 0; +} + +void* sleep2_func0(void* arg) +{ + int sleep_ms = 100; + st_utime_t start = st_utime(); + st_usleep(sleep_ms * 1000); + st_utime_t end = st_utime(); + + srs_trace("sleep ok, sleep=%dus, deviation=%dus", + (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000)); + + return NULL; +} + +void* sleep2_func1(void* arg) +{ + int sleep_ms = 250; + st_utime_t start = st_utime(); + st_usleep(sleep_ms * 1000); + st_utime_t end = st_utime(); + + srs_trace("sleep ok, sleep=%dus, deviation=%dus", + (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000)); + + return NULL; +} + +int sleep2_test() +{ + srs_trace("==================================================="); + srs_trace("sleep2 test: start"); + + st_thread_t trd0 = st_thread_create(sleep2_func0, NULL, 1, 0); + st_thread_t trd1 = st_thread_create(sleep2_func1, NULL, 1, 0); + st_thread_join(trd0, NULL); + st_thread_join(trd1, NULL); + + srs_trace("sleep test: end"); + + return 0; +} + +st_mutex_t sleep_work_cond = NULL; +void* sleep_deviation_func(void* arg) +{ + st_mutex_lock(sleep_work_cond); + srs_trace("2. work thread start."); + + int64_t i; + for (i = 0; i < 3000000000ULL; i++) { + } + + st_mutex_unlock(sleep_work_cond); + srs_trace("3. work thread end."); + + return NULL; +} + +int sleep_deviation_test() +{ + srs_trace("==================================================="); + srs_trace("sleep deviation test: start"); + + sleep_work_cond = st_mutex_new(); + + st_thread_create(sleep_deviation_func, NULL, 0, 0); + st_mutex_lock(sleep_work_cond); + + srs_trace("1. sleep..."); + st_utime_t start = st_utime(); + + // other thread to do some complex work. + st_mutex_unlock(sleep_work_cond); + st_usleep(1000 * 1000); + + st_utime_t end = st_utime(); + + srs_trace("4. sleep ok, sleep=%dus, deviation=%dus", + (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000)); + + st_mutex_lock(sleep_work_cond); + srs_trace("sleep deviation test: end"); + + st_mutex_destroy(sleep_work_cond); + + return 0; +} + +void* thread_func(void* arg) +{ + srs_trace("1. thread run"); + st_usleep(sleep_ms * 1000); + srs_trace("2. thread completed"); + return NULL; +} + +int thread_test() +{ + srs_trace("==================================================="); + srs_trace("thread test: start"); + + st_thread_t trd = st_thread_create(thread_func, NULL, 1, 0); + if (trd == NULL) { + srs_trace("st_thread_create failed"); + return -1; + } + + st_thread_join(trd, NULL); + srs_trace("3. thread joined"); + + srs_trace("thread test: end"); + + return 0; +} + +st_mutex_t sync_start = NULL; +st_cond_t sync_cond = NULL; +st_mutex_t sync_mutex = NULL; +st_cond_t sync_end = NULL; + +void* sync_master(void* arg) +{ + // wait for main to sync_start this thread. + st_mutex_lock(sync_start); + st_mutex_unlock(sync_start); + + st_usleep(sleep_ms * 1000); + st_cond_signal(sync_cond); + + st_mutex_lock(sync_mutex); + srs_trace("2. st mutex is ok"); + st_mutex_unlock(sync_mutex); + + st_usleep(sleep_ms * 1000); + srs_trace("3. st thread is ok"); + st_cond_signal(sync_cond); + + return NULL; +} + +void* sync_slave(void* arg) +{ + // lock mutex to control thread. + st_mutex_lock(sync_mutex); + + // wait for main to sync_start this thread. + st_mutex_lock(sync_start); + st_mutex_unlock(sync_start); + + // wait thread to ready. + st_cond_wait(sync_cond); + srs_trace("1. st cond is ok"); + + // release mutex to control thread + st_usleep(sleep_ms * 1000); + st_mutex_unlock(sync_mutex); + + // wait thread to exit. + st_cond_wait(sync_cond); + srs_trace("4. st is ok"); + + st_cond_signal(sync_end); + + return NULL; +} + +int sync_test() +{ + srs_trace("==================================================="); + srs_trace("sync test: start"); + + if ((sync_start = st_mutex_new()) == NULL) { + srs_trace("st_mutex_new sync_start failed"); + return -1; + } + st_mutex_lock(sync_start); + + if ((sync_cond = st_cond_new()) == NULL) { + srs_trace("st_cond_new cond failed"); + return -1; + } + + if ((sync_end = st_cond_new()) == NULL) { + srs_trace("st_cond_new end failed"); + return -1; + } + + if ((sync_mutex = st_mutex_new()) == NULL) { + srs_trace("st_mutex_new mutex failed"); + return -1; + } + + if (!st_thread_create(sync_master, NULL, 0, 0)) { + srs_trace("st_thread_create failed"); + return -1; + } + + if (!st_thread_create(sync_slave, NULL, 0, 0)) { + srs_trace("st_thread_create failed"); + return -1; + } + + // run all threads. + st_mutex_unlock(sync_start); + + st_cond_wait(sync_end); + srs_trace("sync test: end"); + + return 0; +} + +void* io_client(void* arg) +{ + + int fd; + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + srs_trace("create linux socket error."); + return NULL; + } + srs_trace("6. client create linux socket success. fd=%d", fd); + + st_netfd_t stfd; + if ((stfd = st_netfd_open_socket(fd)) == NULL){ + srs_trace("st_netfd_open_socket open socket failed."); + return NULL; + } + srs_trace("7. client st open socket success. fd=%d", fd); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(io_port); + addr.sin_addr.s_addr = INADDR_ANY; + if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1) { + srs_trace("bind socket error."); + return NULL; + } + + char buf[1024]; + if (st_read_fully(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { + srs_trace("st_read_fully failed"); + return NULL; + } + if (st_write(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { + srs_trace("st_write failed"); + return NULL; + } + + st_netfd_close(stfd); + + return NULL; +} + +int io_test() +{ + srs_trace("==================================================="); + srs_trace("io test: start, port=%d", io_port); + + int fd; + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + srs_trace("create linux socket error."); + return -1; + } + srs_trace("1. server create linux socket success. fd=%d", fd); + + int reuse_socket = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) { + srs_trace("setsockopt reuse-addr error."); + return -1; + } + srs_trace("2. server setsockopt reuse-addr success. fd=%d", fd); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(io_port); + addr.sin_addr.s_addr = INADDR_ANY; + if (bind(fd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) { + srs_trace("bind socket error."); + return -1; + } + srs_trace("3. server bind socket success. fd=%d", fd); + + if (listen(fd, 10) == -1) { + srs_trace("listen socket error."); + return -1; + } + srs_trace("4. server listen socket success. fd=%d", fd); + + st_netfd_t stfd; + if ((stfd = st_netfd_open_socket(fd)) == NULL){ + srs_trace("st_netfd_open_socket open socket failed."); + return -1; + } + srs_trace("5. server st open socket success. fd=%d", fd); + + if (!st_thread_create(io_client, NULL, 0, 0)) { + srs_trace("st_thread_create failed"); + return -1; + } + + st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT); + srs_trace("8. server get a client. fd=%d", st_netfd_fileno(client_stfd)); + + char buf[1024]; + if (st_write(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { + srs_trace("st_write failed"); + return -1; + } + if (st_read_fully(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) { + srs_trace("st_read_fully failed"); + return -1; + } + srs_trace("9. server io completed."); + + st_netfd_close(stfd); + st_netfd_close(client_stfd); + + srs_trace("io test: end"); + return 0; +} + +int pipe_test() +{ + srs_trace("==================================================="); + srs_trace("pipe test: start"); + + int fds[2]; + if (pipe(fds) < 0) { + srs_trace("pipe failed"); + return -1; + } + srs_trace("1. pipe ok, %d=>%d", fds[1], fds[0]); + + st_netfd_t fdw; + if ((fdw = st_netfd_open_socket(fds[1])) == NULL) { + srs_trace("st_netfd_open_socket open socket failed."); + return -1; + } + srs_trace("2. open write fd ok"); + + st_netfd_t fdr; + if ((fdr = st_netfd_open_socket(fds[0])) == NULL) { + srs_trace("st_netfd_open_socket open socket failed."); + return -1; + } + srs_trace("3. open read fd ok"); + + char buf[1024]; + if (st_write(fdw, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) { + srs_trace("st_write socket failed."); + return -1; + } + srs_trace("4. write to pipe ok"); + + if (st_read(fdr, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) { + srs_trace("st_read socket failed."); + return -1; + } + srs_trace("5. read from pipe ok"); + + st_netfd_close(fdw); + st_netfd_close(fdr); + + srs_trace("pipe test: end"); + return 0; +} + +int main(int argc, char** argv) +{ + srs_trace("ETIME=%d", ETIME); + + if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) { + srs_trace("st_set_eventsys failed"); + return -1; + } + + if (st_init() < 0) { + srs_trace("st_init failed"); + return -1; + } + + if (sleep2_test() < 0) { + srs_trace("sleep2_test failed"); + return -1; + } + + if (sleep_test() < 0) { + srs_trace("sleep_test failed"); + return -1; + } + + if (sleep_deviation_test() < 0) { + srs_trace("sleep_deviation_test failed"); + return -1; + } + + if (huge_stack_test() < 0) { + srs_trace("huge_stack_test failed"); + return -1; + } + + if (thread_test() < 0) { + srs_trace("thread_test failed"); + return -1; + } + + if (sync_test() < 0) { + srs_trace("sync_test failed"); + return -1; + } + + if (io_test() < 0) { + srs_trace("io_test failed"); + return -1; + } + + if (pipe_test() < 0) { + srs_trace("pipe_test failed"); + return -1; + } + + // cleanup. + srs_trace("wait for all thread completed"); + st_thread_exit(NULL); + // the following never enter, + // the above code will exit when all thread exit, + // current is a primordial st-thread, when all thread exit, + // the st idle thread will exit(0), see _st_idle_thread_start() + srs_trace("all thread completed"); + + return 0; +} + diff --git a/trunk/research/st/st/init b/trunk/research/st/st/init new file mode 100644 index 000000000..61604b75f --- /dev/null +++ b/trunk/research/st/st/init @@ -0,0 +1,3 @@ +#ifndef _st_icpp_init_stub +#define _st_icpp_init_stub +#endif diff --git a/trunk/research/st/st/st.upp b/trunk/research/st/st/st.upp new file mode 100755 index 000000000..dab6d4958 --- /dev/null +++ b/trunk/research/st/st/st.upp @@ -0,0 +1,18 @@ +file + main readonly separator, + ..\srs.c, + st readonly separator, + ..\common.h, + ..\event.c, + ..\io.c, + ..\key.c, + ..\md.h, + ..\md.S, + ..\public.h, + ..\sched.c, + ..\stk.c, + ..\sync.c; + +mainconfig + "" = "MAIN"; + diff --git a/trunk/research/st/stk.c b/trunk/research/st/stk.c new file mode 100644 index 000000000..c26223ba5 --- /dev/null +++ b/trunk/research/st/stk.c @@ -0,0 +1,169 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include +#include +#include "common.h" + +/* How much space to leave between the stacks, at each end */ +#define REDZONE _ST_PAGE_SIZE + +_st_clist_t _st_free_stacks = ST_INIT_STATIC_CLIST(&_st_free_stacks); +int _st_num_free_stacks = 0; +int _st_randomize_stacks = 0; + +static char *_st_new_stk_segment(int size); + +/** +The below comments is by winlin: +The stack memory struct: + | REDZONE | stack | extra | REDZONE | + +---------+------------------------+---------+---------+ + | 4k | | 4k/0 | 4k | + +---------+------------------------+---------+---------+ + vaddr bottom top +When _st_randomize_stacks is on, by st_randomize_stacks(), +the bottom and top will random movided in the extra: + long offset = (random() % extra) & ~0xf; + ts->stk_bottom += offset; + ts->stk_top += offset; +Both REDZONE are protected by mprotect when DEBUG is on. +*/ +_st_stack_t *_st_stack_new(int stack_size) +{ + _st_clist_t *qp; + _st_stack_t *ts; + int extra; + + // TODO: WINLIN: remove the stack reuse. + for (qp = _st_free_stacks.next; qp != &_st_free_stacks; qp = qp->next) { + ts = _ST_THREAD_STACK_PTR(qp); + if (ts->stk_size >= stack_size) { + /* Found a stack that is big enough */ + ST_REMOVE_LINK(&ts->links); + _st_num_free_stacks--; + ts->links.next = NULL; + ts->links.prev = NULL; + return ts; + } + } + + /* Make a new thread stack object. */ + if ((ts = (_st_stack_t *)calloc(1, sizeof(_st_stack_t))) == NULL) { + return NULL; + } + extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0; + ts->vaddr_size = stack_size + 2*REDZONE + extra; + ts->vaddr = _st_new_stk_segment(ts->vaddr_size); + if (!ts->vaddr) { + free(ts); + return NULL; + } + ts->stk_size = stack_size; + ts->stk_bottom = ts->vaddr + REDZONE; + ts->stk_top = ts->stk_bottom + stack_size; + +#ifdef DEBUG + mprotect(ts->vaddr, REDZONE, PROT_NONE); + mprotect(ts->stk_top + extra, REDZONE, PROT_NONE); +#endif + + if (extra) { + long offset = (random() % extra) & ~0xf; + + ts->stk_bottom += offset; + ts->stk_top += offset; + } + + return ts; +} + +/* + * Free the stack for the current thread + */ +void _st_stack_free(_st_stack_t *ts) +{ + if (!ts) { + return; + } + + /* Put the stack on the free list */ + ST_APPEND_LINK(&ts->links, _st_free_stacks.prev); + _st_num_free_stacks++; +} + +static char *_st_new_stk_segment(int size) +{ +#ifdef MALLOC_STACK + void *vaddr = malloc(size); +#else + #error "Only Supports Malloc Stack" +#endif + + return (char *)vaddr; +} + +/* Not used */ +#if 0 +void _st_delete_stk_segment(char *vaddr, int size) +{ +#ifdef MALLOC_STACK + free(vaddr); +#else + #error Unknown Stack Malloc +#endif +} +#endif + +int st_randomize_stacks(int on) +{ + int wason = _st_randomize_stacks; + + _st_randomize_stacks = on; + if (on) { + srandom((unsigned int) st_utime()); + } + + return wason; +} diff --git a/trunk/research/st/sync.c b/trunk/research/st/sync.c new file mode 100644 index 000000000..3e5324084 --- /dev/null +++ b/trunk/research/st/sync.c @@ -0,0 +1,352 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape Portable Runtime library. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Silicon Graphics, Inc. + * + * Portions created by SGI are Copyright (C) 2000-2001 Silicon + * Graphics, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * This file is derived directly from Netscape Communications Corporation, + * and consists of extensive modifications made during the year(s) 1999-2000. + */ + +#include +#include +#include +#include "common.h" + +extern time_t _st_curr_time; +extern st_utime_t _st_last_tset; +extern int _st_active_count; + +static st_utime_t (*_st_utime)(void) = NULL; + +/***************************************** + * Time functions + */ + +st_utime_t st_utime(void) +{ + if (_st_utime == NULL) { +#ifdef MD_GET_UTIME + MD_GET_UTIME(); +#else + #error Unknown OS +#endif + } + + return (*_st_utime)(); +} + +int st_set_utime_function(st_utime_t (*func)(void)) +{ + if (_st_active_count) { + errno = EINVAL; + return -1; + } + + _st_utime = func; + + return 0; +} + +st_utime_t st_utime_last_clock(void) +{ + return _ST_LAST_CLOCK; +} + +int st_timecache_set(int on) +{ + int wason = (_st_curr_time) ? 1 : 0; + + if (on) { + _st_curr_time = time(NULL); + _st_last_tset = st_utime(); + } else { + _st_curr_time = 0; + } + + return wason; +} + +time_t st_time(void) +{ + if (_st_curr_time) { + return _st_curr_time; + } + + return time(NULL); +} + +int st_usleep(st_utime_t usecs) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if (usecs != ST_UTIME_NO_TIMEOUT) { + me->state = _ST_ST_SLEEPING; + _ST_ADD_SLEEPQ(me, usecs); + } else { + me->state = _ST_ST_SUSPENDED; + } + + _ST_SWITCH_CONTEXT(me); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return 0; +} + +int st_sleep(int secs) +{ + return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : ST_UTIME_NO_TIMEOUT); +} + +/***************************************** + * Condition variable functions + */ +_st_cond_t *st_cond_new(void) +{ + _st_cond_t *cvar; + + cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t)); + if (cvar) { + ST_INIT_CLIST(&cvar->wait_q); + } + + return cvar; +} + +int st_cond_destroy(_st_cond_t *cvar) +{ + if (cvar->wait_q.next != &cvar->wait_q) { + errno = EBUSY; + return -1; + } + + free(cvar); + + return 0; +} + +int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + int rv; + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + /* Put caller thread on the condition variable's wait queue */ + me->state = _ST_ST_COND_WAIT; + ST_APPEND_LINK(&me->wait_links, &cvar->wait_q); + + if (timeout != ST_UTIME_NO_TIMEOUT) { + _ST_ADD_SLEEPQ(me, timeout); + } + + _ST_SWITCH_CONTEXT(me); + + ST_REMOVE_LINK(&me->wait_links); + rv = 0; + + if (me->flags & _ST_FL_TIMEDOUT) { + me->flags &= ~_ST_FL_TIMEDOUT; + errno = ETIME; + rv = -1; + } + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + rv = -1; + } + + return rv; +} + +int st_cond_wait(_st_cond_t *cvar) +{ + return st_cond_timedwait(cvar, ST_UTIME_NO_TIMEOUT); +} + +static int _st_cond_signal(_st_cond_t *cvar, int broadcast) +{ + _st_thread_t *thread; + _st_clist_t *q; + + for (q = cvar->wait_q.next; q != &cvar->wait_q; q = q->next) { + thread = _ST_THREAD_WAITQ_PTR(q); + if (thread->state == _ST_ST_COND_WAIT) { + if (thread->flags & _ST_FL_ON_SLEEPQ) { + _ST_DEL_SLEEPQ(thread); + } + + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + if (!broadcast) { + break; + } + } + } + + return 0; +} + +int st_cond_signal(_st_cond_t *cvar) +{ + return _st_cond_signal(cvar, 0); +} + +int st_cond_broadcast(_st_cond_t *cvar) +{ + return _st_cond_signal(cvar, 1); +} + +/***************************************** + * Mutex functions + */ +_st_mutex_t *st_mutex_new(void) +{ + _st_mutex_t *lock; + + lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t)); + if (lock) { + ST_INIT_CLIST(&lock->wait_q); + lock->owner = NULL; + } + + return lock; +} + +int st_mutex_destroy(_st_mutex_t *lock) +{ + if (lock->owner != NULL || lock->wait_q.next != &lock->wait_q) { + errno = EBUSY; + return -1; + } + + free(lock); + + return 0; +} + +int st_mutex_lock(_st_mutex_t *lock) +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + if (me->flags & _ST_FL_INTERRUPT) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + if (lock->owner == NULL) { + /* Got the mutex */ + lock->owner = me; + return 0; + } + + if (lock->owner == me) { + errno = EDEADLK; + return -1; + } + + /* Put caller thread on the mutex's wait queue */ + me->state = _ST_ST_LOCK_WAIT; + ST_APPEND_LINK(&me->wait_links, &lock->wait_q); + + _ST_SWITCH_CONTEXT(me); + + ST_REMOVE_LINK(&me->wait_links); + + if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) { + me->flags &= ~_ST_FL_INTERRUPT; + errno = EINTR; + return -1; + } + + return 0; +} + +int st_mutex_unlock(_st_mutex_t *lock) +{ + _st_thread_t *thread; + _st_clist_t *q; + + if (lock->owner != _ST_CURRENT_THREAD()) { + errno = EPERM; + return -1; + } + + for (q = lock->wait_q.next; q != &lock->wait_q; q = q->next) { + thread = _ST_THREAD_WAITQ_PTR(q); + if (thread->state == _ST_ST_LOCK_WAIT) { + lock->owner = thread; + /* Make thread runnable */ + thread->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(thread); + return 0; + } + } + + /* No threads waiting on this mutex */ + lock->owner = NULL; + + return 0; +} + +int st_mutex_trylock(_st_mutex_t *lock) +{ + if (lock->owner != NULL) { + errno = EBUSY; + return -1; + } + + /* Got the mutex */ + lock->owner = _ST_CURRENT_THREAD(); + + return 0; +} + diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index 7ebb10b14..b33b4f6f8 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -72,7 +72,7 @@ SrsUdpListener::SrsUdpListener(ISrsUdpHandler* h, string i, int p) handler = h; ip = i; port = p; - lfd = -1; + lfd = NULL; nb_buf = SRS_UDP_MAX_PACKET_SIZE; buf = new char[nb_buf]; @@ -148,7 +148,7 @@ SrsTcpListener::SrsTcpListener(ISrsTcpHandler* h, string i, int p) ip = i; port = p; - lfd = -1; + lfd = NULL; trd = new SrsDummyCoroutine(); } @@ -161,7 +161,7 @@ SrsTcpListener::~SrsTcpListener() int SrsTcpListener::fd() { - return srs_netfd_fileno(lfd); + return srs_netfd_fileno(lfd);; } srs_error_t SrsTcpListener::listen() @@ -191,10 +191,10 @@ srs_error_t SrsTcpListener::cycle() } srs_netfd_t fd = srs_accept(lfd, NULL, NULL, SRS_UTIME_NO_TIMEOUT); - if(fd < 0){ + if(fd == NULL){ return srs_error_new(ERROR_SOCKET_ACCEPT, "accept at fd=%d", srs_netfd_fileno(lfd)); } - + if ((err = srs_fd_closeexec(srs_netfd_fileno(fd))) != srs_success) { return srs_error_wrap(err, "set closeexec"); } diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index e42f8314b..61eeb2309 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -32,8 +32,6 @@ #include using namespace std; -#include - #include #include #include @@ -346,7 +344,7 @@ SrsSignalManager::SrsSignalManager(SrsServer* s) server = s; sig_pipe[0] = sig_pipe[1] = -1; trd = new SrsSTCoroutine("signal", this); - signal_read_stfd = -1; + signal_read_stfd = NULL; } SrsSignalManager::~SrsSignalManager() @@ -370,7 +368,7 @@ srs_error_t SrsSignalManager::initialize() return srs_error_new(ERROR_SYSTEM_CREATE_PIPE, "create pipe"); } - if ((signal_read_stfd = srs_netfd_open(sig_pipe[0])) < 0) { + if ((signal_read_stfd = srs_netfd_open(sig_pipe[0])) == NULL) { return srs_error_new(ERROR_SYSTEM_CREATE_PIPE, "open pipe"); } @@ -796,7 +794,7 @@ srs_error_t SrsServer::ingest() srs_error_t SrsServer::cycle() { srs_error_t err = do_cycle(); - + #ifdef SRS_AUTO_GPERF_MC destroy(); @@ -887,10 +885,6 @@ srs_error_t SrsServer::do_cycle() // the daemon thread, update the time cache // TODO: FIXME: use SrsHourGlass. - - // FIXME: libco will take over user's event loop - co_eventloop(co_get_epoll_ct(), NULL, NULL); - while (true) { if (handler && (err = handler->on_cycle()) != srs_success) { return srs_error_wrap(err, "handle callback"); diff --git a/trunk/src/app/srs_app_st.cpp b/trunk/src/app/srs_app_st.cpp index 5c4c7b87b..831a0613a 100755 --- a/trunk/src/app/srs_app_st.cpp +++ b/trunk/src/app/srs_app_st.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include using namespace std; @@ -79,24 +79,13 @@ int SrsDummyCoroutine::cid() return 0; } -void* co_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stack_size) { - (void)joinable; - (void)stack_size; - - stCoRoutine_t *co = NULL; - co_create(&co, NULL, start, arg); - co_resume(co); - return co; -} - -_ST_THREAD_CREATE_PFN _pfn_st_thread_create = (_ST_THREAD_CREATE_PFN)co_thread_create; +_ST_THREAD_CREATE_PFN _pfn_st_thread_create = (_ST_THREAD_CREATE_PFN)st_thread_create; SrsSTCoroutine::SrsSTCoroutine(string n, ISrsCoroutineHandler* h, int cid) { name = n; handler = h; context = cid; - term = co_cond_alloc(); trd = NULL; trd_err = srs_success; started = interrupted = disposed = cycle_done = false; @@ -105,8 +94,7 @@ SrsSTCoroutine::SrsSTCoroutine(string n, ISrsCoroutineHandler* h, int cid) SrsSTCoroutine::~SrsSTCoroutine() { stop(); - - co_cond_free(term); + srs_freep(trd_err); } @@ -154,8 +142,8 @@ void SrsSTCoroutine::stop() // When not started, the rd is NULL. if (trd) { void* res = NULL; - - co_cond_timedwait(term, -1); + int r0 = st_thread_join((st_thread_t)trd, &res); + srs_assert(!r0); srs_error_t err_res = (srs_error_t)res; if (err_res != srs_success) { @@ -183,6 +171,8 @@ void SrsSTCoroutine::interrupt() if (trd_err == srs_success) { trd_err = srs_error_new(ERROR_THREAD_INTERRUPED, "interrupted"); } + + st_thread_interrupt((st_thread_t)trd); } srs_error_t SrsSTCoroutine::pull() @@ -230,9 +220,6 @@ void* SrsSTCoroutine::pfn(void* arg) p->trd_err = err; } - // FIXME: - //co_cond_signal(term); - return (void*)err; } diff --git a/trunk/src/app/srs_app_st.hpp b/trunk/src/app/srs_app_st.hpp index f158d5ba6..956e00b42 100644 --- a/trunk/src/app/srs_app_st.hpp +++ b/trunk/src/app/srs_app_st.hpp @@ -102,8 +102,6 @@ public: typedef void* (*_ST_THREAD_CREATE_PFN)(void *(*start)(void *arg), void *arg, int joinable, int stack_size); extern _ST_THREAD_CREATE_PFN _pfn_st_thread_create; -struct stCoCond_t; - // A ST-coroutine is a lightweight thread, just like the goroutine. // But the goroutine maybe run on different thread, while ST-coroutine only // run in single thread, because it use setjmp and longjmp, so it may cause @@ -122,7 +120,6 @@ private: std::string name; ISrsCoroutineHandler* handler; private: - stCoCond_t* term; srs_thread_t trd; int context; srs_error_t trd_err; diff --git a/trunk/src/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp index dba7adbe8..f63cd4279 100644 --- a/trunk/src/service/srs_service_st.cpp +++ b/trunk/src/service/srs_service_st.cpp @@ -23,11 +23,10 @@ #include -#include +#include #include #include #include -#include using namespace std; #include @@ -42,17 +41,6 @@ using namespace std; #ifdef __linux__ #include -static int set_fd_nonblock(int fd) -{ - int flags; - - flags = fcntl(fd, F_GETFL, 0); - flags |= O_NONBLOCK; - flags |= O_NDELAY; - int ret = fcntl(fd, F_SETFL, flags); - return ret; -} - bool srs_st_epoll_is_supported(void) { struct epoll_event ev; @@ -68,12 +56,37 @@ bool srs_st_epoll_is_supported(void) srs_error_t srs_st_init() { +#ifdef __linux__ + // check epoll, some old linux donot support epoll. + // @see https://github.com/ossrs/srs/issues/162 + if (!srs_st_epoll_is_supported()) { + return srs_error_new(ERROR_ST_SET_EPOLL, "linux epoll disabled"); + } +#endif + + // Select the best event system available on the OS. In Linux this is + // epoll(). On BSD it will be kqueue. + if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) { + return srs_error_new(ERROR_ST_SET_EPOLL, "st enable st failed, current is %s", st_get_eventsys_name()); + } + + int r0 = 0; + if((r0 = st_init()) != 0){ + return srs_error_new(ERROR_ST_INITIALIZE, "st initialize failed, r0=%d", r0); + } + srs_trace("st_init success, use %s", st_get_eventsys_name()); + return srs_success; } void srs_close_stfd(srs_netfd_t& stfd) { - ::close(stfd); + if (stfd) { + // we must ensure the close is ok. + int err = st_netfd_close((st_netfd_t)stfd); + srs_assert(err != -1); + stfd = NULL; + } } srs_error_t srs_fd_closeexec(int fd) @@ -131,17 +144,18 @@ srs_error_t srs_fd_keepalive(int fd) srs_thread_t srs_thread_self() { - return (srs_thread_t)co_self(); + return (srs_thread_t)st_thread_self(); } srs_error_t srs_tcp_connect(string server, int port, srs_utime_t tm, srs_netfd_t* pstfd) { - srs_utime_t timeout = SRS_UTIME_NO_TIMEOUT; + st_utime_t timeout = ST_UTIME_NO_TIMEOUT; if (tm != SRS_UTIME_NO_TIMEOUT) { timeout = tm; } - - (void)timeout; + + *pstfd = NULL; + srs_netfd_t stfd = NULL; char sport[8]; snprintf(sport, sizeof(sport), "%d", port); @@ -161,15 +175,20 @@ srs_error_t srs_tcp_connect(string server, int port, srs_utime_t tm, srs_netfd_t if(sock == -1){ return srs_error_new(ERROR_SOCKET_CREATE, "create socket"); } - - *pstfd = sock; - // TODO: timeout - if (connect(sock, r->ai_addr, r->ai_addrlen) == -1) { - srs_close_stfd(sock); + srs_assert(!stfd); + stfd = st_netfd_open_socket(sock); + if(stfd == NULL){ + ::close(sock); + return srs_error_new(ERROR_ST_OPEN_SOCKET, "open socket"); + } + + if (st_connect((st_netfd_t)stfd, r->ai_addr, r->ai_addrlen, timeout) == -1){ + srs_close_stfd(stfd); return srs_error_new(ERROR_ST_CONNECT, "connect to %s:%d", server.c_str(), port); } + *pstfd = stfd; return srs_success; } @@ -195,7 +214,7 @@ srs_error_t do_srs_tcp_listen(int fd, addrinfo* r, srs_netfd_t* pfd) return srs_error_wrap(err, "set reuseport"); } - if (::bind(fd, r->ai_addr, r->ai_addrlen) == -1) { + if (bind(fd, r->ai_addr, r->ai_addrlen) == -1) { return srs_error_new(ERROR_SOCKET_BIND, "bind"); } @@ -203,6 +222,10 @@ srs_error_t do_srs_tcp_listen(int fd, addrinfo* r, srs_netfd_t* pfd) return srs_error_new(ERROR_SOCKET_LISTEN, "listen"); } + if ((*pfd = srs_netfd_open_socket(fd)) == NULL){ + return srs_error_new(ERROR_ST_OPEN_SOCKET, "st open"); + } + return err; } @@ -232,15 +255,11 @@ srs_error_t srs_tcp_listen(std::string ip, int port, srs_netfd_t* pfd) r->ai_family, r->ai_socktype, r->ai_protocol); } - set_fd_nonblock(fd); - if ((err = do_srs_tcp_listen(fd, r, pfd)) != srs_success) { ::close(fd); return srs_error_wrap(err, "fd=%d", fd); } - *pfd = fd; - return err; } @@ -264,6 +283,10 @@ srs_error_t do_srs_udp_listen(int fd, addrinfo* r, srs_netfd_t* pfd) return srs_error_new(ERROR_SOCKET_BIND, "bind"); } + if ((*pfd = srs_netfd_open_socket(fd)) == NULL){ + return srs_error_new(ERROR_ST_OPEN_SOCKET, "st open"); + } + return err; } @@ -303,121 +326,85 @@ srs_error_t srs_udp_listen(std::string ip, int port, srs_netfd_t* pfd) srs_cond_t srs_cond_new() { - return (srs_cond_t)co_cond_alloc(); + return (srs_cond_t)st_cond_new(); } int srs_cond_destroy(srs_cond_t cond) { - return co_cond_free((stCoCond_t*)cond); + return st_cond_destroy((st_cond_t)cond); } int srs_cond_wait(srs_cond_t cond) { - return co_cond_timedwait((stCoCond_t*)cond, -1); + return st_cond_wait((st_cond_t)cond); } int srs_cond_timedwait(srs_cond_t cond, srs_utime_t timeout) { - return co_cond_timedwait((stCoCond_t*)cond, timeout); + return st_cond_timedwait((st_cond_t)cond, (st_utime_t)timeout); } int srs_cond_signal(srs_cond_t cond) { - return co_cond_signal((stCoCond_t*)cond); + return st_cond_signal((st_cond_t)cond); } srs_mutex_t srs_mutex_new() { - return NULL; + return (srs_mutex_t)st_mutex_new(); } int srs_mutex_destroy(srs_mutex_t mutex) { - return 0; + if (!mutex) { + return 0; + } + return st_mutex_destroy((st_mutex_t)mutex); } int srs_mutex_lock(srs_mutex_t mutex) { - return 0; + return st_mutex_lock((st_mutex_t)mutex); } int srs_mutex_unlock(srs_mutex_t mutex) { - return 0; + return st_mutex_unlock((st_mutex_t)mutex); } int srs_netfd_fileno(srs_netfd_t stfd) { - return stfd; + return st_netfd_fileno((st_netfd_t)stfd); } int srs_usleep(srs_utime_t usecs) { - // XXX: libco has no API like co_sleep, use co_cond_timedwait instead - stCoCond_t* cond = co_cond_alloc(); - co_cond_timedwait(cond, usecs/1000.0); - - return 0; + return st_usleep((st_utime_t)usecs); } srs_netfd_t srs_netfd_open_socket(int osfd) { - set_fd_nonblock(osfd); - return osfd; + return (srs_netfd_t)st_netfd_open_socket(osfd); } srs_netfd_t srs_netfd_open(int osfd) { - set_fd_nonblock(osfd); - return osfd; + return (srs_netfd_t)st_netfd_open(osfd); } int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, int *fromlen, srs_utime_t timeout) { - // TODO: timeout - return recvfrom(stfd, buf, len, 0, from, (socklen_t*)fromlen); + return st_recvfrom((st_netfd_t)stfd, buf, len, from, fromlen, (st_utime_t)timeout); } srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout) { - struct pollfd pf = { 0 }; - pf.fd = stfd; - pf.events = (POLLIN | POLLERR | POLLHUP); - - srs_utime_t atm = timeout; - if (atm != SRS_UTIME_NO_TIMEOUT) - atm /= 1000; - - int client_fd; - while ((client_fd = accept(stfd, addr, (socklen_t*)addrlen)) < 0) { - if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { - return -1; - } - - co_poll(co_get_epoll_ct(), &pf, 1, atm); - } - - set_fd_nonblock(client_fd); - - return client_fd; + return (srs_netfd_t)st_accept((st_netfd_t)stfd, addr, addrlen, (st_utime_t)timeout); } ssize_t srs_read(srs_netfd_t stfd, void *buf, size_t nbyte, srs_utime_t timeout) { - struct pollfd pf = { 0 }; - pf.fd = stfd; - pf.events = (POLLIN | POLLERR | POLLHUP); - - int n; - while ((n = ::read(stfd, buf, nbyte)) < 0) { - if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { - return -1; - } - - co_poll(co_get_epoll_ct(), &pf, 1, timeout); - } - - return n; + return st_read((st_netfd_t)stfd, buf, nbyte, (st_utime_t)timeout); } bool srs_is_never_timeout(srs_utime_t tm) @@ -427,6 +414,7 @@ bool srs_is_never_timeout(srs_utime_t tm) SrsStSocket::SrsStSocket() { + stfd = NULL; stm = rtm = SRS_UTIME_NO_TIMEOUT; rbytes = sbytes = 0; } @@ -477,15 +465,15 @@ srs_error_t SrsStSocket::read(void* buf, size_t size, ssize_t* nread) ssize_t nb_read; if (rtm == SRS_UTIME_NO_TIMEOUT) { - nb_read = srs_read(stfd, buf, size, -1); + nb_read = st_read((st_netfd_t)stfd, buf, size, ST_UTIME_NO_TIMEOUT); } else { - nb_read = srs_read(stfd, buf, size, rtm / 1000); + nb_read = st_read((st_netfd_t)stfd, buf, size, rtm); } if (nread) { *nread = nb_read; } - + // On success a non-negative integer indicating the number of bytes actually read is returned // (a value of 0 means the network connection is closed or end of file is reached). // Otherwise, a value of -1 is returned and errno is set to indicate the error. @@ -511,25 +499,13 @@ srs_error_t SrsStSocket::read_fully(void* buf, size_t size, ssize_t* nread) { srs_error_t err = srs_success; - ssize_t nb_read = 0; - - int wait_read_bytes = size; - while (wait_read_bytes > 0) { - int bytes = ::read(stfd, buf, wait_read_bytes); - if (bytes > 0) { - nb_read += bytes; - wait_read_bytes -= bytes; - if (nb_read == (ssize_t)size) { - break; - } - } else if (bytes == 0) { - break; - } else { - if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) - break; - } + ssize_t nb_read; + if (rtm == SRS_UTIME_NO_TIMEOUT) { + nb_read = st_read_fully((st_netfd_t)stfd, buf, size, ST_UTIME_NO_TIMEOUT); + } else { + nb_read = st_read_fully((st_netfd_t)stfd, buf, size, rtm); } - + if (nread) { *nread = nb_read; } @@ -559,30 +535,11 @@ srs_error_t SrsStSocket::write(void* buf, size_t size, ssize_t* nwrite) { srs_error_t err = srs_success; - ssize_t nb_write = 0; - - struct pollfd pf = { 0 }; - pf.fd = stfd; - pf.events = (POLLOUT | POLLERR | POLLHUP); - - srs_utime_t wtm = stm; - if (wtm != SRS_UTIME_NO_TIMEOUT) - wtm = stm / 1000; - - int wait_write_bytes = size; - while (wait_write_bytes > 0) { - int n = 0; - if ((n = ::write(stfd, buf, size)) < 0) { - if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { - break; - } - - co_poll(co_get_epoll_ct(), &pf, 1, wtm); - continue; - } - - wait_write_bytes -= n; - nb_write += n; + ssize_t nb_write; + if (stm == SRS_UTIME_NO_TIMEOUT) { + nb_write = st_write((st_netfd_t)stfd, buf, size, ST_UTIME_NO_TIMEOUT); + } else { + nb_write = st_write((st_netfd_t)stfd, buf, size, stm); } if (nwrite) { @@ -609,44 +566,11 @@ srs_error_t SrsStSocket::writev(const iovec *iov, int iov_size, ssize_t* nwrite) { srs_error_t err = srs_success; - srs_utime_t tm = stm; - if (tm != SRS_UTIME_NO_TIMEOUT) - tm = stm / 1000; - - int wait_write_bytes = 0; - for (int i = 0; i < iov_size; ++i) - wait_write_bytes += iov[i].iov_len; - - ssize_t nb_write = 0; - iovec* cur_iov = (iovec*)iov; - int cur_iov_size = iov_size; - - while (wait_write_bytes > 0) { - int n = 0; - if ((n = ::writev(stfd, cur_iov, cur_iov_size)) < 0) { - if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { - break; - } - - struct pollfd pf = {0}; - pf.fd = stfd; - pf.events = (POLLOUT | POLLERR | POLLHUP); - - co_poll(co_get_epoll_ct(), &pf, 1, tm); - continue; - } - - wait_write_bytes -= n; - nb_write += n; - - while (n >= (int)cur_iov->iov_len) { - n -= cur_iov->iov_len; - --cur_iov_size; - ++cur_iov; - } - // FIXME: no modify iov - (*cur_iov).iov_base = (void*)((char*)(*cur_iov).iov_base + n); - (*cur_iov).iov_len -= n; + ssize_t nb_write; + if (stm == SRS_UTIME_NO_TIMEOUT) { + nb_write = st_writev((st_netfd_t)stfd, iov, iov_size, ST_UTIME_NO_TIMEOUT); + } else { + nb_write = st_writev((st_netfd_t)stfd, iov, iov_size, stm); } if (nwrite) { @@ -671,7 +595,7 @@ srs_error_t SrsStSocket::writev(const iovec *iov, int iov_size, ssize_t* nwrite) SrsTcpClient::SrsTcpClient(string h, int p, srs_utime_t tm) { - stfd = -1; + stfd = NULL; io = new SrsStSocket(); host = h; @@ -692,7 +616,7 @@ srs_error_t SrsTcpClient::connect() close(); - srs_assert(stfd == -1); + srs_assert(stfd == NULL); if ((err = srs_tcp_connect(host, port, timeout, &stfd)) != srs_success) { return srs_error_wrap(err, "tcp: connect %s:%d to=%dms", host.c_str(), port, srsu2msi(timeout)); } diff --git a/trunk/src/service/srs_service_st.hpp b/trunk/src/service/srs_service_st.hpp index ca6da3f7c..510b9ba8a 100644 --- a/trunk/src/service/srs_service_st.hpp +++ b/trunk/src/service/srs_service_st.hpp @@ -31,7 +31,7 @@ #include // Wrap for coroutine. -typedef int srs_netfd_t; +typedef void* srs_netfd_t; typedef void* srs_thread_t; typedef void* srs_cond_t; typedef void* srs_mutex_t; From bc22ebe94921046461e5f96184363695bebca78d Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Fri, 21 Feb 2020 23:50:22 +0800 Subject: [PATCH 03/21] add rtc http request and response, exchange sdp. --- trunk/configure | 2 +- trunk/research/players/srs_rtc_player.html | 90 ++++++++++++++++++ trunk/src/app/srs_app_config.cpp | 41 +++++++- trunk/src/app/srs_app_config.hpp | 7 ++ trunk/src/app/srs_app_http_api.cpp | 105 +++++++++++++++++++++ trunk/src/app/srs_app_http_api.hpp | 9 ++ trunk/src/app/srs_app_rtc_udp.cpp | 89 +++++++++++++++++ trunk/src/app/srs_app_rtc_udp.hpp | 52 ++++++++++ trunk/src/app/srs_app_server.cpp | 78 +++++++++++++++ trunk/src/app/srs_app_server.hpp | 16 ++++ trunk/src/kernel/srs_kernel_error.hpp | 1 + 11 files changed, 488 insertions(+), 2 deletions(-) create mode 100644 trunk/research/players/srs_rtc_player.html create mode 100644 trunk/src/app/srs_app_rtc_udp.cpp create mode 100644 trunk/src/app/srs_app_rtc_udp.hpp diff --git a/trunk/configure b/trunk/configure index d233f66db..5efe7eb59 100755 --- a/trunk/configure +++ b/trunk/configure @@ -254,7 +254,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_edge" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static" "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds" - "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" + "srs_app_mpegts_udp" "srs_app_rtc_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" "srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr" "srs_app_coworkers" "srs_app_hybrid") diff --git a/trunk/research/players/srs_rtc_player.html b/trunk/research/players/srs_rtc_player.html new file mode 100644 index 000000000..bbb605ab5 --- /dev/null +++ b/trunk/research/players/srs_rtc_player.html @@ -0,0 +1,90 @@ + + + + + + + + +rtc_media_player:
+ + + + + + + diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 2a7b98bd3..315ec58f7 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -3486,7 +3486,7 @@ srs_error_t SrsConfig::check_normal_config() && n != "srs_log_tank" && n != "srs_log_level" && n != "srs_log_file" && n != "max_connections" && n != "daemon" && n != "heartbeat" && n != "http_api" && n != "stats" && n != "vhost" && n != "pithy_print_ms" - && n != "http_server" && n != "stream_caster" && n != "srt_server" + && n != "http_server" && n != "stream_caster" && n != "rtc" && n != "srt_server" && n != "utc_time" && n != "work_dir" && n != "asprocess" && n != "ff_log_level" && n != "grace_final_wait" && n != "force_grace_quit" && n != "grace_start_wait" @@ -4216,6 +4216,45 @@ int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* conf) return ::atoi(conf->arg0().c_str()); } +int SrsConfig::get_rtc_enabled() +{ + SrsConfDirective* conf = root->get("rtc"); + return get_rtc_enabled(conf); +} + +bool SrsConfig::get_rtc_enabled(SrsConfDirective* conf) +{ + static bool DEFAULT = false; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("enabled"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); +} + +int SrsConfig::get_rtc_listen() +{ + static int DEFAULT = 9527; + + SrsConfDirective* conf = root->get("rtc"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("listen"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + SrsConfDirective* SrsConfig::get_vhost(string vhost, bool try_default_vhost) { srs_assert(root); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 6a58f2ef8..1f85550f7 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -490,6 +490,13 @@ public: virtual int get_stream_caster_rtp_port_min(SrsConfDirective* conf); // Get the max udp port for rtp of stream caster rtsp. virtual int get_stream_caster_rtp_port_max(SrsConfDirective* conf); + +// rtc section +public: + virtual int get_rtc_enabled(); + virtual bool get_rtc_enabled(SrsConfDirective* conf); + virtual int get_rtc_listen(); + // vhost specified section public: // Get the vhost directive by vhost name. diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 5355068eb..634ba647c 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -47,6 +47,73 @@ using namespace std; #include #include +string test_sdp = +"v=0\\r\\n" +"o=- 0 0 IN IP4 127.0.0.1\\r\\n" +"s=-\\r\\n" +"t=0 0\\r\\n" +"a=ice-lite\\r\\n" +"a=group:BUNDLE 0 1\\r\\n" +"a=msid-semantic: WMS 6VrfBKXrwK\\r\\n" +"m=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\n" +"c=IN IP4 0.0.0.0\\r\\n" +"a=candidate:10 1 udp 2115783679 192.168.170.129 9527 typ host generation 0\\r\\n" +"a=rtcp:9 IN IP4 0.0.0.0\\r\\n" +"a=ice-ufrag:xiaozhihongjohn\\r\\n" +"a=ice-pwd:simple_rtmp_server__john\\r\\n" +"a=ice-options:trickle\\r\\n" +"a=fingerprint:sha-256 76:E8:6A:6D:48:F0:86:58:30:2E:69:56:0F:C6:A1:B8:69:98:5D:73:45:93:37:8E:C4:2B:C7:97:04:18:E4:24\\r\\n" +"a=sendrecv\\r\\n" +"a=mid:0\\r\\n" +"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\\r\\n" +"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\n" +"a=rtcp-mux\\r\\n" +"a=rtpmap:111 opus/48000/2\\r\\n" +"a=fmtp:111 minptime=10;useinbandfec=1\\r\\n" +"a=maxptime:60\\r\\n" +"a=ssrc:3233846890 cname:o/i14u9pJrxRKAsu\\r\\n" +"a=ssrc:3233846890 msid:6VrfBKXrwK a0\\r\\n" +"a=ssrc:3233846890 mslabel:6VrfBKXrwK\\r\\n" +"a=ssrc:3233846890 label:6VrfBKXrwKa0\\r\\n" +"m=video 9 UDP/TLS/RTP/SAVPF 96 98 102\\r\\n" +"c=IN IP4 0.0.0.0\\r\\n" +"a=candidate:10 1 udp 2115783679 192.168.170.129 9527 typ host generation 0\\r\\n" +"a=rtcp:9 IN IP4 0.0.0.0\\r\\n" +"b=as:2000000\\r\\n" +"a=ice-ufrag:xiaozhihongjohn\\r\\n" +"a=ice-pwd:simple_rtmp_server__john\\r\\n" +"a=ice-options:trickle\\r\\n" +"a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\\r\\n" +"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\n" +"a=extmap:4 urn:3gpp:video-orientation\\r\\n" +"a=fingerprint:sha-256 76:E8:6A:6D:48:F0:86:58:30:2E:69:56:0F:C6:A1:B8:69:98:5D:73:45:93:37:8E:C4:2B:C7:97:04:18:E4:24\\r\\n" +"a=sendrecv\\r\\n" +"a=mid:1\\r\\n" +"a=rtcp-mux\\r\\n" +"a=rtpmap:96 VP8/90000\\r\\n" +"a=rtcp-fb:96 ccm fir\\r\\n" +"a=rtcp-fb:96 nack\\r\\n" +"a=rtcp-fb:96 nack pli\\r\\n" +"a=rtcp-fb:96 goog-remb\\r\\n" +"a=rtcp-fb:96 transport-cc\\r\\n" +"a=rtpmap:98 VP9/90000\\r\\n" +"a=rtcp-fb:98 ccm fir\\r\\n" +"a=rtcp-fb:98 nack\\r\\n" +"a=rtcp-fb:98 nack pli\\r\\n" +"a=rtcp-fb:98 goog-remb\\r\\n" +"a=rtcp-fb:98 transport-cc\\r\\n" +"a=rtpmap:102 H264/90000\\r\\n" +"a=rtcp-fb:102 goog-remb\\r\\n" +"a=rtcp-fb:102 transport-cc\\r\\n" +"a=rtcp-fb:102 ccm fir \\r\\n" +"a=rtcp-fb:102 nack\\r\\n" +"a=rtcp-fb:102 nack pli \\r\\n" +"a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\\r\\n" +"a=ssrc:3233846889 cname:o/i14u9pJrxRKAsu\\r\\n" +"a=ssrc:3233846889 msid:6VrfBKXrwK v0\\r\\n" +"a=ssrc:3233846889 mslabel:6VrfBKXrwK\\r\\n" +"a=ssrc:3233846889 label:6VrfBKXrwKv0\\r\\n"; + srs_error_t srs_api_response_jsonp(ISrsHttpResponseWriter* w, string callback, string data) { srs_error_t err = srs_success; @@ -780,6 +847,44 @@ srs_error_t SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa return srs_api_response(w, r, obj->dumps()); } +SrsGoApiSdp::SrsGoApiSdp() +{ +} + +SrsGoApiSdp::~SrsGoApiSdp() +{ +} + +srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + srs_error_t err = srs_success; + + SrsStatistic* stat = SrsStatistic::instance(); + + // path: {pattern}{stream_id} + // e.g. /api/v1/streams/100 pattern= /api/v1/streams/, stream_id=100 + int sid = r->parse_rest_id(entry->pattern); + + SrsStatisticStream* stream = NULL; + if (sid >= 0 && (stream = stat->find_stream(sid)) == NULL) { + return srs_api_response_code(w, r, ERROR_RTMP_STREAM_NOT_FOUND); + } + + SrsJsonObject* obj = SrsJsonAny::object(); + SrsAutoFree(SrsJsonObject, obj); + + obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); + obj->set("server", SrsJsonAny::integer(stat->server_id())); + + if (r->is_http_post()) { + obj->set("sdp", SrsJsonAny::str(test_sdp.c_str())); + } else { + return srs_go_http_error(w, SRS_CONSTS_HTTP_MethodNotAllowed); + } + + return srs_api_response(w, r, obj->dumps()); +} + SrsGoApiClients::SrsGoApiClients() { } diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index 5957ff2f3..367edabab 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -164,6 +164,15 @@ public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; +class SrsGoApiSdp : public ISrsHttpHandler +{ +public: + SrsGoApiSdp(); + virtual ~SrsGoApiSdp(); +public: + virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +}; + class SrsGoApiClients : public ISrsHttpHandler { public: diff --git a/trunk/src/app/srs_app_rtc_udp.cpp b/trunk/src/app/srs_app_rtc_udp.cpp new file mode 100644 index 000000000..4866c387a --- /dev/null +++ b/trunk/src/app/srs_app_rtc_udp.cpp @@ -0,0 +1,89 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SrsRtcOverUdp::SrsRtcOverUdp() +{ +} + +SrsRtcOverUdp::~SrsRtcOverUdp() +{ +} + +srs_error_t SrsRtcOverUdp::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) +{ + char address_string[64]; + char port_string[16]; + if(getnameinfo(from, fromlen, + (char*)&address_string, sizeof(address_string), + (char*)&port_string, sizeof(port_string), + NI_NUMERICHOST|NI_NUMERICSERV)) { + return srs_error_new(ERROR_SYSTEM_IP_INVALID, "bad address"); + } + std::string peer_ip = std::string(address_string); + int peer_port = atoi(port_string); + + srs_error_t err = on_udp_bytes(peer_ip, peer_port, buf, nb_buf); + if (err != srs_success) { + return srs_error_wrap(err, "process udp"); + } + return err; +} + +srs_error_t SrsRtcOverUdp::on_udp_bytes(string host, int port, char* buf, int nb_buf) +{ + srs_error_t err = srs_success; + + srs_trace("recv rtc udp packet from %s:%d, nb_buf=%d", host.c_str(), port, nb_buf); + + return err; +} diff --git a/trunk/src/app/srs_app_rtc_udp.hpp b/trunk/src/app/srs_app_rtc_udp.hpp new file mode 100644 index 000000000..707b6b830 --- /dev/null +++ b/trunk/src/app/srs_app_rtc_udp.hpp @@ -0,0 +1,52 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_APP_RTC_UDP_HPP +#define SRS_APP_RTC_UDP_HPP + +#include + +struct sockaddr; +#include +#include + +#include +#include +#include + +// The rtc over udp stream receiver +class SrsRtcOverUdp : virtual public ISrsUdpHandler +{ +private: +public: + SrsRtcOverUdp(); + virtual ~SrsRtcOverUdp(); +// Interface ISrsUdpHandler +public: + virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); +private: + virtual srs_error_t on_udp_bytes(std::string host, int port, char* buf, int nb_buf); +}; + +#endif + diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index e95b4734e..85483a5b9 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -44,6 +44,7 @@ using namespace std; #include #include #include +#include #include #include #include @@ -108,6 +109,8 @@ std::string srs_listener_type2string(SrsListenerType type) return "RTSP"; case SrsListenerFlv: return "HTTP-FLV"; + case SrsListenerRtcOverUdp: + return "RTC over UDP"; default: return "UNKONWN"; } @@ -335,6 +338,45 @@ SrsUdpCasterListener::~SrsUdpCasterListener() srs_freep(caster); } +SrsRtcListener::SrsRtcListener(SrsServer* svr, SrsListenerType t) : SrsListener(svr, t) +{ + srs_assert(type == SrsListenerRtcOverUdp); + rtc = new SrsRtcOverUdp(); +} + +SrsRtcListener::~SrsRtcListener() +{ +} + +srs_error_t SrsRtcListener::listen(std::string i, int p) +{ + srs_error_t err = srs_success; + + // the caller already ensure the type is ok, + // we just assert here for unknown stream caster. + srs_assert(type == SrsListenerRtcOverUdp); + + ip = i; + port = p; + + srs_freep(listener); + listener = new SrsUdpListener(rtc, ip, port); + + if ((err = listener->listen()) != srs_success) { + return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); + } + + // notify the handler the fd changed. + if ((err = rtc->on_stfd_change(listener->stfd())) != srs_success) { + return srs_error_wrap(err, "notify fd change failed"); + } + + string v = srs_listener_type2string(type); + srs_trace("%s listen at udp://%s:%d, fd=%d", v.c_str(), ip.c_str(), port, listener->fd()); + + return err; +} + SrsSignalManager* SrsSignalManager::instance = NULL; SrsSignalManager::SrsSignalManager(SrsServer* s) @@ -728,6 +770,10 @@ srs_error_t SrsServer::listen() if ((err = listen_stream_caster()) != srs_success) { return srs_error_wrap(err, "stream caster listen"); } + + if ((err = listen_rtc()) != srs_success) { + return srs_error_wrap(err, "rtc listen"); + } if ((err = conn_manager->start()) != srs_success) { return srs_error_wrap(err, "connection manager"); @@ -790,6 +836,9 @@ srs_error_t SrsServer::http_handle() if ((err = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != srs_success) { return srs_error_wrap(err, "handle streams"); } + if ((err = http_api_mux->handle("/api/v1/sdp/", new SrsGoApiSdp())) != srs_success) { + return srs_error_wrap(err, "handle sdp"); + } if ((err = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != srs_success) { return srs_error_wrap(err, "handle clients"); } @@ -1188,6 +1237,35 @@ srs_error_t SrsServer::listen_stream_caster() return err; } +srs_error_t SrsServer::listen_rtc() +{ + srs_error_t err = srs_success; + + close_listeners(SrsListenerRtcOverUdp); + + if (!_srs_config->get_rtc_enabled()) { + return err; + } + + SrsListener* listener = NULL; + + listener = new SrsRtcListener(this, SrsListenerRtcOverUdp); + srs_assert(listener != NULL); + + listeners.push_back(listener); + + int port = _srs_config->get_rtc_listen(); + if (port <= 0) { + return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port); + } + + if ((err = listener->listen(srs_any_address_for_listener(), port)) != srs_success) { + return srs_error_wrap(err, "listen at %d", port); + } + + return err; +} + void SrsServer::close_listeners(SrsListenerType type) { std::vector::iterator it; diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 28154600e..77b8964df 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -68,6 +68,8 @@ enum SrsListenerType SrsListenerRtsp = 4, // TCP stream, FLV stream over HTTP. SrsListenerFlv = 5, + // UDP sream, rtp over udp + SrsListenerRtcOverUdp = 6, }; // A common tcp listener, for RTMP/HTTP server. @@ -155,6 +157,19 @@ public: virtual ~SrsUdpCasterListener(); }; +// A UDP listener, for udp stream caster server. +class SrsRtcListener : public SrsListener +{ +protected: + SrsUdpListener* listener; + ISrsUdpHandler* rtc; +public: + SrsRtcListener(SrsServer* svr, SrsListenerType t); + virtual ~SrsRtcListener(); +public: + virtual srs_error_t listen(std::string i, int p); +}; + // Convert signal to io, // @see: st-1.9/docs/notes.html class SrsSignalManager : public ISrsCoroutineHandler @@ -284,6 +299,7 @@ private: virtual srs_error_t listen_http_api(); virtual srs_error_t listen_http_stream(); virtual srs_error_t listen_stream_caster(); + virtual srs_error_t listen_rtc(); // Close the listeners for specified type, // Remove the listen object from manager. virtual void close_listeners(SrsListenerType type); diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index f7375780a..372a0196a 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -321,6 +321,7 @@ #define ERROR_HTTP_302_INVALID 4038 #define ERROR_BASE64_DECODE 4039 #define ERROR_HTTP_STREAM_EOF 4040 +#define ERROR_RTC_PORT 4041 /////////////////////////////////////////////////////// // HTTP API error. From 51abb0844eac6b8d77d5e1b7749fc56e68729911 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Sun, 23 Feb 2020 23:19:40 +0800 Subject: [PATCH 04/21] rtp session manager, stun codding --- trunk/conf/rtc.conf | 38 ++++++ trunk/configure | 2 +- trunk/src/app/srs_app_http_api.cpp | 12 +- trunk/src/app/srs_app_http_api.hpp | 4 +- trunk/src/app/srs_app_rtc_udp.cpp | 167 ++++++++++++++++++++++++-- trunk/src/app/srs_app_rtc_udp.hpp | 46 ++++++- trunk/src/app/srs_app_server.cpp | 25 +++- trunk/src/app/srs_app_server.hpp | 6 + trunk/src/protocol/srs_stun_stack.cpp | 28 +++++ trunk/src/protocol/srs_stun_stack.hpp | 43 +++++++ 10 files changed, 354 insertions(+), 17 deletions(-) create mode 100644 trunk/conf/rtc.conf create mode 100644 trunk/src/protocol/srs_stun_stack.cpp create mode 100644 trunk/src/protocol/srs_stun_stack.hpp diff --git a/trunk/conf/rtc.conf b/trunk/conf/rtc.conf new file mode 100644 index 000000000..5af4172b0 --- /dev/null +++ b/trunk/conf/rtc.conf @@ -0,0 +1,38 @@ +# main config for srs. +# @see full.conf for detail config. + +listen 1935; +max_connections 1000; +srs_log_tank file; +srs_log_file ./objs/srs.log; +http_api { + enabled on; + listen 1985; + raw_api { + enabled on; + allow_reload on; + allow_query on; + allow_update on; + } +} +http_server { + enabled on; + listen 8080; + dir ./objs/nginx/html; +} +rtc { + enabled on; + listen 9527; +} +stats { + network 0; + disk sda sdb xvda xvdb; +} +vhost __defaultVhost__ { + http_remux { + enabled on; + mount [vhost]/[app]/[stream].flv; + } +} + + diff --git a/trunk/configure b/trunk/configure index 5efe7eb59..5de5bc31d 100755 --- a/trunk/configure +++ b/trunk/configure @@ -214,7 +214,7 @@ ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_protocol_amf0" "srs_protocol_io" "srs_rtmp_stack" "srs_rtmp_handshake" "srs_protocol_utility" "srs_rtmp_msg_array" "srs_protocol_stream" "srs_raw_avc" "srs_rtsp_stack" "srs_http_stack" "srs_protocol_kbps" "srs_protocol_json" - "srs_protocol_format") + "srs_stun_stack" "srs_protocol_format") PROTOCOL_INCS="src/protocol"; MODULE_DIR=${PROTOCOL_INCS} . auto/modules.sh PROTOCOL_OBJS="${MODULE_OBJS[@]}" # diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 634ba647c..570d14662 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -46,6 +46,7 @@ using namespace std; #include #include #include +#include string test_sdp = "v=0\\r\\n" @@ -847,8 +848,9 @@ srs_error_t SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa return srs_api_response(w, r, obj->dumps()); } -SrsGoApiSdp::SrsGoApiSdp() +SrsGoApiSdp::SrsGoApiSdp(SrsServer* svr) { + server = svr; } SrsGoApiSdp::~SrsGoApiSdp() @@ -872,6 +874,14 @@ srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* SrsJsonObject* obj = SrsJsonAny::object(); SrsAutoFree(SrsJsonObject, obj); + + SrsRtcListener* rtc_listener = dynamic_cast(server->find_listener(SrsListenerRtcOverUdp)); + if (rtc_listener == NULL) { + return srs_go_http_error(w, SRS_CONSTS_HTTP_Unauthorized); + } + + SrsRtcOverUdp* rtc = rtc_listener->get_rtc(); + rtc->create_rtc_session("192.168.170.169", "xiaozhihongjohn", "ok"); obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); obj->set("server", SrsJsonAny::integer(stat->server_id())); diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index 367edabab..d77ba517b 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -166,8 +166,10 @@ public: class SrsGoApiSdp : public ISrsHttpHandler { +private: + SrsServer* server; public: - SrsGoApiSdp(); + SrsGoApiSdp(SrsServer* svr); virtual ~SrsGoApiSdp(); public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); diff --git a/trunk/src/app/srs_app_rtc_udp.cpp b/trunk/src/app/srs_app_rtc_udp.cpp index 4866c387a..190c8b2c1 100644 --- a/trunk/src/app/srs_app_rtc_udp.cpp +++ b/trunk/src/app/srs_app_rtc_udp.cpp @@ -36,21 +36,39 @@ using namespace std; #include #include #include -#include #include -#include #include #include -#include #include #include +#include +#include #include -#include -#include #include -#include #include +static bool is_stun(const char* data, const int size) { + return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1); +} + +static bool is_rtp_or_rtcp(const char* data, const int size) { + return data != NULL && size > 0 && (data[0] >= 128 && data[0] <= 191); +} + +static bool is_dtls(const char* data, const int size) { + return data != NULL && size > 0 && (data[0] >= 20 && data[0] <= 64); +} + +SrsRtcUserInfo::SrsRtcUserInfo(const std::string& u, const std::string& p) +{ + username = u; + password = p; +} + +SrsRtcUserInfo::~SrsRtcUserInfo() +{ +} + SrsRtcOverUdp::SrsRtcOverUdp() { } @@ -59,6 +77,30 @@ SrsRtcOverUdp::~SrsRtcOverUdp() { } +SrsRtcSession* SrsRtcOverUdp::create_rtc_session(const std::string& peer_ip, const std::string& remote_username, const std::string& remote_password) +{ + SrsRtcSession* rtc_session = new SrsRtcSession(); + // TODO: process exception when session already exist + user_session_map[peer_ip].insert(make_pair(SrsRtcUserInfo(remote_username, remote_password), rtc_session)).second; + + return rtc_session; +} + +SrsRtcSession* SrsRtcOverUdp::find_rtc_session_by_user_info(const std::string& peer_ip, const std::string& remote_username, const std::string& remote_password) +{ + std::map >::iterator iter = user_session_map.find(peer_ip); + if (iter == user_session_map.end()) { + return NULL; + } + + std::map::iterator sub_iter = iter->second.find(SrsRtcUserInfo(remote_username, remote_password)); + if (sub_iter == iter->second.end()) { + return NULL; + } + + return sub_iter->second; +} + srs_error_t SrsRtcOverUdp::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) { char address_string[64]; @@ -71,19 +113,120 @@ srs_error_t SrsRtcOverUdp::on_udp_packet(const sockaddr* from, const int fromlen } std::string peer_ip = std::string(address_string); int peer_port = atoi(port_string); - - srs_error_t err = on_udp_bytes(peer_ip, peer_port, buf, nb_buf); - if (err != srs_success) { - return srs_error_wrap(err, "process udp"); + + std::string peer_id = peer_ip + ":" + std::string(port_string); + + return on_udp_bytes(peer_ip, peer_port, peer_id, buf, nb_buf); +} + +SrsRtcSession* SrsRtcOverUdp::find_rtc_session_by_peer_id(const std::string& peer_id) +{ + map::iterator iter = id_session_map.find(peer_id); + if (iter == id_session_map.end()) { + return NULL; + } + + return iter->second; +} + +srs_error_t SrsRtcOverUdp::on_udp_bytes(const string& host, const int& port, const string& peer_id, char* buf, int nb_buf) +{ + srs_error_t err = srs_success; + + srs_trace("recv rtc udp packet from %s:%d, peer_id=%s, nb_buf=%d", host.c_str(), port, peer_id.c_str(), nb_buf); + + if (is_rtp_or_rtcp(buf, nb_buf)) { + err = on_rtp_or_rtcp(host, port, peer_id, buf, nb_buf); + } else if (is_stun(buf, nb_buf)) { + err = on_stun(host, port, peer_id, buf, nb_buf); + } else if (is_dtls(buf, nb_buf)) { + err = on_dtls(host, port, peer_id, buf, nb_buf); + } else { + return srs_error_wrap(err, "unknown udp packet"); + } + + return err; +} + +srs_error_t SrsRtcOverUdp::on_rtp_or_rtcp(const string& host, const int& port, const string& peer_id, const char* buf, int nb_buf) { + srs_error_t err = srs_success; + + SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(peer_id); + if (rtc_session == NULL) { + return srs_error_wrap(err, "can't find rtc session in rtp/rtcp host=%s, port=%d", + host.c_str(), port); } + + SrsRtpPacket rtp_packet; + SrsBuffer buffer(const_cast(buf), nb_buf); + rtp_packet.decode(&buffer); + + rtc_session->on_rtp_or_rtcp(&rtp_packet); + return err; } -srs_error_t SrsRtcOverUdp::on_udp_bytes(string host, int port, char* buf, int nb_buf) +srs_error_t SrsRtcOverUdp::on_stun(const string& host, const int& port, const string& peer_id, const char* buf, int nb_buf) { + srs_error_t err = srs_success; + + SrsStunPacket stun_packet; + stun_packet.decode(buf, nb_buf); + + SrsRtcSession* rtc_session = find_rtc_session_by_user_info(host, stun_packet.username(), stun_packet.password()); + if (rtc_session == NULL) { + return err; + return srs_error_wrap(err, "can't find rtc session in stun host=%s, port=%d, username=%s, password=%s", + host.c_str(), port, stun_packet.username().c_str(), stun_packet.password().c_str()); + } + + // TODO: process when session mismatch + id_session_map[peer_id] = rtc_session; + + rtc_session->on_stun(&stun_packet); + + return err; +} + +srs_error_t SrsRtcOverUdp::on_dtls(const string& host, const int& port, const string& peer_id, const char* buf, int nb_buf) { + srs_error_t err = srs_success; + + SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(peer_id); + if (rtc_session == NULL) { + return srs_error_wrap(err, "can't find rtc session in dtls host=%s, port=%d", + host.c_str(), port); + } + + rtc_session->on_dtls(); + + return err; +} + +SrsRtcSession::SrsRtcSession() +{ +} + +SrsRtcSession::~SrsRtcSession() +{ +} + +srs_error_t SrsRtcSession::on_rtp_or_rtcp(SrsRtpPacket* rtp_packet) +{ + srs_error_t err = srs_success; + + return err; +} + +srs_error_t SrsRtcSession::on_stun(SrsStunPacket* stun_packet) { srs_error_t err = srs_success; - srs_trace("recv rtc udp packet from %s:%d, nb_buf=%d", host.c_str(), port, nb_buf); + return err; +} + +srs_error_t SrsRtcSession::on_dtls() +{ + srs_error_t err = srs_success; return err; } + diff --git a/trunk/src/app/srs_app_rtc_udp.hpp b/trunk/src/app/srs_app_rtc_udp.hpp index 707b6b830..405805549 100644 --- a/trunk/src/app/srs_app_rtc_udp.hpp +++ b/trunk/src/app/srs_app_rtc_udp.hpp @@ -34,18 +34,62 @@ struct sockaddr; #include #include +class SrsRtcSession; + +class SrsRtcUserInfo { +private: + std::string username; + std::string password; +public: + SrsRtcUserInfo(const std::string& u, const std::string& p); + ~SrsRtcUserInfo(); + + bool operator<(const SrsRtcUserInfo& rhs) const + { + return username < rhs.username && password < rhs.password; + } +}; + // The rtc over udp stream receiver class SrsRtcOverUdp : virtual public ISrsUdpHandler { private: + std::map id_session_map; // ip:port => session + std::map > user_session_map; public: SrsRtcOverUdp(); virtual ~SrsRtcOverUdp(); + + SrsRtcSession* create_rtc_session(const std::string& peer_ip, const std::string& remote_username, const std::string& remote_password); + SrsRtcSession* find_rtc_session_by_user_info(const std::string& peer_ip, const std::string& remote_username, const std::string& remote_password); + SrsRtcSession* find_rtc_session_by_peer_id(const std::string& peer_id); // Interface ISrsUdpHandler public: virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); private: - virtual srs_error_t on_udp_bytes(std::string host, int port, char* buf, int nb_buf); + virtual srs_error_t on_udp_bytes(const std::string& host, const int& port, const std::string& peer_id, char* buf, int nb_buf); + srs_error_t on_rtp_or_rtcp(const std::string& host, const int& port, const std::string& peer_id, const char* buf, int nb_buf); + srs_error_t on_stun(const std::string& host, const int& port, const std::string& peer_id, const char* buf, int nb_buf); + srs_error_t on_dtls(const std::string& host, const int& port, const std::string& peer_id, const char* buf, int nb_buf); +}; + +class SrsRtpPacket; +class SrsStunPacket; + +class SrsRtcSession +{ +private: + std::string local_username; + std::string local_password; + std::string remote_username; + std::string remote_password; +public: + SrsRtcSession(); + virtual ~SrsRtcSession(); + + srs_error_t on_rtp_or_rtcp(SrsRtpPacket* rtp_packet); + srs_error_t on_stun(SrsStunPacket* stun_packet); + srs_error_t on_dtls(); }; #endif diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 85483a5b9..b833803f2 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -348,6 +348,12 @@ SrsRtcListener::~SrsRtcListener() { } + +SrsRtcOverUdp* SrsRtcListener::get_rtc() +{ + return dynamic_cast(rtc); +} + srs_error_t SrsRtcListener::listen(std::string i, int p) { srs_error_t err = srs_success; @@ -836,7 +842,7 @@ srs_error_t SrsServer::http_handle() if ((err = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != srs_success) { return srs_error_wrap(err, "handle streams"); } - if ((err = http_api_mux->handle("/api/v1/sdp/", new SrsGoApiSdp())) != srs_success) { + if ((err = http_api_mux->handle("/api/v1/sdp/", new SrsGoApiSdp(this))) != srs_success) { return srs_error_wrap(err, "handle sdp"); } if ((err = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != srs_success) { @@ -1282,6 +1288,23 @@ void SrsServer::close_listeners(SrsListenerType type) } } +SrsListener* SrsServer::find_listener(SrsListenerType type) +{ + std::vector::iterator it; + for (it = listeners.begin(); it != listeners.end();) { + SrsListener* listener = *it; + + if (listener->listen_type() != type) { + ++it; + continue; + } + + return *it; + } + + return NULL; +} + void SrsServer::resample_kbps() { SrsStatistic* stat = SrsStatistic::instance(); diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 77b8964df..a8ac87d78 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -157,6 +157,8 @@ public: virtual ~SrsUdpCasterListener(); }; +class SrsRtcOverUdp; + // A UDP listener, for udp stream caster server. class SrsRtcListener : public SrsListener { @@ -166,6 +168,8 @@ protected: public: SrsRtcListener(SrsServer* svr, SrsListenerType t); virtual ~SrsRtcListener(); + + SrsRtcOverUdp* get_rtc(); public: virtual srs_error_t listen(std::string i, int p); }; @@ -335,6 +339,8 @@ public: public: virtual srs_error_t on_publish(SrsSource* s, SrsRequest* r); virtual void on_unpublish(SrsSource* s, SrsRequest* r); +// listeners commuction + virtual SrsListener* find_listener(SrsListenerType type); }; #endif diff --git a/trunk/src/protocol/srs_stun_stack.cpp b/trunk/src/protocol/srs_stun_stack.cpp new file mode 100644 index 000000000..5273f5ee0 --- /dev/null +++ b/trunk/src/protocol/srs_stun_stack.cpp @@ -0,0 +1,28 @@ +#include + +using namespace std; + +SrsStunPacket::SrsStunPacket() +{ +} + +SrsStunPacket::~SrsStunPacket() +{ +} + +string SrsStunPacket::username() +{ + return ""; +} + +string SrsStunPacket::password() +{ + return ""; +} + +srs_error_t SrsStunPacket::decode(const char* buf, const int& nb_buf) +{ + srs_error_t err = srs_success; + + return err; +} diff --git a/trunk/src/protocol/srs_stun_stack.hpp b/trunk/src/protocol/srs_stun_stack.hpp new file mode 100644 index 000000000..7f1522c2a --- /dev/null +++ b/trunk/src/protocol/srs_stun_stack.hpp @@ -0,0 +1,43 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_PROTOCOL_STUN_HPP +#define SRS_PROTOCOL_STUN_HPP + +#include + +#include +#include + +class SrsStunPacket { +public: + SrsStunPacket(); + virtual ~SrsStunPacket(); + + std::string username(); + std::string password(); + + srs_error_t decode(const char* buf, const int& nb_buf); +}; + +#endif From 62563bdd8196bf12acbb0f18152ecfd321cde4e8 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Fri, 28 Feb 2020 23:18:39 +0800 Subject: [PATCH 05/21] rtc framework --- trunk/configure | 2 +- trunk/src/app/srs_app_http_api.cpp | 10 +- trunk/src/app/srs_app_listener.cpp | 39 +++++ trunk/src/app/srs_app_listener.hpp | 15 +- trunk/src/app/srs_app_rtc.cpp | 86 ++++++++++ trunk/src/app/srs_app_rtc.hpp | 52 ++++++ trunk/src/app/srs_app_rtc_conn.cpp | 143 ++++++++++++++++ trunk/src/app/srs_app_rtc_conn.hpp | 88 ++++++++++ trunk/src/app/srs_app_rtc_udp.cpp | 232 -------------------------- trunk/src/app/srs_app_rtc_udp.hpp | 96 ----------- trunk/src/app/srs_app_server.cpp | 28 ++-- trunk/src/app/srs_app_server.hpp | 14 +- trunk/src/protocol/srs_stun_stack.cpp | 6 +- trunk/src/protocol/srs_stun_stack.hpp | 9 +- 14 files changed, 448 insertions(+), 372 deletions(-) create mode 100644 trunk/src/app/srs_app_rtc.cpp create mode 100644 trunk/src/app/srs_app_rtc.hpp create mode 100644 trunk/src/app/srs_app_rtc_conn.cpp create mode 100644 trunk/src/app/srs_app_rtc_conn.hpp delete mode 100644 trunk/src/app/srs_app_rtc_udp.cpp delete mode 100644 trunk/src/app/srs_app_rtc_udp.hpp diff --git a/trunk/configure b/trunk/configure index 5de5bc31d..a29047e72 100755 --- a/trunk/configure +++ b/trunk/configure @@ -254,7 +254,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_edge" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static" "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds" - "srs_app_mpegts_udp" "srs_app_rtc_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" + "srs_app_mpegts_udp" "srs_app_rtc" "srs_app_rtc_conn" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" "srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr" "srs_app_coworkers" "srs_app_hybrid") diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 570d14662..fc54303b4 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -46,7 +46,7 @@ using namespace std; #include #include #include -#include +#include string test_sdp = "v=0\\r\\n" @@ -875,14 +875,6 @@ srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* SrsJsonObject* obj = SrsJsonAny::object(); SrsAutoFree(SrsJsonObject, obj); - SrsRtcListener* rtc_listener = dynamic_cast(server->find_listener(SrsListenerRtcOverUdp)); - if (rtc_listener == NULL) { - return srs_go_http_error(w, SRS_CONSTS_HTTP_Unauthorized); - } - - SrsRtcOverUdp* rtc = rtc_listener->get_rtc(); - rtc->create_rtc_session("192.168.170.169", "xiaozhihongjohn", "ok"); - obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); obj->set("server", SrsJsonAny::integer(stat->server_id())); diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index b33b4f6f8..0cd389df5 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -207,3 +207,42 @@ srs_error_t SrsTcpListener::cycle() return err; } +SrsUdpRemuxListener::SrsUdpRemuxListener(ISrsUdpHandler* h, std::string i, int p) : SrsUdpListener(h, i, p) +{ +} + +SrsUdpRemuxListener::~SrsUdpRemuxListener() +{ +} + +srs_error_t SrsUdpRemuxListener::cycle() +{ + srs_error_t err = srs_success; + + while (true) { + if ((err = trd->pull()) != srs_success) { + return srs_error_wrap(err, "udp listener"); + } + + int nread = 0; + sockaddr_storage from; + int nb_from = sizeof(from); + if ((nread = srs_recvfrom(lfd, buf, nb_buf, (sockaddr*)&from, &nb_from, SRS_UTIME_NO_TIMEOUT)) <= 0) { + srs_error("udp recv error"); + // remux udp never return + continue; + } + + if ((err = handler->on_udp_packet((const sockaddr*)&from, nb_from, buf, nread)) != srs_success) { + //srs_error("udp handle packet error"); + // remux udp never return + continue; + } + + if (SrsUdpPacketRecvCycleInterval > 0) { + srs_usleep(SrsUdpPacketRecvCycleInterval); + } + } + + return err; +} diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index d7d930e91..a667af94d 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -68,13 +68,13 @@ public: // Bind udp port, start thread to recv packet and handler it. class SrsUdpListener : public ISrsCoroutineHandler { -private: +protected: srs_netfd_t lfd; SrsCoroutine* trd; -private: +protected: char* buf; int nb_buf; -private: +protected: ISrsUdpHandler* handler; std::string ip; int port; @@ -113,4 +113,13 @@ public: virtual srs_error_t cycle(); }; +class SrsUdpRemuxListener : public SrsUdpListener +{ +public: + SrsUdpRemuxListener(ISrsUdpHandler* h, std::string i, int p); + virtual ~SrsUdpRemuxListener(); +public: + virtual srs_error_t cycle(); +}; + #endif diff --git a/trunk/src/app/srs_app_rtc.cpp b/trunk/src/app/srs_app_rtc.cpp new file mode 100644 index 000000000..6f5378125 --- /dev/null +++ b/trunk/src/app/srs_app_rtc.cpp @@ -0,0 +1,86 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool is_stun(const char* data, const int size) { + return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1); +} + +static bool is_rtp_or_rtcp(const char* data, const int size) { + return data != NULL && size > 0 && (data[0] >= 128 && data[0] <= 191); +} + +static bool is_dtls(const char* data, const int size) { + return data != NULL && size > 0 && (data[0] >= 20 && data[0] <= 64); +} + +SrsRtc::SrsRtc(SrsRtcServer* rtc_svr) +{ + rtc_server = rtc_svr; +} + +SrsRtc::~SrsRtc() +{ +} + +srs_error_t SrsRtc::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) +{ + char address_string[64]; + char port_string[16]; + if(getnameinfo(from, fromlen, + (char*)&address_string, sizeof(address_string), + (char*)&port_string, sizeof(port_string), + NI_NUMERICHOST|NI_NUMERICSERV)) { + return srs_error_new(ERROR_SYSTEM_IP_INVALID, "bad address"); + } + std::string peer_ip = std::string(address_string); + int peer_port = atoi(port_string); + + return rtc_server->on_udp_packet(peer_ip, peer_port, buf, nb_buf); +} diff --git a/trunk/src/app/srs_app_rtc.hpp b/trunk/src/app/srs_app_rtc.hpp new file mode 100644 index 000000000..6e1fb0651 --- /dev/null +++ b/trunk/src/app/srs_app_rtc.hpp @@ -0,0 +1,52 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_APP_RTC_HPP +#define SRS_APP_RTC_HPP + +#include + +struct sockaddr; +#include +#include + +#include +#include +#include + +class SrsRtcServer; + +// The rtc over udp stream receiver +class SrsRtc : virtual public ISrsUdpHandler +{ +private: + SrsRtcServer* rtc_server; +public: + SrsRtc(SrsRtcServer* rtc_svr); + virtual ~SrsRtc(); +// Interface ISrsUdpHandler +public: + virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); +}; + +#endif diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp new file mode 100644 index 000000000..4f858da0a --- /dev/null +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -0,0 +1,143 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +using namespace std; + +#include +#include +#include + +static bool is_stun(const char* data, const int size) { + return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1); +} + +static bool is_rtp_or_rtcp(const char* data, const int size) { + return data != NULL && size > 0 && (data[0] >= 128 && data[0] <= 191); +} + +static bool is_dtls(const char* data, const int size) { + return data != NULL && size > 0 && (data[0] >= 20 && data[0] <= 64); +} + +SrsSDP::SrsSDP() +{ +} + +SrsSDP::~SrsSDP() +{ +} + +SrsRtcSession::SrsRtcSession() +{ + session_state = INIT; +} + +SrsRtcSession::~SrsRtcSession() +{ +} + +srs_error_t SrsRtcSession::on_stun(const SrsStunPacket& stun_packet) +{ + srs_error_t err = srs_success; + + return err; +} + +srs_error_t SrsRtcSession::send_packet() +{ +} + +SrsRtcServer::SrsRtcServer(SrsServer* svr) +{ + server = svr; +} + +SrsRtcServer::~SrsRtcServer() +{ +} + +srs_error_t SrsRtcServer::initialize() +{ + srs_error_t err = srs_success; + + return err; +} + +srs_error_t SrsRtcServer::on_udp_packet(const string& peer_ip, const int peer_port, const char* data, const int size) +{ + srs_error_t err = srs_success; + + if (is_stun(data, size)) { + return on_stun(peer_ip, peer_port, data, size); + } else if (is_dtls(data, size)) { + return on_dtls(peer_ip, peer_port, data, size); + } else if (is_rtp_or_rtcp(data, size)) { + return on_rtp_or_rtcp(peer_ip, peer_port, data, size); + } + + return srs_error_wrap(err, "unknown packet type"); +} + +srs_error_t SrsRtcServer::on_stun(const string& peer_ip, const int peer_port, const char* data, const int size) +{ + srs_error_t err = srs_success; + + srs_trace("peer %s:%d stun", peer_ip.c_str(), peer_port); + + SrsStunPacket stun_packet; + if (stun_packet.decode(data, size) != srs_success) { + return srs_error_wrap(err, "decode stun failed"); + } + + std::string peer_ufrag = stun_packet.ufrag(); + SrsRtcSession* rtc_session = find_rtc_session(peer_ufrag); + if (rtc_session == NULL) { + return srs_error_wrap(err, "can not find rtc_session, ufrag=%s", peer_ufrag.c_str()); + } + + return rtc_session->on_stun(stun_packet); +} + +srs_error_t SrsRtcServer::on_dtls(const string& peer_ip, const int peer_port, const char* data, const int size) +{ + srs_error_t err = srs_success; + return err; +} + +srs_error_t SrsRtcServer::on_rtp_or_rtcp(const string& peer_ip, const int peer_port, const char* data, const int size) +{ + srs_error_t err = srs_success; + return err; +} + +SrsRtcSession* SrsRtcServer::find_rtc_session(const std::string& ufrag) +{ + map::iterator iter = map_sessions.find(ufrag); + if (iter == map_sessions.end()) { + return NULL; + } + + return iter->second; +} diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp new file mode 100644 index 000000000..b5ba40235 --- /dev/null +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -0,0 +1,88 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_APP_RTC_CONN_HPP +#define SRS_APP_RTC_CONN_HPP + +#include + +#include +#include + +class SrsServer; +class SrsStunPacket; + +class SrsSDP +{ +private: +public: + SrsSDP(); + virtual ~SrsSDP(); +}; + +enum SrsRtcSessionStateType +{ + INIT = -1, + WAITING_STUN = 1, + DOING_DTLS_HANDSHAKE = 2, + ESTABLISHED = 3, + CLOSED = 4, +}; + +class SrsRtcSession +{ +public: +private: + SrsSDP peer_sdp; + SrsSDP offer_sdp; + SrsRtcSessionStateType session_state; +public: + SrsRtcSession(); + virtual ~SrsRtcSession(); + + srs_error_t on_udp_packet(const std::string& peer_ip, const int peer_port, const char* data, const int size); + srs_error_t on_stun(const SrsStunPacket& stun_packet); + srs_error_t send_packet(); +}; + +class SrsRtcServer +{ +private: + SrsServer* server; + std::map map_sessions; +public: + SrsRtcServer(SrsServer* svr); + virtual ~SrsRtcServer(); +public: + virtual srs_error_t initialize(); + virtual srs_error_t on_udp_packet(const std::string& peer_ip, const int peer_port, const char* data, const int size); +private: + srs_error_t on_stun(const std::string& peer_ip, const int peer_port, const char* data, const int size); + srs_error_t on_dtls(const std::string& peer_ip, const int peer_port, const char* data, const int size); + srs_error_t on_rtp_or_rtcp(const std::string& peer_ip, const int peer_port, const char* data, const int size); +private: + SrsRtcSession* find_rtc_session(const std::string& ufrag); +}; + +#endif + diff --git a/trunk/src/app/srs_app_rtc_udp.cpp b/trunk/src/app/srs_app_rtc_udp.cpp deleted file mode 100644 index 190c8b2c1..000000000 --- a/trunk/src/app/srs_app_rtc_udp.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2013-2020 Winlin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include - -#include -#include -#include -#include -#include -using namespace std; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static bool is_stun(const char* data, const int size) { - return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1); -} - -static bool is_rtp_or_rtcp(const char* data, const int size) { - return data != NULL && size > 0 && (data[0] >= 128 && data[0] <= 191); -} - -static bool is_dtls(const char* data, const int size) { - return data != NULL && size > 0 && (data[0] >= 20 && data[0] <= 64); -} - -SrsRtcUserInfo::SrsRtcUserInfo(const std::string& u, const std::string& p) -{ - username = u; - password = p; -} - -SrsRtcUserInfo::~SrsRtcUserInfo() -{ -} - -SrsRtcOverUdp::SrsRtcOverUdp() -{ -} - -SrsRtcOverUdp::~SrsRtcOverUdp() -{ -} - -SrsRtcSession* SrsRtcOverUdp::create_rtc_session(const std::string& peer_ip, const std::string& remote_username, const std::string& remote_password) -{ - SrsRtcSession* rtc_session = new SrsRtcSession(); - // TODO: process exception when session already exist - user_session_map[peer_ip].insert(make_pair(SrsRtcUserInfo(remote_username, remote_password), rtc_session)).second; - - return rtc_session; -} - -SrsRtcSession* SrsRtcOverUdp::find_rtc_session_by_user_info(const std::string& peer_ip, const std::string& remote_username, const std::string& remote_password) -{ - std::map >::iterator iter = user_session_map.find(peer_ip); - if (iter == user_session_map.end()) { - return NULL; - } - - std::map::iterator sub_iter = iter->second.find(SrsRtcUserInfo(remote_username, remote_password)); - if (sub_iter == iter->second.end()) { - return NULL; - } - - return sub_iter->second; -} - -srs_error_t SrsRtcOverUdp::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) -{ - char address_string[64]; - char port_string[16]; - if(getnameinfo(from, fromlen, - (char*)&address_string, sizeof(address_string), - (char*)&port_string, sizeof(port_string), - NI_NUMERICHOST|NI_NUMERICSERV)) { - return srs_error_new(ERROR_SYSTEM_IP_INVALID, "bad address"); - } - std::string peer_ip = std::string(address_string); - int peer_port = atoi(port_string); - - std::string peer_id = peer_ip + ":" + std::string(port_string); - - return on_udp_bytes(peer_ip, peer_port, peer_id, buf, nb_buf); -} - -SrsRtcSession* SrsRtcOverUdp::find_rtc_session_by_peer_id(const std::string& peer_id) -{ - map::iterator iter = id_session_map.find(peer_id); - if (iter == id_session_map.end()) { - return NULL; - } - - return iter->second; -} - -srs_error_t SrsRtcOverUdp::on_udp_bytes(const string& host, const int& port, const string& peer_id, char* buf, int nb_buf) -{ - srs_error_t err = srs_success; - - srs_trace("recv rtc udp packet from %s:%d, peer_id=%s, nb_buf=%d", host.c_str(), port, peer_id.c_str(), nb_buf); - - if (is_rtp_or_rtcp(buf, nb_buf)) { - err = on_rtp_or_rtcp(host, port, peer_id, buf, nb_buf); - } else if (is_stun(buf, nb_buf)) { - err = on_stun(host, port, peer_id, buf, nb_buf); - } else if (is_dtls(buf, nb_buf)) { - err = on_dtls(host, port, peer_id, buf, nb_buf); - } else { - return srs_error_wrap(err, "unknown udp packet"); - } - - return err; -} - -srs_error_t SrsRtcOverUdp::on_rtp_or_rtcp(const string& host, const int& port, const string& peer_id, const char* buf, int nb_buf) { - srs_error_t err = srs_success; - - SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(peer_id); - if (rtc_session == NULL) { - return srs_error_wrap(err, "can't find rtc session in rtp/rtcp host=%s, port=%d", - host.c_str(), port); - } - - SrsRtpPacket rtp_packet; - SrsBuffer buffer(const_cast(buf), nb_buf); - rtp_packet.decode(&buffer); - - rtc_session->on_rtp_or_rtcp(&rtp_packet); - - return err; -} - -srs_error_t SrsRtcOverUdp::on_stun(const string& host, const int& port, const string& peer_id, const char* buf, int nb_buf) { - srs_error_t err = srs_success; - - SrsStunPacket stun_packet; - stun_packet.decode(buf, nb_buf); - - SrsRtcSession* rtc_session = find_rtc_session_by_user_info(host, stun_packet.username(), stun_packet.password()); - if (rtc_session == NULL) { - return err; - return srs_error_wrap(err, "can't find rtc session in stun host=%s, port=%d, username=%s, password=%s", - host.c_str(), port, stun_packet.username().c_str(), stun_packet.password().c_str()); - } - - // TODO: process when session mismatch - id_session_map[peer_id] = rtc_session; - - rtc_session->on_stun(&stun_packet); - - return err; -} - -srs_error_t SrsRtcOverUdp::on_dtls(const string& host, const int& port, const string& peer_id, const char* buf, int nb_buf) { - srs_error_t err = srs_success; - - SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(peer_id); - if (rtc_session == NULL) { - return srs_error_wrap(err, "can't find rtc session in dtls host=%s, port=%d", - host.c_str(), port); - } - - rtc_session->on_dtls(); - - return err; -} - -SrsRtcSession::SrsRtcSession() -{ -} - -SrsRtcSession::~SrsRtcSession() -{ -} - -srs_error_t SrsRtcSession::on_rtp_or_rtcp(SrsRtpPacket* rtp_packet) -{ - srs_error_t err = srs_success; - - return err; -} - -srs_error_t SrsRtcSession::on_stun(SrsStunPacket* stun_packet) -{ - srs_error_t err = srs_success; - - return err; -} - -srs_error_t SrsRtcSession::on_dtls() -{ - srs_error_t err = srs_success; - - return err; -} - diff --git a/trunk/src/app/srs_app_rtc_udp.hpp b/trunk/src/app/srs_app_rtc_udp.hpp deleted file mode 100644 index 405805549..000000000 --- a/trunk/src/app/srs_app_rtc_udp.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2013-2020 Winlin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SRS_APP_RTC_UDP_HPP -#define SRS_APP_RTC_UDP_HPP - -#include - -struct sockaddr; -#include -#include - -#include -#include -#include - -class SrsRtcSession; - -class SrsRtcUserInfo { -private: - std::string username; - std::string password; -public: - SrsRtcUserInfo(const std::string& u, const std::string& p); - ~SrsRtcUserInfo(); - - bool operator<(const SrsRtcUserInfo& rhs) const - { - return username < rhs.username && password < rhs.password; - } -}; - -// The rtc over udp stream receiver -class SrsRtcOverUdp : virtual public ISrsUdpHandler -{ -private: - std::map id_session_map; // ip:port => session - std::map > user_session_map; -public: - SrsRtcOverUdp(); - virtual ~SrsRtcOverUdp(); - - SrsRtcSession* create_rtc_session(const std::string& peer_ip, const std::string& remote_username, const std::string& remote_password); - SrsRtcSession* find_rtc_session_by_user_info(const std::string& peer_ip, const std::string& remote_username, const std::string& remote_password); - SrsRtcSession* find_rtc_session_by_peer_id(const std::string& peer_id); -// Interface ISrsUdpHandler -public: - virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); -private: - virtual srs_error_t on_udp_bytes(const std::string& host, const int& port, const std::string& peer_id, char* buf, int nb_buf); - srs_error_t on_rtp_or_rtcp(const std::string& host, const int& port, const std::string& peer_id, const char* buf, int nb_buf); - srs_error_t on_stun(const std::string& host, const int& port, const std::string& peer_id, const char* buf, int nb_buf); - srs_error_t on_dtls(const std::string& host, const int& port, const std::string& peer_id, const char* buf, int nb_buf); -}; - -class SrsRtpPacket; -class SrsStunPacket; - -class SrsRtcSession -{ -private: - std::string local_username; - std::string local_password; - std::string remote_username; - std::string remote_password; -public: - SrsRtcSession(); - virtual ~SrsRtcSession(); - - srs_error_t on_rtp_or_rtcp(SrsRtpPacket* rtp_packet); - srs_error_t on_stun(SrsStunPacket* stun_packet); - srs_error_t on_dtls(); -}; - -#endif - diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index b833803f2..548ee3cdd 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -44,7 +44,8 @@ using namespace std; #include #include #include -#include +#include +#include #include #include #include @@ -109,8 +110,8 @@ std::string srs_listener_type2string(SrsListenerType type) return "RTSP"; case SrsListenerFlv: return "HTTP-FLV"; - case SrsListenerRtcOverUdp: - return "RTC over UDP"; + case SrsListenerRtc: + return "RTC"; default: return "UNKONWN"; } @@ -338,35 +339,29 @@ SrsUdpCasterListener::~SrsUdpCasterListener() srs_freep(caster); } -SrsRtcListener::SrsRtcListener(SrsServer* svr, SrsListenerType t) : SrsListener(svr, t) +SrsRtcListener::SrsRtcListener(SrsServer* svr, SrsRtcServer* rtc_svr, SrsListenerType t) : SrsListener(svr, t) { - srs_assert(type == SrsListenerRtcOverUdp); - rtc = new SrsRtcOverUdp(); + srs_assert(type == SrsListenerRtc); + rtc = new SrsRtc(rtc_svr); } SrsRtcListener::~SrsRtcListener() { } - -SrsRtcOverUdp* SrsRtcListener::get_rtc() -{ - return dynamic_cast(rtc); -} - srs_error_t SrsRtcListener::listen(std::string i, int p) { srs_error_t err = srs_success; // the caller already ensure the type is ok, // we just assert here for unknown stream caster. - srs_assert(type == SrsListenerRtcOverUdp); + srs_assert(type == SrsListenerRtc); ip = i; port = p; srs_freep(listener); - listener = new SrsUdpListener(rtc, ip, port); + listener = new SrsUdpRemuxListener(rtc, ip, port); if ((err = listener->listen()) != srs_success) { return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); @@ -533,6 +528,7 @@ SrsServer::SrsServer() // new these objects in initialize instead. http_api_mux = new SrsHttpServeMux(); http_server = new SrsHttpServer(this); + rtc_server = new SrsRtcServer(this); http_heartbeat = new SrsHttpHeartbeat(); ingester = new SrsIngester(); } @@ -1247,7 +1243,7 @@ srs_error_t SrsServer::listen_rtc() { srs_error_t err = srs_success; - close_listeners(SrsListenerRtcOverUdp); + close_listeners(SrsListenerRtc); if (!_srs_config->get_rtc_enabled()) { return err; @@ -1255,7 +1251,7 @@ srs_error_t SrsServer::listen_rtc() SrsListener* listener = NULL; - listener = new SrsRtcListener(this, SrsListenerRtcOverUdp); + listener = new SrsRtcListener(this, rtc_server, SrsListenerRtc); srs_assert(listener != NULL); listeners.push_back(listener); diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index a8ac87d78..36e4c8b3f 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -41,6 +41,7 @@ class SrsServer; class SrsConnection; class SrsHttpServeMux; class SrsHttpServer; +class SrsRtcServer; class SrsIngester; class SrsHttpHeartbeat; class SrsKbps; @@ -68,8 +69,8 @@ enum SrsListenerType SrsListenerRtsp = 4, // TCP stream, FLV stream over HTTP. SrsListenerFlv = 5, - // UDP sream, rtp over udp - SrsListenerRtcOverUdp = 6, + // UDP remux, rtp over udp + SrsListenerRtc = 6, }; // A common tcp listener, for RTMP/HTTP server. @@ -157,19 +158,15 @@ public: virtual ~SrsUdpCasterListener(); }; -class SrsRtcOverUdp; - -// A UDP listener, for udp stream caster server. +// A UDP listener, for udp remux rtc server class SrsRtcListener : public SrsListener { protected: SrsUdpListener* listener; ISrsUdpHandler* rtc; public: - SrsRtcListener(SrsServer* svr, SrsListenerType t); + SrsRtcListener(SrsServer* svr, SrsRtcServer* rtc_svr, SrsListenerType t); virtual ~SrsRtcListener(); - - SrsRtcOverUdp* get_rtc(); public: virtual srs_error_t listen(std::string i, int p); }; @@ -225,6 +222,7 @@ private: // TODO: FIXME: rename to http_api SrsHttpServeMux* http_api_mux; SrsHttpServer* http_server; + SrsRtcServer* rtc_server; SrsHttpHeartbeat* http_heartbeat; SrsIngester* ingester; SrsCoroutineManager* conn_manager; diff --git a/trunk/src/protocol/srs_stun_stack.cpp b/trunk/src/protocol/srs_stun_stack.cpp index 5273f5ee0..38c8c00a2 100644 --- a/trunk/src/protocol/srs_stun_stack.cpp +++ b/trunk/src/protocol/srs_stun_stack.cpp @@ -10,17 +10,17 @@ SrsStunPacket::~SrsStunPacket() { } -string SrsStunPacket::username() +string SrsStunPacket::ufrag() { return ""; } -string SrsStunPacket::password() +string SrsStunPacket::pwd() { return ""; } -srs_error_t SrsStunPacket::decode(const char* buf, const int& nb_buf) +srs_error_t SrsStunPacket::decode(const char* buf, const int nb_buf) { srs_error_t err = srs_success; diff --git a/trunk/src/protocol/srs_stun_stack.hpp b/trunk/src/protocol/srs_stun_stack.hpp index 7f1522c2a..61269050a 100644 --- a/trunk/src/protocol/srs_stun_stack.hpp +++ b/trunk/src/protocol/srs_stun_stack.hpp @@ -29,15 +29,16 @@ #include #include -class SrsStunPacket { +class SrsStunPacket +{ public: SrsStunPacket(); virtual ~SrsStunPacket(); - std::string username(); - std::string password(); + std::string ufrag(); + std::string pwd(); - srs_error_t decode(const char* buf, const int& nb_buf); + srs_error_t decode(const char* buf, const int nb_buf); }; #endif From 30d8b2209f0718ddaaae8a9921e5eac7f073eb23 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Mon, 2 Mar 2020 22:47:40 +0800 Subject: [PATCH 06/21] parse sdp --- trunk/src/app/srs_app_http_api.cpp | 31 +++++++++++++- trunk/src/app/srs_app_rtc_conn.cpp | 69 +++++++++++++++++++++++++++++- trunk/src/app/srs_app_rtc_conn.hpp | 30 ++++++++++--- 3 files changed, 122 insertions(+), 8 deletions(-) diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index fc54303b4..9273f4bd4 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -47,6 +47,7 @@ using namespace std; #include #include #include +#include string test_sdp = "v=0\\r\\n" @@ -871,7 +872,35 @@ srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* if (sid >= 0 && (stream = stat->find_stream(sid)) == NULL) { return srs_api_response_code(w, r, ERROR_RTMP_STREAM_NOT_FOUND); } - + + string req_json; + r->body_read_all(req_json); + srs_trace("req_json=%s", req_json.c_str()); + + SrsJsonAny* json = SrsJsonAny::loads(req_json); + SrsJsonObject* req_obj = json->to_object(); + + SrsJsonAny* remote_sdp_obj = req_obj->get_property("sdp"); + SrsJsonAny* app_obj = req_obj->get_property("app"); + SrsJsonAny* stream_name_obj = req_obj->get_property("stream"); + + if (remote_sdp_obj == NULL || app_obj == NULL || stream_name_obj == NULL) { + return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest); + } + + string remote_sdp = remote_sdp_obj->to_str(); + string app = app_obj->to_str(); + string stream_name = stream_name_obj->to_str(); + + srs_trace("remote_sdp=%s", remote_sdp.c_str()); + srs_trace("app=%s, stream=%s", app.c_str(), stream_name.c_str()); + + SrsSdp srs_sdp; + err = srs_sdp.parse(remote_sdp); + if (err != srs_success) { + return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest); + } + SrsJsonObject* obj = SrsJsonAny::object(); SrsAutoFree(SrsJsonObject, obj); diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 4f858da0a..93fca7f8c 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -25,6 +25,8 @@ using namespace std; +#include + #include #include #include @@ -41,12 +43,75 @@ static bool is_dtls(const char* data, const int size) { return data != NULL && size > 0 && (data[0] >= 20 && data[0] <= 64); } -SrsSDP::SrsSDP() +SrsSdpMediaInfo::SrsSdpMediaInfo() +{ +} + +SrsSdpMediaInfo::~SrsSdpMediaInfo() +{ +} + +SrsSdp::SrsSdp() +{ +} + +SrsSdp::~SrsSdp() { } -SrsSDP::~SrsSDP() +srs_error_t SrsSdp::parse(const string& sdp) { + srs_error_t err = srs_success; + + if (sdp.size() < 2 || sdp[0] != 'v' || sdp[1] != '=') { + return srs_error_wrap(err, "invalid sdp"); + } + + string line; + istringstream is(sdp); + while (getline(is, line)) { + srs_trace("line=%s", line.c_str()); + + if (line.size() < 2 || line[1] != '=') { + return srs_error_wrap(err, "invalid sdp line=%s", line.c_str()); + } + + switch (line[1]) { + case 'v' :{ + break; + } + case 'o' :{ + break; + } + case 's' :{ + break; + } + case 't' :{ + break; + } + case 'c' :{ + break; + } + case 'a' :{ + if (parse_attr(line) != srs_success) { + return srs_error_wrap(err, "parse sdp line=%s failed", line.c_str()); + } + break; + } + case 'm' :{ + break; + } + } + } + + return err; +} + +srs_error_t SrsSdp::parse_attr(const string& line) +{ + srs_error_t err = srs_success; + + return err; } SrsRtcSession::SrsRtcSession() diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index b5ba40235..df370d585 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -28,16 +28,36 @@ #include #include +#include class SrsServer; class SrsStunPacket; -class SrsSDP +class SrsSdpMediaInfo { private: public: - SrsSDP(); - virtual ~SrsSDP(); + SrsSdpMediaInfo(); + virtual ~SrsSdpMediaInfo(); +}; + +class SrsSdp +{ +private: + std::string sdp; + int version; + std::string ice_ufrag; + std::string ice_pwd; + std::string fingerprint; + std::string setup; + std::vector media_infos; +public: + SrsSdp(); + virtual ~SrsSdp(); + + srs_error_t parse(const std::string& sdp); +private: + srs_error_t parse_attr(const std::string& line); }; enum SrsRtcSessionStateType @@ -53,8 +73,8 @@ class SrsRtcSession { public: private: - SrsSDP peer_sdp; - SrsSDP offer_sdp; + SrsSdp peer_sdp; + SrsSdp offer_sdp; SrsRtcSessionStateType session_state; public: SrsRtcSession(); From 9d5495c0c20fe8b499b6723b2c94e664b7a8b324 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Fri, 6 Mar 2020 23:01:48 +0800 Subject: [PATCH 07/21] "stun and dtls done" --- trunk/3rdparty/libsrtp-2.0.0.zip | Bin 0 -> 178 bytes trunk/auto/depends.sh | 21 ++ trunk/configure | 23 +- trunk/src/app/srs_app_dtls.cpp | 131 +++++++ trunk/src/app/srs_app_dtls.hpp | 52 +++ trunk/src/app/srs_app_http_api.cpp | 100 ++---- trunk/src/app/srs_app_http_api.hpp | 4 +- trunk/src/app/srs_app_listener.cpp | 57 ++- trunk/src/app/srs_app_listener.hpp | 30 +- trunk/src/app/srs_app_rtc.cpp | 4 +- trunk/src/app/srs_app_rtc.hpp | 4 +- trunk/src/app/srs_app_rtc_conn.cpp | 495 ++++++++++++++++++++++++-- trunk/src/app/srs_app_rtc_conn.hpp | 84 ++++- trunk/src/app/srs_app_server.cpp | 2 +- trunk/src/app/srs_app_server.hpp | 4 +- trunk/src/protocol/srs_stun_stack.cpp | 254 ++++++++++++- trunk/src/protocol/srs_stun_stack.hpp | 69 +++- trunk/src/service/srs_service_st.cpp | 5 + trunk/src/service/srs_service_st.hpp | 1 + 19 files changed, 1200 insertions(+), 140 deletions(-) create mode 100644 trunk/3rdparty/libsrtp-2.0.0.zip create mode 100644 trunk/src/app/srs_app_dtls.cpp create mode 100644 trunk/src/app/srs_app_dtls.hpp diff --git a/trunk/3rdparty/libsrtp-2.0.0.zip b/trunk/3rdparty/libsrtp-2.0.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..c149013ac25bd5e7e3d4714a6cdb3fa3035bf4e7 GIT binary patch literal 178 zcmWIWW@h1H0D+kb&K_U}l;C5KVaUl$DlRH1&^6LC&@<2v4dG;9e#E^ojPHM9TxkV2 x10%}|W(Ec@5#Y_pB*%=)BnhaQ0t|m0K}>XWSs~_Pn90fpQq2g2AwU}9JODC)96SI3 literal 0 HcmV?d00001 diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index a4c8730df..998a4fddb 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -242,6 +242,27 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then if [ ! -f ${SRS_OBJS}/st/libst.a ]; then echo "Build state-threads static lib failed."; exit -1; fi fi +##################################################################################### +# srtp +##################################################################################### +if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then + # Patched ST from https://github.com/ossrs/state-threads/tree/srs + if [[ -f ${SRS_OBJS}/srtp2/lib/libsrtp2.a ]]; then + echo "The srtp2 is ok."; + else + echo "Building srtp2."; + ( + rm -rf ${SRS_OBJS}/srtp2 && cd ${SRS_OBJS} && + ln -sf ../3rdparty/libsrtp-2.0.0 && cd libsrtp-2.0.0 && + ./configure --prefix=`pwd`/_release && make ${SRS_JOBS} && make install && + cd .. && rm -f srtp2 && ln -sf libsrtp-2.0.0/_release srtp2 + ) + fi + # check status + ret=$?; if [[ $ret -ne 0 ]]; then echo "Build srtp2 failed, ret=$ret"; exit $ret; fi + if [ ! -f ${SRS_OBJS}/srtp2/lib/libsrtp2.a ]; then echo "Build srtp2 static lib failed."; exit -1; fi +fi + ##################################################################################### # nginx for HLS, nginx-1.5.0 ##################################################################################### diff --git a/trunk/configure b/trunk/configure index a29047e72..047f41883 100755 --- a/trunk/configure +++ b/trunk/configure @@ -148,6 +148,9 @@ END # st(state-threads) the basic network library for SRS. LibSTRoot="${SRS_OBJS_DIR}/st"; LibSTfile="${LibSTRoot}/libst.a" if [[ $SRS_SHARED_ST == YES ]]; then LibSTfile="-lst"; fi +# srtp +LibSrtpRoot="${SRS_OBJS_DIR}/srtp2"; LibSrtpFile="${LibSrtpRoot}/lib/libsrtp2.a" +if [[ $SRS_SHARED_SRTP == YES ]]; then LibSrtpFile="-lsrtp2"; fi # openssl-1.1.0e, for the RTMP complex handshake. LibSSLRoot="";LibSSLfile="" if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL == NO ]]; then @@ -233,7 +236,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="SERVICE" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_service_log" "srs_service_st" "srs_service_http_client" "srs_service_http_conn" "srs_service_rtmp_conn" "srs_service_utility" "srs_service_conn") @@ -246,7 +249,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="APP" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config" @@ -254,7 +257,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_edge" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static" "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds" - "srs_app_mpegts_udp" "srs_app_rtc" "srs_app_rtc_conn" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" + "srs_app_mpegts_udp" "srs_app_rtc" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" "srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr" "srs_app_coworkers" "srs_app_hybrid") @@ -284,7 +287,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then if [[ $SRS_SRT == YES ]]; then MODULE_DEPENDS+=("SRT") fi - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) if [[ $SRS_SRT == YES ]]; then ModuleLibIncs+=("${LibSRTRoot[*]}") fi @@ -297,7 +300,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="MAIN" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) MODULE_FILES=() DEFINES="" # add each modules for main @@ -324,13 +327,13 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then done # # all depends libraries - ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile}) + ModuleLibFiles=(${LibSTfile} ${LibSrtpFile} ${LibSSLfile} ${LibGperfFile}) if [[ $SRS_SRT == YES ]]; then ModuleLibFiles+=("${LibSRTfile[*]}") fi # all depends objects MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${APP_OBJS[@]} ${SERVER_OBJS[@]}" - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${LibSrtpRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) if [[ $SRS_SRT == YES ]]; then MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}" fi @@ -341,7 +344,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then # # For modules, without the app module. MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${MAIN_OBJS[@]}" - ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile}) + ModuleLibFiles=(${LibSTfile} ${LibSrtpFile} ${LibSSLfile} ${LibGperfFile}) # for SRS_MODULE in ${SRS_MODULES[*]}; do . $SRS_MODULE/config @@ -361,11 +364,11 @@ if [ $SRS_UTEST = YES ]; then MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_protocol" "srs_utest_kernel" "srs_utest_core" "srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload" "srs_utest_mp4" "srs_utest_service" "srs_utest_app") - ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot}) + ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSrtpRoot} ${LibSSLRoot}) if [[ $SRS_SRT == YES ]]; then ModuleLibIncs+=("${LibSRTRoot[*]}") fi - ModuleLibFiles=(${LibSTfile} ${LibSSLfile}) + ModuleLibFiles=(${LibSTfile} ${LibSrtpFile} ${LibSSLfile}) if [[ $SRS_SRT == YES ]]; then ModuleLibFiles+=("${LibSRTfile[*]}") fi diff --git a/trunk/src/app/srs_app_dtls.cpp b/trunk/src/app/srs_app_dtls.cpp new file mode 100644 index 000000000..af09dcb2f --- /dev/null +++ b/trunk/src/app/srs_app_dtls.cpp @@ -0,0 +1,131 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +using namespace std; + +#include + +#include + +SrsDtls* SrsDtls::_instance = NULL; + +SrsDtls::SrsDtls() +{ +} + +SrsDtls::~SrsDtls() +{ +} + +SrsDtls* SrsDtls::instance() +{ + if (!_instance) { + _instance = new SrsDtls(); + _instance->init(); + } + return _instance; +} + +void SrsDtls::init() +{ + EVP_PKEY* dtls_private_key = EVP_PKEY_new(); + srs_assert(dtls_private_key); + + RSA* rsa = RSA_new(); + srs_assert(rsa); + + BIGNUM* exponent = BN_new(); + srs_assert(exponent); + + BN_set_word(exponent, RSA_F4); + + const std::string& aor = "www.hw.com"; + int expire_day = 365; + int private_key_len = 1024; + + RSA_generate_key_ex(rsa, private_key_len, exponent, NULL); + + srs_assert(EVP_PKEY_set1_RSA(dtls_private_key, rsa) == 1); + + X509* dtls_cert = X509_new(); + srs_assert(dtls_cert); + + X509_NAME* subject = X509_NAME_new(); + srs_assert(subject); + + int serial = rand(); + ASN1_INTEGER_set(X509_get_serialNumber(dtls_cert), serial); + + X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, (unsigned char *) aor.data(), aor.size(), -1, 0); + + X509_set_issuer_name(dtls_cert, subject); + X509_set_subject_name(dtls_cert, subject); + + const long cert_duration = 60*60*24*expire_day; + + X509_gmtime_adj(X509_get_notBefore(dtls_cert), 0); + X509_gmtime_adj(X509_get_notAfter(dtls_cert), cert_duration); + + srs_assert(X509_set_pubkey(dtls_cert, dtls_private_key) == 1); + + srs_assert(X509_sign(dtls_cert, dtls_private_key, EVP_sha1()) != 0); + + // cleanup + RSA_free(rsa); + BN_free(exponent); + X509_NAME_free(subject); + + dtls_ctx = SSL_CTX_new(DTLSv1_2_method()); + srs_assert(SSL_CTX_use_certificate(dtls_ctx, dtls_cert) == 1); + + srs_assert(SSL_CTX_use_PrivateKey(dtls_ctx, dtls_private_key) == 1); + srs_assert(SSL_CTX_set_cipher_list(dtls_ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH") == 1); + srs_assert(SSL_CTX_set_tlsext_use_srtp(dtls_ctx, "SRTP_AES128_CM_SHA1_80") == 0); + + SSL_CTX_set_verify_depth (dtls_ctx, 4); + SSL_CTX_set_read_ahead(dtls_ctx, 1); + + // dtls fingerprint + char fp[100] = {0}; + char *p = fp; + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int n = 0; + + int r = X509_digest(dtls_cert, EVP_sha256(), md, &n); + + for (unsigned int i = 0; i < n; i++, ++p) { + sprintf(p, "%02X", md[i]); + p += 2; + + if(i < (n-1)) { + *p = ':'; + } else { + *p = '\0'; + } + } + + fingerprint.assign(fp, strlen(fp)); + srs_trace("fingerprint=%s", fingerprint.c_str()); +} diff --git a/trunk/src/app/srs_app_dtls.hpp b/trunk/src/app/srs_app_dtls.hpp new file mode 100644 index 000000000..5853f91b3 --- /dev/null +++ b/trunk/src/app/srs_app_dtls.hpp @@ -0,0 +1,52 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_APP_DTLS_HPP +#define SRS_APP_DTLS_HPP + +#include + +#include + +#include + +class SrsDtls +{ +private: + static SrsDtls* _instance; +private: + std::string fingerprint; + SSL_CTX* dtls_ctx; +private: + SrsDtls(); + virtual ~SrsDtls(); + + void init(); +public: + static SrsDtls* instance(); + SSL_CTX* get_dtls_ctx() { return dtls_ctx; } +public: + std::string get_fingerprint() const { return fingerprint; } +}; + +#endif diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 9273f4bd4..fb33382d2 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -49,73 +49,6 @@ using namespace std; #include #include -string test_sdp = -"v=0\\r\\n" -"o=- 0 0 IN IP4 127.0.0.1\\r\\n" -"s=-\\r\\n" -"t=0 0\\r\\n" -"a=ice-lite\\r\\n" -"a=group:BUNDLE 0 1\\r\\n" -"a=msid-semantic: WMS 6VrfBKXrwK\\r\\n" -"m=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\n" -"c=IN IP4 0.0.0.0\\r\\n" -"a=candidate:10 1 udp 2115783679 192.168.170.129 9527 typ host generation 0\\r\\n" -"a=rtcp:9 IN IP4 0.0.0.0\\r\\n" -"a=ice-ufrag:xiaozhihongjohn\\r\\n" -"a=ice-pwd:simple_rtmp_server__john\\r\\n" -"a=ice-options:trickle\\r\\n" -"a=fingerprint:sha-256 76:E8:6A:6D:48:F0:86:58:30:2E:69:56:0F:C6:A1:B8:69:98:5D:73:45:93:37:8E:C4:2B:C7:97:04:18:E4:24\\r\\n" -"a=sendrecv\\r\\n" -"a=mid:0\\r\\n" -"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\\r\\n" -"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\n" -"a=rtcp-mux\\r\\n" -"a=rtpmap:111 opus/48000/2\\r\\n" -"a=fmtp:111 minptime=10;useinbandfec=1\\r\\n" -"a=maxptime:60\\r\\n" -"a=ssrc:3233846890 cname:o/i14u9pJrxRKAsu\\r\\n" -"a=ssrc:3233846890 msid:6VrfBKXrwK a0\\r\\n" -"a=ssrc:3233846890 mslabel:6VrfBKXrwK\\r\\n" -"a=ssrc:3233846890 label:6VrfBKXrwKa0\\r\\n" -"m=video 9 UDP/TLS/RTP/SAVPF 96 98 102\\r\\n" -"c=IN IP4 0.0.0.0\\r\\n" -"a=candidate:10 1 udp 2115783679 192.168.170.129 9527 typ host generation 0\\r\\n" -"a=rtcp:9 IN IP4 0.0.0.0\\r\\n" -"b=as:2000000\\r\\n" -"a=ice-ufrag:xiaozhihongjohn\\r\\n" -"a=ice-pwd:simple_rtmp_server__john\\r\\n" -"a=ice-options:trickle\\r\\n" -"a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\\r\\n" -"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\n" -"a=extmap:4 urn:3gpp:video-orientation\\r\\n" -"a=fingerprint:sha-256 76:E8:6A:6D:48:F0:86:58:30:2E:69:56:0F:C6:A1:B8:69:98:5D:73:45:93:37:8E:C4:2B:C7:97:04:18:E4:24\\r\\n" -"a=sendrecv\\r\\n" -"a=mid:1\\r\\n" -"a=rtcp-mux\\r\\n" -"a=rtpmap:96 VP8/90000\\r\\n" -"a=rtcp-fb:96 ccm fir\\r\\n" -"a=rtcp-fb:96 nack\\r\\n" -"a=rtcp-fb:96 nack pli\\r\\n" -"a=rtcp-fb:96 goog-remb\\r\\n" -"a=rtcp-fb:96 transport-cc\\r\\n" -"a=rtpmap:98 VP9/90000\\r\\n" -"a=rtcp-fb:98 ccm fir\\r\\n" -"a=rtcp-fb:98 nack\\r\\n" -"a=rtcp-fb:98 nack pli\\r\\n" -"a=rtcp-fb:98 goog-remb\\r\\n" -"a=rtcp-fb:98 transport-cc\\r\\n" -"a=rtpmap:102 H264/90000\\r\\n" -"a=rtcp-fb:102 goog-remb\\r\\n" -"a=rtcp-fb:102 transport-cc\\r\\n" -"a=rtcp-fb:102 ccm fir \\r\\n" -"a=rtcp-fb:102 nack\\r\\n" -"a=rtcp-fb:102 nack pli \\r\\n" -"a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\\r\\n" -"a=ssrc:3233846889 cname:o/i14u9pJrxRKAsu\\r\\n" -"a=ssrc:3233846889 msid:6VrfBKXrwK v0\\r\\n" -"a=ssrc:3233846889 mslabel:6VrfBKXrwK\\r\\n" -"a=ssrc:3233846889 label:6VrfBKXrwKv0\\r\\n"; - srs_error_t srs_api_response_jsonp(ISrsHttpResponseWriter* w, string callback, string data) { srs_error_t err = srs_success; @@ -849,9 +782,10 @@ srs_error_t SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa return srs_api_response(w, r, obj->dumps()); } -SrsGoApiSdp::SrsGoApiSdp(SrsServer* svr) +SrsGoApiSdp::SrsGoApiSdp(SrsServer* svr, SrsRtcServer* rtc_svr) { server = svr; + rtc_server = rtc_svr; } SrsGoApiSdp::~SrsGoApiSdp() @@ -888,15 +822,24 @@ srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest); } - string remote_sdp = remote_sdp_obj->to_str(); + string remote_sdp_str = remote_sdp_obj->to_str(); string app = app_obj->to_str(); string stream_name = stream_name_obj->to_str(); - srs_trace("remote_sdp=%s", remote_sdp.c_str()); + srs_trace("remote_sdp_str=%s", remote_sdp_str.c_str()); srs_trace("app=%s, stream=%s", app.c_str(), stream_name.c_str()); - SrsSdp srs_sdp; - err = srs_sdp.parse(remote_sdp); + SrsSdp remote_sdp; + err = remote_sdp.decode(remote_sdp_str); + if (err != srs_success) { + return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest); + } + + SrsSdp local_sdp; + rtc_server->create_rtc_session(remote_sdp, local_sdp); + + string local_sdp_str = ""; + err = local_sdp.encode(local_sdp_str); if (err != srs_success) { return srs_api_response_code(w, r, SRS_CONSTS_HTTP_BadRequest); } @@ -906,9 +849,20 @@ srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); obj->set("server", SrsJsonAny::integer(stat->server_id())); + + string candidate_str = "candidate:1 1 udp 2115783679 192.168.170.129:9527 typ host generation 0 ufrag " + + local_sdp.get_ice_ufrag() + "netwrok-cost 50"; + + SrsJsonObject* candidate_obj = SrsJsonAny::object(); + //SrsAutoFree(SrsJsonObject, candidate_obj); + + candidate_obj->set("candidate", SrsJsonAny::str(candidate_str.c_str())); + candidate_obj->set("sdpMid", SrsJsonAny::str("0")); + candidate_obj->set("sdpMLineIndex", SrsJsonAny::str("0")); if (r->is_http_post()) { - obj->set("sdp", SrsJsonAny::str(test_sdp.c_str())); + obj->set("sdp", SrsJsonAny::str(local_sdp_str.c_str())); + obj->set("candidate", candidate_obj); } else { return srs_go_http_error(w, SRS_CONSTS_HTTP_MethodNotAllowed); } diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index d77ba517b..c4cf02a6c 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -31,6 +31,7 @@ class ISrsHttpMessage; class SrsHttpParser; class SrsHttpHandler; class SrsServer; +class SrsRtcServer; #include #include @@ -168,8 +169,9 @@ class SrsGoApiSdp : public ISrsHttpHandler { private: SrsServer* server; + SrsRtcServer* rtc_server; public: - SrsGoApiSdp(SrsServer* svr); + SrsGoApiSdp(SrsServer* svr, SrsRtcServer* rtc_svr); virtual ~SrsGoApiSdp(); public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index 0cd389df5..2c1703250 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -59,6 +59,19 @@ srs_error_t ISrsUdpHandler::on_stfd_change(srs_netfd_t /*fd*/) return srs_success; } +ISrsUdpRemuxHandler::ISrsUdpRemuxHandler() +{ +} + +ISrsUdpRemuxHandler::~ISrsUdpRemuxHandler() +{ +} + +srs_error_t ISrsUdpRemuxHandler::on_stfd_change(srs_netfd_t /*fd*/) +{ + return srs_success; +} + ISrsTcpHandler::ISrsTcpHandler() { } @@ -207,12 +220,51 @@ srs_error_t SrsTcpListener::cycle() return err; } -SrsUdpRemuxListener::SrsUdpRemuxListener(ISrsUdpHandler* h, std::string i, int p) : SrsUdpListener(h, i, p) +SrsUdpRemuxListener::SrsUdpRemuxListener(ISrsUdpRemuxHandler* h, std::string i, int p) { + handler = h; + ip = i; + port = p; + lfd = NULL; + + nb_buf = SRS_UDP_MAX_PACKET_SIZE; + buf = new char[nb_buf]; + + trd = new SrsDummyCoroutine(); } SrsUdpRemuxListener::~SrsUdpRemuxListener() { + srs_freep(trd); + srs_close_stfd(lfd); + srs_freepa(buf); +} + +int SrsUdpRemuxListener::fd() +{ + return srs_netfd_fileno(lfd); +} + +srs_netfd_t SrsUdpRemuxListener::stfd() +{ + return lfd; +} + +srs_error_t SrsUdpRemuxListener::listen() +{ + srs_error_t err = srs_success; + + if ((err = srs_udp_listen(ip, port, &lfd)) != srs_success) { + return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); + } + + srs_freep(trd); + trd = new SrsSTCoroutine("udp", this); + if ((err = trd->start()) != srs_success) { + return srs_error_wrap(err, "start thread"); + } + + return err; } srs_error_t SrsUdpRemuxListener::cycle() @@ -233,9 +285,10 @@ srs_error_t SrsUdpRemuxListener::cycle() continue; } - if ((err = handler->on_udp_packet((const sockaddr*)&from, nb_from, buf, nread)) != srs_success) { + if ((err = handler->on_udp_packet(lfd, (const sockaddr*)&from, nb_from, buf, nread)) != srs_success) { //srs_error("udp handle packet error"); // remux udp never return + srs_error("udp remux error:%s", srs_error_desc(err).c_str()); continue; } diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index a667af94d..3b4d96b80 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -54,6 +54,16 @@ public: virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) = 0; }; +class ISrsUdpRemuxHandler +{ +public: + ISrsUdpRemuxHandler(); + virtual ~ISrsUdpRemuxHandler(); +public: + virtual srs_error_t on_stfd_change(srs_netfd_t fd); + virtual srs_error_t on_udp_packet(srs_netfd_t fd, const sockaddr* from, const int fromlen, char* buf, int nb_buf) = 0; +}; + // The tcp connection handler. class ISrsTcpHandler { @@ -113,11 +123,27 @@ public: virtual srs_error_t cycle(); }; -class SrsUdpRemuxListener : public SrsUdpListener +class SrsUdpRemuxListener : public ISrsCoroutineHandler { +protected: + srs_netfd_t lfd; + SrsCoroutine* trd; +protected: + char* buf; + int nb_buf; +protected: + ISrsUdpRemuxHandler* handler; + std::string ip; + int port; public: - SrsUdpRemuxListener(ISrsUdpHandler* h, std::string i, int p); + SrsUdpRemuxListener(ISrsUdpRemuxHandler* h, std::string i, int p); virtual ~SrsUdpRemuxListener(); +public: + virtual int fd(); + virtual srs_netfd_t stfd(); +public: + virtual srs_error_t listen(); +// Interface ISrsReusableThreadHandler. public: virtual srs_error_t cycle(); }; diff --git a/trunk/src/app/srs_app_rtc.cpp b/trunk/src/app/srs_app_rtc.cpp index 6f5378125..e7fbf7fc2 100644 --- a/trunk/src/app/srs_app_rtc.cpp +++ b/trunk/src/app/srs_app_rtc.cpp @@ -69,7 +69,7 @@ SrsRtc::~SrsRtc() { } -srs_error_t SrsRtc::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) +srs_error_t SrsRtc::on_udp_packet(srs_netfd_t fd, const sockaddr* from, const int fromlen, char* buf, int nb_buf) { char address_string[64]; char port_string[16]; @@ -82,5 +82,5 @@ srs_error_t SrsRtc::on_udp_packet(const sockaddr* from, const int fromlen, char* std::string peer_ip = std::string(address_string); int peer_port = atoi(port_string); - return rtc_server->on_udp_packet(peer_ip, peer_port, buf, nb_buf); + return rtc_server->on_udp_packet(fd, peer_ip, peer_port, from, fromlen, buf, nb_buf); } diff --git a/trunk/src/app/srs_app_rtc.hpp b/trunk/src/app/srs_app_rtc.hpp index 6e1fb0651..8c15bbf16 100644 --- a/trunk/src/app/srs_app_rtc.hpp +++ b/trunk/src/app/srs_app_rtc.hpp @@ -37,7 +37,7 @@ struct sockaddr; class SrsRtcServer; // The rtc over udp stream receiver -class SrsRtc : virtual public ISrsUdpHandler +class SrsRtc : virtual public ISrsUdpRemuxHandler { private: SrsRtcServer* rtc_server; @@ -46,7 +46,7 @@ public: virtual ~SrsRtc(); // Interface ISrsUdpHandler public: - virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); + virtual srs_error_t on_udp_packet(srs_netfd_t fd, const sockaddr* from, const int fromlen, char* buf, int nb_buf); }; #endif diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 93fca7f8c..2a570b4a9 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -25,24 +25,52 @@ using namespace std; +#include +#include +#include + +#include + #include -#include +#include +#include #include +#include #include +#include -static bool is_stun(const char* data, const int size) { +static bool is_stun(const char* data, const int size) +{ return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1); } -static bool is_rtp_or_rtcp(const char* data, const int size) { +static bool is_rtp_or_rtcp(const char* data, const int size) +{ return data != NULL && size > 0 && (data[0] >= 128 && data[0] <= 191); } -static bool is_dtls(const char* data, const int size) { +static bool is_dtls(const char* data, const int size) +{ return data != NULL && size > 0 && (data[0] >= 20 && data[0] <= 64); } +static string gen_random_str(int len) +{ + static string random_table = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + string ret; + ret.reserve(len); + for (int i = 0; i < len; ++i) { + ret.append(1, random_table[random() % random_table.size()]); + } + + return ret; +} + +const int SRTP_MASTER_KEY_KEY_LEN = 16; +const int SRTP_MASTER_KEY_SALT_LEN = 14; + SrsSdpMediaInfo::SrsSdpMediaInfo() { } @@ -59,16 +87,16 @@ SrsSdp::~SrsSdp() { } -srs_error_t SrsSdp::parse(const string& sdp) +srs_error_t SrsSdp::decode(const string& sdp_str) { srs_error_t err = srs_success; - if (sdp.size() < 2 || sdp[0] != 'v' || sdp[1] != '=') { - return srs_error_wrap(err, "invalid sdp"); + if (sdp_str.size() < 2 || sdp_str[0] != 'v' || sdp_str[1] != '=') { + return srs_error_wrap(err, "invalid sdp_str"); } string line; - istringstream is(sdp); + istringstream is(sdp_str); while (getline(is, line)) { srs_trace("line=%s", line.c_str()); @@ -76,7 +104,7 @@ srs_error_t SrsSdp::parse(const string& sdp) return srs_error_wrap(err, "invalid sdp line=%s", line.c_str()); } - switch (line[1]) { + switch (line[0]) { case 'v' :{ break; } @@ -94,7 +122,7 @@ srs_error_t SrsSdp::parse(const string& sdp) } case 'a' :{ if (parse_attr(line) != srs_success) { - return srs_error_wrap(err, "parse sdp line=%s failed", line.c_str()); + return srs_error_wrap(err, "decode sdp line=%s failed", line.c_str()); } break; } @@ -107,29 +135,354 @@ srs_error_t SrsSdp::parse(const string& sdp) return err; } +srs_error_t SrsSdp::encode(string& sdp_str) +{ + srs_error_t err = srs_success; + + // FIXME: + sdp_str = + "v=0\\r\\n" + "o=- 0 0 IN IP4 127.0.0.1\\r\\n" + "s=-\\r\\n" + "t=0 0\\r\\n" + "a=ice-lite\\r\\n" + "a=group:BUNDLE 0 1\\r\\n" + "a=msid-semantic: WMS 6VrfBKXrwK\\r\\n" + "m=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\n" + "c=IN IP4 0.0.0.0\\r\\n" + "a=candidate:10 1 udp 2115783679 192.168.170.129 9527 typ host generation 0\\r\\n" + "a=rtcp:9 IN IP4 0.0.0.0\\r\\n" + "a=ice-ufrag:" + ice_ufrag + "\\r\\n" + "a=ice-pwd:" + ice_pwd + "\\r\\n" + "a=ice-options:trickle\\r\\n" + "a=fingerprint:sha-256 " + SrsDtls::instance()->get_fingerprint() + "\\r\\n" + "a=sendrecv\\r\\n" + "a=mid:0\\r\\n" + "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\\r\\n" + "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\n" + "a=rtcp-mux\\r\\n" + "a=rtpmap:111 opus/48000/2\\r\\n" + "a=fmtp:111 minptime=10;useinbandfec=1\\r\\n" + "a=maxptime:60\\r\\n" + "a=ssrc:3233846890 cname:o/i14u9pJrxRKAsu\\r\\n" + "a=ssrc:3233846890 msid:6VrfBKXrwK a0\\r\\n" + "a=ssrc:3233846890 mslabel:6VrfBKXrwK\\r\\n" + "a=ssrc:3233846890 label:6VrfBKXrwKa0\\r\\n" + "m=video 9 UDP/TLS/RTP/SAVPF 96 98 102\\r\\n" + "c=IN IP4 0.0.0.0\\r\\n" + "a=candidate:10 1 udp 2115783679 192.168.170.129 9527 typ host generation 0\\r\\n" + "a=rtcp:9 IN IP4 0.0.0.0\\r\\n" + "b=as:2000000\\r\\n" + "a=ice-ufrag:" + ice_ufrag + "\\r\\n" + "a=ice-pwd:" + ice_pwd + "\\r\\n" + "a=ice-options:trickle\\r\\n" + "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\\r\\n" + "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\n" + "a=extmap:4 urn:3gpp:video-orientation\\r\\n" + "a=fingerprint:sha-256 " + SrsDtls::instance()->get_fingerprint() + "\\r\\n" + "a=sendrecv\\r\\n" + "a=mid:1\\r\\n" + "a=rtcp-mux\\r\\n" + "a=rtpmap:96 VP8/90000\\r\\n" + "a=rtcp-fb:96 ccm fir\\r\\n" + "a=rtcp-fb:96 nack\\r\\n" + "a=rtcp-fb:96 nack pli\\r\\n" + "a=rtcp-fb:96 goog-remb\\r\\n" + "a=rtcp-fb:96 transport-cc\\r\\n" + "a=rtpmap:98 VP9/90000\\r\\n" + "a=rtcp-fb:98 ccm fir\\r\\n" + "a=rtcp-fb:98 nack\\r\\n" + "a=rtcp-fb:98 nack pli\\r\\n" + "a=rtcp-fb:98 goog-remb\\r\\n" + "a=rtcp-fb:98 transport-cc\\r\\n" + "a=rtpmap:102 H264/90000\\r\\n" + "a=rtcp-fb:102 goog-remb\\r\\n" + "a=rtcp-fb:102 transport-cc\\r\\n" + "a=rtcp-fb:102 ccm fir \\r\\n" + "a=rtcp-fb:102 nack\\r\\n" + "a=rtcp-fb:102 nack pli \\r\\n" + "a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\\r\\n" + "a=ssrc:3233846889 cname:o/i14u9pJrxRKAsu\\r\\n" + "a=ssrc:3233846889 msid:6VrfBKXrwK v0\\r\\n" + "a=ssrc:3233846889 mslabel:6VrfBKXrwK\\r\\n" + "a=ssrc:3233846889 label:6VrfBKXrwKv0\\r\\n"; + + return err; +} + srs_error_t SrsSdp::parse_attr(const string& line) { srs_error_t err = srs_success; + string key = ""; + string val = ""; + string* p = &key; + for (int i = 2; i < line.size(); ++i) { + if (line[i] == ':' && p == &key) { + p = &val; + } else { + if (line[i] != '\r' && line[i] != '\n') { + p->append(1, line[i]); + } + } + } + + srs_trace("sdp attribute key=%s, val=%s", key.c_str(), val.c_str()); + + if (key == "ice-ufrag") { + ice_ufrag = val; + } else if (key == "ice-pwd") { + ice_pwd = val; + } else if (key == "fingerprint") { + + } else { + } + + return err; +} + +SrsDtlsSession::SrsDtlsSession(srs_netfd_t lfd, const sockaddr* f, int fl) +{ + dtls = NULL; + bio_in = NULL; + bio_out = NULL; + + client_key = ""; + server_key = ""; + + srtp_send = NULL; + srtp_recv = NULL; + + fd = lfd; + from = f; + fromlen = fl; + + handshake_done = false; +} + +SrsDtlsSession::~SrsDtlsSession() +{ +} + +srs_error_t SrsDtlsSession::handshake() +{ + srs_error_t err = srs_success; + + int ret = SSL_do_handshake(dtls); + + unsigned char *out_bio_data; + int out_bio_len = BIO_get_mem_data(bio_out, &out_bio_data); + + int ssl_err = SSL_get_error(dtls, ret); + switch(ssl_err) { + case SSL_ERROR_NONE: { + srs_trace("dtls handshake done"); + handshake_done = true; + srtp_init(); + srtp_sender_side_init(); + srtp_receiver_side_init(); + } + break; + + case SSL_ERROR_WANT_READ: { + break; + } + + case SSL_ERROR_WANT_WRITE: { + break; + } + + default: { + break; + } + } + + if (out_bio_len) { + srs_trace("send dtls handshake data"); + srs_sendto(fd, out_bio_data, out_bio_len, from, fromlen, 0); + } + + return err; +} + +srs_error_t SrsDtlsSession::on_dtls(const char* data, const int len) +{ + srs_error_t err = srs_success; + if (! handshake_done) { + BIO_reset(bio_in); + BIO_reset(bio_out); + BIO_write(bio_in, data, len); + + handshake(); + } else { + BIO_reset(bio_in); + BIO_reset(bio_out); + BIO_write(bio_in, data, len); + + while (BIO_ctrl_pending(bio_in) > 0) { + char dtls_read_buf[8092]; + int nb = SSL_read(dtls, dtls_read_buf, sizeof(dtls_read_buf)); + + if (nb > 0) { + on_dtls_application_data(dtls_read_buf, nb); + } + } + } + + return err; +} + +srs_error_t SrsDtlsSession::on_dtls_application_data(const char* buf, const int nb_buf) +{ + srs_error_t err = srs_success; + + return err; +} + + +void SrsDtlsSession::send_client_hello() +{ + if (dtls == NULL) { + srs_trace("send client hello"); + + dtls = SSL_new(SrsDtls::instance()->get_dtls_ctx()); + SSL_set_connect_state(dtls); + + bio_in = BIO_new(BIO_s_mem()); + bio_out = BIO_new(BIO_s_mem()); + + SSL_set_bio(dtls, bio_in, bio_out); + + handshake(); + } +} + +srs_error_t SrsDtlsSession::srtp_init() +{ + srs_error_t err = srs_success; + + unsigned char material[SRTP_MASTER_KEY_LEN * 2] = {0}; // client(SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN) + server + char dtls_srtp_lable[] = "EXTRACTOR-dtls_srtp"; + if (! SSL_export_keying_material(dtls, material, sizeof(material), dtls_srtp_lable, strlen(dtls_srtp_lable), NULL, 0, 0)) { + return srs_error_wrap(err, "SSL_export_keying_material failed"); + } + + size_t offset = 0; + + std::string sClientMasterKey(reinterpret_cast(material), SRTP_MASTER_KEY_KEY_LEN); + offset += SRTP_MASTER_KEY_KEY_LEN; + std::string sServerMasterKey(reinterpret_cast(material + offset), SRTP_MASTER_KEY_KEY_LEN); + offset += SRTP_MASTER_KEY_KEY_LEN; + std::string sClientMasterSalt(reinterpret_cast(material + offset), SRTP_MASTER_KEY_SALT_LEN); + offset += SRTP_MASTER_KEY_SALT_LEN; + std::string sServerMasterSalt(reinterpret_cast(material + offset), SRTP_MASTER_KEY_SALT_LEN); + + client_key = sClientMasterKey + sClientMasterSalt; + server_key = sServerMasterKey + sServerMasterSalt; + + srtp_sender_side_init(); + srtp_receiver_side_init(); +} + +srs_error_t SrsDtlsSession::srtp_sender_side_init() +{ + srs_error_t err = srs_success; + + srtp_policy_t policy; + bzero(&policy, sizeof(policy)); + + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); + + policy.ssrc.type = ssrc_any_outbound; + + policy.ssrc.value = 0; + policy.window_size = 8192; // seq 鐩稿樊8192璁や负鏃犳晥 + policy.allow_repeat_tx = 1; + policy.next = NULL; + + uint8_t *key = new uint8_t[client_key.size()]; + memcpy(key, client_key.data(), client_key.size()); + policy.key = key; + + if (srtp_create(&srtp_send, &policy) != 0) { + return srs_error_wrap(err, "srtp_create failed"); + } + + delete [] key; + + return err; +} + +srs_error_t SrsDtlsSession::srtp_receiver_side_init() +{ + srs_error_t err = srs_success; + + srtp_policy_t policy; + bzero(&policy, sizeof(policy)); + + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); + + policy.ssrc.type = ssrc_any_inbound; + + policy.ssrc.value = 0; + policy.window_size = 8192; // seq 鐩稿樊8192璁や负鏃犳晥 + policy.allow_repeat_tx = 1; + policy.next = NULL; + + uint8_t *key = new uint8_t[server_key.size()]; + memcpy(key, server_key.data(), server_key.size()); + policy.key = key; + + if (srtp_create(&srtp_recv, &policy) != 0) { + return srs_error_wrap(err, "srtp_create failed"); + } + + delete [] key; + return err; } SrsRtcSession::SrsRtcSession() { session_state = INIT; + dtls_session = NULL; } SrsRtcSession::~SrsRtcSession() { } -srs_error_t SrsRtcSession::on_stun(const SrsStunPacket& stun_packet) +srs_error_t SrsRtcSession::on_binding_request(const SrsStunPacket& stun_packet, const string& peer_ip, const uint16_t peer_port, + SrsStunPacket& stun_binding_response) { srs_error_t err = srs_success; + stun_binding_response.set_message_type(BindingResponse); + stun_binding_response.set_local_ufrag(stun_packet.get_remote_ufrag()); + stun_binding_response.set_remote_ufrag(stun_packet.get_local_ufrag()); + stun_binding_response.set_transcation_id(stun_packet.get_transcation_id()); + stun_binding_response.set_mapped_address(be32toh(inet_addr(peer_ip.c_str()))); + stun_binding_response.set_mapped_port(peer_port); + return err; } +srs_error_t SrsRtcSession::send_client_hello(srs_netfd_t fd, const sockaddr* from, int fromlen) +{ + if (dtls_session == NULL) { + dtls_session = new SrsDtlsSession(fd, from, fromlen); + } + + dtls_session->send_client_hello(); +} + +srs_error_t SrsRtcSession::on_dtls(const char* buf, const int nb_buf) +{ + dtls_session->on_dtls(buf, nb_buf); +} + srs_error_t SrsRtcSession::send_packet() { } @@ -150,59 +503,147 @@ srs_error_t SrsRtcServer::initialize() return err; } -srs_error_t SrsRtcServer::on_udp_packet(const string& peer_ip, const int peer_port, const char* data, const int size) +srs_error_t SrsRtcServer::on_udp_packet(srs_netfd_t fd, const string& peer_ip, const int peer_port, + const sockaddr* from, const int fromlen, const char* data, const int size) { srs_error_t err = srs_success; if (is_stun(data, size)) { - return on_stun(peer_ip, peer_port, data, size); + return on_stun(fd, peer_ip, peer_port, from, fromlen, data, size); } else if (is_dtls(data, size)) { - return on_dtls(peer_ip, peer_port, data, size); + srs_trace("dtls"); + return on_dtls(fd, peer_ip, peer_port, from, fromlen, data, size); } else if (is_rtp_or_rtcp(data, size)) { - return on_rtp_or_rtcp(peer_ip, peer_port, data, size); + return on_rtp_or_rtcp(fd, peer_ip, peer_port, from, fromlen, data, size); } return srs_error_wrap(err, "unknown packet type"); } -srs_error_t SrsRtcServer::on_stun(const string& peer_ip, const int peer_port, const char* data, const int size) +SrsRtcSession* SrsRtcServer::create_rtc_session(const SrsSdp& remote_sdp, SrsSdp& local_sdp) +{ + SrsRtcSession* session = new SrsRtcSession(); + + std::string local_ufrag = gen_random_str(8); + std::string local_pwd = gen_random_str(32); + + while (true) { + bool ret = map_ufrag_sessions.insert(make_pair(remote_sdp.get_ice_ufrag(), session)).second; + if (ret) { + break; + } + } + + local_sdp.set_ice_ufrag(local_ufrag); + local_sdp.set_ice_pwd(local_pwd); + + session->set_remote_sdp(remote_sdp); + session->set_local_sdp(local_sdp); + + session->set_session_state(WAITING_STUN); + + return session; +} + +SrsRtcSession* SrsRtcServer::find_rtc_session_by_ip_port(const string& peer_ip, const uint16_t peer_port) +{ + ostringstream os; + os << peer_ip << ":" << peer_port; + string key = os.str(); + map::iterator iter = map_ip_port_sessions.find(key); + if (iter == map_ip_port_sessions.end()) { + return NULL; + } + + return iter->second; +} + +srs_error_t SrsRtcServer::on_stun(srs_netfd_t fd, const string& peer_ip, const int peer_port, + const sockaddr* from, const int fromlen, const char* data, const int size) { srs_error_t err = srs_success; srs_trace("peer %s:%d stun", peer_ip.c_str(), peer_port); - SrsStunPacket stun_packet; - if (stun_packet.decode(data, size) != srs_success) { + SrsStunPacket stun_req; + if (stun_req.decode(data, size) != srs_success) { return srs_error_wrap(err, "decode stun failed"); } - std::string peer_ufrag = stun_packet.ufrag(); - SrsRtcSession* rtc_session = find_rtc_session(peer_ufrag); + std::string remote_ufrag = stun_req.get_remote_ufrag(); + SrsRtcSession* rtc_session = find_rtc_session_by_ufrag(remote_ufrag); if (rtc_session == NULL) { - return srs_error_wrap(err, "can not find rtc_session, ufrag=%s", peer_ufrag.c_str()); + return srs_error_wrap(err, "can not find rtc_session, ufrag=%s", remote_ufrag.c_str()); + } + + SrsStunPacket stun_rsp; + char buf[1460]; + SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); + SrsAutoFree(SrsBuffer, stream); + + if (stun_req.is_binding_request()) { + if (rtc_session->on_binding_request(stun_req, peer_ip, peer_port, stun_rsp) != srs_success) { + return srs_error_wrap(err, "stun binding request failed"); + } + } + + if (stun_rsp.encode(rtc_session->get_local_sdp()->get_ice_pwd(), stream) != srs_success) { + return srs_error_wrap(err, "stun rsp encode failed"); } - return rtc_session->on_stun(stun_packet); + srs_sendto(fd, stream->data(), stream->pos(), from, fromlen, 0); + + if (rtc_session->get_session_state() == WAITING_STUN) { + rtc_session->set_session_state(DOING_DTLS_HANDSHAKE); + rtc_session->send_client_hello(fd, from, fromlen); + + insert_into_ip_port_sessions(peer_ip, peer_port, rtc_session); + } + + return err; } -srs_error_t SrsRtcServer::on_dtls(const string& peer_ip, const int peer_port, const char* data, const int size) +srs_error_t SrsRtcServer::on_dtls(srs_netfd_t fd, const string& peer_ip, const int peer_port, + const sockaddr* from, const int fromlen, const char* data, const int size) { srs_error_t err = srs_success; + srs_trace("on dtls"); + + // FIXME + SrsRtcSession* rtc_session = find_rtc_session_by_ip_port(peer_ip, peer_port); + + if (rtc_session == NULL) { + return srs_error_wrap(err, "can not find rtc session by ip=%s, port=%u", peer_ip.c_str(), peer_port); + } + + rtc_session->on_dtls(data, size); + return err; } -srs_error_t SrsRtcServer::on_rtp_or_rtcp(const string& peer_ip, const int peer_port, const char* data, const int size) +srs_error_t SrsRtcServer::on_rtp_or_rtcp(srs_netfd_t fd, const string& peer_ip, const int peer_port, + const sockaddr* from, const int fromlen, const char* data, const int size) { srs_error_t err = srs_success; + srs_trace("on rtp/rtcp"); return err; } -SrsRtcSession* SrsRtcServer::find_rtc_session(const std::string& ufrag) +SrsRtcSession* SrsRtcServer::find_rtc_session_by_ufrag(const std::string& ufrag) { - map::iterator iter = map_sessions.find(ufrag); - if (iter == map_sessions.end()) { + map::iterator iter = map_ufrag_sessions.find(ufrag); + if (iter == map_ufrag_sessions.end()) { return NULL; } return iter->second; } + +bool SrsRtcServer::insert_into_ip_port_sessions(const string& peer_ip, const uint16_t peer_port, SrsRtcSession* rtc_session) +{ + ostringstream os; + os << peer_ip << ":" << peer_port; + string key = os.str(); + + return map_ip_port_sessions.insert(make_pair(key, rtc_session)).second; +} diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index df370d585..c40076686 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -25,11 +25,15 @@ #define SRS_APP_RTC_CONN_HPP #include +#include #include #include #include +#include +#include + class SrsServer; class SrsStunPacket; @@ -55,7 +59,14 @@ public: SrsSdp(); virtual ~SrsSdp(); - srs_error_t parse(const std::string& sdp); + srs_error_t decode(const std::string& sdp_str); + srs_error_t encode(std::string& sdp_str); + + std::string get_ice_ufrag() const { return ice_ufrag; } + std::string get_ice_pwd() const { return ice_pwd; } + + void set_ice_ufrag(const std::string& u) { ice_ufrag = u; } + void set_ice_pwd(const std::string& p) { ice_pwd = p; } private: srs_error_t parse_attr(const std::string& line); }; @@ -69,19 +80,63 @@ enum SrsRtcSessionStateType CLOSED = 4, }; -class SrsRtcSession +class SrsDtlsSession { +private: + SSL* dtls; + BIO* bio_in; + BIO* bio_out; + + std::string client_key; + std::string server_key; + + srtp_t srtp_send; + srtp_t srtp_recv; + + srs_netfd_t fd; + const sockaddr* from; + int fromlen; + + bool handshake_done; + public: + SrsDtlsSession(srs_netfd_t lfd, const sockaddr* f, int fl); + virtual ~SrsDtlsSession(); + + srs_error_t on_dtls(const char* data, const int len); + srs_error_t on_dtls_application_data(const char* data, const int len); + + void send_client_hello(); + srs_error_t handshake(); + srs_error_t srtp_init(); + srs_error_t srtp_sender_side_init(); + srs_error_t srtp_receiver_side_init(); +}; + +class SrsRtcSession +{ private: - SrsSdp peer_sdp; - SrsSdp offer_sdp; + SrsSdp remote_sdp; + SrsSdp local_sdp; SrsRtcSessionStateType session_state; + SrsDtlsSession* dtls_session; public: SrsRtcSession(); virtual ~SrsRtcSession(); + SrsSdp* get_local_sdp() { return &local_sdp; } + SrsSdp* get_remote_sdp() { return &remote_sdp; } + SrsRtcSessionStateType get_session_state() { return session_state; } + + void set_local_sdp(const SrsSdp& sdp) { local_sdp = sdp; } + void set_remote_sdp(const SrsSdp& sdp) { remote_sdp = sdp; } + void set_session_state(SrsRtcSessionStateType state) { session_state = state; } + srs_error_t on_udp_packet(const std::string& peer_ip, const int peer_port, const char* data, const int size); - srs_error_t on_stun(const SrsStunPacket& stun_packet); + srs_error_t on_binding_request(const SrsStunPacket& stun_packet, const std::string& peer_ip, const uint16_t peer_port, + SrsStunPacket& stun_binding_response); + srs_error_t on_dtls(const char* buf, const int nb_buf); + srs_error_t send_client_hello(srs_netfd_t fd, const sockaddr* from, int fromlen); srs_error_t send_packet(); }; @@ -89,19 +144,26 @@ class SrsRtcServer { private: SrsServer* server; - std::map map_sessions; + std::map map_ufrag_sessions; + std::map map_ip_port_sessions; public: SrsRtcServer(SrsServer* svr); virtual ~SrsRtcServer(); public: virtual srs_error_t initialize(); - virtual srs_error_t on_udp_packet(const std::string& peer_ip, const int peer_port, const char* data, const int size); + virtual srs_error_t on_udp_packet(srs_netfd_t fd, const std::string& peer_ip, const int peer_port, + const sockaddr* from, const int fromlen, const char* data, const int size); + + SrsRtcSession* create_rtc_session(const SrsSdp& remote_sdp, SrsSdp& local_sdp); + bool insert_into_ip_port_sessions(const std::string& peer_ip, const uint16_t peer_port, SrsRtcSession* rtc_session); private: - srs_error_t on_stun(const std::string& peer_ip, const int peer_port, const char* data, const int size); - srs_error_t on_dtls(const std::string& peer_ip, const int peer_port, const char* data, const int size); - srs_error_t on_rtp_or_rtcp(const std::string& peer_ip, const int peer_port, const char* data, const int size); + srs_error_t on_stun(srs_netfd_t fd, const std::string& peer_ip, const int peer_port, const sockaddr* from, const int fromlen, const char* data, const int size); + srs_error_t on_dtls(srs_netfd_t fd, const std::string& peer_ip, const int peer_port, const sockaddr* from, const int fromlen, const char* data, const int size); + srs_error_t on_rtp_or_rtcp(srs_netfd_t fd, const std::string& peer_ip, const int peer_port, + const sockaddr* from, const int fromlen, const char* data, const int size); private: - SrsRtcSession* find_rtc_session(const std::string& ufrag); + SrsRtcSession* find_rtc_session_by_ufrag(const std::string& ufrag); + SrsRtcSession* find_rtc_session_by_ip_port(const std::string& peer_ip, const uint16_t peer_port); }; #endif diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 548ee3cdd..cdd939408 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -838,7 +838,7 @@ srs_error_t SrsServer::http_handle() if ((err = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != srs_success) { return srs_error_wrap(err, "handle streams"); } - if ((err = http_api_mux->handle("/api/v1/sdp/", new SrsGoApiSdp(this))) != srs_success) { + if ((err = http_api_mux->handle("/api/v1/sdp/", new SrsGoApiSdp(this, rtc_server))) != srs_success) { return srs_error_wrap(err, "handle sdp"); } if ((err = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != srs_success) { diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 36e4c8b3f..8fa17ff9d 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -162,8 +162,8 @@ public: class SrsRtcListener : public SrsListener { protected: - SrsUdpListener* listener; - ISrsUdpHandler* rtc; + SrsUdpRemuxListener* listener; + ISrsUdpRemuxHandler* rtc; public: SrsRtcListener(SrsServer* svr, SrsRtcServer* rtc_svr, SrsListenerType t); virtual ~SrsRtcListener(); diff --git a/trunk/src/protocol/srs_stun_stack.cpp b/trunk/src/protocol/srs_stun_stack.cpp index 38c8c00a2..50284c785 100644 --- a/trunk/src/protocol/srs_stun_stack.cpp +++ b/trunk/src/protocol/srs_stun_stack.cpp @@ -2,27 +2,271 @@ using namespace std; +#include +#include +#include +#include + +#include +#include +#include +#include + +static string dump_string_hex(const std::string& str, const int& max_len = 128) +{ + char buf[1024*16]; + int len = 0; + + for (int i = 0; i < str.size() && i < max_len; ++i) { + int nb = snprintf(buf + len, sizeof(buf) - len - 1, "%02X ", (uint8_t)str[i]); + if (nb <= 0) + break; + + len += nb; + } + buf[len] = '\0'; + + return string(buf, len); +} + +static srs_error_t hmac_encode(const std::string& algo, const char* key, const int& key_length, + const char* input, const int input_length, char* output, unsigned int& output_length) +{ + srs_error_t err = srs_success; + + const EVP_MD* engine = NULL; + if (algo == "sha512") { + engine = EVP_sha512(); + } else if(algo == "sha256") { + engine = EVP_sha256(); + } else if(algo == "sha1") { + engine = EVP_sha1(); + } else if(algo == "md5") { + engine = EVP_md5(); + } else if(algo == "sha224") { + engine = EVP_sha224(); + } else if(algo == "sha384") { + engine = EVP_sha384(); + } else { + return srs_error_wrap(err, "unknown algo=%s", algo.c_str()); + } + + HMAC_CTX* ctx = HMAC_CTX_new(); + if (HMAC_Init_ex(ctx, key, key_length, engine, NULL) < 0) { + HMAC_CTX_free(ctx); + return srs_error_wrap(err, "hmac init faied"); + } + + if (HMAC_Update(ctx, (const unsigned char*)input, input_length) < 0) { + HMAC_CTX_free(ctx); + return srs_error_wrap(err, "hmac update faied"); + } + + if (HMAC_Final(ctx, (unsigned char*)output, &output_length) < 0) { + HMAC_CTX_free(ctx); + return srs_error_wrap(err, "hmac final faied"); + } + + HMAC_CTX_free(ctx); + + return err; +} + + SrsStunPacket::SrsStunPacket() { + message_type = 0; + local_ufrag = ""; + remote_ufrag = ""; } SrsStunPacket::~SrsStunPacket() { } -string SrsStunPacket::ufrag() +srs_error_t SrsStunPacket::decode(const char* buf, const int nb_buf) { - return ""; + srs_error_t err = srs_success; + + SrsBuffer* stream = new SrsBuffer(const_cast(buf), nb_buf); + SrsAutoFree(SrsBuffer, stream); + + if (stream->left() < 20) { + return srs_error_wrap(err, "invalid stun packet, size=%d", stream->size()); + } + + srs_trace("stun packet, nb_buf=%d", nb_buf); + + message_type = stream->read_2bytes(); + uint16_t message_len = stream->read_2bytes(); + string magic_cookie = stream->read_string(4); + transcation_id = stream->read_string(12); + + srs_trace("message_type=%u, message_len=%u, magic_cookie=%s, transcation_id=%s", + message_type, message_len, magic_cookie.c_str(), transcation_id.c_str()); + + if (nb_buf != 20 + message_len) { + return srs_error_wrap(err, "invalid stun packet, message_len=%d, nb_buf=%d", message_len, nb_buf); + } + + while (stream->left() >= 4) { + uint16_t type = stream->read_2bytes(); + uint16_t len = stream->read_2bytes(); + + srs_trace("type=%u, len=%u", type, len); + + if (stream->left() < len) { + return srs_error_wrap(err, "invalid stun packet"); + } + + string val = stream->read_string(len); + // padding + if (len % 4 != 0) { + stream->read_string(4 - (len % 4)); + } + //srs_trace("val=%s", val.c_str()); + + switch (type) { + // FIXME: enum + case 6: { + size_t p = val.find(":"); + if (p != string::npos) { + local_ufrag = val.substr(0, p); + remote_ufrag = val.substr(p + 1); + srs_trace("stun packet local_ufrag=%s, remote_ufrag=%s", local_ufrag.c_str(), remote_ufrag.c_str()); + } + break; + } + + default: { + break; + } + } + } + + return err; } -string SrsStunPacket::pwd() +srs_error_t SrsStunPacket::encode(const string& pwd, SrsBuffer* stream) { - return ""; + srs_error_t err = srs_success; + if (is_binding_response()) { + return encode_binding_response(pwd, stream); + } + + return srs_error_wrap(err, "unknown stun type=%d", get_message_type()); } -srs_error_t SrsStunPacket::decode(const char* buf, const int nb_buf) +// FIXME: make this function easy to read +srs_error_t SrsStunPacket::encode_binding_response(const string& pwd, SrsBuffer* stream) { srs_error_t err = srs_success; + string property_username = encode_username(); + string mapped_address = encode_mapped_address(); + + stream->write_2bytes(BindingResponse); + stream->write_2bytes(property_username.size() + mapped_address.size()); + stream->write_4bytes(0x2112A442); + stream->write_string(transcation_id); + stream->write_string(property_username); + stream->write_string(mapped_address); + + stream->data()[2] = ((stream->pos() - 20 + 20 + 4) & 0x0000FF00) >> 8; + stream->data()[3] = ((stream->pos() - 20 + 20 + 4) & 0x000000FF); + + char hmac_buf[20] = {0}; + unsigned int hmac_buf_len = 0; + if (hmac_encode("sha1", pwd.c_str(), pwd.size(), stream->data(), stream->pos(), hmac_buf, hmac_buf_len) != srs_success) { + return srs_error_wrap(err, "hmac encode failed"); + } + + string hmac = encode_hmac(hmac_buf, hmac_buf_len); + + stream->write_string(hmac); + stream->data()[2] = ((stream->pos() - 20 + 8) & 0x0000FF00) >> 8; + stream->data()[3] = ((stream->pos() - 20 + 8) & 0x000000FF); + + uint32_t crc32 = srs_crc32_ieee(stream->data(), stream->pos(), 0) ^ 0x5354554E; + + string fingerprint = encode_fingerprint(crc32); + + stream->write_string(fingerprint); + + stream->data()[2] = ((stream->pos() - 20) & 0x0000FF00) >> 8; + stream->data()[3] = ((stream->pos() - 20) & 0x000000FF); + return err; } + +string SrsStunPacket::encode_username() +{ + char buf[1460]; + SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); + SrsAutoFree(SrsBuffer, stream); + + string username = remote_ufrag + ":" + local_ufrag; + + stream->write_2bytes(Username); + stream->write_2bytes(username.size()); + stream->write_string(username); + + if (stream->pos() % 4 != 0) { + static char padding[4] = {0}; + stream->write_bytes(padding, 4 - (stream->pos() % 4)); + } + + return string(stream->data(), stream->pos()); +} + +string SrsStunPacket::encode_mapped_address() +{ + char buf[1460]; + SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); + SrsAutoFree(SrsBuffer, stream); + + uint32_t magic_cookie = 0x2112A442; +#if 1 + stream->write_2bytes(XorMappedAddress); + stream->write_2bytes(8); + stream->write_1bytes(0); // ignore this bytes + stream->write_1bytes(1); // ipv4 family + stream->write_2bytes(mapped_port ^ (magic_cookie >> 16)); + stream->write_4bytes(mapped_address ^ magic_cookie); +#else + stream->write_2bytes(MappedAddress); + stream->write_2bytes(8); + stream->write_1bytes(0); // ignore this bytes + stream->write_1bytes(1); // ipv4 family + stream->write_2bytes(mapped_port); + stream->write_4bytes(mapped_address); +#endif + + return string(stream->data(), stream->pos()); +} + +string SrsStunPacket::encode_hmac(char* hmac_buf, const int hmac_buf_len) +{ + char buf[1460]; + SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); + SrsAutoFree(SrsBuffer, stream); + + stream->write_2bytes(MessageIntegrity); + stream->write_2bytes(hmac_buf_len); + stream->write_bytes(hmac_buf, hmac_buf_len); + + return string(stream->data(), stream->pos()); +} + +string SrsStunPacket::encode_fingerprint(uint32_t crc32) +{ + char buf[1460]; + SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); + SrsAutoFree(SrsBuffer, stream); + + stream->write_2bytes(Fingerprint); + stream->write_2bytes(4); + stream->write_4bytes(crc32); + + return string(stream->data(), stream->pos()); +} diff --git a/trunk/src/protocol/srs_stun_stack.hpp b/trunk/src/protocol/srs_stun_stack.hpp index 61269050a..80bba8a15 100644 --- a/trunk/src/protocol/srs_stun_stack.hpp +++ b/trunk/src/protocol/srs_stun_stack.hpp @@ -29,16 +29,81 @@ #include #include +class SrsBuffer; + +enum SrsStunMessageType +{ + // see @ https://tools.ietf.org/html/rfc3489#section-11.1 + BindingRequest = 0x0001, + BindingResponse = 0x0101, + BindingErrorResponse = 0x0111, + SharedSecretRequest = 0x0002, + SharedSecretResponse = 0x0102, + SharedSecretErrorResponse = 0x0112, +}; + +enum SrsStunMessageAttribute +{ + // see @ https://tools.ietf.org/html/rfc3489#section-11.2 + MappedAddress = 0x0001, + ResponseAddress = 0x0002, + ChangeRequest = 0x0003, + SourceAddress = 0x0004, + ChangedAddress = 0x0005, + Username = 0x0006, + Password = 0x0007, + MessageIntegrity = 0x0008, + ErrorCode = 0x0009, + UnknownAttributes = 0x000A, + ReflectedFrom = 0x000B, + + // see @ https://tools.ietf.org/html/rfc5389#section-18.2 + Realm = 0x0014, + Nonce = 0x0015, + XorMappedAddress = 0x0020, + Software = 0x8022, + AlternateServer = 0x8023, + Fingerprint = 0x8028, +}; + class SrsStunPacket { +private: + uint16_t message_type; + std::string local_ufrag; + std::string remote_ufrag; + std::string transcation_id; + uint32_t mapped_address; + uint16_t mapped_port; public: SrsStunPacket(); virtual ~SrsStunPacket(); - std::string ufrag(); - std::string pwd(); + bool is_binding_request() const { return message_type == BindingRequest; } + bool is_binding_response() const { return message_type == BindingResponse; } + + uint16_t get_message_type() const { return message_type; } + std::string get_local_ufrag() const { return local_ufrag; } + std::string get_remote_ufrag() const { return remote_ufrag; } + std::string get_transcation_id() const { return transcation_id; } + uint32_t get_mapped_address() const { return mapped_address; } + uint16_t get_mapped_port() const { return mapped_port; } + + void set_message_type(const uint16_t& m) { message_type = m; } + void set_local_ufrag(const std::string& u) { local_ufrag = u; } + void set_remote_ufrag(const std::string& u) { remote_ufrag = u; } + void set_transcation_id(const std::string& t) { transcation_id = t; } + void set_mapped_address(const uint32_t& addr) { mapped_address = addr; } + void set_mapped_port(const uint32_t& port) { mapped_port = port; } srs_error_t decode(const char* buf, const int nb_buf); + srs_error_t encode(const std::string& pwd, SrsBuffer* stream); +private: + srs_error_t encode_binding_response(const std::string& pwd, SrsBuffer* stream); + std::string encode_username(); + std::string encode_mapped_address(); + std::string encode_hmac(char* hamc_buf, const int hmac_buf_len); + std::string encode_fingerprint(uint32_t crc32); }; #endif diff --git a/trunk/src/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp index f63cd4279..555ff5a33 100644 --- a/trunk/src/service/srs_service_st.cpp +++ b/trunk/src/service/srs_service_st.cpp @@ -397,6 +397,11 @@ int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, in return st_recvfrom((st_netfd_t)stfd, buf, len, from, fromlen, (st_utime_t)timeout); } +int srs_sendto(srs_netfd_t stfd, void *buf, int len, const struct sockaddr * to, int tolen, srs_utime_t timeout) +{ + return st_sendto((st_netfd_t)stfd, buf, len, to, tolen, (st_utime_t)timeout); +} + srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout) { return (srs_netfd_t)st_accept((st_netfd_t)stfd, addr, addrlen, (st_utime_t)timeout); diff --git a/trunk/src/service/srs_service_st.hpp b/trunk/src/service/srs_service_st.hpp index 510b9ba8a..4894e7049 100644 --- a/trunk/src/service/srs_service_st.hpp +++ b/trunk/src/service/srs_service_st.hpp @@ -88,6 +88,7 @@ extern srs_netfd_t srs_netfd_open_socket(int osfd); extern srs_netfd_t srs_netfd_open(int osfd); extern int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, int *fromlen, srs_utime_t timeout); +extern int srs_sendto(srs_netfd_t stfd, void *buf, int len, const struct sockaddr *to, int tolen, srs_utime_t timeout); extern srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout); From 936ba7583eefb0d54b0af54cfe9cc7f9eac27ecf Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Fri, 6 Mar 2020 23:40:20 +0800 Subject: [PATCH 08/21] modify depend.sh --- trunk/3rdparty/libsrtp-2.0.0.zip | Bin 178 -> 359773 bytes trunk/auto/depends.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/3rdparty/libsrtp-2.0.0.zip b/trunk/3rdparty/libsrtp-2.0.0.zip index c149013ac25bd5e7e3d4714a6cdb3fa3035bf4e7..e0f7c414dc96b67f0c362936ee76038a5fd86bee 100644 GIT binary patch literal 359773 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[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%0D=)g$VMe zeY1->C3xsm#sWcY*Zt?NlSpBH!=()2Y+$}Ri>)7?y&lGXgxNtuXfBS29(&fHH$AU$ zqMj{O+VAC{$6iR>NUK#@YKUv}l*iiRzq6OK1D>Nm8cUnPsSrR<`&(H*Wi9(zx@H$z zQ6E$`o6oK+y15#BcR%|t4@BGhE7LYwuG#B1KQOE++$-WngPD{M343rhUkrN5h94N# zs}!sn==;DCK>B*G)ck8{y`nL*qfIAb1mHD;!@29LUyI`2#)LK1^d!6w3NT?|SIe@i=v$H}N~d$Gu_qIHMXzGr?c)-R^1DhchT;C03HxGyBJtpeuX%r?tW98g$;HWa)Yz(+LYf{c=oZJBDe z(aZBe@st}JH_K(CwychUq%kT336(F6@`VGp(`#Iwx3gYq#gkLnn!+Mc;BVgEzl&wK z*#c5AJB+JJ6(zp0UR__0Yt&{)wt?+q_B62#U#xO=xk?v>Sg7tFSV-OhT$R_m>9rO1 zsUCz5*;}>G3tAGa5H4>T%Uoj=2iGT<9h>tA8}lOLtN}QVTiVD`LbPhMw{3qKqWC-td1K{NIaf-I-72vnD0Vhekvl_lH$V5 z*!)A>lIJnc(&N9hC~T74w+dm=?6;U;wC&muOfrxLUkO%-or0EKtDMhISP$KDIoq1* z>p=fVyc@-5S=}{me_dA1ZxjD*HrLQ}YNa)#!Beae?_{=P*2J}1?mC$V7ay4Cuy%YA zV+^_CqBd_PI}SM^oTW3NgE$9IO2cvffCJAS9IbH*7=*7$yDegAD7K07kioZs->l=E z)t%8P@#>kOI57eR=rx>*;l*r~d6eTKR!|h3VCx>;~UdW9~Z5 zrd&@TdgCx*ROK$JqP8jp%q50>;Ol>T%>OfB4S(ry8}nlkD^mP79N~XGa<2d7l^asG zuvr(!{L0p0C`oe1CunIt!_9hd6bj8y6~~7v0T?KaiAykY67V~WFbkA_1c|eoq zEs0|(QA8eh?dTl4`ovr|P-FUVK;DLRgHZ2B5WDKi#e(nuwr>u<$Jp1g&lcv^SBsT) zi=Z*els>H&MYNk?LJc=czn|1?gSgrcZHifya!tEM!Ew^ZfMj)z<;a;pyK z7tFQxl}$2p<>qK8`{;%|!5uL&!Hr4* zH4lO9fLbCMZiH&@#2CGR@1ctnhoDSSJi7R;pN5p&7P=>f4Hj51* zIl)uNTF*0T3>0o`0RSTb%J{&b?1_~E$1hmr%NNG|Is|XSE9URLRnK!@&kgPBD;f8L zYKhBx*|m-~%$m~mTmLznYKMp4!d2~c&JK*nCt#l2GA_VNn)~vVxBhlci`NgPv5WWK z#=6F97ue6cgX^ISuyNs1b4TlCYXTJ*cKH(K*I1L!KAfGyss+XT&sxrN_D|X8G)i~# z%%_FVi&&iJHCzY2i0F2JVa8VTyFibF%hpxHM#qh+ZsK?S2Im@vP2l+-Mb-6k*ZOEdG>=l*MtEWL={&+6Ad@_!O`AKHh&TC_(> z;I?yc2hE@PnxNPHo@2N)`vsimy3KUl#UqD{!zthV(l}kbu7g`cJfvD`1} zS32;Me@*nB``mDC??!4pgNb{%0M~$W4m1I4z54(TL1g1RXUCim@)o^D%i}i_HSQVrKBCle!v+<+6VRQD^ zSG%}f>bP%3i^zBco)qxiAmd$j^W}1NlYZ5~jJt??-l5(U^Krs%$v+u=c`SFo05GEk z4sV7iKzr^BhzDd*82}(*l@ibFiw%-plz;YN@h3lj9iEuO#f@Z-n59z3Y3vZjlifa; zK3QLL0I_Am$GSa9F7>T7@F>JP_CD>ntXA}7Wv@SM3IBn1lO%z|i`}}D4B{k-Mn}1W zilA@;u|x>WajU*hIQZ7SV_pnn(wZLe4JJyX6wlQ>Vklx4ATjo-P5I{b3W#~W@V#oD zOhBo}7$KM!Wk*6(>pwt&rQ~=Y-Zcv;p?Q<}xMEBbU zAB)!r_XU#T5RXQ`C!4;99{rudm}8H?J?G}p1*Iz_4A&4ML|d#@n@Z3nhbu&;=jyx1 zBz=k-=J?SoIIc#t{1AC}!aI&nA%=LUD@H75nO^KtTTHl6=h`jms>Umu)Jk1R+Th9X z7B51ystrnp&1{hL_Gjg_^4XEero-&w+JPqlUJ8$hS z%uO2nz$cTfX*GKyf$AT@Q^at>x5yJzFY;c*@@EqZ5z?y6SVBW1_LnT6MI#OvMUvF{ z$@oEW1TlA*Z}ZtMP-<2lLKF!ySK(cl4+4h-22X+!X8hEzxIcM>cFbD+-BDbDd;0Chiuen1^4nk(bm!t&dJl=rSga39Hyg?hwRHpS7NfZX#|nk{-y>Svo~M*^tZg2S z9^3e^w;Ds*-*!3XU%6i> zMwl=@qBnC7RGf;D+!A>j9k615mys+oc!ClG`xFP!h)f((ba1 zRhJdPj1SGdZV~Y@DYQ_!v5$htUygTX9LNb4 zv}E43r3B_QjQ`znf2>$JKcI;n{rXoiwDh4(Dsbuzc{)*oP0_FU7c!)hXn1E>IgQkv zV7Igr{?)l+ER<4(6Urj5cy|?ExOmxOHgpRs=)|M5Qf1fxjhWTtJ&MqhciQTc6)cgg zqC7ExE9j5KiLQn!p~OdE&n-m&tLZP}7Hr(K^63Q81DH`~PN(RsQ@rHSbAmq50^^|n zQc_($ZTUVt?-Wz{=tGJ)ny1s%=*=)L2w`H4>|H6HadD~P{8l{nC6E8e2eUs1wztLQ z@B32#S7HN6xEmL`x?qgbhzRv~vG!9S#IWd0SWFPU>aNZp-hmY^SHf3_4lGxLE@haZ z&urSIPkF%+J_oB*k?O9GnJ){?9~+ZgXWv``M^-6U{zTId&3-J0T`2WbH4D0x7@BeS z)FEQM5JM$tb}QiQuH1GsNOxx#)UPK-@FM@m%cQVPtMpQQtglN&BCot;fMrY!r9;f; z26!ItW2bP3O3r*FeppMEqa#(=+vSA7t=nCU2AqT@@A*&jjl8zk=LSI&z3ATw;_smP z!>6x59d2?y5`^RT8clYmoAtLE_1ZK;s9ptmK!;NnAmV^qm4I}6B7Nui3SoKISDVU_ z*GH45(q)I{oxhdT`eaUidU#)`RzN4M^w9{F4T#TRh9SEKhiaG+sT50VQs)cv^K@O4 zqlkkCi?9zZ1|=hmYOfZ)tLj}zfTCW-mk`f4smQ^$GR}vDx1Hqo@%snkK<}CMsX<{^ zU$aBnMuk(+gdqI*pKh|uM4E1TE`$XM(53v@%VgEg^=Uwz=D90U&2S1{E>b7vlm}@V zq*%w;Eg6p;^4LaX(vHOx&<|LaYuo9Zlo|KTFGUI|!*bDxa;!}|sIE!9tn4@&o=ZG) zQDKME#Kc+37Nhr#6A1rwXab^o33oUwJxuv8(qi_Lq_ZREx-(bV0~?44ZS0}gbNo3+ z<06U^G;uHM4kxi$Itf;ta}8&sjM^|EvbjWqj55shdf^y3BPV*aRlGQyi{9g1N7ELtR968Ji8NxT^-uqQ!-rVa1ZF(V=1LPZuU|4Hif7LEHFc zYvn`oG!aqyC6tf!1!)t1iG@F!yaE?$i8Op!whEVTN8W~-Ys&z`U}ku72bTCPhb&fQ zj;J3$a#~=3jJW}{jdhjVgi_~I>^oNZ%$1+EO^7~Fzg9{jakGvcaWNvl( zBAm-;ICvEsWn6Ak#iRDqR0D*66FdvaEgR1_{0zNhuM#Jf8kK;$c^7qA6-u!Azg?)2 zZ?#WtqL-p;PwE@n#!?n1f<`|+g+zU%3ad@6&1}~h7}*RKx<%~M6RN0mw|-e}%<XZ=Cahac!_ij_$S-4bFJ?kBDE&d<5U&n$(PTb3YN-JJyoz!dKBJ$9e6Lb z`aiXQ<|nBxv>-a{|7AD3 z!GuZV&BeiCCxX_H>Qv$(9(hq?&sNb=X2G_M8b5s}0wZ`utmx39n&e#}Xm^NXNm7NX z=Pvxq@VIMPY(nj7{0&E`tz#>Cg)z)i+1i`!ac{2%v@8;B$fErFf3vs#=Uhir*@}Yj zlLL|esoNy}cLwl(&UH?XIu2%5|7EhvQqr>7U`6)I_8AfihjGqlmj=JcGvgXXwL+>T zk--xSXo4^dGY=h$r;-zE$ouvRNz9~_danfx6N>vM3i21l&6zvbzWWq>!rQ<9;6_tFxa{Y^F((3Uoa8X@&iye*m??OekF)P|{ zL?WE(@2nT;V#64-ENV)-niAuBX>mwga}wt!p-$1^R9-CTY376+Nzpc+(t{BVp#eid zIrZV#C+FPWBasdIvcfsJV`Pz2tZ8%mObjO zsTScS5yhO;joKw2^E(w%{K=9-_9vnT*d@Yhbw^b6JzWg?A|A$BWtPUwC0F`VYh$t+ zk4S}bsZTtO00B_zyb<8XOD#8_C=VxNi^d&!yVB#>+irHEhEFJ6p!HOU-UK&)24dzy$EZ&ZJ`W?^QpcZtQswrs2>E zx1iyPTJgiZUo+sv3PqDOy>%s25tyLc zUQfPSi?IUy6wRM}siT2o+4~g=A1Gvh(ey*t7`|x}lKJRko%$({WAR8u6kNbR&_iZa zcg-%Kut@ZMP4OJ|hf;U*v-`=61aanBjRS)&|0tSf$hKC5rXiEh1yZ9tE1CTg*O07! zl~I->=vQsRtl0M-7Eu+S^zV9eOYwT!D_~eF7t^T#VAK8H#O3`nwPeYf&8oMc4knT{ z?wBXx;FCACUbrXd+o(PDavY3zPA$F~To){jMo%d(nU=?h^wGhmdvnNu*Y2b2>>59H zjMg^Hn`8lKj1oe^67TU_zb7;4tdoH~Js$p`if!+DdPUiAB;bv-*nrdBoQ_M9Ia!Eo zp}2W~kgJsfZYS8c#8eQ}+g5KSwXR1SM7-5(AbbX3vNvRu<8Kz6+6vm3h1&GCscl?O zYe%2BaPpL>IC?X@FruF@!_YvL1s4m-g$`!AjYNs7vnP_d`2iYB?~D_o{wbKsUyz<9kcglhG^v8N<4bggAZyydvg8I_FX4H;QG6R4+kZD0 zBp3og&-|{MXlOQ0zI>hdh6cM>JFPldF(a_SwC^cZIQ^U|2sG1cCX#U zcly3J>OQ=@>*=OHQOHvCZu|xM@4cmeo6B%Ca&Y{2&Jt(s1N*>Fy&pN^e{(ti#{qgq z4mt*UmInXja;{RfvRVJx(CPdLS8Klv3WX1eZ6U2X@I|VW@U_S&l*1x0z(cCgCF+Sa zRQbGqPBY+QFI;n8szP@6oX#{c&c>L5zOYm>jj%+J!S=Xf#_`_cOVanL%Y>3=Nq8a8 z+7LsJ3+YLN5Huw7rcWp#w9Owe+6w`!x^Sb^-!uuW?z+>G&u~1|6x_>h;q<-;G~^u) zQyqkv2QCS_%n~;WvH2hs>OV9%n7KkM=WsHo7{6lWg~hDdByr*;qrg)j)<>uc+@Q{+ z4?G_cJCIQ@%fe|*9QOb4-J_4fCU7bC))k4lKp)tCAH(Sr`QnpA5>^YWzJ86KLWij* z;+H_6`5;E1r73`y){om<`^5`ldvt%3-DcO{`}itPO)=aRB)W~(JQwW55Tvb_jMNS< zy$?c5!a8ZB>w#JQLS8~P0MS3_A_xrRE$Q%MH>uY--n1#ro=#f%5ab`77ymX2OYTQz zKQE@g9!vizB@_GJOo!IBLOkql&r6^cj9nGlbcTx2|L8Ig>0NnfsQE!m>j}enLm#d;P9z0dg1iZNi`%-2lF37Fg+S`h zI2@Z!YpIP=?9M*L9xg!Fc04({5Vcoi%f4p2a7-P^KIKpScdzv#ZfQY^G30o;$=o#X zaTHzqRI5tDgpGZ%lnHwa{Y_{lf4g>P2|8hsAFTs;Vs>VDd6pidxIDd8Gs)y^H@H!T zNPtNS6SGh!`i=7@w^tTCxjHS^WcD%P_hzdK&67 z0jx!Fzc_9nef@hFzMsHpN#gNOg7u=d{6i#OU>Or@qeL@qVd_59!hBU-(cF;js!;;Q z&|)N2S3w%tB3^oYS6vxP?udMmd#*1hwt*Kbs}m`Lu|~RHq)0L^_98|RTPK**{~+ug zyEB2Jtijl>m=)W$ZQHi3iYvBl+qP|0Y&%b!Nl&kt^>+7~o-gMooV)Kn8w_g0S2ZL) z9C3C*_4$s2ZBt=U;z2du*w64}8Vx1KLutS3jFU<8(R{Q>%`NkEAD>`t=jLBr#spS7 z$|bo~gxNL4W|QdOlG)cHo;Tm8qnvkMp# z0>z4&l6Z@&FR39X@|}ussau#-afeld_XQ2yTbDOBty=*AL<9uH*qE4DTW>B`+*P!J z2Mcf2e9{S>xW8eCyyO1-9V+>?`;4iyb|2k%Xigub=l`_E`|B6cBfMP<2;`EV@zr9u zd3gqwEjQP9TB)&&?R{p^@Sm2bZ39npFEA4ILT-BRK+SUs2(t@bJin?u0rmF~%`<=( zB|kNA#p%v-uLBl29sZ<-;%X???{z&iQ?D5e>dHBHY9=|>lbIL`*k)(8Js6$RUIQo? zqd1S|3RF*ddt)wec7n6V@Wt3iPjA>pRbXJX8z8$lYDmQKM#9$rndMv&W8venoS)9C9 zAAIp~r-5)1{1gk*fouVkdyhl3$U3&|ELa)L7m8;!qKr@rN&*Q_ArwWH@Oi97Ow9HD zqJew-Q@syJc!=usOl#n_U1(54^K^L99f>JV399~PSdMKGR8aH! zDoL?tI~<*hN3sLX4BzLawlqaT_K4e364FQ+i#KBr{ib1J17jML#K@0 zxfe|F_w|HFw_C}{lRiyJHdZ2A;6M-PA-a9+A z3kI*)JK`Y@6dt3ubVH4CR%O=Iis3Z6V>)?iOl1thiY@Kb>!F}4qiv0~hYvl_LQNR@ zof22S*oIAd87l=^cq4ywsMel6LAef}1GfwEt{KY!R1o;($M1P2tZaEbeq44#L?%H-ruU(N^t!oRuyiV*f)^5S@MJQrCpiuZJq{ig5mPZ`yS4PRQOkJ9(;J z@%~wIoXuM;zh*z)UW-aS@6nLX9CA%qNwUwc<}R+!utU%NubsUa-~O$OGi8_XuIm)9 z;HK0U*s+bIt}!%Ucis*k$?cJb$&|C~s`T3Bh?q9jR2}BQr!GD-niGC38>`6W4LYGXU>$OdA!SBq{9{O>_aJmPDE^KfSOSq2&mD~py-FzUw@Bem!+ZJp;`2`IG^nm(P%J%>DRr^mg;Ob(bZ{lR>X8M0d zsuXM3I&Vn)0D?EP?rkX$gb@@r%>5^|@w~N)!QDg=T=j$k2+Lqq;P8atUpu;A@2-F- zkTp3v_Y*2&#zp$s#Vuyu!Mz^f_unf=TPzca-s$YZxVwxBph)JNumW9jgG}QKLeg+U zz9<1(!Iif}srnXWxu}K^U&4wLrt?7TKY~CX3(T;hJDf<9$}pbvon!^ri#aCQ3m|^3 zXoAzgZVjMGjJQdmV*t~>;~T&v3Dq=bY9$y}1iQZ@F0M$y77)N zZ0v8&dIt&hB#qQQ393mUFi~n7Y@{)?yMuY(u=6Dar%{3<)^R9Ioa%hgo1} z*_?SB(m0Cpt1-o?6EzJQDhWw~ybyCPhv5d6h(G`+%!k!F* zwBiKMf3#LyCL_z?Uf40N7jPs%UnELUr6lCdzcZVL@US^$r54dp)s!>k9DxgY{z)%N z5YoaLV;NACTn?H`(WDd&vv)L?K<(`a^xUBTl^_3>~o$a z?_YZupUSx^yvLtiFVuF}*7uIN8C(MeoO_)X@Ve}-7xuaOz|EHNXTklO7X_Vzh#CRL z!s<}B`;*Y$)?brrHSj{K!>RX;(z|hTJqK^Mex%Kgna|C1kFLAnVCx;QnG0u&SVO-3 zwC4Wj1zx9@ziErwd`<-J6eG)hh63n&0Uu$U+#XLJvvAyYz~TD0+Plvwh5eZOu9qCY zMO+8>g8pApF_d@&oLzlx-&DOU0K!5;_hu;D1-E~09+KQZUrz%)Zi%8dc0u-wLB`v! z*><8wOjLiTYe+i=Xxui;SV*w+s+SnR%oX<+CEO^Vxcxpi{Nt~+OZ;ksQwQvwS@Irf zL6Nn91JA^ZKD^j-lOrdeAVZ-#HY-Gq-2>#sk-yR1+6~foyFM(t#|!~5jjjeh za0dKqDI&F)vj&kgQCX4s-g{AR2-7j+bI}*vQG?Gqxh6VCA0uM3ze?nn1J{-=)CAc% zdUt!ky>Ahw;l`p;Mhk9rb%k;Jzl!AqlwI*J$wvqzvFTRYqQ5kg6cbo)z6L@d!`}bZ zI5-xY>QpA4#_STMQ%cglsP2lP@uBOKwhfDL2Rdt-RVqQ-kpRl**>0X@{67^_30 z`H8T|%ZFTyu1i2qe4H4Iaf2)|@2Txv*2?dlhG7=(4M|JaOj0I0-&e{FclCm~FR{xt z@No6fogaj6er5IZ!vj57k2Y<<`;4sAi_SjF+%_)%B8zpOGWHTv8yi%N#TRk`=au&o zPr18F;$rqmK}6rR6BPk{H}YbBu2Y!J8@3_n|GkGeN{)luu*gLiiDyWKt8@!!a~;ST zcvcJp*62p(rL`tIqa0T3>_;!qq&I>UN0uJJHpazg7PfRVpDHrF^PwPaaaph>poC31 zy_cv&YZRM}(j0`*o_ZU)Y?vLp9D2NL86ftWDY~S99d(SHci$(@IcpMDem}gcTQ-B` zy(rr>@a%UY&qt$%#glf(i;d~zO%D4dwh3RIbH8pDrO09om;W~=>B!DBzw)KE6M3}I z7QfNNx`btFMN+*!-7$EO12t=qLVFf{VV5eykUrChKOH@R%UL!V{c?x3?$rrR7K5p} zFvT&KG`^m0NM~h$JARO_rSUZ>!zkOiGIbt5vMT@s+~-da$`?2!;P%Y8WA#t;0OiDF zt)!-P^XALZX32ByC18>I+lXuEX`3mwGc-UgniS95Dp=ysIVuz6&kcZpkMai1At0G8 z11{{6P+ZT$N&|zVP|qT5iey<@g8rgO;+XRJiXt;*93_@$QOO7^H{jLdO^+r)0#C$a zWmhzIN=S1~(z4ks%14jVLUdR2kWN#gOcRD82U|h;1}Osm&Z1Axvv)K=(0~!&eeV(u zK_Ofm$gCYTciAQ2BO zuxQ}7u+}>d+2hOnqNobK1HFW|XZ&_bcgyH|ycU9{d~&PA;I{(~xF`MSnXI?t2@|)z zp-^Y6{Bv}{Qk>{Oh;auT7RlCvA=qQpIhC~;qHD-+ZqUx{<5x<(SZFoQ z_})3}Pk=-!o#p^{$g@817uK*Yk7R_>X|EVkDx27JP2uM2xWt9x7;*2tD_9hOiaZLa!%FBa| zcV|hwFAu~L!QBA9(2un2DCxn3uIywE4?<`f@Zr zwulP!ZnAy_yN&iEHvgDwMEVt5-t1To2S>K1#6zwnj8B8?(>7O-ksf6>QkDD*c&8%& z%4OdYJ*8nOs)xM+NzLdu@qGH{W7_)w(hSvjL?Xym`{C?gxSLF0Q zYerrW(2k@HOc#TDv+H3we8FJbW@TtsH2yInrJJoe_+hAO=2gr`-g2 z>x=3!N#3$dud}xMG%Fxb85xu}5M7e%^fg{Vbe&vsTutq^DU&7Ljz**#*^7y@72Pg? zgpUzrrQE5wxm1miHPF91XcJzF^J?L~`P+(zY5|XNSpMcq{!gEvYUhYQ*{6x$ok+XK^p$=U&@4yT8|!hw{$Z0e)fqt{2-E{Qtw({3jQ_hdmqA^h326 z{^XHU{;vSb*wVqm)aidhtSfG7=S7K@+`C$iL6tzftg!4preK954kE|udS_e6lu0Ez zQL9l9&3g%_-<_G?ezTlgw9O`*ju2*T1Z=8Mn&*5se6f7hTh%Z&0SD8iMA)72?z9DX zkOcFK=wpx!(8LlYAT=VtiDnfC3ER_QSs#abo^>p)`m|42Q1s6fHzTu=LfIXO%pCE! z0vdy2-KmpxGw%L|1wUktb!Lz$xLVEq+qAYhIP&anUtQB@B+%+XMDa|G9D)U{1I0kE2crMOp(7HrOP;D{AkIGYE8z2-R9WEp^xGjGDM|Cx>(^{O zv>4u{M4yfbGreRm>;r{}*d1B=2K1x}%A4QIGHgvBH0WxfB%@%yCv$w)Qj=gn2j)OH z*(9_sZr9&mCY1H@Iv_Usp{tKl;SvKsjEl1f9;6533sg*=$=~#T9@W2#ceeJSUb?a{ z1uR1al7ePi;0dWp#N?$Uyjj@LzlotyYwb>H%!njxs#|qcB-7&Jr!T>5XPysKo>8Ev zV@|NMFlnBHM;|OD3}(UN#Q>ycAwDJt8uWT7)_S1OJW)m@M-X)2>8K58flxX=RAXLo zO`2&?aVWxzF5uO;qUT^cQdq-#B(G8_`BU)w?G9sW4khAhnDOc03%mPkhZZg?SOaz6 zW{*9rbwD?>ke}|^zb?RH_;;mYf6^}ygJ#awp=RE;epxdFtKER*K8pp-+zprUW1^&u zRujtkP2Fg9BN}`j_!z*4%@GKkTzR$z-6HkpE#tUW5k{1*Uh8GKc$K;eV9s5{LP@kf zv@j3E7y4x@($5Be;dofXZSTR_#)OXSTL4lZA zfG~6RFVuBKM%9U+PsTa6)A`tPCi19r85=*HE7@_~ zq6u66j^;4qggIWGkS}IZKSbXe*Yha%C@l!$>iwR3oSPXTU%4Bmyi}!H%|B6ddL7_E zc4U$(QEY3=n8A=_jY)XbfR{9wEXPx$`j8Qs=MW?B{W~fNEJpJI4LUJ90v9Am0w!#R zpb+@s#F!3+;)Es+!W*%kKbbE-S4yAI9(8@Dp!VmNSiD1mz!0O(_8(@n84Rz{M8;r4 z8{`1z!FsPf4x(ohR2Q3_*y6LTQO$rprag)gvdF&pfW%*PS)-OAGA+0`;ldq6+&N79 z&9@thCOcJ2vQ%4KD)@T@nq(P2nqY7+25|`4BEKc*74Bz`NwSpwS*9hBslN~*BNCg! z*IDr82L*dd6Kzfa(+9>}44Jm0+?C{1(5tQzwbY6%r*9zJ7o*506`a{HC#7%PzN$%q;{PTH+^Z#cRnWtKaSxv`?P(O4mn^$4S zyu^Vd+dTn&8>(&W!+4;Wb{MPu5mtUj1cODR3LwKbLKpgtXeky<1EMHpr0_D9U9BP@;-Y|4-rjIhTr5POY} z%)No?B|_us4>cm^AVsQCA2%UhSQZrN#GyEZcNidARfm8sEL2jUH!NuWwQZH4waW0I z^*Cd0gyT^mEQqy~C6`w}QX@h%`Z&^&0|Pj;sQKjghfPe3iiK1%R*I}DlOK17)A{c! zyqj8nzXUdQoqh+f?npa;LhnQ-&CXosQYwRpo4fwd^k@)9F4CW-qi%a|Z`TgCmyN>< zK7GWOx2W3L*SFS=oQFXF?-BLP+EgFcm-BnoTi1dU7EJk|yZ;N37`RK%bP@__Y}Iz)M$o+EnLAAPN>nCD2!Rlah+KXOBioX;cY+B1_b@X~73F z%ie(RBSRnzrIhYD655!&{HFb=e=8px_qTQQ4x;v!Vt~ab`PPxVN&i?H9Qx@QsHVi# zK}&+?*py%?vYn`A@1d~@MZ0%bL)1n``qc)KCDsyxY@xnAHjJI^04(}ro@eNW43oVo z*+D2*X!*30=I%rrCWJIvFdQ3G^{_&7l9@BF7CIywUJST*69VvkGX6I?u{EU$oxLO9 z(fZWK#MwlKQ+$hv#_$$ISbh!5a8g;PdsLI2RCr>uIi+_^oRahPF1!FGjndRWkKiYBJqcB2boj4D^ zcawkRr_nItH?X|Dd;!fUk;6z&UUkc7NmV4(I=;elL%aK}(AC$_qQhRNi`G!6=ZmTx z7^%K+b9d5((EQ3g{`a`Z^qP_~=a-mGRXSHO@E^=t2Ie&feW%%>bwAk)gZE}?Bl7#> z`%4tQ;dCzmq-t(XB;||*I+88o3Y(HBpwe}6DBeFnA;t67E_U>q3L~Uq61Qt zyV=8u&W3V-adCBl=bPK%;J=j$>NAxmF4{Iv`RU%Sjm(gpb4b1M4{e{o(+5?cbw-&x zahzV|eW#r$m$q)oBvsKK=H_9C*@q(213FjPOmA5vw3_z_V#c{^IrKW|&*R5w=h5RU zY3(i%Glwe%Ms}n14$TkcMbtn23%8N;3Z34Z-$&7|zCihl7jyZUcXz{?HOqs|3p_8^ zCEhJF7q{&_U6{_c;MbwV4DAyShqnS?k05(eKHdZm#C*%kkt6q!wB`XuFb=Zz`8dpm zp4Y9h(I@{*63X>9Otl`~{zx_?H~A|l>ZY?|{CLxU%jT8&w|gyl31TQ5=bPB&WzFGt z;tQj_yq_sAsEEF*(IY@&NyH zN`keBBq65c-B&W3`Ya~W1Fh5=!&3Qe7vz!#gdVp91CiIS2k6Ewb08U ztto~)Q2+}u>KzyZ7d==t9e7e}kPm^4R5`9AdASwLe1%CMX9DWFuw~@Xb3@jb7GpXW z>_F{q*dDGBS4UI;hm=ZFl%phhPVEzdUKMFrqz-Zwx``Ry#43c{GgOY%!M=NiW0`qR zLH~vgspJd`+}WW(40bfKz#E#YlT9M`c%rcJ+te4)ob-J-L}qCaPDEOR+-Qk01gsv? zkUfcwIDTYulbM7>XvR<}+yUz^El-m9UtyaWpQ!J?Es1Zn(^C%jd)4=4BI-a`?k(Q7 ziOn;@P2hYeQC$|22ejB-v1;+yoNBO?%9O(5aTSBX(ZQG#CC%|vU3A(xhusuV^}|Up z7pD^A;}-GTB_lV%jhxx6^D$@n2;mG^a%UKe)bkWo3)8?BZP8%)&XH2*2o) zE+C`N;Ru$(k$yJ%$hGIhDj?!I<3N9~WvT-Ym}qlWk56m_tECD4xy%MjOZxX`3g4s3 zwX5@IKF}G~7i5R{VQemEIxFL-xH7nveLhc8O=#_*v^ z_Hm1{yBTL0di=-0a3!9cn<}-=XJ!bGgKYUrD2$qwmA{%zaby7GESA^eSVA1eSA|%1wW;n~@*!@Ut*RKD=SRb`;Nm3_`b_twR%hU!tzDQzn zjIA&bXFZ`no9aPKY+OKQMqd0$3-n(&s-rFC)vqFEaQF9S#J%guzvrWCZ?Gvpkk2jH zl7(+4{hcGDtRfqLIw5@X=FIMiJuA2NK&(OheN_A4z$ds@yQ5S-_NGnB*rp)~)`dYz zUE(08pzns#K6Y%~i-PMgYZ^&45Rl$V6`+?WJBE!GzmdM*+zAhAuQ zGh<5EE{72poniT94AOQ%g?@Y0vP|t8;_#b9iXJuy#vhI9579a9_^>sQJp`i zSD=jgG+a56_5iFCy9yNeu zeZ=u9D$Qe!S^iRluhRTb(cJm@yc2fdY|VhLWt*I9?Hq=ol7q&A(3#P;N$@njEUIpCaoLzDk)vb*6%-z44r8-pSF$n9n!X22}oo#-NR8%byDV&U@v&A{5h zoH+4wD@7|sWx(#5_FFT&*{jiM9w z%)Kx|GK%7F*?>fN`vPTIzA2 zOwlmD;*kU|Y50R&m>{G9UB@w`Dz+R{F8ga-fH|}ni;0U_v%)yZDn^7U1)zeZ*&dKq z|0e>TmQ&SG$`t(~@E1|+SZAHC!@K!*^O?qX zN+Sl=tTw2UceY+0Qg6p`Tz1`MzOBl{>;|HWzZQ4WHUH+1ujg{}JOPBmRF1he-NK@^ zJ0xDGyB*NmAH5x7a^3fZXNAQJ9ID3_(#P4|1*Z)R59^!_-OWx#!daWzu)VQytWcJ{ z2^XGD-qQ>O9r&!u9&6yei#5AW>y1>k(ojwVojN{kUc`T}jhn@36|OdfVyT{e4pxRW z$Cj%L^GNoiZdEPjaqU}^c2J~D;puMh4z*P+vKTA5=mSjIIK<8uTHm;NC@}fPdKo7v zfr{%TWqpWt%HF#gsY$#E`v`Knv(FGY;3uDcVM=85HAg-zn<<-tb~~z<@H&%y9699! zg&0K$!ydj|KXOpy#b=sPEFDj`UUgD>j8E$#=tS1}jzXv#0IH2i)y=;+{e9u9FyK`T z^crrFI-}{SzmD;JEvc(RWn-nOpg*^*F6&(F?iSGnKF*F7UFjo3t`=#o76d-i9VTCB!DiI>gfBVZW!6*YGGI&S1#vIyO-n z^Sh6S;y@#0tp~1%^JQ)BElupJt?hm60yR&EkBrz)up^pjS8jQ zHc+a4Bzih<1QEJDW|I110~_T@O1-UUlTwGYhXyt+2@V;hTP)0$y7oF(^xj@DFP!Vz zo(Dc;L0Q4V)M2v0erX+f#51wu&8(VOnQq2H)jahr=sFcLgpsX$E47VzYdL=ijW)15 zUrTwr-K7zJ7ze{xvqBqV`JB2g&jQ~{95DC!&K%s_*6G$*@A4Lh?DeO&O$Ez$l+ClD zz3je40Aq7vA4Y|H?1@uO1p;?OoijC|9gkNGsRL+F>+M_3JP)>%N1pYxAPjlldETXA zQ11|QAoCquZ`yT>OcPWz@Gt#vi|H5a)MoheMS;Ju%YSe0=7r_Z9fs3&%95?=)H|+& z50Od7Nuxe?^;t+j_z^UMw+k5uZisrC0eUl~qdr*-fS$fmFhMHTAWJ*Z&i zkt>Ok#=qlEu=V*>bpXre=5~<~0k8IZC8^HIhzii97`2|KgIL?&0g4uSfyKglfnTaA3D-5pdNcsdjioQz|saQAp{bc-hwr}d92 zK=dg9HM1x3KF{ava`LhKwA*@wg|)!%fimprSo#2Jql45xPeVo>9-*BhTGR$)2VPAuV`)S$hwdNeRPy4(ntWb$1VE?7<-nyQW) zGm5wg1W?&n4V@2?n@=5K&#jJ-FvO%h=7uH(bn$yeCmndj)rz3MFr-J46;+i>^J1+e zf2V&8SgPG#WzX6KcFEqdWHQ+QvRgl-Sec9Io$%?d6*oSDudF$tI{P)0#kGChJP2>Q zPgWY;%6VlVoX#>`k>7O}B9#0YPXO_iR!#7ru(H(PRp@#9HMlog*!x}x zr0?)0o7-%3zTx|nDC2#F>(GqdiMonYy0YC@%#BO5 za6QYr0}qiF3|u++Ha+tSRVpu=VrY7U@z|kH(}pjsC8_z<`GB(7s{?sGLsf@!E~`tP zvg-v4mP|;<8&v|5cx>Uwz|+C7!NFL9*qDwjp?7RXkqHOw^R>W_dJ{?nf7c)nq~*Vg zX(pIc$H`$ts@97XRG5{UJgZ2gYo(`^9eIHpq?n9ST98^MyeUWu;iya{AqP96lqBb{ zx&w8JLA?}LF!d6|{C#|*Ro zV}>F4U%`*5o!kE*wN6&swA&Oz@{Q>+%FFwcG?t1gh`(O~BwAApPNGCg$qfMlh1%YP zQ7jRFfg?8Wf0It?T2ITr6p+1qJ(bREZszWCS1z6_aTI&Ph~BlG!c|L6m61WDm8w;& zQemsOqH?IhPoc%mCA}t^D%zvyxy8vfUnpmRFE8~plxwRy3=Tf4TFtin*91S;_`Sfr ze}y1QwBDEQ=~GxX$U~x6U(b(oFxURZBO%$;WQv-hLglp#rE#{mN6JMt)oW`Bo2QT` zzeK6Xxgw$@DtTS=f`=-Xl2$6zci{;Xt@xE-ooS*(!lmNiFQXW0azsnTztaviXcx*j z0`w>XvD!*H|G2*ow~!t)z$yT=H;AzAmPeWCzIB!NLdJxG)Xf!`bxY zi*Y5}Q^+&C^^1Fn8?A!BEfe#(8 z))(ThO+@;Bufk%Pkex6Eo1R9?oVMm1>MrWMJfjj+*Jq92&IEU*7Ii$ zZAk4&WA^@wLNh8z@k5AP?0X_8njmdR4QlA2(F3<8*3xfJE25Y2*#48)NB_mM|GsPF0TvHV`vAe9|D zek)~i4t0h)bA%6Rc9uhKphgkvi=8g7Wd*dA-eXj(%wu0ZqiRHBqX8#hxL!Md*=lif zgn6`d=zlW&i)`otT^Pdtf=kPMHi=~dQ$f3`#HJMDTq%i`1~20A^+6K6N|VBVh?~eI zj6TiX`vxkLx#ps}#}{wXfwgtu4B1Kf4tJ&><>6yLF#Cfm@@xoHUBHCacvArqi(bs|9+&@)-nRivrE^`H- zUPz*33z|A($rK&PDITmOv`Hma$q8M*c`;DYE{bQ&lcjCf5!IQe!`v{SK4Xh{s>Xk(-D6aC0gt7Hs3+V3 z3O0sNSl(pco^H>6wN@4k@nmz92qc7(A`FMv`p;UuZV;NHfIk9x`d~y0{+CdkA82xb z@|d%xG9#b##hVFZARK+@qatQ_O+B z@X{!WP6wuZaG|~d<7sm#qg$jVTcZh{g8`$m;zIr(YgLXYvYzTc)@r=9V6u_Lw8rhA zlCo@_wq?=12)~p0(v_GMC6OJQht%~z;3iEawp{sDRABx=;*B2IYKr-<8uA?PqsJ@0 z>>Y!}k{s^#yR3eOynOES%^dlaw%BFIUCxumz9*m1^+q3#n{-}lAK~ZJaPxP*c6`p% z#CT91u-huf-8H^LEP&Wz4>P_C6ef!sCxDU2f zg_Vnk)r7IF@S?AknJo{zAk|^mXcJLA!nGt6*Yjd>f_ndJ^jtN~vmR}QN1Sx_X>qLB zT}3C7mwo$3yQ`*a$Lcb=l$r(G@TUIfj9JDuw&XUuBP=Sg+fet9o$DoTvsN{LDO^-Y z_+lc5Fdf#M0jK5ma^5>#)H3|_jXLK@WZV6uw|V%59N(%{sWVco6apx~n;r~AU?99U=P`div%jsJ2Skli zepa1Q3Z@HI;xZe+97_8J##hP@dtIjD4+i*2yz`Bx|cyztoujBanw2f zTo)p!Dmr$*d1;3ncH5gjiDu2J8B!fs-Db?1a~lU?t+aGLiQUcU1*-7fCC+N2DG1HB zY&OBa8SoB{do}DB+@POqJxoC9x^gXNZ+~IA$-AB@fI@e}Kr(`RpgNM{Mr>3OQ5r z_ghg(7Y^g}w^cz+o%|9Hx|STj(mnwv6@o9lIkY3h5H@kQh&SV=@pnZ+LdT^;*EjK+ zB&i4!$? zFn+g+am{o5SYxHAImRc5S&RSeby+;+m-WK|0xIPF|MNsRTNpC_&$p#G)7l<)wEI|J zJhvb=(E0ZngOckOH5Y<_bd#t6dNYaX=qbQx$RCGJ<)VB(slg zeNi{{`>1{CM}6VRz=M@?bKs#?gU-KNS(*;}sfDZN+K#=V9_HrBn$@-Oz3B0IHu>^U z0QW8*W`Dn`{*>x>Hf)qmAC{<}TItpE4+nw%UYU;H`*v5;e;;v?HkH7?TkGS|kCkU4 z7Ls!4h5XAD;Ksv!>HoqY1E3G9?5I-bUuDw0`Nmys(5R_FKU=hN^kf{0Wb)BkF?3|4 zq372<;?k9jjP!W2*7qfNP8~k~>=S$Qr{}MJqc}-5ok!<M%+CsPiIF|EOe|2}4ZnrJ?h~zCRMxd<;ubuJX9J1Y}spErVX2PYpn9oYQPJ<>45pGp{U53Z@_PQdC+5c~(^Fo_9r3@$R zA8hnJ6o6DuYrY|wtYhtGtE zjAPs1*~`O0$YZ<9fjxxGH_PHRD94f2RFu{+(oygXhHZL#;~r`!WWMgY&ZBh=eM_QjU)wfQ@M z??3!lN&@de_gaKDzki@SqPE{$4CG+9!)G6IW~4c#jnfGB2t9tjFHilM7?7Wfg?w}F zVdRjDT+gwH@^8Fh7vsH~=Ev@f&)&^-El;tHf!mq)+_-+Mm%Se;hI-G2H8-UcatO87 z77@FV>Z{Nm9cDjNe{7_h6^`DOzqiAPUJuvD+vPH-mi^1YK9TdjB0+Dhjk}a^wdT1Mrn1Rm27K7ZgM`~ZwEK&+1*}c5z)`@p(_Lh!Z}2#zJpCo&hoQdDOi<8DDLh*yc^$^3m<& zx{C2lI%>|U4f3zumqs4#m%<`d45{+GE70d4=6>I-C+_VpdV=5vbWC~4tX(SCCAy7MmW8y#sH<{pH3XMW$kdQr@|WBFIOt1*4=B$_|$m<>LM z`@Qg3ESCAMy}2v6xpv299{IA-1YJd(Cq}*jmX9YMAbYnuPhNu{0R)+-OUF_t;@8@G z)qFY2m6eU8HULY{mbZ^_jfukdAl|dsTwfjCD|p1O+prm#n}g-sL*LJL&DfRcW$*P~ ze{X{K?JIUHKc3~e&$H6?omGuc`q0f{o*l0X8iht^wG)C;7_YwJO>WFf9wPYr!lV7~ zrAasYomc{ZAiAvwi}!Kl-gM_`ErI-1wBDObNY^_}*INv*&CqXk~kXsh$*8U4?_pup$X3b|8b!$to0LF8*`Z)-;^^7O6wAnv4Xox}TNhh+(H zDdYY5jn7PPuD)11HT;X$La;w@Pabx>Fu-pGar4kHwKwQ$2*S<7QYUE5A?O|T_pbegICTh zzaM__>!&xb8gG!qU*Nk(QWTdB(ufbEWqjUC8^7E8*~^aQO0NKdjGXs}+uX@--@7_u zO7_{zC!595*qX?b(9#wbhuqKC73hZ}P7!c7y;;DG?8~bhH>DnR-X5S~ebWTf@1nd* zbL~cC_uK!QJ@I3v=KGWP8v%C+kzdy?tpIee8SeClrwg71Q`4%lGjI?(!AWVWwa*zN zxcSFAjz-(|vZ*G@Fb!*AcdWl?hdv06c+KDS! zW4L#IYqbOwo;!OFj{NT9AdJf=QfXkBPlw;%`(Hqsx3h;6mk3CsmhZ)g`h32EnJz7CT#}7YBg&~MRwbSpzogA!;|U*wu)OsxtR{YXT)+>ch2U;6+Vjv1tis>n*P$ zQHx9P##tUr8c5E%VQ4v-sWC1z>h8hT!>ClOR>n5K3|m!7@MRU~S{r;p-BOlM#Z5)X zl$B0U&5{d)%R3ZVw@3G*!)I_xohYMOv}v7cY9MS|?FZtw!!)f_#V!I>RMID0|H0V@ z#*McZ6|u@nEDK3#f@khy23f_chNx6*ZVNYlU?V@AHbM`J9TmWppJ|pj(=4+nmk|{# z3EA`)FBpv(OK`Rh;oHh=y6>5nBlJv|j54Apk4z=1otd#SN0T%cQw1wh#THa#odTVK z-7yzO*d$MRB|dWh3elXw>+WMpd`hlyDy%9w)p{_a5?5`bvM>`6Ow5Q2sp$8ZKYi3F zW>8G2E94Yoob)e~i)wBw5XTBiVw>QgQoOP;ms=yNaxQFd?4zmy6jT=CW& z(OryglxqcFgAH_i;_EM%2gHl?W^aA@l#){ym zN`a|AwOAuABzf}7gfjfR6&jVU5#NIGJq_dtrE;2Bk*sKY92JOK1R(gocrI565}2Xv5@0yhS@OT{g3THl~JvmjZ-NwTwJEP^P%ACR52s#!6g1t`e*5 zcuHYFIlKd!5hj_Lc8goic|M(SVtVLOqJsu@y+ovuAfGUQDlOPiYT-kxxfnP170r+mI7W7tUy1L^yh@PlEEc=5C z5?7!;MdZj$LLS_rRuTtXAQG%r3{oV@6r#8veXlVzJxDHJIb*qALMw{~<5Wg^Qp1m$ zC2GZ;0-ns07oEQJx2SDlkP^|FfG&Fn3Q{E2D}y!hnJy_LlMv*v1TvL+7Xh(IJ}Bm< zNQLFUdUJMa`dWeLsYm2uVqaJR5KIzp@p^|BVW7(Yi?DMF&NS-U^c&l@ZQHhOn;my- z+qP}nww-irCmsC#)znN))mKxq&!3}xu%BA%zOO63sjbOJ!l(%+V7fi+0-}~!u&ILsI zlei*>duFan6s@AjXg;kgaF>)uGcX-#P=pB@I{8jZBv$|X1?I3~gcc-CplB^JSS1XS zS(1C@B(UTdKu^b*alLyn4@BL?fJ1gohiL*k(FegB+6np zb%vn8i*i-XT18!QLUMMkgj4S79)1XAtCXiwLy|=5aik zgsczNa$gW&^nHD^489%d#e1M|d;=gFHLwJ#@q1lS#p*R-b{4U);N_aOOh~Wlgaqu{5?88oE{;@O2$oA|c%rt)> zTrf!He7UY@=S%9fSGqP9td(%DG;!5O_46;P2eKCW8MTRt6eaKGc*(A zv_u1m;IEiC>_LtRQ)+J7=oxpBb1Akrz0BrFTXb0>qaiotltS#*twvFrU?+8v6%t_P zni6Tqeq~+>&}WkQi!Mtnfifk(>GJrP{i*A7p#7t+JASb&N(@7Um5yek zxiFzZgCufnvj+}I0exT&qaPbekSYqkQynu^K%#d0Q|!=&uo(y6|z4+ z6A)cVE8arzE@Wo;!u^-4=sS6U0RXR6wRzJ`4rvho5+AeTGr0@&5dGP5Z|bTrF;8z+ ztUQ&_KhPa1k+50t%lQCH4t8CJYCELLw*$S1tJXh6ZKLFRPVi6n%+f(xw3(IQnTcwe z98iM9Y*fpt_aewBGErX5!BCkHh^9uR1(mo}$rHb9qybu@pGD7-lF9AP!Sk5i(_Mtt zH%^P|&y05FSrQzs;g-qZC%r&w9lv2|a^S{akYpBt=hx?D2jc)hc8iA4qzTy-Q?*1? za988udzVhGUmhZbZb%!J0F65_e)}4hOm7y%$k_v-0(MredMO4pUD9;Td6iZ`^!jvp z1p4GG@c=43kWL&y>0d8FmJyO6PJsv^QfoQg06IVm0Va?${>Y?TH{GZiB9k3QH{<$f zCFtG+Hx+93=#$j6h=X^t1b9P2AL^zg_88OoE;DvRTEiBY;pD$3jevhRzg*P^?75N9l; zp1AMNdF-};VK<+=K?BQZj)frkKAY?^IJ#m_f{Y4vN}hn0I@TGSKUd9UzOBF8G75*3 zoE?BnXDkLMG`2`6X-ewwr5L{3)WerJBlNpy9%`SixPO}sv>IXyNG*k8EF{$F@g)Y% zZ$4uJzzq&hk(%=xrh4PA4CVvd4f8a#fxAv*y>O|YD~)7N?IzOh8FCpOzxqp3C=x)0 z)#bvNRdU;vUNpGhu%#XxlK_{Fld5ZYaBF6^f*W5W4(uscT|5p)F}hl;#L8M2k7w?R z6Ia!1iCGew0F53LwuFLOUkjvaq%pl1D=Q>gp42q%wIUzaUn}H{)xQ*&R~zFooK!C0 z12z*vb(;28RcwHbwTuTQBswk#yeUXaSzL=1RxE}`9&~w|%MBK;T}L+=Y2iggAkzQ2 z&>+FO(RB%zPZ_KM35*F@k7Z02sGnie0uq*fq{$7Ry#sXW#(a$Bs~g=+h`BqIucAJB z7*Ov5LgAPNp9V>CqglPfstl&-7_KBl86t{(3Sx~VblwdR3`lB_K{-?w-9tC7a&<8J z*L8yw+NU{AW`vHFPfF+hV+cuq_C*>=bxQO1aj9EoQUj%LyEs$~`+YX}gy7~z z2#jKNo?2)vm|8IMi3`cmpK{h>$1%~n1CJ1?_q zC#$u{QpM_vNFm&@y+>ejN@(c@ibj}i=7lQpgwv!(;D2l!nXm@r4R127ClM9Tt!45T zb|?c0fO;|<24tb7cf6Rb8ZoJev#G{45>Ut!n*z0V11!BxT2x+o36ue-oWG|-L6-~s zzwu(4X8-tFjvg0I0{2L5GX`Q!{3(aS_)WK}h&DKTl1=HXD=adK1@+5lG}4XbMsAR5 z9%AqgQi*btRT>wE)buA5Dgr4G+hn`tMh->VEWTdr#iU$(aiQKZ@U}uc&M^5GJ<`si z05sBozR3AouH5-2J* zM)*uHXrBvjGS)r&{;VShqqdS7#HsbG9qOGII&{R1dkLBn{jU*9%%=CMW$rhPw36^p zX|iBKdo&8{+jj#EtJYs36Nz*G;d=Ngg~Q=$U1)h#s-c#`FU>8f+0X6Yu%Q=J#?97Atix1(q&Uo z=+Ml{e-R_FJQ8_T+~PnXXw7D$byDDjSptC$e1xN-j+$n}B@)Esf`YX*K)GuO@GuL) zdcrUmB8Vf>jU20x`wjWURIw}a6VxrKugZLxOs7m$kx2@03o77#E0T|LKq*?J~1 zhY<3|4;LfJeivJQA4qS3OG07V>r6AFsFK|;!a++OG8`tl;)Gvq5&=h-*g8o3*Z>j- z=Nc~0m_BqhKC6_!s6Ye0wh_;UxUY_vBZskUv|p*OZG3Xjl?X;7DG;|pBusIlw8S1? zNUr~=elr2Rfc4*vyVJ#}bMcf6*O53>k_@MTovOvN-E3WBp8`TxZ5zgOsBck4iUqs4 zg_Zsqq;~-}R}+e}r6N*#?~7~j<664Vod2rS%rN|2NhPXfc5{KL#N|a+@6IIkG!>nl zUY7?49iyEMuar4R-v0%^D~!IBTI{jLQDaV`-g7z1{zshO$l}~g4xOS}@i>{6v0UY+ zb)jr^0&Qh5xU`j-+IEbTUASIou~Uc`TvtF-@=kzHS+-nsP#Gt)mQiws`zIFxQaPV* zkr={jTBsFGTeUDgmhmLQ*%6b+jfv-Dejue^rG(U^)3^v)sn*hXyXIP5EAc$qvb;vA zT-?S~4fd;gT-v_OoX3h7lewe-&I`NG7?_+I#!mPG8c5FMESC_6+_rXfbeG!1@ULl) zADCJeu&kDxTu$E`@21|K%GtXW)KUd4%`uYAPcQ6u%-1;g{MfpsWQuRW`Mfdoo4a6J zIq$rXlzN#WpqC30G2Z-O)zX7Xuot<46sFkT-w0HVcoP$1e_~Fd$T-dfV~UAn@g8`Y zt({hJUMfR>>Iv7j&xFQzX}31-UvN4!5LIPc^e0mXtZv~F{!0aCn@Jk9nv$@b2nRIv z4 zHyvr}q)OFjzmw3FwM(DXQ>|)CznKyibWza7$fA@>m9TaqVG!zAp_=eQV$oCG< zCK{6vpJ;X$V!4g3{n`#7)ou5sjmf!0Qrq+u(o~L$MNc5Z>U?$~@~Th)=ZUmC$cqq@ zljvA*UhmUVx_eR&dw9AJ;Wb&PC{f6OM(IAfAfFc~2a6INl{xcdcmOimru0ummj77eE_9f zRBWhNBL)=MfaLbz_jsh>+~Grf>=VE>+)oAXf;m=JJhFp98nQbpn5THW=lt{*R;YTv zWD3mI{>x^5g2WmzmGBT5ECfzP_cqe+hrI`SOyG;*6Q8aZaV~7+P9TWw^R@5i@BMS^ z<;u!TRj<2&{!bkpL$3lu_K%VFJoQig+x>YC#@Z=?ZlBugw1dy_OU}SEP0|;i!s`7S zI~0cll5;{!Q|32?L{eDE6Mv z-eg40_;er>lL34m9yTyqvdwOgvRBJ0mVlxBRD{ z3K<*gM{`xz?3sNU7AjULV&{HPJl@e$f9x0;7)qfaFTC!w+DD&v1#n6PdVDWEzds+` ztkw`om;c`7UH=1~E{&sqXMaW(6^cnY z?G;3wIg@cX;arwFt-5VqJ16|vOmZV^An613`bRwdo82I{O&%QhGDIvm=xRQD`Ah#K z+MPYCS<33fLdS^slz0QazTYN8l7)KJ5|<)*2U6^`BE|_HEF-$8x%lSe7gl1goW+0@ zG5?*iM5&PMtX*b=Rb>Je*p}w56}x+SQGw*)h8=O81G6=l-R9oT&eq_O;$K*X^CT#$ zk7VS8NK<;@mf0UWq)AqpW3^eg1cH#{vOrT&t89t@MH&s}ha{bsuo2(*4Y&ceZ=6#s znT*i_+^K#GEbRA){0zxb<|$FhCeqL(j|uxv*#o9niln!o=vu1qqJMpU%HE1`eYMsF zkW1yru@o&K+(1r<(ui^I^r^tFDvwzmGTxAA%2`uFR>bjSa>OxZ^vIN;*P6gt!^0`6 z!4FTB{6=sYGw5u%U+J$CnSt*~QU(rkM21GlDZQai@d{$e?`r?yn_}`n$$iO^F6I^I z)iVT__fzd=Qm|n_2vhr#M|@ToF1`7PX%EDK`ER{NumkYvHU_sYrE-o{ z4a2=0?wPj>y5n!I`PF3Oa{h*#1@2mXP-2(+qI;qBRcpjkjL@lx*WT*M>rA(0#Dvoc zast;o*(p63$JdS(x-wXE;r7|# zW$E0i2lATWxOCSA?8cR22(c4g-)!n!!aiIQ+uZ}0+m1fmWKd77kLn5lipCPgveSmxLxXE z6FmSWdSzfo&0h!P4?AMhl}zfCN4bZk%K}TiJxf0{TMy_ckUw6VJXB5pQVo|3$WgOH zE_P7tKo>epCnKK7M!{C;5UY8?r*POwf^%LISSwmxOYo|y_XICd&u@4pJ#>{t5-QxL zX`&FrG7WIUE>2I!T;7Bu#psH%JRE#)E+&N@eUuUj1yfX6=jOG4Ry`BX9-qg&6BN~2 zTo#pZQ4>e z>+dboi+yp>TOFC{6ET3f^3Hhlk>FtuRco=kgtS7Bm%S5O%;KgO!pab z&P)~lCT)+Q3U@q9)a^GYyF;Jv6FVZFu@f1~p2;zjgzgPLr~02%v129bI+7<9^U+cc zq1WGXe;M4|J+m}Q#Bbm9yoZJSld}1!@}Drp-9pm56TFzy@zp8};bH=go(RS~@ML>> zzS8u0QIs;}@hf6GUJP*Q+b4$$|LQhl;jR#RFxc<$3V6dXz|6Y~+}v1yZmZ1sJo75f zonE{P^C7G}YQ*>&l8d>G8P0NP^ZJVhlp~W(mg%cUL)TTL3U>3qkR2}R7IUrvfkQ-l zqOhHm{>hHgb)XS0Awo-cHM#vEj?hs^VNQA5#@2p#zCn`usF74poXc%V@iB{(iM^{u z4lz$WR;mEcF*O29CBa|hhg(VKXZ(>R*HMb)YC|j`!eAmdl^ypzj-ag89nTn5@+MpWugPo$^C z<`uc>+fSywU!SQbE{GXy`Rj7Lz4163O2^D(+ZF?@)TmG)xKqoE7&>uFt5sf6YA~_V z*;5;z^cuQN>>219%+?8P>otMP%89^XhJ0|W1%x93%L~(9?pi~&c-(_`Vkrx~6sx^d zy>eG0%g6usGmMXrw4T`N;q|!oR+bP6f2Y3&)RFz?Y6x6+J^gm}! z*dL2qV7_y?XuR&$R}~|F(&{fJF3@B0s25aqY0PUvPkMMQ4}h;p;uAc?_)An&@#v#^ zomG7BHN9;rT!NYK4M{95*)2tk4_`Ou;@m)&XYRSA7{&nwtE}A&sWcJf7@Z(2jENLKyb+@V4*1AWibrO5# zY2|s^58DhN*!7E%8T(wiqsQ&JwSIE;QB3t3`loxwUm)sjknmPXd;8!ocrdDXNvrbn z`SR-kVO=HLiTrTbX2Y3!-g^r({>I`mfb=*zNJ|*~;Gg$c2gi0d6XI(;emDK`mF|6k zI}w_AoQ+J}zF~`2^g#ucSF+O zN`4Ko1WCivuCg+NrJYA!?iqr-RH@2B0asrSrD;FQmRyM?HD&Rq1L2fM;tnM<)f!dR z%1Y@*oi~y=d(~#T72;0M!aDSK?=E#MG!3a-u|FzW6>QPX{tFIj=y-g*ye`La^7`BO z5>wsbhn6bGKZ=i)&$zkQrBp#H(xWqTlo!2rak!zN#pzQn6jr`p2ancsE!()lMIz6VHsv6NPd z60qTrsT2B$m6YUYR?H+i<8>?O^4CG{4%4pACyW$0Az)uE8z9AgkIPS^@y@Qd-=Brb z-@jK00fzcvm8Eg_wa|37!BjmE#qf#%;8w|?K12=BB5&IY)S%ki|Cwaq$)N3D@pxME zb>PNvWB99tws(jQ_%Ojo~=O$(3=h^bL>>@LlS-!NB-8djNz{v?*4~ySCVCW&S~eNhEKI zTyU^RzU~#KbrhcM&g|=+&9V5}Qq|@8l?!8Nn8Xo6<|lTGbJ0nri$sVtQ0niH`e6>U zTzeW<+2A;td|Ta`!Z0-3t{#swzV;1u(UbvXN(=E)Awyr{3hid2qzsN}5dg+){K+qT zWbp&f7EsUd0Jh3@{|z2ap~_S>m!#1RydF-7TA_q`NodX5ZVzS^$a`$nh2g4%>p~vh zuAAKFS0y;}o}JM)LKi@-8nZz_vm^Z2VPs05}dimks1o8@2*-R5?NyBbCNAWf%v{ zx>J^1Y%m+bfIi(a4enuaxS#wY>FlNwL+xR*uNGMbb;|pnk>+p=C}5njJEH`U{ip3Y zYxtz>Q7k#P&YC-g-)-lX?S%6 zjEORYnLGzk_OX;Kvd_4!r2~*|CUv)&1{5LG z$;y&Z@kyE+lC}z#ICJpLr`ICZiS3P~*Tsy-JVEZPSJ6}zQun>*unszu2k8Qm(aT=9 z*3a)Op!zX?Uhm11uYl^a-iA1W%Lj^R3B;`dM>l5fFGi3_+(@gJWmQWK!l0Xe2C-8B zK*T-%qOxVKC4{bsC~NrmVF&W>@M9-=HmI>Tx`gf$&Zq^v`k8dR5yN;dwSEOjrhRk!_~auX{u2I3hlg#sLtc)J|h2p9GfPV>uf!iFQ-2_B!ZLq2hQ)qr_#X*OQM!W4D!@T$N}N)5!&3 zh*wy`9t{Jc7bBKRHnqAdAu`5fuad$at+FkN2fFeK;U%xhRNg8gwD|vSgNSx zGE1KU0YmBj7!pj~co=QNTg}pfJ=~2kccX@!*KuKnoH%r_Jb2soPy0v^eocs)sDd$- zWpLnnWaNLD1}Npl8-|}V+l!jTJN3yd)L?^HRUIvptaBCPyhN2aBlPyRErq=I`c*g- zy@TDMmh{~hA+Xqj^N2!?M{BPsw11N@FUZeV%S{DUX#lcP+IQ8&8i%k+!hy#>H&I9{ zsg9=~;IIU*6k~-3dPmf}Cff`Xc6GE8Z7j|MK(RmurZfB}CV@tyPYYKHncm3ojFR6J zCKOVjWoe8#qtq$9()=gB1*_wD!~pu+6u&ma!*fj7QLYWqu%F;?K$9L9JUVS;)|&kb zKD~GhBBqIW2QuIHy%{YUgS!oKy09S@*=Xx-Rrjk z$ixBbX1oqZ*iZ5X{i{PhXM~M9AxTzD=P|Wz&U%JzmiWs;z^b^h6jI0f zqH`xwFuD>nO2>L5{ixAjiAR^n1vAm>f|$+eH&<*HIFwh%yYBhzzcJR%Py#j{VVp)B z>8L9*aT5g=8RBk}!GR5PzBB_4fyUwft?3TD5=m;@&jNgg79_f>jqM zQ^X)U1d_hD%uLXo4SlT z_owaNU0JbZbjl_`lA=%f#hk4xtU&1d6Z%vuP*oHy~4VKyD;`$wLHMx;%* zmZ;8j;}USt6&T|Rig@kk5c*Rm0s@ibsibjb=`?>{eG+|C%AgaqW69$`JvKw#tE4uF z^ji^K*tZ$fo9bdD;>{&uU7mn)W1Y}zq*72#S)xG-D&wYgylvBjSP7Zboi_IT9H5t; zbZO=1#*VLNaIjDMnz@=U!5Y}9=hckHy2uP^@50Aw|L&(J)^|QW_c`p#2}3a> zR$c#!O?|w&^e)rV`?)DmWXv-DW!e^L>c2Tt}yPYnv&Tt z-*FhC-vRiPr7BL)kNHatvb>QS6KDk1lM@)boewfwH}4Dk&7rj|EHEz47yb#m(PuvI zrEc^K|I_WmCSH@05&i`9&ZxbgkRT@wUVH*#O-PD1^`kW9DPeu1HGRVnq4xw$=Jo-F zQFJGIuehi}I&r7af^x@T3Exi~7?8BNY!a=Fj{6LD_yId7`$l^CJO&s34wYN*bRP7u zGIo`528b4jFHl>cUmA^V4HDQiqKvxSyWSfUO5ZvRvI5g6Pm~>@MRbpHi1KLq=Ns|i z3ET=wTKEgtCyGI8kMj^Vk$+O0JipVfwh~h!QT=OKUFgg}rOtOI?=7)|kog?8g92r& z08w;cil8z3D0|w^8dpo2s^2!qslmrJlA=NvPRT()eB)` z%7_Pm|ADH+oJkVbjBEb=nO@yy6Lt;x8_z?Q8GtQl-k5+q#G)ijwFWb^k#?1q=qcK{ z&VmN)P0gM#9se0k#DE{x{D-@YgesI}HhcV^c)$WqjqeFjFAWuPzTY>@aXzAeMdm!a z4ih6JAckzI9v;Ix8Sv*eX(ZKh0CfKiD#_4dU9>64^tyyw1=VeW+V75>sxS_+>Td>G zq0wczVw+P#emZ)hi+F2#!~*7AxX4)Zu;qZUIlYtoTzF}4<2oaCNJm^FIj#{45W=Q$CKzWf zRPy|3fq`!H6dIz`KB0+%M}6{+J}chsy3%_k|F!XFu8u9 zsF?_gD4w@#Scb#(8O6RiSM>gT9(uo3?w(fQ4KXs2!?m&;2{gH$f*|*Xq6VRVsVrsG zkS040atkhkYmx?yx$k zRN8ckFTVWv2?+uCGZ8&AyewV(x&3|h`Z2q2%JlV5JN{G9iAD<@VED?cS2uERTyg#D zg05l4^IBBHb0UNHH5)4qL4_IDkPUa`(K8^)c9A7$>FtzT=Ci5yBD7#Jgzd3~*^QJ!c zSyA7h7F$Cw;;z1@cziFhpaE0-9Pfo*^sJ)x?Q1uresV>0%5;o3{63;X^6W?oH2ZVk(xZEgl-Ku$=WX9rzWni-Q{hcB|q z4OTAazAr{o|M5#FL(*69yK44jl#|ggXMPUy6F4`TZK?ce3GVhl@JeaZpu)+>Qt9Lj z#^))l3f<&B$hdr$L2w;0)}cIFhP0J21-%_^>7)S}x!d{SI{6sEnnIZB!Z+ymNH_9y zIU{(UzUrhe)w(yBQbT&sSl|S8dKwZQQqNC#Re}2^0Z)~QHvwhzWtxmjz}g$wl3tVt zOxlj0rA|6s`!tj0Kx3x4=~whUTT4DPdOFTe5kXuWF!bl=i_Y6SLi5JYx)k!K&>s1d zo$wyHR^+U^}8x`Fv{rH?z0E82TrnzX@zAbe>JLD0aF|A z7;A7AHjehYD)n?IdfdRqK{*V8YNqd|zY+h2nZH)PJGSB1t4m|I!|Cnbb08dE&mZFK9mH7DzZ?riz*#g;D9)F8*1PSf-hRE5&(os8X0pDwFD=`ymlnmOW&LM%Axh z^ClMk9;9g^OvZ7<&vvt@M0&xhn(l^P%7k|5!OAD8Yy$WB3UAd?bWQ7F{MHG4PI;uC z{~{gVAu+yv(AW^@DeVCU3WjfrAprxme5oqjS`MukK3zNzu{MJSi`KYh=9FIU^&!&z z^qrsG)rjR%d5JJLcM(bb=a@9U$w`&E1jLNMl{^wNdMr2)#3-jrm}W`z{0@xS;rkZ( z40C)#=|d%mQxNwnn!yyrS6(jN`VyUNlwRp#xeI5(5FP2rYadPZ!{OGj7!c+Yq_q3H z$#Iw3Yu@>Ohv79Pi_|jwV!-V6ipDm`G^V6Pt`-5D?W;!c-*jpqfRWZ*iqy!_9GiZp z6~DPX+!o^OLFTr3H}d8v-ZlEipHMgz#K`qT`GgcY^{T+$+C2 zdt2w>f7kGR+$;1ev9b*M)X|yDnMup8L`)rnf4FMxAUR(E zvXMtv)Z`h%#U?B&eBRzT5&H%V?}Ji%=?d~;#qG*{v><{%*g%qWo%a@}x%#J3U?QRW zA7H#b$mT{#B;f_Jvm@=BPf*%p{86XP<(@$9tln6jLM22oP+Y`#`Ip#hU)C-yo<>C| z?=0Xx_=Um|7rvL?4&=M>hrKVma_TKxC@exc%e|<%_0QtEB5w!#8h<@!L>~vROesg_ zaC#<0RvtHLytD2$9N?UUX?g&<6e5zytSG0E*K}y#oDqsplesT+t)qW@Vt;8vm$_dx>RG{pfrm^%V)!M%9LH z`DiUsbtkIo@kYvzyFc@O;I8?Hzu2Bqt{*5`&{-biUJz?azDClSR6~G;>PVTwA{HWp z+BPMKb(-c>wW=nrc@qikHZevlV_61@9?TG--3F-NPK$XEYN6XRsa-uS{>?TKKk8ZsOR^Y&4_0*Ax{+_(4~ z@t8)+szCk(OCq(DnmgQ&jato;gZGjq^Qa3vO$uJCHobZ7K`e>KnM!!mkMAadd~@qx z(Q*RaLTH*v(k5&X@A8-Gd!gjbd=!u7W-6PyLU`HT8(Z}(Hwt==v0y!6C+?qSHt+Cq zIk|j#_+)KH)?PMM$US)wBl^$x;4CPfgoj>8G`;v{f(=G)BVEqSiXA$90~4Yp%|S-Y17;xxmN2#?y=?n> zq6570KQU=6qUa${wvM@ETJFmkb*p2g0EHEf*;!lTAlLCho^W>4u^fZhZWG6sM)yc_ zJYr&$XlG}q3?zsM)B`D-D4COh7fLEgJud69=#=&$OOwzn+0sE|U4Ra$mri@py z0@I~M`Y0Seh!DWhJie;guieEGl+#G!@u=8s70}%xBane5JF+#YAkwJ)uxz`iOToJa zu3=@ccJ-{NZT}c|Vzk%RO8HWM5^8{xV$A&)p{E?R^I@Y53X+trf}9`N_o?bGfRpzGevC?M?3j*v46KKq0Z6UpL>Wk*uf6pDn5$NY7Oo7>i+&C zh$Cnz@h)C;B0W&|5q>iMYA|z9;{dk+U2vN0wA)1`vQc`xbNb3;z zHdk=Xi1+%};h!f&=ow3Cj`k`w!#ZZexe1?4UFf}Z7@R>;gxQHpy8-OyWW26&mc`7y zyGt*g0jlKJK1Y1GgCwhC0Ar;(HQ%UXmn7^`7%=$Y@+lPBP-~G2ZZY07xi@4hZTnJa z?bc{ahYLzE!(-mRo373-sO(4HEzbe2G*eB#L3_=W}ZhzdEV0b`S%dm^G(0Nr*5UgzU9@nJ*)T4)D`hdu6N)KzW zLg4P7ifg=His63Jd5J31`3IvOu06ZvD1K05cR`Sj04s>&v>k%K#TxLTz3^>Qe8y%0 zro^~7nubA;UgXhIm+$nZz=q7s=Avx^i)Rm!nYB0T(3$US>&l~n=C;#7jzh&dTq?wP zQSVu;a0td)wH+D^A&&3ia0J8&lf_gGMGz&wipmSq85FV#fA)c}n^kK&%LU2^YF|>- z9E@#egDcW~>-`blxH=V#J`9a_swlr%EH?#$_SNjpZ-RgNz>H{5B90-qt3DDRbX*}e z12#DZJRB)TthjGx{lNv?=05aqHew;0pCYv2|)6w}7WL?nBidHNcPSf15Hx zfal-nlxFeRVcF|Ar~N@7>>pN z%{MoyV}I>_O&1iq);A?`V$?2B$*u)_h9*391~ztScO=HfV>_k=Tc9|1+#lt)#lUMMLr4Dnt|`i8~d zAzIkvB!5h$P{%`cEGrJ?7n6b8M?LX4fWbI}22<(0>lL*}4Pp z`WNMRFrnnQl=VDLEm#qL)u=lL*^~CZ-XFoWQtF<<+ z@sv<<)vuc$%L0C6&gJ>@9E9+>hoh%>?1WW7F*I<@e2c=^T*YaBO$`2mrlR5p>3w4_ zK_qa>UD!y)?G=7YOg#!Hz4Z7vX=B!Ps@&<^#4tyv^~pW=CO$TxC<%{r_i&y1`NAm5 zWg3(Ka!PKXRI9v*h|^<)lwLr?t=)f=Ugon-FtkuzBOIj|q{ivxTE`w$^0m-uFHeQg z8id#Gl@9{~9$owXvu)=ECFYAWG-WW{nG}WTr)_~$NE9Q+y7FNT)~XdC4PlxfjbS$C zc#WZ2+oT)hi+9-hEb|a%Zu-U+~VYVOTNvpQ1#*tVNvpY^tR7#O3wTE^izd zY;NV7M)vB`YYSDK&7fWBSVHqsnd?Vh21ciKu2aV|g`OljAe-*H-&*yzeqGzOuLH}q zIfEeMu?~(Q9C@D(jF1f3wz2eA2XtgSTOj%zf7CxP3Xo%qQ3JDjWlZz@D-l7TAyXxV zZ;KbH4PNmV>E!oYT;(^16KrmGa9I>;d&Z{Yc0GR@yEqgF&r*Nw^YEV<1`YaTXMzU3 zNuys8_mr`;1j_3?K<;MmSpj5Lot#ATC8mO%VN%6i$0AtkT(97?@h zU&n7+C+F&|s!c{e#eai$Aq){TEg7Dk`23~-WyKRRf;UEMLDtNfXZ{0NPU25@MQ^J5`_t@)W+IJa=}hdBMA5mqI^e&YBu zrI4%h)*6zZQo;2CM3pN`$w2MTykCr%(=NsGd-CJcs$echa_-l62o`#fm+5^sp5^Vr zvN;DAC`ktXQ3qY5+M$T-(=48D)NouNNXAySsf73NV0rJX#X2|Coy0MO0aoOtV% zP$WZ0e>`~CHY9*=7Via#WH`PMh?SNga>de$>IQc<69lF8=_dI|l|8Wkj2L;z z(H^1*aA8C@n!o?q2KWab4vq0S>(>A3x015|ur4Y_tZ7A2tvJI?EC!jzHsudRYc z0Yinu{dLcs;3=`Nqww{YTesq(yy9?SEyJ3)0%GmSM88=Z2fh>j8mVsa$pAEP#!b_p zz1Mv=VcEjAy-5Lag~lbtjf%g+`x8!)bD+ufuBBQ&=wQ)@OlCYGWzJ2e_mZ;sL|f?` zId&2g8)sT>I#OBXjQCI#f+Q+)Fr?5!sW)-3nYAHkv6z40!R1Qx)+F)ZScrAuY6M39 zK=F~{5fWxt83F9yGG`llq1Bw}Yz9Pl?f^SMynegI+t}ZyANcV>nM*dMG&MT3y!j3& z1;Rd!Xb!5G0_)1Q_ND#rMRPe-8sU8n;?3Z4l80VNxSEq?XUIT`Uvpcg=zd$od{#nZTsuC!g8E%fDMPi z1uy)YA)$iGR#KsHR<_kABRA>}GA*L$tRsex?%(5H5A@F5Ly_n!CHS)=vAL3R(?0jR zYN>DAQ7S&=DxzKYjm)#R%zp?U}x-W)c%|mIgwDVv3LVZ1o@hX*te$f*POifGqpY~b}I4c(0QyN#qBW-X4LrQ(oKe) zwX?rV&{#8*g+82UeWCld;!x_f$JgXrB4HN8G|9>X-`fKSavo^T^P>@McfLzOl)SE9 zy6W+h=jk)EuOy@^1O1&TfI%O2^lNFkR=fvX=Z2z{J#>j6-`pb6Y$E^Sw;|3@YwntO zX`&ZIcTQ}JZ*ZN(J1KthMjr&6n`%&aGadP(olu|*{Gm{I`%z;mo5wN{ESADqWrLWp z8H@b!8fV6niqB3(j3Xnoa|_P%{y>sGKXd^#gU6pf&g>`$pI@}!Mope50C7AHhr}x~ z{*S+$QBmE6gMD>pl#3&`O-n+rw%a|Q&4=qK2`HnbfuUl^DnDe?M`OUb>|{LYcPIvW zrtT=aS5Y4UP2^K_dhC;XZr#2t@~1C!&$~Jq9rMFlp@Z)1I8oraG!)g2yFOcaIkBZ5 zL=TWxY^BalPsr?Za=d*@uRF@*fY+1+5$VcfNiNLr3vl#WycDQJ;Nb<76Ddg)ux@KK z5keO$$jS0eU{?0fHNM{KNW?-`wilpV=H7{rG>_x)T{(rUK|d26=T*qAmQol<&AjxJ zuz<`>I&0h9!o_5os^^kS@16FW73K<|*q2y&74~K~Y*4ir@&j9;kL@|qxLXs(2r56Z zFyeP)WBPzZh-W@(3(1hb$EWOi&LJ4h+Z1G_wBNS3FrgYZ$Pu<~Wj_{^u$?(YiabhG zCr`VQweVwKPwaBV#bS9(Lx!ez{@1i^(`W1XL*57RAn{5|CG`HEHhvLu{t~sbP_k*6 zMwcE6a2zP4%L`#>m{h>}rQMZV^seRz; zg6bsD1QeHX^mJVm#7z!+f8>rA+`*XJ=+5|m+3p8h!N6Z{ZY~)twV*S@Djco*lIL4Z z7Q7)L=S*k0QceiNB%DS;VRX)Fs4qn(;;8XaMpIO*TiI1B6JX{1iQ{ina z=~?Z1Ej(sks7k!1XZfx?LsOv16d*!6tsJ6o}(CNph=P`0D9 z|4(%ZH;79;z!`R%qM$gTVCZzq^%AZ&l-$x{C7p(GBVI`F+S#V3e9_zj&TOR00M1`KgK}xaglE& zm|LeEQup+$Om9W2^k^NDcvmIW+2||=dib089XQN4`j4H17qeSiAIypFbQ>1w{FNBt zhC;J!cJS8xFSD#lox5PsmyFnsTvV6R(Ice}G@V25!L?|QrWa*`e1pqqZAh706Yu(v zj_^dd8CLvh;eH3cR&x!%2;*af1Hc`jS#_CdV^F8|3WXLQL6A?k*ZP(%K9E^B2_WLm z9MaDg!$)KQ{#}w(bgxAwUdh&|K2HD@=c>7-!SNt0q&|ZIpr237iR;_q#q!z%DaqItYiRroca^Z3ATXN@54xcW7!5eS@t-zgLutO!?K8&98Q>1pIs zPYLC(VlU<8_e?>u1IHDR`ShDZ8S!k~Nql6fV{f6)UK)&7R4~V-i{<2yA+Z}YPg^y1 z4fJxT^k_;C73|In+Im2HWzTDW{!A^rwX{G#Uc`6kO!_po>RwmEdG$J=W;`m z+jZfvDO)Pin{3C#JJ|j{#7P~=$waxVbZF~~o+W9S+>)`IMF%f}RBt?G+yVJO_kCzS zlQb_`79Jz!L9Ih|(gmkA<3;vJ>A5U}7}A!{O7d1Vu&P@+W_oXZ9PA>#Sdt*sRGDp7 z4117@5OfFH&uT=m`M6mAL5|1Y0&RXF6!n-JN7W5Tww)w?9}G5$6+$;wp%tB2=AVhV z3NYC7XlqO3!fWTyW;gY5eKTWg=T!WO)Ayd}T4N4c1pCK>K2GKjV_D_w84^f4J9+B1 z=EB%tZNs71J(XY6dDP2?CdAqDtN_Q@wdG7``-+{;)9H;KzNL!M?XIV9IlfJUO~Y<* znD+!q-B9TuDB1>wXu$n4V6D0-2Fd3WZ_uQ*xNujUf1`7oGAV4vE$VIRDFTV4r@N#5 zn;HJenA!ll+?K{(r;@0A=-Ltnc?yk_jN%%98Y->If-BLqa<(X(L?#U)=*~Slq`rk4 zUahg=$}T`9eVsQ2x#cr}5{E25=H5LIELbmMR5>&u%R9#pZe(+c7n{IsBS%g`D{*pk z*xO&7i`;uvoP*?C;R^k1Zb>nAKA&tOhk2b~PwXsOF@YXpEh;%Be0<6HvQ9?KvL%N6 z3cF$FL~C5O{D5Y{D>P`Tt2dGFO6!{P`ZR9sOK_8+H^jD}grtFd+GhLc`VK_^8AZk< zI(R9(5DKzavi_!VThKApgb8oF7xYu4)a5jR6b$u{Cc@R_Q?o?u6ZAjV=qo;b4AZ|F z<@}d#O!{BesELz@gR{Ni|A;nbaCUe8pOspj{Qp&I=#3`~0q>wx4XCSS{go0N?-ekd zP60kigQ=8rq|It)T*E$vW#VAVyAf5il+Tai_(cWFv7@Ly;UL?zTYGA z2)!molfZenKwrX{Zb}x7G2>sBBu`^q| zSqcex<|OGy!xf0a!^Y@hKkDNl_4%8t0uXmJ*kQd(aw^vIr4~Z7N)b?L7g5j^9ys(Z ziZ;tb=y)m@bwBI`M(7wc?V8716N@BI-Dv@NdLm3pwbSzkt95LHHI(C8#Inl#mf2uJ z6<5eU0-vR??D@<=ZIYx?f8l()k+K>x1JOEYFmh2{4jx9`Quck|T&3kLKH{60LG%(S z6DHQ|aC~bG{Lm_yi?>Wnx=Pzv2+ky=K(-_pO%^6QopM=4LsfrYj-=0_fMS0vLJaB8 z-h!^>Om4{B-Awe$xzlK>0n8!{?P#HslSczjFCwozzDTy$39-BEd*(*GgBH)r%JwE( zSjzLBX_9zW5#AS=|aV)QInM7v)b)UqtcV7yO~mF1%V- zFT~gR$=TG>M)^e({)7ChV22lI;f$>V4Vv2+dbL1I8V};q)LW*lpL$_Xif%r<3<`AE z{Zk*@Cc*gB7w5wad0!#19E$=b%K1V(TA`=J%fc)eZRENXSBbvtn~3`pzIz&NZ^6Vz zk9!WX0#a{5bVkqj|3NB%?y0g6`$sC!f&~I1{x2)f0$^+kaAI%<*gDu4JO3}!L-#*c z0WK$!&rWSRW|2?ZSx^8^Y!*+rwWt#+nn6*1Tcgf^o|CW>eL3up*Njq{R4nLY zc_?wUS?F)>-)3!+i**%T(Ws13qysl$MErUb^8^la-!rM_HJ8d`Gnx;0y3kgBi#A>iUeLW<+^X*l zoun-6<%f^PdJ4KiN&V zmNp))DH;pd^3mu+!d@#fY~TcTYJLB!ItDA;jH&IWY}4z-W)-glIjbIm4Am2n2|L zbtF1kXOng@a<48!%Bb|g9++7$S$JM#IK8c?nr=P(%q%bk{p458187P(!zGgb>S^rw z6V?9ZRq4ar;kn%mJ^!3x48@7t2eA@z$xoP6zUVetPJZADZ`a1-mP|a}Jz? z=>!h|pq$JKu{`LS#b*aTMwW#da!4F#_IYqC)0#NBF4Ty0QI9pg<$J$n!!H{y1LN9yUX%pU zBAC8i38^}Fmd@}L<_`pr&hV#(ug(>{ujEkvV0QnO3d`xg)wkp$3P#(0piLSUqS}Y4Z*9D5{4wp6Q>!|nSRihi4{Lm#R51btia_gI<5^o9f zbs^b022osJgDkkF_8NeI9PBmwf0#sliHPff1y%N;w|Or^+3p6cW$X$ssLO2*BkObV z53dp>!-?E420W$*%aO!es*P^LGZU9MJ}Af9g5kxn7n==dHlg&YF7u~?vS6X=<80d4 zoRM;*M1(+01!g=s5f1lUcuT!GQXh&or-;pgHF*XTfFIpj0V zG;2Hkjd1Iyk~HL^SZ|d;u$xBymj#X1A+$7Z4CTCC8y;2U$KPkP{&;&yRp)+Ox;y>u zfrp8zHmzYL0uAYiM(>T}7YvcDFStUF$R%1o^|E-MC_wvy_*q`{19H5Ok)rHZ6L>m@ zm{LqVboQ3aFwKxBF(+h{s;dF8VJt~2U33DV8Ltjhpwj-$rLfuBR|NLn^Kn74dwZ6D zd&$yFzT0YYO#)HyHCDmMn1sh8!q?LmN#6k}GJJ7fc=UH^vJM6AWcZWsR*_fqjvQav zFNhbMAosr5L@;yXBRKK_E3ex{P_53@hBGIog+`NsgvzXs`k1wZ?j-LQEiq`c9JE(zQF{?4)@dDh@;+zDuL zV<2`KmQ7Ulxp#mq^kMxo_W~^3T-I8Qda;3R^~io&L}e!2WRNRa%qkQMRc20aysXuXvPFHa(fca&h2dPPPQui zV;mk9kAH)+HZ8h}m_6d{m-uEhJdrSsODH!A*(wWz&amL-g{mKH2q;6f`|M{U z7D5mjoe?4}u_2}P?4+aEvakz|0^wJOHkgRWX}iqInN2MNM-TZd@NJUl!oz_pajM1w zMbR&+fbvi)17e-BN6H<1o!zMtw?vOeH#5j|C#-cM${(dagAApd)XUqKegNysgbkmm zU><4aS?pZ9Ijy=->NS>BuhJ;?{2W5(I*?p#;##8 zz&p`T#LEW(bqp~ZoK=6!gxx#+M`v-NvhX%a>p3p+k~6k4J{dS($U@mYB@@m>0plw4 zc-r`CbfPRtS!k4lcJ1VaADW~FM4ZseF^k?w4Xd$xR)r_9D4}(C^L5;S;&3FK?epc5 ze?J@bBN}Qqz+(?eqQ?+6G8GNetED7EI4UzDNSLEY2BqseS?kQoMw|LU9Cz6!-t8(l z6^N%`7(jy})KZh6545{T@UhTMs+pjj>WJ>qhU4N`T4JcC>*sa_CY@|Pc!F4j`dMZb z(Vzbwj-@JU_UNarksvNla4oh{D9WqY?nfeYpqpZ#Ii99ao7b=N)XRl8jKk>Q$Z8Rf zfu}RA`p;l>0ZNRpuN`S_e_^BkCNd1B0QZvVcGU3Xz=30=5cR=0KsQ^+%^g@H2gXyb zN#3w$S!$!&6X0yZf*m32#t)FDn45cvcamn^XOrVtWdJBT5-V^_8ky$eCS zjLAIYL#%mbjYN^_NvkQffjz~@=lE#gR0_C|%qMAQ zvv_em5V;YitsZ6nOmX}Pm)c|R2(4mzTe+j$rtDz&Cjusu476utCN{xfnM=}caG?AyKU z?!(IaZQ|6~y+Tpo{OqHEK+nJjFK)X#@BAgrymatR?1fR`?(r87L5`jx;|S`LzF%O* z&Rx{^kR<=-$dWr(#_2>oL5^Wgj1b)7$XC>v1y_I*1BCfXX;G9LK0L_#zEj1t#7Pl# zs5~QY*XcK#&znXb;z-cTYoi|v53ao6y{En80&LZv?O*jhJ?C+yMg33OepR(KBbsTq z4rI$`7A#yBanoU?{8h2&Pq%4%Lu!iKFEi5?!p)52r%NG=ln$vy-(zb^4=P9x&TIq- zCHU+eb-hw7We9M&E{}&+Tv&5)dxD|yC4%3p^=y=bKOMtFU5lk(58s`8V$IJkg%>>i zC2%z(gTH^_M5gQI$(0nHJvo={=o{psG+MFX{eYATa4(yup=zu=nXdN3EC?{dq0FKi z9K4J@nTqLv4leAud;75C>l-vJeM}(E!n$9YoKsi6+y{z>L^6yJMo^vkH}A|5ZX@1# z%i#dzpQN!-Eo_*o5eGe$cGWal&ZG7^8|OOrhK!`_86(ES=1f;LzV0kUKqVS3!vvPQ z%o4_E`cgitGHk+HRpNk975#u?Q~{7C50s?VR8gfAN5h1(R1Q2Oasj3!-e=+ASey!{vGCcvJ#eAT!=p}Sxj-=wy%MQ#An84gwaC6>X zbkiZmRx;CL18ih0+0hH$+jd4Da_35+?YzO3%BvW70#`h0b8iij5|3siWEm><8p#* zWa(BH$zM13Xe)CC@dRk*0*5X#Pan*;C$B^e%U(`EdZ>{X$>-7SNU;(?Vu{`GQBje; zb*=^8je0LtKlH|}+mP#x$#A#PS9$mksr}g9SE&|Z3|o7?Jpb{+5-QT~3j*6q z0P!PMZU@4I7YUm&SUy9-`mGTLH#Uob`a8VJ3B}0os9RV_5BkTbX*DKXK1}Eh2Nh!Y zF9QW~7So8{vg;%ehPEB@42nV;l0^g}td7|&(&+92PT8Qx7TF)zuWv`;HD2JK;07a- zOVtppIsx&*hYbjCWUE|O5(+yX>uv8v& z7#1=lckP^mtPG~_RdZ%uAkgLEfGGachPSws0mNsaqiE0dXt84aSRCB9*~$isS5W2M&M0fa@JM1cKQD_iFZ9g;NNIdB4)H$+ zNn_tjzmA>zua2DtkDoym6G1At`~?RE{hQ-*rqY-k@gaxKH@&N)fhhIM6LP6oQ6O;3 z;tdfrz1sepQlXQC%O^Q1o=CJlscOC1;ARo>(h!BmBOKm6uVd?v^fISBMAvVGx^A`D z5+-=TjfW(o-%J_{F_Sip@oT0VraV2l$M+nuuvy7_8J_ot(8ChCSm-R=xjjU~unLTo z;Bw=Sm=xkw@SOqF`wlJ#7{p;XBvK4={a7>%8o=tsuBAa%iQ3;(4r4ofLx+K9Zl!6Y z5XAm_t2m+C^b^0ul+y9w2wH3}?XNxPxs@p4R6ze;Oq1dU1~C>&c{dgwP@g;)>AltS z-x)bSg5fVtEgiJNmAxozzh}U(tt7AG-g&tC(a~UJ&1&fCwHFe_hyo+kKb>qXZ?QLY z&lnC5(vq2uHVXK8@QR4wY6@)p5){blJ_45oDBlovm{+5V-=BSJ0Dv>Q}bR)5-aU zQ_^d&APnmS$A8Ih@Ahca*=N5M>F4|Q8R#O&RSUKHb3HyTb^M59gxwnwbmXx3?IzQ)r=I?ZXNj=|IL|HTi#WB9X@w0%FI>Znp$)KAYwSjwGjT-5Tt@>Z{$qW@ zc(8rw6bbpAV+^K-*Ya^~hd4y=O-HUS}>@FMif3Y>{TeO09(RWL3VS4A|0 z>)@IKPSVJHuh3(M8|p(BsJv)e>F?7vSYyNLK;YT89#XPdQ}rK^Y(%kgZs2c!z?_!) zOGXia#>YWDPCllM~=B={oOP?c&N$-*; zXwSO9=vP{AZ$VeG+S=vUGsB{R4(xzUgx{iB3*qU|k(PCe@VtrV zQ3p{!nuZF?zXtFoo7IdhDB;yQpG%I$qI-hn-n|Q=z(EqA6 z)1f}7ZG|aAqQ6^p*I8=VYptt+Gm0uH2+#yK8u&TKSi{{(yxGieeJL9x@ZI>UR@Arz zZT}YBej}gQ^l!`GUL2Ft310F8??bCPujkLG_PT$>5RMv)&tbKJtC+FG#mCaPc}NX@ z3IjFvhg04`MVrQeR<>fK5V&#{w-=jg7-y*(~DAv2Vh>AGL|~c^E%yW8(Z}w0aU{vG@}GaT0hGl zohwS7yvU9?gtgGE$a?{BwDQEQh+ za}$`7jBy71@W+y2d%sN#rVM-B0SJcp;4_GMkdll=nEiBop5OaQQa-f+?0ux`2!$nj zIG$NWuTQ}*m>@bKnt5)1YM^rU3ITt(tXI)Ux;GTm}cHVQwLlWwY$IsQ= z-tO+3!|OS{7GdC%fYuFRH=oa(AU4xQ4H17vG^E(o+CouUEkayw&#QETUxDT!jlsqD z^iKcv&Ie-R7o}s@i%oN)IH^#l})UOFjy`?`CM|n@;(+LC1}mwhkC z)uOTPDtsTsC4-Dzo!`GUn$3!n#3S>2926r+o8{Hyd3~;T?ir};HZj33A;#2^y~9;E zN{+IM>Aac5M>Hy-^>~#o`-gbe(AZV_uw@vUMjv>3Mi%YdTUl%mg%CSsvA_-U`P3^stI8S}#8c$o+G7Bcf4} zQXxLPQzLP5O;BHt?o23LjylLa{+gjrJHl){MW02*ZtvJ*%2_zr>UbR+6~+u5s7q4* zXHL1Mb#AH|9sgYJ7wZ8POjmX?v$&D*&W-aRT6bV&6pOl&XsCLecvqZtaS6;-vg#WB zQlzjGDyD3^nM?F&Ojzyj%_xS7YVvKZUq|Ul1mu&*?gwm5v&qdLRu?YRx1F5J@ikvnnt#>OqQ%3OO#zA9MUBMWujWEf-5!w z(r-vyGh=%cH}$J=rJ7xh-k`LxeUnF$o`Xa6?C6Rr_1{6-mneIEb0goY!G&4_mjxkA zGm+ywDWo@dZsEr97odPKwv^*F8sd6NgQSCa`R`G3TIzNV*4`@_$i8 z#w%nfB73r>CPh<6U?ZSDC`nm@&7EnpNS$n)&Z&|C=_jF;iL^+h!-x5T^+e>Fl%P+t zZ6cla^SiG$?87pL0>Ozk)6@tM1y&Hsswi1lhOK|kqBrb8@0{-;343^S+MIpbV&Px% zJ8XW)fJfSt4w+)Z2l017J^X{Lhbk%WL(pp-U^m%5m z)KrVj)a+Iu9E8wFAl^eh)#QdlsD-N+02yOBPSs&uRAYfbg`)%ME|XKx0@|jY_!|;? z&NB=tR>MrD!PItLB|a_Hh{Y}Trl0IMC`3Pq#cC|q$eXZJsw!N z1{XB!Akl>`tebfl1Vh8gcsKP+|wF%FFu}y~fa=5*u;z9Zo*5Z1TF^ zjYMTp&Ffo@kp^5fg~9Jw@SmN=+p!tsFT;#ZM#&PTg;Lj(Us{$sS^L-xCpvQRp0|`0 ztMQ9_dWMMMqds2bUp_DHd?s{_867x6HMIp0=3f^RvNX|>Ne-mas z8fIYzJZjaB(yoE=mx=yifot(5Q-uP{UMpDB#)@p026@jbP!m+oL+G3Ae6 z6;SlNn6J|m(O5Q%i#eDkwB$^lj{gPN~$?fGf|2P#Om4pYH_2}F~ zFw+iL*+gwYZC>1NBPot|wfE%`)p*w(yR@d3J_v$kE%&*&Q!GcHT2ZLBz}yXaPLd~t;8$hM1>K`^Cj3rPQZB*c6$HE62q*g|#hwX)v$w)fF z_~wxl#NyeIEpO8S!V2J7(y=Rb;3YBs1`}vqhWT-JbYnUkCdMWFKi7DVOPuV-T~67< zdIcZd2#b|7(5_HuC7&V~=ADQ--rbL@@pT*}1H#wV*?|-1&$&x>!b2JAbvZZe#jduq zMan9&2L=(I>Sfx00=qAlR8b((;eL0Jlc}y6|t=ZkI)r+5o)2zp1_TscqcH0M9 zLV{&c=Oz8WS1y%9oDW?D=AnYr6ch>?&d0 z5A?3DuX%`}t(>1q83KC_$G5h7Tz9N)fy^^gb5&dVDwOf3rT7wtmjTYKt7r~>}Ni#uL z!cZ4^Y%T;ZT`b$++5~r_R&Pqp74v$#xLX)GEs+_i6#A3J=`$HZR4uGdE;Z2B?VLzU zp99rEiP5Cg2i<0Te2NLFaiNzST&l8pwAWgL$;#Tr2d7SLx7VA{NuC7QJ)mjJ<1=4sl+hl4ly=Yk70 zu}Uc^R)!HZjVjkwiPeFvdG;tB(~4RD9B4i}TYVFtB=3iDI*Qq~HogZ!bRKDura?Np zXnaiusYmZm{rz8GK5_3PUPoKNQNiqgyRA#`IB&5FK@&92g6SY#Qhj%ahT$}De16Tc213*vL--wj zLgtI!fzZP!Y``DG>ZyH?u9ZPwpajN-`R6x}q=#{^0*0t%sTHlC( z>xv0Rdme=*o{bp~prJ654He_P+K6jdc)FPe8T&0j&>@hMC)9~iF^H^S!JN;ECA`GN zf&d>1e;K)7s&W!b(zGoH$`i``nxSI}XV1C}xr<=D9Po+OADou(MSfaOT6B2`m*j?A z5>6i$LXT)>>EhXH{}STrf~HS4flfLm5v#)8ofBe zqTHAjG0XPUDHZWCkog{~uTwNI#LhmAqU;Mv+Gg5{zQHlMduLUXLyNGkf;Zwv)n~A) zA^(#0Tjwa4!c3;;)+5yR(>zcnX2M8?z#w{-R5Zzh!zN)5LmO>W=M;)`h55cZh>2-> z$V3>FPT|{4zMW|2uNR#0uy@|TSdY)fErZ&X?0DPjK>%*m8cmAAPi&EgHs=&o-BdJu zn%s3Fy7=@=S0OU&2-maeV`jFhy3b~5&|qtmRSJmfsM2pC{`k&5WZn6?8?SjM7^(-5 z?~Wr(X_&%_=cH(!o(SKcWO%m*F}%6>ZkJJRS_OT3$JO01#H?ZA6Fz%#yC=l|IP?EI zH523iI&(`q6B`#(!2c^b(>XvOeijM{NF4pY+F$<1%K&R<28;itUeeN6Jdnchx&Dl# zeK!VfH_lv&HVX^WPr|C>A=BQl#HrTMFxCuTioJ4437E9_{>aq@P$GbtY3PvkIc|02 zl}>l3EF1viCwK*RLwRRgo`CIjL{}~>RV>aW44!+VT4qS>ns#jQNtB#cbbzC zG$z|6R_X}cofwxSCJr>uJN?1Ck2(aaRfH_1`|6iUUh0@8NXz7*i;^mEEJV>KxM@l0)Eur%Mod*48 ziBUG>7fG%90b>R_Jeo*0>kd!6_Ls*ovOk@i8vV=URwnWj#oA*cQ8jj@RMHVCnHnHO zlVUX-?X5C$dz0G+WZg)VEUX{e#YnVcHFpmLTEwx{+2yh&>X_EXDQV4twALMIaOKI0 z!E`EJDbWONEi}z!Sy;0v|iqquWJ_!#^+3g&JBelGZPPtJVzf$^JTQ1+C{yM~iRmPiem zpqzh?K^u=1u17yOV7r)#Erq_PY%WC!;lQrR?z@rBGT=rY5(L|wpm;1;VzkCqd+$i4 zBvePzeY=xN(wq;#2d%xG89(a@x3H7aeAxoHHJqT67^XIf?!zAHac6A^CQAw=@peWL z!%S9s?<6R1fnCQw&gDs9YPasvZE=p>(V5krK}%o}dpa;pW^_cmNCj_069zZFq(1_R z#3=U8Rtp@C;eC8>dpw{f;IFeaY)`*_BM{Tgk9IrO@}UmzU7%eZLIQB6L2p94B~=FA z#IfGy`0h`wc<}2ds>mM$YrkhP<)4oWPWluMaUNy^7mcxJFdt@5$Of)PVFTaj z?n{$Mm!0^##WQ!SqP9qrZqNP($3)8q@h(riF+Dk)KCcgc_@ml19JfS3C-fOFrUk1X zvYvy>qTX0t8hRuZj8v*>IA4V2-VMTk#sj>qKbJ#}+M)C2Kz1%o5NBh{8Q;g7+I{RtP8Mn6!LTPmE|0jPDLRENg$ThjZf{%I;zzZ`^8 zOj5)`if9vUPe|1-XzXCRmRaU3&}6e^g+sipI3Eb8tsJBplE>V}`xtXN{wU>emg|{@ z$l;5IVynWgD$r2KYiOyzt&jFAkvJX-uT`V|SQhU?`2y?apYaE@kh;>yoDhLLEmj} zk*hN4wQ8j9A#CsZ&_Etv-5dC@Y!>fEksGHA1VJD76Wq?GN04p(1bV42JdHo!E>Bi* z4L-TodUJ*hyE=g zjZ8b$D0HmQqrh2EcVI(0!ovoyrZKwts_}E&Rx#-&gHGnIo}lZ;|?v1c#> zE9T`~A*3P5aEEGJTeO#28J!Fy9u*KI9ssfkQnG&%UW5c%V@r>GfI3IRN3h z=@uaT>@mFezHP{-OMszwm9dgFf*_(R=wKh-i!^5}0j;6qohCEp!cW+cDNBT7?MfP6 zUd{z``u5zr8H-*cDvmt*4gQZjL2yhAO24tmmyoFm%k6>iCNlZjr*Mpl^KEBo?+;r-KKr$((v}-4lnnO*H|C^ zYD&j&DU10o6&Hf28IbTnDTr9Ssp(Vi+Ci9=qS zK~0G}qs}-cQDyCiYRtZ-sYvxtnhTbX<_+gQ;kz189a9#?PTETN%QEsP-Q2iIhriag zFz-3wY{1if$Xd7&3e-0thd~B79PW^Sst4rO@#`$;XtDX}=&-CrHH{k0;%E6u%Tx3U zms>>tuXWS%$Dctn-4nmfm5dxz=C5(ZZs_!0H5MR_BhUDdDskSq%uXgn(x?(CxfqY^ z7VeF$N5W$*iJ`um3vKRd+Cy4}#Zz+xMpYOo^-&FK}kI?@y5t##=?H!yAfB$b|QHn;t-2n#@|B4~= zYBw2db>me**fE)Gw7gKNUV_3eH1T|?CQ%_0_ykuDza4Cu*lu~<{II`d=EqZ6BX_f$ zh7`BpuHzfsc|E(CE$i+}_LVvTtxCeBG1nCs&aL+9rO~xrEfAL&pHVm_wKc|GOzhYs zf$mq$FXR4=r)nOz;~r(t9~vvf>+mCxrJ+OASm)zHenQToOv9PAl_Iy2tF0+J>pj0g zN3zD?eLlq$BDQ*;JYV+rDtbZA~C}yPYZo zD=4xT49jt@yle9i1LOdiLdvSdVFcg>N#?c{-|!$=PzKF@z#CzTUbn&XYZCS&BBW)W z;9g5^WKy6X+XOB_QNkE#4%QpXJywlD>zNXeE>I!+JV$=KjzPWkznwuztJgVBy@g`D zj%bTz`<5E{6D`%u`)lUVi zpZ+z2@-2P*S%U*Imtk67UwwD5lZWf(a*E2yl?a#`wdyMl~&a3p>%OpHSL{YR9q zJ-tG;H1sE|Wq@bech8{7tyE_U$j&QFZF^T+S(g2ANu{J;ReURGk>>&(F4EYVWAx3v zutWSJKcP|gM<$241-GrcqI``>MHMr4r*qa}T3>?EK^%iibASB$iW* zIW}&CaW^%iHpyW8!CPAajk2_9rZ|xX-CmPX9Na|Yggn~-%<~6M&McH=SM&*8vg4C- zA>m`cwahZPE{YeLc$N9TWnL=Wv3LRsKm@s}>jWCLH6ER$tQCW9fR6>?3>o~7C~J+m zb-%P&F}=2Sjb?rCf#kgEW>CR;uztt$u5K=)mEZbV~@F6A=Rs3erh(>QtJ*HuyBep6r)g5LCC(Gc zr!VSUh)kybpfNAkO`}|WeBm#ZC#a}};gF&OLO+QEJ0@cwdG5x}o*cPFH!*f|*!` zaBg4te#xpwg$iluoG+;0YB`z8IvI0sv*E~x4l!skW1}JzGSfjz;zDK`gH>Wv509V{ z3srTh&yETib*=*B7C>ve95l=I+x8Q+)Bi=4cPW-}g5s2k3UTr`m#*B*9_E4Q)`od1 z&lW~J2+53PUscS;CV(wa<3o3-F)wV&9KtDSSBf_mYE*1M!bA^7(WZfk`ZbboN+WbM zhFO5jPCuA+m?BM#LcFqEhSO^n?3WfZOzxAHPW5n962FiU`fvzRv)%i1i~9X>+Jz+0 z_Q0L~1+zu1TqV96F*TQbY^+1Z01;B5JbF6S#IucF1GU~ zgE~&4cqB$~?2H!<3oNTlSG-IT0!TRE-s?M+dY8X{>>TR+>Ov}=0aT&H0jXt( z0HTWSp^m>goGR)Lo;R{g1*%>%PPuns9u@1jGiWm!G&!!0%=kdyJY6GnsTzM_*~q^g zde-SSJl3u|)~j3BM8~XepEv}*-z(DXh5;CV?+!p1s%B+Kn-Rv{3++uWRSLhX8^Ot{>4KG1Mp4{glX|JG>F;=Es1+Iyr;pb6{; z0)p=J@pjJkK_HGGdIFcg<1|*tZuSqXC}+64`Jx*rR%~DaAxr*i2jAtRzJSpdhLccl z-*q~s@apdmpLE?n@d z=Ap*z{Xwb&p_enrb_3$49iT6%8BEiN~;o$uiSdKfaU>p_gf0&42RwK@N=V;Hdmjc1#ww~YUA9$iQd_X!* zh*|kI{ll8VbWdIwJP9&xsdfLsTbEJRnNMIpAvUk^2b`fxHshp`?^(j419J>KfU2*2 z=DCtkyJ{$q#0DK}9YaMkBJLwFEUQnp>+45I)EIGo$2}VZ%N)~I_4EQnib8slwXL7M z4&)|}!02SG;BVS7-*W_Oo~P<6=J?$JF?ILyw@#gWmCK4}*MtWX-F8z7xE~8_CfCBU zN$u$a2LcsD$Pl%JbmD5s$)6)=j9v4Ngp*>?LqlTSm`BriyMyRkbxOjBn==Q`r^9qQ zs8M`zQRRJw@^urdddmQQ3fD2?qK2CW1Xx0W@6QWHcZ9{1_+^S~Z5i8sS7&|QW$D^x#zaxt|g zaxWfwoZA|$+Bbx~v~9<}AK@HW>P3NbZ*K;YR*(o=qv;J7dL^sn<>e;D=~0)3W^wt} z9nLd(>}5-{%+Ir%>zs(dRWXyr0z%TuA&vaNkT{f-j51e!G^$QA;(xKqNZQ>oEVd?u zo4gEsC&TFJ&)c&!DqVot26AC&qiFnSZ1gugWxx>SUa(Rvk8O4cWvC|{Lf{deCF2HO zJ;J(>P{er#@^~0Z&FuYH(tWGHsjuh*Oyz8Qk*T$f2b-8@R>ObsnhSxoIJ5oHA%|b6 zPa)E+mb0`Rw`re0VEolcS|47|Ne6Vopw7q1?nu$2@w{|b-uhr_SipJ2ohhb~pVT@O zaFcNI52hvVqp+O{5zRG{=q0yl;FWj9o-qSxC>S@Al-y2M>wsy8nPL{6!Tk}d$?@!$SH#&#ao@RDmDsb105r-X; z*#K9tw2+R~WtZ@^XsH2g#xdlkc1m?QShwY#<3WjeEqsu+3_8!74%hsuBz!TeVNVJcIH%lJA01`Lf{ngRv zGOczh5ryKorw}s8=#*cxfw|!w)?iB`E6!h_w6)eIc(b`{c_`=d+-Ii!#uI3l%YUDHSyv9 z!#1>r#IEx*qE!(4!so}h{Nz?W1+?wTui_D6E=n5Gpu;2|qAa=)v^ZVO%ET2~6oDAt zy;V+d=X_gcA9o!>VS>kb%2-ltTr?70Qdh)m9U5@)QBKm045QXD??u7A%ae4bAjTJX z1dPZXZ0Q>Rt9!0a`XF2uoGyYX(}Fs+UEvQ&I%G-RYkyw-A<(BTMY~G84AKT2MtB%J znQYT8%$4*2SDaV_0uVEiG<(3`U$ksy=WMstf*K+nf%Iv65Un=UzX}k0j$}^3HjqbQ zbXi8z7fB{fD2OKjsWb@1j3_kG8LRgev3PZixA=-|W$;lZ=Vw3M0#p*CfOUiGp%!ri5aG1V_IC0 zyNvWbkI<5O<-xGd4bE0j1zCq3;zy(iDa2!D+hM)T_IhKZAGOs|fZ(}(f&B$ew@xf4 z#$+OZ5Q1_5=K(o5&AfcFn)>%4riF$2oQbH_eJ^mi~(}MW* zk#%j1GE4=|gi2`d75v}mI;ZYXq5#dtww)W>wr$(Cof|thwr$(CZQHgdqczjrGq3dn zYSpRp?Y)N%3bQr8{uA~bS>6YnPXz$*wE_Tu^Z&hVaQcrQr&oWi)HX$%Pt6{vEaRJG zdl&rrq7N7nyyioqx2U+iEy50*mU(aq$^=F%%fu9cc^d9_ZhV%6YJ3nr#le;xffOxP zs>{Esb(&i6YkmV*GZACUsGlLe!V-w4L^JRzapmmoy(#zSy&cg&OIwO!-xt)KePSO9 z*Cm$p6Os0rZpwUjExUKm>#RPvVVZ>r8fZGYsFqth@*Q&7m)8Bh*>0M=DqHNTi@a(Y zsiA8w7_&~FZdhOny2f+AwWE1q4~g7j4+!!K?krP3M&7nCB)W_;g>-_|jM7#`3(+6B z5>P?R5&`B2Z+HXe#FSu5B-4XZ@_v4qdX!;^9uzfQz#m9JS)39!1;?^aW=O(MaxwDP zEp}>M%S~m_4(%eMsN@{5S@(2w`oex-XT(~!INbxPG`x_wLXpEzTyumY`j1;s{yN+R zfC+Vh9G^>(1Nse1G0=ABVvz!%K-l8eSuE`D)!ar-R+!%WLgPw-bGZ=%MR(7wW&;UD z_k^-K@JBQc2ixOp|JnYzmTB=!0=!VfzcDgMb~uEVuU^0!eeBj;nkl8DPwL2E*`^%1 zj7sPP@ydRK_@sc`uyLLMO%9qt5oR#rOaPfREagP*6HbXG1^ZwWLZdQA3A18`fpkQL z{V-$H1_2XJZo+1E@4zhVZ+DkEFO4O;FD)fxd@t~x;O9$T?rTaPWOh3_&w4!^N_Mhv z@SY{WOFX=OB_n1fUuq|2WF^2L4=Qc|_`V`H?ISlTHz_S8U48snndRSe8w0$(fpD@N zBW7RQ0L3TnKewN`zMt&{03n>b972CWzY4xF8@aj8%xy;byS z{9JxkmgweW;`|whFq!50YU|m-zAPI^0G!%>%U$vJnQmX?;rA~KT)F+Ico4uRL!9Nk z$jZ*{;kn4cvHO$AMs}m04zNb+{g)rv$5DD91e1&7Z;ZVIuQ?Cr`BnxVH{TFsvBt&t zAdEiH%gko-EY;*m8k4%qrq|z9-z~1~o4pNWkjHAiQ$)P2g>Qh3$F>JIozu6>keEfF z*1H-w0XOr%DnDXNJg=C&IDarK;Ds4Zp<(A>u6yD@R}F_6x|*AxU6>^U>vlx&zq~i zXRyer6z%u`f+nc5WFgA2g1%AMF?~>^YbD0>=33_ zp+pYP*1k7rKmjCr;RG$Kd`1CQ$5_l;$vgApx9TR>o4L`&wg3^PZcDa5_6VfeM~RWV&oBmkVUXYk4U2h$m{3EJ-P-PJscW-N#$yBi zdaPgJ_xECZCS)6%gb_zfM()!S)$PgbIVgr@3A7T>;CwCam=D519KxuKHlfS)6%u?M zx~52vpw~Lj>YSq+#5DvvBKx<2a85u2scP~I31?>i5KsejHs6E@rd(ianK))|fCqvS zU;M#Jp316Re@sTJ+ql4k0pA*6axppOm_1jAO#+*vvM_2^5Q5vE2VODCY_l^}r=4!irNHXXzY)5)AD#?`UZ z61X>g9ZB#fLYe3EN6qdv`c_ef=}s-T9}K{ELkF+-x`ESg%Pxlwe)p_emoimK^QGbs zc6}EFn1|x6aCpWlN>@m9d%h~m@h%z}{G%d0& z!F~?N6-`|~5p%1)xUp+Q69o)Fh5P$K_qxa3zFj$2T5f8OO0E*!>T)O@U@Q=aOeKch zED&XZ>~>Zr9!yA${2cCM$B5_Q)BxHxkc7TD9fC7O;2O$sd0Dr^(=v`k^e8Lec zo|n}5Y~Ifmy1!*TGDRw-8sU6>`V0rGtxoXvs@s2`7lgWOc)&5ohyJX31y${(Sfu(% zk;FHyzn{@I(mL8|`-LW=Z@cHG3n(3M&v*+G=a77>B$LkZX()$VL00)Xjk>_2Xk!HS z0qBwcxgc10a+LCEY18q5R3l5^n%!5ta04fN6HLjh+9*a{h8S>+<`;!&5^E2|ePBFz zkU1z59V1tvc|{~%Uhq011sKzWC!DvN|2LS|Fle%A7fGB8s9tx$Hl~wmTt9TzM4C~Q z7;Uqi?NBqXW8IfJSOVa)aJp zLvo6ZhN)@f#4u6LC&NrtlboxwgLKmw4tje_2VtkjyEO+gF+mAN+a*jW-VZc-1x#+H ze$}H)3eXos=SDpSgt5D3%?gP9$+tB9!Zcg-2-zuaaW<)hCt%<{V+KDFR=ommgskv1`0$l%JU z3j%72cCw3K;2dbbGu0xZM%n_KwiShzHtJ%IaVttOI|Z_cs|I370knjD(uv00;@#r8 zj}o_JOcA;k$ElhUjTcqS>Pc!37)~zIY0E_CL@a^I8oGi0ACN-;(w$^4^$h!`kdMWo zoDs9fDRMw?jz!5|#KA%cItub@73NY<8U0r;pe=>HgeYQ9Ta6(}*FQhp$l!m=9n@0mClmeMX|r z3aW9x&;skl8e)_>&%H(*gsMuP1Av$d6p6BNf&=}%zCt6Mt2B@1MO zHN>(z!_*#8blM>lUccmiv`)1enNWL3>Ml*{=DbQ8F(hZEiu1% zAM+>Ep*E5z6@cL9$BjSufj4?}nR8>L+F4Q?`Hr=7v!n#OIPxle4-1x`3#fR`j#IK`O_3{b>i4C~QvsL+hIc~%1U%Fb z%+IiGLi+6WWCKeMI_PKe)7GDf*7}et?W9q90#+J|0LX^#(w0(jhY}WZ*p#B=j*6p} zdUMPnqT7(_Q{t}*Dvk}v41~Hiso$eU2UA?Ba;%`lC7*k_nt_??yTJd((SYh7l2;A< zRYy`+hce3yXa367t9Zq~#!NAD1mp#DC|3hN@-+TLL$g1cBNf9Re>MfHHI4drw_DLjtfq{7TRzVmGr4b>loT!R#f=g z^mQiD5>S#RgZ`_kTK0Szm&Jca$nKw`fjZ1Z%rh3Bqm~p??fNJH$iX0R0wbgzP#00t zxz6h!SSMnLIK3s*wU1PdTe;#J>4I{8L{MvFv&&gdSiFfkS!cR-d0A(6Q!rz3Yi zOiL6@71GzRL=E;3Be2Qilj*pbu1ySo4aIVOaQ!_9MKLKuXS2f-XM#*@HNmysbukRx zA%3cl`}>B$f-ci@#Q(8@ln_b!OSy#zW%$p>QOvAq6P&>zFYOGLDr3g33iq8MHidV#Tu+~Jw)4I7pn!5J6F>(J&1!R5akD#rvNUM>%w&y z*T7adJgQf61_e4IH?RAqoLBI(wXx0H$GPjNTPXf3;F4pqDhjQQt2VVEYY}i&1r<)%;JMBQW@IdgZNW zBPhGaed1$twsf*>dtRP*db@=oV{N6_d2F62thd1R`gt{@WpSU5z0%aE#^P;N0R+v{ zrAkyOV{lV2J1#X>M1EyYKoat+E}Az}jA4o&ctMe|eX%B7U~;Lkkmn+9`;Mj;bQn5H z68gyNUkk?vOZX?1^;|rF2n3k4@&~@8WWy2`vPW{FRGep2Q2oO=Q&p%JRkPCWlK?(u zS23rwRMNg)c&>=sm(rgjs>5X=#l{u zDooWOaHYZ8HWY^eVU3@QH?RO|lM$LK!*!kmDLr}Xfrx~yqXCC6J;KCnlc58bQ{s4a zh(H2*u+pkFhU~E86_I6VBIWx(mhuIFxf0{ni1jK71R5RpW*?dUjeYFt6sG4Jm58># zEZlWvX`qO)p6MoNPuvUN5V=7gw8ENt^75hfI0{& zR#w47PoREG2|>_y_7seEAoY4?#=v$c8iyA;+|sN-P~z@#X%?jWHh~$NgCwV)^3i~n zU3(^%+?9!u=1r@CUa`#4vw#*A)$qP8l<~u}u<4{~ zVbjtRZ)+$PGVuWfKlTFP{d7E52+q*+-14lq%M$TnOnT*GjZfS)UC^bN>9I*7|utE)VzDyeHgFdPX*$ zl9+Y%>7;^B-$HAK;`6Ezd^u)9zaiKdvQQHIW^LAk#&+!e!-;B(l@`@eC z&eXzhmoq5k!QbWPS8%2bp0=JKdk)pME*&p+RKyM{p#{8(J92D?`olpd#Va{_H8PAi zMJ9nLE?~tVD(W4+RB2I!aScxy7hN)5e`D}YTe7-7H`yBfZ$P)H>npUEc{Oi#c#CMOABYpJNil@geUCY4^U z8fWIu3l0E13Tt2RclgR6T`-JycX2Mo>!RmVHRQ4w>OeP4HBpR{+E7rNs`tq-)88lc zL{qYco@7##9tMxbMMb2EQo=-OU5SL<-`{%kbKZS36ag<UUuF4V2Xz&jUaRdq0z`<}aEFB%8ATtaFhty)Vq zoFXAY2S@Ut3Kt;S!yj+P{h-Kn7M7k%HWUy<3k50}daK=$rJsh3IWVkYU__RJ_t4_o zI|Pni4F)F5NxOy><#s5>FzB@(8PH1b1~A!$-=5EBhrTLY=T8;@7~(ZJ4Mg6wuE;E0 z(3W0+vRhnlT3)j9Z-hejO=Z~!0Ww3K#ootFU|q&3 z6AXF|#}A!zMg>G^numzFu+Z>pk@|Tc*iMUdN4z_X-(2mfbLh~OdJz%->VGiTFi*WR zVP&0eS?kEJ^w%0X#o72vGB_Z>vuG$a=p23hBSYpdP0#=tXx)H}XB8at^8)LR_6kQ? ziH$R`_ZvxrsUAUq-?Xb565D{ni3QF_z;>01%rDuq6aX;*?jASh$9oCWO!YBb*auYJ z4cYV}F@nanA!tW>1z@+Zqk=eOE1zg30C*`SyKB@&&Ag;z*%aV(Sx5tR*CHX219+g= zz+h$mx%s=1sEZ+?a_(I7e;lv-(^iQOS&uh)*OnecArY^xUQo?pTv3GH26PD_vGNm% zIOE<+Z~?G5alj!CI@J*$-Y(Lvi#1~W5qJGDJJEt^zIN^Q`WAfAH40~Bd$2%RYXbd2 z1Vu7qS!5$AC4w>-RaBtF9NIh zIl)avxoi!eO~jU4foHD2aKVdbjwq**x5V~<8tt8V*{R-k8ebFVLsI3T;=trQCKRM+ z1`+a+6O8(#p!MS%pqSaBvMP_6TLh&x4cECJ~Zd5NiPq?h%NxFZYFjuQqV| zBx&Q$W3%oBses8Qh!n6q<`GP6VV&4B$W#%MjKju-1H0l*z4{@db=4+HW? zKv+#2d`)OWc+?!ROYC-E`H3-M6?$B9VFeQNAdNNfq#?_IAeW)?i#|_cC!+m~NiUBw z5n{Y*BbG~~ybu=b$}Q)0b54h+7F&%i!+r6jI+xa(Eapbs+8~TAg)t&pmbzuflP#r2 z8}NA?tbua(=f&$Ok#Q+ypjdYq>(O|!Bt$=N-L<@ z85Ol!jVxz(B&e<(&7$Nc!BzhpenJ)2mc%V9Oe+*6MC=GLIKZ_QGN!PEI!L9J)_oF} zTv<=Bf?av#oWfA56Ikc$RZ@D(H3T};6Jp^tesA`mO&rHqoyWwPjwKo2e4LXDU~hyh zIA*`aIjPF;3-+Nc2#3q=yx3m&$|q#A3Zui5_;@^&NJ-a0!##2OcA2qf@w zkR2HQ+eOI?>BFI!LmJvb+M;=*$e=a?hfo#%E_{GQ}`C5(9gZtD&?}h~R3u zXX1vMyk}IT(=R;W6RYEPgo_$x|je|q8LUdctw~aac?DcVrLgvpRgsp`qGJxA3F1M}J zM<4hjlRfq>IGVdPqv)ooAkatU5M%rPM}OEMx~cSWBRooCiFl#-;?N;2qnw*_J=Gd_nq$`Y(Gl*FJMB`DAb^t*y`|bAFzUFqoL&SCwHTc(+O zu|KJ!^3K}T@ive_mdU>LgR#Ogqotgu=BG@engF97h!PfMgHaBvzg`oZFmkYI>_`Ld zJrw+iy9={~#{4C~ za01FhL3f%ilRQ>f;x7tI@qySBN@xM`XDi<3&93aF{^oI-_%1O>F&S1eNgp25jB|R% zS3g0nlYRIdSMTBfd}EA4KjiThW6#wLXY)v6L932d240zBkzA^vGWX0+;Fdmu-R@$H+)~K!G?0 zoiSVXKJN?@uFs~7)v&vxkVcAeu9mLq~p?29E08&iO z$)2Qt4Jo>(cR0e;eD3fOuDW0CAk=&TucEG1P?$#EHJj<5ntJ%vK?2-v@g8ptP~+s` zIN2HqlMQQAsjoF9^lNz|RT<1bwe2$lD!`WNK?p`_q&Gu<& z2>i2L6JdDo==E`YY)SI4fh`faov5IM4h7ZjxI@Vp_qk`)p*4Ihq1i-@+i1-OxAz z5TyqL0$Jr0Xl8 zpkSL}IV^*ec>Y!Vj+wxqpRMiK1WK454v|;9kxo9piS9vsCEH;o!#P98(TT8a&nsq{ zLJLylP@uk6L-emCpb3Y4wzH-rJ0f|T)fuf17(^)}m5(gJ5@%{hM#QouGL=l1yI`LQ z_sF(>Wl{TT*4SRTrWf@{W0FdHm6n#Xlu{FfZg+O?LQ&VFF0bjnj_NL7SP7jH_nL|@ z`2>w|$DcGZG0n8Y7g1_Uzq`JA$HHM~=k4}E&ZU3NGk@gFh279ej{qfy<)%{0!~rE} ziR#YRr$k1%41kJ9=PBel8)fetI-h#aeW6tCTDcx{+j7L;-7*jjGxeVaxikwtd#H9v;Wwy{28aqLVd{C)pc3{0V^f z$Vks}u_%OJrc;;(E+IDA76WjU$|}54sfKZnWyzUG)E+EGwd|xL6(-(F;6Su;#akEtEEVP1t*)g z`xDH-Xt)neE{*C0^j5ShP{(<|f5`k5IYfQzUD;QSCH{>OG4ebC9bdCQicn7X8>p;V zGU>}J=lABvD-H%5p)-56Pc#`|rxTQ<$U3U@3?q2Nn;xy~;F&5Krg1NtG1! zG)S2a@81}lH0u>{uXIbILgcS7IU^qM`Z^lN&U$j+J8}$mOgiUnDw6M349a$6hOJJA zA+f+DAx7V!C7iG7_rBi)oWd1?1)%NJA61@$ z{u98s&zSn=9Pm`5-yX#?NBEp_xPW#cNZy&Y&YE7HNwZVtOE+)uGl2yET@nlLbR+M9 zfB+A)zYQAX>jtTS2CvBEC+`{&EtyTdQsr8WhsGY%5QHEO_=yxGDZtWRv>Xo?k?M+%CACCAb1{!izAi{@DU%zbAc#@k zv)&U2cM31_@MuKEAmI-y#^27OetRt}bbhE^(|?Zf35>9oqQRLZ?xCmjH{|F8o<96BxlC4$E+KSbnW8i_UX@EXAFnmMT91bR zG_AGkRKUza`bvo}stFc~s(h%xZyeULeV;J-F#TLBx-S0`cHCtAydYwWSSxFO4OX|U zclp^5(k3oO}IRU2eon|IW+FMxi zRqgx;Tc>{;6TQkHeO2*KwjO22!$40e%tj>fIpC?55UyfHiX@6uVHxuRQV~*H#oH4S zNwji1dX0fwER-o*inK69Qh7M5q*j#d6kS$WZh8e-IY$xlZ{SFpmUTkah%&{{&``4r zOG)Z#kS-Tz@0;-n;$;Jh>hEPhkni8;4oM`ePCf$}!Hp7)qYoO%)0qlMbG7q=WRgs8 z@{mC~bL}d{)9@+$vPoq@w>Lq#!Jy)0r83UEf$NDxPeQTrs^`LM#fxzA$CngxW`pyL zWp$Senc-)x@|>$h9O^XZC_ZK}k|I6YfW|6lDB4o_Q+BE(#^7!(=rUnWo0c#&n=r88 zSHyBuPELjz3K0-_MiXsgEGslk!$vSB3CVJ;)ndjIWg2Vp%sS0BgN>@9&Gh9;@~L2~ z1kgvEqPE20!m}>6A`LD7h;2c3DO8ZkF_yfW;Ear9)cOrjpECcr5G1YsqI-fR3S z_T4Y@=suo`=u0>jAf3QR+lT?2-u4tue-gtUr96J>IbVf>#YfCzprn=HE1|6Yg(ByqgBXR$ zTQ~f6HGEGcxh5Zn8&|LwiW8Bg`9`Aru>t7iog2bj3+tC7ijV(Y#fTblk|+{pmfwf3 zvxJkRrH5Vnn_}smxO3z+>=;CLLFed;VmJT|9o zZ1AnQmfsEOks6*2sikdK?4INt%WkG{1si=Olw#g#^&j9wOYLP!lKXtb>ZmqverD++ zA?h!7X6I!ADeTn48><*R1%&W<^od4uM{=p+&NuxHzhT&8Zd`v8oT!3+Khm)2M5sN6 z@7c^}8<`iVVz4gE00F9#f3WL;0A7pRJks{Hf-kGuZGR6aRX)Uu5ouQ~Pm{}hG5llP zCvZK1@ji6hD$+zIRt6h8ILeOl zR<)k0t!uMLaNUSQ5Stn)rv**Nj^%Bsyc5ziPs0&eR})bAoY)JD*UuQ{hF{QX2rra1 z3-e5ec#Z_;SuWQBIbs`acS*fyQIV*|s|(?<_w^}qL#kS^mXk-g?fgv$L)qh8^P&`s zqU6p+Dc2X#R-(Go$MisYKvbowOw$b#{GPo%E|O=M&k6gpx9(_Zm}XqtL0t%mr915uJAGSsDUvBZMzlwjn1{2TwMxuBMIU9r!& z*2h;W+D3lUL^ecAN9#7VD2BxpwWigp%hK9TEtfR}KrC`Qfg=IbLvda3c~d0zCp}n^ z^Yp*H!Jril*~0@hIDO^5(>QZb+xx@$cDqG9S4-Uz*Wv=y%DDNmge;OaFHXI&#Qm;O zdS_dutyY-g!&7rTWnR4dkqdVC2d@HP*ti%tAUtSssF^LzI9(Q5*_$KE#iVPkcNSSW z1YK#m&;#bw;XTezyHvlkC5092bc(S4KuprS*9c9$_)M3wcg-q3f?+wag6Q=>VMlF zUEsb6Uhxwsijb8YOx4M_c;+QXC^YRY0V+}|km^^9w~8xJKE?v*FZ5N*F980+!qGJ@ zG|iJ&a$mygJ|3GC3_#vLN!(2S`?z3}BXFT(FT~B8^;$kzvy! z(Yo~g;#J-O3)X*$GjF>2^u4UIr#vnP}fViJs0FYcI5Mwur$ z#;MYTf=V)!iRKblUP(=jRrP12!%nMVJ&MLgE!Abv5m1fQ{Gr;uSHQ;{;LUT z1ixJxLS_2Qr5JxwHKmghNwEm+_IbNsh~TRrbBpuIW}!b zr&&B+M*R>{(U?#+AEl;3YBRaexYR061`&JRsaK^oQJMZ#gpg7X z21UNgPYCbD_+@g_?bPoF&)27#aWYc}zo`m-71TvVS5}m2Gn*D2;S9PI?Z!*$(?ZLd z9)&2da*B#7lvAU^LDWO_frA%bqFRB&R|QNn%he~vc0CmHn@~|lQP{ic4s>R*gm{?E z|JX$#*0P0H);us)spZQ{B(zW!7J`V*L!nYM5ZAcYu?afwGuurhBu5g(S@@7r#3$)5 zynY1M2jF{gW~E50MAZZ%>W)JdTU(H}7VfG__e{cO2emU6dg%)%a1LhY~*xivXa6qU}m9vF7mcu%U&K~*$El0W``H!C{Wlz~`gdI7K+E$s;F~AGrfU+@o-{Dm(=Q?(A!KS_m&VqN$iJf;3qiAD)Z)_}43>K|mdCD>&!wL7Xc#A~yI8 zb;?2B!`|#d7r1*b;Ie{~jlLjAEq)V49_rmc&FT5k4(xZg?a~_t*U5nF&)EgnR)2iP zD7!?ywps3_Yp14O)`vApbFq_@Pc29A7z8GLhGaYu#P{Y9XONE)K5zySRmoxq4uj8>&0>y+u2ti&sK^?J|2Q9^&!Rc>3Oev-E$p-2`H1MzoCUG+ z6-U3Z6rsE6iQe2jA)GDKR$ZfqO~Ai>#8z*BNs-k)j`l@LF}gH}D<;b0F|Jc>n~Vh- zX`29S3o2*j^EM-{GbTj{q;RCNd6h0&IE33wSq+GdU?qOB_d>%wp(^`uv`urg_@z0-WLx_E`uU+ zz0}!VF1HK$9wWu~ZFVX!cneI})~^E7a6tiRVQk8~T6)0`IrG(~P(DZwL6!qmK~&6G%NTl@k3tl*a~E0AkVs zdU)E8X|~2y$IU2Yk633cb5HxDiV-5P<8SaT>Qowwa+z`px$1q{JtoN9iqy08N2G1B zA}uzW7Ov8m!xiD#BUw3L0~Sh`#}&Gawo;`bM@q)|9Q`v&N_Gsw5U?9V`5@s={w0j6&#o^OtO622zB8G;`;hIuG@D705@G= z>+25ZSqJsbkv68Ra?iNzwRONjjg8$hnoFBxK6MARH=|%vf2@jkIXfG}Eti#N$!aNH zbpNw%`w@6Z+&a_DJuDSMQ4POZChGd!`(+AZI{u4fb6#Q4Dy~%hY74c-Qm>j_J7H>K zC#py#zQXt7ZO`-i58Rdk3Vhd`Z?%8NdAf=E&A!_gxDO}fR?O2seq5Ze0AI7tG z1y2iepH5}&gn4`9c?*G} zf~Tgi(kH{*bD9o>Tz0Wim`*wE#qd6)Yw?PmOn1!lKN)=>9yjb_Ej%^J5C*h{bpj2Y z%nLxiYUZUGf-bXT_nZG{WCYSTZ4sv;_HW;N(V+XaDyf6E@G16N)+i-R#vrcKUBA+E zJaQeC%oH;|O|ibz(aam8+Ou_JuHamkp-J3ynS1mQ#WAc*3rU+Z>jMQ`uS?oW+g8#e zg(hV7v`IC!r57rKd|V8N>tda#$+U~-)1arl=~5(6oa!ES`dqN0CVFZMr$ak?^0UL_ zg>x9@1j1g6zDPWz-lvvWJH36+i7_r%$02sY%wl^2#Hn?o~D=k|*0A#H% zIzlHoJCan@Ln!!PO^j&zDxGkxiu0a91wc}dYm{VLP%SoF3s_evMd1Ro9f7pai4{nV zzYc{qX4sVAG`1z9}6 zAr@J*OBle7!B~3`M^&}x?vqef$xVw<=cIfk0VMF&Yf1ujDlw0`YcOLqiQjw~fV)vh zmyN16Yi#4}Z9Oego$+XLoaTi}S;996uY^P{*g{`!_1j-N-c31 zWCRpI+Ov%}^VU4HAcZ(4Mo9JEp9wgGTU{|34)S9L?p7bhxvS+zBjzvz`X&8Z!`vCM zfercNk3o6p{7>vhS(MPSx!#;^}8yWBx zJ7-_w{ZHDDA&dl?gJxANq(0!%wlfg*G6pE{79n3BaWXZ<;dwis$oDqApU?=}Fs3-n zkkT<5og+{qT~s(t9%RDSeRL3Yu`%FzTprwjt)01L z;5@e9&u`lvoq>lI4zM}h-XK4Lby41ZFUG+27G4Kr+oH)kD6o^w13z9lH_g~0*7nSM z+*yXuR+ z2ZQ-*E{T90YW4V>7uzryXkxND{*^gL%k#t!4H|CDEeci?7g%K2u=?}^A0H6T_U~m& zOXhNit*0V4;}1ghqFwTT#-9@Jj+Wg_bEU)rkw5NApLh5|>O5{?kG!C~c0@-=e56gC zBb+pA&W@k6`2YNu^No`xc;&C(XZzQBi1Gh;wK+K(SXeg}Q*EuhH4VpDS16{Z2eu{y%m?;D<%_y_yT!7(rh;s=N8 zt`pv}F0+ZG>SS8E=XjnxsuL51Fpn3Ri1qZSq$BCp#q;% zD5Y&KhXAQ{HeJXDv=quIDy7t^=$js;=EZ3XB}h9E+R0qeu8i<`W6!7=X)e1bs1`Fp z)iiskXLrSww5A=&3U>`J@W#nPnKDW`9>8jYf=btXTWgz4q}Bwgid>@7($4Wg)sIbP z-AdG1fw`#Z+H_&@Mf1ZH6k-^J5m4k&V-R)Zug@6(TVe4_yhJ2Y6HdQzED--9(O)^ zUaAm4KQzerkSU$MnS=ZhC4m@II}0eG8pMPyRMOFNxmO3ZkL0q@R~N^v)y8xns&>7J z7YIRWu=&vf&s>`1QyaD8y1qbF8*LttF4j2VprGw_|ov4Gf3TF zlThNc@_zsI^6-A^ZH-PCr+$031bjdCFQ=Dvd09y=YRw5yUxf1O#XdM=lp!t!pqQq( z*fEzeFfQ#Q3;RPPG1EGJ6j@7E<=a;3(Mr?_j5>#s{1KoNhqF#(mIa|GZQS~wy23#9 zX7AJ3fKX?Jr76KpaVfAQHe=i|jxRPT3cEjKRU1n=+jo|l3zpIIOE#aLo~}60=4mZc z&F3vMK3>=@b(0&|8w20(6CT3FCe{gD?N^Z#4+*tX6RXW89d173*v*5M9$6L}n71_t z2@m?fG{`!5ko&PVN0Nkc3JTG~2R?T_^S0*xpP9*+^g!5i^R-`-zO_VI)9HuM<|@W9))M_M9d8^txZ(Y^xL5LR{QkP|-X0X! zura{T^nb@^-KQ$>wAf@soX3t`&wEyul*ZNtaPY|5W6vT~6C0vD4(*%hF^mjv6G9w0 zpbQ}Mj;v<^ha3E?AoKA&XM$%N{F~jUUCd`d*T0(ZxyZkBps)&VLmcpIpK|hi>Fp$N zG${)eWU;C*vF)keAhu7a_PzOyC*lI^QJ|rc>|uvuLI23dNx=;yP~pDuu7a_W4CTv8 zpYt7UqPo!L)4^CIBJjWO*eK;4E(NEyc9*R_OVn-pmfxk?>)8KElo}*S#)~Dwyw`0V zsGI7=%#u2b97RpISdUW%`Ywt+<^>X`_WDDEa&BVG{jCRaUPfn3P>^m`VBDvAke04p z%fV@~5H@{AlnVg1xZ*`mHU`=2^tb%XSlmP?D`KkKMRt-b?v%8Ei!?fLM z5mWY%w_^-I>5(W&f@%)YXJcgX4CTJ*uEHv0v}6Gn!y+Pj5upL(#Z?!dmW$|tE9{M5 zTsGX)|GXxY`Y$uJJ2n4NjB)?1%|Q2*%8>jX$x@hjue(K8 z@9k~hoKvzG@^7E8U(DNK^G;_$TJK|q8?%94?1nZR2?OW-t+L52SC*!uW`kawFI)_O zXf|&SDKqG_NZ}jnsT14b`_%o^16zj0=fQx}Cv19wHF6LcbMoA+7jxEgly5!RajJ%1 z#7n@(cPUqJ#??#5*cBjxXdb|bd*tO~=x|Wqk^3wP>DHq2&n3~>Q|svwJIXWN;;#BG z8S`L7_p<`G&x_7{O0X+qt6qml=gRf{kkGY$Kq4@ml?*>{COwWf9N8sr)WS4)%t2#^ zUV)rBVa~$ulZb^TH09>1bV6J-cm7qbi-L6R*tTukwr$(CZRf;JPHdgnwr$&X@@3}Mom=0XJ3n@K(e&!93W^X6!t?oHdNd)hqoAHUe_9DWQ7Az0M%!?yM@reQAd*HMChI>JPSRt^ zd2W>4Qa*e>B>Xvk1@zoEMiMDS!ZX_?^Zj3R=>NqM`+_uGrv(H6sQe{@Dvbxa%!RN+lQoY7dcv`2RxRMk*0T+2WLFD?=zFf3RLk%Ud(p;>{ zu#RMWsk*XjG~v!YU2jJHCl>ez<|q&@R{cp8T<-IFIUT1MDcsIB?*;%%uoZ ziNpmF5-!nTEhQ3+!KD7b$)%bWtH;#WWF>BRLX>|)TH2Ogq92{l#=vLcbnRbNax+Ce zic9!pqo+{#CS@=#Xlx2}>fNU!3-|}e&TTX_T~Nun46S@G5wLw1!pLbK-M5zaI=)R~ zR~xk=G~UyyH)qB}*vzVCGVOM~F}y{jH!$&=ONQSAweT))FPgc#l?U5@txWv?FL!nq zsHf^i004lG-{-#rGHfi3{zEorqH{2&v$Xp!q_%EcJygE{g2=~L6fMoax_Xq5SS%6@ zV~Ihv=?O8o>CJlgAfoRMEa)MtFm`4Jy;jy0%r`yA*YWq=pM2Jl;n5gADFZHBdf1oC z3&?y0^IW2oF{7NSdErDGr668uhMJGPkAD@7wt{ts`9rU(BW;{ff3Tb7>T29b;jEX@ z4XZTPET&_)6?Jdrh*J0eE=%|@by2VFe*1m| zkpKV1g1XxxI)mRU2L0PL`0o(%|HOfqn4109d3{y-Qa1=-IzO!2H-ua8XPrOR@w^TU z6KuDQ33zrUA?BLp$b>%<7XK$-$B-4sL5|&Adls-vTZaPV6Z3>j8%eP8Eu zsP*~@l4s)dXt%Qd5{%^B$WDOBkIe6;>ssw?Z8)oSV!vC209ed=tO&-y_=+^PdzH4& zS7pH7vsxrHYzsz{E7ZbH3qDLnw)fDn2N72cO%5U#?`gHQH-zwHTb2BBs9BTWiEy7( z=F$m^YjWAOU*OEL>#XSm(j{6pL!oC@gYvnjiZV?uuQf~D*(^V`c?5a3WR z#Ps~}+*VgFInbhWlbLdX(H0&J&O9S560{Ir5kg`WvwlL_1W+~ctbk*$?9MJ#Dx^`z z__&2?i#s-+S&U)q&$N|EFeALo%-z$MA9kpw;)rY>p=ybl5h{6PvnJ4;xT@$esBrB7Usy#{6tOvj|#;0d#!;Up zs7x)yw8nmqSJ64_G0Jhv^CGUan^;I4j76`0aVRNMRzN$eYdmC(*B!*#{-$=loo98o zb#`dJ-L`aJJJ9QZ#Pi^B?W`P2G0y!%Q(AOXx;G;JQD#> z{%O<~=r$S$@z!Mcc;{bdnSE^olqq25mxYn8LTs#%5Z&_G;(ej0+TF4)nfX_yyFCtc zWs@xa@AQp{O&Le)k5HiJnaFUGKovi`VK=9(`gmvme_B^U<*POrSE;(p~3Wzeh$mVfR%rf1OXXmJClMtBT{MV*&bQp@jgk1pK4|vT< zO%`57Q-{RopG0qS=!6{cYyJ(hYB79q)P)!w^QUX_u|Y#?z(-SgZV$pOl@hRbuv*YOR&l%1M4+LQAr zx#F$fQk+2C{dp!2J!bx|v?$|VFWm#iu|DaHL-uM2qmi{DX-?sxbS|Zj%J+nR+4_0s zln>S>R$+lixoFn0BGhU)@I%_@BwMb?pH6O8$f9xqz*PF9Ycv-ab_M>R_qkjFZrr&x zGak$J^6W^g@`_0KS8j1>Z!v$o9^H{9v?IVTYNQJR`?>19O4Y;wMdMiWB z7Orfn#$I4Fnva3(m4@z#?qiKMe`n@gh3qSvO`;7bR0|6uTO26#35cd)NFeVRb^KIMXefyv8 z{Ve~dvZ-w5RV{^n#@A}g=W5K^Bxfcd=jE1nKHpou$ zK?jpMiA-k*fD}lSz5h3njX!VgiQcwvA3J}a3A=)|4VkNIO7GkNc`d?$kkb%)IFC+j z4{i<+BrD~42c)t6W+$KsxIGj$pT7Ug*l6F@7^H(%Ur#vShuUx(=nR_1a54>RUC$iy zAwIOZh2w#KFaw-rS$ggFk0KP(Jklxh#Sk z$>#?)7D%f=-^<@GZ#0>6e1dnXIR?n2+gwEj3 zAW5rcl}LmTY1o18z5b-p9;wzaY?Qf(9|)6c&J<^Mjs$@>iT%JQMQq+!-R_XVOz)98 zx!}l)T$~8QoN9@>iS-cz9)09=Tpr~qpaB4XT2kv85&PLyqnk?cpiKD?Klqu;n_XFd z^0<)G1xj)5(Gd{U;}$q5JnWUV%a(pdjuf+-p~fYYO9#0`k%E9_dYI_)QK}w+VeK{>k8dBSTR&V7Ee7gJ!I&VKvK{U?4Uq)aZ(BDnPH8g0Dk52O{zeEOB$0az!Hl z1T>PnnsUQG)?xn`+B=BTEs5HQ|GjQrgT>T6wr76)F(;NK{JT$d#q@WPgLc@#;MuBN z(WOilTBt3Eu%xLx@UIR(?J&>f4Tz<i5jrw_P)LacJl-L^OVvJUy7Q0okSfK^l zg=>dGwmCig&@7f`(Ngin7bZ5(*gu!j)W0+ z|949u#{Ga@Qw#uM{ub=<`bO`=m%J_ezUHbnp12X=vtlOn&?Y=I7^o8RmO3KL% z4Ojp0OBuXkULy{+xc~vCj^yDx8~t0~1SUz`WW&t;cH>>CjRXru&OAovypQ-**6!M& zM+^6?^-p|X>ss7n*PJ`@-jr-8c{`f)%`+WEGgCUVnA5X{|Csf&7JqwCnJZq-*>PgW z+Yzz&kQSN}O*Ac=WPRjT@FJj?PlC8@B$GTH z5nAjk?+POgixftz{OTluL=P&sdq8Um!mV^Y*D=yuY$ntCWeNr~6hZgVfi@~3jiD3< zb%D>Km7^4423yMozLkV=4OAVMNtoZK@)kO=Q!yddHyWBx>ZLbkk*@DW_5T~?cjmbl zBmWkkXo8$Ec7Nis1aYTi=QjF;Tl2qKlGwGjyy7a zKxQ?EU`!xLL(J#e4=!>SpG-LNb|kIBZM^biRJoIE!zW zdiu&?;VN+6xXRh9biIVZ`Ds-0+8+?Akt!1yv zcJAnbhnbar2KPQqayNqot5P`kvsg4Ar5S46f|r|ljLQ zSJxiaV`tJc`eyx-@h@b5boY|}c_lX18gD$=dCNm{ZUsGYi~wm3jj_!e_~FE2T2FpJ zz~ock8vg;Dy4grcm`J$+y&9>?6R!|`1d@y@6EH^9ZDM@;++s=I zdvJ&0iemmJ6sTh+WE{s~(`E~wo}0@o_6$d}0j<~Vg}vH!Agp{p!TxA-5r|W+icWBt zi7)7(F@3c6ewvmFc_&?|>X3%sx5|k>6SYbMR{4rW=p8=Sl+gDJcT=`yF|weScKAS> zc&mWMjbGojv;&xm6>Dc>>^9b&4ODhCsBF6YNAfZ z7Q1qlLCvA1Vdz|`bz93Tor7=0Z(zG(5AUA)o+{fA6)|4X8(D0K4T8?{!@=CbTV^@Ow7qw_l9>3) zqB~XevZv4{T?jdtEKK{%g^Kc($<0z=3_w% z;B4Ct;d;BA{T&fO!>bQd#jf5mVBOXXY_{Y3qm7-O;JvwRRbtW}Zw_rK9x$wmh3Bj^^j+Bund6zo*b3U{8#+1H>qv zC6m6AqS-kp;VZDcLz?R9?R$brCqfhjv9Cp?>Vnb>Jw?vrD|%Cmg}c9jL!;bSuwZ(T zAjwU#DEFfMKlxt`w);%b6nzh6bk0T{M(Jo^y`WiF$E-eGo|U_DKD{U!qcU zkpG)Jh>*?-Bs3^r)!~s4AlLjMLI3KGx>Mv97L4?7zJ?V?EQa_uDkGEl+Gzh@1xR95 zao9O1+mC0exSG~Q0;y~Bh`48B8z}Yq@)m=FU3W`D!X3p9ne!anP{qB~3@lZ<&5J4v zVXsxkPSklitSvcT7T2qT#$V()h3)Nk^`$!Rdhp-?;^Q{WXeqioAWNAnY=u|Jgw-ON zo21RfB3yC5hi-DBlnl~_z{nex5~676kbmTN=1cj545YgXK^iHIyEEST7QY~Zqz`MoIBCqYi zzG@TuyQ^ZVuu_Mg`eHFTjlq&S?-|t|ZsQ$e2hLDc`|h2$2MAE1HP+mDH7l|Nfx^LT z?oe%4lJt=T6Rd`Sw`kW`t5UUOz|JDGh1HZOEAOXUo=8&c@}D3XYep!sA=j(u4*MAy zOk<4*(PY{UEwMBe9JUG|W;NWPi_aHO-Mzq)_$|8K$fcDG48IcgRBt4 zPg<_9*ji7b`E(`N$yZdxuB;o{bLF)F-c9RX0gde5j8aIJ_6+)f0X(W>$+YdnuWWyB zdaR3L?}zY-Bh<+MN~LS$zn$zPVdEvUJeU0d{qHLKf3;S+pYN)_)P7ql(}4ejL`DCf zI@SLvmalw2+>yl_Zf8zwPwUJhO-zijg*0HY4+4^2X#i`kNV2lBjrSG5~uBs$JRV++6Qvix3;&}LVmnAYfmel$&p2`nA4TeX6O$PedB((qr@ds zXOb2~bBmNwWyP%$ho~|tXmlsT8GW5HWeVS7u}Ymmyi*QOsB#v1&WOY*(9b9^$p;_| zA5}rD^M-y$5@rq%YcQ4}dJaz%Xj4gun5~6T3(9Y(e#&BkfIM_W@ zpO^P0OgEpF_Zo>qr{`wCy0a;3`TfGNQ$IaB-YpR1IqGXK@sKubN;1uJ%nbuyKShN7d_C^L!h{L*2NFKcx4!4b_9kxhZ}7|AcksKsy?yc>d~4z2^}DzEKd*O} z&?W(gRl)gjIyn_bcCLH;VvUi`D5^08+M(6-aeH-Mdc1sl3;VqEaq)0FdCrVq2H5Yx zYmnTK%--8;<92@c2KC{-9RViR`QOXw4dUUyFT5>4^efaqcCP%L1tv~YTShnVIZD!Y z@Ns>@8^dKezrQOFk_M7)9L(*uwU;7Z6OQ${c<^?Rqf%rJ$+xN`m)l%U$3-59uM~J z0H=(9PH!4k+Ly^zG>T#^ym5nV-?*0wsXoh#yox zpii^2h|Rmf(z$-#-A>(Yr1|gQ`fNCV^Z_Ro54?12MxOx0fbk>Uo$bSvwxoJ5Z2t6 z!~n8k)MA8m=gNnh9e;d|c!|rmDlBHu$^!i9oe1l+y*OdC%@kpAjs{W4#b}rU*292rTYN$8{^->z?}(ucu2v+B+}G&8rxBeg9P05D4Fz>#Zi|r;T zuz7PjP@$;J#{NDE8IaG$z}D0PiMslkPx4(HjU$LHoM1V8&bZszN7rd!o!$?SN-g&vlCT zB>fe-%R6G-Q1Bb+URB4S`>W{D{NumQAxjMgp;Xwyv)0NLPZ%9}{jIMHqv-FB4fPj` z^H=)mtnW}s>wh3D4845bHF*0+t-#J5z{nqr-#F>_uUM^j*%{te6}y~j@Kb|iccwgJ z&1EBd7{&M~zWbC9&m=5;<`hIC%038x2uEohc#=5Ajjr7W%|ji2=yOYN?QRt(5LS-` zPvWvI+_jIt1?`KrvC|p)C2ALu5jJ4&JJZh9^RIbsysbXo zFKWHd*O;5A(e?f1(@%YR9#+3{>yvu6o#{b|_Q3an;$ATv%CI#+v+d!e1E9re(1{G- z>D6&`5ol9NVRxwgrd~VxJn%+zUxE$&Vo8v`jjWBO62O-8$v>LJvuZ7{Oem^~nM$ zTjcsbJ*K7mTHY5}K0{1Uy%Gqb1Zs)l4(9aHwg!Vuz%oa#{1c_L6ro(BD5NXWNQw$Vk!CXq?=ZO;$d3Z#v=JcL1oqE9hGb&pmq`R0%1?`yg30uUB)gUN+V4<+0kq)fu6uf6#=oDtBvKz^Y&UjO zahLMVT0npp%Pm43fe`h0QPJ>?)+pAdqs5JkCc}O+=v4X}bp`aMk^Z2U9ynd-+0PKs zHMrP!`+v2k8j^SbZ^Co=tkR@oJzpPC>+#2liTGGM59nmEGUJ^diE^?ztKIYE${$3^ z1Z=pOy975MniZ67sRtZIoJeg^Z!(-v_u%iMIaz+~ueh8%oW^Lh@|wC)-AhJ^w>@~y zcoV#4jv`;wEDPVeMGw>I+S^?R-FT`<(hu9#tds-oRNH1qp`u~6wt62?RRTH1AOYy(v^#AX`FTFM()WrBc-BOX*6#47 zr36;y_d*VD%tA`qq_?rVyIF>&1WJvY3BHYqspDR_kXFm1JR~Fb8nocWw*A}A`D-_fT_FHrAfLL5l&p)U>4Me>^5Doe6hT>xd$*__ zyMZ{1t@+ApD6MLq(MW~V7NrbbNmxm4Gg7N++&uIhAmv;RY<|Co(@~ge#pi^0@Yse; zSaU=Mk8L3)iQG>1m$4Ic3BrE$&*Q*R_li&o!hYz!p&qFvLdMZ5l9WHahv1LtlDoRK@qmSdBw{_W`dv0n&-W<$_@_uP2;r$x|)~r_3rPB z=cJ46ExxHLbtU&7LX}U_;x0v-J(p+*R%t`?eb2BE$kwMxj-huQMM3&n723wmljnRx z6;t@nb!+`U^IEs`Z~!NcHNIJ2bWCII;g&ONqLl>VJph|R+dUMu>wGVojw-u^QG}QJ z`X)9L(Csr{Ju2w}S<(YHxxr9sxz&diG?-NLVKo2XyzuJfSFC_ zAy$8HIDe@bomuQ3Pac*6C=`ptLw2a*M-z*9K!DX$)RqaB+nTgM71u@=;Gdv$J5}{T zx5jA1{h*D2Y&mFMF6t@a;NFqp7W$kml=SGyIyABP2^%3uIDeff>-6u;ISRp9eU*BA z2H}|SfQ^tuWTYO4sHg-pR$T#j)sfvR&~}U~67`ZV$+S2Kt^J4!Le- zk+F$_oN{CptGu##&{J#g8~~cZ%V#d5aifkbA=>R6qP;ZWnHs6p`;H0zAl1#K#Q<&v zb(5`C=y5=bm)zF&5LP<1EiE70FX(F-14g|s+}|%E#E0SC zJFY$WUcj?2@X$Nlze_ySe=hp32hq}&j>#i^Y9P_wKMfCpJbuRqfKARmy*(R0BLQVI zJMex&?S;RAHfH*1nNlzN-J5~~jZawoM-OxHyRSa%*`ejLiOkXJB=Oz^zq~XLok*avyIF`48k;u{IpcQ z1y!>u4_NxclztJz{p)02s+e|y4V=FdyOYN!ynqq=?#Mp zy89HuX0Uw;WG}OQSOSe~BJXiGjhc~80MPKN#{<&6(r6~K=}NMj`Nj_}70{BjrZGaF z`$lJ+?G-0>h!te-l}<-5^t!0r^9{9`UG+Oezb$KhVR_C#-6!gLxW~qHtK-(-Sf6F&`3M{EuJ$S3?PALPu*`?lRf2^okzttjlqJ5Cy zlv+&h(oxShy5`SV2uktE9BG^Tz+3CeDYLK3lzYSg0@M8gxENMQq)Pz$Kj}u6K13#D zzFJsUjxL#<#?-5;-%mr!lT~JtvS-Va=EJiwIG}?ekdx955|s0gr@anixQ+f5tnYc; zaLR;z5+O5!zT0zuI)Ms|41t!Q6StUJ^jsjirb_nw+IF1x*$#D~;$8L_j@GsJpQBe7 z>(g@zH_O!%D`#>6v366{F-@sVq@Bd?=7`a3c|hnVADXsjbonA~5yqE__Q>mnixWu( zydef1@2z(Mi%`_>(8azo;9DY~NbZkEJ@1)PN!ti9ak`34`2%R>bOM1P(vO>$iW&FS zwsAz_itQJmrgEp{lACo-JC5Lc)A?lvyb64Wx?3OeabrjUomTMK9^dX-u~of&!iJ*=rl^?Mt@TY9%Ub+Vm1@=%ELES&lx%*5G+>*#T4>6H)(KB8O<%J zHi{ymjQ<%tT%y-ZM?#9BmCoJAf}uO+#1$xWmEGIQ}%&#$Ej^OxwPZTpv*-LUJ6 zhqYDTFA(~o=yLtbT|8U0Pxm1;iak-W{4Lym23}C%IiR@!{$p${UyQ^l5z;dQg zCVKV%6ybM#8=6#)ZS_Lz$37jB61pEPuA_g;m!~7X54Vfg@4y(x{g-U64LdOvX2-oO zj2}n&JD|!KW*T8|v_A~(tNURguA!6fhIWf}LvdVgVB={}b6`=Gu$4#t_9nWcdT5mz z@z?0h_(ks_<%$9lcA!^5YD(lXSY&t1z|9{@1cz2{x@o9Zsy=u+toeeM#^uEw)c~bj z)NyqR9mmJ8)Rq7fbjsOXF{@Zn1_`)y2^!q-ap0t2zHr3bg~7vzUOrA^QH1yBkgDDB zbNOh>5eRRS_g!K@{Qct8&T^xI8*YL(+_t@)zJ$4?1nzkr@o6> zSz>KyL7J$=8XUSP+J!$$XvH1wJ3EYxrg5^k^>HzHTILPO!`tZ7P{Xx$g4357XUobK zivwSx*|pZaS?EaH@9lj&Hc27erj+$TxW(bK!hfbu9w!aG{#_%MCuuo77>W zh{A-Wx{d#!z@y&p#5`LS*DTp=X3~!2vev$v2V9=ON&k(ENu3-@f>v5&YzB z=&;WM?$8pMe4T1<*-mV7gw)@;q?367!$7)cCOj;Zz> z8OBlp6=J@c9%POdKSRqd1*m38UFH;6@?)!TsXcDmX~L)y{RAVJ`<|b}IWO>XpS{Km zuQNQ&c~#;F46m!mWwxe@L%?=srP>BOz1xgK!4=tT5aCli!Du=gk{-O+QCoNw!Bwu> zSF2YdtxJ(OV|%%-!Ld09X1EfFeQAoc_GVjyz>Y_t&tDxON#Ruf`TVV0#@JiDrHdRgHC)=x@}EAZ$7?OR@0V1?S{uGnJ%X;f*^?_1|M3DPlXpUwV%|< z?{Ny5UEM=kC;;iXL96@ukhPZntK#yNEBOu|bR4~&FHsG@B`WAh;!QSXTR$xipqY9lc_#s| zh-8nwyQy=St1OvAXxE4 zPBX#dAVQHh1s|e@=04u#Zp_P{6c!1GgrwtL9#1eZlJ;ejU(s01jEGP4f; zkw)Pt0H&SCX?OMZg@xkRmAN|6 zt|~eyfCkAvoDfzu@y&4DS0d=1`(Ys3sE!HrAH1xErR!wFB_lV#UyNywYFCx9zqvT^k%omOn1+Qe_hF5MKAoN4_%NfXFO7DIeX+K%ky>%6XeJ z#Yj4crS+6%jP5D80LjB=G*Z`H{IfS8fvAnYMVnHZ;`o z`%J~Z>XPh_QaA*g`NiJySTR3yD)eu}$10>^dxKE~2RimL>(z4c4Jd#V4xRdSej^>6 zx~JR6w*id4-ODl!_~u7rw1&TrEB5C!N_t_*1}F!XqEIX5mLlJ)M{TmmIkZ4XypCVL zd72{Bm?RZ67%0oglu=i38l0jPa{XzT%^3!A57`Uis)iM9MlQ$})!0DTeravZ)@j7k zK2Z`vo`8`Z(Wr@8nXVFi$J=(qh@5vxaR=_+%XfAW=!q)NxSrBR_)DyvIqz{7M7Ilg z9gVgJWy}X3C~~fVhV({!h-hH#%dmAWc!w`Le~Ha1D|v)H$5pt;kb9JU06;=t(Ve3> zTtb;krYg2k1Z+CeOoB{t+)2r;q!PjazJ0_5r{IP2>I{3xGEB76JeWkIkPUa46kkeM zb}lf0`KA>&JH}Va=KXYo)}w{#8d*_uKO&n%;l! zwd6*`U>npAT-hq4y|P2!hY+8zdXdzi6b@wDI#9 zUAil0I7`28xXj+OQbeEf^i1|SqXQ{A(M!7qSx3)(;3C+SY$RZlUhnk&UuaO-iVX>@ z%Ld-T_w^;`fP$0GC8I!8Mdf)=mD>;3Z#yaBmTPpR<|fSMn^jwW(^T?6N@&zoG4@_; zV5n(;r<>xHsJ>kOxGmT_uaudf*}2F?iVkkpVkCY}n%%_CLsW$41h#C9V5bFQTm20d4d7N~TM(&M!!`P)w}_hK)w=g&92 zw_FR66~|65gACnM_e4)Ocm>tw&dK-?kF&-rUXQ$4Z&GZ8I?4P2j?=-DP*+IL@X5>< zF9iLN<3xSrWELdYc1zBk2ThT&xpzmP6VIj+xw@tHIN(Yv?$d3mjyOGAC*bwX<9b5m zmK0MUc-zMu(W*GrC<7c53a`C!RnYyv@M%{uj6+gy2mNH!WW<&z;5wB&*ptVD;2)%e&~A@%RMRE z_Do781k=#JOUbu@-67EY^7|7Uozg}6{fEHUE7)VCUmfM!Yl6pj48tsIe~r*O7!$y1 z&C!06yp7m!L=0K9385NM6_gaD^$Ks316DcG{3n3p=NmxbE6uDd)VOzUT%^#$qE)C!A! z!{H!J&v5X-EWYZqux?SSUeA+(5x8Gpa|o((#l?1%GfAdY@`M}G|bUQRXA-+?P zGiV2GS9-{mvM~+%d%7|Y%6>zCipy$hFd6Gz5DA~`HP5-p-QdHZT+tpvn1Vby@FNGa zLJuG%hYSg2Q>a&{4kT2?x=Jcc-G%y#`&fvD96i$-(Z)h0#!X*06(`ErP34$f-A83a z5SBK-+$}174_`wm2#s;YzqL0;Z1Zq$%SDva;;r9=8)Sol<7OsA#&KXwQevmLFFnyT z>L-xLgru=JYjstoYD_d*-sLd>hr+;2V(!LWfRgXORI~T;eAoWIAZ%3n3Wbw-=?y!s zx-+vVqLo22W_mMudKP-jxT(Imsy5ZM+P2NJgrzo4g_y9;TtlYI^EDLN)8fs`>QXbp z(8lhKS5c$yzsyOz$of<*!*TPcwNK2$EgF2}JNr(e%Rp{HBwR-0!76#u28vAx)H?)F zvNC^V=D`v?)r0Fy%#!9RMcg3#$P)3AaoO`ATdV^tasX6oQQ!-Bt&J`YU@7}D@+;2} z+J5b*qHqf7BLVJaH8F`UnOw1XJAYn68!#>+>W2}Mm~h4;3(h0?+Q@L*OEDY%6=~-) zWAjS?*m`s4e9TbWQop3kp-1VR0%rN-IfYG82nPoGi0#R;FJ;aQS>x18(C1b8>t+$G z#{FEjC9$Ws6yCmS^6FB<##T-HR(X1l`-mA2MBy8s(;9zkll)dWba6ilEc#LdG=<%Le&Q*M7fay9Ql1R{@ z-EBm}@uPC)jV}+tZBd$Rv>L0U#)>U*|9^~~W3wnxv|W#F+qR8+ zY}>YN+cxg8ZQHhO+sq9rd3i~y@&o!)SNG~(d(1hGYOaK0tS@+eI(3~p4I;FS-7DrQ z@qXP}^+rq(&GW5lYt>JNc6k#TsnUBK+j2&eAfrPeEg2cQlC!kcg~=4;X)fl)V_Kk=L4s`>&yswQY6y zw!S?+(G#J+;N8SU@`i0*Cr9Jnq)-5l4qK$CGJQ4au7&*D&Xg>SKoX&=0?yQ0kLk|2 z5AJcFWl1NxDTlAi9Vq5q$xDYEA181%Ml+S6T0hm3F+c6c7o`lss*n^s)U=N9P5!#0 z`ii1<7v_@>3IO`A_^*(NP3HB771bTsZd;@=d4*r-;2md4KrQrQGiYn*5ip!<0wL;i z+Nb3D=DE!pv20YefSP`q`Gg~FoTmHdC^PTsSI|Iyxh@a&own0}n_Zg-D6)uHcRrW< znhy#`LgYR)aJ%kq;=79p^FT6VP!kFRok~$kj4nn^I%Nw+(6Wko54?i`#9g_58Rl|+Om1_tMOgC zn7CNBQAXKZZu%yxV8zGAIo_a6e;Lk;w9$i4(+$-dE&Wx+%0+YZpQWXtgs6)7L|wSw zwC_zZKWZGZAx%mh5bGXN;C24O8&B>Yu?R~eb{MghHNzRC$$$z4VRBUwToN&Xesw%ni z*#5AnN~w{&T|fyI(70df-$OJD@gRjze^a&^SD~wa2Iu4w07h6Ic~GJk&NS1Z4+7Nb z0khHi5R6jt1D0W=4G#LYNYWB0QH3&EzGCk7(PV8;`l&f4h_(=X*5N=yl*B=5o1)74 zU{{6dfvPnP(JEAmcKr{PZb+_GJ&_7X2kSoqNo}xc%d!VQbB);LjZ7&$H_@)-icf1d zz{c^Jfciid<9MG{1=1aPH1-}0Rv-aa7VINM03S`5IGuNg5o|Sn*Pn!9Zf|l zJ(Dk&Qm|~O?ClYRP4A3_HJoIFVdE~JE(5G0LIquG_h2Vei0#VWh9vM6{}+v!GAr%_XYL@eT{wOk4pi2M=qRHN zl?}*sgx2*?bn`wY=Ob|}#<&f7+)50oR$1*hf||$xlE2N~>(6K{TFb$=<+BEp^J0)! zs!o-Dby=#<_7DN7?LZ0dU}}0(X5zFk$sy&i(W%p2W`|HAz9^OkvrcMi7>dXwVTz#5 z-sh5kc35ncrORQ`MN9`F<#t4siNpA~Ju=|NrI z52@=s%hO8tJdleIOAvJJ^#2z999i|JAiqgi z2FubCS9Q;iEPk>#kx}$&>@qcq4^uDX^K+8$@inOd`_4>ZSpEyU5g*U%d(FkOfyBd|Y%TG>rhtGC-sqZXV-PfOhVbEWy&b z?xIS$jHIqYU%pIHg~O*deRR*({_M_Vxt)A#^1P`!N-v~-M#{laXvhfb%u-KB#-mw4 z#`xN&!JoSXH1iZ}bJklf9wqmqrk%z={io!aMl>5V`P~E4A zTum)N2YmS`{0C&e*ol1NoYvd%p(gpphJnLY; z%vywL==^UP{W6^mjWQ&JQB81c*188vDU4XA8uMC^WANvbB&eIonkCLF&xG)caIoO! zMAhLI<@dpS)r?^dcg(xWAlDj?MQQ|b(FzQ(FRCGbywEFFi|GwslhIlBsh7o!xFv-$ zqqwNU`A!mdzE;6$ClPrQ4DnJTyeERVQU=|F(u5=;9w)`EaXO8wZeS;pm&&+KiZO-b zMDyyXajXR5x5+Q@L}ywDsxOuJ2#W|0v(SF;@Ko^f0lzIk#-3I2?qDA23IVUNg4oXHBOb&qz&im3%P) zmLD)I@?HiJo2N-`AAOO7)#E^BG<3BL^c0H2%h-!*2w7I3LMFR9vT`Xa6Sxc{PizH7 znYoDibfA4{&Uqg*PWm%;$NHH=ed1uq!|lBdvE>S)d?VSRYq5>jpzm~UL>U@UH6AZ_ zpF&&`q9wZj^LRana5i^~nZ(o?gR_XF@q*^A5?9VMKmZmL5=%v8i>gcUl@uadmD=XA z%2?e`7|h|j8|(pP_Yz%09ZOZ9MJ{FvE!HPhLyyFvZ3@N6tYcn|T?`xPVm6DUslSh= zMZ8B^Jknn+`<>~&-e}b{r^*tMW?TX&s(u^S>D06cinJYkGoa$lrc)LJk#IXi+I=iRYY~k^FjhDx`p7qdFGU#vQ9kORq}p zJJq-z!S%K0BN%SS8u|^YKfD;UfG-r0@b z+m`F8@bF9r{kCIa_*~v?hu}1AsCRkV8v%|sKS^#!rQ8jLdcs zHX~Ub(A?<;FEBf7FmZw~pMF=wl@ckg)ldbCLj{KQrH!II1csnVPM07`Gidh|aqI#0 ziX=5Uk5)C_lQUsca2Y^JZ&{S58F@zD=iXI}Qq*AEFGOFL zy&0>Y^a+yIfwq+!hDlcAwWPfg`U|ca z`0yfuyhEsA;ne1kF7A(5a*UdiF43QXVw{*sD0zAdTj@V%dLCo5w%I&rZHK+m)cB<7 z^{3_*a#R|%U&fFGY{)9p;eL#g>C|5q=W0`zCWm-vJTM`EbU+38h3=-NieE;>V!dpn z>!25f^fF4+XQ=crvE4O}X%>&D4@0$7KPPC;DV44%8jG@|g@#TX0kf8GD5SUwSva<7 z&K;Gm9U6;zW6)zY(W5mlu?yWGFM9{@zN8iDSNwU`f58@+e+~>H^*wv{#1}{6Oxxd< zch?g}bemLoELKj$8;kH|sM)cmNw+2=~t2d=Q=C*bMO4j{E<4d|< zrJEn6y>>B~b;~@0Ncs!14(WAO=GzY6hcMbpj35%x*49x;rtIw*>-|I3nV7XOs``*W zO7oznq*qa*_L8%=S}WK96H{F}O*Xa`o9$aa_ltB@C2T@0_;cI9Tmg*{i8MSY2;&R0 zDLMY6Pd9%u$=KZ7a54loD&r_9x-#VhF-L!+Ob4V{CU5-UotRn7+l;I${1k^gO6Ow* znC3>C^a}y7L5}`z4IJ(ks7+L&HQaPrd$LKJ3>I6gCUjLxzEsTwwzN1y36OqaIZ>XO z>C^r&UvHW55#D0*%Z1)b5n?JpeE%Cr8yO|zT81uWkcZ`ktkYe9cg}b}1TTtHZoH9Z z2cw~^koy)<3NuT9y9eb?vL3NTdqqA_m$GH+MGcW6B6`OZGd!JfkYrR=V}m8AD{Cib zEZ|RKl@?h}=hcfh1y6F_=Oz-9lch;i#@Ci!exf&7#KO9T;9c>|Pp|`-2ptcw42vTt zjuu~F1+PMnbjw9JlUy`(l^ELgktt+pC7WApyT!hC zb{QikuoBuUhsUbNM=b_DT{%%&ca)Zn_F7SXTE$+QjypD!WHAn+dS)Bxt~Zv)Ec6kS zTZ)XrjgOjMy{vMAQ13bKxf7QTTv}PP>ko8h)d`y}99A#vR=2jMarI5#sJ3Q+wTPJuw(eXQwzb8a2v93GW=G>hqhc@G%W!C3( zU{g`cLo!kFP3xX~lLr>3LC=d3Tu%4a7g*U{!2Z0y=(9@Pq%fld%8LL{otXuJD9a%G z%^B0FH}E(YO38e6iDisqekZ6b$$l#ab$UB2^jMd5CcfKbuFUG|13d-UePO$U@)viD zYG66j>DCAu>z0}Z3?^2V$5JX*-K3WkhdknMjJfSrk)1ILO0p;lb!`15e{iey*IC4s z7_3#JNwRKY9zWOR+iThN6JZbhHdH{tY`8_wC4QOT!dq^3Dc{P#5I08gzr);V*S}_c zB5CM2XxFQZCqHPKC1^@gCumgth6jBm9LMdl+%H!WTCJeEU`My1@V!5swXDQjmXj?j zO~UONr&k;q2bu{7d>yL;Bdnhh_Tfp3w3NuIn~&!$#|l(9GBRN_CpM22k(VHOa^YQFk<%mL%?|^3~ACEABOZ4x(%_?UC}jA>D_mqYt`ilL05!X-&;WEcGDkae}%8 zkqek~&}kJI`L0aM0WAhiub>#SQ{kJ*xdUnn+@43Nr3#BEV43~>O+JXtbM6NyuF%kt z(wdk98ZkR{V^G2+?xj6tF1mae>eQEUKVIGk>iq&2?C|OxkMRL>SQjhHapr4{8aQll zkAA4-;|K^wxU9v-8~d&Nu4mAL;gk(bXZizddnDQIyX%}%i$A+DQB4?TCp&1@m1G=| z8o)x;yqU==*S9Kyv3`zu=y2Fp&tF%TQ-PP=Kcg`Tr4co>-e|Ffs9Ql1b7=43DM!mk z)+or8#;&QnIM{BD33r%;Bx1A@!mH%DdILIb!i-99%xuU&IpMRmbL1&5uS(bch!K*x zNp$PFMG`Aj*``5_JUIbduEcOwCC&Do=zjuE*oWQ{EXnq=Out+pr62_S^X@n_dKvf< zr4cM5&jvnQaYo+Ib<^vKsFdr{Pri)VBas=RJ*`Ta;s~h=^0!q6ZLnhh*#tSefhmeK z5ep1ui+##;^g$60^R^v5@VSmt7|ivSuldH`TrVJ>H0$70(qP-ifk+qehI6>xa4z`& zTbzL&=XXU3c1jd6;vp)-0BXwuO6O(tY> z#-}5HJmbsMo>}{0cwLR*v0r;M#8ui->|n8XtfPECIQ>jaD;G@2f=yylqepPnf_eCa z`GQ4+MbZWb7wNnKO5zb?Fyt3k$Nms$u|~M-z!Vb;0u;reG1lXc2i7kdd#CL$ zpSjAIr5!L8{G8_C?Lepp#@AnPY(uz2;RNY+)NKUHT=mcBFKEKLroq;RsH_!ah!nYK z4}EJI{ms+jw~nee-Mp!aT{GOCfTWD$gKc;}j5vx6lUZ&>1L8eK@ln~v-26w7{9zi% zct%?5bWkcYwe%$!MSY2Jf1JidMkCtv{0OAIqq>yEgp$Mu=k_T&FBjoIz@WK=G5zXz zmqf^O`yP68HVvwG1bw7x#iXPM6mA362b zcN48yPxua~kFC)pn`+o+faesF*ede)r=gZBqC67AlHywJVtlyB(PSi`0ymK>GgmR` zi!_`m!lhoX=>Pj7~1t1x6|E^l$0YHp}PZRXe!(UdW3{XP7j{ z^fmsaSmaJ@@9mzSkz%!ND2I$U(WZZuYaJx;9M={sT|FrKA6q)sCd6GF94NL*KgjD& zQa)u!N&>eOXO)%^4v=6^wi$#)Z$*JA>6E5j6sJy-rl}_+CoNg<#v2-3a9zbMTfivj z2bvoaUXxw1l5s;IwpK=O8wAXcsU`KMnVj`CCMTCjBRt333KKy%XI$wEEbJIs=|Bd5MBUIEXdSD z*_mk_sYjs{YxfdW5z#$4!Pl&b^{#}_eIxt>Ih|81a=3@WWGe##GdDUsAPcF+`i^BEu+aC6Ud&3L9}K6G`dn<+K8gfdNp@T?0(T zp``@fk!fGAco|~_O+2(h?e%tuC+{kjolvi$obY-G6xYV?4$}&O4TmMhK>NtNjnPhv zv%V*MWd&Aph+=4yaMA;x!xVC<$8}MrugX?pgZh z(ksK2oDJ5ZLFVWh+8{`x!lF12poA_dUi6V$%^77I1XNOsm!IbBVh`hogK^UsY?gSi zPPMMqpZYk4{soX7@00~eIH1T&>^C|2KG;!Yga10ax&AF%BmpmA%IYIDo)7*KMg;M0 zZ=BRaIs-p44TDrZ#i447c$rJs@c82F)X3kgz_qeGNS!$-f9b)NL`s4yv0W-5Ln=Mq zFxUZIX<4*&0hYNll}t=tKY`XDGUVWW_QgVD&&gZWl|$a(--08@Jvv;#E@FVd0yf6; zZEqZW#maMNO2gZX{<(JhqzC_(W4?Wh&*3xmzG~Fs4;tN!VU9m@3T#n=zLvmcz%{J? zNKQ*z&`#dcz1)yR2*=nv>`O2I zdkgM?^=&zoG=m;1j!h+Uu~G7{mgAkV7i{T0?+P*A}O;CssQpPB^RI!d42 z{}EtbS2?nB#?s!|A;-BfHI!&ouDs?&{<)JNc3Nv{OvGrNMx}hg{QBN2JskYy!}xOV z`u!O43%$@~{iCx5;u#)VdikNJC8f=mzVw@EjE`k>eyuJ#eYKVnZ-QhRUgXy z<<_2f$K|=v962aGH<%%#RXlC(J@i>CAC(wUxw;RZXo72_f$rd&8|&-}mc-w8 zbT@7c@!@RdWfi7#=aYd1GUJXsn+YQICvf_jUZIVo&X^lV6nW~l=JD7vOF#LUF%+p} zw|mzp%Al&QiP-eActwtMQ()grs~or3^?zdl2)r-s7-X6om2RB-}>wzF7Ty|B0tLK=EO~1n@a@7U}^Lu7`3xha#T!zXOrHT;BGNY zGys80w`$pu$cVE3HNP0+!6U+K?jDm3K!owP@ea2`c^cwn{^Jo6<4s%v;!#K#72!{r z>^zL51b9OAr5e{kb0nHtV!~I*{O+j>4a2`I`v{Z=Oyac-$1d$xsv-9hBR#Oy2lE!e zQmPg?>9e2)VE(yUG~oyGZb=q-U*(#7!rhKUya8|ZUUiwSD{Z8~#rEU3WXY|TZR)hz zP+6r)w9>fax;s$koH#lK&YqTM=zUyfC7fT%TGjrs!pusRA>91Vmj=@1cq4Q^&fmwY zUxhd2k2n~;HP+BVVn(xrIz_X}3A9ng9WZ1yXeoLo7n^QxEPWM#fa#h)0Kh-2pMTGF zI9bV8X=xVziP&D=V??orTt+02!A^?qne&+&j5y9#j+1&EMgzf9T>Z>i`84QdlE5l+ zDFmUUpD&Fgb18icv+S;_hPBu;HlFt2Y1sOMRz-$LVGQTs!bIVj+q2C_g7KV9gVxMQ zb&kv^!MqNZ_UFIn+t&L?io$bEU!bD#>TUr5IR7x-nhad1aCtWPWh|)|a0j(2TiCJ4 zo~rvOQQMKP3M{A9-KxRRq6lAOv~C#WkyZRy7nb_bgJdRG6wv9QFN2a{mg2}Z1EsS# zqgMwktBI*hrifKj$5*7%CTQMfYS9sBMxPsS9W2O}TV1$@sIDw`l}xD;I(;aDwuX|W zsO>LE<|9qt7?%_UKjv58pDXjv1E^948oIKpuaT&)8ka$5U1h}5WhO7wbuJ6%^mt~i zx6hPo2!_xoR#7A5$Qss|am9@ufQWAZkQkx}UhWtcq&;FN%yGOIqccOvnOZa5#$&4L zg*cY4F1lk7_6o@JpZ=_$9@U`B)D&b*;eHr0jwfL*2=^I-C707>X!pDsYVq!DFj6u; zhwsq2bq+q7-=HzJ>#ePR4qLzY;)WPF9b<7>adYw4l5oPnf$8Dqjse>y#8cEyb;R?s zYYPth9wxy)qW0~cFp&=7SUZ-JKBJ9lql-AUzo*~mK;wNt)ZQKcXW^Kuvp!oOF)7NxI?V5=8&xk@g$8pC-IooVDQ`t z5yk;EmwVsuCei~@A_K|YV?GH{B7&J0{)a2h3TNPf22)<#@Z zNQc^nK$*di%&|01Fv5WSd~}V)tlZl?J!Czkt79&@QeQnv(e{huqU%eRlQ<@uv{ffh z%e$&pP=8>nk>)|f2^B9z>l-|dp(iG(qF7+Jl%bQa)-C6w%kczx&JMB@!7$R3(_sU)mW&k?shEi+Y-A2$^M11E*+Atkzph z@5D4+mC*7mt1()NL%Wb{DetJTE6Jw9lFKai|dHgQJTGE{SS%D#bF& z=gKh1Eh+#^idjmwOe&q8pTu;EyGkTalU+nIYG)xsoQ*txU*wuaF!Pbi zMUX1W^&$@4kBW@4s!7a=Q`4hr)xJtXxx(5ed0Hs#qG7kR*g*|Q5(eG`xf6nGMkPGT z6sr)gJP;WDP)DGSLK?*~k`u+g31qEzY>!3!E;SOtp}Ts9dzD%%G2Y@(8WTEw+IG2Q zjrweP*>2HUfp3&L_dJM)u$V!VL_37RC3&%;S#7~i0W#2}#n@kAz*wy!WXpO&l^!eV zSh&eL6!@x{`MkG)VR9%(Ugc0VM^0Dl3uj42{S-%9OnShzS{om!ltG!q7RN6JxmFCX z;wie4+Af$^$#|$JtdWp-)_~c^N~$cxl&M&ODU8V!0T?=H5`>KbD>23*LDWnJ4ecw} zfn5yC=lyuGhaYtN68GEFkQoU^WpQktV`8IGe~47k4&|b5{Q%IYc!jLw30eAFx^O91 zyh>j3RKPVhBj@H}xTBR+VU4Mi@yp^1HPOJ`x|rBLtjZsmTBYsG+V=2l-iMPoeF>{a z<@h~+WvJ0=q$klQAmE-rsw_}zziHB$iGXJSd&yeb<;dD6sxx}R)B*Qtyw!4cjWw}T zQ+;Qna;_Btrz7{i*^JU>Ll%a9CNMvU%S!tCpHlaopiG$ZU~>x@>Xl+HG^L-QMlkRhcqtf^m%0 zF+<1Xse0n1Qd>tGW#lZ{ibTX-570SE#s*0} z&7JQo706ykrxH!Yl7eF9886!I{zj=Tsg_O(gikb%hjgl-C3&d8kU=P%7=tXXEGAxjDz(fgS>lXD<7{vj_{-YQi89d5^O?@%Zlfm7Yj^=6ZN(% z0{a#-gIbo$lv(rz;~HGx6>>{e7=bmu-_3Pq(>kONoZ7h!M<#oA`Nzi{bhQoh)SeIP zH0w4pW0jck-NcGQ+}2MDnN zoz8Ps9I#Lsv0@f4+UPUUOAKyR7%hn_AvWVT2CX2v88BS3>Y&X~i=jJLAvJeYLe9Gt zL8~Z{$y^6fpf2`q$uslCkb+V$4JJMWnc@KRYW?*`m0_0j3B9+}RQ2dU>TA0);tj?< z!0L|x=H+^1Oi%&X5mlNw&sc?1Sca3WlcSQGBgic%4BHgH#C@K|EbEC)si%R324aC2 z3Mq2?W@y8kNyS8Jn39mxMpMC6`VnBR8)G9R*z`z{(5v!jGmOy2Wp16)cdesoDOaX_SU z1y9yOU|6An8x_fI|M@ zj4cGR&#x)5x`kljOEExs%CK-)PTiVIRST6xaAw#V%1Z7xI%F42qa}G~O&WPgEhv?Y z>ax43hF(EuYT>FxP&VtS+!td_2A)p*9PK-~z!bWoROeL_NDMy%=uF%!MExmzAnt#M z*jfT{r7$DmcV8}$(elGos&qWIPtMEmdA|engpa8Vd5mY**s|+(6ay6I%>U#2VuBl$ zp(0y<@{*shBbaY_QUG-|5XGrs?gI3lcE2f26Wmg$m_a1Xf_;qEKRL_b6Y}*51pR?_ zI;2ZIgSG)WrY6$tOR6_?{Vbiit6`Jj7kNsceqF~oT~>WaW0?^?(64C6rRo#&JYF+i ztv?I^l_AlbA*vXlreXO-5$In{rWICC_PMC}$gOF`Lhw`(V9Jji_O|`Q4@UmE3!{tQ z^=>k210o()<=v(dv_GoFFC#U@i9bvd$)Y(*m|Uvi5r#MQhWQmSg$>n*(C5PDUEJ4### z`d}bM{Z_%nvTMu58N~EYAEgCznk!`dh=OcE)*H8&e>i5BdRje9chP%gsNwSb8b7R& zpZH)p9{ti*n*_#R#nETH5K2cE%GiL(sFEUlLR47qUJRg#_}TH6q8Z`gk?>tB8qP2wS5S^l&7{zI_~k|dx;?&OrxCCm4VbV34*y&W zGwvhb25DwH=h_YXtxE4#yekufu^K57L@En8qe#v?H)Fsh3&C!*#RxxI8i)sqadCR& z2MR`5{I?h3!T?8w{5=p_@JK1_z1%#!$+iz}91)u~en%I9%3=qw3KQh_t(?AYl zBoj~k?w%L?b*&BI?AjCqx`J265Tz;GrXFZQ>>71#B#h`gFr^9Ls&-ek z>HxpLiIy@jfLphcIB)>)>>AQCr7z-J!2w*pB;)VIJSd-FUU^Zsw0D(QLa=Morz>RC zMcIPtV9HPHGS7kCn1G~Nb*dF0(Jc#ei<$)qOVU ztN++t7S4JbwJQnhP9xIC#E~K>p~PmG1pcT#Teg3NUBpIYij(S*#_7)(k-`U|kRn{4 zG*>gf4e_*%BtViH!zB*l>^hyQj{}Zztm?xYp++=7Q8&rPL2rLu?SODMZ61{6vItV{jg&0$y7|nAt&@ z+F@oYz!>Wp*@xoy^z7V4A1F1%sy{c2RIm?~qsx|%iRyI5G>6N(F|{^9{aK88?rnvY z?6vvhNrAzeZrj5tTtmoJ^Fe7ZE#oPt1{G_Ci?vF`AS-9woYxi^&*9^ltyH9Y2M^k2G#L|H zUli8I2%!-OZ!w$kMe#TO82?=}@)H<9o4WieOGSkwNxJ;c;z%DN48Rybri#jV(e7IF zj1*llzGz?oAKOz_+aOe=f(B=(eu~={A^WNzl;sgKZLzUE)!<_dutL4V4mMLHGs4Yk8scK7LvwhyH2mv*+spYm)b{0uvV4Y& z${Ofu*U4+57xhu|Yu9=FYDaXDPK+o}DeD&wYf&~VZ=w!%MhJ)l{=!3}iPX(E<@e}9$YCzi5q>T#nd)3^dyjmA*`+< zB9kNnxj`(n#*3ehqeaKx;!z*)gr<9ksrsSYIJd)0mj<+%}2Zxg|_A z>`!$`VH-x%o%flAaeHs{b zJ8$j~{`x#2yxW%46aZiZ2LJ%;|8Z0Pvvo_u#tw@U;U`=28-Bim>sk4} z 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 DD#8JG literal 178 zcmWIWW@h1H0D+kb&K_U}l;C5KVaUl$DlRH1&^6LC&@<2v4dG;9e#E^ojPHM9TxkV2 x10%}|W(Ec@5#Y_pB*%=)BnhaQ0t|m0K}>XWSs~_Pn90fpQq2g2AwU}9JODC)96SI3 diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index 998a4fddb..b2c69bbdb 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -253,7 +253,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then echo "Building srtp2."; ( rm -rf ${SRS_OBJS}/srtp2 && cd ${SRS_OBJS} && - ln -sf ../3rdparty/libsrtp-2.0.0 && cd libsrtp-2.0.0 && + unzip -q ../3rdparty/libsrtp-2.0.0.zip && cd libsrtp-2.0.0 && ./configure --prefix=`pwd`/_release && make ${SRS_JOBS} && make install && cd .. && rm -f srtp2 && ln -sf libsrtp-2.0.0/_release srtp2 ) From a36ed6954fc71652636bf3485eca494b48f0b966 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Fri, 6 Mar 2020 23:59:59 +0800 Subject: [PATCH 09/21] add rtc.html temp --- trunk/research/players/rtc.html | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 trunk/research/players/rtc.html diff --git a/trunk/research/players/rtc.html b/trunk/research/players/rtc.html new file mode 100644 index 000000000..4dd8e44f2 --- /dev/null +++ b/trunk/research/players/rtc.html @@ -0,0 +1,90 @@ + + + + + + + + +rtc_media_player:
+ + + + + + + \ No newline at end of file From b730458d5145439123485916677ac43f6b5fc2cd Mon Sep 17 00:00:00 2001 From: HuyaJohn Date: Fri, 6 Mar 2020 23:28:15 -0800 Subject: [PATCH 10/21] add candidates ip list, add rtc_upload.html to test --- trunk/conf/rtc.conf | 2 + trunk/research/players/rtc_upload.html | 97 ++++++++++++++++++++++++++ trunk/src/app/srs_app_config.cpp | 17 +++++ trunk/src/app/srs_app_config.hpp | 1 + trunk/src/app/srs_app_http_api.cpp | 15 ++-- trunk/src/app/srs_app_rtc_conn.cpp | 42 ++++++++++- trunk/src/app/srs_app_rtc_conn.hpp | 11 +++ 7 files changed, 176 insertions(+), 9 deletions(-) create mode 100644 trunk/research/players/rtc_upload.html diff --git a/trunk/conf/rtc.conf b/trunk/conf/rtc.conf index 5af4172b0..fe8d545ec 100644 --- a/trunk/conf/rtc.conf +++ b/trunk/conf/rtc.conf @@ -23,6 +23,8 @@ http_server { rtc { enabled on; listen 9527; + # candidate device ip: *(all interface), 192.168.1.1 ... + candidate *; } stats { network 0; diff --git a/trunk/research/players/rtc_upload.html b/trunk/research/players/rtc_upload.html new file mode 100644 index 000000000..68c515d8e --- /dev/null +++ b/trunk/research/players/rtc_upload.html @@ -0,0 +1,97 @@ + + + + + + + + +
local_media_player:
+ +
rtc_media_player:
+ + + + + + + diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 315ec58f7..babb870cf 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -4255,6 +4255,23 @@ int SrsConfig::get_rtc_listen() return ::atoi(conf->arg0().c_str()); } +std::string SrsConfig::get_rtc_candidates() +{ + static string DEFAULT = "*"; + + SrsConfDirective* conf = root->get("rtc"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("candidate"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return (conf->arg0().c_str()); +} + SrsConfDirective* SrsConfig::get_vhost(string vhost, bool try_default_vhost) { srs_assert(root); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 1f85550f7..97d8f4851 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -496,6 +496,7 @@ public: virtual int get_rtc_enabled(); virtual bool get_rtc_enabled(SrsConfDirective* conf); virtual int get_rtc_listen(); + virtual std::string get_rtc_candidates(); // vhost specified section public: diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index fb33382d2..5e1486093 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -850,19 +850,20 @@ srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); obj->set("server", SrsJsonAny::integer(stat->server_id())); - string candidate_str = "candidate:1 1 udp 2115783679 192.168.170.129:9527 typ host generation 0 ufrag " - + local_sdp.get_ice_ufrag() + "netwrok-cost 50"; + // XXX: ice candidate + //string candidate_str = "candidate:1 1 udp 2115783679 192.168.170.129:9527 typ host generation 0 ufrag " + // + local_sdp.get_ice_ufrag() + "netwrok-cost 50"; - SrsJsonObject* candidate_obj = SrsJsonAny::object(); + //SrsJsonObject* candidate_obj = SrsJsonAny::object(); //SrsAutoFree(SrsJsonObject, candidate_obj); - candidate_obj->set("candidate", SrsJsonAny::str(candidate_str.c_str())); - candidate_obj->set("sdpMid", SrsJsonAny::str("0")); - candidate_obj->set("sdpMLineIndex", SrsJsonAny::str("0")); + //candidate_obj->set("candidate", SrsJsonAny::str(candidate_str.c_str())); + //candidate_obj->set("sdpMid", SrsJsonAny::str("0")); + //candidate_obj->set("sdpMLineIndex", SrsJsonAny::str("0")); if (r->is_http_post()) { obj->set("sdp", SrsJsonAny::str(local_sdp_str.c_str())); - obj->set("candidate", candidate_obj); + // obj->set("candidate", candidate_obj); } else { return srs_go_http_error(w, SRS_CONSTS_HTTP_MethodNotAllowed); } diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 2a570b4a9..65cd4fe10 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -39,6 +39,8 @@ using namespace std; #include #include #include +#include +#include static bool is_stun(const char* data, const int size) { @@ -71,6 +73,33 @@ static string gen_random_str(int len) const int SRTP_MASTER_KEY_KEY_LEN = 16; const int SRTP_MASTER_KEY_SALT_LEN = 14; +SrsCandidate::SrsCandidate() +{ +} + +SrsCandidate::~SrsCandidate() +{ +} + +std::vector SrsCandidate::get_candidate_ips() +{ + std::vector candidate_ips; + + string candidate = _srs_config->get_rtc_candidates(); + if (candidate == "*" || candidate == "0.0.0.0") { + std::vector tmp = srs_get_local_ips(); + for (int i = 0; i < tmp.size(); ++i) { + if (tmp[i] != "127.0.0.1") { + candidate_ips.push_back(tmp[i]); + } + } + } else { + candidate_ips.push_back(candidate); + } + + return candidate_ips; +} + SrsSdpMediaInfo::SrsSdpMediaInfo() { } @@ -139,6 +168,15 @@ srs_error_t SrsSdp::encode(string& sdp_str) { srs_error_t err = srs_success; + string candidate_lines = ""; + + std::vector candidate_ips = SrsCandidate::get_candidate_ips(); + for (int i = 0; i < candidate_ips.size(); ++i) { + ostringstream os; + os << "a=candidate:10 1 udp 2115783679 " << candidate_ips[i] << " " << _srs_config->get_rtc_listen() <<" typ host generation 0\\r\\n"; + candidate_lines += os.str(); + } + // FIXME: sdp_str = "v=0\\r\\n" @@ -150,7 +188,7 @@ srs_error_t SrsSdp::encode(string& sdp_str) "a=msid-semantic: WMS 6VrfBKXrwK\\r\\n" "m=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\n" "c=IN IP4 0.0.0.0\\r\\n" - "a=candidate:10 1 udp 2115783679 192.168.170.129 9527 typ host generation 0\\r\\n" + + candidate_lines + "a=rtcp:9 IN IP4 0.0.0.0\\r\\n" "a=ice-ufrag:" + ice_ufrag + "\\r\\n" "a=ice-pwd:" + ice_pwd + "\\r\\n" @@ -170,7 +208,7 @@ srs_error_t SrsSdp::encode(string& sdp_str) "a=ssrc:3233846890 label:6VrfBKXrwKa0\\r\\n" "m=video 9 UDP/TLS/RTP/SAVPF 96 98 102\\r\\n" "c=IN IP4 0.0.0.0\\r\\n" - "a=candidate:10 1 udp 2115783679 192.168.170.129 9527 typ host generation 0\\r\\n" + + candidate_lines + "a=rtcp:9 IN IP4 0.0.0.0\\r\\n" "b=as:2000000\\r\\n" "a=ice-ufrag:" + ice_ufrag + "\\r\\n" diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index c40076686..b9d60d07e 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -37,6 +37,16 @@ class SrsServer; class SrsStunPacket; +class SrsCandidate +{ +private: +public: + SrsCandidate(); + virtual ~SrsCandidate(); + + static std::vector get_candidate_ips(); +}; + class SrsSdpMediaInfo { private: @@ -151,6 +161,7 @@ public: virtual ~SrsRtcServer(); public: virtual srs_error_t initialize(); + virtual srs_error_t on_udp_packet(srs_netfd_t fd, const std::string& peer_ip, const int peer_port, const sockaddr* from, const int fromlen, const char* data, const int size); From c62901a3ac767110fada3449bfd2d478a5e91404 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Sun, 8 Mar 2020 00:30:31 +0800 Subject: [PATCH 11/21] make code easy, wrap udp remux socket --- trunk/configure | 2 +- trunk/src/app/srs_app_http_api.cpp | 1 - trunk/src/app/srs_app_listener.cpp | 64 ++++++- trunk/src/app/srs_app_listener.hpp | 31 +++- trunk/src/app/srs_app_rtc.cpp | 86 ---------- trunk/src/app/srs_app_rtc.hpp | 52 ------ trunk/src/app/srs_app_rtc_conn.cpp | 232 +++++++++++++------------- trunk/src/app/srs_app_rtc_conn.hpp | 63 +++---- trunk/src/app/srs_app_server.cpp | 5 +- trunk/src/protocol/srs_stun_stack.cpp | 1 + trunk/src/protocol/srs_stun_stack.hpp | 2 + 11 files changed, 245 insertions(+), 294 deletions(-) delete mode 100644 trunk/src/app/srs_app_rtc.cpp delete mode 100644 trunk/src/app/srs_app_rtc.hpp diff --git a/trunk/configure b/trunk/configure index 047f41883..6d3da2bff 100755 --- a/trunk/configure +++ b/trunk/configure @@ -257,7 +257,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_edge" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static" "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds" - "srs_app_mpegts_udp" "srs_app_rtc" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" + "srs_app_mpegts_udp" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" "srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr" "srs_app_coworkers" "srs_app_hybrid") diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 5e1486093..bb892c57c 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -46,7 +46,6 @@ using namespace std; #include #include #include -#include #include srs_error_t srs_api_response_jsonp(ISrsHttpResponseWriter* w, string callback, string data) diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index 2c1703250..c8ecec2c6 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -220,6 +221,57 @@ srs_error_t SrsTcpListener::cycle() return err; } +SrsUdpRemuxSocket::SrsUdpRemuxSocket(srs_netfd_t fd) +{ + nb_buf = SRS_UDP_MAX_PACKET_SIZE; + buf = new char[nb_buf]; + nread = 0; + + lfd = fd; + + fromlen = 0; +} + +SrsUdpRemuxSocket::~SrsUdpRemuxSocket() +{ + srs_freepa(buf); +} + +int SrsUdpRemuxSocket::recvfrom(srs_utime_t timeout) +{ + fromlen = sizeof(from); + nread = srs_recvfrom(lfd, buf, nb_buf, (sockaddr*)&from, &fromlen, timeout); + + if (nread > 0) { + char address_string[64]; + char port_string[16]; + if (getnameinfo((sockaddr*)&from, fromlen, + (char*)&address_string, sizeof(address_string), + (char*)&port_string, sizeof(port_string), + NI_NUMERICHOST|NI_NUMERICSERV)) { + return -1; + } + + peer_ip = std::string(address_string); + peer_port = atoi(port_string); + } + + return nread; +} + +int SrsUdpRemuxSocket::sendto(void* data, int size, srs_utime_t timeout) +{ + return srs_sendto(lfd, data, size, (sockaddr*)&from, fromlen, timeout); +} + +std::string SrsUdpRemuxSocket::get_peer_id() +{ + char id_buf[1024]; + int len = snprintf(id_buf, sizeof(id_buf), "%s:%d", peer_ip.c_str(), peer_port); + + return string(id_buf, len); +} + SrsUdpRemuxListener::SrsUdpRemuxListener(ISrsUdpRemuxHandler* h, std::string i, int p) { handler = h; @@ -276,19 +328,17 @@ srs_error_t SrsUdpRemuxListener::cycle() return srs_error_wrap(err, "udp listener"); } - int nread = 0; - sockaddr_storage from; - int nb_from = sizeof(from); - if ((nread = srs_recvfrom(lfd, buf, nb_buf, (sockaddr*)&from, &nb_from, SRS_UTIME_NO_TIMEOUT)) <= 0) { + SrsUdpRemuxSocket udp_remux_socket(lfd); + + if (udp_remux_socket.recvfrom(SRS_UTIME_NO_TIMEOUT) <= 0) { srs_error("udp recv error"); // remux udp never return continue; } - if ((err = handler->on_udp_packet(lfd, (const sockaddr*)&from, nb_from, buf, nread)) != srs_success) { - //srs_error("udp handle packet error"); + if ((err = handler->on_udp_packet(&udp_remux_socket)) != srs_success) { // remux udp never return - srs_error("udp remux error:%s", srs_error_desc(err).c_str()); + srs_error("udp packet handler error:%s", srs_error_desc(err).c_str()); continue; } diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index 3b4d96b80..fc25551e8 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -26,6 +26,8 @@ #include +#include + #include #include @@ -33,6 +35,8 @@ struct sockaddr; +class SrsUdpRemuxSocket; + // The udp packet handler. class ISrsUdpHandler { @@ -61,7 +65,7 @@ public: virtual ~ISrsUdpRemuxHandler(); public: virtual srs_error_t on_stfd_change(srs_netfd_t fd); - virtual srs_error_t on_udp_packet(srs_netfd_t fd, const sockaddr* from, const int fromlen, char* buf, int nb_buf) = 0; + virtual srs_error_t on_udp_packet(SrsUdpRemuxSocket* udp_remux_socket) = 0; }; // The tcp connection handler. @@ -123,6 +127,31 @@ public: virtual srs_error_t cycle(); }; +class SrsUdpRemuxSocket +{ +private: + char* buf; + int nb_buf; + int nread; + srs_netfd_t lfd; + sockaddr_storage from; + int fromlen; + std::string peer_ip; + int peer_port; +public: + SrsUdpRemuxSocket(srs_netfd_t fd); + virtual ~SrsUdpRemuxSocket(); + + int recvfrom(srs_utime_t timeout); + int sendto(void* data, int size, srs_utime_t timeout); + + char* data() { return buf; } + int size() { return nread; } + std::string get_peer_ip() const { return peer_ip; } + int get_peer_port() const { return peer_port; } + std::string get_peer_id(); +}; + class SrsUdpRemuxListener : public ISrsCoroutineHandler { protected: diff --git a/trunk/src/app/srs_app_rtc.cpp b/trunk/src/app/srs_app_rtc.cpp deleted file mode 100644 index e7fbf7fc2..000000000 --- a/trunk/src/app/srs_app_rtc.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2013-2020 Winlin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include - -#include -#include -#include -#include -#include -using namespace std; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static bool is_stun(const char* data, const int size) { - return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1); -} - -static bool is_rtp_or_rtcp(const char* data, const int size) { - return data != NULL && size > 0 && (data[0] >= 128 && data[0] <= 191); -} - -static bool is_dtls(const char* data, const int size) { - return data != NULL && size > 0 && (data[0] >= 20 && data[0] <= 64); -} - -SrsRtc::SrsRtc(SrsRtcServer* rtc_svr) -{ - rtc_server = rtc_svr; -} - -SrsRtc::~SrsRtc() -{ -} - -srs_error_t SrsRtc::on_udp_packet(srs_netfd_t fd, const sockaddr* from, const int fromlen, char* buf, int nb_buf) -{ - char address_string[64]; - char port_string[16]; - if(getnameinfo(from, fromlen, - (char*)&address_string, sizeof(address_string), - (char*)&port_string, sizeof(port_string), - NI_NUMERICHOST|NI_NUMERICSERV)) { - return srs_error_new(ERROR_SYSTEM_IP_INVALID, "bad address"); - } - std::string peer_ip = std::string(address_string); - int peer_port = atoi(port_string); - - return rtc_server->on_udp_packet(fd, peer_ip, peer_port, from, fromlen, buf, nb_buf); -} diff --git a/trunk/src/app/srs_app_rtc.hpp b/trunk/src/app/srs_app_rtc.hpp deleted file mode 100644 index 8c15bbf16..000000000 --- a/trunk/src/app/srs_app_rtc.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2013-2020 Winlin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SRS_APP_RTC_HPP -#define SRS_APP_RTC_HPP - -#include - -struct sockaddr; -#include -#include - -#include -#include -#include - -class SrsRtcServer; - -// The rtc over udp stream receiver -class SrsRtc : virtual public ISrsUdpRemuxHandler -{ -private: - SrsRtcServer* rtc_server; -public: - SrsRtc(SrsRtcServer* rtc_svr); - virtual ~SrsRtc(); -// Interface ISrsUdpHandler -public: - virtual srs_error_t on_udp_packet(srs_netfd_t fd, const sockaddr* from, const int fromlen, char* buf, int nb_buf); -}; - -#endif diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 65cd4fe10..bfc5c09f4 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -279,7 +279,7 @@ srs_error_t SrsSdp::parse_attr(const string& line) return err; } -SrsDtlsSession::SrsDtlsSession(srs_netfd_t lfd, const sockaddr* f, int fl) +SrsDtlsSession::SrsDtlsSession() { dtls = NULL; bio_in = NULL; @@ -291,10 +291,6 @@ SrsDtlsSession::SrsDtlsSession(srs_netfd_t lfd, const sockaddr* f, int fl) srtp_send = NULL; srtp_recv = NULL; - fd = lfd; - from = f; - fromlen = fl; - handshake_done = false; } @@ -302,7 +298,7 @@ SrsDtlsSession::~SrsDtlsSession() { } -srs_error_t SrsDtlsSession::handshake() +srs_error_t SrsDtlsSession::handshake(SrsUdpRemuxSocket* udp_remux_socket) { srs_error_t err = srs_success; @@ -314,11 +310,7 @@ srs_error_t SrsDtlsSession::handshake() int ssl_err = SSL_get_error(dtls, ret); switch(ssl_err) { case SSL_ERROR_NONE: { - srs_trace("dtls handshake done"); - handshake_done = true; - srtp_init(); - srtp_sender_side_init(); - srtp_receiver_side_init(); + err = on_dtls_handshake_done(); } break; @@ -337,25 +329,25 @@ srs_error_t SrsDtlsSession::handshake() if (out_bio_len) { srs_trace("send dtls handshake data"); - srs_sendto(fd, out_bio_data, out_bio_len, from, fromlen, 0); + udp_remux_socket->sendto(out_bio_data, out_bio_len, 0); } return err; } -srs_error_t SrsDtlsSession::on_dtls(const char* data, const int len) +srs_error_t SrsDtlsSession::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) { srs_error_t err = srs_success; if (! handshake_done) { BIO_reset(bio_in); BIO_reset(bio_out); - BIO_write(bio_in, data, len); + BIO_write(bio_in, udp_remux_socket->data(), udp_remux_socket->size()); - handshake(); + handshake(udp_remux_socket); } else { BIO_reset(bio_in); BIO_reset(bio_out); - BIO_write(bio_in, data, len); + BIO_write(bio_in, udp_remux_socket->data(), udp_remux_socket->size()); while (BIO_ctrl_pending(bio_in) > 0) { char dtls_read_buf[8092]; @@ -370,6 +362,14 @@ srs_error_t SrsDtlsSession::on_dtls(const char* data, const int len) return err; } +srs_error_t SrsDtlsSession::on_dtls_handshake_done() +{ + srs_trace("dtls handshake done"); + + handshake_done = true; + return srtp_init(); +} + srs_error_t SrsDtlsSession::on_dtls_application_data(const char* buf, const int nb_buf) { srs_error_t err = srs_success; @@ -377,8 +377,7 @@ srs_error_t SrsDtlsSession::on_dtls_application_data(const char* buf, const int return err; } - -void SrsDtlsSession::send_client_hello() +void SrsDtlsSession::send_client_hello(SrsUdpRemuxSocket* udp_remux_socket) { if (dtls == NULL) { srs_trace("send client hello"); @@ -391,7 +390,7 @@ void SrsDtlsSession::send_client_hello() SSL_set_bio(dtls, bio_in, bio_out); - handshake(); + handshake(udp_remux_socket); } } @@ -400,8 +399,8 @@ srs_error_t SrsDtlsSession::srtp_init() srs_error_t err = srs_success; unsigned char material[SRTP_MASTER_KEY_LEN * 2] = {0}; // client(SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN) + server - char dtls_srtp_lable[] = "EXTRACTOR-dtls_srtp"; - if (! SSL_export_keying_material(dtls, material, sizeof(material), dtls_srtp_lable, strlen(dtls_srtp_lable), NULL, 0, 0)) { + static string dtls_srtp_lable = "EXTRACTOR-dtls_srtp"; + if (! SSL_export_keying_material(dtls, material, sizeof(material), dtls_srtp_lable.c_str(), dtls_srtp_lable.size(), NULL, 0, 0)) { return srs_error_wrap(err, "SSL_export_keying_material failed"); } @@ -418,8 +417,15 @@ srs_error_t SrsDtlsSession::srtp_init() client_key = sClientMasterKey + sClientMasterSalt; server_key = sServerMasterKey + sServerMasterSalt; - srtp_sender_side_init(); - srtp_receiver_side_init(); + if (srtp_sender_side_init() != srs_success) { + return srs_error_wrap(err, "srtp sender size init failed"); + } + + if (srtp_receiver_side_init() != srs_success) { + return srs_error_wrap(err, "srtp receiver size init failed"); + } + + return err; } srs_error_t SrsDtlsSession::srtp_sender_side_init() @@ -435,7 +441,8 @@ srs_error_t SrsDtlsSession::srtp_sender_side_init() policy.ssrc.type = ssrc_any_outbound; policy.ssrc.value = 0; - policy.window_size = 8192; // seq 鐩稿樊8192璁や负鏃犳晥 + // TODO: adjust window_size + policy.window_size = 8192; policy.allow_repeat_tx = 1; policy.next = NULL; @@ -444,6 +451,7 @@ srs_error_t SrsDtlsSession::srtp_sender_side_init() policy.key = key; if (srtp_create(&srtp_send, &policy) != 0) { + delete [] key; return srs_error_wrap(err, "srtp_create failed"); } @@ -465,7 +473,8 @@ srs_error_t SrsDtlsSession::srtp_receiver_side_init() policy.ssrc.type = ssrc_any_inbound; policy.ssrc.value = 0; - policy.window_size = 8192; // seq 鐩稿樊8192璁や负鏃犳晥 + // TODO: adjust window_size + policy.window_size = 8192; policy.allow_repeat_tx = 1; policy.next = NULL; @@ -474,6 +483,7 @@ srs_error_t SrsDtlsSession::srtp_receiver_side_init() policy.key = key; if (srtp_create(&srtp_recv, &policy) != 0) { + delete [] key; return srs_error_wrap(err, "srtp_create failed"); } @@ -482,8 +492,9 @@ srs_error_t SrsDtlsSession::srtp_receiver_side_init() return err; } -SrsRtcSession::SrsRtcSession() +SrsRtcSession::SrsRtcSession(SrsRtcServer* svr) { + rtc_server = svr; session_state = INIT; dtls_session = NULL; } @@ -492,42 +503,73 @@ SrsRtcSession::~SrsRtcSession() { } -srs_error_t SrsRtcSession::on_binding_request(const SrsStunPacket& stun_packet, const string& peer_ip, const uint16_t peer_port, - SrsStunPacket& stun_binding_response) +srs_error_t SrsRtcSession::on_stun(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req) { srs_error_t err = srs_success; - stun_binding_response.set_message_type(BindingResponse); - stun_binding_response.set_local_ufrag(stun_packet.get_remote_ufrag()); - stun_binding_response.set_remote_ufrag(stun_packet.get_local_ufrag()); - stun_binding_response.set_transcation_id(stun_packet.get_transcation_id()); - stun_binding_response.set_mapped_address(be32toh(inet_addr(peer_ip.c_str()))); - stun_binding_response.set_mapped_port(peer_port); + if (stun_req->is_binding_request()) { + if (on_binding_request(udp_remux_socket, stun_req) != srs_success) { + return srs_error_wrap(err, "stun binding request failed"); + } + } return err; } -srs_error_t SrsRtcSession::send_client_hello(srs_netfd_t fd, const sockaddr* from, int fromlen) +srs_error_t SrsRtcSession::on_binding_request(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req) { - if (dtls_session == NULL) { - dtls_session = new SrsDtlsSession(fd, from, fromlen); + srs_error_t err = srs_success; + + SrsStunPacket stun_binding_response; + char buf[1460]; + SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); + SrsAutoFree(SrsBuffer, stream); + + stun_binding_response.set_message_type(BindingResponse); + stun_binding_response.set_local_ufrag(stun_req->get_remote_ufrag()); + stun_binding_response.set_remote_ufrag(stun_req->get_local_ufrag()); + stun_binding_response.set_transcation_id(stun_req->get_transcation_id()); + // FIXME: inet_addr is deprecated, IPV6 support + stun_binding_response.set_mapped_address(be32toh(inet_addr(udp_remux_socket->get_peer_ip().c_str()))); + stun_binding_response.set_mapped_port(udp_remux_socket->get_peer_port()); + + if (stun_binding_response.encode(get_local_sdp()->get_ice_pwd(), stream) != srs_success) { + return srs_error_wrap(err, "stun binding response encode failed"); } - dtls_session->send_client_hello(); + if (udp_remux_socket->sendto(stream->data(), stream->pos(), 0) <= 0) { + return srs_error_wrap(err, "stun binding response send failed"); + } + + if (get_session_state() == WAITING_STUN) { + set_session_state(DOING_DTLS_HANDSHAKE); + send_client_hello(udp_remux_socket); + + string peer_id = udp_remux_socket->get_peer_id(); + rtc_server->insert_into_id_sessions(peer_id, this); + } + + // TODO: dtls send client retry + + return err; } -srs_error_t SrsRtcSession::on_dtls(const char* buf, const int nb_buf) +srs_error_t SrsRtcSession::send_client_hello(SrsUdpRemuxSocket* udp_remux_socket) { - dtls_session->on_dtls(buf, nb_buf); + if (dtls_session == NULL) { + dtls_session = new SrsDtlsSession(); + } + + dtls_session->send_client_hello(udp_remux_socket); } -srs_error_t SrsRtcSession::send_packet() +srs_error_t SrsRtcSession::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) { + return dtls_session->on_dtls(udp_remux_socket); } -SrsRtcServer::SrsRtcServer(SrsServer* svr) +SrsRtcServer::SrsRtcServer() { - server = svr; } SrsRtcServer::~SrsRtcServer() @@ -541,32 +583,32 @@ srs_error_t SrsRtcServer::initialize() return err; } -srs_error_t SrsRtcServer::on_udp_packet(srs_netfd_t fd, const string& peer_ip, const int peer_port, - const sockaddr* from, const int fromlen, const char* data, const int size) +srs_error_t SrsRtcServer::on_udp_packet(SrsUdpRemuxSocket* udp_remux_socket) { srs_error_t err = srs_success; - if (is_stun(data, size)) { - return on_stun(fd, peer_ip, peer_port, from, fromlen, data, size); - } else if (is_dtls(data, size)) { - srs_trace("dtls"); - return on_dtls(fd, peer_ip, peer_port, from, fromlen, data, size); - } else if (is_rtp_or_rtcp(data, size)) { - return on_rtp_or_rtcp(fd, peer_ip, peer_port, from, fromlen, data, size); + if (is_stun(udp_remux_socket->data(), udp_remux_socket->size())) { + return on_stun(udp_remux_socket); + } else if (is_dtls(udp_remux_socket->data(), udp_remux_socket->size())) { + return on_dtls(udp_remux_socket); + } else if (is_rtp_or_rtcp(udp_remux_socket->data(), udp_remux_socket->size())) { + return on_rtp_or_rtcp(udp_remux_socket); } - return srs_error_wrap(err, "unknown packet type"); + return srs_error_wrap(err, "unknown udp packet type"); } SrsRtcSession* SrsRtcServer::create_rtc_session(const SrsSdp& remote_sdp, SrsSdp& local_sdp) { - SrsRtcSession* session = new SrsRtcSession(); + SrsRtcSession* session = new SrsRtcSession(this); - std::string local_ufrag = gen_random_str(8); std::string local_pwd = gen_random_str(32); - + std::string local_ufrag = ""; while (true) { - bool ret = map_ufrag_sessions.insert(make_pair(remote_sdp.get_ice_ufrag(), session)).second; + local_ufrag = gen_random_str(8); + std::string username = local_ufrag + ":" + remote_sdp.get_ice_ufrag(); + + bool ret = map_username_session.insert(make_pair(username, session)).second; if (ret) { break; } @@ -583,105 +625,71 @@ SrsRtcSession* SrsRtcServer::create_rtc_session(const SrsSdp& remote_sdp, SrsSdp return session; } -SrsRtcSession* SrsRtcServer::find_rtc_session_by_ip_port(const string& peer_ip, const uint16_t peer_port) +SrsRtcSession* SrsRtcServer::find_rtc_session_by_peer_id(const string& peer_id) { - ostringstream os; - os << peer_ip << ":" << peer_port; - string key = os.str(); - map::iterator iter = map_ip_port_sessions.find(key); - if (iter == map_ip_port_sessions.end()) { + map::iterator iter = map_id_session.find(peer_id); + if (iter == map_id_session.end()) { return NULL; } return iter->second; } -srs_error_t SrsRtcServer::on_stun(srs_netfd_t fd, const string& peer_ip, const int peer_port, - const sockaddr* from, const int fromlen, const char* data, const int size) +srs_error_t SrsRtcServer::on_stun(SrsUdpRemuxSocket* udp_remux_socket) { srs_error_t err = srs_success; - srs_trace("peer %s:%d stun", peer_ip.c_str(), peer_port); + srs_trace("recv stun packet from %s", udp_remux_socket->get_peer_id().c_str()); SrsStunPacket stun_req; - if (stun_req.decode(data, size) != srs_success) { - return srs_error_wrap(err, "decode stun failed"); + if (stun_req.decode(udp_remux_socket->data(), udp_remux_socket->size()) != srs_success) { + return srs_error_wrap(err, "decode stun packet failed"); } - std::string remote_ufrag = stun_req.get_remote_ufrag(); - SrsRtcSession* rtc_session = find_rtc_session_by_ufrag(remote_ufrag); + std::string username = stun_req.get_username(); + SrsRtcSession* rtc_session = find_rtc_session_by_username(username); if (rtc_session == NULL) { - return srs_error_wrap(err, "can not find rtc_session, ufrag=%s", remote_ufrag.c_str()); + return srs_error_wrap(err, "can not find rtc_session, stun username=%s", username.c_str()); } - SrsStunPacket stun_rsp; - char buf[1460]; - SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); - SrsAutoFree(SrsBuffer, stream); - - if (stun_req.is_binding_request()) { - if (rtc_session->on_binding_request(stun_req, peer_ip, peer_port, stun_rsp) != srs_success) { - return srs_error_wrap(err, "stun binding request failed"); - } - } - - if (stun_rsp.encode(rtc_session->get_local_sdp()->get_ice_pwd(), stream) != srs_success) { - return srs_error_wrap(err, "stun rsp encode failed"); - } - - srs_sendto(fd, stream->data(), stream->pos(), from, fromlen, 0); - - if (rtc_session->get_session_state() == WAITING_STUN) { - rtc_session->set_session_state(DOING_DTLS_HANDSHAKE); - rtc_session->send_client_hello(fd, from, fromlen); - - insert_into_ip_port_sessions(peer_ip, peer_port, rtc_session); - } - - return err; + return rtc_session->on_stun(udp_remux_socket, &stun_req); } -srs_error_t SrsRtcServer::on_dtls(srs_netfd_t fd, const string& peer_ip, const int peer_port, - const sockaddr* from, const int fromlen, const char* data, const int size) +srs_error_t SrsRtcServer::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) { srs_error_t err = srs_success; srs_trace("on dtls"); // FIXME - SrsRtcSession* rtc_session = find_rtc_session_by_ip_port(peer_ip, peer_port); + SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(udp_remux_socket->get_peer_id()); if (rtc_session == NULL) { - return srs_error_wrap(err, "can not find rtc session by ip=%s, port=%u", peer_ip.c_str(), peer_port); + return srs_error_wrap(err, "can not find rtc session by peer_id=%s", udp_remux_socket->get_peer_id().c_str()); } - rtc_session->on_dtls(data, size); + rtc_session->on_dtls(udp_remux_socket); return err; } -srs_error_t SrsRtcServer::on_rtp_or_rtcp(srs_netfd_t fd, const string& peer_ip, const int peer_port, - const sockaddr* from, const int fromlen, const char* data, const int size) +srs_error_t SrsRtcServer::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) { srs_error_t err = srs_success; srs_trace("on rtp/rtcp"); return err; } -SrsRtcSession* SrsRtcServer::find_rtc_session_by_ufrag(const std::string& ufrag) +SrsRtcSession* SrsRtcServer::find_rtc_session_by_username(const std::string& username) { - map::iterator iter = map_ufrag_sessions.find(ufrag); - if (iter == map_ufrag_sessions.end()) { + map::iterator iter = map_username_session.find(username); + if (iter == map_username_session.end()) { return NULL; } return iter->second; } -bool SrsRtcServer::insert_into_ip_port_sessions(const string& peer_ip, const uint16_t peer_port, SrsRtcSession* rtc_session) +bool SrsRtcServer::insert_into_id_sessions(const string& peer_id, SrsRtcSession* rtc_session) { - ostringstream os; - os << peer_ip << ":" << peer_port; - string key = os.str(); - - return map_ip_port_sessions.insert(make_pair(key, rtc_session)).second; + return map_id_session.insert(make_pair(peer_id, rtc_session)).second; } diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index b9d60d07e..e715e8151 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -25,6 +25,7 @@ #define SRS_APP_RTC_CONN_HPP #include +#include #include #include @@ -34,6 +35,7 @@ #include #include +class SrsUdpRemuxSocket; class SrsServer; class SrsStunPacket; @@ -103,37 +105,39 @@ private: srtp_t srtp_send; srtp_t srtp_recv; - srs_netfd_t fd; - const sockaddr* from; - int fromlen; - bool handshake_done; public: - SrsDtlsSession(srs_netfd_t lfd, const sockaddr* f, int fl); + SrsDtlsSession(); virtual ~SrsDtlsSession(); - srs_error_t on_dtls(const char* data, const int len); + srs_error_t on_dtls(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t on_dtls_handshake_done(); srs_error_t on_dtls_application_data(const char* data, const int len); - void send_client_hello(); - srs_error_t handshake(); + void send_client_hello(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t handshake(SrsUdpRemuxSocket* udp_remux_socket); + +private: srs_error_t srtp_init(); srs_error_t srtp_sender_side_init(); srs_error_t srtp_receiver_side_init(); }; +class SrsRtcServer; + class SrsRtcSession { private: + SrsRtcServer* rtc_server; SrsSdp remote_sdp; SrsSdp local_sdp; SrsRtcSessionStateType session_state; SrsDtlsSession* dtls_session; public: - SrsRtcSession(); + SrsRtcSession(SrsRtcServer* svr); virtual ~SrsRtcSession(); - +public: SrsSdp* get_local_sdp() { return &local_sdp; } SrsSdp* get_remote_sdp() { return &remote_sdp; } SrsRtcSessionStateType get_session_state() { return session_state; } @@ -141,40 +145,37 @@ public: void set_local_sdp(const SrsSdp& sdp) { local_sdp = sdp; } void set_remote_sdp(const SrsSdp& sdp) { remote_sdp = sdp; } void set_session_state(SrsRtcSessionStateType state) { session_state = state; } - - srs_error_t on_udp_packet(const std::string& peer_ip, const int peer_port, const char* data, const int size); - srs_error_t on_binding_request(const SrsStunPacket& stun_packet, const std::string& peer_ip, const uint16_t peer_port, - SrsStunPacket& stun_binding_response); - srs_error_t on_dtls(const char* buf, const int nb_buf); - srs_error_t send_client_hello(srs_netfd_t fd, const sockaddr* from, int fromlen); - srs_error_t send_packet(); +public: + srs_error_t on_stun(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req); + srs_error_t on_dtls(SrsUdpRemuxSocket* udp_remux_socket); +public: + srs_error_t send_client_hello(SrsUdpRemuxSocket* udp_remux_socket); +private: + srs_error_t on_binding_request(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req); }; -class SrsRtcServer +class SrsRtcServer : public ISrsUdpRemuxHandler { private: - SrsServer* server; - std::map map_ufrag_sessions; - std::map map_ip_port_sessions; + std::map map_username_session; // key: username(local_ufrag + ":" + remote_ufrag) + std::map map_id_session; // key: peerip(ip + ":" + port) public: - SrsRtcServer(SrsServer* svr); + SrsRtcServer(); virtual ~SrsRtcServer(); public: virtual srs_error_t initialize(); - virtual srs_error_t on_udp_packet(srs_netfd_t fd, const std::string& peer_ip, const int peer_port, - const sockaddr* from, const int fromlen, const char* data, const int size); + virtual srs_error_t on_udp_packet(SrsUdpRemuxSocket* udp_remux_socket); SrsRtcSession* create_rtc_session(const SrsSdp& remote_sdp, SrsSdp& local_sdp); - bool insert_into_ip_port_sessions(const std::string& peer_ip, const uint16_t peer_port, SrsRtcSession* rtc_session); + bool insert_into_id_sessions(const std::string& peer_id, SrsRtcSession* rtc_session); private: - srs_error_t on_stun(srs_netfd_t fd, const std::string& peer_ip, const int peer_port, const sockaddr* from, const int fromlen, const char* data, const int size); - srs_error_t on_dtls(srs_netfd_t fd, const std::string& peer_ip, const int peer_port, const sockaddr* from, const int fromlen, const char* data, const int size); - srs_error_t on_rtp_or_rtcp(srs_netfd_t fd, const std::string& peer_ip, const int peer_port, - const sockaddr* from, const int fromlen, const char* data, const int size); + srs_error_t on_stun(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t on_dtls(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket); private: - SrsRtcSession* find_rtc_session_by_ufrag(const std::string& ufrag); - SrsRtcSession* find_rtc_session_by_ip_port(const std::string& peer_ip, const uint16_t peer_port); + SrsRtcSession* find_rtc_session_by_username(const std::string& ufrag); + SrsRtcSession* find_rtc_session_by_peer_id(const std::string& peer_id); }; #endif diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index cdd939408..d630ca35d 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -44,7 +44,6 @@ using namespace std; #include #include #include -#include #include #include #include @@ -342,7 +341,7 @@ SrsUdpCasterListener::~SrsUdpCasterListener() SrsRtcListener::SrsRtcListener(SrsServer* svr, SrsRtcServer* rtc_svr, SrsListenerType t) : SrsListener(svr, t) { srs_assert(type == SrsListenerRtc); - rtc = new SrsRtc(rtc_svr); + rtc = rtc_svr; } SrsRtcListener::~SrsRtcListener() @@ -528,7 +527,7 @@ SrsServer::SrsServer() // new these objects in initialize instead. http_api_mux = new SrsHttpServeMux(); http_server = new SrsHttpServer(this); - rtc_server = new SrsRtcServer(this); + rtc_server = new SrsRtcServer(); http_heartbeat = new SrsHttpHeartbeat(); ingester = new SrsIngester(); } diff --git a/trunk/src/protocol/srs_stun_stack.cpp b/trunk/src/protocol/srs_stun_stack.cpp index 50284c785..a057715d6 100644 --- a/trunk/src/protocol/srs_stun_stack.cpp +++ b/trunk/src/protocol/srs_stun_stack.cpp @@ -129,6 +129,7 @@ srs_error_t SrsStunPacket::decode(const char* buf, const int nb_buf) switch (type) { // FIXME: enum case 6: { + username = val; size_t p = val.find(":"); if (p != string::npos) { local_ufrag = val.substr(0, p); diff --git a/trunk/src/protocol/srs_stun_stack.hpp b/trunk/src/protocol/srs_stun_stack.hpp index 80bba8a15..aa2bc2192 100644 --- a/trunk/src/protocol/srs_stun_stack.hpp +++ b/trunk/src/protocol/srs_stun_stack.hpp @@ -70,6 +70,7 @@ class SrsStunPacket { private: uint16_t message_type; + std::string username; std::string local_ufrag; std::string remote_ufrag; std::string transcation_id; @@ -83,6 +84,7 @@ public: bool is_binding_response() const { return message_type == BindingResponse; } uint16_t get_message_type() const { return message_type; } + std::string get_username() const { return username; } std::string get_local_ufrag() const { return local_ufrag; } std::string get_remote_ufrag() const { return remote_ufrag; } std::string get_transcation_id() const { return transcation_id; } From 2e68c375e34de1aaa5849c1bbd59aebe05f4d2fe Mon Sep 17 00:00:00 2001 From: HuyaJohn Date: Sun, 8 Mar 2020 04:20:46 -0700 Subject: [PATCH 12/21] start coding flv->rtp --- trunk/configure | 2 +- trunk/src/app/srs_app_rtc_conn.cpp | 8 +- trunk/src/app/srs_app_rtp.cpp | 285 +++++++++++++++++++++++++++++ trunk/src/app/srs_app_rtp.hpp | 87 +++++++++ trunk/src/app/srs_app_source.cpp | 25 ++- trunk/src/app/srs_app_source.hpp | 3 + 6 files changed, 404 insertions(+), 6 deletions(-) create mode 100644 trunk/src/app/srs_app_rtp.cpp create mode 100644 trunk/src/app/srs_app_rtp.hpp diff --git a/trunk/configure b/trunk/configure index 6d3da2bff..c939a96c4 100755 --- a/trunk/configure +++ b/trunk/configure @@ -257,7 +257,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_edge" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static" "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds" - "srs_app_mpegts_udp" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" + "srs_app_mpegts_udp" "srs_app_rtp" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" "srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr" "srs_app_coworkers" "srs_app_hybrid") diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index bfc5c09f4..97f640f33 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -47,14 +47,14 @@ static bool is_stun(const char* data, const int size) return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1); } -static bool is_rtp_or_rtcp(const char* data, const int size) +static bool is_dtls(const char* data, size_t len) { - return data != NULL && size > 0 && (data[0] >= 128 && data[0] <= 191); + return (len >= 13 && (data[0] > 19 && data[0] < 64)); } -static bool is_dtls(const char* data, const int size) +static bool is_rtp_or_rtcp(const char* data, size_t len) { - return data != NULL && size > 0 && (data[0] >= 20 && data[0] <= 64); + return (len >= 12 && (data[0] & 0xC0) == 0x80); } static string gen_random_str(int len) diff --git a/trunk/src/app/srs_app_rtp.cpp b/trunk/src/app/srs_app_rtp.cpp new file mode 100644 index 000000000..015141f84 --- /dev/null +++ b/trunk/src/app/srs_app_rtp.cpp @@ -0,0 +1,285 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len); +static string dump_string_hex(const std::string& str, const int& max_len = 128) +{ + return dump_string_hex(str.c_str(), str.size(), max_len); +} + +static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = 128) +{ + char tmp_buf[1024*16]; + int len = 0; + + for (int i = 0; i < nb_buf && i < max_len; ++i) { + int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 1, "%02X ", (uint8_t)buf[i]); + if (nb <= 0) + break; + + len += nb; + } + tmp_buf[len] = '\0'; + + return string(tmp_buf, len); +} + + +SrsRtpRawFrame::SrsRtpRawFrame(char* buf, int len) +{ + if (buf && len > 0) { + payload = new char[len]; + size = len; + memcpy(payload, buf, len); + } else { + payload = NULL; + size = 0; + } +} + +SrsRtpRawFrame::~SrsRtpRawFrame() +{ + if (payload) { + delete [] payload; + payload = NULL; + size = 0; + } +} + +srs_error_t SrsRtpRawFrame::avcc_to_annexb() +{ + srs_error_t err = srs_success; + + if (! (payload[0] == 0x00 && payload[1] == 0x00 && payload[2] == 0x00 && payload[3] == 0x01)) { + } + + return err; +} + +srs_error_t SrsRtpRawFrame::frame_to_packet() +{ + srs_error_t err = srs_success; + if (payload == NULL || size <= 4) { + return srs_error_wrap(err, "invalid rtp raw frame"); + } + + avcc_to_annexb(); + + char buf[1500] = {0}; + SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); +} + +SrsRtpMuxer::SrsRtpMuxer() +{ +} + +SrsRtpMuxer::~SrsRtpMuxer() +{ +} + +srs_error_t SrsRtpMuxer::video_frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format) +{ + srs_error_t err = srs_success; + + if (shared_video->size < 5) { + return srs_error_wrap(err, "invalid video size:%d", shared_video->size); + } + + SrsRtpRawFrame* rtp_raw_frame = new SrsRtpRawFrame(shared_video->payload + 5, shared_video->size - 5); + SrsAutoFree(SrsRtpRawFrame, rtp_raw_frame); + + rtp_raw_frame->frame_to_packet(); + + srs_trace("video dump=%s", dump_string_hex(shared_video->payload, shared_video->size).c_str()); + + //srs_avcc_to_annexb(raw, raw_len); + + return err; +} + +srs_error_t SrsRtpMuxer::audio_frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format) +{ + srs_error_t err = srs_success; + + return err; +} + +SrsRtp::SrsRtp() +{ + req = NULL; + hub = NULL; + + enabled = false; + disposable = false; + last_update_time = 0; +} + +SrsRtp::~SrsRtp() +{ +} + +void SrsRtp::dispose() +{ + if (enabled) { + on_unpublish(); + } +} + +srs_error_t SrsRtp::cycle() +{ + srs_error_t err = srs_success; + + return err; +} + +srs_error_t SrsRtp::initialize(SrsOriginHub* h, SrsRequest* r) +{ + srs_error_t err = srs_success; + + hub = h; + req = r; + + rtp_muxer = new SrsRtpMuxer(); + + return err; +} + +srs_error_t SrsRtp::on_publish() +{ + srs_error_t err = srs_success; + + // update the hls time, for hls_dispose. + last_update_time = srs_get_system_time(); + + // support multiple publish. + if (enabled) { + return err; + } + + // if enabled, open the muxer. + enabled = true; + + // ok, the hls can be dispose, or need to be dispose. + disposable = true; + + return err; +} + +void SrsRtp::on_unpublish() +{ + srs_error_t err = srs_success; + + // support multiple unpublish. + if (!enabled) { + return; + } + + enabled = false; +} + +srs_error_t SrsRtp::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format) +{ + srs_error_t err = srs_success; + + if (!enabled) { + return err; + } + + // Ignore if no format->acodec, it means the codec is not parsed, or unknown codec. + // @issue https://github.com/ossrs/srs/issues/1506#issuecomment-562079474 + if (!format->acodec) { + return err; + } + + // update the hls time, for hls_dispose. + last_update_time = srs_get_system_time(); + + SrsSharedPtrMessage* audio = shared_audio->copy(); + SrsAutoFree(SrsSharedPtrMessage, audio); + + // ts support audio codec: aac/mp3 + SrsAudioCodecId acodec = format->acodec->id; + if (acodec != SrsAudioCodecIdAAC && acodec != SrsAudioCodecIdMP3) { + return err; + } + + // ignore sequence header + srs_assert(format->audio); + + return rtp_muxer->audio_frame_to_packet(audio, format); +} + +srs_error_t SrsRtp::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format) +{ + srs_error_t err = srs_success; + + if (!enabled) { + return err; + } + + // Ignore if no format->vcodec, it means the codec is not parsed, or unknown codec. + // @issue https://github.com/ossrs/srs/issues/1506#issuecomment-562079474 + if (!format->vcodec) { + return err; + } + + // update the hls time, for hls_dispose. + last_update_time = srs_get_system_time(); + + SrsSharedPtrMessage* video = shared_video->copy(); + SrsAutoFree(SrsSharedPtrMessage, video); + + // ignore info frame, + // @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909 + srs_assert(format->video); + return rtp_muxer->video_frame_to_packet(video, format); +} diff --git a/trunk/src/app/srs_app_rtp.hpp b/trunk/src/app/srs_app_rtp.hpp new file mode 100644 index 000000000..097d2a8a6 --- /dev/null +++ b/trunk/src/app/srs_app_rtp.hpp @@ -0,0 +1,87 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_APP_RTP_HPP +#define SRS_APP_RTP_HPP + +#include + +#include +#include +#include + +class SrsFormat; +class SrsSharedPtrMessage; +class SrsRequest; +class SrsOriginHub; + +class SrsRtpRawFrame +{ +public: + int64_t timestamp; + char* payload; + int size; +public: + SrsRtpRawFrame(char* buf, int len); + virtual ~SrsRtpRawFrame(); +public: + srs_error_t avcc_to_annexb(); + srs_error_t frame_to_packet(); +}; + +class SrsRtpMuxer +{ +private: + std::map packet_queue; +public: + SrsRtpMuxer(); + virtual ~SrsRtpMuxer(); +public: + srs_error_t video_frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format); + srs_error_t audio_frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format); +}; + +class SrsRtp +{ +private: + SrsRequest* req; + bool enabled; + bool disposable; + srs_utime_t last_update_time; + SrsRtpMuxer* rtp_muxer; + SrsOriginHub* hub; +public: + SrsRtp(); + virtual ~SrsRtp(); +public: + virtual void dispose(); + virtual srs_error_t cycle(); +public: + virtual srs_error_t initialize(SrsOriginHub* h, SrsRequest* r); + virtual srs_error_t on_publish(); + virtual void on_unpublish(); + virtual srs_error_t on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format); + virtual srs_error_t on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format); +}; + +#endif diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index f5acb05da..f30eb0163 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -32,6 +32,7 @@ using namespace std; #include #include #include +#include #include #include #include @@ -824,6 +825,7 @@ SrsOriginHub::SrsOriginHub() dash = new SrsDash(); dvr = new SrsDvr(); encoder = new SrsEncoder(); + rtp = new SrsRtp(); #ifdef SRS_AUTO_HDS hds = new SrsHds(); #endif @@ -868,6 +870,10 @@ srs_error_t SrsOriginHub::initialize(SrsSource* s, SrsRequest* r) return srs_error_wrap(err, "format initialize"); } + if ((err = rtp->initialize(this, req)) != srs_success) { + return srs_error_wrap(err, "rtp initialize"); + } + if ((err = hls->initialize(this, req)) != srs_success) { return srs_error_wrap(err, "hls initialize"); } @@ -965,6 +971,12 @@ srs_error_t SrsOriginHub::on_audio(SrsSharedPtrMessage* shared_audio) flv_sample_sizes[c->sound_size], flv_sound_types[c->sound_type], srs_flv_srates[c->sound_rate]); } + + if ((err = rtp->on_audio(msg, format)) != srs_success) { + srs_warn("rtp: ignore audio error %s", srs_error_desc(err).c_str()); + srs_error_reset(err); + rtp->on_unpublish(); + } if ((err = hls->on_audio(msg, format)) != srs_success) { // apply the error strategy for hls. @@ -1058,6 +1070,12 @@ srs_error_t SrsOriginHub::on_video(SrsSharedPtrMessage* shared_video, bool is_se if (format->vcodec && !format->vcodec->is_avc_codec_ok()) { return err; } + + if ((err = rtp->on_video(msg, format)) != srs_success) { + srs_warn("rtp: ignore video error %s", srs_error_desc(err).c_str()); + srs_error_reset(err); + rtp->on_unpublish(); + } if ((err = hls->on_video(msg, format)) != srs_success) { // apply the error strategy for hls. @@ -1126,6 +1144,10 @@ srs_error_t SrsOriginHub::on_publish() return srs_error_wrap(err, "encoder publish"); } + if ((err = rtp->on_publish()) != srs_success) { + return srs_error_wrap(err, "rtp publish"); + } + if ((err = hls->on_publish()) != srs_success) { return srs_error_wrap(err, "hls publish"); } @@ -1163,6 +1185,7 @@ void SrsOriginHub::on_unpublish() destroy_forwarders(); encoder->on_unpublish(); + rtp->on_unpublish(); hls->on_unpublish(); dash->on_unpublish(); dvr->on_unpublish(); @@ -2224,7 +2247,7 @@ srs_error_t SrsSource::on_video(SrsCommonMessage* shared_video) return srs_error_wrap(err, "create message"); } - // directly process the audio message. + // directly process the video message. if (!mix_correct) { return on_video_imp(&msg); } diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index 8cb79f478..674ffc99e 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -54,6 +54,7 @@ class SrsNgExec; class SrsConnection; class SrsMessageHeader; class SrsHls; +class SrsRtp; class SrsDvr; class SrsDash; class SrsEncoder; @@ -335,6 +336,8 @@ private: private: // The format, codec information. SrsRtmpFormat* format; + // rtp handler + SrsRtp* rtp; // hls handler. SrsHls* hls; // The DASH encoder. From 2f462775a020649d00403a4a7409be5ce024c0a1 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Mon, 9 Mar 2020 00:40:30 +0800 Subject: [PATCH 13/21] rtp support --- trunk/src/app/srs_app_rtp.cpp | 75 +++-------------------------- trunk/src/app/srs_app_rtp.hpp | 20 ++------ trunk/src/kernel/srs_kernel_flv.cpp | 6 ++- trunk/src/kernel/srs_kernel_flv.hpp | 6 +++ 4 files changed, 22 insertions(+), 85 deletions(-) diff --git a/trunk/src/app/srs_app_rtp.cpp b/trunk/src/app/srs_app_rtp.cpp index 015141f84..52574fd22 100644 --- a/trunk/src/app/srs_app_rtp.cpp +++ b/trunk/src/app/srs_app_rtp.cpp @@ -73,83 +73,23 @@ static string dump_string_hex(const char* buf, const int nb_buf, const int& max_ return string(tmp_buf, len); } - -SrsRtpRawFrame::SrsRtpRawFrame(char* buf, int len) -{ - if (buf && len > 0) { - payload = new char[len]; - size = len; - memcpy(payload, buf, len); - } else { - payload = NULL; - size = 0; - } -} - -SrsRtpRawFrame::~SrsRtpRawFrame() -{ - if (payload) { - delete [] payload; - payload = NULL; - size = 0; - } -} - -srs_error_t SrsRtpRawFrame::avcc_to_annexb() -{ - srs_error_t err = srs_success; - - if (! (payload[0] == 0x00 && payload[1] == 0x00 && payload[2] == 0x00 && payload[3] == 0x01)) { - } - - return err; -} - -srs_error_t SrsRtpRawFrame::frame_to_packet() -{ - srs_error_t err = srs_success; - if (payload == NULL || size <= 4) { - return srs_error_wrap(err, "invalid rtp raw frame"); - } - - avcc_to_annexb(); - - char buf[1500] = {0}; - SrsBuffer* stream = new SrsBuffer(buf, sizeof(buf)); -} - SrsRtpMuxer::SrsRtpMuxer() { + sequence = 0; } SrsRtpMuxer::~SrsRtpMuxer() { } -srs_error_t SrsRtpMuxer::video_frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format) +srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsFormat* format) { srs_error_t err = srs_success; - if (shared_video->size < 5) { - return srs_error_wrap(err, "invalid video size:%d", shared_video->size); + for (int i = 0; i < format->video->nb_samples; ++i) { + SrsSample sample = format->video->samples[i]; } - SrsRtpRawFrame* rtp_raw_frame = new SrsRtpRawFrame(shared_video->payload + 5, shared_video->size - 5); - SrsAutoFree(SrsRtpRawFrame, rtp_raw_frame); - - rtp_raw_frame->frame_to_packet(); - - srs_trace("video dump=%s", dump_string_hex(shared_video->payload, shared_video->size).c_str()); - - //srs_avcc_to_annexb(raw, raw_len); - - return err; -} - -srs_error_t SrsRtpMuxer::audio_frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format) -{ - srs_error_t err = srs_success; - return err; } @@ -188,7 +128,7 @@ srs_error_t SrsRtp::initialize(SrsOriginHub* h, SrsRequest* r) hub = h; req = r; - rtp_muxer = new SrsRtpMuxer(); + rtp_h264_muxer = new SrsRtpMuxer(); return err; } @@ -255,7 +195,8 @@ srs_error_t SrsRtp::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* forma // ignore sequence header srs_assert(format->audio); - return rtp_muxer->audio_frame_to_packet(audio, format); + // TODO: rtc no support aac + return err; } srs_error_t SrsRtp::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* format) @@ -281,5 +222,5 @@ srs_error_t SrsRtp::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* forma // ignore info frame, // @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909 srs_assert(format->video); - return rtp_muxer->video_frame_to_packet(video, format); + return rtp_h264_muxer->frame_to_packet(video, format); } diff --git a/trunk/src/app/srs_app_rtp.hpp b/trunk/src/app/srs_app_rtp.hpp index 097d2a8a6..a11317948 100644 --- a/trunk/src/app/srs_app_rtp.hpp +++ b/trunk/src/app/srs_app_rtp.hpp @@ -35,30 +35,16 @@ class SrsSharedPtrMessage; class SrsRequest; class SrsOriginHub; -class SrsRtpRawFrame -{ -public: - int64_t timestamp; - char* payload; - int size; -public: - SrsRtpRawFrame(char* buf, int len); - virtual ~SrsRtpRawFrame(); -public: - srs_error_t avcc_to_annexb(); - srs_error_t frame_to_packet(); -}; - class SrsRtpMuxer { private: + uint32_t sequence; std::map packet_queue; public: SrsRtpMuxer(); virtual ~SrsRtpMuxer(); public: - srs_error_t video_frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format); - srs_error_t audio_frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format); + srs_error_t frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format); }; class SrsRtp @@ -68,7 +54,7 @@ private: bool enabled; bool disposable; srs_utime_t last_update_time; - SrsRtpMuxer* rtp_muxer; + SrsRtpMuxer* rtp_h264_muxer; SrsOriginHub* hub; public: SrsRtp(); diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index 0259def06..4dc8f68da 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -203,6 +203,8 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload() { payload = NULL; size = 0; + rtp_fragments = NULL; + nb_rtp_fragments = 0; shared_count = 0; } @@ -214,7 +216,7 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload() srs_freepa(payload); } -SrsSharedPtrMessage::SrsSharedPtrMessage() : timestamp(0), stream_id(0), size(0), payload(NULL) +SrsSharedPtrMessage::SrsSharedPtrMessage() : timestamp(0), stream_id(0), size(0), payload(NULL), rtp_fragments(NULL), nb_rtp_fragments(0) { ptr = NULL; } @@ -345,6 +347,8 @@ SrsSharedPtrMessage* SrsSharedPtrMessage::copy() copy->stream_id = stream_id; copy->payload = ptr->payload; copy->size = ptr->size; + copy->rtp_fragments = ptr->rtp_fragments; + copy->nb_rtp_fragments = ptr->nb_rtp_fragments; return copy; } diff --git a/trunk/src/kernel/srs_kernel_flv.hpp b/trunk/src/kernel/srs_kernel_flv.hpp index ebc587f39..30052b881 100644 --- a/trunk/src/kernel/srs_kernel_flv.hpp +++ b/trunk/src/kernel/srs_kernel_flv.hpp @@ -38,6 +38,7 @@ class ISrsWriter; class ISrsReader; class SrsFileReader; class SrsPacket; +class SrsSample; #define SRS_FLV_TAG_HEADER_SIZE 11 #define SRS_FLV_PREVIOUS_TAG_SIZE 4 @@ -285,6 +286,9 @@ public: // @remark, not all message payload can be decoded to packet. for example, // video/audio packet use raw bytes, no video/audio packet. char* payload; + + SrsSample* rtp_fragments; + int nb_rtp_fragments; private: class SrsSharedPtrPayload { @@ -298,6 +302,8 @@ private: int size; // The reference count int shared_count; + SrsSample* rtp_fragments; + int nb_rtp_fragments; public: SrsSharedPtrPayload(); virtual ~SrsSharedPtrPayload(); From 3ae510b843c6b54c2e4a5d78c04eecfb036c9819 Mon Sep 17 00:00:00 2001 From: HuyaJohn Date: Mon, 9 Mar 2020 04:46:27 -0700 Subject: [PATCH 14/21] rtp dispatch done, but video can not play in chrome --- trunk/configure | 2 +- trunk/src/app/srs_app_http_api.cpp | 3 +- trunk/src/app/srs_app_rtc_conn.cpp | 214 +++++++++++++++++++++++++--- trunk/src/app/srs_app_rtc_conn.hpp | 52 ++++++- trunk/src/app/srs_app_rtp.cpp | 62 ++++++++ trunk/src/app/srs_app_server.cpp | 2 +- trunk/src/app/srs_app_source.cpp | 3 + trunk/src/kernel/srs_kernel_flv.cpp | 17 +++ trunk/src/kernel/srs_kernel_flv.hpp | 2 + 9 files changed, 325 insertions(+), 32 deletions(-) diff --git a/trunk/configure b/trunk/configure index c939a96c4..495400bb5 100755 --- a/trunk/configure +++ b/trunk/configure @@ -149,7 +149,7 @@ END LibSTRoot="${SRS_OBJS_DIR}/st"; LibSTfile="${LibSTRoot}/libst.a" if [[ $SRS_SHARED_ST == YES ]]; then LibSTfile="-lst"; fi # srtp -LibSrtpRoot="${SRS_OBJS_DIR}/srtp2"; LibSrtpFile="${LibSrtpRoot}/lib/libsrtp2.a" +LibSrtpRoot="${SRS_OBJS_DIR}/srtp2/include"; LibSrtpFile="${SRS_OBJS_DIR}/srtp2/lib/libsrtp2.a" if [[ $SRS_SHARED_SRTP == YES ]]; then LibSrtpFile="-lsrtp2"; fi # openssl-1.1.0e, for the RTMP complex handshake. LibSSLRoot="";LibSSLfile="" diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index bb892c57c..fd418bf86 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -835,7 +835,8 @@ srs_error_t SrsGoApiSdp::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* } SrsSdp local_sdp; - rtc_server->create_rtc_session(remote_sdp, local_sdp); + SrsRtcSession* rtc_session = rtc_server->create_rtc_session(remote_sdp, local_sdp); + rtc_session->set_app_stream(app, stream_name); string local_sdp_str = ""; err = local_sdp.encode(local_sdp_str); diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 97f640f33..b543c3cae 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -38,8 +38,12 @@ using namespace std; #include #include #include +#include +#include #include #include +#include +#include #include static bool is_stun(const char* data, const int size) @@ -73,6 +77,29 @@ static string gen_random_str(int len) const int SRTP_MASTER_KEY_KEY_LEN = 16; const int SRTP_MASTER_KEY_SALT_LEN = 14; +static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len); +static string dump_string_hex(const std::string& str, const int& max_len = 128) +{ + return dump_string_hex(str.c_str(), str.size(), max_len); +} + +static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = 128) +{ + char tmp_buf[1024*16]; + int len = 0; + + for (int i = 0; i < nb_buf && i < max_len; ++i) { + int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 1, "%02X ", (uint8_t)buf[i]); + if (nb <= 0) + break; + + len += nb; + } + tmp_buf[len] = '\0'; + + return string(tmp_buf, len); +} + SrsCandidate::SrsCandidate() { } @@ -206,7 +233,7 @@ srs_error_t SrsSdp::encode(string& sdp_str) "a=ssrc:3233846890 msid:6VrfBKXrwK a0\\r\\n" "a=ssrc:3233846890 mslabel:6VrfBKXrwK\\r\\n" "a=ssrc:3233846890 label:6VrfBKXrwKa0\\r\\n" - "m=video 9 UDP/TLS/RTP/SAVPF 96 98 102\\r\\n" + "m=video 9 UDP/TLS/RTP/SAVPF 102\\r\\n" "c=IN IP4 0.0.0.0\\r\\n" + candidate_lines + "a=rtcp:9 IN IP4 0.0.0.0\\r\\n" @@ -221,18 +248,6 @@ srs_error_t SrsSdp::encode(string& sdp_str) "a=sendrecv\\r\\n" "a=mid:1\\r\\n" "a=rtcp-mux\\r\\n" - "a=rtpmap:96 VP8/90000\\r\\n" - "a=rtcp-fb:96 ccm fir\\r\\n" - "a=rtcp-fb:96 nack\\r\\n" - "a=rtcp-fb:96 nack pli\\r\\n" - "a=rtcp-fb:96 goog-remb\\r\\n" - "a=rtcp-fb:96 transport-cc\\r\\n" - "a=rtpmap:98 VP9/90000\\r\\n" - "a=rtcp-fb:98 ccm fir\\r\\n" - "a=rtcp-fb:98 nack\\r\\n" - "a=rtcp-fb:98 nack pli\\r\\n" - "a=rtcp-fb:98 goog-remb\\r\\n" - "a=rtcp-fb:98 transport-cc\\r\\n" "a=rtpmap:102 H264/90000\\r\\n" "a=rtcp-fb:102 goog-remb\\r\\n" "a=rtcp-fb:102 transport-cc\\r\\n" @@ -279,8 +294,10 @@ srs_error_t SrsSdp::parse_attr(const string& line) return err; } -SrsDtlsSession::SrsDtlsSession() +SrsDtlsSession::SrsDtlsSession(SrsRtcSession* s) { + rtc_session = s; + dtls = NULL; bio_in = NULL; bio_out = NULL; @@ -310,7 +327,7 @@ srs_error_t SrsDtlsSession::handshake(SrsUdpRemuxSocket* udp_remux_socket) int ssl_err = SSL_get_error(dtls, ret); switch(ssl_err) { case SSL_ERROR_NONE: { - err = on_dtls_handshake_done(); + err = on_dtls_handshake_done(udp_remux_socket); } break; @@ -362,12 +379,20 @@ srs_error_t SrsDtlsSession::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) return err; } -srs_error_t SrsDtlsSession::on_dtls_handshake_done() +srs_error_t SrsDtlsSession::on_dtls_handshake_done(SrsUdpRemuxSocket* udp_remux_socket) { + srs_error_t err = srs_success; srs_trace("dtls handshake done"); handshake_done = true; - return srtp_init(); + if ((err = srtp_initialize()) != srs_success) { + srs_error("srtp init failed, err=%s", srs_error_desc(err).c_str()); + return srs_error_wrap(err, "srtp init failed"); + } + + rtc_session->on_connection_established(udp_remux_socket); + + return err; } srs_error_t SrsDtlsSession::on_dtls_application_data(const char* buf, const int nb_buf) @@ -394,7 +419,7 @@ void SrsDtlsSession::send_client_hello(SrsUdpRemuxSocket* udp_remux_socket) } } -srs_error_t SrsDtlsSession::srtp_init() +srs_error_t SrsDtlsSession::srtp_initialize() { srs_error_t err = srs_success; @@ -417,6 +442,12 @@ srs_error_t SrsDtlsSession::srtp_init() client_key = sClientMasterKey + sClientMasterSalt; server_key = sServerMasterKey + sServerMasterSalt; + srs_trace("client_key size=%d, server_key=%d", client_key.size(), server_key.size()); + + if (srtp_init() != 0) { + return srs_error_wrap(err, "srtp init failed"); + } + if (srtp_sender_side_init() != srs_success) { return srs_error_wrap(err, "srtp sender size init failed"); } @@ -492,11 +523,132 @@ srs_error_t SrsDtlsSession::srtp_receiver_side_init() return err; } -SrsRtcSession::SrsRtcSession(SrsRtcServer* svr) +srs_error_t SrsDtlsSession::srtp_sender_protect(char* protected_buf, const char* ori_buf, int& nb_protected_buf) +{ + srs_error_t err = srs_success; + + if (srtp_send) { + memcpy(protected_buf, ori_buf, nb_protected_buf); + if (srtp_protect(srtp_send, protected_buf, &nb_protected_buf) != 0) { + srs_error("srtp sender protect failed"); + return srs_error_wrap(err, "srtp sender protect failed"); + } + + return err; + } + + return srs_error_wrap(err, "srtp sender protect failed"); +} + +SrsRtcSenderThread::SrsRtcSenderThread(SrsRtcSession* s, SrsUdpRemuxSocket* u, int parent_cid) + : ukt(NULL) +{ + _parent_cid = parent_cid; + trd = new SrsDummyCoroutine(); + + rtc_session = s; + ukt = *u; +} + +SrsRtcSenderThread::~SrsRtcSenderThread() +{ + srs_freep(trd); +} + +int SrsRtcSenderThread::cid() +{ + return trd->cid(); +} + +srs_error_t SrsRtcSenderThread::start() +{ + srs_error_t err = srs_success; + + srs_freep(trd); + trd = new SrsSTCoroutine("recv", this, _parent_cid); + + if ((err = trd->start()) != srs_success) { + return srs_error_wrap(err, "recv thread"); + } + + return err; +} + +void SrsRtcSenderThread::stop() +{ + trd->stop(); +} + +void SrsRtcSenderThread::stop_loop() { - rtc_server = svr; + trd->interrupt(); +} + + +srs_error_t SrsRtcSenderThread::cycle() +{ + srs_error_t err = srs_success; + + SrsSource* source = NULL; + SrsRequest req; + req.app = rtc_session->app; + req.stream = rtc_session->stream; + + if (_srs_sources->fetch_or_create(&req, rtc_session->server, &source) != srs_success) { + srs_error("rtc fetch source failed"); + return srs_error_wrap(err, "rtc fetch source failed"); + } + + srs_trace("rtc fetch source success, app=%s, stream=%s", rtc_session->app.c_str(), rtc_session->stream.c_str()); + + SrsConsumer* consumer = NULL; + if (source->create_consumer(NULL, consumer) != srs_success) { + srs_trace("rtc create consumer, app=%s, stream=%s", rtc_session->app.c_str(), rtc_session->stream.c_str()); + return srs_error_wrap(err, "rtc create consumer, app=%s, stream=%s", rtc_session->app.c_str(), rtc_session->stream.c_str()); + } + + SrsAutoFree(SrsConsumer, consumer); + + while (true) { + SrsMessageArray msgs(SRS_PERF_MW_MSGS); + + int msg_count = 0; + if (consumer->dump_packets(&msgs, msg_count) != srs_success) { + srs_trace("rtc pop no rtp packets"); + continue; + } + + srs_trace("rtc pop %d rtp packets", msg_count); + + for (int i = 0; i < msg_count; i++) { + SrsSharedPtrMessage* msg = msgs.msgs[i]; + + for (int i = 0; i < msg->nb_rtp_fragments; ++i) { + srs_trace("rtp fragment size=%d, payload=%s", msg->rtp_fragments[i].size, + dump_string_hex(msg->rtp_fragments[i].bytes, msg->rtp_fragments[i].size, 128).c_str()); + + if (rtc_session->dtls_session) { + char rtp_send_protected_buf[1500]; + int rtp_send_protected_len = msg->rtp_fragments[i].size; + rtc_session->dtls_session->srtp_sender_protect(rtp_send_protected_buf, msg->rtp_fragments[i].bytes, rtp_send_protected_len); + ukt.sendto(rtp_send_protected_buf, rtp_send_protected_len, 0); + } + } + } + + srs_usleep(16000); + } +} + + +SrsRtcSession::SrsRtcSession(SrsServer* svr, SrsRtcServer* rtc_svr) +{ + server = svr; + rtc_server = rtc_svr; session_state = INIT; dtls_session = NULL; + + strd = NULL; } SrsRtcSession::~SrsRtcSession() @@ -557,19 +709,35 @@ srs_error_t SrsRtcSession::on_binding_request(SrsUdpRemuxSocket* udp_remux_socke srs_error_t SrsRtcSession::send_client_hello(SrsUdpRemuxSocket* udp_remux_socket) { if (dtls_session == NULL) { - dtls_session = new SrsDtlsSession(); + dtls_session = new SrsDtlsSession(this); } dtls_session->send_client_hello(udp_remux_socket); } +void SrsRtcSession::on_connection_established(SrsUdpRemuxSocket* udp_remux_socket) +{ + start_play(udp_remux_socket); +} + +srs_error_t SrsRtcSession::start_play(SrsUdpRemuxSocket* udp_remux_socket) +{ + srs_error_t err = srs_success; + + strd = new SrsRtcSenderThread(this, udp_remux_socket, _srs_context->get_id()); + strd->start(); + + return err; +} + srs_error_t SrsRtcSession::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) { return dtls_session->on_dtls(udp_remux_socket); } -SrsRtcServer::SrsRtcServer() +SrsRtcServer::SrsRtcServer(SrsServer* svr) { + server = svr; } SrsRtcServer::~SrsRtcServer() @@ -600,7 +768,7 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpRemuxSocket* udp_remux_socket) SrsRtcSession* SrsRtcServer::create_rtc_session(const SrsSdp& remote_sdp, SrsSdp& local_sdp) { - SrsRtcSession* session = new SrsRtcSession(this); + SrsRtcSession* session = new SrsRtcSession(server, this); std::string local_pwd = gen_random_str(32); std::string local_ufrag = ""; diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index e715e8151..4f0606571 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -37,7 +37,10 @@ class SrsUdpRemuxSocket; class SrsServer; +class SrsConsumer; class SrsStunPacket; +class SrsRtcServer; +class SrsRtcSession; class SrsCandidate { @@ -95,6 +98,8 @@ enum SrsRtcSessionStateType class SrsDtlsSession { private: + SrsRtcSession* rtc_session; + SSL* dtls; BIO* bio_in; BIO* bio_out; @@ -108,34 +113,62 @@ private: bool handshake_done; public: - SrsDtlsSession(); + SrsDtlsSession(SrsRtcSession* s); virtual ~SrsDtlsSession(); srs_error_t on_dtls(SrsUdpRemuxSocket* udp_remux_socket); - srs_error_t on_dtls_handshake_done(); + srs_error_t on_dtls_handshake_done(SrsUdpRemuxSocket* udp_remux_socket); srs_error_t on_dtls_application_data(const char* data, const int len); void send_client_hello(SrsUdpRemuxSocket* udp_remux_socket); srs_error_t handshake(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t srtp_sender_protect(char* protected_buf, const char* ori_buf, int& nb_protected_buf); private: - srs_error_t srtp_init(); + srs_error_t srtp_initialize(); srs_error_t srtp_sender_side_init(); srs_error_t srtp_receiver_side_init(); }; -class SrsRtcServer; +class SrsRtcSenderThread : public ISrsCoroutineHandler +{ +protected: + SrsCoroutine* trd; + int _parent_cid; +private: + SrsRtcSession* rtc_session; + SrsUdpRemuxSocket ukt; +public: + // Constructor. + // @param tm The receive timeout in srs_utime_t. + SrsRtcSenderThread(SrsRtcSession* s, SrsUdpRemuxSocket* u, int parent_cid); + virtual ~SrsRtcSenderThread(); +public: + virtual int cid(); +public: + virtual srs_error_t start(); + virtual void stop(); + virtual void stop_loop(); +public: + virtual srs_error_t cycle(); +}; class SrsRtcSession { + friend class SrsRtcSenderThread; private: + SrsServer* server; SrsRtcServer* rtc_server; SrsSdp remote_sdp; SrsSdp local_sdp; SrsRtcSessionStateType session_state; SrsDtlsSession* dtls_session; + SrsRtcSenderThread* strd; public: - SrsRtcSession(SrsRtcServer* svr); + std::string app; + std::string stream; +public: + SrsRtcSession(SrsServer* svr, SrsRtcServer* rtc_svr); virtual ~SrsRtcSession(); public: SrsSdp* get_local_sdp() { return &local_sdp; } @@ -145,22 +178,29 @@ public: void set_local_sdp(const SrsSdp& sdp) { local_sdp = sdp; } void set_remote_sdp(const SrsSdp& sdp) { remote_sdp = sdp; } void set_session_state(SrsRtcSessionStateType state) { session_state = state; } + void set_app_stream(const std::string& a, const std::string& s) { app = a; stream = s; } public: srs_error_t on_stun(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req); srs_error_t on_dtls(SrsUdpRemuxSocket* udp_remux_socket); public: srs_error_t send_client_hello(SrsUdpRemuxSocket* udp_remux_socket); + void on_connection_established(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t start_play(SrsUdpRemuxSocket* udp_remux_socket); private: srs_error_t on_binding_request(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req); +private: + srs_error_t do_playing(SrsConsumer* consumer, SrsUdpRemuxSocket* udp_remux_socket); }; class SrsRtcServer : public ISrsUdpRemuxHandler { +private: + SrsServer* server; private: std::map map_username_session; // key: username(local_ufrag + ":" + remote_ufrag) std::map map_id_session; // key: peerip(ip + ":" + port) public: - SrsRtcServer(); + SrsRtcServer(SrsServer* svr); virtual ~SrsRtcServer(); public: virtual srs_error_t initialize(); diff --git a/trunk/src/app/srs_app_rtp.cpp b/trunk/src/app/srs_app_rtp.cpp index 52574fd22..e4d005cf5 100644 --- a/trunk/src/app/srs_app_rtp.cpp +++ b/trunk/src/app/srs_app_rtp.cpp @@ -86,9 +86,71 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF { srs_error_t err = srs_success; + SrsSample* samples = new SrsSample[2000]; + int sample_index = 0; for (int i = 0; i < format->video->nb_samples; ++i) { SrsSample sample = format->video->samples[i]; + + srs_trace("nal size=%d, dump=%s", sample.size, dump_string_hex(sample.bytes, sample.size, 128).c_str()); + + static int max_packet_size = 900; + if (sample.size < max_packet_size) { + char* buf = new char[1460]; + SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsAutoFree(SrsBuffer, stream); + // write rtp header first + stream->write_1bytes(0x80); + stream->write_1bytes((1 << 7) | 102); + + stream->write_2bytes(sequence++); + stream->write_4bytes((int32_t)shared_frame->timestamp); + stream->write_4bytes((int32_t)3233846889); + stream->write_bytes(sample.bytes, sample.size); + + samples[sample_index].bytes = stream->data(); + samples[sample_index].size = stream->pos(); + ++sample_index; + } else { + int num_of_packet = (sample.size + max_packet_size) / max_packet_size; + char* p = sample.bytes + 1; + int left_bytes = sample.size - 1; + for (int n = 0; n < num_of_packet; ++n) { + char* buf = new char[1460]; + SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsAutoFree(SrsBuffer, stream); + // write rtp header first + stream->write_1bytes(0x80); + if (n == num_of_packet - 1) { + stream->write_1bytes((1 << 7) | 102); + } else { + stream->write_1bytes(102); + } + + stream->write_2bytes(sequence++); + stream->write_4bytes((int32_t)shared_frame->timestamp); + stream->write_4bytes((int32_t)3233846889); + + stream->write_1bytes(sample.bytes[0] & 0xE0 | 28); + if (n == 0) { + stream->write_1bytes(0x80 | sample.bytes[0] & 0x1F); + } else if (n == num_of_packet - 1) { + stream->write_1bytes(0x40 | sample.bytes[0] & 0x1F); + } else { + stream->write_1bytes(0x00 | sample.bytes[0] & 0x1F); + } + + int len = max_packet_size ? max_packet_size : left_bytes; + stream->write_bytes(p, len); + left_bytes -= len; + p += len; + + samples[sample_index].bytes = stream->data(); + samples[sample_index].size = stream->pos(); + ++sample_index; + } + } } + shared_frame->set_rtp_fragments(samples, sample_index); return err; } diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index d630ca35d..eee3a9784 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -527,7 +527,7 @@ SrsServer::SrsServer() // new these objects in initialize instead. http_api_mux = new SrsHttpServeMux(); http_server = new SrsHttpServer(this); - rtc_server = new SrsRtcServer(); + rtc_server = new SrsRtcServer(this); http_heartbeat = new SrsHttpHeartbeat(); ingester = new SrsIngester(); } diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index f30eb0163..1452bf93d 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -1696,6 +1696,7 @@ srs_error_t SrsSourceManager::fetch_or_create(SrsRequest* r, ISrsSourceHandler* SrsSource* source = NULL; if ((source = fetch(r)) != NULL) { + srs_trace("found source"); *pps = source; return err; } @@ -1705,6 +1706,8 @@ srs_error_t SrsSourceManager::fetch_or_create(SrsRequest* r, ISrsSourceHandler* // should always not exists for create a source. srs_assert (pool.find(stream_url) == pool.end()); + + srs_trace("new source, stream_url=%s", stream_url.c_str()); source = new SrsSource(); if ((err = source->initialize(r, h)) != srs_success) { diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index 4dc8f68da..aa0d3a7ec 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -214,6 +214,14 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload() srs_memory_unwatch(payload); #endif srs_freepa(payload); + + for (int i = 0; i < nb_rtp_fragments; ++i) { + srs_freep(rtp_fragments[i].bytes); + } + + if (nb_rtp_fragments) { + srs_freepa(rtp_fragments); + } } SrsSharedPtrMessage::SrsSharedPtrMessage() : timestamp(0), stream_id(0), size(0), payload(NULL), rtp_fragments(NULL), nb_rtp_fragments(0) @@ -307,6 +315,15 @@ bool SrsSharedPtrMessage::check(int stream_id) return false; } +void SrsSharedPtrMessage::set_rtp_fragments(SrsSample* samples, int nb_samples) +{ + ptr->rtp_fragments = samples; + ptr->nb_rtp_fragments = nb_samples; + + rtp_fragments = samples; + nb_rtp_fragments = nb_samples; +} + bool SrsSharedPtrMessage::is_av() { return ptr->header.message_type == RTMP_MSG_AudioMessage diff --git a/trunk/src/kernel/srs_kernel_flv.hpp b/trunk/src/kernel/srs_kernel_flv.hpp index 30052b881..fe8fd9aa0 100644 --- a/trunk/src/kernel/srs_kernel_flv.hpp +++ b/trunk/src/kernel/srs_kernel_flv.hpp @@ -333,6 +333,8 @@ public: // check perfer cid and stream id. // @return whether stream id already set. virtual bool check(int stream_id); + + virtual void set_rtp_fragments(SrsSample* samples, int nb_samples); public: virtual bool is_av(); virtual bool is_audio(); From e2675109fbfa61ba5269e5302f22c3119b71e65a Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Tue, 10 Mar 2020 00:45:40 +0800 Subject: [PATCH 15/21] fix rtp h264 packet bug --- trunk/src/app/srs_app_rtc_conn.cpp | 2 +- trunk/src/app/srs_app_rtp.cpp | 69 ++++++++++++++++++------------ trunk/src/app/srs_app_rtp.hpp | 2 +- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index b543c3cae..f60172072 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -625,7 +625,7 @@ srs_error_t SrsRtcSenderThread::cycle() for (int i = 0; i < msg->nb_rtp_fragments; ++i) { srs_trace("rtp fragment size=%d, payload=%s", msg->rtp_fragments[i].size, - dump_string_hex(msg->rtp_fragments[i].bytes, msg->rtp_fragments[i].size, 128).c_str()); + dump_string_hex(msg->rtp_fragments[i].bytes, msg->rtp_fragments[i].size, 1460).c_str()); if (rtc_session->dtls_session) { char rtp_send_protected_buf[1500]; diff --git a/trunk/src/app/srs_app_rtp.cpp b/trunk/src/app/srs_app_rtp.cpp index e4d005cf5..4081f4534 100644 --- a/trunk/src/app/srs_app_rtp.cpp +++ b/trunk/src/app/srs_app_rtp.cpp @@ -58,19 +58,17 @@ static string dump_string_hex(const std::string& str, const int& max_len = 128) static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = 128) { - char tmp_buf[1024*16]; - int len = 0; + string ret; + ret.reserve(nb_buf > max_len ? nb_buf * 4 : max_len * 4); + char tmp[64]; for (int i = 0; i < nb_buf && i < max_len; ++i) { - int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 1, "%02X ", (uint8_t)buf[i]); - if (nb <= 0) - break; - - len += nb; + int nb = snprintf(tmp, sizeof(tmp), "%02X ", (uint8_t)buf[i]); + assert(nb == 3); + ret.append(tmp, nb); } - tmp_buf[len] = '\0'; - return string(tmp_buf, len); + return ret; } SrsRtpMuxer::SrsRtpMuxer() @@ -86,15 +84,29 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF { srs_error_t err = srs_success; - SrsSample* samples = new SrsSample[2000]; - int sample_index = 0; - for (int i = 0; i < format->video->nb_samples; ++i) { - SrsSample sample = format->video->samples[i]; + int nb_samples = format->video->nb_samples; + SrsSample* samples = format->video->samples; + + SrsSample sps_pps_samples[2]; + if (format->is_avc_sequence_header()) { + sps_pps_samples[0].bytes = format->vcodec->sequenceParameterSetNALUnit.data(); + sps_pps_samples[0].size = format->vcodec->sequenceParameterSetNALUnit.size(); + sps_pps_samples[1].bytes = format->vcodec->pictureParameterSetNALUnit.data(); + sps_pps_samples[1].size = format->vcodec->pictureParameterSetNALUnit.size(); + + nb_samples = 2; + samples = sps_pps_samples; + } + + SrsSample* rtp_fragment_samples = new SrsSample[2000]; + int rtp_fragment_samples_index = 0; + for (int i = 0; i < nb_samples; ++i) { + SrsSample sample = samples[i]; - srs_trace("nal size=%d, dump=%s", sample.size, dump_string_hex(sample.bytes, sample.size, 128).c_str()); + srs_trace("nal size=%d, dump=%s", sample.size, dump_string_hex(sample.bytes, sample.size, sample.size).c_str()); static int max_packet_size = 900; - if (sample.size < max_packet_size) { + if (sample.size <= max_packet_size) { char* buf = new char[1460]; SrsBuffer* stream = new SrsBuffer(buf, 1460); SrsAutoFree(SrsBuffer, stream); @@ -107,9 +119,10 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF stream->write_4bytes((int32_t)3233846889); stream->write_bytes(sample.bytes, sample.size); - samples[sample_index].bytes = stream->data(); - samples[sample_index].size = stream->pos(); - ++sample_index; + rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); + rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); + + ++rtp_fragment_samples_index; } else { int num_of_packet = (sample.size + max_packet_size) / max_packet_size; char* p = sample.bytes + 1; @@ -130,27 +143,29 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF stream->write_4bytes((int32_t)shared_frame->timestamp); stream->write_4bytes((int32_t)3233846889); - stream->write_1bytes(sample.bytes[0] & 0xE0 | 28); + stream->write_1bytes((sample.bytes[0] & 0xE0) | 28); if (n == 0) { - stream->write_1bytes(0x80 | sample.bytes[0] & 0x1F); + stream->write_1bytes(0x80 | (sample.bytes[0] & 0x1F)); } else if (n == num_of_packet - 1) { - stream->write_1bytes(0x40 | sample.bytes[0] & 0x1F); + stream->write_1bytes(0x40 | (sample.bytes[0] & 0x1F)); } else { - stream->write_1bytes(0x00 | sample.bytes[0] & 0x1F); + stream->write_1bytes(0x00 | (sample.bytes[0] & 0x1F)); } - int len = max_packet_size ? max_packet_size : left_bytes; + int len = left_bytes > max_packet_size ? max_packet_size : left_bytes; stream->write_bytes(p, len); left_bytes -= len; p += len; - samples[sample_index].bytes = stream->data(); - samples[sample_index].size = stream->pos(); - ++sample_index; + rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); + rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); + + ++rtp_fragment_samples_index; + } } } - shared_frame->set_rtp_fragments(samples, sample_index); + shared_frame->set_rtp_fragments(rtp_fragment_samples, rtp_fragment_samples_index); return err; } diff --git a/trunk/src/app/srs_app_rtp.hpp b/trunk/src/app/srs_app_rtp.hpp index a11317948..15b206097 100644 --- a/trunk/src/app/srs_app_rtp.hpp +++ b/trunk/src/app/srs_app_rtp.hpp @@ -38,7 +38,7 @@ class SrsOriginHub; class SrsRtpMuxer { private: - uint32_t sequence; + uint16_t sequence; std::map packet_queue; public: SrsRtpMuxer(); From ff0e03800d306f0cb0db311dd7eb6312f9a1faed Mon Sep 17 00:00:00 2001 From: HuyaJohn Date: Tue, 10 Mar 2020 04:47:49 -0700 Subject: [PATCH 16/21] h264 rtp debuging --- trunk/src/app/srs_app_rtc_conn.cpp | 93 +++++++++++- trunk/src/app/srs_app_rtc_conn.hpp | 2 + trunk/src/app/srs_app_rtp.cpp | 219 +++++++++++++++++++++++++++-- 3 files changed, 300 insertions(+), 14 deletions(-) diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index f60172072..a901efab9 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -86,14 +86,20 @@ static string dump_string_hex(const std::string& str, const int& max_len = 128) static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = 128) { char tmp_buf[1024*16]; - int len = 0; + tmp_buf[0] = '\n'; + int len = 1; for (int i = 0; i < nb_buf && i < max_len; ++i) { - int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 1, "%02X ", (uint8_t)buf[i]); + //int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "(%03d)%02X ", i, (uint8_t)buf[i]); + int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "%02X ", (uint8_t)buf[i]); if (nb <= 0) break; len += nb; + + if (i % 16 == 15) { + tmp_buf[len++] = '\n'; + } } tmp_buf[len] = '\0'; @@ -540,6 +546,23 @@ srs_error_t SrsDtlsSession::srtp_sender_protect(char* protected_buf, const char* return srs_error_wrap(err, "srtp sender protect failed"); } +srs_error_t SrsDtlsSession::srtp_receiver_unprotect(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf) +{ + srs_error_t err = srs_success; + + if (srtp_send) { + memcpy(unprotected_buf, ori_buf, nb_unprotected_buf); + if (srtp_unprotect(srtp_recv, unprotected_buf, &nb_unprotected_buf) != 0) { + srs_error("srtp receiver unprotect failed"); + return srs_error_wrap(err, "srtp receiver unprotect failed"); + } + + return err; + } + + return srs_error_wrap(err, "srtp receiver unprotect failed"); +} + SrsRtcSenderThread::SrsRtcSenderThread(SrsRtcSession* s, SrsUdpRemuxSocket* u, int parent_cid) : ukt(NULL) { @@ -627,6 +650,12 @@ srs_error_t SrsRtcSenderThread::cycle() srs_trace("rtp fragment size=%d, payload=%s", msg->rtp_fragments[i].size, dump_string_hex(msg->rtp_fragments[i].bytes, msg->rtp_fragments[i].size, 1460).c_str()); + SrsBuffer stream(msg->rtp_fragments[i].bytes + 2, 2); + static uint16_t seq = 0; + stream.write_2bytes(++seq); + + srs_trace("seq=%u", seq); + if (rtc_session->dtls_session) { char rtp_send_protected_buf[1500]; int rtp_send_protected_len = msg->rtp_fragments[i].size; @@ -735,6 +764,56 @@ srs_error_t SrsRtcSession::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) return dtls_session->on_dtls(udp_remux_socket); } +srs_error_t SrsRtcSession::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) +{ + srs_error_t err = srs_success; + if (dtls_session == NULL) { + return srs_error_wrap(err, "recv unexpect rtp/rtcp packet before dtls done"); + } + + char srtp_unprotect_buf[1460]; + int nb_srtp_unprotect_buf = udp_remux_socket->size(); + if (dtls_session->srtp_receiver_unprotect(srtp_unprotect_buf, udp_remux_socket->data(), nb_srtp_unprotect_buf) != srs_success) { + return srs_error_wrap(err, "srtp receiver unprotect failed"); + } + + //srs_trace("srtp unprotect success, %s", dump_string_hex(srtp_unprotect_buf, nb_srtp_unprotect_buf, nb_srtp_unprotect_buf).c_str()); + + SrsBuffer* stream = new SrsBuffer(srtp_unprotect_buf, nb_srtp_unprotect_buf); + uint8_t first = stream->read_1bytes(); + uint8_t second = stream->read_1bytes(); + bool marker = (second & 0x80) == 0x80; + uint8_t payload_type = second &0x7F; + + uint16_t sequence = stream->read_2bytes(); + uint32_t timestamp = stream->read_4bytes(); + uint32_t ssrc = stream->read_4bytes(); + + srs_trace("sequence=%u, timestamp=%u, ssrc=%u, marker=%d, payload_type=%u", sequence, timestamp, ssrc, marker, payload_type); + + if (first & 0x10) { + uint16_t extern_profile = stream->read_2bytes(); + uint16_t extern_length = stream->read_2bytes(); + + srs_trace("extern_profile=%u, extern_length=%u", extern_profile, extern_length); + + stream->read_string(extern_length * 4); + } + + if (payload_type == 102) { + char rtp_send_protected_buf[1500]; + int rtp_send_protected_len = nb_srtp_unprotect_buf; + SrsBuffer stream(srtp_unprotect_buf + 8, 4); + stream.write_4bytes(3233846889); + dtls_session->srtp_sender_protect(rtp_send_protected_buf, srtp_unprotect_buf, rtp_send_protected_len); + udp_remux_socket->sendto(rtp_send_protected_buf, rtp_send_protected_len, 0); + } + + srs_trace("rtp payload, %s", dump_string_hex(stream->data() + stream->pos(), stream->left(), stream->left()).c_str()); + + return err; +} + SrsRtcServer::SrsRtcServer(SrsServer* svr) { server = svr; @@ -828,7 +907,6 @@ srs_error_t SrsRtcServer::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) srs_error_t err = srs_success; srs_trace("on dtls"); - // FIXME SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(udp_remux_socket->get_peer_id()); if (rtc_session == NULL) { @@ -844,6 +922,15 @@ srs_error_t SrsRtcServer::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) { srs_error_t err = srs_success; srs_trace("on rtp/rtcp"); + + SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(udp_remux_socket->get_peer_id()); + + if (rtc_session == NULL) { + return srs_error_wrap(err, "can not find rtc session by peer_id=%s", udp_remux_socket->get_peer_id().c_str()); + } + + rtc_session->on_rtp_or_rtcp(udp_remux_socket); + return err; } diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index 4f0606571..fab2afc4c 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -123,6 +123,7 @@ public: void send_client_hello(SrsUdpRemuxSocket* udp_remux_socket); srs_error_t handshake(SrsUdpRemuxSocket* udp_remux_socket); srs_error_t srtp_sender_protect(char* protected_buf, const char* ori_buf, int& nb_protected_buf); + srs_error_t srtp_receiver_unprotect(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf); private: srs_error_t srtp_initialize(); @@ -182,6 +183,7 @@ public: public: srs_error_t on_stun(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req); srs_error_t on_dtls(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket); public: srs_error_t send_client_hello(SrsUdpRemuxSocket* udp_remux_socket); void on_connection_established(SrsUdpRemuxSocket* udp_remux_socket); diff --git a/trunk/src/app/srs_app_rtp.cpp b/trunk/src/app/srs_app_rtp.cpp index 4081f4534..d3bf6534f 100644 --- a/trunk/src/app/srs_app_rtp.cpp +++ b/trunk/src/app/srs_app_rtp.cpp @@ -53,20 +53,34 @@ using namespace std; static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len); static string dump_string_hex(const std::string& str, const int& max_len = 128) { - return dump_string_hex(str.c_str(), str.size(), max_len); + return dump_string_hex(str.c_str(), str.size(), max_len); } static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = 128) { string ret; - ret.reserve(nb_buf > max_len ? nb_buf * 4 : max_len * 4); + ret.reserve((nb_buf > max_len ? nb_buf : max_len) * 8); + + char tmp_buf[1024*16]; + tmp_buf[0] = '\n'; + int len = 1; - char tmp[64]; for (int i = 0; i < nb_buf && i < max_len; ++i) { - int nb = snprintf(tmp, sizeof(tmp), "%02X ", (uint8_t)buf[i]); - assert(nb == 3); - ret.append(tmp, nb); + //int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "(%03d)%02X ", i, (uint8_t)buf[i]); + int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "%02X ", (uint8_t)buf[i]); + if (nb <= 0) + break; + + len += nb; + + if (i % 16 == 15) { + tmp_buf[len++] = '\n'; + ret.append(tmp_buf, len); + len = 0; + } } + tmp_buf[len] = '\0'; + ret.append(tmp_buf, len); return ret; } @@ -80,6 +94,7 @@ SrsRtpMuxer::~SrsRtpMuxer() { } +#if 0 srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsFormat* format) { srs_error_t err = srs_success; @@ -87,6 +102,15 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF int nb_samples = format->video->nb_samples; SrsSample* samples = format->video->samples; + SrsSample* rtp_fragment_samples = new SrsSample[2000]; + int rtp_fragment_samples_index = 0; + + static int debug_fd = -1; + static uint8_t start_code[4] = {0x00, 0x00, 0x00, 0x01}; + if (debug_fd < 0) { + debug_fd = open("./raw.264", O_CREAT|O_TRUNC|O_RDWR, 0664); + } + SrsSample sps_pps_samples[2]; if (format->is_avc_sequence_header()) { sps_pps_samples[0].bytes = format->vcodec->sequenceParameterSetNALUnit.data(); @@ -96,15 +120,181 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF nb_samples = 2; samples = sps_pps_samples; + + { + char* buf = new char[1460]; + SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsAutoFree(SrsBuffer, stream); + // write rtp header first + stream->write_1bytes(0x80); + stream->write_1bytes(102); + + stream->write_2bytes(sequence++); + stream->write_4bytes((int32_t)shared_frame->timestamp * 90); + stream->write_4bytes((int32_t)3233846889); + + stream->write_1bytes(24/*STAP-A*/); + // AUD + stream->write_2bytes(2); + stream->write_1bytes(0x09); + stream->write_1bytes(0x10); + + stream->write_2bytes(sps_pps_samples[0].size); + stream->write_bytes(sps_pps_samples[0].bytes, sps_pps_samples[0].size); + stream->write_2bytes(sps_pps_samples[1].size); + stream->write_bytes(sps_pps_samples[1].bytes, sps_pps_samples[1].size); + + if (debug_fd >= 0) { + write(debug_fd, start_code, sizeof(start_code)); + write(debug_fd, sps_pps_samples[0].bytes, sps_pps_samples[0].size); + write(debug_fd, start_code, sizeof(start_code)); + write(debug_fd, sps_pps_samples[1].bytes, sps_pps_samples[1].size); + } + + rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); + rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); + + ++rtp_fragment_samples_index; + } + shared_frame->set_rtp_fragments(rtp_fragment_samples, rtp_fragment_samples_index); + + return err; + } + + for (int i = 0; i < nb_samples; ++i) { + SrsSample sample = samples[i]; + + srs_trace("nal size=%d, dump=%s", sample.size, dump_string_hex(sample.bytes, sample.size, sample.size).c_str()); + + if ((sample.bytes[0] & 0x1F) == 0x06) { + srs_trace("ignore SEI"); + continue; + } + + if (debug_fd >= 0) { + write(debug_fd, start_code, sizeof(start_code)); + write(debug_fd, sample.bytes, sample.size); + } + + static int max_packet_size = 900; + if (sample.size <= max_packet_size) { + char* buf = new char[1460]; + SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsAutoFree(SrsBuffer, stream); + // write rtp header first + stream->write_1bytes(0x80); + if ((sample.bytes[0] & 0x1F) <= 5) { + stream->write_1bytes((1 << 7) | 102); + } else { + stream->write_1bytes(102); + } + + stream->write_2bytes(sequence++); + stream->write_4bytes((int32_t)shared_frame->timestamp * 90); + stream->write_4bytes((int32_t)3233846889); + +#if 0 // single nalu + stream->write_bytes(sample.bytes, sample.size); +#else + stream->write_1bytes((sample.bytes[0] & 0xE0) | 24/*STAP-A*/); + stream->write_2bytes(sample.size); + stream->write_bytes(sample.bytes, sample.size); +#endif + + rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); + rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); + + ++rtp_fragment_samples_index; + } else { + int num_of_packet = (sample.size + max_packet_size) / max_packet_size; + char* p = sample.bytes + 1; + int left_bytes = sample.size - 1; + for (int n = 0; n < num_of_packet; ++n) { + char* buf = new char[1460]; + SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsAutoFree(SrsBuffer, stream); + // write rtp header first + stream->write_1bytes(0x80); + if ((sample.bytes[0] & 0x1F) <= 5) { + stream->write_1bytes((1 << 7) | 102); + } else { + stream->write_1bytes(102); + } + + stream->write_2bytes(sequence++); + stream->write_4bytes((int32_t)shared_frame->timestamp * 90); + stream->write_4bytes((int32_t)3233846889); + + stream->write_1bytes((sample.bytes[0] & 0xE0) | 28); + if (n == 0) { + stream->write_1bytes(0x80 | (sample.bytes[0] & 0x1F)); + } else if (n == num_of_packet - 1) { + stream->write_1bytes(0x40 | (sample.bytes[0] & 0x1F)); + } else { + stream->write_1bytes(0x00 | (sample.bytes[0] & 0x1F)); + } + + int len = left_bytes > max_packet_size ? max_packet_size : left_bytes; + stream->write_bytes(p, len); + left_bytes -= len; + p += len; + + rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); + rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); + + ++rtp_fragment_samples_index; + + } + } } + shared_frame->set_rtp_fragments(rtp_fragment_samples, rtp_fragment_samples_index); + + return err; +} +#endif + +srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsFormat* format) +{ + srs_error_t err = srs_success; + + int nb_samples = format->video->nb_samples; + SrsSample* samples = format->video->samples; SrsSample* rtp_fragment_samples = new SrsSample[2000]; int rtp_fragment_samples_index = 0; + + static int debug_fd = -1; + static uint8_t start_code[4] = {0x00, 0x00, 0x00, 0x01}; + if (debug_fd < 0) { + debug_fd = open("./raw.264", O_CREAT|O_TRUNC|O_RDWR, 0664); + } + + SrsSample sps_pps_samples[2]; + if (format->is_avc_sequence_header()) { + sps_pps_samples[0].bytes = format->vcodec->sequenceParameterSetNALUnit.data(); + sps_pps_samples[0].size = format->vcodec->sequenceParameterSetNALUnit.size(); + sps_pps_samples[1].bytes = format->vcodec->pictureParameterSetNALUnit.data(); + sps_pps_samples[1].size = format->vcodec->pictureParameterSetNALUnit.size(); + + nb_samples = 2; + samples = sps_pps_samples; + } + for (int i = 0; i < nb_samples; ++i) { SrsSample sample = samples[i]; srs_trace("nal size=%d, dump=%s", sample.size, dump_string_hex(sample.bytes, sample.size, sample.size).c_str()); + if ((sample.bytes[0] & 0x1F) == 0x06) { + srs_trace("ignore SEI"); + continue; + } + + if (debug_fd >= 0) { + write(debug_fd, start_code, sizeof(start_code)); + write(debug_fd, sample.bytes, sample.size); + } + static int max_packet_size = 900; if (sample.size <= max_packet_size) { char* buf = new char[1460]; @@ -112,12 +302,19 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF SrsAutoFree(SrsBuffer, stream); // write rtp header first stream->write_1bytes(0x80); - stream->write_1bytes((1 << 7) | 102); + if ((sample.bytes[0] & 0x1F) <= 5) { + stream->write_1bytes((1 << 7) | 102); + } else { + stream->write_1bytes(102); + } stream->write_2bytes(sequence++); - stream->write_4bytes((int32_t)shared_frame->timestamp); + stream->write_4bytes((int32_t)shared_frame->timestamp * 90); stream->write_4bytes((int32_t)3233846889); - stream->write_bytes(sample.bytes, sample.size); + + stream->write_1bytes((sample.bytes[0] & 0xE0) | 28/*FU-A*/); + stream->write_1bytes(0xC0 | (sample.bytes[0] & 0x1F)); + stream->write_bytes(sample.bytes + 1, sample.size - 1); rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); @@ -133,14 +330,14 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF SrsAutoFree(SrsBuffer, stream); // write rtp header first stream->write_1bytes(0x80); - if (n == num_of_packet - 1) { + if ((sample.bytes[0] & 0x1F) <= 5) { stream->write_1bytes((1 << 7) | 102); } else { stream->write_1bytes(102); } stream->write_2bytes(sequence++); - stream->write_4bytes((int32_t)shared_frame->timestamp); + stream->write_4bytes((int32_t)shared_frame->timestamp * 90); stream->write_4bytes((int32_t)3233846889); stream->write_1bytes((sample.bytes[0] & 0xE0) | 28); From e831f3254a64d50a03458ec3788a71b36c691ca5 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Wed, 11 Mar 2020 00:04:12 +0800 Subject: [PATCH 17/21] some code --- trunk/src/app/srs_app_log.cpp | 2 +- trunk/src/app/srs_app_rtc_conn.cpp | 15 +- trunk/src/app/srs_app_rtp.cpp | 264 +++++++++++++++++++---------- trunk/src/app/srs_app_rtp.hpp | 8 +- 4 files changed, 197 insertions(+), 92 deletions(-) diff --git a/trunk/src/app/srs_app_log.cpp b/trunk/src/app/srs_app_log.cpp index 4d73ebf8e..f743a056c 100644 --- a/trunk/src/app/srs_app_log.cpp +++ b/trunk/src/app/srs_app_log.cpp @@ -37,7 +37,7 @@ #include // the max size of a line of log. -#define LOG_MAX_SIZE 4096 +#define LOG_MAX_SIZE 4096000 // the tail append to each log. #define LOG_TAIL '\n' diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index a901efab9..af9fad322 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -85,10 +85,13 @@ static string dump_string_hex(const std::string& str, const int& max_len = 128) static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = 128) { + string ret; + ret.reserve(max_len * 4); + char tmp_buf[1024*16]; tmp_buf[0] = '\n'; int len = 1; - + for (int i = 0; i < nb_buf && i < max_len; ++i) { //int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "(%03d)%02X ", i, (uint8_t)buf[i]); int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "%02X ", (uint8_t)buf[i]); @@ -97,13 +100,17 @@ static string dump_string_hex(const char* buf, const int nb_buf, const int& max_ len += nb; - if (i % 16 == 15) { + if (i % 48 == 47) { tmp_buf[len++] = '\n'; - } + ret.append(tmp_buf, len); + len = 0; + } } tmp_buf[len] = '\0'; + ret.append(tmp_buf, len); + + return ret; - return string(tmp_buf, len); } SrsCandidate::SrsCandidate() diff --git a/trunk/src/app/srs_app_rtp.cpp b/trunk/src/app/srs_app_rtp.cpp index d3bf6534f..3a00d3d34 100644 --- a/trunk/src/app/srs_app_rtp.cpp +++ b/trunk/src/app/srs_app_rtp.cpp @@ -59,7 +59,7 @@ static string dump_string_hex(const std::string& str, const int& max_len = 128) static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = 128) { string ret; - ret.reserve((nb_buf > max_len ? nb_buf : max_len) * 8); + ret.reserve(max_len * 4); char tmp_buf[1024*16]; tmp_buf[0] = '\n'; @@ -73,7 +73,7 @@ static string dump_string_hex(const char* buf, const int nb_buf, const int& max_ len += nb; - if (i % 16 == 15) { + if (i % 48 == 47) { tmp_buf[len++] = '\n'; ret.append(tmp_buf, len); len = 0; @@ -85,6 +85,7 @@ static string dump_string_hex(const char* buf, const int nb_buf, const int& max_ return ret; } + SrsRtpMuxer::SrsRtpMuxer() { sequence = 0; @@ -253,116 +254,207 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF } #endif -srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsFormat* format) -{ - srs_error_t err = srs_success; +const int max_payload_size = 1200; +const int kRtpPacketSize = 1500; - int nb_samples = format->video->nb_samples; - SrsSample* samples = format->video->samples; +const uint8_t kMarker = 0x80; +const uint8_t kH264PayloadType = 102; - SrsSample* rtp_fragment_samples = new SrsSample[2000]; - int rtp_fragment_samples_index = 0; +const uint8_t kNalTypeMask = 0x1F; - static int debug_fd = -1; - static uint8_t start_code[4] = {0x00, 0x00, 0x00, 0x01}; - if (debug_fd < 0) { - debug_fd = open("./raw.264", O_CREAT|O_TRUNC|O_RDWR, 0664); - } +const uint8_t kIdr = 5; +const uint8_t kStapA = 24; +const uint8_t kFua = 28; - SrsSample sps_pps_samples[2]; - if (format->is_avc_sequence_header()) { - sps_pps_samples[0].bytes = format->vcodec->sequenceParameterSetNALUnit.data(); - sps_pps_samples[0].size = format->vcodec->sequenceParameterSetNALUnit.size(); - sps_pps_samples[1].bytes = format->vcodec->pictureParameterSetNALUnit.data(); - sps_pps_samples[1].size = format->vcodec->pictureParameterSetNALUnit.size(); +const uint8_t kStart = 0x80; +const uint8_t kEnd = 0x40; - nb_samples = 2; - samples = sps_pps_samples; +const uint32_t kVideoSSRC = 3233846889; + +srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsFormat* format) +{ + srs_error_t err = srs_success; + + if (format->is_avc_sequence_header()) { + sps.assign(format->vcodec->sequenceParameterSetNALUnit.data(), format->vcodec->sequenceParameterSetNALUnit.size()); + pps.assign(format->vcodec->pictureParameterSetNALUnit.data(), format->vcodec->pictureParameterSetNALUnit.size()); } - for (int i = 0; i < nb_samples; ++i) { - SrsSample sample = samples[i]; + for (int i = 0; i < format->video->nb_samples; ++i) { + SrsSample sample = format->video->samples[i]; - srs_trace("nal size=%d, dump=%s", sample.size, dump_string_hex(sample.bytes, sample.size, sample.size).c_str()); + uint8_t header = sample.bytes[0]; + uint8_t nal_type = header & 0x1F; - if ((sample.bytes[0] & 0x1F) == 0x06) { + if (nal_type == 0x06) { srs_trace("ignore SEI"); continue; } - if (debug_fd >= 0) { - write(debug_fd, start_code, sizeof(start_code)); - write(debug_fd, sample.bytes, sample.size); + if (sample.size <= max_payload_size) { + packet_single_nalu(shared_frame, format, &sample); + } else { + packet_fu_a(shared_frame, format, &sample); } - static int max_packet_size = 900; - if (sample.size <= max_packet_size) { - char* buf = new char[1460]; - SrsBuffer* stream = new SrsBuffer(buf, 1460); - SrsAutoFree(SrsBuffer, stream); - // write rtp header first - stream->write_1bytes(0x80); - if ((sample.bytes[0] & 0x1F) <= 5) { - stream->write_1bytes((1 << 7) | 102); - } else { - stream->write_1bytes(102); - } + srs_trace("nal size=%d, nal=%s", sample.size, dump_string_hex(sample.bytes, sample.size, sample.size).c_str()); + for (int i = 0; i < shared_frame->nb_rtp_fragments; ++i) { + srs_trace("rtp=%s", dump_string_hex(shared_frame->rtp_fragments[i].bytes, shared_frame->rtp_fragments[i].size, kRtpPacketSize).c_str()); + } + } - stream->write_2bytes(sequence++); - stream->write_4bytes((int32_t)shared_frame->timestamp * 90); - stream->write_4bytes((int32_t)3233846889); + return err; +} - stream->write_1bytes((sample.bytes[0] & 0xE0) | 28/*FU-A*/); - stream->write_1bytes(0xC0 | (sample.bytes[0] & 0x1F)); - stream->write_bytes(sample.bytes + 1, sample.size - 1); +srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample) +{ + srs_error_t err = srs_success; - rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); - rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); + vector rtp_packet_vec; - ++rtp_fragment_samples_index; - } else { - int num_of_packet = (sample.size + max_packet_size) / max_packet_size; - char* p = sample.bytes + 1; - int left_bytes = sample.size - 1; - for (int n = 0; n < num_of_packet; ++n) { - char* buf = new char[1460]; - SrsBuffer* stream = new SrsBuffer(buf, 1460); - SrsAutoFree(SrsBuffer, stream); - // write rtp header first - stream->write_1bytes(0x80); - if ((sample.bytes[0] & 0x1F) <= 5) { - stream->write_1bytes((1 << 7) | 102); - } else { - stream->write_1bytes(102); - } + char* p = sample->bytes + 1; + int nb_left = sample->size - 1; + uint8_t header = sample->bytes[0]; + uint8_t nal_type = header & kNalTypeMask; - stream->write_2bytes(sequence++); - stream->write_4bytes((int32_t)shared_frame->timestamp * 90); - stream->write_4bytes((int32_t)3233846889); + if (nal_type == kIdr) { + packet_stap_a(sps, pps, shared_frame, rtp_packet_vec); + } - stream->write_1bytes((sample.bytes[0] & 0xE0) | 28); - if (n == 0) { - stream->write_1bytes(0x80 | (sample.bytes[0] & 0x1F)); - } else if (n == num_of_packet - 1) { - stream->write_1bytes(0x40 | (sample.bytes[0] & 0x1F)); - } else { - stream->write_1bytes(0x00 | (sample.bytes[0] & 0x1F)); - } + int num_of_packet = (sample->size - 1 + max_payload_size) / max_payload_size; + int avg_packet_size = sample->size / num_of_packet; + for (int i = 0; i < num_of_packet; ++i) { + char* buf = new char[kRtpPacketSize]; + SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsAutoFree(SrsBuffer, stream); + + int packet_size = min(nb_left, max_payload_size); + + // v=2,p=0,x=0,cc=0 + stream->write_1bytes(0x80); + // marker payloadtype + stream->write_1bytes(kMarker | kH264PayloadType); + // sequenct + stream->write_2bytes(sequence++); + // timestamp + stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); + // ssrc + stream->write_4bytes(int32_t(kVideoSSRC)); + + // fu-indicate + uint8_t fu_indicate = kFua; + fu_indicate |= (nal_type & (~kNalTypeMask)); + stream->write_1bytes(fu_indicate); + + uint8_t fu_header = nal_type & kNalTypeMask; + if (i == 0) + fu_header |= kStart; + if (i == num_of_packet - 1) + fu_header |= kEnd; + stream->write_1bytes(fu_header); + + stream->write_bytes(p, packet_size); + p += packet_size; + nb_left -= max_payload_size; + + + SrsSample rtp_packet; + rtp_packet.bytes = stream->data(); + rtp_packet.size = stream->pos(); + + rtp_packet_vec.push_back(rtp_packet); + } - int len = left_bytes > max_packet_size ? max_packet_size : left_bytes; - stream->write_bytes(p, len); - left_bytes -= len; - p += len; + SrsSample* rtp_samples = new SrsSample[rtp_packet_vec.size()]; + for (int i = 0; i < rtp_packet_vec.size(); ++i) { + rtp_samples[i] = rtp_packet_vec[i]; + } - rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); - rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); + shared_frame->set_rtp_fragments(rtp_samples, rtp_packet_vec.size()); +} - ++rtp_fragment_samples_index; +srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample) +{ + srs_error_t err = srs_success; - } - } + vector rtp_packet_vec; + + uint8_t header = sample->bytes[0]; + uint8_t nal_type = header & kNalTypeMask; + + char* buf = new char[kRtpPacketSize]; + SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsAutoFree(SrsBuffer, stream); + + if (nal_type == kIdr) { + packet_stap_a(sps, pps, shared_frame, rtp_packet_vec); } - shared_frame->set_rtp_fragments(rtp_fragment_samples, rtp_fragment_samples_index); + + // v=2,p=0,x=0,cc=0 + stream->write_1bytes(0x80); + // marker payloadtype + stream->write_1bytes(kMarker | kH264PayloadType); + // sequenct + stream->write_2bytes(sequence++); + // timestamp + stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); + // ssrc + stream->write_4bytes(int32_t(kVideoSSRC)); + + stream->write_bytes(sample->bytes, sample->size); + + SrsSample rtp_packet; + rtp_packet.bytes = stream->data(); + rtp_packet.size = stream->pos(); + + SrsSample* rtp_samples = new SrsSample[rtp_packet_vec.size()]; + for (int i = 0; i < rtp_packet_vec.size(); ++i) { + rtp_samples[i] = rtp_packet_vec[i]; + } + + shared_frame->set_rtp_fragments(rtp_samples, rtp_packet_vec.size()); + + return err; +} + +srs_error_t SrsRtpMuxer::packet_stap_a(const string &sps, const string& pps, SrsSharedPtrMessage* shared_frame, vector& rtp_packet_vec) +{ + srs_error_t err = srs_success; + + uint8_t header = sps[0]; + uint8_t nal_type = header & kNalTypeMask; + + char* buf = new char[kRtpPacketSize]; + SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsAutoFree(SrsBuffer, stream); + + // v=2,p=0,x=0,cc=0 + stream->write_1bytes(0x80); + // marker payloadtype + stream->write_1bytes(kMarker | kH264PayloadType); + // sequenct + stream->write_2bytes(sequence++); + // timestamp + stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); + // ssrc + stream->write_4bytes(int32_t(kVideoSSRC)); + + // stap-a header + uint8_t stap_a_header = kStapA; + stap_a_header |= (nal_type & (~kNalTypeMask)); + stream->write_1bytes(stap_a_header); + + stream->write_2bytes(sps.size()); + stream->write_bytes((char*)sps.data(), sps.size()); + + stream->write_2bytes(pps.size()); + stream->write_bytes((char*)pps.data(), pps.size()); + + SrsSample rtp_packet; + rtp_packet.bytes = stream->data(); + rtp_packet.size = stream->pos(); + + rtp_packet_vec.push_back(rtp_packet); return err; } diff --git a/trunk/src/app/srs_app_rtp.hpp b/trunk/src/app/srs_app_rtp.hpp index 15b206097..1d0f510dd 100644 --- a/trunk/src/app/srs_app_rtp.hpp +++ b/trunk/src/app/srs_app_rtp.hpp @@ -31,6 +31,7 @@ #include class SrsFormat; +class SrsSample; class SrsSharedPtrMessage; class SrsRequest; class SrsOriginHub; @@ -39,12 +40,17 @@ class SrsRtpMuxer { private: uint16_t sequence; - std::map packet_queue; + std::string sps; + std::string pps; public: SrsRtpMuxer(); virtual ~SrsRtpMuxer(); public: srs_error_t frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format); +private: + srs_error_t packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample); + srs_error_t packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample); + srs_error_t packet_stap_a(const std::string &sps, const std::string& pps, SrsSharedPtrMessage* shared_frame, std::vector& rtp_packet_vec); }; class SrsRtp From da72caf8b9e4b5a3fb5b944ad9f869bab37228f9 Mon Sep 17 00:00:00 2001 From: HuyaJohn Date: Wed, 11 Mar 2020 04:21:44 -0700 Subject: [PATCH 18/21] h264 packet done, chrome play well --- trunk/src/app/srs_app_rtc_conn.cpp | 129 ++++++++----- trunk/src/app/srs_app_rtp.cpp | 274 ++++------------------------ trunk/src/app/srs_app_rtp.hpp | 21 ++- trunk/src/app/srs_app_utility.cpp | 34 ++++ trunk/src/app/srs_app_utility.hpp | 4 + trunk/src/kernel/srs_kernel_flv.cpp | 4 +- 6 files changed, 169 insertions(+), 297 deletions(-) diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index af9fad322..a92a2b5f8 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -30,6 +30,8 @@ using namespace std; #include #include +#include +#include #include @@ -41,7 +43,9 @@ using namespace std; #include #include #include +#include #include +#include #include #include #include @@ -77,42 +81,6 @@ static string gen_random_str(int len) const int SRTP_MASTER_KEY_KEY_LEN = 16; const int SRTP_MASTER_KEY_SALT_LEN = 14; -static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len); -static string dump_string_hex(const std::string& str, const int& max_len = 128) -{ - return dump_string_hex(str.c_str(), str.size(), max_len); -} - -static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = 128) -{ - string ret; - ret.reserve(max_len * 4); - - char tmp_buf[1024*16]; - tmp_buf[0] = '\n'; - int len = 1; - - for (int i = 0; i < nb_buf && i < max_len; ++i) { - //int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "(%03d)%02X ", i, (uint8_t)buf[i]); - int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "%02X ", (uint8_t)buf[i]); - if (nb <= 0) - break; - - len += nb; - - if (i % 48 == 47) { - tmp_buf[len++] = '\n'; - ret.append(tmp_buf, len); - len = 0; - } - } - tmp_buf[len] = '\0'; - ret.append(tmp_buf, len); - - return ret; - -} - SrsCandidate::SrsCandidate() { } @@ -654,14 +622,10 @@ srs_error_t SrsRtcSenderThread::cycle() SrsSharedPtrMessage* msg = msgs.msgs[i]; for (int i = 0; i < msg->nb_rtp_fragments; ++i) { - srs_trace("rtp fragment size=%d, payload=%s", msg->rtp_fragments[i].size, - dump_string_hex(msg->rtp_fragments[i].bytes, msg->rtp_fragments[i].size, 1460).c_str()); - SrsBuffer stream(msg->rtp_fragments[i].bytes + 2, 2); - static uint16_t seq = 0; - stream.write_2bytes(++seq); - - srs_trace("seq=%u", seq); + uint16_t seq = stream.read_2bytes(); + srs_trace("rtp fragment size=%d, seq=%u, payload=%s", msg->rtp_fragments[i].size, seq, + dump_string_hex(msg->rtp_fragments[i].bytes, msg->rtp_fragments[i].size, 1460).c_str()); if (rtc_session->dtls_session) { char rtp_send_protected_buf[1500]; @@ -670,6 +634,8 @@ srs_error_t SrsRtcSenderThread::cycle() ukt.sendto(rtp_send_protected_buf, rtp_send_protected_len, 0); } } + + srs_freep(msg); } srs_usleep(16000); @@ -778,27 +744,40 @@ srs_error_t SrsRtcSession::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) return srs_error_wrap(err, "recv unexpect rtp/rtcp packet before dtls done"); } + uint8_t payload_type = udp_remux_socket->data()[1] & 0x7F; + char srtp_unprotect_buf[1460]; int nb_srtp_unprotect_buf = udp_remux_socket->size(); if (dtls_session->srtp_receiver_unprotect(srtp_unprotect_buf, udp_remux_socket->data(), nb_srtp_unprotect_buf) != srs_success) { - return srs_error_wrap(err, "srtp receiver unprotect failed"); + return srs_error_wrap(err, "srtp receiver unprotect failed, payload_type=%u", payload_type); } //srs_trace("srtp unprotect success, %s", dump_string_hex(srtp_unprotect_buf, nb_srtp_unprotect_buf, nb_srtp_unprotect_buf).c_str()); SrsBuffer* stream = new SrsBuffer(srtp_unprotect_buf, nb_srtp_unprotect_buf); + SrsAutoFree(SrsBuffer, stream); uint8_t first = stream->read_1bytes(); uint8_t second = stream->read_1bytes(); - bool marker = (second & 0x80) == 0x80; - uint8_t payload_type = second &0x7F; + + bool padding = (first & 0x20); + bool ext = (first & 0x10); + uint8_t cc = (first & 0x0F); + + bool marker = (second & 0x80); uint16_t sequence = stream->read_2bytes(); uint32_t timestamp = stream->read_4bytes(); uint32_t ssrc = stream->read_4bytes(); - srs_trace("sequence=%u, timestamp=%u, ssrc=%u, marker=%d, payload_type=%u", sequence, timestamp, ssrc, marker, payload_type); + srs_trace("sequence=%u, timestamp=%u, ssrc=%u, padding=%d, ext=%d, cc=%u, marker=%d, payload_type=%u", + sequence, timestamp, ssrc, padding, ext, cc, marker, payload_type); - if (first & 0x10) { + for (uint8_t i = 0; i < cc; ++i) { + uint32_t csrc = 0; + csrc = stream->read_4bytes(); + } + + if (ext) { uint16_t extern_profile = stream->read_2bytes(); uint16_t extern_length = stream->read_2bytes(); @@ -807,6 +786,58 @@ srs_error_t SrsRtcSession::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) stream->read_string(extern_length * 4); } + if (payload_type == 102) { + static uint32_t pre_seq = 0; + uint32_t seq = sequence; + + srs_assert(pre_seq == 0 || (pre_seq + 1 == seq)); + + pre_seq = seq; + + static uint8_t start_code[4] = {0x00, 0x00, 0x00, 0x01}; + static int fd = -1; + if (fd < 0) { + fd = open("rtc.264", O_CREAT|O_TRUNC|O_RDWR, 0664); + } + + const uint8_t* p = (const uint8_t*)stream->data() + stream->pos(); + int len = stream->left(); + uint8_t header = p[0]; + uint8_t nal_type = header & kNalTypeMask; + + srs_trace("nal_type=%u, seq=%u, rtp payload, %s", nal_type, sequence, dump_string_hex(stream->data() + stream->pos(), stream->left(), stream->left()).c_str()); + + if (nal_type >=1 && nal_type <= 23) { + srs_trace("single nalu"); + write(fd, start_code, sizeof(start_code)); + write(fd, p, len); + } else if (nal_type == kFuA) { + srs_trace("FuA"); + if (p[1] & 0x80) { + uint8_t nal_type = ((p[0] & (~kNalTypeMask)) | (p[1] & kNalTypeMask)); + write(fd, start_code, sizeof(start_code)); + write(fd, &nal_type, 1); + write(fd, p + 2, len - 2); + } else { + write(fd, p + 2, len - 2); + } + } else if (nal_type == kStapA) { + srs_trace("StapA"); + int pos = 1; + while (pos < len) { + int nal_len = p[pos] << 8 | p[pos + 1]; + srs_trace("nal_len=%d", nal_len); + write(fd, start_code, sizeof(start_code)); + write(fd, p + pos + 2, nal_len); + pos += nal_len + 2; + } + srs_assert(pos == len); + } else { + srs_assert(false); + } + } + + // XXX:send h264 back to client, for debug if (payload_type == 102) { char rtp_send_protected_buf[1500]; int rtp_send_protected_len = nb_srtp_unprotect_buf; @@ -816,8 +847,6 @@ srs_error_t SrsRtcSession::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) udp_remux_socket->sendto(rtp_send_protected_buf, rtp_send_protected_len, 0); } - srs_trace("rtp payload, %s", dump_string_hex(stream->data() + stream->pos(), stream->left(), stream->left()).c_str()); - return err; } diff --git a/trunk/src/app/srs_app_rtp.cpp b/trunk/src/app/srs_app_rtp.cpp index 3a00d3d34..dafdbd0b8 100644 --- a/trunk/src/app/srs_app_rtp.cpp +++ b/trunk/src/app/srs_app_rtp.cpp @@ -50,42 +50,6 @@ using namespace std; #include #include -static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len); -static string dump_string_hex(const std::string& str, const int& max_len = 128) -{ - return dump_string_hex(str.c_str(), str.size(), max_len); -} - -static string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = 128) -{ - string ret; - ret.reserve(max_len * 4); - - char tmp_buf[1024*16]; - tmp_buf[0] = '\n'; - int len = 1; - - for (int i = 0; i < nb_buf && i < max_len; ++i) { - //int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "(%03d)%02X ", i, (uint8_t)buf[i]); - int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "%02X ", (uint8_t)buf[i]); - if (nb <= 0) - break; - - len += nb; - - if (i % 48 == 47) { - tmp_buf[len++] = '\n'; - ret.append(tmp_buf, len); - len = 0; - } - } - tmp_buf[len] = '\0'; - ret.append(tmp_buf, len); - - return ret; -} - - SrsRtpMuxer::SrsRtpMuxer() { sequence = 0; @@ -95,182 +59,6 @@ SrsRtpMuxer::~SrsRtpMuxer() { } -#if 0 -srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsFormat* format) -{ - srs_error_t err = srs_success; - - int nb_samples = format->video->nb_samples; - SrsSample* samples = format->video->samples; - - SrsSample* rtp_fragment_samples = new SrsSample[2000]; - int rtp_fragment_samples_index = 0; - - static int debug_fd = -1; - static uint8_t start_code[4] = {0x00, 0x00, 0x00, 0x01}; - if (debug_fd < 0) { - debug_fd = open("./raw.264", O_CREAT|O_TRUNC|O_RDWR, 0664); - } - - SrsSample sps_pps_samples[2]; - if (format->is_avc_sequence_header()) { - sps_pps_samples[0].bytes = format->vcodec->sequenceParameterSetNALUnit.data(); - sps_pps_samples[0].size = format->vcodec->sequenceParameterSetNALUnit.size(); - sps_pps_samples[1].bytes = format->vcodec->pictureParameterSetNALUnit.data(); - sps_pps_samples[1].size = format->vcodec->pictureParameterSetNALUnit.size(); - - nb_samples = 2; - samples = sps_pps_samples; - - { - char* buf = new char[1460]; - SrsBuffer* stream = new SrsBuffer(buf, 1460); - SrsAutoFree(SrsBuffer, stream); - // write rtp header first - stream->write_1bytes(0x80); - stream->write_1bytes(102); - - stream->write_2bytes(sequence++); - stream->write_4bytes((int32_t)shared_frame->timestamp * 90); - stream->write_4bytes((int32_t)3233846889); - - stream->write_1bytes(24/*STAP-A*/); - // AUD - stream->write_2bytes(2); - stream->write_1bytes(0x09); - stream->write_1bytes(0x10); - - stream->write_2bytes(sps_pps_samples[0].size); - stream->write_bytes(sps_pps_samples[0].bytes, sps_pps_samples[0].size); - stream->write_2bytes(sps_pps_samples[1].size); - stream->write_bytes(sps_pps_samples[1].bytes, sps_pps_samples[1].size); - - if (debug_fd >= 0) { - write(debug_fd, start_code, sizeof(start_code)); - write(debug_fd, sps_pps_samples[0].bytes, sps_pps_samples[0].size); - write(debug_fd, start_code, sizeof(start_code)); - write(debug_fd, sps_pps_samples[1].bytes, sps_pps_samples[1].size); - } - - rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); - rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); - - ++rtp_fragment_samples_index; - } - shared_frame->set_rtp_fragments(rtp_fragment_samples, rtp_fragment_samples_index); - - return err; - } - - for (int i = 0; i < nb_samples; ++i) { - SrsSample sample = samples[i]; - - srs_trace("nal size=%d, dump=%s", sample.size, dump_string_hex(sample.bytes, sample.size, sample.size).c_str()); - - if ((sample.bytes[0] & 0x1F) == 0x06) { - srs_trace("ignore SEI"); - continue; - } - - if (debug_fd >= 0) { - write(debug_fd, start_code, sizeof(start_code)); - write(debug_fd, sample.bytes, sample.size); - } - - static int max_packet_size = 900; - if (sample.size <= max_packet_size) { - char* buf = new char[1460]; - SrsBuffer* stream = new SrsBuffer(buf, 1460); - SrsAutoFree(SrsBuffer, stream); - // write rtp header first - stream->write_1bytes(0x80); - if ((sample.bytes[0] & 0x1F) <= 5) { - stream->write_1bytes((1 << 7) | 102); - } else { - stream->write_1bytes(102); - } - - stream->write_2bytes(sequence++); - stream->write_4bytes((int32_t)shared_frame->timestamp * 90); - stream->write_4bytes((int32_t)3233846889); - -#if 0 // single nalu - stream->write_bytes(sample.bytes, sample.size); -#else - stream->write_1bytes((sample.bytes[0] & 0xE0) | 24/*STAP-A*/); - stream->write_2bytes(sample.size); - stream->write_bytes(sample.bytes, sample.size); -#endif - - rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); - rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); - - ++rtp_fragment_samples_index; - } else { - int num_of_packet = (sample.size + max_packet_size) / max_packet_size; - char* p = sample.bytes + 1; - int left_bytes = sample.size - 1; - for (int n = 0; n < num_of_packet; ++n) { - char* buf = new char[1460]; - SrsBuffer* stream = new SrsBuffer(buf, 1460); - SrsAutoFree(SrsBuffer, stream); - // write rtp header first - stream->write_1bytes(0x80); - if ((sample.bytes[0] & 0x1F) <= 5) { - stream->write_1bytes((1 << 7) | 102); - } else { - stream->write_1bytes(102); - } - - stream->write_2bytes(sequence++); - stream->write_4bytes((int32_t)shared_frame->timestamp * 90); - stream->write_4bytes((int32_t)3233846889); - - stream->write_1bytes((sample.bytes[0] & 0xE0) | 28); - if (n == 0) { - stream->write_1bytes(0x80 | (sample.bytes[0] & 0x1F)); - } else if (n == num_of_packet - 1) { - stream->write_1bytes(0x40 | (sample.bytes[0] & 0x1F)); - } else { - stream->write_1bytes(0x00 | (sample.bytes[0] & 0x1F)); - } - - int len = left_bytes > max_packet_size ? max_packet_size : left_bytes; - stream->write_bytes(p, len); - left_bytes -= len; - p += len; - - rtp_fragment_samples[rtp_fragment_samples_index].bytes = stream->data(); - rtp_fragment_samples[rtp_fragment_samples_index].size = stream->pos(); - - ++rtp_fragment_samples_index; - - } - } - } - shared_frame->set_rtp_fragments(rtp_fragment_samples, rtp_fragment_samples_index); - - return err; -} -#endif - -const int max_payload_size = 1200; -const int kRtpPacketSize = 1500; - -const uint8_t kMarker = 0x80; -const uint8_t kH264PayloadType = 102; - -const uint8_t kNalTypeMask = 0x1F; - -const uint8_t kIdr = 5; -const uint8_t kStapA = 24; -const uint8_t kFua = 28; - -const uint8_t kStart = 0x80; -const uint8_t kEnd = 0x40; - -const uint32_t kVideoSSRC = 3233846889; - srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsFormat* format) { srs_error_t err = srs_success; @@ -280,11 +68,13 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF pps.assign(format->vcodec->pictureParameterSetNALUnit.data(), format->vcodec->pictureParameterSetNALUnit.size()); } + vector rtp_packet_vec; + for (int i = 0; i < format->video->nb_samples; ++i) { SrsSample sample = format->video->samples[i]; uint8_t header = sample.bytes[0]; - uint8_t nal_type = header & 0x1F; + uint8_t nal_type = header & kNalTypeMask; if (nal_type == 0x06) { srs_trace("ignore SEI"); @@ -292,9 +82,9 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF } if (sample.size <= max_payload_size) { - packet_single_nalu(shared_frame, format, &sample); + packet_single_nalu(shared_frame, format, &sample, rtp_packet_vec); } else { - packet_fu_a(shared_frame, format, &sample); + packet_fu_a(shared_frame, format, &sample, rtp_packet_vec); } srs_trace("nal size=%d, nal=%s", sample.size, dump_string_hex(sample.bytes, sample.size, sample.size).c_str()); @@ -303,15 +93,20 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF } } + SrsSample* rtp_samples = new SrsSample[rtp_packet_vec.size()]; + for (int i = 0; i < rtp_packet_vec.size(); ++i) { + rtp_samples[i] = rtp_packet_vec[i]; + } + + shared_frame->set_rtp_fragments(rtp_samples, rtp_packet_vec.size()); + return err; } -srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample) +srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector& rtp_packet_vec) { srs_error_t err = srs_success; - vector rtp_packet_vec; - char* p = sample->bytes + 1; int nb_left = sample->size - 1; uint8_t header = sample->bytes[0]; @@ -325,7 +120,7 @@ srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsForma int avg_packet_size = sample->size / num_of_packet; for (int i = 0; i < num_of_packet; ++i) { char* buf = new char[kRtpPacketSize]; - SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsBuffer* stream = new SrsBuffer(buf, kRtpPacketSize); SrsAutoFree(SrsBuffer, stream); int packet_size = min(nb_left, max_payload_size); @@ -333,8 +128,13 @@ srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsForma // v=2,p=0,x=0,cc=0 stream->write_1bytes(0x80); // marker payloadtype - stream->write_1bytes(kMarker | kH264PayloadType); - // sequenct + if (i == num_of_packet - 1) { + stream->write_1bytes(kMarker | kH264PayloadType); + } else { + stream->write_1bytes(kH264PayloadType); + } + // sequence + srs_trace("sequence=%u", sequence); stream->write_2bytes(sequence++); // timestamp stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); @@ -342,11 +142,11 @@ srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsForma stream->write_4bytes(int32_t(kVideoSSRC)); // fu-indicate - uint8_t fu_indicate = kFua; - fu_indicate |= (nal_type & (~kNalTypeMask)); + uint8_t fu_indicate = kFuA; + fu_indicate |= (header & (~kNalTypeMask)); stream->write_1bytes(fu_indicate); - uint8_t fu_header = nal_type & kNalTypeMask; + uint8_t fu_header = nal_type; if (i == 0) fu_header |= kStart; if (i == num_of_packet - 1) @@ -355,7 +155,7 @@ srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsForma stream->write_bytes(p, packet_size); p += packet_size; - nb_left -= max_payload_size; + nb_left -= packet_size; SrsSample rtp_packet; @@ -364,26 +164,17 @@ srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsForma rtp_packet_vec.push_back(rtp_packet); } - - SrsSample* rtp_samples = new SrsSample[rtp_packet_vec.size()]; - for (int i = 0; i < rtp_packet_vec.size(); ++i) { - rtp_samples[i] = rtp_packet_vec[i]; - } - - shared_frame->set_rtp_fragments(rtp_samples, rtp_packet_vec.size()); } -srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample) +srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector& rtp_packet_vec) { srs_error_t err = srs_success; - vector rtp_packet_vec; - uint8_t header = sample->bytes[0]; uint8_t nal_type = header & kNalTypeMask; char* buf = new char[kRtpPacketSize]; - SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsBuffer* stream = new SrsBuffer(buf, kRtpPacketSize); SrsAutoFree(SrsBuffer, stream); if (nal_type == kIdr) { @@ -395,6 +186,7 @@ srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, S // marker payloadtype stream->write_1bytes(kMarker | kH264PayloadType); // sequenct + srs_trace("sequence=%u", sequence); stream->write_2bytes(sequence++); // timestamp stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); @@ -407,12 +199,7 @@ srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, S rtp_packet.bytes = stream->data(); rtp_packet.size = stream->pos(); - SrsSample* rtp_samples = new SrsSample[rtp_packet_vec.size()]; - for (int i = 0; i < rtp_packet_vec.size(); ++i) { - rtp_samples[i] = rtp_packet_vec[i]; - } - - shared_frame->set_rtp_fragments(rtp_samples, rtp_packet_vec.size()); + rtp_packet_vec.push_back(rtp_packet); return err; } @@ -425,7 +212,7 @@ srs_error_t SrsRtpMuxer::packet_stap_a(const string &sps, const string& pps, Srs uint8_t nal_type = header & kNalTypeMask; char* buf = new char[kRtpPacketSize]; - SrsBuffer* stream = new SrsBuffer(buf, 1460); + SrsBuffer* stream = new SrsBuffer(buf, kRtpPacketSize); SrsAutoFree(SrsBuffer, stream); // v=2,p=0,x=0,cc=0 @@ -433,6 +220,7 @@ srs_error_t SrsRtpMuxer::packet_stap_a(const string &sps, const string& pps, Srs // marker payloadtype stream->write_1bytes(kMarker | kH264PayloadType); // sequenct + srs_trace("sequence=%u", sequence); stream->write_2bytes(sequence++); // timestamp stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); diff --git a/trunk/src/app/srs_app_rtp.hpp b/trunk/src/app/srs_app_rtp.hpp index 1d0f510dd..9000fd830 100644 --- a/trunk/src/app/srs_app_rtp.hpp +++ b/trunk/src/app/srs_app_rtp.hpp @@ -36,6 +36,23 @@ class SrsSharedPtrMessage; class SrsRequest; class SrsOriginHub; +const int max_payload_size = 1200; +const int kRtpPacketSize = 1500; + +const uint8_t kMarker = 0x80; +const uint8_t kH264PayloadType = 102; + +const uint8_t kNalTypeMask = 0x1F; + +const uint8_t kIdr = 5; +const uint8_t kStapA = 24; +const uint8_t kFuA = 28; + +const uint8_t kStart = 0x80; +const uint8_t kEnd = 0x40; + +const uint32_t kVideoSSRC = 3233846889; + class SrsRtpMuxer { private: @@ -48,8 +65,8 @@ public: public: srs_error_t frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format); private: - srs_error_t packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample); - srs_error_t packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample); + srs_error_t packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector& rtp_packet_vec); + srs_error_t packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector& rtp_packet_vec); srs_error_t packet_stap_a(const std::string &sps, const std::string& pps, SrsSharedPtrMessage* shared_frame, std::vector& rtp_packet_vec); }; diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index 04ece7184..66ae11269 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -1193,3 +1193,37 @@ void srs_api_dump_summaries(SrsJsonObject* obj) sys->set("conn_srs", SrsJsonAny::integer(nrs->nb_conn_srs)); } +string dump_string_hex(const std::string& str, const int& max_len) +{ + return dump_string_hex(str.c_str(), str.size(), max_len); +} + +string dump_string_hex(const char* buf, const int nb_buf, const int& max_len) +{ + string ret; + ret.reserve(max_len * 4); + + char tmp_buf[1024*16]; + tmp_buf[0] = '\n'; + int len = 1; + + for (int i = 0; i < nb_buf && i < max_len; ++i) { + //int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "(%03d)%02X ", i, (uint8_t)buf[i]); + int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "%02X ", (uint8_t)buf[i]); + if (nb <= 0) + break; + + len += nb; + + if (i % 48 == 47) { + tmp_buf[len++] = '\n'; + ret.append(tmp_buf, len); + len = 0; + } + } + tmp_buf[len] = '\0'; + ret.append(tmp_buf, len); + + return ret; + +} diff --git a/trunk/src/app/srs_app_utility.hpp b/trunk/src/app/srs_app_utility.hpp index be7cbced6..77d28f802 100644 --- a/trunk/src/app/srs_app_utility.hpp +++ b/trunk/src/app/srs_app_utility.hpp @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -649,5 +650,8 @@ extern bool srs_is_boolean(std::string str); // Dump summaries for /api/v1/summaries. extern void srs_api_dump_summaries(SrsJsonObject* obj); +extern std::string dump_string_hex(const std::string& str, const int& max_len = INT_MAX); +extern std::string dump_string_hex(const char* buf, const int nb_buf, const int& max_len = INT_MAX); + #endif diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index aa0d3a7ec..f2f773049 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -216,10 +216,10 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload() srs_freepa(payload); for (int i = 0; i < nb_rtp_fragments; ++i) { - srs_freep(rtp_fragments[i].bytes); + srs_freepa(rtp_fragments[i].bytes); } - if (nb_rtp_fragments) { + if (rtp_fragments != NULL && nb_rtp_fragments > 0) { srs_freepa(rtp_fragments); } } From 6decdc7838e9f9a941e93611c206c55759fd3277 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Fri, 13 Mar 2020 00:24:56 +0800 Subject: [PATCH 19/21] adjust code style, fix some bug, add rtc session timeout --- trunk/src/app/srs_app_dtls.cpp | 6 + trunk/src/app/srs_app_listener.cpp | 97 +++++- trunk/src/app/srs_app_listener.hpp | 30 +- trunk/src/app/srs_app_rtc_conn.cpp | 484 +++++++++++++++++---------- trunk/src/app/srs_app_rtc_conn.hpp | 111 ++++-- trunk/src/app/srs_app_rtp.cpp | 5 +- trunk/src/app/srs_app_server.cpp | 6 +- trunk/src/app/srs_app_server.hpp | 4 +- trunk/src/service/srs_service_st.cpp | 5 + trunk/src/service/srs_service_st.hpp | 1 + 10 files changed, 503 insertions(+), 246 deletions(-) diff --git a/trunk/src/app/srs_app_dtls.cpp b/trunk/src/app/srs_app_dtls.cpp index af09dcb2f..4a863fd92 100644 --- a/trunk/src/app/srs_app_dtls.cpp +++ b/trunk/src/app/srs_app_dtls.cpp @@ -29,6 +29,8 @@ using namespace std; #include +#include + SrsDtls* SrsDtls::_instance = NULL; SrsDtls::SrsDtls() @@ -50,6 +52,10 @@ SrsDtls* SrsDtls::instance() void SrsDtls::init() { + // srtp init first + srs_assert(srtp_init() == 0); + + // init dtls context EVP_PKEY* dtls_private_key = EVP_PKEY_new(); srs_assert(dtls_private_key); diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index c8ecec2c6..0ba7684cd 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -60,15 +60,15 @@ srs_error_t ISrsUdpHandler::on_stfd_change(srs_netfd_t /*fd*/) return srs_success; } -ISrsUdpRemuxHandler::ISrsUdpRemuxHandler() +ISrsUdpMuxHandler::ISrsUdpMuxHandler() { } -ISrsUdpRemuxHandler::~ISrsUdpRemuxHandler() +ISrsUdpMuxHandler::~ISrsUdpMuxHandler() { } -srs_error_t ISrsUdpRemuxHandler::on_stfd_change(srs_netfd_t /*fd*/) +srs_error_t ISrsUdpMuxHandler::on_stfd_change(srs_netfd_t /*fd*/) { return srs_success; } @@ -221,7 +221,7 @@ srs_error_t SrsTcpListener::cycle() return err; } -SrsUdpRemuxSocket::SrsUdpRemuxSocket(srs_netfd_t fd) +SrsUdpMuxSocket::SrsUdpMuxSocket(srs_netfd_t fd) { nb_buf = SRS_UDP_MAX_PACKET_SIZE; buf = new char[nb_buf]; @@ -232,12 +232,31 @@ SrsUdpRemuxSocket::SrsUdpRemuxSocket(srs_netfd_t fd) fromlen = 0; } -SrsUdpRemuxSocket::~SrsUdpRemuxSocket() +SrsUdpMuxSocket::~SrsUdpMuxSocket() { srs_freepa(buf); } -int SrsUdpRemuxSocket::recvfrom(srs_utime_t timeout) +SrsUdpMuxSocket::SrsUdpMuxSocket(const SrsUdpMuxSocket& rhs) +{ + operator=(rhs); +} + +SrsUdpMuxSocket& SrsUdpMuxSocket::operator=(const SrsUdpMuxSocket& rhs) +{ + buf = NULL; + nb_buf = 0; + nread = 0; + lfd = rhs.lfd; + from = rhs.from; + fromlen = rhs.fromlen; + peer_ip = rhs.peer_ip; + peer_port = rhs.peer_port; + + return *this; +} + +int SrsUdpMuxSocket::recvfrom(srs_utime_t timeout) { fromlen = sizeof(from); nread = srs_recvfrom(lfd, buf, nb_buf, (sockaddr*)&from, &fromlen, timeout); @@ -259,12 +278,23 @@ int SrsUdpRemuxSocket::recvfrom(srs_utime_t timeout) return nread; } -int SrsUdpRemuxSocket::sendto(void* data, int size, srs_utime_t timeout) +int SrsUdpMuxSocket::sendto(void* data, int size, srs_utime_t timeout) { return srs_sendto(lfd, data, size, (sockaddr*)&from, fromlen, timeout); } -std::string SrsUdpRemuxSocket::get_peer_id() +int SrsUdpMuxSocket::sendtov(struct iovec* iov, size_t iovlen, srs_utime_t timeout) +{ + struct msghdr udphdr = {0}; + udphdr.msg_name = &from; + udphdr.msg_namelen = fromlen; + udphdr.msg_iov = iov; + udphdr.msg_iovlen = iovlen; + + return srs_sendmsg(lfd, &udphdr, 0, timeout); +} + +std::string SrsUdpMuxSocket::get_peer_id() { char id_buf[1024]; int len = snprintf(id_buf, sizeof(id_buf), "%s:%d", peer_ip.c_str(), peer_port); @@ -272,7 +302,7 @@ std::string SrsUdpRemuxSocket::get_peer_id() return string(id_buf, len); } -SrsUdpRemuxListener::SrsUdpRemuxListener(ISrsUdpRemuxHandler* h, std::string i, int p) +SrsUdpMuxListener::SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p) { handler = h; ip = i; @@ -285,30 +315,32 @@ SrsUdpRemuxListener::SrsUdpRemuxListener(ISrsUdpRemuxHandler* h, std::string i, trd = new SrsDummyCoroutine(); } -SrsUdpRemuxListener::~SrsUdpRemuxListener() +SrsUdpMuxListener::~SrsUdpMuxListener() { srs_freep(trd); srs_close_stfd(lfd); srs_freepa(buf); } -int SrsUdpRemuxListener::fd() +int SrsUdpMuxListener::fd() { return srs_netfd_fileno(lfd); } -srs_netfd_t SrsUdpRemuxListener::stfd() +srs_netfd_t SrsUdpMuxListener::stfd() { return lfd; } -srs_error_t SrsUdpRemuxListener::listen() +srs_error_t SrsUdpMuxListener::listen() { srs_error_t err = srs_success; if ((err = srs_udp_listen(ip, port, &lfd)) != srs_success) { return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); } + + set_socket_buffer(); srs_freep(trd); trd = new SrsSTCoroutine("udp", this); @@ -319,7 +351,38 @@ srs_error_t SrsUdpRemuxListener::listen() return err; } -srs_error_t SrsUdpRemuxListener::cycle() +void SrsUdpMuxListener::set_socket_buffer() +{ + int sndbuf_size = 0; + socklen_t opt_len = sizeof(sndbuf_size); + getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&sndbuf_size, &opt_len); + srs_trace("default udp remux socket sndbuf=%d", sndbuf_size); + + sndbuf_size = 1024*1024*10; // 10M + if (setsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&sndbuf_size, sizeof(sndbuf_size)) < 0) { + srs_warn("set sock opt SO_SNDBUFFORCE failed"); + } + + opt_len = sizeof(sndbuf_size); + getsockopt(fd(), SOL_SOCKET, SO_SNDBUF, (void*)&sndbuf_size, &opt_len); + srs_trace("udp remux socket sndbuf=%d", sndbuf_size); + + int rcvbuf_size = 0; + opt_len = sizeof(rcvbuf_size); + getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&rcvbuf_size, &opt_len); + srs_trace("default udp remux socket rcvbuf=%d", rcvbuf_size); + + rcvbuf_size = 1024*1024*10; // 10M + if (setsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&rcvbuf_size, sizeof(rcvbuf_size)) < 0) { + srs_warn("set sock opt SO_RCVBUFFORCE failed"); + } + + opt_len = sizeof(rcvbuf_size); + getsockopt(fd(), SOL_SOCKET, SO_RCVBUF, (void*)&rcvbuf_size, &opt_len); + srs_trace("udp remux socket rcvbuf=%d", rcvbuf_size); +} + +srs_error_t SrsUdpMuxListener::cycle() { srs_error_t err = srs_success; @@ -328,15 +391,15 @@ srs_error_t SrsUdpRemuxListener::cycle() return srs_error_wrap(err, "udp listener"); } - SrsUdpRemuxSocket udp_remux_socket(lfd); + SrsUdpMuxSocket udp_mux_skt(lfd); - if (udp_remux_socket.recvfrom(SRS_UTIME_NO_TIMEOUT) <= 0) { + if (udp_mux_skt.recvfrom(SRS_UTIME_NO_TIMEOUT) <= 0) { srs_error("udp recv error"); // remux udp never return continue; } - if ((err = handler->on_udp_packet(&udp_remux_socket)) != srs_success) { + if ((err = handler->on_udp_packet(&udp_mux_skt)) != srs_success) { // remux udp never return srs_error("udp packet handler error:%s", srs_error_desc(err).c_str()); continue; diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index fc25551e8..605929902 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -35,7 +35,7 @@ struct sockaddr; -class SrsUdpRemuxSocket; +class SrsUdpMuxSocket; // The udp packet handler. class ISrsUdpHandler @@ -58,14 +58,14 @@ public: virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) = 0; }; -class ISrsUdpRemuxHandler +class ISrsUdpMuxHandler { public: - ISrsUdpRemuxHandler(); - virtual ~ISrsUdpRemuxHandler(); + ISrsUdpMuxHandler(); + virtual ~ISrsUdpMuxHandler(); public: virtual srs_error_t on_stfd_change(srs_netfd_t fd); - virtual srs_error_t on_udp_packet(SrsUdpRemuxSocket* udp_remux_socket) = 0; + virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* udp_mux_skt) = 0; }; // The tcp connection handler. @@ -127,7 +127,7 @@ public: virtual srs_error_t cycle(); }; -class SrsUdpRemuxSocket +class SrsUdpMuxSocket { private: char* buf; @@ -139,11 +139,15 @@ private: std::string peer_ip; int peer_port; public: - SrsUdpRemuxSocket(srs_netfd_t fd); - virtual ~SrsUdpRemuxSocket(); + SrsUdpMuxSocket(srs_netfd_t fd); + virtual ~SrsUdpMuxSocket(); + + SrsUdpMuxSocket(const SrsUdpMuxSocket& rhs); + SrsUdpMuxSocket& operator=(const SrsUdpMuxSocket& rhs); int recvfrom(srs_utime_t timeout); int sendto(void* data, int size, srs_utime_t timeout); + int sendtov(struct iovec* iov, size_t iovlen, srs_utime_t timeout); char* data() { return buf; } int size() { return nread; } @@ -152,7 +156,7 @@ public: std::string get_peer_id(); }; -class SrsUdpRemuxListener : public ISrsCoroutineHandler +class SrsUdpMuxListener : public ISrsCoroutineHandler { protected: srs_netfd_t lfd; @@ -161,12 +165,12 @@ protected: char* buf; int nb_buf; protected: - ISrsUdpRemuxHandler* handler; + ISrsUdpMuxHandler* handler; std::string ip; int port; public: - SrsUdpRemuxListener(ISrsUdpRemuxHandler* h, std::string i, int p); - virtual ~SrsUdpRemuxListener(); + SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p); + virtual ~SrsUdpMuxListener(); public: virtual int fd(); virtual srs_netfd_t stfd(); @@ -175,6 +179,8 @@ public: // Interface ISrsReusableThreadHandler. public: virtual srs_error_t cycle(); +private: + void set_socket_buffer(); }; #endif diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index a92a2b5f8..07d0fc900 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -65,6 +65,11 @@ static bool is_rtp_or_rtcp(const char* data, size_t len) return (len >= 12 && (data[0] & 0xC0) == 0x80); } +static bool is_rtcp(const char* data, size_t len) +{ + return (len >=12) && (data[0] & 0x80) && (data[1] >= 200 && data[1] <= 209); +} + static string gen_random_str(int len) { static string random_table = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -294,9 +299,22 @@ SrsDtlsSession::SrsDtlsSession(SrsRtcSession* s) SrsDtlsSession::~SrsDtlsSession() { + if (dtls) { + // this function will free bio_in and bio_out + SSL_free(dtls); + dtls = NULL; + } + + if (srtp_send) { + srtp_dealloc(srtp_send); + } + + if (srtp_recv) { + srtp_dealloc(srtp_recv); + } } -srs_error_t SrsDtlsSession::handshake(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsDtlsSession::handshake(SrsUdpMuxSocket* udp_mux_skt) { srs_error_t err = srs_success; @@ -308,7 +326,7 @@ srs_error_t SrsDtlsSession::handshake(SrsUdpRemuxSocket* udp_remux_socket) int ssl_err = SSL_get_error(dtls, ret); switch(ssl_err) { case SSL_ERROR_NONE: { - err = on_dtls_handshake_done(udp_remux_socket); + err = on_dtls_handshake_done(udp_mux_skt); } break; @@ -327,25 +345,25 @@ srs_error_t SrsDtlsSession::handshake(SrsUdpRemuxSocket* udp_remux_socket) if (out_bio_len) { srs_trace("send dtls handshake data"); - udp_remux_socket->sendto(out_bio_data, out_bio_len, 0); + udp_mux_skt->sendto(out_bio_data, out_bio_len, 0); } return err; } -srs_error_t SrsDtlsSession::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsDtlsSession::on_dtls(SrsUdpMuxSocket* udp_mux_skt) { srs_error_t err = srs_success; if (! handshake_done) { BIO_reset(bio_in); BIO_reset(bio_out); - BIO_write(bio_in, udp_remux_socket->data(), udp_remux_socket->size()); + BIO_write(bio_in, udp_mux_skt->data(), udp_mux_skt->size()); - handshake(udp_remux_socket); + handshake(udp_mux_skt); } else { BIO_reset(bio_in); BIO_reset(bio_out); - BIO_write(bio_in, udp_remux_socket->data(), udp_remux_socket->size()); + BIO_write(bio_in, udp_mux_skt->data(), udp_mux_skt->size()); while (BIO_ctrl_pending(bio_in) > 0) { char dtls_read_buf[8092]; @@ -360,7 +378,7 @@ srs_error_t SrsDtlsSession::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) return err; } -srs_error_t SrsDtlsSession::on_dtls_handshake_done(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsDtlsSession::on_dtls_handshake_done(SrsUdpMuxSocket* udp_mux_skt) { srs_error_t err = srs_success; srs_trace("dtls handshake done"); @@ -371,7 +389,7 @@ srs_error_t SrsDtlsSession::on_dtls_handshake_done(SrsUdpRemuxSocket* udp_remux_ return srs_error_wrap(err, "srtp init failed"); } - rtc_session->on_connection_established(udp_remux_socket); + rtc_session->on_connection_established(udp_mux_skt); return err; } @@ -383,7 +401,7 @@ srs_error_t SrsDtlsSession::on_dtls_application_data(const char* buf, const int return err; } -void SrsDtlsSession::send_client_hello(SrsUdpRemuxSocket* udp_remux_socket) +void SrsDtlsSession::send_client_hello(SrsUdpMuxSocket* udp_mux_skt) { if (dtls == NULL) { srs_trace("send client hello"); @@ -396,7 +414,7 @@ void SrsDtlsSession::send_client_hello(SrsUdpRemuxSocket* udp_remux_socket) SSL_set_bio(dtls, bio_in, bio_out); - handshake(udp_remux_socket); + handshake(udp_mux_skt); } } @@ -405,42 +423,36 @@ srs_error_t SrsDtlsSession::srtp_initialize() srs_error_t err = srs_success; unsigned char material[SRTP_MASTER_KEY_LEN * 2] = {0}; // client(SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN) + server - static string dtls_srtp_lable = "EXTRACTOR-dtls_srtp"; + static const string dtls_srtp_lable = "EXTRACTOR-dtls_srtp"; if (! SSL_export_keying_material(dtls, material, sizeof(material), dtls_srtp_lable.c_str(), dtls_srtp_lable.size(), NULL, 0, 0)) { return srs_error_wrap(err, "SSL_export_keying_material failed"); } size_t offset = 0; - std::string sClientMasterKey(reinterpret_cast(material), SRTP_MASTER_KEY_KEY_LEN); + std::string client_master_key(reinterpret_cast(material), SRTP_MASTER_KEY_KEY_LEN); offset += SRTP_MASTER_KEY_KEY_LEN; - std::string sServerMasterKey(reinterpret_cast(material + offset), SRTP_MASTER_KEY_KEY_LEN); + std::string server_master_key(reinterpret_cast(material + offset), SRTP_MASTER_KEY_KEY_LEN); offset += SRTP_MASTER_KEY_KEY_LEN; - std::string sClientMasterSalt(reinterpret_cast(material + offset), SRTP_MASTER_KEY_SALT_LEN); + std::string client_master_salt(reinterpret_cast(material + offset), SRTP_MASTER_KEY_SALT_LEN); offset += SRTP_MASTER_KEY_SALT_LEN; - std::string sServerMasterSalt(reinterpret_cast(material + offset), SRTP_MASTER_KEY_SALT_LEN); + std::string server_master_salt(reinterpret_cast(material + offset), SRTP_MASTER_KEY_SALT_LEN); - client_key = sClientMasterKey + sClientMasterSalt; - server_key = sServerMasterKey + sServerMasterSalt; + client_key = client_master_key + client_master_salt; + server_key = server_master_key + server_master_salt; - srs_trace("client_key size=%d, server_key=%d", client_key.size(), server_key.size()); - - if (srtp_init() != 0) { - return srs_error_wrap(err, "srtp init failed"); - } - - if (srtp_sender_side_init() != srs_success) { - return srs_error_wrap(err, "srtp sender size init failed"); + if (srtp_send_init() != srs_success) { + return srs_error_wrap(err, "srtp send init failed"); } - if (srtp_receiver_side_init() != srs_success) { - return srs_error_wrap(err, "srtp receiver size init failed"); + if (srtp_recv_init() != srs_success) { + return srs_error_wrap(err, "srtp recv init failed"); } return err; } -srs_error_t SrsDtlsSession::srtp_sender_side_init() +srs_error_t SrsDtlsSession::srtp_send_init() { srs_error_t err = srs_success; @@ -463,16 +475,16 @@ srs_error_t SrsDtlsSession::srtp_sender_side_init() policy.key = key; if (srtp_create(&srtp_send, &policy) != 0) { - delete [] key; + srs_freepa(key); return srs_error_wrap(err, "srtp_create failed"); } - delete [] key; + srs_freepa(key); return err; } -srs_error_t SrsDtlsSession::srtp_receiver_side_init() +srs_error_t SrsDtlsSession::srtp_recv_init() { srs_error_t err = srs_success; @@ -495,50 +507,80 @@ srs_error_t SrsDtlsSession::srtp_receiver_side_init() policy.key = key; if (srtp_create(&srtp_recv, &policy) != 0) { - delete [] key; + srs_freepa(key); return srs_error_wrap(err, "srtp_create failed"); } - delete [] key; + srs_freepa(key); return err; } -srs_error_t SrsDtlsSession::srtp_sender_protect(char* protected_buf, const char* ori_buf, int& nb_protected_buf) +srs_error_t SrsDtlsSession::protect_rtp(char* out_buf, const char* in_buf, int& nb_out_buf) { srs_error_t err = srs_success; if (srtp_send) { - memcpy(protected_buf, ori_buf, nb_protected_buf); - if (srtp_protect(srtp_send, protected_buf, &nb_protected_buf) != 0) { - srs_error("srtp sender protect failed"); - return srs_error_wrap(err, "srtp sender protect failed"); + memcpy(out_buf, in_buf, nb_out_buf); + if (srtp_protect(srtp_send, out_buf, &nb_out_buf) != 0) { + return srs_error_wrap(err, "rtp protect failed"); } return err; } - return srs_error_wrap(err, "srtp sender protect failed"); + return srs_error_wrap(err, "rtp protect failed"); } -srs_error_t SrsDtlsSession::srtp_receiver_unprotect(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf) +srs_error_t SrsDtlsSession::unprotect_rtp(char* out_buf, const char* in_buf, int& nb_out_buf) +{ + srs_error_t err = srs_success; + + if (srtp_recv) { + memcpy(out_buf, in_buf, nb_out_buf); + if (srtp_unprotect(srtp_recv, out_buf, &nb_out_buf) != 0) { + return srs_error_wrap(err, "rtp unprotect failed"); + } + + return err; + } + + return srs_error_wrap(err, "rtp unprotect failed"); +} + +srs_error_t SrsDtlsSession::protect_rtcp(char* out_buf, const char* in_buf, int& nb_out_buf) { srs_error_t err = srs_success; if (srtp_send) { - memcpy(unprotected_buf, ori_buf, nb_unprotected_buf); - if (srtp_unprotect(srtp_recv, unprotected_buf, &nb_unprotected_buf) != 0) { - srs_error("srtp receiver unprotect failed"); - return srs_error_wrap(err, "srtp receiver unprotect failed"); + memcpy(out_buf, in_buf, nb_out_buf); + if (srtp_protect_rtcp(srtp_send, out_buf, &nb_out_buf) != 0) { + return srs_error_wrap(err, "rtcp protect failed"); + } + + return err; + } + + return srs_error_wrap(err, "rtcp protect failed"); +} + +srs_error_t SrsDtlsSession::unprotect_rtcp(char* out_buf, const char* in_buf, int& nb_out_buf) +{ + srs_error_t err = srs_success; + + if (srtp_recv) { + memcpy(out_buf, in_buf, nb_out_buf); + if (srtp_unprotect_rtcp(srtp_recv, out_buf, &nb_out_buf) != 0) { + return srs_error_wrap(err, "rtcp unprotect failed"); } return err; } - return srs_error_wrap(err, "srtp receiver unprotect failed"); + return srs_error_wrap(err, "rtcp unprotect failed"); } -SrsRtcSenderThread::SrsRtcSenderThread(SrsRtcSession* s, SrsUdpRemuxSocket* u, int parent_cid) +SrsRtcSenderThread::SrsRtcSenderThread(SrsRtcSession* s, SrsUdpMuxSocket* u, int parent_cid) : ukt(NULL) { _parent_cid = parent_cid; @@ -563,10 +605,10 @@ srs_error_t SrsRtcSenderThread::start() srs_error_t err = srs_success; srs_freep(trd); - trd = new SrsSTCoroutine("recv", this, _parent_cid); + trd = new SrsSTCoroutine("rtc_sender", this, _parent_cid); if ((err = trd->start()) != srs_success) { - return srs_error_wrap(err, "recv thread"); + return srs_error_wrap(err, "rtc_sender"); } return err; @@ -608,69 +650,91 @@ srs_error_t SrsRtcSenderThread::cycle() SrsAutoFree(SrsConsumer, consumer); while (true) { + if ((err = trd->pull()) != srs_success) { + return srs_error_wrap(err, "rtc sender thread"); + } + SrsMessageArray msgs(SRS_PERF_MW_MSGS); +#ifdef SRS_PERF_QUEUE_COND_WAIT + consumer->wait(0, SRS_PERF_MW_SLEEP); +#endif + int msg_count = 0; if (consumer->dump_packets(&msgs, msg_count) != srs_success) { - srs_trace("rtc pop no rtp packets"); continue; } - srs_trace("rtc pop %d rtp packets", msg_count); + if (msg_count <= 0) { +#ifndef SRS_PERF_QUEUE_COND_WAIT + srs_usleep(mw_sleep); +#endif + // ignore when nothing got. + continue; + } - for (int i = 0; i < msg_count; i++) { - SrsSharedPtrMessage* msg = msgs.msgs[i]; + send_and_free_messages(msgs.msgs, msg_count, &ukt); + } +} - for (int i = 0; i < msg->nb_rtp_fragments; ++i) { - SrsBuffer stream(msg->rtp_fragments[i].bytes + 2, 2); - uint16_t seq = stream.read_2bytes(); - srs_trace("rtp fragment size=%d, seq=%u, payload=%s", msg->rtp_fragments[i].size, seq, - dump_string_hex(msg->rtp_fragments[i].bytes, msg->rtp_fragments[i].size, 1460).c_str()); +void SrsRtcSenderThread::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, SrsUdpMuxSocket* udp_mux_skt) +{ + for (int i = 0; i < nb_msgs; i++) { + SrsSharedPtrMessage* msg = msgs[i]; - if (rtc_session->dtls_session) { - char rtp_send_protected_buf[1500]; - int rtp_send_protected_len = msg->rtp_fragments[i].size; - rtc_session->dtls_session->srtp_sender_protect(rtp_send_protected_buf, msg->rtp_fragments[i].bytes, rtp_send_protected_len); - ukt.sendto(rtp_send_protected_buf, rtp_send_protected_len, 0); - } - } + for (int i = 0; i < msg->nb_rtp_fragments; ++i) { + if (rtc_session->dtls_session) { + char protected_buf[kRtpPacketSize]; + int nb_protected_buf = msg->rtp_fragments[i].size; - srs_freep(msg); + rtc_session->dtls_session->protect_rtp(protected_buf, msg->rtp_fragments[i].bytes, nb_protected_buf); + udp_mux_skt->sendto(protected_buf, nb_protected_buf, 0); + } } - srs_usleep(16000); + srs_freep(msg); } } - -SrsRtcSession::SrsRtcSession(SrsServer* svr, SrsRtcServer* rtc_svr) +SrsRtcSession::SrsRtcSession(SrsServer* svr, SrsRtcServer* rtc_svr, const string& un) { server = svr; rtc_server = rtc_svr; session_state = INIT; dtls_session = NULL; - strd = NULL; + + username = un; + + last_stun_time = srs_get_system_time(); } SrsRtcSession::~SrsRtcSession() { + srs_freep(dtls_session); + + if (strd) { + strd->stop(); + } + srs_freep(strd); } -srs_error_t SrsRtcSession::on_stun(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req) +srs_error_t SrsRtcSession::on_stun(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* stun_req) { srs_error_t err = srs_success; if (stun_req->is_binding_request()) { - if (on_binding_request(udp_remux_socket, stun_req) != srs_success) { + if (on_binding_request(udp_mux_skt, stun_req) != srs_success) { return srs_error_wrap(err, "stun binding request failed"); } } + last_stun_time = srs_get_system_time(); + return err; } -srs_error_t SrsRtcSession::on_binding_request(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req) +srs_error_t SrsRtcSession::on_binding_request(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* stun_req) { srs_error_t err = srs_success; @@ -684,77 +748,74 @@ srs_error_t SrsRtcSession::on_binding_request(SrsUdpRemuxSocket* udp_remux_socke stun_binding_response.set_remote_ufrag(stun_req->get_local_ufrag()); stun_binding_response.set_transcation_id(stun_req->get_transcation_id()); // FIXME: inet_addr is deprecated, IPV6 support - stun_binding_response.set_mapped_address(be32toh(inet_addr(udp_remux_socket->get_peer_ip().c_str()))); - stun_binding_response.set_mapped_port(udp_remux_socket->get_peer_port()); + stun_binding_response.set_mapped_address(be32toh(inet_addr(udp_mux_skt->get_peer_ip().c_str()))); + stun_binding_response.set_mapped_port(udp_mux_skt->get_peer_port()); if (stun_binding_response.encode(get_local_sdp()->get_ice_pwd(), stream) != srs_success) { return srs_error_wrap(err, "stun binding response encode failed"); } - if (udp_remux_socket->sendto(stream->data(), stream->pos(), 0) <= 0) { + if (udp_mux_skt->sendto(stream->data(), stream->pos(), 0) <= 0) { return srs_error_wrap(err, "stun binding response send failed"); } if (get_session_state() == WAITING_STUN) { set_session_state(DOING_DTLS_HANDSHAKE); - send_client_hello(udp_remux_socket); + send_client_hello(udp_mux_skt); - string peer_id = udp_remux_socket->get_peer_id(); + peer_id = udp_mux_skt->get_peer_id(); rtc_server->insert_into_id_sessions(peer_id, this); } - // TODO: dtls send client retry - return err; } -srs_error_t SrsRtcSession::send_client_hello(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsRtcSession::send_client_hello(SrsUdpMuxSocket* udp_mux_skt) { if (dtls_session == NULL) { dtls_session = new SrsDtlsSession(this); } - dtls_session->send_client_hello(udp_remux_socket); + dtls_session->send_client_hello(udp_mux_skt); } -void SrsRtcSession::on_connection_established(SrsUdpRemuxSocket* udp_remux_socket) +void SrsRtcSession::on_connection_established(SrsUdpMuxSocket* udp_mux_skt) { - start_play(udp_remux_socket); + srs_trace("rtc session=%s, connection established", id().c_str()); + start_play(udp_mux_skt); } -srs_error_t SrsRtcSession::start_play(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsRtcSession::start_play(SrsUdpMuxSocket* udp_mux_skt) { srs_error_t err = srs_success; - strd = new SrsRtcSenderThread(this, udp_remux_socket, _srs_context->get_id()); + srs_freep(strd); + strd = new SrsRtcSenderThread(this, udp_mux_skt, _srs_context->get_id()); strd->start(); return err; } -srs_error_t SrsRtcSession::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsRtcSession::on_dtls(SrsUdpMuxSocket* udp_mux_skt) { - return dtls_session->on_dtls(udp_remux_socket); + return dtls_session->on_dtls(udp_mux_skt); } -srs_error_t SrsRtcSession::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsRtcSession::on_rtp(SrsUdpMuxSocket* udp_mux_skt) { srs_error_t err = srs_success; if (dtls_session == NULL) { - return srs_error_wrap(err, "recv unexpect rtp/rtcp packet before dtls done"); + return srs_error_wrap(err, "recv unexpect rtp packet before dtls done"); } - uint8_t payload_type = udp_remux_socket->data()[1] & 0x7F; - - char srtp_unprotect_buf[1460]; - int nb_srtp_unprotect_buf = udp_remux_socket->size(); - if (dtls_session->srtp_receiver_unprotect(srtp_unprotect_buf, udp_remux_socket->data(), nb_srtp_unprotect_buf) != srs_success) { - return srs_error_wrap(err, "srtp receiver unprotect failed, payload_type=%u", payload_type); + char unprotected_buf[1460]; + int nb_unprotected_buf = udp_mux_skt->size(); + if (dtls_session->unprotect_rtp(unprotected_buf, udp_mux_skt->data(), nb_unprotected_buf) != srs_success) { + return srs_error_wrap(err, "rtp unprotect failed"); } - //srs_trace("srtp unprotect success, %s", dump_string_hex(srtp_unprotect_buf, nb_srtp_unprotect_buf, nb_srtp_unprotect_buf).c_str()); - - SrsBuffer* stream = new SrsBuffer(srtp_unprotect_buf, nb_srtp_unprotect_buf); + // FIXME: use SrsRtpPacket + SrsBuffer* stream = new SrsBuffer(unprotected_buf, nb_unprotected_buf); SrsAutoFree(SrsBuffer, stream); uint8_t first = stream->read_1bytes(); uint8_t second = stream->read_1bytes(); @@ -769,7 +830,7 @@ srs_error_t SrsRtcSession::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) uint32_t timestamp = stream->read_4bytes(); uint32_t ssrc = stream->read_4bytes(); - srs_trace("sequence=%u, timestamp=%u, ssrc=%u, padding=%d, ext=%d, cc=%u, marker=%d, payload_type=%u", + srs_verbose("sequence=%u, timestamp=%u, ssrc=%u, padding=%d, ext=%d, cc=%u, marker=%d, payload_type=%u", sequence, timestamp, ssrc, padding, ext, cc, marker, payload_type); for (uint8_t i = 0; i < cc; ++i) { @@ -781,70 +842,40 @@ srs_error_t SrsRtcSession::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) uint16_t extern_profile = stream->read_2bytes(); uint16_t extern_length = stream->read_2bytes(); - srs_trace("extern_profile=%u, extern_length=%u", extern_profile, extern_length); + srs_verbose("extern_profile=%u, extern_length=%u", extern_profile, extern_length); stream->read_string(extern_length * 4); } - if (payload_type == 102) { - static uint32_t pre_seq = 0; - uint32_t seq = sequence; - - srs_assert(pre_seq == 0 || (pre_seq + 1 == seq)); - - pre_seq = seq; + return err; +} - static uint8_t start_code[4] = {0x00, 0x00, 0x00, 0x01}; - static int fd = -1; - if (fd < 0) { - fd = open("rtc.264", O_CREAT|O_TRUNC|O_RDWR, 0664); - } +srs_error_t SrsRtcSession::on_rtcp(SrsUdpMuxSocket* udp_mux_skt) +{ + srs_error_t err = srs_success; + if (dtls_session == NULL) { + return srs_error_wrap(err, "recv unexpect rtcp packet before dtls done"); + } - const uint8_t* p = (const uint8_t*)stream->data() + stream->pos(); - int len = stream->left(); - uint8_t header = p[0]; - uint8_t nal_type = header & kNalTypeMask; - - srs_trace("nal_type=%u, seq=%u, rtp payload, %s", nal_type, sequence, dump_string_hex(stream->data() + stream->pos(), stream->left(), stream->left()).c_str()); - - if (nal_type >=1 && nal_type <= 23) { - srs_trace("single nalu"); - write(fd, start_code, sizeof(start_code)); - write(fd, p, len); - } else if (nal_type == kFuA) { - srs_trace("FuA"); - if (p[1] & 0x80) { - uint8_t nal_type = ((p[0] & (~kNalTypeMask)) | (p[1] & kNalTypeMask)); - write(fd, start_code, sizeof(start_code)); - write(fd, &nal_type, 1); - write(fd, p + 2, len - 2); - } else { - write(fd, p + 2, len - 2); - } - } else if (nal_type == kStapA) { - srs_trace("StapA"); - int pos = 1; - while (pos < len) { - int nal_len = p[pos] << 8 | p[pos + 1]; - srs_trace("nal_len=%d", nal_len); - write(fd, start_code, sizeof(start_code)); - write(fd, p + pos + 2, nal_len); - pos += nal_len + 2; - } - srs_assert(pos == len); - } else { - srs_assert(false); - } + char unprotected_buf[1460]; + int nb_unprotected_buf = udp_mux_skt->size(); + if (dtls_session->unprotect_rtcp(unprotected_buf, udp_mux_skt->data(), nb_unprotected_buf) != srs_success) { + return srs_error_wrap(err, "rtcp unprotect failed"); } - // XXX:send h264 back to client, for debug - if (payload_type == 102) { - char rtp_send_protected_buf[1500]; - int rtp_send_protected_len = nb_srtp_unprotect_buf; - SrsBuffer stream(srtp_unprotect_buf + 8, 4); - stream.write_4bytes(3233846889); - dtls_session->srtp_sender_protect(rtp_send_protected_buf, srtp_unprotect_buf, rtp_send_protected_len); - udp_remux_socket->sendto(rtp_send_protected_buf, rtp_send_protected_len, 0); + // FIXME: use SrsRtpPacket + SrsBuffer* stream = new SrsBuffer(unprotected_buf, nb_unprotected_buf); + SrsAutoFree(SrsBuffer, stream); + uint8_t first = stream->read_1bytes(); + uint8_t payload_type = stream->read_1bytes(); + + if (payload_type == kSR) { + } else if (payload_type == kRR) { + } else if (kSDES) { + } else if (kBye) { + } else if (kApp) { + } else { + return srs_error_wrap(err, "unknown rtcp type=%u", payload_type); } return err; @@ -857,25 +888,32 @@ SrsRtcServer::SrsRtcServer(SrsServer* svr) SrsRtcServer::~SrsRtcServer() { + rttrd->stop(); + srs_freep(rttrd); } srs_error_t SrsRtcServer::initialize() { srs_error_t err = srs_success; + rttrd = new SrsRtcTimerThread(this, _srs_context->get_id()); + if (rttrd->start() != srs_success) { + return srs_error_wrap(err, "rtc timer thread init failed"); + } + return err; } -srs_error_t SrsRtcServer::on_udp_packet(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* udp_mux_skt) { srs_error_t err = srs_success; - if (is_stun(udp_remux_socket->data(), udp_remux_socket->size())) { - return on_stun(udp_remux_socket); - } else if (is_dtls(udp_remux_socket->data(), udp_remux_socket->size())) { - return on_dtls(udp_remux_socket); - } else if (is_rtp_or_rtcp(udp_remux_socket->data(), udp_remux_socket->size())) { - return on_rtp_or_rtcp(udp_remux_socket); + if (is_stun(udp_mux_skt->data(), udp_mux_skt->size())) { + return on_stun(udp_mux_skt); + } else if (is_dtls(udp_mux_skt->data(), udp_mux_skt->size())) { + return on_dtls(udp_mux_skt); + } else if (is_rtp_or_rtcp(udp_mux_skt->data(), udp_mux_skt->size())) { + return on_rtp_or_rtcp(udp_mux_skt); } return srs_error_wrap(err, "unknown udp packet type"); @@ -883,20 +921,20 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpRemuxSocket* udp_remux_socket) SrsRtcSession* SrsRtcServer::create_rtc_session(const SrsSdp& remote_sdp, SrsSdp& local_sdp) { - SrsRtcSession* session = new SrsRtcSession(server, this); - std::string local_pwd = gen_random_str(32); std::string local_ufrag = ""; + std::string username = ""; while (true) { local_ufrag = gen_random_str(8); - std::string username = local_ufrag + ":" + remote_sdp.get_ice_ufrag(); - bool ret = map_username_session.insert(make_pair(username, session)).second; - if (ret) { + username = local_ufrag + ":" + remote_sdp.get_ice_ufrag(); + if (! map_username_session.count(username)) break; - } } + SrsRtcSession* session = new SrsRtcSession(server, this, username); + map_username_session.insert(make_pair(username, session)); + local_sdp.set_ice_ufrag(local_ufrag); local_sdp.set_ice_pwd(local_pwd); @@ -918,14 +956,14 @@ SrsRtcSession* SrsRtcServer::find_rtc_session_by_peer_id(const string& peer_id) return iter->second; } -srs_error_t SrsRtcServer::on_stun(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsRtcServer::on_stun(SrsUdpMuxSocket* udp_mux_skt) { srs_error_t err = srs_success; - srs_trace("recv stun packet from %s", udp_remux_socket->get_peer_id().c_str()); + srs_trace("recv stun packet from %s", udp_mux_skt->get_peer_id().c_str()); SrsStunPacket stun_req; - if (stun_req.decode(udp_remux_socket->data(), udp_remux_socket->size()) != srs_success) { + if (stun_req.decode(udp_mux_skt->data(), udp_mux_skt->size()) != srs_success) { return srs_error_wrap(err, "decode stun packet failed"); } @@ -935,37 +973,40 @@ srs_error_t SrsRtcServer::on_stun(SrsUdpRemuxSocket* udp_remux_socket) return srs_error_wrap(err, "can not find rtc_session, stun username=%s", username.c_str()); } - return rtc_session->on_stun(udp_remux_socket, &stun_req); + return rtc_session->on_stun(udp_mux_skt, &stun_req); } -srs_error_t SrsRtcServer::on_dtls(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsRtcServer::on_dtls(SrsUdpMuxSocket* udp_mux_skt) { srs_error_t err = srs_success; srs_trace("on dtls"); - SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(udp_remux_socket->get_peer_id()); + SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(udp_mux_skt->get_peer_id()); if (rtc_session == NULL) { - return srs_error_wrap(err, "can not find rtc session by peer_id=%s", udp_remux_socket->get_peer_id().c_str()); + return srs_error_wrap(err, "can not find rtc session by peer_id=%s", udp_mux_skt->get_peer_id().c_str()); } - rtc_session->on_dtls(udp_remux_socket); + rtc_session->on_dtls(udp_mux_skt); return err; } -srs_error_t SrsRtcServer::on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket) +srs_error_t SrsRtcServer::on_rtp_or_rtcp(SrsUdpMuxSocket* udp_mux_skt) { srs_error_t err = srs_success; - srs_trace("on rtp/rtcp"); - SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(udp_remux_socket->get_peer_id()); + SrsRtcSession* rtc_session = find_rtc_session_by_peer_id(udp_mux_skt->get_peer_id()); if (rtc_session == NULL) { - return srs_error_wrap(err, "can not find rtc session by peer_id=%s", udp_remux_socket->get_peer_id().c_str()); + return srs_error_wrap(err, "can not find rtc session by peer_id=%s", udp_mux_skt->get_peer_id().c_str()); } - rtc_session->on_rtp_or_rtcp(udp_remux_socket); + if (is_rtcp(udp_mux_skt->data(), udp_mux_skt->size())) { + rtc_session->on_rtcp(udp_mux_skt); + } else { + rtc_session->on_rtp(udp_mux_skt); + } return err; } @@ -984,3 +1025,82 @@ bool SrsRtcServer::insert_into_id_sessions(const string& peer_id, SrsRtcSession* { return map_id_session.insert(make_pair(peer_id, rtc_session)).second; } + +void SrsRtcServer::check_and_clean_timeout_session() +{ + map::iterator iter = map_username_session.begin(); + while (iter != map_username_session.end()) { + SrsRtcSession* session = iter->second; + if (session == NULL) { + map_username_session.erase(iter++); + continue; + } + + if (session->is_stun_timeout()) { + srs_trace("rtc session=%s, stun timeout", session->id().c_str()); + map_username_session.erase(iter++); + map_id_session.erase(session->get_peer_id()); + delete session; + continue; + } + + ++iter; + } +} + +SrsRtcTimerThread::SrsRtcTimerThread(SrsRtcServer* rtc_svr, int parent_cid) +{ + _parent_cid = parent_cid; + trd = new SrsDummyCoroutine(); + + rtc_server = rtc_svr; +} + +SrsRtcTimerThread::~SrsRtcTimerThread() +{ + srs_freep(trd); +} + +int SrsRtcTimerThread::cid() +{ + return trd->cid(); +} + +srs_error_t SrsRtcTimerThread::start() +{ + srs_error_t err = srs_success; + + srs_freep(trd); + trd = new SrsSTCoroutine("rtc_timer", this, _parent_cid); + + if ((err = trd->start()) != srs_success) { + return srs_error_wrap(err, "rtc_timer"); + } + + return err; +} + +void SrsRtcTimerThread::stop() +{ + trd->stop(); +} + +void SrsRtcTimerThread::stop_loop() +{ + trd->interrupt(); +} + +srs_error_t SrsRtcTimerThread::cycle() +{ + srs_error_t err = srs_success; + + while (true) { + if ((err = trd->pull()) != srs_success) { + srs_trace("rtc_timer cycle failed"); + return srs_error_wrap(err, "rtc timer thread"); + } + + srs_usleep(1*1000*1000LL); + rtc_server->check_and_clean_timeout_session(); + } +} diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index fab2afc4c..fc67ba431 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -35,12 +36,21 @@ #include #include -class SrsUdpRemuxSocket; +class SrsUdpMuxSocket; class SrsServer; class SrsConsumer; class SrsStunPacket; class SrsRtcServer; class SrsRtcSession; +class SrsSharedPtrMessage; + +const uint8_t kSR = 200; +const uint8_t kRR = 201; +const uint8_t kSDES = 202; +const uint8_t kBye = 203; +const uint8_t kApp = 204; + +const srs_utime_t kSrsRtcSessionStunTimeoutUs = 10*1000*1000LL; class SrsCandidate { @@ -116,19 +126,22 @@ public: SrsDtlsSession(SrsRtcSession* s); virtual ~SrsDtlsSession(); - srs_error_t on_dtls(SrsUdpRemuxSocket* udp_remux_socket); - srs_error_t on_dtls_handshake_done(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t on_dtls(SrsUdpMuxSocket* udp_mux_skt); + srs_error_t on_dtls_handshake_done(SrsUdpMuxSocket* udp_mux_skt); srs_error_t on_dtls_application_data(const char* data, const int len); - void send_client_hello(SrsUdpRemuxSocket* udp_remux_socket); - srs_error_t handshake(SrsUdpRemuxSocket* udp_remux_socket); - srs_error_t srtp_sender_protect(char* protected_buf, const char* ori_buf, int& nb_protected_buf); - srs_error_t srtp_receiver_unprotect(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf); - + void send_client_hello(SrsUdpMuxSocket* udp_mux_skt); +public: + srs_error_t protect_rtp(char* protected_buf, const char* ori_buf, int& nb_protected_buf); + srs_error_t unprotect_rtp(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf); + srs_error_t protect_rtcp(char* protected_buf, const char* ori_buf, int& nb_protected_buf); + srs_error_t unprotect_rtcp(char* unprotected_buf, const char* ori_buf, int& nb_unprotected_buf); +private: + srs_error_t handshake(SrsUdpMuxSocket* udp_mux_skt); private: srs_error_t srtp_initialize(); - srs_error_t srtp_sender_side_init(); - srs_error_t srtp_receiver_side_init(); + srs_error_t srtp_send_init(); + srs_error_t srtp_recv_init(); }; class SrsRtcSenderThread : public ISrsCoroutineHandler @@ -138,11 +151,11 @@ protected: int _parent_cid; private: SrsRtcSession* rtc_session; - SrsUdpRemuxSocket ukt; + SrsUdpMuxSocket ukt; public: // Constructor. // @param tm The receive timeout in srs_utime_t. - SrsRtcSenderThread(SrsRtcSession* s, SrsUdpRemuxSocket* u, int parent_cid); + SrsRtcSenderThread(SrsRtcSession* s, SrsUdpMuxSocket* u, int parent_cid); virtual ~SrsRtcSenderThread(); public: virtual int cid(); @@ -152,6 +165,8 @@ public: virtual void stop_loop(); public: virtual srs_error_t cycle(); +private: + void send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, SrsUdpMuxSocket* udp_mux_skt); }; class SrsRtcSession @@ -165,39 +180,76 @@ private: SrsRtcSessionStateType session_state; SrsDtlsSession* dtls_session; SrsRtcSenderThread* strd; + std::string username; + std::string peer_id; + srs_utime_t last_stun_time; public: std::string app; std::string stream; public: - SrsRtcSession(SrsServer* svr, SrsRtcServer* rtc_svr); + SrsRtcSession(SrsServer* svr, SrsRtcServer* rtc_svr, const std::string& un); virtual ~SrsRtcSession(); public: SrsSdp* get_local_sdp() { return &local_sdp; } - SrsSdp* get_remote_sdp() { return &remote_sdp; } - SrsRtcSessionStateType get_session_state() { return session_state; } - void set_local_sdp(const SrsSdp& sdp) { local_sdp = sdp; } + + SrsSdp* get_remote_sdp() { return &remote_sdp; } void set_remote_sdp(const SrsSdp& sdp) { remote_sdp = sdp; } + + SrsRtcSessionStateType get_session_state() { return session_state; } void set_session_state(SrsRtcSessionStateType state) { session_state = state; } + + std::string id() const { return peer_id + "_" + username; } + void set_app_stream(const std::string& a, const std::string& s) { app = a; stream = s; } + + std::string get_peer_id() const { return peer_id; } + void set_peer_id(const std::string& id) { peer_id = id; } +public: + srs_error_t on_stun(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* stun_req); + srs_error_t on_dtls(SrsUdpMuxSocket* udp_mux_skt); + srs_error_t on_rtp(SrsUdpMuxSocket* udp_mux_skt); + srs_error_t on_rtcp(SrsUdpMuxSocket* udp_mux_skt); public: - srs_error_t on_stun(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req); - srs_error_t on_dtls(SrsUdpRemuxSocket* udp_remux_socket); - srs_error_t on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t send_client_hello(SrsUdpMuxSocket* udp_mux_skt); + void on_connection_established(SrsUdpMuxSocket* udp_mux_skt); + srs_error_t start_play(SrsUdpMuxSocket* udp_mux_skt); public: - srs_error_t send_client_hello(SrsUdpRemuxSocket* udp_remux_socket); - void on_connection_established(SrsUdpRemuxSocket* udp_remux_socket); - srs_error_t start_play(SrsUdpRemuxSocket* udp_remux_socket); + bool is_stun_timeout() { return last_stun_time + kSrsRtcSessionStunTimeoutUs < srs_get_system_time(); } private: - srs_error_t on_binding_request(SrsUdpRemuxSocket* udp_remux_socket, SrsStunPacket* stun_req); + srs_error_t on_binding_request(SrsUdpMuxSocket* udp_mux_skt, SrsStunPacket* stun_req); private: - srs_error_t do_playing(SrsConsumer* consumer, SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t do_playing(SrsConsumer* consumer, SrsUdpMuxSocket* udp_mux_skt); +}; + +// XXX: is there any other timer thread? +class SrsRtcTimerThread : public ISrsCoroutineHandler +{ +protected: + SrsCoroutine* trd; + int _parent_cid; +private: + SrsRtcServer* rtc_server; +public: + // Constructor. + // @param tm The receive timeout in srs_utime_t. + SrsRtcTimerThread(SrsRtcServer* rtc_svr, int parent_cid); + virtual ~SrsRtcTimerThread(); +public: + virtual int cid(); +public: + virtual srs_error_t start(); + virtual void stop(); + virtual void stop_loop(); +public: + virtual srs_error_t cycle(); }; -class SrsRtcServer : public ISrsUdpRemuxHandler +class SrsRtcServer : public ISrsUdpMuxHandler { private: SrsServer* server; + SrsRtcTimerThread* rttrd; private: std::map map_username_session; // key: username(local_ufrag + ":" + remote_ufrag) std::map map_id_session; // key: peerip(ip + ":" + port) @@ -207,14 +259,15 @@ public: public: virtual srs_error_t initialize(); - virtual srs_error_t on_udp_packet(SrsUdpRemuxSocket* udp_remux_socket); + virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* udp_mux_skt); SrsRtcSession* create_rtc_session(const SrsSdp& remote_sdp, SrsSdp& local_sdp); bool insert_into_id_sessions(const std::string& peer_id, SrsRtcSession* rtc_session); + void check_and_clean_timeout_session(); private: - srs_error_t on_stun(SrsUdpRemuxSocket* udp_remux_socket); - srs_error_t on_dtls(SrsUdpRemuxSocket* udp_remux_socket); - srs_error_t on_rtp_or_rtcp(SrsUdpRemuxSocket* udp_remux_socket); + srs_error_t on_stun(SrsUdpMuxSocket* udp_mux_skt); + srs_error_t on_dtls(SrsUdpMuxSocket* udp_mux_skt); + srs_error_t on_rtp_or_rtcp(SrsUdpMuxSocket* udp_mux_skt); private: SrsRtcSession* find_rtc_session_by_username(const std::string& ufrag); SrsRtcSession* find_rtc_session_by_peer_id(const std::string& peer_id); diff --git a/trunk/src/app/srs_app_rtp.cpp b/trunk/src/app/srs_app_rtp.cpp index dafdbd0b8..f3981babe 100644 --- a/trunk/src/app/srs_app_rtp.cpp +++ b/trunk/src/app/srs_app_rtp.cpp @@ -87,10 +87,12 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF packet_fu_a(shared_frame, format, &sample, rtp_packet_vec); } +#if 0 srs_trace("nal size=%d, nal=%s", sample.size, dump_string_hex(sample.bytes, sample.size, sample.size).c_str()); for (int i = 0; i < shared_frame->nb_rtp_fragments; ++i) { srs_trace("rtp=%s", dump_string_hex(shared_frame->rtp_fragments[i].bytes, shared_frame->rtp_fragments[i].size, kRtpPacketSize).c_str()); } +#endif } SrsSample* rtp_samples = new SrsSample[rtp_packet_vec.size()]; @@ -134,7 +136,6 @@ srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsForma stream->write_1bytes(kH264PayloadType); } // sequence - srs_trace("sequence=%u", sequence); stream->write_2bytes(sequence++); // timestamp stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); @@ -186,7 +187,6 @@ srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, S // marker payloadtype stream->write_1bytes(kMarker | kH264PayloadType); // sequenct - srs_trace("sequence=%u", sequence); stream->write_2bytes(sequence++); // timestamp stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); @@ -220,7 +220,6 @@ srs_error_t SrsRtpMuxer::packet_stap_a(const string &sps, const string& pps, Srs // marker payloadtype stream->write_1bytes(kMarker | kH264PayloadType); // sequenct - srs_trace("sequence=%u", sequence); stream->write_2bytes(sequence++); // timestamp stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index eee3a9784..330fd8d10 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -360,7 +360,7 @@ srs_error_t SrsRtcListener::listen(std::string i, int p) port = p; srs_freep(listener); - listener = new SrsUdpRemuxListener(rtc, ip, port); + listener = new SrsUdpMuxListener(rtc, ip, port); if ((err = listener->listen()) != srs_success) { return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); @@ -649,6 +649,10 @@ srs_error_t SrsServer::initialize(ISrsServerCycle* ch) if ((err = http_server->initialize()) != srs_success) { return srs_error_wrap(err, "http server initialize"); } + + if ((err = rtc_server->initialize()) != srs_success) { + return srs_error_wrap(err, "rtc server initialize"); + } return err; } diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 8fa17ff9d..cf677994d 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -162,8 +162,8 @@ public: class SrsRtcListener : public SrsListener { protected: - SrsUdpRemuxListener* listener; - ISrsUdpRemuxHandler* rtc; + SrsUdpMuxListener* listener; + ISrsUdpMuxHandler* rtc; public: SrsRtcListener(SrsServer* svr, SrsRtcServer* rtc_svr, SrsListenerType t); virtual ~SrsRtcListener(); diff --git a/trunk/src/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp index 555ff5a33..947150f61 100644 --- a/trunk/src/service/srs_service_st.cpp +++ b/trunk/src/service/srs_service_st.cpp @@ -402,6 +402,11 @@ int srs_sendto(srs_netfd_t stfd, void *buf, int len, const struct sockaddr * to, return st_sendto((st_netfd_t)stfd, buf, len, to, tolen, (st_utime_t)timeout); } +int srs_sendmsg(srs_netfd_t stfd, const struct msghdr *msg, int flags, srs_utime_t timeout) +{ + return st_sendmsg((st_netfd_t)stfd, msg, flags, (st_utime_t)timeout); +} + srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout) { return (srs_netfd_t)st_accept((st_netfd_t)stfd, addr, addrlen, (st_utime_t)timeout); diff --git a/trunk/src/service/srs_service_st.hpp b/trunk/src/service/srs_service_st.hpp index 4894e7049..947950e85 100644 --- a/trunk/src/service/srs_service_st.hpp +++ b/trunk/src/service/srs_service_st.hpp @@ -89,6 +89,7 @@ extern srs_netfd_t srs_netfd_open(int osfd); extern int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, int *fromlen, srs_utime_t timeout); extern int srs_sendto(srs_netfd_t stfd, void *buf, int len, const struct sockaddr *to, int tolen, srs_utime_t timeout); +extern int srs_sendmsg(srs_netfd_t stfd, const struct msghdr *msg, int flags, srs_utime_t timeout); extern srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout); From 027d34bbd304ce79b48603fa80b2f392d8668306 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Fri, 13 Mar 2020 20:34:40 +0800 Subject: [PATCH 20/21] add rtp shared packet --- trunk/configure | 2 +- trunk/src/app/srs_app_rtc_conn.cpp | 7 ++-- trunk/src/app/srs_app_rtp.cpp | 51 ++++++++++----------------- trunk/src/app/srs_app_rtp.hpp | 7 ++-- trunk/src/app/srs_app_source.cpp | 2 +- trunk/src/kernel/srs_kernel_error.hpp | 1 + trunk/src/kernel/srs_kernel_flv.cpp | 39 +++++++++----------- trunk/src/kernel/srs_kernel_flv.hpp | 11 +++--- 8 files changed, 51 insertions(+), 69 deletions(-) diff --git a/trunk/configure b/trunk/configure index 495400bb5..211bdbdff 100755 --- a/trunk/configure +++ b/trunk/configure @@ -204,7 +204,7 @@ MODULE_ID="KERNEL" MODULE_DEPENDS=("CORE") ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_buffer" - "srs_kernel_utility" "srs_kernel_flv" "srs_kernel_codec" "srs_kernel_io" + "srs_kernel_utility" "srs_kernel_flv" "srs_kernel_rtp" "srs_kernel_codec" "srs_kernel_io" "srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts" "srs_kernel_stream" "srs_kernel_balance" "srs_kernel_mp4" "srs_kernel_file") KERNEL_INCS="src/kernel"; MODULE_DIR=${KERNEL_INCS} . auto/modules.sh diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 07d0fc900..802e911c8 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -37,6 +37,7 @@ using namespace std; #include #include +#include #include #include #include @@ -682,12 +683,12 @@ void SrsRtcSenderThread::send_and_free_messages(SrsSharedPtrMessage** msgs, int for (int i = 0; i < nb_msgs; i++) { SrsSharedPtrMessage* msg = msgs[i]; - for (int i = 0; i < msg->nb_rtp_fragments; ++i) { + for (int i = 0; i < msg->rtp_packets.size(); ++i) { if (rtc_session->dtls_session) { char protected_buf[kRtpPacketSize]; - int nb_protected_buf = msg->rtp_fragments[i].size; + int nb_protected_buf = msg->rtp_packets[i]->size; - rtc_session->dtls_session->protect_rtp(protected_buf, msg->rtp_fragments[i].bytes, nb_protected_buf); + rtc_session->dtls_session->protect_rtp(protected_buf, msg->rtp_packets[i]->payload, nb_protected_buf); udp_mux_skt->sendto(protected_buf, nb_protected_buf, 0); } } diff --git a/trunk/src/app/srs_app_rtp.cpp b/trunk/src/app/srs_app_rtp.cpp index f3981babe..ee88f419d 100644 --- a/trunk/src/app/srs_app_rtp.cpp +++ b/trunk/src/app/srs_app_rtp.cpp @@ -38,6 +38,7 @@ using namespace std; #include #include #include +#include #include #include #include @@ -68,7 +69,7 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF pps.assign(format->vcodec->pictureParameterSetNALUnit.data(), format->vcodec->pictureParameterSetNALUnit.size()); } - vector rtp_packet_vec; + vector rtp_packet_vec; for (int i = 0; i < format->video->nb_samples; ++i) { SrsSample sample = format->video->samples[i]; @@ -95,17 +96,12 @@ srs_error_t SrsRtpMuxer::frame_to_packet(SrsSharedPtrMessage* shared_frame, SrsF #endif } - SrsSample* rtp_samples = new SrsSample[rtp_packet_vec.size()]; - for (int i = 0; i < rtp_packet_vec.size(); ++i) { - rtp_samples[i] = rtp_packet_vec[i]; - } - - shared_frame->set_rtp_fragments(rtp_samples, rtp_packet_vec.size()); + shared_frame->set_rtp_packets(rtp_packet_vec); return err; } -srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector& rtp_packet_vec) +srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector& rtp_packet_vec) { srs_error_t err = srs_success; @@ -136,7 +132,7 @@ srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsForma stream->write_1bytes(kH264PayloadType); } // sequence - stream->write_2bytes(sequence++); + stream->write_2bytes(sequence); // timestamp stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); // ssrc @@ -159,15 +155,14 @@ srs_error_t SrsRtpMuxer::packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsForma nb_left -= packet_size; - SrsSample rtp_packet; - rtp_packet.bytes = stream->data(); - rtp_packet.size = stream->pos(); + SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket(); + rtp_shared_pkt->create((shared_frame->timestamp * 90), sequence++, kVideoSSRC, kH264PayloadType, stream->data(), stream->pos()); - rtp_packet_vec.push_back(rtp_packet); + rtp_packet_vec.push_back(rtp_shared_pkt); } } -srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector& rtp_packet_vec) +srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, vector& rtp_packet_vec) { srs_error_t err = srs_success; @@ -187,7 +182,7 @@ srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, S // marker payloadtype stream->write_1bytes(kMarker | kH264PayloadType); // sequenct - stream->write_2bytes(sequence++); + stream->write_2bytes(sequence); // timestamp stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); // ssrc @@ -195,16 +190,15 @@ srs_error_t SrsRtpMuxer::packet_single_nalu(SrsSharedPtrMessage* shared_frame, S stream->write_bytes(sample->bytes, sample->size); - SrsSample rtp_packet; - rtp_packet.bytes = stream->data(); - rtp_packet.size = stream->pos(); + SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket(); + rtp_shared_pkt->create((shared_frame->timestamp * 90), sequence++, kVideoSSRC, kH264PayloadType, stream->data(), stream->pos()); - rtp_packet_vec.push_back(rtp_packet); + rtp_packet_vec.push_back(rtp_shared_pkt); return err; } -srs_error_t SrsRtpMuxer::packet_stap_a(const string &sps, const string& pps, SrsSharedPtrMessage* shared_frame, vector& rtp_packet_vec) +srs_error_t SrsRtpMuxer::packet_stap_a(const string &sps, const string& pps, SrsSharedPtrMessage* shared_frame, vector& rtp_packet_vec) { srs_error_t err = srs_success; @@ -220,7 +214,7 @@ srs_error_t SrsRtpMuxer::packet_stap_a(const string &sps, const string& pps, Srs // marker payloadtype stream->write_1bytes(kMarker | kH264PayloadType); // sequenct - stream->write_2bytes(sequence++); + stream->write_2bytes(sequence); // timestamp stream->write_4bytes(int32_t(shared_frame->timestamp * 90)); // ssrc @@ -237,11 +231,10 @@ srs_error_t SrsRtpMuxer::packet_stap_a(const string &sps, const string& pps, Srs stream->write_2bytes(pps.size()); stream->write_bytes((char*)pps.data(), pps.size()); - SrsSample rtp_packet; - rtp_packet.bytes = stream->data(); - rtp_packet.size = stream->pos(); + SrsRtpSharedPacket* rtp_shared_pkt = new SrsRtpSharedPacket(); + rtp_shared_pkt->create((shared_frame->timestamp * 90), sequence++, kVideoSSRC, kH264PayloadType, stream->data(), stream->pos()); - rtp_packet_vec.push_back(rtp_packet); + rtp_packet_vec.push_back(rtp_shared_pkt); return err; } @@ -336,9 +329,6 @@ srs_error_t SrsRtp::on_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* forma // update the hls time, for hls_dispose. last_update_time = srs_get_system_time(); - SrsSharedPtrMessage* audio = shared_audio->copy(); - SrsAutoFree(SrsSharedPtrMessage, audio); - // ts support audio codec: aac/mp3 SrsAudioCodecId acodec = format->acodec->id; if (acodec != SrsAudioCodecIdAAC && acodec != SrsAudioCodecIdMP3) { @@ -369,11 +359,8 @@ srs_error_t SrsRtp::on_video(SrsSharedPtrMessage* shared_video, SrsFormat* forma // update the hls time, for hls_dispose. last_update_time = srs_get_system_time(); - SrsSharedPtrMessage* video = shared_video->copy(); - SrsAutoFree(SrsSharedPtrMessage, video); - // ignore info frame, // @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909 srs_assert(format->video); - return rtp_h264_muxer->frame_to_packet(video, format); + return rtp_h264_muxer->frame_to_packet(shared_video, format); } diff --git a/trunk/src/app/srs_app_rtp.hpp b/trunk/src/app/srs_app_rtp.hpp index 9000fd830..2a23440cd 100644 --- a/trunk/src/app/srs_app_rtp.hpp +++ b/trunk/src/app/srs_app_rtp.hpp @@ -33,6 +33,7 @@ class SrsFormat; class SrsSample; class SrsSharedPtrMessage; +class SrsRtpSharedPacket; class SrsRequest; class SrsOriginHub; @@ -65,9 +66,9 @@ public: public: srs_error_t frame_to_packet(SrsSharedPtrMessage* shared_video, SrsFormat* format); private: - srs_error_t packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector& rtp_packet_vec); - srs_error_t packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector& rtp_packet_vec); - srs_error_t packet_stap_a(const std::string &sps, const std::string& pps, SrsSharedPtrMessage* shared_frame, std::vector& rtp_packet_vec); + srs_error_t packet_fu_a(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector& rtp_packet_vec); + srs_error_t packet_single_nalu(SrsSharedPtrMessage* shared_frame, SrsFormat* format, SrsSample* sample, std::vector& rtp_packet_vec); + srs_error_t packet_stap_a(const std::string &sps, const std::string& pps, SrsSharedPtrMessage* shared_frame, std::vector& rtp_packet_vec); }; class SrsRtp diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 1452bf93d..5f1de1149 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -2300,7 +2300,7 @@ srs_error_t SrsSource::on_video_imp(SrsSharedPtrMessage* msg) if ((err = hub->on_video(msg, is_sequence_header)) != srs_success) { return srs_error_wrap(err, "hub consume video"); } - + // copy to all consumer if (!drop_for_reduce) { for (int i = 0; i < (int)consumers.size(); i++) { diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 372a0196a..264903e45 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -322,6 +322,7 @@ #define ERROR_BASE64_DECODE 4039 #define ERROR_HTTP_STREAM_EOF 4040 #define ERROR_RTC_PORT 4041 +#define ERROR_RTP_PACKET_CREATE 4042 /////////////////////////////////////////////////////// // HTTP API error. diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index f2f773049..5996b60b5 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -36,6 +36,7 @@ using namespace std; #include #include #include +#include #include #include #include @@ -203,8 +204,6 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload() { payload = NULL; size = 0; - rtp_fragments = NULL; - nb_rtp_fragments = 0; shared_count = 0; } @@ -214,17 +213,9 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload() srs_memory_unwatch(payload); #endif srs_freepa(payload); - - for (int i = 0; i < nb_rtp_fragments; ++i) { - srs_freepa(rtp_fragments[i].bytes); - } - - if (rtp_fragments != NULL && nb_rtp_fragments > 0) { - srs_freepa(rtp_fragments); - } } -SrsSharedPtrMessage::SrsSharedPtrMessage() : timestamp(0), stream_id(0), size(0), payload(NULL), rtp_fragments(NULL), nb_rtp_fragments(0) +SrsSharedPtrMessage::SrsSharedPtrMessage() : timestamp(0), stream_id(0), size(0), payload(NULL) { ptr = NULL; } @@ -238,6 +229,10 @@ SrsSharedPtrMessage::~SrsSharedPtrMessage() ptr->shared_count--; } } + + for (int i = 0; i < rtp_packets.size(); ++i) { + srs_freep(rtp_packets[i]); + } } srs_error_t SrsSharedPtrMessage::create(SrsCommonMessage* msg) @@ -315,15 +310,6 @@ bool SrsSharedPtrMessage::check(int stream_id) return false; } -void SrsSharedPtrMessage::set_rtp_fragments(SrsSample* samples, int nb_samples) -{ - ptr->rtp_fragments = samples; - ptr->nb_rtp_fragments = nb_samples; - - rtp_fragments = samples; - nb_rtp_fragments = nb_samples; -} - bool SrsSharedPtrMessage::is_av() { return ptr->header.message_type == RTMP_MSG_AudioMessage @@ -364,12 +350,19 @@ SrsSharedPtrMessage* SrsSharedPtrMessage::copy() copy->stream_id = stream_id; copy->payload = ptr->payload; copy->size = ptr->size; - copy->rtp_fragments = ptr->rtp_fragments; - copy->nb_rtp_fragments = ptr->nb_rtp_fragments; - + + for (int i = 0; i < rtp_packets.size(); ++i) { + copy->rtp_packets.push_back(rtp_packets[i]->copy()); + } + return copy; } +void SrsSharedPtrMessage::set_rtp_packets(const std::vector& pkts) +{ + rtp_packets = pkts; +} + SrsFlvTransmuxer::SrsFlvTransmuxer() { writer = NULL; diff --git a/trunk/src/kernel/srs_kernel_flv.hpp b/trunk/src/kernel/srs_kernel_flv.hpp index fe8fd9aa0..3bf3afc69 100644 --- a/trunk/src/kernel/srs_kernel_flv.hpp +++ b/trunk/src/kernel/srs_kernel_flv.hpp @@ -27,6 +27,7 @@ #include #include +#include // For srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 @@ -39,6 +40,7 @@ class ISrsReader; class SrsFileReader; class SrsPacket; class SrsSample; +class SrsRtpSharedPacket; #define SRS_FLV_TAG_HEADER_SIZE 11 #define SRS_FLV_PREVIOUS_TAG_SIZE 4 @@ -287,8 +289,7 @@ public: // video/audio packet use raw bytes, no video/audio packet. char* payload; - SrsSample* rtp_fragments; - int nb_rtp_fragments; + std::vector rtp_packets; private: class SrsSharedPtrPayload { @@ -302,8 +303,6 @@ private: int size; // The reference count int shared_count; - SrsSample* rtp_fragments; - int nb_rtp_fragments; public: SrsSharedPtrPayload(); virtual ~SrsSharedPtrPayload(); @@ -333,8 +332,6 @@ public: // check perfer cid and stream id. // @return whether stream id already set. virtual bool check(int stream_id); - - virtual void set_rtp_fragments(SrsSample* samples, int nb_samples); public: virtual bool is_av(); virtual bool is_audio(); @@ -347,6 +344,8 @@ public: // copy current shared ptr message, use ref-count. // @remark, assert object is created. virtual SrsSharedPtrMessage* copy(); +public: + virtual void set_rtp_packets(const std::vector& pkts); }; // Transmux RTMP packets to FLV stream. From 768598a3bb2a0429a689008ca374d45ab2b16be7 Mon Sep 17 00:00:00 2001 From: xiaozhihong Date: Fri, 13 Mar 2020 20:35:07 +0800 Subject: [PATCH 21/21] add kernel rtp packet --- trunk/src/kernel/srs_kernel_rtp.cpp | 112 ++++++++++++++++++++++++++++ trunk/src/kernel/srs_kernel_rtp.hpp | 62 +++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 trunk/src/kernel/srs_kernel_rtp.cpp create mode 100644 trunk/src/kernel/srs_kernel_rtp.hpp diff --git a/trunk/src/kernel/srs_kernel_rtp.cpp b/trunk/src/kernel/srs_kernel_rtp.cpp new file mode 100644 index 000000000..516ae89ff --- /dev/null +++ b/trunk/src/kernel/srs_kernel_rtp.cpp @@ -0,0 +1,112 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +using namespace std; + +#include +#include +#include +#include + +SrsRtpSharedPacket::SrsRtpSharedPacketPayload::SrsRtpSharedPacketPayload() +{ + payload = NULL; + size = 0; + shared_count = 0; +} + +SrsRtpSharedPacket::SrsRtpSharedPacketPayload::~SrsRtpSharedPacketPayload() +{ + srs_freepa(payload); +} + +SrsRtpSharedPacket::SrsRtpSharedPacket() +{ + payload_ptr = NULL; + + payload = NULL; + size = 0; + + timestamp = -1; + sequence = 0; + ssrc = 0; + payload_type = 0; +} + +SrsRtpSharedPacket::~SrsRtpSharedPacket() +{ + if (payload_ptr) { + if (payload_ptr->shared_count == 0) { + srs_freep(payload_ptr); + } else { + --payload_ptr->shared_count; + } + } +} + +srs_error_t SrsRtpSharedPacket::create(int64_t t, uint16_t seq, uint32_t sc, uint16_t pt, char* p, int s) +{ + srs_error_t err = srs_success; + + if (size < 0) { + return srs_error_new(ERROR_RTP_PACKET_CREATE, "create packet size=%d", size); + } + + srs_assert(!payload_ptr); + + timestamp = t; + sequence = seq; + ssrc = sc; + payload_type = pt; + + payload_ptr = new SrsRtpSharedPacketPayload(); + payload_ptr->payload = p; + payload_ptr->size = s; + + payload = payload_ptr->payload; + size = payload_ptr->size; + + return err; +} + +SrsRtpSharedPacket* SrsRtpSharedPacket::copy() +{ + SrsRtpSharedPacket* copy = new SrsRtpSharedPacket(); + + copy->payload_ptr = payload_ptr; + payload_ptr->shared_count++; + + copy->payload = payload; + copy->size = size; + + copy->timestamp = timestamp; + copy->sequence = sequence; + copy->ssrc = ssrc; + copy->payload_type = payload_type; + + return copy; +} diff --git a/trunk/src/kernel/srs_kernel_rtp.hpp b/trunk/src/kernel/srs_kernel_rtp.hpp new file mode 100644 index 000000000..b348946ec --- /dev/null +++ b/trunk/src/kernel/srs_kernel_rtp.hpp @@ -0,0 +1,62 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_KERNEL_RTP_HPP +#define SRS_KERNEL_RTP_HPP + +#include + +#include + +class SrsRtpSharedPacket +{ +private: + class SrsRtpSharedPacketPayload + { + public: + char* payload; + int size; + int shared_count; + public: + SrsRtpSharedPacketPayload(); + virtual ~SrsRtpSharedPacketPayload(); + }; +private: + SrsRtpSharedPacketPayload* payload_ptr; +public: + char* payload; + int size; +public: + int64_t timestamp; + uint16_t sequence; + uint32_t ssrc; + uint16_t payload_type; +public: + SrsRtpSharedPacket(); + virtual ~SrsRtpSharedPacket(); +public: + srs_error_t create(int64_t t, uint16_t seq, uint32_t sc, uint16_t pt, char* p, int s); + SrsRtpSharedPacket* copy(); +}; + +#endif