diff --git a/3rdparty/st-srs/Dockerfile.cov b/3rdparty/st-srs/Dockerfile.cov new file mode 100644 index 0000000..5bd8c44 --- /dev/null +++ b/3rdparty/st-srs/Dockerfile.cov @@ -0,0 +1,12 @@ +FROM ossrs/srs:dev-gcc7 + +# Install depends tools. +RUN yum install -y gcc make gcc-c++ patch unzip perl git + +# Build and install SRS. +COPY . /st +WORKDIR /st + +# Note that we must enable the gcc7 or link failed. +RUN scl enable devtoolset-7 -- make linux-debug-gcov + diff --git a/3rdparty/st-srs/Dockerfile.test b/3rdparty/st-srs/Dockerfile.test new file mode 100644 index 0000000..776c8a9 --- /dev/null +++ b/3rdparty/st-srs/Dockerfile.test @@ -0,0 +1,15 @@ +FROM ossrs/srs:dev-gcc7 + +# Install depends tools. +RUN yum install -y gcc make gcc-c++ patch unzip perl git + +# Build and install SRS. +COPY . /st +WORKDIR /st + +# Note that we must enable the gcc7 or link failed. +RUN scl enable devtoolset-7 -- make linux-debug-utest + +# Run utest +RUN ./obj/st_utest + diff --git a/3rdparty/st-srs/LICENSE b/3rdparty/st-srs/LICENSE new file mode 100644 index 0000000..acf66c7 --- /dev/null +++ b/3rdparty/st-srs/LICENSE @@ -0,0 +1,30 @@ +The state-threads is provided under the terms of the MPL-1.1 or the +GPL-2.0-or-later. For more information about these licenses, please see +https://spdx.org/licenses/MPL-1.1.html and +https://spdx.org/licenses/GPL-2.0-or-later.html + +Individual files contain the following tag instead of the full license text. + + SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later + +New source code and all source code in the "tools" and "utest" directory +is distributed under the MIT style license. + + SPDX-License-Identifier: MIT + +This enables machine processing of license information based on the SPDX +License Identifiers that are here available: http://spdx.org/licenses/ + +--------------------------------------------------------------------------- +Note: https://github.com/ossrs/state-threads/blob/srs/README#L68 + +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. + diff --git a/3rdparty/st-srs/Makefile b/3rdparty/st-srs/Makefile index 934914a..14d4658 100644 --- a/3rdparty/st-srs/Makefile +++ b/3rdparty/st-srs/Makefile @@ -38,20 +38,8 @@ VERSION = 1.9 ########################## # Supported OSes: # -#OS = AIX -#OS = CYGWIN #OS = DARWIN -#OS = FREEBSD -#OS = HPUX -#OS = HPUX_64 -#OS = IRIX -#OS = IRIX_64 #OS = LINUX -#OS = NETBSD -#OS = OPENBSD -#OS = OSF1 -#OS = SOLARIS -#OS = SOLARIS_64 # Please see the "Other possible defines" section below for # possible compilation options. @@ -62,6 +50,7 @@ AR = ar LD = ld RANLIB = ranlib LN = ln +STATIC_ONLY = yes SHELL = /bin/sh ECHO = /bin/echo @@ -69,10 +58,21 @@ ECHO = /bin/echo BUILD = DBG TARGETDIR = $(OS)_$(shell uname -r)_$(BUILD) +# For Cygwin, it pass a default OS env, we ignore it. +ifeq ($(OS), Windows_NT) +OS = +endif + +# For cygwin/windows, the 'uname -r' generate path with parentheses, +# which cause the make fails, so we use 'uname -s' instead. +ifeq ($(OS), CYGWIN64) +TARGETDIR = $(OS)_$(shell uname -s)_$(BUILD) +endif + DEFINES = -D$(OS) CFLAGS = SFLAGS = -ARFLAGS = -rv +ARFLAGS = -r LNFLAGS = -s DSO_SUFFIX = so @@ -83,146 +83,47 @@ DESC = st.pc # Platform section. # Possible targets: -TARGETS = aix-debug aix-optimized \ - cygwin-debug cygwin-optimized \ - darwin-debug darwin-optimized \ - freebsd-debug freebsd-optimized \ - hpux-debug hpux-optimized \ - hpux-64-debug hpux-64-optimized \ - irix-n32-debug irix-n32-optimized \ - irix-64-debug irix-64-optimized \ +TARGETS = darwin-debug darwin-optimized \ linux-debug linux-optimized \ - netbsd-debug netbsd-optimized \ - openbsd-debug openbsd-optimized \ - osf1-debug osf1-optimized \ - solaris-debug solaris-optimized \ - solaris-64-debug solaris-64-optimized + cygwin64-debug + +UTEST_TARGETS = darwin-debug-utest linux-debug-utest \ + darwin-debug-gcov linux-debug-gcov \ + cygwin64-debug-utest # # Platform specifics # -ifeq ($(OS), AIX) -AIX_VERSION = $(shell uname -v).$(shell uname -r) -TARGETDIR = $(OS)_$(AIX_VERSION)_$(BUILD) -CC = xlC -STATIC_ONLY = yes -ifeq ($(BUILD), OPT) -OTHER_FLAGS = -w -endif -ifneq ($(filter-out 4.1 4.2, $(AIX_VERSION)),) -DEFINES += -DMD_HAVE_SOCKLEN_T -endif -endif - -ifeq ($(OS), CYGWIN) -TARGETDIR = $(OS)_$(BUILD) -CC = gcc -LD = gcc -DSO_SUFFIX = dll -SLIBRARY = $(TARGETDIR)/libst.dll.a -DLIBRARY = $(TARGETDIR)/libst.dll -DEF_FILE = $(TARGETDIR)/libst.def -LDFLAGS = libst.def -shared --enable-auto-image-base -Wl,--output-def,$(DEF_FILE),--out-implib,$(SLIBRARY) -OTHER_FLAGS = -Wall -endif - ifeq ($(OS), DARWIN) EXTRA_OBJS = $(TARGETDIR)/md_darwin.o LD = cc SFLAGS = -fPIC -fno-common DSO_SUFFIX = dylib -RELEASE = $(shell uname -r | cut -d. -f1) -PPC = $(shell test $(RELEASE) -le 9 && echo yes) -INTEL = $(shell test $(RELEASE) -ge 9 && echo yes) -ifeq ($(PPC), yes) -CFLAGS += -arch ppc -LDFLAGS += -arch ppc -endif -ifeq ($(INTEL), yes) -CFLAGS += -arch x86_64 -LDFLAGS += -arch x86_64 -endif +CPU_ARCHS = $(shell g++ -dM -E - $@ @@ -373,18 +270,20 @@ $(HEADER): public.h rm -f $@ cp public.h $@ -$(TARGETDIR)/md.o: md.S +$(TARGETDIR)/md_linux.o: md_linux.S + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGETDIR)/md_linux2.o: md_linux2.S $(CC) $(CFLAGS) -c $< -o $@ $(TARGETDIR)/md_darwin.o: md_darwin.S $(CC) $(CFLAGS) -c $< -o $@ -$(TARGETDIR)/%.o: %.c common.h md.h +$(TARGETDIR)/md_cygwin64.o: md_cygwin64.S $(CC) $(CFLAGS) -c $< -o $@ -examples: $(SLIBRARY) - @echo Making $@ - @cd $@; $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" OS="$(OS)" TARGETDIR="$(TARGETDIR)" +$(TARGETDIR)/%.o: %.c common.h md.h + $(CC) $(CFLAGS) -c $< -o $@ clean: rm -rf *_OPT *_DBG obj st.pc @@ -405,80 +304,40 @@ endif ########################## # Target rules: -default-debug: - . ./osguess.sh; $(MAKE) OS="$$OS" BUILD="DBG" -default default-optimized: - . ./osguess.sh; $(MAKE) OS="$$OS" BUILD="OPT" - -aix-debug: - $(MAKE) OS="AIX" BUILD="DBG" -aix-optimized: - $(MAKE) OS="AIX" BUILD="OPT" - -cygwin-debug: - $(MAKE) OS="CYGWIN" BUILD="DBG" -cygwin-optimized: - $(MAKE) OS="CYGWIN" BUILD="OPT" - darwin-debug: $(MAKE) OS="DARWIN" BUILD="DBG" darwin-optimized: $(MAKE) OS="DARWIN" BUILD="OPT" -freebsd-debug: - $(MAKE) OS="FREEBSD" BUILD="DBG" -freebsd-optimized: - $(MAKE) OS="FREEBSD" BUILD="OPT" - -hpux-debug: - $(MAKE) OS="HPUX" BUILD="DBG" -hpux-optimized: - $(MAKE) OS="HPUX" BUILD="OPT" -hpux-64-debug: - $(MAKE) OS="HPUX_64" BUILD="DBG" -hpux-64-optimized: - $(MAKE) OS="HPUX_64" BUILD="OPT" - -irix-n32-debug: - $(MAKE) OS="IRIX" BUILD="DBG" -irix-n32-optimized: - $(MAKE) OS="IRIX" BUILD="OPT" -irix-64-debug: - $(MAKE) OS="IRIX_64" BUILD="DBG" -irix-64-optimized: - $(MAKE) OS="IRIX_64" BUILD="OPT" - linux-debug: $(MAKE) OS="LINUX" BUILD="DBG" linux-optimized: $(MAKE) OS="LINUX" BUILD="OPT" -# compatibility -linux-ia64-debug: linux-debug -linux-ia64-optimized: linux-optimized - -netbsd-debug: - $(MAKE) OS="NETBSD" BUILD="DBG" -netbsd-optimized: - $(MAKE) OS="NETBSD" BUILD="OPT" - -openbsd-debug: - $(MAKE) OS="OPENBSD" BUILD="DBG" -openbsd-optimized: - $(MAKE) OS="OPENBSD" BUILD="OPT" - -osf1-debug: - $(MAKE) OS="OSF1" BUILD="DBG" -osf1-optimized: - $(MAKE) OS="OSF1" BUILD="OPT" - -solaris-debug: - $(MAKE) OS="SOLARIS" BUILD="DBG" -solaris-optimized: - $(MAKE) OS="SOLARIS" BUILD="OPT" -solaris-64-debug: - $(MAKE) OS="SOLARIS_64" BUILD="DBG" -solaris-64-optimized: - $(MAKE) OS="SOLARIS_64" BUILD="OPT" + +cygwin64-debug: + $(MAKE) OS="CYGWIN64" BUILD="DBG" + +darwin-debug-utest: + @echo "Build utest for state-threads" + $(MAKE) OS="DARWIN" BUILD="DBG" + cd utest && $(MAKE) +linux-debug-utest: + @echo "Build utest for state-threads" + $(MAKE) OS="LINUX" BUILD="DBG" + cd utest && $(MAKE) +cygwin64-debug-utest: + @echo "Build utest for state-threads" + $(MAKE) OS="CYGWIN64" BUILD="DBG" + cd utest && $(MAKE) UTEST_FLAGS="-std=gnu++0x" # @see https://www.codenong.com/18784112/ + +darwin-debug-gcov: + @echo "Build utest with gcov for state-threads" + $(MAKE) OS="DARWIN" BUILD="DBG" UTEST_FLAGS="-fprofile-arcs -ftest-coverage" STATIC_ONLY=yes + cd utest && $(MAKE) UTEST_FLAGS="-fprofile-arcs -ftest-coverage" +linux-debug-gcov: + @echo "Build utest with gcov for state-threads" + $(MAKE) OS="LINUX" BUILD="DBG" UTEST_FLAGS="-fprofile-arcs -ftest-coverage" STATIC_ONLY=yes + cd utest && $(MAKE) UTEST_FLAGS="-fprofile-arcs -ftest-coverage" ########################## diff --git a/3rdparty/st-srs/README.md b/3rdparty/st-srs/README.md index b05a6d8..e1959a7 100644 --- a/3rdparty/st-srs/README.md +++ b/3rdparty/st-srs/README.md @@ -1,7 +1,9 @@ # 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) +[![](https://github.com/ossrs/state-threads/actions/workflows/test.yml/badge.svg?branch=srs)](https://github.com/ossrs/state-threads/actions?query=workflow%3ATest+branch%3Asrs) +[![](https://codecov.io/gh/ossrs/state-threads/branch/srs/graph/badge.svg)](https://codecov.io/gh/ossrs/state-threads/branch/srs) +[![](https://cloud.githubusercontent.com/assets/2777660/22814959/c51cbe72-ef92-11e6-81cc-32b657b285d5.png)](https://ossrs.net/lts/zh-cn/contact) Fork from http://sourceforge.net/projects/state-threads, patched for [SRS](https://github.com/ossrs/srs/tree/2.0release). @@ -9,60 +11,35 @@ Fork from http://sourceforge.net/projects/state-threads, patched for [SRS](https 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). -- [x] Support OSX for Apple Darwin, macOS, [#11](https://github.com/ossrs/state-threads/issues/11). -- [x] Support sendmmsg for UDP, [#12](https://github.com/ossrs/state-threads/issues/12). - -## Docs +## LICENSE -* 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 +[state-threads](https://github.com/ossrs/state-threads/blob/srs/README#L68) is licenced under [MPL or GPLv2](https://ossrs.net/lts/zh-cn/license#state-threads). -## Analysis - -* About setjmp and longjmp, read [setjmp](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-setjmp.jpg). -* About the stack structure, read [stack](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-stack.jpg) -* About asm code comments, read [#91d530e](https://github.com/ossrs/state-threads/commit/91d530e#diff-ed9428b14ff6afda0e9ab04cc91d4445R25). -* About the scheduler, read [#13-scheduler](https://github.com/ossrs/state-threads/issues/13#issuecomment-616025527). -* About the IO event system, read [#13-IO](https://github.com/ossrs/state-threads/issues/13#issuecomment-616096568). - -## Usage +## Linux: Usage Get code: -``` -git clone https://github.com/ossrs/state-threads.git st-1.9 && -git checkout -b srs origin/srs +```bash +git clone -b srs https://github.com/ossrs/state-threads.git ``` For Linux: -``` -make linux-debug EXTRA_CFLAGS="-DMD_HAVE_EPOLL" +```bash +make linux-debug ``` -For OSX: +For Linux aarch64, which fail with `Unknown CPU architecture`: +```bash +make linux-debug EXTRA_CFLAGS="-D__aarch64__" ``` -make darwin-debug EXTRA_CFLAGS="-DMD_HAVE_KQUEUE" -``` + +> Note: For more CPU architectures, please see [#22](https://github.com/ossrs/state-threads/issues/22) Linux with valgrind: -``` +```bash make linux-debug EXTRA_CFLAGS="-DMD_VALGRIND" ``` @@ -70,17 +47,85 @@ make linux-debug EXTRA_CFLAGS="-DMD_VALGRIND" Linux with valgrind and epoll: -``` +```bash make linux-debug EXTRA_CFLAGS="-DMD_HAVE_EPOLL -DMD_VALGRIND" ``` -For OSX, user must specifies the valgrind header files: +## Mac: Usage +Get code: + +```bash +git clone -b srs https://github.com/ossrs/state-threads.git ``` + +For OSX: + +```bash +make darwin-debug +``` + +For OSX, user must specifies the valgrind header files: + +```bash 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. +> Remark: M1 is unsupported by ST, please use docker to run, please read [SRS#2747](https://github.com/ossrs/srs/issues/2747). + +## Windows: Usage + +Get code: + +```bash +git clone -b srs https://github.com/ossrs/state-threads.git +``` + +For Cygwin(Windows): + +``` +make cygwin64-debug +``` + +> Remark: Windows native build is unsupported right now. + +## Branch SRS + +The branch [srs](https://github.com/ossrs/state-threads/tree/srs) was patched and refined: + +- [x] ARM: Patch [st.arm.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/1.st.arm.patch), for ARM. +- [x] OSX: Patch [st.osx.kqueue.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/3.st.osx.kqueue.patch), for osx. +- [x] Linux: Patch [st.disable.examples.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/4.st.disable.examples.patch), for ubuntu. +- [x] System: [Refine TAB of code](https://github.com/ossrs/state-threads/compare/c2001d30ca58f55d72a6cc6b9b6c70391eaf14db...d2101b26988b0e0db0aabc53ddf452068c1e2cbc). +- [x] ARM: 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] Valgrind: Merge from [toffaletti](https://github.com/toffaletti/state-threads), support [valgrind](https://github.com/ossrs/state-threads/issues/2) for ST. +- [x] OSX: 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] ARM: Support macro `MD_ST_NO_ASM` to disable ASM, [#8](https://github.com/ossrs/state-threads/issues/8). +- [x] AARCH64: 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). +- [x] OSX: Support OSX for Apple Darwin, macOS, [#11](https://github.com/ossrs/state-threads/issues/11). +- [x] System: Refine performance for sleep or epoll_wait(0), [#17](https://github.com/ossrs/state-threads/issues/17). +- [x] System: Support utest by gtest and coverage by gcov/gocvr. +- [x] System: Only support for Linux and Darwin. [#19](https://github.com/ossrs/state-threads/issues/19), [srs#2188](https://github.com/ossrs/srs/issues/2188). +- [x] System: Improve the performance of timer. [9fe8cfe5b](https://github.com/ossrs/state-threads/commit/9fe8cfe5b1c9741a2e671a46215184f267fba400), [7879c2b](https://github.com/ossrs/state-threads/commit/7879c2b), [387cddb](https://github.com/ossrs/state-threads/commit/387cddb) +- [x] Windows: Support Windows 64bits. [#20](https://github.com/ossrs/state-threads/issues/20). +- [x] MIPS: Support Linux/MIPS for OpenWRT, [#21](https://github.com/ossrs/state-threads/issues/21). +- [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). +- [x] AppleM1: Support Apple Silicon M1(aarch64), [#30](https://github.com/ossrs/state-threads/issues/30). +- [x] IDE: Support CLion for debugging and learning. +- [x] Define and use a new jmpbuf, because the structure is different. +- [x] Check capability for backtrack. +- [x] Support set specifics for any thread. +- [x] Support st_destroy to free resources for asan. +- [x] Support free the stack, [#38](https://github.com/ossrs/state-threads/issues/38). +- [ ] System: Support sendmmsg for UDP, [#12](https://github.com/ossrs/state-threads/issues/12). + +## GDB Tools + +- [x] Support [nn_coroutines](https://github.com/ossrs/state-threads/issues/15#issuecomment-742218041), show number of coroutines. +- [x] Support [show_coroutines](https://github.com/ossrs/state-threads/issues/15#issuecomment-742218612), show all coroutines and caller function. ## Valgrind @@ -95,4 +140,107 @@ Important cli options: 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. +## Linux: UTest + +> Note: We use [Google test](https://github.com/google/googletest/releases/tag/release-1.11.0) in `utest/gtest-fit`. + +To make ST with utest and run it: + +```bash +make linux-debug-utest && ./obj/st_utest +``` + +Note that the gcc(4.8) of CentOS is too old, please use docker(`ossrs/srs:dev-gcc7`) to run: + +```bash +docker run --rm -it -v $(pwd):/state-threads -w /state-threads \ + registry.cn-hangzhou.aliyuncs.com/ossrs/srs:dev-gcc7 \ + bash -c 'make linux-debug-utest && ./obj/st_utest' +``` + +## Mac: UTest + +> Note: We use [Google test](https://github.com/google/googletest/releases/tag/release-1.11.0) in `utest/gtest-fit`. + +To make ST with utest and run it: + +```bash +make darwin-debug-utest && ./obj/st_utest +``` + +## Linux: Coverage + +> Note: We use [Google test](https://github.com/google/googletest/releases/tag/release-1.11.0) in `utest/gtest-fit`. + +To make ST with utest and run it: + +```bash +make linux-debug-gcov && ./obj/st_utest +``` + +Note that the gcc(4.8) of CentOS is too old, please use docker(`ossrs/srs:dev-gcc7`) to run: + +```bash +docker run --rm -it -v $(pwd):/state-threads -w /state-threads \ + registry.cn-hangzhou.aliyuncs.com/ossrs/srs:dev-gcc7 \ + bash -c 'make linux-debug-gcov && ./obj/st_utest' +``` + +Then, install [gcovr](https://gcovr.com/en/stable/guide.html) for coverage: + +```bash +yum install -y python2-pip && +pip install lxml && pip install gcovr +``` + +Finally, run test and get the report: + +```bash +bash auto/coverage.sh +``` + +## Mac: Coverage + +> Note: We use [Google test](https://github.com/google/googletest/releases/tag/release-1.11.0) in `utest/gtest-fit`. + +To make ST with utest and run it: + +```bash +make darwin-debug-gcov && ./obj/st_utest +``` + +Then, install [gcovr](https://gcovr.com/en/stable/guide.html) for coverage: + +```bash +pip install gcovr +``` + +Finally, run test and get the report: + +```bash +bash auto/coverage.sh +``` + +## Docs & Analysis + +* 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 + +* [How to porting ST to other OS/CPU?](https://github.com/ossrs/state-threads/issues/22) +* About setjmp and longjmp, read [setjmp](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-setjmp.jpg). +* About the stack structure, read [stack](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-stack.jpg) +* About asm code comments, read [#91d530e](https://github.com/ossrs/state-threads/commit/91d530e#diff-ed9428b14ff6afda0e9ab04cc91d4445R25). +* About the scheduler, read [#13-scheduler](https://github.com/ossrs/state-threads/issues/13#issuecomment-616025527). +* About the IO event system, read [#13-IO](https://github.com/ossrs/state-threads/issues/13#issuecomment-616096568). +* Code analysis, please read [#15](https://github.com/ossrs/state-threads/issues/15). + +## CLion + +Use [CLion](https://www.jetbrains.com/clion/) to open directory state-threads. + +Then, open `ide/st_clion/CMakeLists.txt` and click `Load CMake project`. + +Finally, select a configuration to run or debug. + Winlin 2016 diff --git a/3rdparty/st-srs/auto/codecov.sh b/3rdparty/st-srs/auto/codecov.sh new file mode 100755 index 0000000..1200438 --- /dev/null +++ b/3rdparty/st-srs/auto/codecov.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# 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 + +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://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="$CODECOV_TOKEN" && +bash <(curl -s https://codecov.io/bash) $CODECOV_ARGS -f '!*gtest*' -f '!*c++*' && +echo "Done" && exit 0 + diff --git a/3rdparty/st-srs/auto/coverage.sh b/3rdparty/st-srs/auto/coverage.sh new file mode 100755 index 0000000..0aa220e --- /dev/null +++ b/3rdparty/st-srs/auto/coverage.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +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" + exit -1 +fi + +IS_LINUX=yes +uname -s|grep Darwin >/dev/null && IS_DARWIN=yes && IS_LINUX=no +echo "IS_LINUX: $IS_LINUX, IS_DARWIN: $IS_DARWIN" + +echo "Build and run utest" +if [[ $IS_DARWIN == yes ]]; then + make clean && make darwin-debug-gcov && ./obj/st_utest +else + make clean && make linux-debug-gcov && ./obj/st_utest +fi + +echo "Generating coverage" +mkdir -p coverage && +(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/3rdparty/st-srs/auto/fast.sh b/3rdparty/st-srs/auto/fast.sh new file mode 100755 index 0000000..57f4856 --- /dev/null +++ b/3rdparty/st-srs/auto/fast.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +PWD=$(cd `dirname $0`/.. && pwd) + +pushd $PWD +echo "Run UTest in $(pwd)" + +IS_LINUX=yes +uname -s|grep Darwin >/dev/null && IS_DARWIN=yes && IS_LINUX=no +echo "IS_LINUX: $IS_LINUX, IS_DARWIN: $IS_DARWIN" + +echo "Clean gcda files" +rm -f ./obj/*.gcda + +echo "Build and run utest" +if [[ $IS_DARWIN == yes ]]; then + make darwin-debug-gcov && ./obj/st_utest +else + make linux-debug-gcov && ./obj/st_utest +fi +ret=$?; if [[ 0 -ne $ret ]]; then echo "Make ST utest fail, ret=$ret"; exit $ret; fi + +echo "Generating coverage" +mkdir -p coverage && +(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)" \ No newline at end of file diff --git a/3rdparty/st-srs/common.h b/3rdparty/st-srs/common.h index 0c0685b..acedf3d 100644 --- a/3rdparty/st-srs/common.h +++ b/3rdparty/st-srs/common.h @@ -1,4 +1,6 @@ -/* +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* * 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 @@ -155,9 +157,6 @@ typedef struct _st_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 */ @@ -171,7 +170,6 @@ 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 { @@ -199,7 +197,7 @@ struct _st_thread { _st_cond_t *term; /* Termination condition variable for join */ - jmp_buf context; /* Thread's context */ + _st_jmp_buf_t context; /* Thread's context */ }; @@ -228,6 +226,7 @@ typedef struct _st_eventsys_ops { int (*fd_new)(int); /* New descriptor allocated */ int (*fd_close)(int); /* Descriptor closed */ int (*fd_getlimit)(void); /* Descriptor hard limit */ + void (*destroy)(void); /* Destroy the event object */ } _st_eventsys_t; @@ -267,9 +266,9 @@ typedef struct _st_netfd { * 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; +extern __thread _st_vp_t _st_this_vp; +extern __thread _st_thread_t *_st_this_thread; +extern __thread _st_eventsys_t *_st_eventsys; #define _ST_CURRENT_THREAD() (_st_this_thread) #define _ST_SET_CURRENT_THREAD(_thread) (_st_this_thread = (_thread)) @@ -299,6 +298,7 @@ extern _st_eventsys_t *_st_eventsys; #define _ST_DEL_IOQ(_pq) ST_REMOVE_LINK(&_pq.links) #define _ST_ADD_RUNQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_RUNQ) +#define _ST_INSERT_RUNQ(_thr) ST_INSERT_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) @@ -367,11 +367,7 @@ extern _st_eventsys_t *_st_eventsys; #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 +#define ST_DEFAULT_STACK_SIZE (128*1024) /* Includes register stack size */ #ifndef ST_KEYS_MAX #define ST_KEYS_MAX 16 diff --git a/3rdparty/st-srs/event.c b/3rdparty/st-srs/event.c index 142882a..00a951c 100644 --- a/3rdparty/st-srs/event.c +++ b/3rdparty/st-srs/event.c @@ -1,4 +1,6 @@ -/* +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* * 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 @@ -47,12 +49,20 @@ #include #endif -#if defined(USE_POLL) && !defined(MD_HAVE_POLL) - /* Force poll usage if explicitly asked for it */ - #define MD_HAVE_POLL +// Global stat. +#if defined(DEBUG) && defined(DEBUG_STATS) +__thread unsigned long long _st_stat_epoll = 0; +__thread unsigned long long _st_stat_epoll_zero = 0; +__thread unsigned long long _st_stat_epoll_shake = 0; +__thread unsigned long long _st_stat_epoll_spin = 0; +#endif + +#if !defined(MD_HAVE_KQUEUE) && !defined(MD_HAVE_EPOLL) && !defined(MD_HAVE_SELECT) + #error Only support epoll(for Linux), kqueue(for Darwin) or select(for Cygwin) #endif +#ifdef MD_HAVE_SELECT static struct _st_seldata { fd_set fd_read_set, fd_write_set, fd_exception_set; int fd_ref_cnts[FD_SETSIZE][3]; @@ -66,19 +76,7 @@ static struct _st_seldata { #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 */ +#endif #ifdef MD_HAVE_KQUEUE @@ -88,7 +86,7 @@ typedef struct _kq_fd_data { int revents; } _kq_fd_data_t; -static struct _st_kqdata { +static __thread struct _st_kqdata { _kq_fd_data_t *fd_data; struct kevent *evtlist; struct kevent *addlist; @@ -121,7 +119,7 @@ typedef struct _epoll_fd_data { int revents; } _epoll_fd_data_t; -static struct _st_epolldata { +static __thread struct _st_epolldata { _epoll_fd_data_t *fd_data; struct epoll_event *evtlist; int fd_data_size; @@ -129,7 +127,6 @@ static struct _st_epolldata { int evtlist_cnt; int fd_hint; int epfd; - pid_t pid; } *_st_epoll_data; #ifndef ST_EPOLL_EVTLIST_SIZE @@ -150,9 +147,10 @@ static struct _st_epolldata { #endif /* MD_HAVE_EPOLL */ -_st_eventsys_t *_st_eventsys = NULL; +__thread _st_eventsys_t *_st_eventsys = NULL; +#ifdef MD_HAVE_SELECT /***************************************** * select event system */ @@ -240,7 +238,7 @@ ST_HIDDEN void _st_select_find_bad_fd(void) notify = 0; epds = pq->pds + pq->npds; pq_max_osfd = -1; - + for (pds = pq->pds; pds < epds; pds++) { osfd = pds->fd; pds->revents = 0; @@ -321,7 +319,7 @@ ST_HIDDEN void _st_select_dispatch(void) tvp = NULL; } else { min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : - (_ST_SLEEPQ->due - _ST_LAST_CLOCK); + (_ST_SLEEPQ->due - _ST_LAST_CLOCK); timeout.tv_sec = (int) (min_timeout / 1000000); timeout.tv_usec = (int) (min_timeout % 1000000); tvp = &timeout; @@ -338,7 +336,7 @@ ST_HIDDEN void _st_select_dispatch(void) notify = 0; epds = pq->pds + pq->npds; pq_max_osfd = -1; - + for (pds = pq->pds; pds < epds; pds++) { osfd = pds->fd; events = pds->events; @@ -432,170 +430,24 @@ 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) +ST_HIDDEN void _st_select_destroy(void) { - /* - * We don't maintain I/O counts for poll event system - * so nothing to check here. - */ - return 0; + /* TODO: FIXME: Implements it */ } -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 +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, + _st_select_destroy }; -#endif /* MD_HAVE_POLL */ +#endif #ifdef MD_HAVE_KQUEUE @@ -948,7 +800,6 @@ ST_HIDDEN void _st_kq_dispatch(void) 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 */ @@ -993,6 +844,11 @@ ST_HIDDEN int _st_kq_fd_getlimit(void) return 0; } +ST_HIDDEN void _st_kq_destroy(void) +{ + /* TODO: FIXME: Implements it */ +} + static _st_eventsys_t _st_kq_eventsys = { "kqueue", ST_EVENTSYS_ALT, @@ -1002,7 +858,8 @@ static _st_eventsys_t _st_kq_eventsys = { _st_kq_pollset_del, _st_kq_fd_new, _st_kq_fd_close, - _st_kq_fd_getlimit + _st_kq_fd_getlimit, + _st_kq_destroy }; #endif /* MD_HAVE_KQUEUE */ @@ -1011,7 +868,6 @@ static _st_eventsys_t _st_kq_eventsys = { /***************************************** * epoll event system */ - ST_HIDDEN int _st_epoll_init(void) { int fdlim; @@ -1031,7 +887,6 @@ ST_HIDDEN int _st_epoll_init(void) 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; @@ -1205,36 +1060,41 @@ ST_HIDDEN void _st_epoll_dispatch(void) int events, op; short revents; + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_epoll; + #endif + 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(); + // At least wait 1ms when <1ms, to avoid epoll_wait spin loop. + if (timeout == 0) { + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_epoll_zero; + #endif - /* 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); + if (min_timeout > 0) { + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_epoll_shake; + #endif + + timeout = 1; + } } } /* Check for I/O operations */ nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, _st_epoll_data->evtlist_size, timeout); + #if defined(DEBUG) && defined(DEBUG_STATS) + if (nfd <= 0) { + ++_st_stat_epoll_spin; + } + #endif + if (nfd > 0) { for (i = 0; i < nfd; i++) { osfd = _st_epoll_data->evtlist[i].data.fd; @@ -1344,6 +1204,17 @@ ST_HIDDEN int _st_epoll_is_supported(void) return (errno != ENOSYS); } +ST_HIDDEN void _st_epoll_destroy(void) +{ + 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; +} + static _st_eventsys_t _st_epoll_eventsys = { "epoll", ST_EVENTSYS_ALT, @@ -1353,7 +1224,8 @@ static _st_eventsys_t _st_epoll_eventsys = { _st_epoll_pollset_del, _st_epoll_fd_new, _st_epoll_fd_close, - _st_epoll_fd_getlimit + _st_epoll_fd_getlimit, + _st_epoll_destroy }; #endif /* MD_HAVE_EPOLL */ @@ -1369,36 +1241,27 @@ int st_set_eventsys(int eventsys) return -1; } - switch (eventsys) { - case ST_EVENTSYS_DEFAULT: -#ifdef USE_POLL - _st_eventsys = &_st_poll_eventsys; -#else + if (eventsys == ST_EVENTSYS_SELECT || eventsys == ST_EVENTSYS_DEFAULT) { +#if defined (MD_HAVE_SELECT) _st_eventsys = &_st_select_eventsys; + return 0; #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 (eventsys == ST_EVENTSYS_ALT) { #if defined (MD_HAVE_KQUEUE) _st_eventsys = &_st_kq_eventsys; + return 0; #elif defined (MD_HAVE_EPOLL) - if (_st_epoll_is_supported()) + if (_st_epoll_is_supported()) { _st_eventsys = &_st_epoll_eventsys; + return 0; + } #endif - break; - default: - errno = EINVAL; - return -1; } - return 0; + errno = EINVAL; + return -1; } int st_get_eventsys(void) diff --git a/3rdparty/st-srs/ide/st_clion/CMakeLists.txt b/3rdparty/st-srs/ide/st_clion/CMakeLists.txt new file mode 100644 index 0000000..d54d8d3 --- /dev/null +++ b/3rdparty/st-srs/ide/st_clion/CMakeLists.txt @@ -0,0 +1,130 @@ +# Name of the project. +# Language "C" is required for find_package(Threads). +if (CMAKE_VERSION VERSION_LESS 3.0) + project(st CXX C ASM) +else() + cmake_policy(SET CMP0048 NEW) + project(st VERSION 4.0.0 LANGUAGES CXX C ASM) +endif() +cmake_minimum_required(VERSION 2.8.12) + +# For utest required C++11. +set (CMAKE_CXX_STANDARD 11) + +########################################################### +execute_process( + COMMAND bash -c "cd ${PROJECT_SOURCE_DIR}/../../ && pwd" + OUTPUT_VARIABLE ST_DIR +) +string(STRIP ${ST_DIR} ST_DIR) +message("ST home is ${ST_DIR}") + +########################################################### +# Start to configure ST with jobs of number of CPUs. +include(ProcessorCount) +ProcessorCount(JOBS) + +# We should always configure ST for switching between branches. +IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + ADD_DEFINITIONS("-arch x86_64 -DDARWIN -DMD_HAVE_KQUEUE -DMD_HAVE_SELECT -DDEBUG") +ELSE () + ADD_DEFINITIONS("-DLINUX -DMD_HAVE_EPOLL -DMD_HAVE_SELECT -DDEBUG") +ENDIF () + +EXEC_PROGRAM("cd ${ST_DIR} && mkdir -p obj && cp public.h obj/st.h") + +########################################################### +# For whole project. +INCLUDE_DIRECTORIES(${ST_DIR}/obj ${ST_DIR}/utest ${ST_DIR}/utest/gtest/include) + +# Common used sources for SRS and utest. +list(APPEND SOURCE_FILES ${ST_DIR}/event.c) +list(APPEND SOURCE_FILES ${ST_DIR}/io.c) +list(APPEND SOURCE_FILES ${ST_DIR}/key.c) +list(APPEND SOURCE_FILES ${ST_DIR}/sched.c) +list(APPEND SOURCE_FILES ${ST_DIR}/stk.c) +list(APPEND SOURCE_FILES ${ST_DIR}/sync.c) +IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + list(APPEND SOURCE_FILES ${ST_DIR}/md_darwin.S) +ELSE () + list(APPEND SOURCE_FILES ${ST_DIR}/md_linux.S) + list(APPEND SOURCE_FILES ${ST_DIR}/md_linux2.S) +ENDIF () + +ADD_DEFINITIONS("-g -O0") + +########################################################### +# Setup ST utest project +ADD_SUBDIRECTORY(${ST_DIR}/utest/gtest-fit gtest-fit) +INCLUDE_DIRECTORIES(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) + +set(ST_UTEST_SOURCE_FILES ${SOURCE_FILES}) +AUX_SOURCE_DIRECTORY(${ST_DIR}/utest ST_UTEST_SOURCE_FILES) + +ADD_EXECUTABLE(st_utest ${ST_UTEST_SOURCE_FILES}) +TARGET_LINK_LIBRARIES(st_utest gtest gtest_main) +TARGET_LINK_LIBRARIES(st_utest dl) +TARGET_LINK_LIBRARIES(st_utest ${DEPS_LIBS}) +TARGET_LINK_LIBRARIES(st_utest -ldl -pthread) + +########################################################### +# Setup tools/backtrace project +set(ST_BACKTRACE_SOURCE_FILES ${SOURCE_FILES}) +AUX_SOURCE_DIRECTORY(${ST_DIR}/tools/backtrace ST_BACKTRACE_SOURCE_FILES) + +ADD_EXECUTABLE(st_backtrace ${ST_BACKTRACE_SOURCE_FILES}) +TARGET_LINK_LIBRARIES(st_backtrace ${DEPS_LIBS}) +TARGET_LINK_LIBRARIES(st_backtrace -ldl) + +########################################################### +# Setup tools/helloworld project +set(ST_HELLOWORLD_SOURCE_FILES ${SOURCE_FILES}) +AUX_SOURCE_DIRECTORY(${ST_DIR}/tools/helloworld ST_HELLOWORLD_SOURCE_FILES) + +ADD_EXECUTABLE(st_helloworld ${ST_HELLOWORLD_SOURCE_FILES}) +TARGET_LINK_LIBRARIES(st_helloworld ${DEPS_LIBS}) + +########################################################### +# Setup tools/jmpbuf project +set(ST_JMPBUF_SOURCE_FILES ${SOURCE_FILES}) +AUX_SOURCE_DIRECTORY(${ST_DIR}/tools/jmpbuf ST_JMPBUF_SOURCE_FILES) + +ADD_EXECUTABLE(st_jmpbuf ${ST_JMPBUF_SOURCE_FILES}) +TARGET_LINK_LIBRARIES(st_jmpbuf ${DEPS_LIBS}) + +########################################################### +# Setup tools/pcs project +set(ST_PCS_SOURCE_FILES ${SOURCE_FILES}) +AUX_SOURCE_DIRECTORY(${ST_DIR}/tools/pcs ST_PCS_SOURCE_FILES) + +ADD_EXECUTABLE(st_pcs ${ST_PCS_SOURCE_FILES}) +TARGET_LINK_LIBRARIES(st_pcs ${DEPS_LIBS}) + +########################################################### +# Setup tools/porting project +set(ST_PORTING_SOURCE_FILES ${SOURCE_FILES}) +AUX_SOURCE_DIRECTORY(${ST_DIR}/tools/porting ST_PORTING_SOURCE_FILES) + +ADD_EXECUTABLE(st_porting ${ST_PORTING_SOURCE_FILES}) +TARGET_LINK_LIBRARIES(st_porting ${DEPS_LIBS}) + +########################################################### +# Setup tools/stack project +set(ST_STACK_SOURCE_FILES ${SOURCE_FILES}) +AUX_SOURCE_DIRECTORY(${ST_DIR}/tools/stack ST_STACK_SOURCE_FILES) + +ADD_EXECUTABLE(st_stack ${ST_STACK_SOURCE_FILES}) +TARGET_LINK_LIBRARIES(st_stack ${DEPS_LIBS}) + +########################################################### +# Setup tools/verify project +set(ST_VERIFY_SOURCE_FILES ${SOURCE_FILES}) +AUX_SOURCE_DIRECTORY(${ST_DIR}/tools/verify ST_VERIFY_SOURCE_FILES) + +ADD_EXECUTABLE(st_verify ${ST_VERIFY_SOURCE_FILES}) +TARGET_LINK_LIBRARIES(st_verify ${DEPS_LIBS}) + +########################################################### +# Done +MESSAGE(STATUS "@see https://github.com/ossrs/state-threads#usage") + diff --git a/3rdparty/st-srs/io.c b/3rdparty/st-srs/io.c index 7e05f99..08777e7 100644 --- a/3rdparty/st-srs/io.c +++ b/3rdparty/st-srs/io.c @@ -1,4 +1,6 @@ -/* +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* * 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 @@ -52,6 +54,23 @@ #include #include "common.h" +// Global stat. +#if defined(DEBUG) && defined(DEBUG_STATS) +__thread unsigned long long _st_stat_recvfrom = 0; +__thread unsigned long long _st_stat_recvfrom_eagain = 0; +__thread unsigned long long _st_stat_sendto = 0; +__thread unsigned long long _st_stat_sendto_eagain = 0; +__thread unsigned long long _st_stat_read = 0; +__thread unsigned long long _st_stat_read_eagain = 0; +__thread unsigned long long _st_stat_readv = 0; +__thread unsigned long long _st_stat_readv_eagain = 0; +__thread unsigned long long _st_stat_writev = 0; +__thread unsigned long long _st_stat_writev_eagain = 0; +__thread unsigned long long _st_stat_recvmsg = 0; +__thread unsigned long long _st_stat_recvmsg_eagain = 0; +__thread unsigned long long _st_stat_sendmsg = 0; +__thread unsigned long long _st_stat_sendmsg_eagain = 0; +#endif #if EAGAIN != EWOULDBLOCK #define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK)) @@ -62,7 +81,7 @@ #define _LOCAL_MAXIOV 16 /* File descriptor object free list */ -static _st_netfd_t *_st_netfd_freelist = NULL; +static __thread _st_netfd_t *_st_netfd_freelist = NULL; /* Maximum number of file descriptors that the process can open */ static int _st_osfd_limit = -1; @@ -93,7 +112,7 @@ int _st_io_init(void) /** * 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 + * @see https://github.com/ossrs/srs/issues/336 */ if ((int)rlim.rlim_max < 0) { _st_osfd_limit = (int)(fdlim > 0? fdlim : rlim.rlim_cur); @@ -243,7 +262,6 @@ int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeout) } -#ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT /* No-op */ int st_netfd_serialize_accept(_st_netfd_t *fd) { @@ -291,112 +309,6 @@ _st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_ 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) { @@ -437,12 +349,21 @@ int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_uti ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout) { ssize_t n; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_read; + #endif while ((n = read(fd->osfd, buf, nbyte)) < 0) { if (errno == EINTR) continue; if (!_IO_NOT_READY_ERROR) return -1; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_read_eagain; + #endif + /* Wait until the socket becomes readable */ if (st_netfd_poll(fd, POLLIN, timeout) < 0) return -1; @@ -470,12 +391,21 @@ int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout) ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout) { ssize_t n; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_readv; + #endif while ((n = readv(fd->osfd, iov, iov_size)) < 0) { if (errno == EINTR) continue; if (!_IO_NOT_READY_ERROR) return -1; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_readv_eagain; + #endif + /* Wait until the socket becomes readable */ if (st_netfd_poll(fd, POLLIN, timeout) < 0) return -1; @@ -572,6 +502,10 @@ ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_uti nleft = nbyte; tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */ iov_cnt = iov_size; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_writev; + #endif while (nleft > 0) { if (iov_cnt == 1) { @@ -616,6 +550,11 @@ ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_uti tmp_iov[iov_cnt].iov_len = iov[index].iov_len; } } + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_writev_eagain; + #endif + /* Wait until the socket becomes writable */ if (st_netfd_poll(fd, POLLOUT, timeout) < 0) { rv = -1; @@ -633,6 +572,10 @@ ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_uti int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout) { ssize_t n; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_writev; + #endif while (*iov_size > 0) { if (*iov_size == 1) @@ -659,6 +602,11 @@ int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime (*iov)->iov_base = (char *) (*iov)->iov_base + n; (*iov)->iov_len -= n; } + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_writev_eagain; + #endif + /* Wait until the socket becomes writable */ if (st_netfd_poll(fd, POLLOUT, timeout) < 0) return -1; @@ -674,12 +622,21 @@ int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout) { int n; - + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_recvfrom; + #endif + while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) < 0) { if (errno == EINTR) continue; if (!_IO_NOT_READY_ERROR) return -1; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_recvfrom_eagain; + #endif + /* Wait until the socket becomes readable */ if (st_netfd_poll(fd, POLLIN, timeout) < 0) return -1; @@ -692,12 +649,21 @@ int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout) { int n; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_sendto; + #endif while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) { if (errno == EINTR) continue; if (!_IO_NOT_READY_ERROR) return -1; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_sendto_eagain; + #endif + /* Wait until the socket becomes writable */ if (st_netfd_poll(fd, POLLOUT, timeout) < 0) return -1; @@ -710,12 +676,21 @@ int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr * int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeout) { int n; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_recvmsg; + #endif while ((n = recvmsg(fd->osfd, msg, flags)) < 0) { if (errno == EINTR) continue; if (!_IO_NOT_READY_ERROR) return -1; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_recvmsg_eagain; + #endif + /* Wait until the socket becomes readable */ if (st_netfd_poll(fd, POLLIN, timeout) < 0) return -1; @@ -728,12 +703,21 @@ int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeou int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, st_utime_t timeout) { int n; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_sendmsg; + #endif while ((n = sendmsg(fd->osfd, msg, flags)) < 0) { if (errno == EINTR) continue; if (!_IO_NOT_READY_ERROR) return -1; + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_sendmsg_eagain; + #endif + /* Wait until the socket becomes writable */ if (st_netfd_poll(fd, POLLOUT, timeout) < 0) return -1; @@ -742,60 +726,6 @@ int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, st_utime_t return n; } -int st_sendmmsg(st_netfd_t fd, struct st_mmsghdr *msgvec, unsigned int vlen, int flags, st_utime_t timeout) -{ -#if defined(MD_HAVE_SENDMMSG) && defined(_GNU_SOURCE) - int n; - int left; - struct mmsghdr *p; - - left = (int)vlen; - while (left > 0) { - p = (struct mmsghdr*)msgvec + (vlen - left); - - if ((n = sendmmsg(fd->osfd, p, left, flags)) < 0) { - if (errno == EINTR) - continue; - if (!_IO_NOT_READY_ERROR) - break; - /* Wait until the socket becomes writable */ - if (st_netfd_poll(fd, POLLOUT, timeout) < 0) - break; - } - - left -= n; - } - - // An error is returned only if no datagrams could be sent. - if (left == (int)vlen) { - return n; - } - return (int)vlen - left; -#else - struct st_mmsghdr *p; - int i, n; - - // @see http://man7.org/linux/man-pages/man2/sendmmsg.2.html - for (i = 0; i < (int)vlen; ++i) { - p = msgvec + i; - n = st_sendmsg(fd, &p->msg_hdr, flags, timeout); - if (n < 0) { - // An error is returned only if no datagrams could be sent. - if (i == 0) { - return n; - } - return i + 1; - } - - p->msg_len = n; - } - - // Returns the number of messages sent from msgvec; if this is less than vlen, the caller can retry with a - // further sendmmsg() call to send the remaining messages. - return vlen; -#endif -} - /* * To open FIFOs or other special files. diff --git a/3rdparty/st-srs/key.c b/3rdparty/st-srs/key.c index 5e64022..2374680 100644 --- a/3rdparty/st-srs/key.c +++ b/3rdparty/st-srs/key.c @@ -1,4 +1,6 @@ -/* +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* * 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 @@ -77,7 +79,12 @@ int st_key_getlimit(void) int st_thread_setspecific(int key, void *value) { _st_thread_t *me = _ST_CURRENT_THREAD(); - + return st_thread_setspecific2(me, key, value); +} + + +int st_thread_setspecific2(_st_thread_t *me, int key, void *value) +{ if (key < 0 || key >= key_max) { errno = EINVAL; return -1; diff --git a/3rdparty/st-srs/md.h b/3rdparty/st-srs/md.h index 8c0a222..d3158db 100644 --- a/3rdparty/st-srs/md.h +++ b/3rdparty/st-srs/md.h @@ -1,4 +1,6 @@ -/* +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* * 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 @@ -54,77 +56,49 @@ #define MAP_FAILED -1 #endif +/* We define the jmpbuf, because the system's is different in different OS */ +typedef struct _st_jmp_buf { + /* + * OS CPU SIZE + * Darwin __amd64__/__x86_64__ long[8] + * Darwin __aarch64__ long[22] + * Linux __i386__ long[6] + * Linux __amd64__/__x86_64__ long[8] + * Linux __aarch64__ long[22] + * Linux __arm__ long[16] + * Linux __mips__/__mips64 long[13] + * Linux __riscv long[14] + * Linux __loongarch64 long[12] + * Cygwin64 __amd64__/__x86_64__ long[8] + */ + long __jmpbuf[22]; +} _st_jmp_buf_t[1]; + +extern int _st_md_cxt_save(_st_jmp_buf_t env); +extern void _st_md_cxt_restore(_st_jmp_buf_t env, int val); + +/* Always use builtin setjmp/longjmp, use asm code. */ +#define MD_USE_BUILTIN_SETJMP +#define MD_SETJMP(env) _st_md_cxt_save(env) +#define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val) +#if defined(USE_LIBC_SETJMP) +#error The libc setjmp is not supported now +#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) +#if 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_USE_BUILTIN_SETJMP - #if defined(__amd64__) || defined(__x86_64__) - #define JB_SP 12 - #define MD_GET_SP(_t) *((long *)&((_t)->context[JB_SP])) + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[6])) + #elif defined(__aarch64__) + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[13])) #else #error Unknown CPU architecture #endif @@ -136,136 +110,11 @@ MD_GET_SP(_thread) = (long) (_sp); \ ST_END_MACRO - #if defined(MD_USE_BUILTIN_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); - #endif - #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) /* @@ -274,7 +123,6 @@ */ #define MD_USE_BSD_ANON_MMAP #define MD_ACCEPT_NB_NOT_INHERITED - #define MD_ALWAYS_UNSERIALIZED_ACCEPT /* * Modern GNU/Linux is Posix.1g compliant. */ @@ -289,288 +137,59 @@ (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 + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[4])) + #elif defined(__amd64__) || defined(__x86_64__) + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[6])) + #elif defined(__aarch64__) + /* https://github.com/ossrs/state-threads/issues/9 */ + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[13])) + #elif defined(__arm__) + /* https://github.com/ossrs/state-threads/issues/1#issuecomment-244648573 */ + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[8])) + #elif defined(__mips64) + /* https://github.com/ossrs/state-threads/issues/21 */ + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0])) + #elif defined(__mips__) + /* https://github.com/ossrs/state-threads/issues/21 */ + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0])) + #elif defined(__riscv) + /* https://github.com/ossrs/state-threads/pull/28 */ + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0])) + #elif defined(__loongarch64) + /* https://github.com/ossrs/state-threads/issues/24 */ + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0])) #else - #error Unknown CPU architecture - #endif + #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(); \ - (_thread)->context[MD_JB_SP] = (long) (_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 (CYGWIN64) -#elif defined (OPENBSD) - - #define MD_STACK_GROWS_DOWN + // For CYGWIN64, build SRS on Windows. #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) + #define MD_USE_BUILTIN_SETJMP - #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 + #if defined(__amd64__) || defined(__x86_64__) + #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[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); \ + #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() \ @@ -578,57 +197,10 @@ (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 diff --git a/3rdparty/st-srs/md_cygwin64.S b/3rdparty/st-srs/md_cygwin64.S new file mode 100644 index 0000000..a1036b9 --- /dev/null +++ b/3rdparty/st-srs/md_cygwin64.S @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2021-2022 The SRS Authors */ + +/* If user disable the ASM, such as avoiding bugs in ASM, donot compile it. */ +#if !defined(MD_ST_NO_ASM) + +#if defined(__amd64__) || defined(__x86_64__) + + /****************************************************************/ + + /* + * Internal __jmp_buf layout + */ + #define JB_RBX 0 + #define JB_RBP 1 + #define JB_R12 2 /* R12:R15 Nonvolatile Must be preserved by callee */ + #define JB_R13 3 /* @see https://docs.microsoft.com/en-us/cpp/build/x64-software-conventions?view=msvc-160#register-usage */ + #define JB_R14 4 /* RBX, RBP, RDI, RSI, R12, R14, R14, and R15 must be saved in any function using them. */ + #define JB_R15 5 /* @see https://software.intel.com/content/www/us/en/develop/articles/introduction-to-x64-assembly.html */ + #define JB_RSP 6 + #define JB_PC 7 + + .file "md_cygwin64.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ /* The env is rcx, https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160 */ + .globl _st_md_cxt_save + .align 16 + _st_md_cxt_save: + /* + * Save registers. + */ + movq %rbx, (JB_RBX*8)(%rcx) /* Save rbx to env[0], *(int64_t*)(rcx+0)=rbx */ + movq %rbp, (JB_RBP*8)(%rcx) /* Save rbp to env[1], *(int64_t*)(rcx+1)=rbp */ + movq %r12, (JB_R12*8)(%rcx) /* Save r12 to env[2], *(int64_t*)(rcx+2)=r12 */ + movq %r13, (JB_R13*8)(%rcx) /* Save r13 to env[3], *(int64_t*)(rcx+3)=r13 */ + movq %r14, (JB_R14*8)(%rcx) /* Save r14 to env[4], *(int64_t*)(rcx+4)=r14 */ + movq %r15, (JB_R15*8)(%rcx) /* Save r15 to env[5], *(int64_t*)(rcx+5)=r15 */ + /* Save SP */ + leaq 8(%rsp), %r8 /* Save *(int64_t*)(rsp+8) to r8, https://github.com/ossrs/state-threads/issues/20#issuecomment-887569093 */ + movq %r8, (JB_RSP*8)(%rcx) /* Save r8(rsp) to env[6], *(int64_t*)(rcx+6)=r8 */ + /* Save PC we are returning to */ + movq (%rsp), %r9 /* Save PC(parent function address) %(rsp) to r9, https://github.com/ossrs/state-threads/issues/20#issuecomment-887569093 */ + movq %r9, (JB_PC*8)(%rcx) /* Save r9(PC) to env[7], *(int64_t*)(rcx+7)=r9 */ + xorq %rax, %rax /* Reset rax(return value) to 0 */ + ret + + + /****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ /* The env is rcx, val is edx/rdx, https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160 */ + .globl _st_md_cxt_restore + .align 16 + _st_md_cxt_restore: + /* + * Restore registers. + */ + movq (JB_RBX*8)(%rcx), %rbx /* Load rbx from env[0] */ + movq (JB_RBP*8)(%rcx), %rbp /* Load rbp from env[1] */ + movq (JB_R12*8)(%rcx), %r12 /* Load r12 from env[2] */ + movq (JB_R13*8)(%rcx), %r13 /* Load r13 from env[3] */ + movq (JB_R14*8)(%rcx), %r14 /* Load r14 from env[4] */ + movq (JB_R15*8)(%rcx), %r15 /* Load r15 from env[5] */ + /* Set return value */ /* The edx is param1 val, the eax is return value */ + test %edx, %edx /* if (!val) { */ + mov $01, %eax /* val=1; */ + cmove %eax, %edx /* } */ + mov %edx, %eax /* return val; */ + /* Restore PC and RSP */ + movq (JB_PC*8)(%rcx), %r8 /* Load r8(PC) from env[7], https://github.com/ossrs/state-threads/issues/20#issuecomment-887569093 */ + movq (JB_RSP*8)(%rcx), %rsp /* Load rsp from env[6] */ + /* Jump to saved PC */ + jmpq *%r8 /* Jump to r8(PC) */ + + /****************************************************************/ + +#endif + +#endif diff --git a/3rdparty/st-srs/md_darwin.S b/3rdparty/st-srs/md_darwin.S index 6cd163d..7a3c2ee 100644 --- a/3rdparty/st-srs/md_darwin.S +++ b/3rdparty/st-srs/md_darwin.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2021-2022 The SRS Authors */ /* If user disable the ASM, such as avoiding bugs in ASM, donot compile it. */ #if !defined(MD_ST_NO_ASM) @@ -11,17 +13,17 @@ */ #define JB_RBX 0 #define JB_RBP 1 - #define JB_R12 2 /* Backup IP, https://www.cnblogs.com/Five100Miles/p/8458561.html */ - #define JB_R13 3 /* Backup SP, https://www.cnblogs.com/Five100Miles/p/8458561.html */ - #define JB_R14 4 /* Backup LR, https://www.cnblogs.com/Five100Miles/p/8458561.html */ - #define JB_R15 5 /* Backup PC, https://www.cnblogs.com/Five100Miles/p/8458561.html */ + #define JB_R12 2 /* The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, R9. */ + #define JB_R13 3 /* If the callee wishes to use registers RBX, RSP, RBP, and R12–R15, it must restore their original values before returning control to the caller. */ + #define JB_R14 4 /* @see https://en.wikipedia.org/wiki/X86_calling_conventions */ + #define JB_R15 5 /* @see https://www.cnblogs.com/Five100Miles/p/8458561.html */ #define JB_RSP 6 #define JB_PC 7 .file "md_darwin.S" .text - /* _st_md_cxt_save(__jmp_buf env) */ /* The env is rdi, http://blog.chinaunix.net/uid-20157960-id-1974354.html */ + /* _st_md_cxt_save(__jmp_buf env) */ /* The env is rdi, https://en.wikipedia.org/wiki/X86_calling_conventions */ .globl __st_md_cxt_save .align 16 __st_md_cxt_save: @@ -35,18 +37,18 @@ movq %r14, (JB_R14*8)(%rdi) /* Save r14 to env[4], *(int64_t*)(rdi+4)=r14 */ movq %r15, (JB_R15*8)(%rdi) /* Save r15 to env[5], *(int64_t*)(rdi+5)=r15 */ /* Save SP */ - leaq 8(%rsp), %rdx /* Save *(int64_t*)(rsp+8) to rdx, https://my.oschina.net/guonaihong/blog/508907 */ - movq %rdx, (JB_RSP*8)(%rdi) /* Save rdx(rsp) to env[6], *(int64_t*)(rdi+6)=rdx */ + leaq 8(%rsp), %r8 /* Save *(int64_t*)(rsp+8) to r8, https://github.com/ossrs/state-threads/issues/11#issuecomment-888709759 */ + movq %r8, (JB_RSP*8)(%rdi) /* Save r8(rsp) to env[6], *(int64_t*)(rdi+6)=r8 */ /* Save PC we are returning to */ - movq (%rsp), %rax /* Save PC(parent function address) %(rsp) to rax */ - movq %rax, (JB_PC*8)(%rdi) /* Save rax(PC) to env[7], *(int64_t*)(rdi+7)=rax */ + movq (%rsp), %r9 /* Save PC(parent function address) %(rsp) to r9 */ + movq %r9, (JB_PC*8)(%rdi) /* Save r9(PC) to env[7], *(int64_t*)(rdi+7)=r9 */ xorq %rax, %rax /* Reset rax to 0 */ ret /****************************************************************/ - /* _st_md_cxt_restore(__jmp_buf env, int val) */ /* The env is rdi, val is esi/rsi, http://blog.chinaunix.net/uid-20157960-id-1974354.html */ + /* _st_md_cxt_restore(__jmp_buf env, int val) */ /* The env is rdi, val is esi/rsi, https://en.wikipedia.org/wiki/X86_calling_conventions */ .globl __st_md_cxt_restore .align 16 __st_md_cxt_restore: @@ -64,13 +66,136 @@ mov $01, %eax /* val=1; */ cmove %eax, %esi /* } */ mov %esi, %eax /* return val; */ - movq (JB_PC*8)(%rdi), %rdx /* Load rdx(PC) from env[7] */ + /* Restore PC and RSP */ + movq (JB_PC*8)(%rdi), %r8 /* Load r8(PC) from env[7] */ movq (JB_RSP*8)(%rdi), %rsp /* Load rsp from env[6] */ /* Jump to saved PC */ - jmpq *%rdx /* Jump to rdx(PC) */ + jmpq *%r8 /* Jump to r8(PC) */ /****************************************************************/ -#endif + + + + + + + + + + +#elif defined(__aarch64__) + + /****************************************************************/ + /* See https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms */ + /* See https://developer.arm.com/documentation/102374/0100/Function-calls */ + /* See https://developer.arm.com/documentation/102374/0100/Procedure-Call-Standard */ + /* See https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#machine-registers */ + /* See https://wiki.cdot.senecacollege.ca/wiki/AArch64_Register_and_Instruction_Quick_Start */ + /* + * See setjmp.h of Darwin. + * + * _JBLEN is the number of ints required to save the following: + * r21-r29, sp, fp, lr == 12 registers, 8 bytes each. d8-d15 + * are another 8 registers, each 8 bytes long. (aapcs64 specifies + * that only 64-bit versions of FP registers need to be saved). + * Finally, two 8-byte fields for signal handling purposes. + */ + + /* The called routine is expected to preserve r19-r28 *** These registers are generally + safe to use in your program. */ + #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 + /* r29 and r30 are used as the frame register and link register (avoid) */ + #define JB_X29 10 + #define JB_LR 11 + /* Register '31' is one of two registers depending on the instruction context: + For instructions dealing with the stack, it is the stack pointer, named rsp */ + #define JB_SP 13 + + /* FP registers */ + #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 + .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 + + /****************************************************************/ + + /* _st_md_cxt_restore(__jmp_buf env, int val) */ + .globl __st_md_cxt_restore + .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 + + /* x0 = (x1 || 1); */ + cmp x1, #0 + mov x0, #1 + csel x0, x1, x0, ne + + ret + + /****************************************************************/ + + + + + + + + + + #endif + +#endif \ No newline at end of file diff --git a/3rdparty/st-srs/md_linux.S b/3rdparty/st-srs/md_linux.S new file mode 100644 index 0000000..b5b9469 --- /dev/null +++ b/3rdparty/st-srs/md_linux.S @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* 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(__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 + +#endif diff --git a/3rdparty/st-srs/md_linux2.S b/3rdparty/st-srs/md_linux2.S new file mode 100644 index 0000000..0ee21ff --- /dev/null +++ b/3rdparty/st-srs/md_linux2.S @@ -0,0 +1,512 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2021-2022 The SRS Authors */ + +/* If user disable the ASM, such as avoiding bugs in ASM, donot compile it. */ +#if !defined(MD_ST_NO_ASM) + +#if defined(__aarch64__) + + /****************************************************************/ + /* See https://developer.arm.com/documentation/102374/0100/Function-calls */ + /* See https://developer.arm.com/documentation/102374/0100/Procedure-Call-Standard */ + /* See https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#machine-registers */ + /* See https://wiki.cdot.senecacollege.ca/wiki/AArch64_Register_and_Instruction_Quick_Start */ + /* See https://chromium.googlesource.com/native_client/nacl-glibc/+/glibc-2.21/sysdeps/aarch64/__longjmp.S */ + /* See https://chromium.googlesource.com/native_client/nacl-glibc/+/glibc-2.21/sysdeps/aarch64/setjmp.S */ + + /* The called routine is expected to preserve r19-r28 *** These registers are generally + safe to use in your program. */ + #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 + /* r29 and r30 are used as the frame register and link register (avoid) */ + #define JB_X29 10 + #define JB_LR 11 + /* Register '31' is one of two registers depending on the instruction context: + For instructions dealing with the stack, it is the stack pointer, named rsp */ + #define JB_SP 13 + + /* FP registers */ + #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 + + /****************************************************************/ + + + + + +#elif defined(__riscv) + + /****************************************************************/ + /* + * Internal __jmp_buf layout + * riscv-asm: https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md + */ + #define JB_SP 0 /* A0, SP, Stack pointer */ + #define JB_RA 1 /* RA, Return address */ + #define JB_FP 2 /* FP/S0 Frame pointer */ + #define JB_S1 3 /* S1 Saved register*/ + #define JB_S2 4 /* S2-S11, Saved register */ + #define JB_S3 5 /* S2-S11, Saved register */ + #define JB_S4 6 /* S2-S11, Saved register */ + #define JB_S5 7 /* S2-S11, Saved register */ + #define JB_S6 8 /* S2-S11, Saved register */ + #define JB_S7 9 /* S2-S11, Saved register */ + #define JB_S8 10 /* S2-S11, Saved register */ + #define JB_S9 11 /* S2-S11, Saved register */ + #define JB_S10 12 /* S2-S11, Saved register */ + #define JB_S11 13 /* S2-S11, Saved register */ + + + .file "md_linux.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ /* The env is $a0, https://en.wikipedia.org/wiki/RISC-V#Register_sets */ + .globl _st_md_cxt_save + .type _st_md_cxt_save, %function + .align 2 + _st_md_cxt_save: + sd sp, JB_SP * 8(a0) + sd ra, JB_RA * 8(a0) + sd s0, JB_FP * 8(a0) + sd s1, JB_S1 * 8(a0) + sd s2, JB_S2 * 8(a0) + sd s3, JB_S3 * 8(a0) + sd s4, JB_S4 * 8(a0) + sd s5, JB_S5 * 8(a0) + sd s6, JB_S6 * 8(a0) + sd s7, JB_S7 * 8(a0) + sd s8, JB_S8 * 8(a0) + sd s9, JB_S9 * 8(a0) + sd s10, JB_S10 * 8(a0) + sd s11, JB_S11 * 8(a0) + li a0, 0 + jr ra + .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, JB_SP * 8(a0) + ld ra, JB_RA * 8(a0) + ld s0, JB_FP * 8(a0) + ld s1, JB_S1 * 8(a0) + ld s2, JB_S2 * 8(a0) + ld s3, JB_S3 * 8(a0) + ld s4, JB_S4 * 8(a0) + ld s5, JB_S5 * 8(a0) + ld s6, JB_S6 * 8(a0) + ld s7, JB_S7 * 8(a0) + ld s8, JB_S8 * 8(a0) + ld s9, JB_S9 * 8(a0) + ld s10, JB_S10 * 8(a0) + ld s11, JB_S11 * 8(a0) + li a0, 1 + jr ra + .size _st_md_cxt_restore, .-_st_md_cxt_restore + + /****************************************************************/ + + + + + + +#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 + */ + #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: + sw $sp, 0($a0) /* Save sp to env[0], *(long*)($a0+0) =sp */ + sw $ra, 4($a0) /* Save ra to env[1], *(long*)($a0+4)=ra, the return address, https://chortle.ccsu.edu/AssemblyTutorial/Chapter-26/ass26_4.html */ + sw $gp, 8($a0) /* Save gp to env[2], *(long*)($a0+8) =gp */ + sw $s0, 12($a0) /* Save s0 to env[3], *(long*)($a0+12)=s0 */ + sw $s1, 16($a0) /* Save s1 to env[4], *(long*)($a0+16)=s1 */ + sw $s2, 20($a0) /* Save s2 to env[5], *(long*)($a0+20)=s2 */ + sw $s3, 24($a0) /* Save s3 to env[6], *(long*)($a0+24)=s3 */ + sw $s4, 28($a0) /* Save s4 to env[7], *(long*)($a0+28)=s4 */ + sw $s5, 32($a0) /* Save s5 to env[8], *(long*)($a0+32)=s5 */ + sw $s6, 36($a0) /* Save s6 to env[9], *(long*)($a0+36)=s6 */ + sw $s7, 40($a0) /* Save s7 to env[10], *(long*)($a0+40)=s7 */ + sw $fp, 44($a0) /* Save fp to env[11], *(long*)($a0+44) =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: + lw $sp, 0($a0) /* Load sp from env[0], sp=*(long*)($a0+0) */ + lw $ra, 4($a0) /* Load sp from env[1], ra=*(long*)($a0+4), the saved return address */ + lw $gp, 8($a0) /* Load sp from env[2], gp=*(long*)($a0+8) */ + lw $s0, 12($a0) /* Load sp from env[3], s0=*(long*)($a0+12) */ + lw $s1, 16($a0) /* Load sp from env[4], s1=*(long*)($a0+16) */ + lw $s2, 20($a0) /* Load sp from env[5], s2=*(long*)($a0+20) */ + lw $s3, 24($a0) /* Load sp from env[6], s3=*(long*)($a0+24) */ + lw $s4, 28($a0) /* Load sp from env[7], s4=*(long*)($a0+28) */ + lw $s5, 32($a0) /* Load sp from env[8], s5=*(long*)($a0+32) */ + lw $s6, 36($a0) /* Load sp from env[9], s6=*(long*)($a0+36) */ + lw $s7, 40($a0) /* Load sp from env[10], s7=*(long*)($a0+40) */ + lw $fp, 44($a0) /* Load sp from env[2], fp=*(long*)($a0+44) */ + 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(__loongarch64) + + /****************************************************************/ + + /* + * Internal __jmp_buf layout + */ + #define JB_SP 0 /* R3, SP, Stack pointer */ + #define JB_RA 1 /* R1, RA, Return address */ + #define JB_FP 2 /* FP/R22 Frame pointer */ + #define JB_S0 3 /* R23-R31, S0-S8, Subroutine register variable */ + #define JB_S1 4 /* R23-R31, S0-S8, Subroutine register variable */ + #define JB_S2 5 /* R23-R31, S0-S8, Subroutine register variable */ + #define JB_S3 6 /* R23-R31, S0-S8, Subroutine register variable */ + #define JB_S4 7 /* R23-R31, S0-S8, Subroutine register variable */ + #define JB_S5 8 /* R23-R31, S0-S8, Subroutine register variable */ + #define JB_S6 9 /* R23-R31, S0-S8, Subroutine register variable */ + #define JB_S7 10 /* R23-R31, S0-S8, Subroutine register variable */ + #define JB_S8 11 /* R23-R31, S0-S8, Subroutine register variable */ + + .file "md_linux.S" + .text + + /* _st_md_cxt_save(__jmp_buf env) */ /* The env is $r4, https://github.com/ossrs/state-threads/issues/24#porting */ + .globl _st_md_cxt_save + .type _st_md_cxt_save, %function + .align 2 + _st_md_cxt_save: + st.d $r3, $r4, 0 /* Save sp to env[0], *(long*)($r4+0) = sp */ + st.d $r1, $r4, 8 /* Save ra to env[1], *(long*)($r4+8) = r1 */ + st.d $r22, $r4, 16 /* Save fp to env[2], *(long*)($r4+16) = r22 */ + st.d $r23, $r4, 24 /* Save r23 to env[3], *(long*)($r4+24) = r23 */ + st.d $r24, $r4, 32 /* Save r24 to env[4], *(long*)($r4+32) = r24 */ + st.d $r25, $r4, 40 /* Save r25 to env[5], *(long*)($r4+40) = r25 */ + st.d $r26, $r4, 48 /* Save r26 to env[6], *(long*)($r4+48) = r26 */ + st.d $r27, $r4, 56 /* Save r27 to env[7], *(long*)($r4+56) = r27 */ + st.d $r28, $r4, 64 /* Save r28 to env[8], *(long*)($r4+64) = r28 */ + st.d $r29, $r4, 72 /* Save r29 to env[9], *(long*)($r4+72) = r29 */ + st.d $r30, $r4, 80 /* Save r30 to env[10], *(long*)($r4+80) = r30 */ + st.d $r31, $r4, 88 /* Save r31 to env[11], *(long*)($r4+88) = r31 */ + addi.w $r12, $r0, 0 /* Set return value to 0 */ + move $r4, $r12 /* Set return value to 0 */ + jirl $r0, $r1, 0 /* 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.d $r3, $r4, 0 /* Load sp from env[0], sp=*(long*)($r4+0) */ + ld.d $r1, $r4, 8 /* Load ra from env[1], r1=*(long*)($r4+8) */ + ld.d $r22, $r4, 16 /* Load fp from env[2], r22=*(long*)($r4+16) */ + ld.d $r23, $r4, 24 /* Load r23 from env[3], r23=*(long*)($r4+24) */ + ld.d $r24, $r4, 32 /* Load r24 from env[4], r24=*(long*)($r4+32) */ + ld.d $r25, $r4, 40 /* Load r25 from env[5], r25=*(long*)($r4+40) */ + ld.d $r26, $r4, 48 /* Load r26 from env[6], r26=*(long*)($r4+48) */ + ld.d $r27, $r4, 56 /* Load r27 from env[7], r27=*(long*)($r4+56) */ + ld.d $r28, $r4, 64 /* Load r28 from env[8], r28=*(long*)($r4+64) */ + ld.d $r29, $r4, 72 /* Load r29 from env[9], r29=*(long*)($r4+72) */ + ld.d $r30, $r4, 80 /* Load r30 from env[10], r30=*(long*)($r4+80) */ + ld.d $r31, $r4, 88 /* Load r31 from env[11], r31=*(long*)($r4+88) */ + addi.w $r12, $r0, 1 /* Set return value to 1 */ + move $r4, $r12 /* Set return value to 1 */ + jirl $r0, $r1, 0 /* Return to the saved return address */ + + .size _st_md_cxt_restore, .-_st_md_cxt_restore + + /****************************************************************/ + +#endif + +#endif diff --git a/3rdparty/st-srs/public.h b/3rdparty/st-srs/public.h index 80fd933..97da146 100644 --- a/3rdparty/st-srs/public.h +++ b/3rdparty/st-srs/public.h @@ -1,4 +1,6 @@ -/* +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* * 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 @@ -66,7 +68,6 @@ #define ST_EVENTSYS_DEFAULT 0 #define ST_EVENTSYS_SELECT 1 -#define ST_EVENTSYS_POLL 2 #define ST_EVENTSYS_ALT 3 #ifdef __cplusplus @@ -98,6 +99,7 @@ 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 void st_thread_yield(); 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)); @@ -152,16 +154,11 @@ extern int st_sendto(st_netfd_t fd, const void *msg, int len, const struct socka 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); -// @see http://man7.org/linux/man-pages/man2/sendmmsg.2.html -#include -struct st_mmsghdr { - struct msghdr msg_hdr; /* Message header */ - unsigned int msg_len; /* Number of bytes transmitted */ -}; -extern int st_sendmmsg(st_netfd_t fd, struct st_mmsghdr *msgvec, unsigned int vlen, int flags, st_utime_t timeout); - extern st_netfd_t st_open(const char *path, int oflags, mode_t mode); +extern void st_destroy(void); +extern int st_thread_setspecific2(st_thread_t thread, int key, void *value); + #ifdef DEBUG extern void _st_show_thread_stack(st_thread_t thread, const char *messg); extern void _st_iterate_threads(void); diff --git a/3rdparty/st-srs/sched.c b/3rdparty/st-srs/sched.c index 8751582..176e9d1 100644 --- a/3rdparty/st-srs/sched.c +++ b/3rdparty/st-srs/sched.c @@ -1,4 +1,6 @@ -/* +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* * 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 @@ -52,15 +54,35 @@ #include #endif +// Global stat. +#if defined(DEBUG) && defined(DEBUG_STATS) +__thread unsigned long long _st_stat_sched_15ms = 0; +__thread unsigned long long _st_stat_sched_20ms = 0; +__thread unsigned long long _st_stat_sched_25ms = 0; +__thread unsigned long long _st_stat_sched_30ms = 0; +__thread unsigned long long _st_stat_sched_35ms = 0; +__thread unsigned long long _st_stat_sched_40ms = 0; +__thread unsigned long long _st_stat_sched_80ms = 0; +__thread unsigned long long _st_stat_sched_160ms = 0; +__thread unsigned long long _st_stat_sched_s = 0; + +__thread unsigned long long _st_stat_thread_run = 0; +__thread unsigned long long _st_stat_thread_idle = 0; +__thread unsigned long long _st_stat_thread_yield = 0; +__thread unsigned long long _st_stat_thread_yield2 = 0; +#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 */ +__thread _st_vp_t _st_this_vp; /* This VP */ +__thread _st_thread_t *_st_this_thread; /* Current thread */ +__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 */ +__thread time_t _st_curr_time = 0; /* Current time as returned by time(2) */ +__thread st_utime_t _st_last_tset; /* Last time it was fetched */ +// We should initialize the thread-local variable in st_init(). +extern __thread _st_clist_t _st_free_stacks; int st_poll(struct pollfd *pds, int npds, st_utime_t timeout) { @@ -118,10 +140,18 @@ void _st_vp_schedule(void) _st_thread_t *thread; if (_ST_RUNQ.next != &_ST_RUNQ) { + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_thread_run; + #endif + /* Pull thread off of the run queue */ thread = _ST_THREAD_PTR(_ST_RUNQ.next); _ST_DEL_RUNQ(thread); } else { + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_thread_idle; + #endif + /* If there are no threads to run, switch to the idle thread */ thread = _st_this_vp.idle_thread; } @@ -139,7 +169,7 @@ void _st_vp_schedule(void) int st_init(void) { _st_thread_t *thread; - + if (_st_active_count) { /* Already initialized */ return 0; @@ -150,7 +180,11 @@ int st_init(void) if (_st_io_init() < 0) return -1; - + + // Initialize the thread-local variables. + ST_INIT_CLIST(&_st_free_stacks); + + // Initialize ST. memset(&_st_this_vp, 0, sizeof(_st_vp_t)); ST_INIT_CLIST(&_ST_RUNQ); @@ -195,6 +229,15 @@ int st_init(void) } +/* + * Destroy this Virtual Processor + */ +void st_destroy(void) +{ + (*_st_eventsys->destroy)(); +} + + #ifdef ST_SWITCH_CB st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb) { @@ -477,11 +520,38 @@ void _st_del_sleep_q(_st_thread_t *thread) void _st_vp_check_clock(void) { _st_thread_t *thread; - st_utime_t elapsed, now; - + st_utime_t now; +#if defined(DEBUG) && defined(DEBUG_STATS) + st_utime_t elapsed; +#endif + now = st_utime(); - elapsed = now - _ST_LAST_CLOCK; +#if defined(DEBUG) && defined(DEBUG_STATS) + elapsed = now < _ST_LAST_CLOCK? 0 : now - _ST_LAST_CLOCK; // Might step back. +#endif _ST_LAST_CLOCK = now; + + #if defined(DEBUG) && defined(DEBUG_STATS) + if (elapsed <= 10000) { + ++_st_stat_sched_15ms; + } else if (elapsed <= 21000) { + ++_st_stat_sched_20ms; + } else if (elapsed <= 25000) { + ++_st_stat_sched_25ms; + } else if (elapsed <= 30000) { + ++_st_stat_sched_30ms; + } else if (elapsed <= 35000) { + ++_st_stat_sched_35ms; + } else if (elapsed <= 40000) { + ++_st_stat_sched_40ms; + } else if (elapsed <= 80000) { + ++_st_stat_sched_80ms; + } else if (elapsed <= 160000) { + ++_st_stat_sched_160ms; + } else { + ++_st_stat_sched_s; + } + #endif if (_st_curr_time && now - _st_last_tset > 999000) { _st_curr_time = time(NULL); @@ -502,11 +572,41 @@ void _st_vp_check_clock(void) /* Make thread runnable */ ST_ASSERT(!(thread->flags & _ST_FL_IDLE_THREAD)); thread->state = _ST_ST_RUNNABLE; - _ST_ADD_RUNQ(thread); + // Insert at the head of RunQ, to execute timer first. + _ST_INSERT_RUNQ(thread); } } +void st_thread_yield() +{ + _st_thread_t *me = _ST_CURRENT_THREAD(); + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_thread_yield; + #endif + + /* Check sleep queue for expired threads */ + _st_vp_check_clock(); + + // If not thread in RunQ to yield to, ignore and continue to run. + if (_ST_RUNQ.next == &_ST_RUNQ) { + return; + } + + #if defined(DEBUG) && defined(DEBUG_STATS) + ++_st_stat_thread_yield2; + #endif + + // Append thread to the tail of RunQ, we will back after all threads executed. + me->state = _ST_ST_RUNNABLE; + _ST_ADD_RUNQ(me); + + // Yield to other threads in the RunQ. + _ST_SWITCH_CONTEXT(me); +} + + void st_thread_interrupt(_st_thread_t *thread) { /* If thread is already dead */ @@ -527,24 +627,12 @@ void st_thread_interrupt(_st_thread_t *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) @@ -555,23 +643,7 @@ _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinabl 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); @@ -581,21 +653,7 @@ _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinabl 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 *)); @@ -604,21 +662,9 @@ _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinabl 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 - + + _ST_INIT_CONTEXT(thread, stack->sp, _st_thread_main); + /* If thread is joinable, allocate a termination condition variable */ if (joinable) { thread->term = st_cond_new(); @@ -652,7 +698,6 @@ _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) @@ -665,20 +710,20 @@ int _st_iterate_threads_flag = 0; void _st_iterate_threads(void) { - static _st_thread_t *thread = NULL; - static jmp_buf orig_jb, save_jb; + static __thread _st_thread_t *thread = NULL; + static __thread _st_jmp_buf_t orig_jb, save_jb; _st_clist_t *q; if (!_st_iterate_threads_flag) { if (thread) { - memcpy(thread->context, save_jb, sizeof(jmp_buf)); + memcpy(thread->context, save_jb, sizeof(_st_jmp_buf_t)); MD_LONGJMP(orig_jb, 1); } return; } if (thread) { - memcpy(thread->context, save_jb, sizeof(jmp_buf)); + memcpy(thread->context, save_jb, sizeof(_st_jmp_buf_t)); _st_show_thread_stack(thread, NULL); } else { if (MD_SETJMP(orig_jb)) { @@ -698,7 +743,7 @@ void _st_iterate_threads(void) thread = _ST_THREAD_THREADQ_PTR(q); if (thread == _ST_CURRENT_THREAD()) MD_LONGJMP(orig_jb, 1); - memcpy(save_jb, thread->context, sizeof(jmp_buf)); + memcpy(save_jb, thread->context, sizeof(_st_jmp_buf_t)); MD_LONGJMP(thread->context, 1); } #endif /* DEBUG */ diff --git a/3rdparty/st-srs/stk.c b/3rdparty/st-srs/stk.c index 3e681e5..02088a4 100644 --- a/3rdparty/st-srs/stk.c +++ b/3rdparty/st-srs/stk.c @@ -1,4 +1,6 @@ -/* +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* * 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 @@ -50,18 +52,21 @@ /* 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; +__thread _st_clist_t _st_free_stacks; +__thread int _st_num_free_stacks = 0; +__thread int _st_randomize_stacks = 0; static char *_st_new_stk_segment(int size); +static void _st_delete_stk_segment(char *vaddr, int size); _st_stack_t *_st_stack_new(int stack_size) { _st_clist_t *qp; _st_stack_t *ts; int extra; - + + /* If cache stack, we try to use stack from the cache list. */ +#ifdef MD_CACHE_STACK 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) { @@ -73,11 +78,34 @@ _st_stack_t *_st_stack_new(int stack_size) return ts; } } +#endif + + extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0; + /* If not cache stack, we will free all stack in the list, which contains the stack to be freed. + * Note that we should never directly free it at _st_stack_free, because it is still be used, + * and will cause crash. */ +#ifndef MD_CACHE_STACK + for (qp = _st_free_stacks.next; qp != &_st_free_stacks;) { + ts = _ST_THREAD_STACK_PTR(qp); + /* Before qp is freed, move to next one, because the qp will be freed when free the ts. */ + qp = qp->next; + + ST_REMOVE_LINK(&ts->links); + _st_num_free_stacks--; + +#if defined(DEBUG) && !defined(MD_NO_PROTECT) + mprotect(ts->vaddr, REDZONE, PROT_READ | PROT_WRITE); + mprotect(ts->stk_top + extra, REDZONE, PROT_READ | PROT_WRITE); +#endif + + _st_delete_stk_segment(ts->vaddr, ts->vaddr_size); + free(ts); + } +#endif /* 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) { @@ -87,8 +115,9 @@ _st_stack_t *_st_stack_new(int stack_size) ts->stk_size = stack_size; ts->stk_bottom = ts->vaddr + REDZONE; ts->stk_top = ts->stk_bottom + stack_size; - -#ifdef DEBUG + + /* For example, in OpenWRT, the memory at the begin minus 16B by mprotect is read-only. */ +#if defined(DEBUG) && !defined(MD_NO_PROTECT) mprotect(ts->vaddr, REDZONE, PROT_NONE); mprotect(ts->stk_top + extra, REDZONE, PROT_NONE); #endif @@ -111,7 +140,7 @@ 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++; @@ -149,8 +178,6 @@ static char *_st_new_stk_segment(int size) } -/* Not used */ -#if 0 void _st_delete_stk_segment(char *vaddr, int size) { #ifdef MALLOC_STACK @@ -159,7 +186,6 @@ void _st_delete_stk_segment(char *vaddr, int size) (void) munmap(vaddr, size); #endif } -#endif int st_randomize_stacks(int on) { diff --git a/3rdparty/st-srs/sync.c b/3rdparty/st-srs/sync.c index 907fdfa..74138bd 100644 --- a/3rdparty/st-srs/sync.c +++ b/3rdparty/st-srs/sync.c @@ -1,4 +1,6 @@ -/* +/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ + +/* * 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 @@ -45,9 +47,9 @@ #include "common.h" -extern time_t _st_curr_time; -extern st_utime_t _st_last_tset; -extern int _st_active_count; +extern __thread time_t _st_curr_time; +extern __thread st_utime_t _st_last_tset; +extern __thread int _st_active_count; static st_utime_t (*_st_utime)(void) = NULL; diff --git a/3rdparty/st-srs/tools/backtrace/.gitignore b/3rdparty/st-srs/tools/backtrace/.gitignore new file mode 100644 index 0000000..f587f48 --- /dev/null +++ b/3rdparty/st-srs/tools/backtrace/.gitignore @@ -0,0 +1 @@ +backtrace \ No newline at end of file diff --git a/3rdparty/st-srs/tools/backtrace/backtrace.c b/3rdparty/st-srs/tools/backtrace/backtrace.c new file mode 100644 index 0000000..0af24cf --- /dev/null +++ b/3rdparty/st-srs/tools/backtrace/backtrace.c @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2013-2024 The SRS Authors */ + +#ifdef __linux__ +#define _GNU_SOURCE +#endif + +#include +#include +#include + +#include + +#ifdef __linux__ +#include +#include +#include + +void* parse_symbol_offset(char* frame) +{ + char* p = NULL; + char* p_symbol = NULL; + int nn_symbol = 0; + char* p_offset = NULL; + int nn_offset = 0; + + // Read symbol and offset, for example: + // /tools/backtrace(foo+0x1820) [0x555555555820] + for (p = frame; *p; p++) { + if (*p == '(') { + p_symbol = p + 1; + } else if (*p == '+') { + if (p_symbol) nn_symbol = p - p_symbol; + p_offset = p + 1; + } else if (*p == ')') { + if (p_offset) nn_offset = p - p_offset; + } + } + if (!nn_symbol && !nn_offset) { + return NULL; + } + + // Convert offset(0x1820) to pointer, such as 0x1820. + char tmp[128]; + if (!nn_offset || nn_offset >= sizeof(tmp)) { + return NULL; + } + + int r0 = EOF; + void* offset = NULL; + tmp[nn_offset] = 0; + if ((r0 = sscanf(strncpy(tmp, p_offset, nn_offset), "%p", &offset)) == EOF) { + return NULL; + } + + // Covert symbol(foo) to offset, such as 0x2fba. + if (!nn_symbol || nn_symbol >= sizeof(tmp)) { + return offset; + } + + void* object_file; + if ((object_file = dlopen(NULL, RTLD_LAZY)) == NULL) { + return offset; + } + + void* address; + tmp[nn_symbol] = 0; + if ((address = dlsym(object_file, strncpy(tmp, p_symbol, nn_symbol))) == NULL) { + dlclose(object_file); + return offset; + } + + Dl_info symbol_info; + if ((r0 = dladdr(address, &symbol_info)) == 0) { + dlclose(object_file); + return offset; + } + + dlclose(object_file); + return symbol_info.dli_saddr - symbol_info.dli_fbase + offset; +} + +char* addr2line_format(void* addr, char* symbol, char* buffer, int nn_buffer) +{ + char cmd[512] = {0}; + int r0 = snprintf(cmd, sizeof(cmd), "addr2line -C -p -s -f -a -e %s %p", "backtrace", addr - 1); + if (r0 < 0 || r0 >= sizeof(cmd)) return symbol; + + FILE* fp = popen(cmd, "r"); + if (!fp) return symbol; + + char* p = fgets(buffer, nn_buffer, fp); + pclose(fp); + + if (p == NULL) return symbol; + if ((r0 = strlen(p)) == 0) return symbol; + + // Trait the last newline if exists. + if (p[r0 - 1] == '\n') p[r0 - 1] = '\0'; + + // Find symbol not match by addr2line, like + // 0x0000000000021c87: ?? ??:0 + // 0x0000000000002ffa: _start at ??:? + for (p = buffer; p < buffer + r0 - 1; p++) { + if (p[0] == '?' && p[1] == '?') return symbol; + } + + return buffer; +} +#endif + +#ifdef __linux__ +void bar2() +{ + void* addresses[64]; + int nn_addresses = backtrace(addresses, sizeof(addresses) / sizeof(void*)); + printf("\naddresses:\n"); + for (int i = 0; i < nn_addresses; i++) { + printf("%p\n", addresses[i]); + } + + char** symbols = backtrace_symbols(addresses, nn_addresses); + printf("\nsymbols:\n"); + for (int i = 0; i < nn_addresses; i++) { + printf("%s\n", symbols[i]); + } + + char buffer[128]; + printf("\nframes:\n"); + for (int i = 0; i < nn_addresses; i++) { + void* frame = parse_symbol_offset(symbols[i]); + char* fmt = addr2line_format(frame, symbols[i], buffer, sizeof(buffer)); + int parsed = (fmt == buffer); + printf("%p %d %s\n", frame, parsed, fmt); + } + + free(symbols); + + printf("bar2 OK\n"); + return; +} +#endif + +int always_use_builtin = 0; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wframe-address" +void bar() { + // Each item in the array pointed to by buffer is of type void *, and is the return address from the corresponding + // stack frame. + void* addresses[64]; + int nn_addresses = backtrace(addresses, sizeof(addresses) / sizeof(void*)); + + if (!nn_addresses || always_use_builtin) { + printf("Try to get return addresses by __builtin_return_address\n"); + void* p = NULL; nn_addresses = 0; + if ((p = __builtin_return_address(0)) != NULL) { + addresses[nn_addresses++] = p; + if ((p = __builtin_return_address(1)) != NULL) { + addresses[nn_addresses++] = p; + if ((p = __builtin_return_address(2)) != NULL) { + addresses[nn_addresses++] = p; + if ((p = __builtin_return_address(3)) != NULL) { + addresses[nn_addresses++] = p; + if ((p = __builtin_return_address(4)) != NULL) { + addresses[nn_addresses++] = p; + if ((p = __builtin_return_address(5)) != NULL) { + addresses[nn_addresses++] = p; + } + } + } + } + } + } + } + + char** symbols = backtrace_symbols(addresses, nn_addresses); + printf("nn_addresses=%d, symbols=%p, symbols[0]=%p\n", nn_addresses, symbols, symbols[0]); + + printf("\naddresses:\n"); + for (int i = 0; i < nn_addresses; i++) { + printf("%p\n", addresses[i]); + } + + printf("\nsymbols:\n"); + for (int i = 0; i < nn_addresses; i++) { + printf("%s\n", symbols[i]); + } + free(symbols); + + printf("bar OK\n"); + return; +} +#pragma GCC diagnostic pop + +void foo() { + bar(); +#ifdef __linux__ + bar2(); +#endif + + printf("foo OK\n"); + return; +} + +void* start(void* arg) +{ + foo(); + + printf("coroutine OK\n"); + return NULL; +} + +int main(int argc, char** argv) +{ + if (argc > 1) { + always_use_builtin = 1; + } + + st_init(); + + st_thread_create(start, NULL, 0, 0); + st_thread_exit(NULL); + + printf("main done\n"); + return 0; +} + diff --git a/3rdparty/st-srs/tools/helloworld/.gitignore b/3rdparty/st-srs/tools/helloworld/.gitignore new file mode 100644 index 0000000..31e0fce --- /dev/null +++ b/3rdparty/st-srs/tools/helloworld/.gitignore @@ -0,0 +1 @@ +helloworld diff --git a/3rdparty/st-srs/tools/helloworld/helloworld.c b/3rdparty/st-srs/tools/helloworld/helloworld.c new file mode 100644 index 0000000..5c30d69 --- /dev/null +++ b/3rdparty/st-srs/tools/helloworld/helloworld.c @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2013-2024 The SRS Authors */ + +#include + +#include + +int main(int argc, char** argv) +{ + st_init(); + + int i; + for (i = 0; i < 10000; i++) { + printf("#%03d, Hello, state-threads world!\n", i); + st_sleep(1); + } + + return 0; +} + diff --git a/3rdparty/st-srs/tools/jmpbuf/.gitignore b/3rdparty/st-srs/tools/jmpbuf/.gitignore new file mode 100644 index 0000000..c3a424a --- /dev/null +++ b/3rdparty/st-srs/tools/jmpbuf/.gitignore @@ -0,0 +1,3 @@ +jmpbuf +jmpbuf.E.txt + diff --git a/3rdparty/st-srs/tools/jmpbuf/jmpbuf.c b/3rdparty/st-srs/tools/jmpbuf/jmpbuf.c new file mode 100644 index 0000000..ebde2f5 --- /dev/null +++ b/3rdparty/st-srs/tools/jmpbuf/jmpbuf.c @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2022 Winlin */ + +#include +#include + +/* We define the jmpbuf, because the system's is different in different OS */ +typedef struct _st_jmp_buf { + /* + * OS CPU SIZE + * Darwin __amd64__/__x86_64__ long[8] + * Darwin __aarch64__ long[22] + * Linux __i386__ long[6] + * Linux __amd64__/__x86_64__ long[8] + * Linux __aarch64__ long[22] + * Linux __arm__ long[16] + * Linux __mips__/__mips64 long[13] + * Linux __riscv long[14] + * Linux __loongarch64 long[12] + * Cygwin64 __amd64__/__x86_64__ long[8] + */ + long __jmpbuf[22]; +} _st_jmp_buf_t[1]; + +int main(int argc, char** argv) +{ + jmp_buf ctx = {0}; + int r0 = setjmp(ctx); + int nn_jb = sizeof(ctx); + printf("jmp_buf: r0=%d, sizeof(jmp_buf)=%d (unsigned long long [%d])\n", r0, nn_jb, nn_jb/8); + + _st_jmp_buf_t ctx2 = {0}; + int r1 = sizeof(_st_jmp_buf_t); + int r2 = sizeof(ctx2); + printf("_st_jmp_buf_t: sizeof(_st_jmp_buf_t)=%d/%d (unsigned long long [%d])\n", r1, r2, r2/8); + + return 0; +} + diff --git a/3rdparty/st-srs/tools/pcs/.gitignore b/3rdparty/st-srs/tools/pcs/.gitignore new file mode 100644 index 0000000..42e648f --- /dev/null +++ b/3rdparty/st-srs/tools/pcs/.gitignore @@ -0,0 +1,2 @@ +pcs + diff --git a/3rdparty/st-srs/tools/pcs/pcs.c b/3rdparty/st-srs/tools/pcs/pcs.c new file mode 100644 index 0000000..27e65db --- /dev/null +++ b/3rdparty/st-srs/tools/pcs/pcs.c @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2022 Winlin */ + +void foo() { +} + +void foo2(char a) { +} + +void foo3(int a) { +} + +void foo4(long a) { +} + +void foo5(long long a) { +} + +long foo6(long a) { + return a + 1; +} + +// Note: Use b *main to set to the first instruction of main, +// see https://stackoverflow.com/questions/40960758/break-main-vs-break-main-in-gdb +int main(int argc, char** argv) +{ + foo(); + foo2('s'); + foo3(0x7); + foo4(0x7); + foo5(0x7); + foo6(0x7); + return 0; +} + diff --git a/3rdparty/st-srs/tools/porting/.gitignore b/3rdparty/st-srs/tools/porting/.gitignore new file mode 100644 index 0000000..d9bcb42 --- /dev/null +++ b/3rdparty/st-srs/tools/porting/.gitignore @@ -0,0 +1 @@ +porting diff --git a/3rdparty/st-srs/tools/porting/porting.c b/3rdparty/st-srs/tools/porting/porting.c new file mode 100644 index 0000000..7267ac7 --- /dev/null +++ b/3rdparty/st-srs/tools/porting/porting.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2013-2024 The SRS Authors */ + +#include +#include +#include +#include + +void bar() +{ +} + +void foo() +{ + bar(); +} + +int main(int argc, char** argv) +{ + printf("OS specs:\n"); +#ifdef __linux__ + printf("__linux__: %d\n", __linux__); +#endif +#ifdef __APPLE__ + printf("__APPLE__: %d\n", __APPLE__); +#endif +#ifdef __CYGWIN__ + printf("__CYGWIN__: %d\n", __CYGWIN__); +#endif +#ifdef _WIN32 + printf("_WIN32: %d\n", _WIN32); +#endif + + printf("\nCPU specs:\n"); +#ifdef __mips__ + // 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 __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"); +#ifdef __GLIBC__ + printf("__GLIBC__: %d\n", __GLIBC__); +#endif + + printf("\nCalling conventions:\n"); + foo(); + + printf("\nCall setjmp:\n"); + jmp_buf ctx; + if (!setjmp(ctx)) { + printf("Call longjmp with return=1\n"); + longjmp(ctx, 1); + + // Not reachable code. + printf("Should never be here.\n"); + } + + printf("\nDone\n"); + return 0; +} + diff --git a/3rdparty/st-srs/tools/stack/.gitignore b/3rdparty/st-srs/tools/stack/.gitignore new file mode 100644 index 0000000..881ae71 --- /dev/null +++ b/3rdparty/st-srs/tools/stack/.gitignore @@ -0,0 +1,2 @@ +stack + diff --git a/3rdparty/st-srs/tools/stack/stack.c b/3rdparty/st-srs/tools/stack/stack.c new file mode 100644 index 0000000..e634960 --- /dev/null +++ b/3rdparty/st-srs/tools/stack/stack.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2022 Winlin */ + +long foo() { + char c; + int i; + long l; + long long ll; + return c + i + l + ll; +} + +int main(int argc, char** argv) +{ + foo(); + return 0; +} + diff --git a/3rdparty/st-srs/tools/verify/.gitignore b/3rdparty/st-srs/tools/verify/.gitignore new file mode 100644 index 0000000..0b59873 --- /dev/null +++ b/3rdparty/st-srs/tools/verify/.gitignore @@ -0,0 +1 @@ +verify diff --git a/3rdparty/st-srs/tools/verify/verify.c b/3rdparty/st-srs/tools/verify/verify.c new file mode 100644 index 0000000..ea96e23 --- /dev/null +++ b/3rdparty/st-srs/tools/verify/verify.c @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2013-2024 The SRS Authors */ + +#include + +#include +#include + +st_mutex_t lock; +st_cond_t cond; + +void* start(void* arg) +{ + printf("ST: thread run\n"); + + printf("ST: thread wait for a while\n"); + st_usleep(1.5 * 1000 * 1000); + printf("ST: thread wait done\n"); + + int r0 = st_cond_signal(cond); + printf("ST: thread cond signal, r0=%d\n", r0); + + printf("ST: thread lock\n"); + r0 = st_mutex_lock(lock); + assert(r0 == 0); + + r0 = st_mutex_unlock(lock); + printf("ST: thread unlock\n"); + + return NULL; +} + +int main(int argc, char** argv) +{ + int r0 = st_init(); + assert(r0 == 0); + printf("ST: main init ok\n"); + + lock = st_mutex_new(); + cond = st_cond_new(); + + st_thread_t trd = st_thread_create(start, NULL, 1, 0); + printf("ST: main create ok\n"); + + printf("ST: main lock\n"); + r0 = st_mutex_lock(lock); + assert(r0 == 0); + + printf("ST: main cond waiting\n"); + r0 = st_cond_wait(cond); + printf("ST: main cond wait ok, r0=%d\n", r0); + + r0 = st_mutex_unlock(lock); + printf("ST: main unlock\n"); + + st_thread_join(trd, NULL); + printf("ST: main done\n"); + + st_mutex_destroy(lock); + st_cond_destroy(cond); + + return 0; +} + diff --git a/3rdparty/st-srs/utest/st_utest.cpp b/3rdparty/st-srs/utest/st_utest.cpp new file mode 100644 index 0000000..4571bfb --- /dev/null +++ b/3rdparty/st-srs/utest/st_utest.cpp @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2013-2024 The SRS Authors */ + +#include + +#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) { + // Select the best event system available on the OS. In Linux this is + // epoll(). On BSD it will be kqueue. On Cygwin it will be select. +#if __CYGWIN__ + assert(st_set_eventsys(ST_EVENTSYS_SELECT) != -1); +#else + assert(st_set_eventsys(ST_EVENTSYS_ALT) != -1); +#endif + + // Initialize state-threads, create idle coroutine. + assert(st_init() == 0); + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +// basic test and samples. +VOID TEST(SampleTest, ExampleIntSizeTest) +{ + EXPECT_EQ(1, (int)sizeof(int8_t)); + EXPECT_EQ(2, (int)sizeof(int16_t)); + EXPECT_EQ(4, (int)sizeof(int32_t)); + EXPECT_EQ(8, (int)sizeof(int64_t)); +} + diff --git a/3rdparty/st-srs/utest/st_utest.hpp b/3rdparty/st-srs/utest/st_utest.hpp new file mode 100644 index 0000000..26d9933 --- /dev/null +++ b/3rdparty/st-srs/utest/st_utest.hpp @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2013-2024 The SRS Authors */ + +#ifndef ST_UTEST_PUBLIC_HPP +#define ST_UTEST_PUBLIC_HPP + +// Before define the private/protected, we must include some system header files. +// Or it may fail with: +// redeclared with different access struct __xfer_bufptrs +// @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; if (trd) st_thread_join(trd, (void**)&r0); SrsUniquePtr r0_uptr(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/3rdparty/st-srs/utest/st_utest_coroutines.cpp b/3rdparty/st-srs/utest/st_utest_coroutines.cpp new file mode 100644 index 0000000..fe57aa7 --- /dev/null +++ b/3rdparty/st-srs/utest/st_utest_coroutines.cpp @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2013-2024 The SRS Authors */ + +#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/3rdparty/st-srs/utest/st_utest_tcp.cpp b/3rdparty/st-srs/utest/st_utest_tcp.cpp new file mode 100644 index 0000000..ecbd839 --- /dev/null +++ b/3rdparty/st-srs/utest/st_utest_tcp.cpp @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2013-2024 The SRS Authors */ + +#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); +} +