From 1589858cb0be003946ca6637cb6ee4039071a62f Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 6 Aug 2022 13:03:45 +0800 Subject: [PATCH] Support MIPS 64bits for loongson 3A4000/3B3000. v5.0.34 --- trunk/3rdparty/st-srs/README.md | 3 + trunk/3rdparty/st-srs/auto/codecov.sh | 64 ++++------ trunk/3rdparty/st-srs/auto/coverage.sh | 20 +-- trunk/3rdparty/st-srs/auto/fast.sh | 17 +-- trunk/3rdparty/st-srs/md.h | 6 +- trunk/3rdparty/st-srs/md_linux.S | 80 +++++++++++- .../st-srs/tools/helloworld/helloworld.c | 3 +- trunk/3rdparty/st-srs/tools/porting/porting.c | 68 +++++++++- trunk/3rdparty/st-srs/tools/verify/verify.c | 2 +- trunk/3rdparty/st-srs/utest/Makefile | 25 +++- trunk/3rdparty/st-srs/utest/st_utest.cpp | 114 ++--------------- trunk/3rdparty/st-srs/utest/st_utest.hpp | 111 ++++++++++++++++ .../st-srs/utest/st_utest_coroutines.cpp | 120 ++++++++++++++++++ trunk/3rdparty/st-srs/utest/st_utest_tcp.cpp | 92 ++++++++++++++ trunk/doc/CHANGELOG.md | 1 + trunk/src/core/srs_core_version5.hpp | 2 +- 16 files changed, 543 insertions(+), 185 deletions(-) create mode 100644 trunk/3rdparty/st-srs/utest/st_utest_coroutines.cpp create mode 100644 trunk/3rdparty/st-srs/utest/st_utest_tcp.cpp diff --git a/trunk/3rdparty/st-srs/README.md b/trunk/3rdparty/st-srs/README.md index 738b5ec94..dc838fa18 100644 --- a/trunk/3rdparty/st-srs/README.md +++ b/trunk/3rdparty/st-srs/README.md @@ -35,6 +35,8 @@ For Linux aarch64, which fail with `Unknown CPU architecture`: make linux-debug EXTRA_CFLAGS="-D__aarch64__" ``` +> Note: For more CPU architectures, please see [#22](https://github.com/ossrs/state-threads/issues/22) + Linux with valgrind: ```bash @@ -110,6 +112,7 @@ The branch [srs](https://github.com/ossrs/state-threads/tree/srs) will be patche - [x] LOONGARCH: Support loongarch for loongson CPU, [#24](https://github.com/ossrs/state-threads/issues/24). - [x] System: Support Multiple Threads for Linux and Darwin. [#19](https://github.com/ossrs/state-threads/issues/19), [srs#2188](https://github.com/ossrs/srs/issues/2188). - [x] RISCV: Support RISCV for RISCV CPU, [#24](https://github.com/ossrs/state-threads/pull/28). +- [x] MIPS: Support Linux/MIPS64 for loongson 3A4000/3B3000, [#21](https://github.com/ossrs/state-threads/pull/21). - [ ] IDE: Support CLion for debugging and learning. - [ ] System: Support sendmmsg for UDP, [#12](https://github.com/ossrs/state-threads/issues/12). diff --git a/trunk/3rdparty/st-srs/auto/codecov.sh b/trunk/3rdparty/st-srs/auto/codecov.sh index 5f964b772..1200438ca 100755 --- a/trunk/3rdparty/st-srs/auto/codecov.sh +++ b/trunk/3rdparty/st-srs/auto/codecov.sh @@ -1,52 +1,38 @@ #!/bin/bash -# In .circleci/config.yml, generate *.gcno with -# ./configure --gcov --without-research --without-librtmp && make -# and generate *.gcda by -# ./objs/srs_utest - -# Workdir is objs/cover. -workdir=`pwd`/codecov && rm -rf $workdir - -# Tool git is required to map the right path. -git --version >/dev/null 2>&1 -ret=$?; if [[ $ret -ne 0 ]]; then echo "Tool git is required, ret=$ret"; exit $ret; fi +# Workdir is obj/coverage. +workdir=`pwd`/coverage # Create trunk under workdir. mkdir -p $workdir && cd $workdir ret=$?; if [[ $ret -ne 0 ]]; then echo "Enter workdir failed, ret=$ret"; exit $ret; fi -# Collect all *.gcno and *.gcda to objs/cover. -cd $workdir && for file in $(cd .. && ls *.c); do - cp ../$file $file && echo "Copy $file" && - if [[ -f ../obj/${file%.*}.gcno ]]; then - cp ../obj/${file%.*}.* . - fi -done -ret=$?; if [[ $ret -ne 0 ]]; then echo "Collect *.gcno and *.gcda failed, ret=$ret"; exit $ret; fi - -# Generate *.gcov for coverage. -cd $workdir && -for file in $(ls *.c); do - gcov $file -o `dirname $file` - ret=$?; if [[ $ret -ne 0 ]]; then echo "Collect $file failed, ret=$ret"; exit $ret; fi -done - -# Filter the gcov files, remove utest or gtest. -cd $workdir && -rm -f *gtest*.gcov *utest*.gcov -ret=$?; if [[ $ret -ne 0 ]]; then echo "Cook gcov files failed, ret=$ret"; exit $ret; fi +CODECOV_ARGS="" +if [[ $ST_PROJECT != '' ]]; then + # -R root dir Used when not in git/hg project to identify project root directory + # -p dir Project root directory. Also used when preparing gcov + CODECOV_ARGS="$CODECOV_ARGS -R $ST_PROJECT -p $ST_PROJECT" +fi +if [[ $ST_BRANCH != '' ]]; then + # -B branch Specify the branch name + CODECOV_ARGS="$CODECOV_ARGS -B $ST_BRANCH" +fi +if [[ $ST_SHA != '' ]]; then + # -C sha Specify the commit sha + CODECOV_ARGS="$CODECOV_ARGS -C $ST_SHA" +fi +if [[ $ST_PR != '' ]]; then + # -P pr Specify the pull request number + CODECOV_ARGS="$CODECOV_ARGS -P $ST_PR" +fi # Upload report with *.gcov # Remark: The file codecov.yml is not neccessary. It literally depends on git. # Note: The right path is like: -# https://codecov.io/gh/ossrs/srs/src/3.0release/trunk/src/protocol/srs_rtmp_stack.cpp -# https://codecov.io/gh/ossrs/srs/src/20fbb4466fdc8ba5d810b8570df6004063212838/trunk/src/protocol/srs_rtmp_stack.cpp -# Remark: It takes a few minutes to sync with github, so it might not available when CircleCI is done. -# https://circleci.com/gh/ossrs/srs/tree/3.0release -# -# Note: Use '-X gcov' to avoid generate the gcov files again. +# https://app.codecov.io/gh/ossrs/state-threads/blob/srs/sched.c +# https://app.codecov.io/gh/ossrs/state-threads/blob/593cf748f055ca383867003e409a423efd8f8f86/sched.c cd $workdir && -export CODECOV_TOKEN="0d616496-f781-4e7c-b285-d1f70a1cdf24" && -bash <(curl -s https://codecov.io/bash) -X gcov && +export CODECOV_TOKEN="$CODECOV_TOKEN" && +bash <(curl -s https://codecov.io/bash) $CODECOV_ARGS -f '!*gtest*' -f '!*c++*' && echo "Done" && exit 0 + diff --git a/trunk/3rdparty/st-srs/auto/coverage.sh b/trunk/3rdparty/st-srs/auto/coverage.sh index 93f717c85..0aa220ebd 100755 --- a/trunk/3rdparty/st-srs/auto/coverage.sh +++ b/trunk/3rdparty/st-srs/auto/coverage.sh @@ -1,23 +1,14 @@ #!/bin/bash -if [[ ! -f utest/gtest/include/gtest/gtest.h ]]; then - ( - cd utest && rm -rf gtest && - curl https://github.com/google/googletest/archive/release-1.6.0.tar.gz -L -o googletest-release-1.6.0.tar.gz && - tar xf googletest-release-1.6.0.tar.gz && - ln -sf googletest-release-1.6.0 gtest && - echo "Setup gtest ok" - ) -fi -if [[ ! -f utest/gtest/include/gtest/gtest.h ]]; then - echo "No utest/gtest, please download from https://github.com/google/googletest/releases/tag/release-1.6.0" +if [[ ! -f utest/gtest-fit/googletest/include/gtest/gtest.h ]]; then + echo "No utest/gtest, please download from https://github.com/google/googletest/releases/tag/release-1.11.0" exit -1 else echo "Check utest/gtest ok" fi if [[ $(gcovr --version >/dev/null && echo yes) != yes ]]; then - echo "Please install gcovr: https://github.com/ossrs/state-threads/tree/srs#utest-and-coverage" + echo "Please install gcovr" exit -1 fi @@ -34,6 +25,9 @@ fi echo "Generating coverage" mkdir -p coverage && -gcovr -r . -e LINUX -e DARWIN -e examples --html --html-details -o coverage/st.html && +(cd obj && rm -f gtest-all.gcda gtest-all.gcno) && +(cd obj && rm -f *.c *.cpp gtest-fit && ln -sf ../*.c . && ln -sf ../utest/*.cpp && ln -sf ../utest/gtest-fit .) && +(cd obj && gcovr --gcov-exclude gtest --html --html-details -o ../coverage/st.html) && +(cd obj && rm -f *.c *.cpp gtest-fit) && echo "Coverage report at coverage/st.html" && open coverage/st.html diff --git a/trunk/3rdparty/st-srs/auto/fast.sh b/trunk/3rdparty/st-srs/auto/fast.sh index 8ee39f77b..57f485650 100755 --- a/trunk/3rdparty/st-srs/auto/fast.sh +++ b/trunk/3rdparty/st-srs/auto/fast.sh @@ -22,19 +22,12 @@ ret=$?; if [[ 0 -ne $ret ]]; then echo "Make ST utest fail, ret=$ret"; exit $ret echo "Generating coverage" mkdir -p coverage && -gcovr -r . -e LINUX -e DARWIN -e examples --html --html-details -o coverage/st.html && +(cd obj && rm -f gtest-all.gcda gtest-all.gcno) && +(cd obj && rm -f *.c *.cpp gtest-fit && ln -sf ../*.c . && ln -sf ../utest/*.cpp && ln -sf ../utest/gtest-fit .) && +(cd obj && gcovr --gcov-exclude gtest --html --html-details -o ../coverage/st.html) && +(cd obj && rm -f *.c *.cpp gtest-fit) && echo "Coverage report at coverage/st.html" && open coverage/st.html popd -echo "UTest done, restore $(pwd)" - -cat << END > /dev/stdout - - # CLI For DARWIN - cd $PWD && rm -f ./obj/*.gcda && - make darwin-debug-gcov && ./obj/st_utest && - mkdir -p coverage && gcovr -r . -e LINUX -e DARWIN -e examples --html --html-details -o coverage/st.html && - open coverage/st.html - -END \ No newline at end of file +echo "UTest done, restore $(pwd)" \ No newline at end of file diff --git a/trunk/3rdparty/st-srs/md.h b/trunk/3rdparty/st-srs/md.h index 703e783de..a5c75819d 100644 --- a/trunk/3rdparty/st-srs/md.h +++ b/trunk/3rdparty/st-srs/md.h @@ -182,6 +182,10 @@ #error "ARM/Linux pre-glibc2 not supported yet" #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + #elif defined(__mips64) + /* https://github.com/ossrs/state-threads/issues/21 */ + #define MD_USE_BUILTIN_SETJMP + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0])) #elif defined(__mips__) /* https://github.com/ossrs/state-threads/issues/21 */ #define MD_USE_BUILTIN_SETJMP @@ -191,7 +195,7 @@ #define MD_USE_BUILTIN_SETJMP #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0])) - #elif defined(__loongarch__) + #elif defined(__loongarch64) /* https://github.com/ossrs/state-threads/issues/24 */ #define MD_USE_BUILTIN_SETJMP #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0])) diff --git a/trunk/3rdparty/st-srs/md_linux.S b/trunk/3rdparty/st-srs/md_linux.S index 2c666183f..ac194d074 100644 --- a/trunk/3rdparty/st-srs/md_linux.S +++ b/trunk/3rdparty/st-srs/md_linux.S @@ -433,9 +433,87 @@ +#elif defined(__mips64) + + /****************************************************************/ + /* For MIPS64, see https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MIPS_Architecture_MIPS64_InstructionSet_%20AFP_P_MD00087_06.05.pdf */ + + /* + * Internal __jmp_buf layout + */ + #define JB_SP 0 /* Stack pointer */ + #define JB_RA 11 /* Return address */ + #define JB_GP 1 /* Global pointer */ + #define JB_S0 3 /* S0-S7, Saved temporaries */ + #define JB_S1 4 /* S0-S7, Saved temporaries */ + #define JB_S2 5 /* S0-S7, Saved temporaries */ + #define JB_S3 6 /* S0-S7, Saved temporaries */ + #define JB_S4 7 /* S0-S7, Saved temporaries */ + #define JB_S5 8 /* S0-S7, Saved temporaries */ + #define JB_S6 9 /* S0-S7, Saved temporaries */ + #define JB_S7 10 /* S0-S7, Saved temporaries */ + #define JB_FP 2 /* FP/S8 Frame pointer */ + + .file "md_linux.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ /* The env is $a0, https://en.wikipedia.org/wiki/MIPS_architecture#Calling_conventions */ + .globl _st_md_cxt_save + .type _st_md_cxt_save, %function + .align 2 + _st_md_cxt_save: + sd $sp, 0($a0) /* Save sp to env[0], *(long*)($a0+ 0) =sp */ + sd $ra, 8($a0) /* Save ra to env[1], *(long*)($a0+ 8)=ra, the return address, https://chortle.ccsu.edu/AssemblyTutorial/Chapter-26/ass26_4.html */ + sd $gp, 16($a0) /* Save gp to env[2], *(long*)($a0+16) =gp */ + sd $s0, 24($a0) /* Save s0 to env[3], *(long*)($a0+24)=s0 */ + sd $s1, 32($a0) /* Save s1 to env[4], *(long*)($a0+32)=s1 */ + sd $s2, 40($a0) /* Save s2 to env[5], *(long*)($a0+40)=s2 */ + sd $s3, 48($a0) /* Save s3 to env[6], *(long*)($a0+48)=s3 */ + sd $s4, 56($a0) /* Save s4 to env[7], *(long*)($a0+56)=s4 */ + sd $s5, 64($a0) /* Save s5 to env[8], *(long*)($a0+64)=s5 */ + sd $s6, 72($a0) /* Save s6 to env[9], *(long*)($a0+72)=s6 */ + sd $s7, 80($a0) /* Save s7 to env[10], *(long*)($a0+80)=s7 */ + sd $fp, 88($a0) /* Save fp to env[11], *(long*)($a0+88) =fp */ + li $v0, 0 /* Set return value to 0 */ + jr $ra /* Return */ + + .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: + ld $sp, 0($a0) /* Load sp from env[0], sp=*(long*)($a0+ 0) */ + ld $ra, 8($a0) /* Load sp from env[1], ra=*(long*)($a0+ 8), the saved return address */ + ld $gp, 16($a0) /* Load sp from env[2], gp=*(long*)($a0+16) */ + ld $s0, 24($a0) /* Load sp from env[3], s0=*(long*)($a0+24) */ + ld $s1, 32($a0) /* Load sp from env[4], s1=*(long*)($a0+32) */ + ld $s2, 40($a0) /* Load sp from env[5], s2=*(long*)($a0+40) */ + ld $s3, 48($a0) /* Load sp from env[6], s3=*(long*)($a0+48) */ + ld $s4, 56($a0) /* Load sp from env[7], s4=*(long*)($a0+56) */ + ld $s5, 64($a0) /* Load sp from env[8], s5=*(long*)($a0+64) */ + ld $s6, 72($a0) /* Load sp from env[9], s6=*(long*)($a0+72) */ + ld $s7, 80($a0) /* Load sp from env[10], s7=*(long*)($a0+80) */ + ld $fp, 88($a0) /* Load sp from env[2], fp=*(long*)($a0+88) */ + li $v0, 1 /* Set return value to 1 */ + jr $ra /* Return to the saved return address */ + + .size _st_md_cxt_restore, .-_st_md_cxt_restore + + /****************************************************************/ + + + + + + #elif defined(__mips__) /****************************************************************/ + /* For MIPS32, see https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00565-2B-MIPS32-QRC-01.01.pdf */ /* * Internal __jmp_buf layout @@ -513,7 +591,7 @@ -#elif defined(__loongarch__) +#elif defined(__loongarch64) /****************************************************************/ diff --git a/trunk/3rdparty/st-srs/tools/helloworld/helloworld.c b/trunk/3rdparty/st-srs/tools/helloworld/helloworld.c index 2f3481a5c..0ce1de5de 100644 --- a/trunk/3rdparty/st-srs/tools/helloworld/helloworld.c +++ b/trunk/3rdparty/st-srs/tools/helloworld/helloworld.c @@ -9,7 +9,8 @@ int main(int argc, char** argv) { st_init(); - for (int i = 0; i < 10000; i++) { + int i; + for (i = 0; i < 10000; i++) { printf("#%03d, Hello, state-threads world!\n", i); st_sleep(1); } diff --git a/trunk/3rdparty/st-srs/tools/porting/porting.c b/trunk/3rdparty/st-srs/tools/porting/porting.c index 5032e89e1..de515656d 100644 --- a/trunk/3rdparty/st-srs/tools/porting/porting.c +++ b/trunk/3rdparty/st-srs/tools/porting/porting.c @@ -25,11 +25,23 @@ int main(int argc, char** argv) // https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00565-2B-MIPS32-QRC-01.01.pdf printf("__mips__: %d, __mips: %d, _MIPSEL: %d\n", __mips__, __mips, _MIPSEL); #endif +#ifdef __mips64 + printf("__mips64: %d\n", __mips64); +#endif #ifdef __x86_64__ printf("__x86_64__: %d\n", __x86_64__); #endif -#ifdef __loongarch__ - printf("__loongarch__: %d, __loongarch64 :%d\n", __loongarch__, __loongarch64); +#ifdef __loongarch64 + printf("__loongarch__: %d __loongarch64: %d\n", __loongarch__, __loongarch64); +#endif +#ifdef __riscv + printf("__riscv: %d\n", __riscv); +#endif +#ifdef __arm__ + printf("__arm__: %d\n", __arm__); +#endif +#ifdef __aarch64__ + printf("__aarch64__: %d\n", __aarch64__); #endif printf("\nCompiler specs:\n"); @@ -71,7 +83,52 @@ int foo_return_one_arg1(int r0) } #ifdef __linux__ -#ifdef __mips__ + +#if defined(__riscv) || defined(__arm__) || defined(__aarch64__) +void print_jmpbuf() { +} +#elif __mips64 +void print_jmpbuf() +{ + // https://en.wikipedia.org/wiki/MIPS_architecture#Calling_conventions + register void* ra asm("ra"); + register void* gp asm("gp"); + register void* sp asm("sp"); + register void* fp asm("fp"); + // $s0–$s7 $16–$23 saved temporaries + register void* s0 asm("s0"); + register void* s1 asm("s1"); + register void* s2 asm("s2"); + register void* s3 asm("s3"); + register void* s4 asm("s4"); + register void* s5 asm("s5"); + register void* s6 asm("s6"); + register void* s7 asm("s7"); + + /* + typedef unsigned long long __jmp_buf[13]; + typedef struct __jmp_buf_tag { + __jmp_buf __jmpbuf; + int __mask_was_saved; + __sigset_t __saved_mask; + } jmp_buf[1]; + */ + jmp_buf ctx = {0}; + int r0 = setjmp(ctx); + if (!r0) { + longjmp(ctx, 1); + } + + printf("ra=%p, sp=%p, s0=%p, s1=%p, s2=%p, s3=%p, s4=%p, s5=%p, s6=%p, s7=%p, fp=%p, gp=%p\n", + ra, sp, s0, s1, s2, s3, s4, s5, s6, s7, fp, gp); + + int nn_jb = sizeof(ctx[0].__jmpbuf); + printf("sizeof(jmp_buf)=%d (unsigned long long [%d])\n", nn_jb, nn_jb/8); + + unsigned char* p = (unsigned char*)ctx[0].__jmpbuf; + print_buf(p, nn_jb); +} +#elif __mips__ void print_jmpbuf() { // https://en.wikipedia.org/wiki/MIPS_architecture#Calling_conventions @@ -112,7 +169,7 @@ void print_jmpbuf() unsigned char* p = (unsigned char*)ctx[0].__jb; print_buf(p, nn_jb); } -#elif __loongarch__ +#elif __loongarch64 void print_jmpbuf() { // https://github.com/ossrs/state-threads/issues/24#porting @@ -192,7 +249,8 @@ void print_buf(unsigned char* p, int nn_jb) { printf(" "); - for (int i = 0; i < nn_jb; i++) { + int i; + for (i = 0; i < nn_jb; i++) { printf("0x%02x ", (unsigned char)p[i]); int newline = ((i + 1) % sizeof(void*)); diff --git a/trunk/3rdparty/st-srs/tools/verify/verify.c b/trunk/3rdparty/st-srs/tools/verify/verify.c index 9a4264064..ff91b966a 100644 --- a/trunk/3rdparty/st-srs/tools/verify/verify.c +++ b/trunk/3rdparty/st-srs/tools/verify/verify.c @@ -48,7 +48,7 @@ void verify_jmpbuf() unsigned char* p = (unsigned char*)ctx[0].__jb; print_buf(p, nn_jb); } -#elif __loongarch__ +#elif __loongarch64 void verify_jmpbuf() { // https://github.com/ossrs/state-threads/issues/24#porting diff --git a/trunk/3rdparty/st-srs/utest/Makefile b/trunk/3rdparty/st-srs/utest/Makefile index a294af7b6..2cd304b65 100644 --- a/trunk/3rdparty/st-srs/utest/Makefile +++ b/trunk/3rdparty/st-srs/utest/Makefile @@ -4,7 +4,7 @@ ST_DIR = .. # The main dir of st utest. ST_UTEST = . # The main dir of gtest. -GTEST_DIR = $(ST_UTEST)/gtest +GTEST_DIR = $(ST_UTEST)/gtest-fit/googletest # Flags passed to the C++ compiler. CXXFLAGS += -g -O0 -std=c++11 @@ -45,16 +45,29 @@ $(ST_DIR)/obj/gtest.a : $(ST_DIR)/obj/gtest-all.o ST_UTEST_DEPS = $(ST_DIR)/obj/libst.a # Depends, utest header files -UTEST_DEPS = $(ST_UTEST)/st_utest.hpp +UTEST_DEPS = $(ST_UTEST)/st_utest.hpp Makefile + +# Compile all sources files at current directory. For example: +# (st_utest.cpp st_utest_coroutines.cpp st_utest_tcp.cpp) +SOURCE_FILES = $(shell ls *.cpp) +# +# Convert all source files to object files. For example: +# (st_utest.o st_utest_coroutines.o st_utest_tcp.o) +# https://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_8.html +OBJECTS_FILES = $(patsubst %.cpp,%.o,$(SOURCE_FILES)) +# +# Prefix object files to objects. For example: +# ($(ST_DIR)/obj/st_utest.o $(ST_DIR)/obj/st_utest_coroutines.o $(ST_DIR)/obj/st_utest_tcp.o) +OBJECTS = $(addprefix $(ST_DIR)/obj/,$(OBJECTS_FILES)) # Objects, build each object of utest -$(ST_DIR)/obj/st_utest.o : st_utest.cpp $(ST_UTEST_DEPS) $(UTEST_DEPS) - $(CXX) -c st_utest.cpp -o $@ \ +$(ST_DIR)/obj/%.o : %.cpp $(ST_UTEST_DEPS) $(UTEST_DEPS) + $(CXX) -c $< -o $@ \ $(CXXFLAGS) $(UTEST_FLAGS) \ $(WARNFLAGS) \ -I$(GTEST_DIR)/include -I$(ST_UTEST) -I$(ST_DIR) -I$(ST_DIR)/obj -# generate the utest binary -$(ST_DIR)/obj/st_utest : $(ST_DIR)/obj/st_utest.o $(ST_DIR)/obj/gtest.a $(ST_UTEST_DEPS) +# Generate the utest binary +$(ST_DIR)/obj/st_utest : $(OBJECTS) $(ST_DIR)/obj/gtest.a $(ST_UTEST_DEPS) $(CXX) -o $@ $(CXXFLAGS) $(UTEST_FLAGS) \ -lpthread -ldl $^ diff --git a/trunk/3rdparty/st-srs/utest/st_utest.cpp b/trunk/3rdparty/st-srs/utest/st_utest.cpp index 469b16e95..f679a5dd6 100644 --- a/trunk/3rdparty/st-srs/utest/st_utest.cpp +++ b/trunk/3rdparty/st-srs/utest/st_utest.cpp @@ -6,6 +6,14 @@ #include #include +std::ostream& operator<<(std::ostream& out, const ErrorObject* err) { + if (!err) return out; + if (err->r0_) out << "r0=" << err->r0_; + if (err->errno_) out << ", errno=" << err->errno_; + if (!err->message_.empty()) out << ", msg=" << err->message_; + return out; +} + // We could do something in the main of utest. // Copy from gtest-1.6.0/src/gtest_main.cc GTEST_API_ int main(int argc, char **argv) { @@ -25,7 +33,7 @@ GTEST_API_ int main(int argc, char **argv) { } // basic test and samples. -VOID TEST(SampleTest, FastSampleInt64Test) +VOID TEST(SampleTest, ExampleIntSizeTest) { EXPECT_EQ(1, (int)sizeof(int8_t)); EXPECT_EQ(2, (int)sizeof(int16_t)); @@ -33,107 +41,3 @@ VOID TEST(SampleTest, FastSampleInt64Test) EXPECT_EQ(8, (int)sizeof(int64_t)); } -void* pfn_coroutine(void* /*arg*/) -{ - st_usleep(0); - return NULL; -} - -VOID TEST(SampleTest, StartCoroutine) -{ - st_thread_t trd = st_thread_create(pfn_coroutine, NULL, 1, 0); - EXPECT_TRUE(trd != NULL); - - // Wait for joinable coroutine to quit. - st_thread_join(trd, NULL); -} - -VOID TEST(SampleTest, StartCoroutineX3) -{ - st_thread_t trd0 = st_thread_create(pfn_coroutine, NULL, 1, 0); - st_thread_t trd1 = st_thread_create(pfn_coroutine, NULL, 1, 0); - st_thread_t trd2 = st_thread_create(pfn_coroutine, NULL, 1, 0); - EXPECT_TRUE(trd0 != NULL && trd1 != NULL && trd2 != NULL); - - // Wait for joinable coroutine to quit. - st_thread_join(trd1, NULL); - st_thread_join(trd2, NULL); - st_thread_join(trd0, NULL); -} - -void* pfn_coroutine_add(void* arg) -{ - int v = 0; - int* pi = (int*)arg; - - // Load the change of arg. - while (v != *pi) { - v = *pi; - st_usleep(0); - } - - // Add with const. - v += 100; - *pi = v; - - return NULL; -} - -VOID TEST(SampleTest, StartCoroutineAdd) -{ - int v = 0; - st_thread_t trd = st_thread_create(pfn_coroutine_add, &v, 1, 0); - EXPECT_TRUE(trd != NULL); - - // Wait for joinable coroutine to quit. - st_thread_join(trd, NULL); - - EXPECT_EQ(100, v); -} - -VOID TEST(SampleTest, StartCoroutineAddX3) -{ - int v = 0; - st_thread_t trd0 = st_thread_create(pfn_coroutine_add, &v, 1, 0); - st_thread_t trd1 = st_thread_create(pfn_coroutine_add, &v, 1, 0); - st_thread_t trd2 = st_thread_create(pfn_coroutine_add, &v, 1, 0); - EXPECT_TRUE(trd0 != NULL && trd1 != NULL && trd2 != NULL); - - // Wait for joinable coroutine to quit. - st_thread_join(trd0, NULL); - st_thread_join(trd1, NULL); - st_thread_join(trd2, NULL); - - EXPECT_EQ(300, v); -} - -int pfn_coroutine_params_x4(int a, int b, int c, int d) -{ - int e = 0; - - st_usleep(0); - - e += a + b + c + d; - e += 100; - return e; -} - -void* pfn_coroutine_params(void* arg) -{ - int r0 = pfn_coroutine_params_x4(1, 2, 3, 4); - *(int*)arg = r0; - return NULL; -} - -VOID TEST(SampleTest, StartCoroutineParams) -{ - int r0 = 0; - st_thread_t trd = st_thread_create(pfn_coroutine_params, &r0, 1, 0); - EXPECT_TRUE(trd != NULL); - - // Wait for joinable coroutine to quit. - st_thread_join(trd, NULL); - - EXPECT_EQ(110, r0); -} - diff --git a/trunk/3rdparty/st-srs/utest/st_utest.hpp b/trunk/3rdparty/st-srs/utest/st_utest.hpp index 8d39a06b2..b4825a29d 100644 --- a/trunk/3rdparty/st-srs/utest/st_utest.hpp +++ b/trunk/3rdparty/st-srs/utest/st_utest.hpp @@ -10,7 +10,118 @@ // @see https://stackoverflow.com/questions/47839718/sstream-redeclared-with-public-access-compiler-error #include +#include +#include + #define VOID +// Close the fd automatically. +#define StFdCleanup(fd, stfd) impl__StFdCleanup _ST_free_##fd(&fd, &stfd) +#define StStfdCleanup(stfd) impl__StFdCleanup _ST_free_##stfd(NULL, &stfd) +class impl__StFdCleanup { + int* fd_; + st_netfd_t* stfd_; +public: + impl__StFdCleanup(int* fd, st_netfd_t* stfd) : fd_(fd), stfd_(stfd) { + } + virtual ~impl__StFdCleanup() { + if (stfd_ && *stfd_) { + st_netfd_close(*stfd_); + } else if (fd_ && *fd_ > 0) { + ::close(*fd_); + } + } +}; + +// For coroutine function to return with error object. +struct ErrorObject { + int r0_; + int errno_; + std::string message_; + + ErrorObject(int r0, std::string message) : r0_(r0), errno_(errno), message_(message) { + } +}; +extern std::ostream& operator<<(std::ostream& out, const ErrorObject* err); +#define ST_ASSERT_ERROR(error, r0, message) if (error) return new ErrorObject(r0, message) +#define ST_COROUTINE_JOIN(trd, r0) ErrorObject* r0 = NULL; SrsAutoFree(ErrorObject, r0); if (trd) st_thread_join(trd, (void**)&r0) +#define ST_EXPECT_SUCCESS(r0) EXPECT_TRUE(!r0) << r0 +#define ST_EXPECT_FAILED(r0) EXPECT_TRUE(r0) << r0 + +#include + +// To free the instance in the current scope, for instance, MyClass* ptr, +// which is a ptr and this class will: +// 1. free the ptr. +// 2. set ptr to NULL. +// +// Usage: +// MyClass* po = new MyClass(); +// // ...... use po +// SrsAutoFree(MyClass, po); +// +// Usage for array: +// MyClass** pa = new MyClass*[size]; +// // ....... use pa +// SrsAutoFreeA(MyClass*, pa); +// +// @remark the MyClass can be basic type, for instance, SrsAutoFreeA(char, pstr), +// where the char* pstr = new char[size]. +// To delete object. +#define SrsAutoFree(className, instance) \ + impl_SrsAutoFree _auto_free_##instance(&instance, false, false, NULL) +// To delete array. +#define SrsAutoFreeA(className, instance) \ + impl_SrsAutoFree _auto_free_array_##instance(&instance, true, false, NULL) +// Use free instead of delete. +#define SrsAutoFreeF(className, instance) \ + impl_SrsAutoFree _auto_free_##instance(&instance, false, true, NULL) +// Use hook instead of delete. +#define SrsAutoFreeH(className, instance, hook) \ + impl_SrsAutoFree _auto_free_##instance(&instance, false, false, hook) +// The template implementation. +template +class impl_SrsAutoFree +{ +private: + T** ptr; + bool is_array; + bool _use_free; + void (*_hook)(T*); +public: + // If use_free, use free(void*) to release the p. + // If specified hook, use hook(p) to release it. + // Use delete to release p, or delete[] if p is an array. + impl_SrsAutoFree(T** p, bool array, bool use_free, void (*hook)(T*)) { + ptr = p; + is_array = array; + _use_free = use_free; + _hook = hook; + } + + virtual ~impl_SrsAutoFree() { + if (ptr == NULL || *ptr == NULL) { + return; + } + + if (_use_free) { + free(*ptr); + } else if (_hook) { + _hook(*ptr); + } else { + if (is_array) { + delete[] *ptr; + } else { + delete *ptr; + } + } + + *ptr = NULL; + } +}; + +// The time unit in ms, for example 100 * SRS_UTIME_MILLISECONDS means 100ms. +#define SRS_UTIME_MILLISECONDS 1000 + #endif diff --git a/trunk/3rdparty/st-srs/utest/st_utest_coroutines.cpp b/trunk/3rdparty/st-srs/utest/st_utest_coroutines.cpp new file mode 100644 index 000000000..1ac022afb --- /dev/null +++ b/trunk/3rdparty/st-srs/utest/st_utest_coroutines.cpp @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2021 Winlin */ + +#include + +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// The utest for empty coroutine. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void* coroutine(void* /*arg*/) +{ + st_usleep(0); + return NULL; +} + +VOID TEST(CoroutineTest, StartCoroutine) +{ + st_thread_t trd = st_thread_create(coroutine, NULL, 1, 0); + EXPECT_TRUE(trd != NULL); + + // Wait for joinable coroutine to quit. + st_thread_join(trd, NULL); +} + +VOID TEST(CoroutineTest, StartCoroutineX3) +{ + st_thread_t trd0 = st_thread_create(coroutine, NULL, 1, 0); + st_thread_t trd1 = st_thread_create(coroutine, NULL, 1, 0); + st_thread_t trd2 = st_thread_create(coroutine, NULL, 1, 0); + EXPECT_TRUE(trd0 != NULL && trd1 != NULL && trd2 != NULL); + + // Wait for joinable coroutine to quit. + st_thread_join(trd1, NULL); + st_thread_join(trd2, NULL); + st_thread_join(trd0, NULL); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// The utest for adding coroutine. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void* coroutine_add(void* arg) +{ + int v = 0; + int* pi = (int*)arg; + + // Load the change of arg. + while (v != *pi) { + v = *pi; + st_usleep(0); + } + + // Add with const. + v += 100; + *pi = v; + + return NULL; +} + +VOID TEST(CoroutineTest, StartCoroutineAdd) +{ + int v = 0; + st_thread_t trd = st_thread_create(coroutine_add, &v, 1, 0); + EXPECT_TRUE(trd != NULL); + + // Wait for joinable coroutine to quit. + st_thread_join(trd, NULL); + + EXPECT_EQ(100, v); +} + +VOID TEST(CoroutineTest, StartCoroutineAddX3) +{ + int v = 0; + st_thread_t trd0 = st_thread_create(coroutine_add, &v, 1, 0); + st_thread_t trd1 = st_thread_create(coroutine_add, &v, 1, 0); + st_thread_t trd2 = st_thread_create(coroutine_add, &v, 1, 0); + EXPECT_TRUE(trd0 != NULL && trd1 != NULL && trd2 != NULL); + + // Wait for joinable coroutine to quit. + st_thread_join(trd0, NULL); + st_thread_join(trd1, NULL); + st_thread_join(trd2, NULL); + + EXPECT_EQ(300, v); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// The utest for output params coroutine. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +int coroutine_params_x4(int a, int b, int c, int d) +{ + int e = 0; + + st_usleep(0); + + e += a + b + c + d; + e += 100; + return e; +} + +void* coroutine_params(void* arg) +{ + int r0 = coroutine_params_x4(1, 2, 3, 4); + *(int*)arg = r0; + return NULL; +} + +VOID TEST(CoroutineTest, StartCoroutineParams) +{ + int r0 = 0; + st_thread_t trd = st_thread_create(coroutine_params, &r0, 1, 0); + EXPECT_TRUE(trd != NULL); + + // Wait for joinable coroutine to quit. + st_thread_join(trd, NULL); + + EXPECT_EQ(110, r0); +} + diff --git a/trunk/3rdparty/st-srs/utest/st_utest_tcp.cpp b/trunk/3rdparty/st-srs/utest/st_utest_tcp.cpp new file mode 100644 index 000000000..87168fe1a --- /dev/null +++ b/trunk/3rdparty/st-srs/utest/st_utest_tcp.cpp @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2021 Winlin */ + +#include + +#include +#include + +#include +#include +#include + +#define ST_UTEST_PORT 26878 +#define ST_UTEST_TIMEOUT (100 * SRS_UTIME_MILLISECONDS) + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// The utest for ping-pong TCP server coroutine. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void* tcp_server(void* /*arg*/) +{ + int fd = -1; + st_netfd_t stfd = NULL; + StFdCleanup(fd, stfd); + + fd = socket(AF_INET, SOCK_STREAM, 0); + ST_ASSERT_ERROR(fd == -1, fd, "Create socket"); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(ST_UTEST_PORT); + + int v = 1; + int r0 = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(int)); + ST_ASSERT_ERROR(r0, r0, "Set SO_REUSEADDR"); + + r0 = ::bind(fd, (const sockaddr*)&addr, sizeof(addr)); + ST_ASSERT_ERROR(r0, r0, "Bind socket"); + + r0 = ::listen(fd, 10); + ST_ASSERT_ERROR(r0, r0, "Listen socket"); + + stfd = st_netfd_open_socket(fd); + ST_ASSERT_ERROR(!stfd, fd, "Open ST socket"); + + st_netfd_t client = NULL; + StStfdCleanup(client); + + client = st_accept(stfd, NULL, NULL, ST_UTEST_TIMEOUT); + ST_ASSERT_ERROR(!client, fd, "Accept client"); + + return NULL; +} + +void* tcp_client(void* /*arg*/) +{ + int fd = -1; + st_netfd_t stfd = NULL; + StFdCleanup(fd, stfd); + + fd = socket(AF_INET, SOCK_STREAM, 0); + ST_ASSERT_ERROR(fd == -1, fd, "Create socket"); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = htons(ST_UTEST_PORT); + + stfd = st_netfd_open_socket(fd); + ST_ASSERT_ERROR(!stfd, fd, "Open ST socket"); + + int r0 = st_connect(stfd, (const sockaddr*)&addr, sizeof(addr), ST_UTEST_TIMEOUT); + ST_ASSERT_ERROR(r0, r0, "Connect to server"); + + return NULL; +} + +VOID TEST(TcpTest, TcpConnection) +{ + st_thread_t svr = st_thread_create(tcp_server, NULL, 1, 0); + EXPECT_TRUE(svr != NULL); + + st_thread_t client = st_thread_create(tcp_client, NULL, 1, 0); + EXPECT_TRUE(client != NULL); + + ST_COROUTINE_JOIN(svr, r0); + ST_COROUTINE_JOIN(client, r1); + + ST_EXPECT_SUCCESS(r0); + ST_EXPECT_SUCCESS(r1); +} + diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 8dab0a322..804eb8419 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -6,6 +6,7 @@ The changelog for SRS. ## SRS 5.0 Changelog +* v5.0, 2022-08-06, Support MIPS 64bits for loongson 3A4000/3B3000. v5.0.34 * v5.0, 2022-06-29, Support multiple threads by thread pool. v5.0.32 * v5.0, 2022-06-28, ST: Support thread-local for multiple threads. v5.0.31 * v5.0, 2022-06-17, Merge [#3010](https://github.com/ossrs/srs/pull/3010): SRT: Support Coroutine Native SRT over ST. (#3010). v5.0.30 diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp index 67b4b3a3a..eccfebd33 100644 --- a/trunk/src/core/srs_core_version5.hpp +++ b/trunk/src/core/srs_core_version5.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 5 #define VERSION_MINOR 0 -#define VERSION_REVISION 33 +#define VERSION_REVISION 34 #endif