diff --git a/README.md b/README.md index a3c1058101..f867be0587 100755 --- a/README.md +++ b/README.md @@ -177,6 +177,8 @@ The ports used by SRS: ## V5 changes +* v5.0, 2021-04-07, Threads: Support multiple hybrid threads, [#2188](https://github.com/ossrs/srs/issues/2188). 5.0.3 +* v5.0, 2021-03-31, Threads: Support multiple threads with locks, [#2188](https://github.com/ossrs/srs/issues/2188). 5.0.2 * v5.0, 2021-03-17, Live: Refine edge to follow client and HTTP/302. 5.0.1 * v5.0, 2021-03-15, Init SRS/5. 5.0.0 @@ -1119,7 +1121,6 @@ The data for publishing RTMP was benchmarked by [SB][srs-bench]: The data for playing HTTP FLV was benchmarked by [SB][srs-bench]: - | Update | SRS | Clients | Type | CPU | Memory | Commit | | ------------- | --------- | ------------- | ------------- | --------- | -------- | ------------ | | 2014-05-25 | 2.0.171 | 6.0k(6000) | players | 84% | 297MB | [code][p20] | @@ -1133,25 +1134,33 @@ The data for playing HTTP FLV was benchmarked by [SB][srs-bench]: The RTC benchmark data, by [srs-bench](https://github.com/ossrs/srs-bench/tree/feature/rtc#usage): - -| Update | SRS | Clients | Type | CPU | Memory | Threads | -| ------------- | --------- | ------------- | ------------- | --------- | -------- | ------- | -| 2021-03-31 | 4.0.87 | 550 | publishers | ~86% | 1.3GB | 1 | -| 2021-03-31 | 4.0.87 | 800 | players | ~94% | 444MB | 1 | - -> Note: CentOS7, 500Kbps, 4CPU, 2.5 GHz Intel Xeon Platinum 8163/8269CY. +| Update | Server | Clients | Type | CPU | Memory | Threads | Commit | +| ------------- | ------------ | ----- | ---------- | -------- | -------- | ----- | --------- | +| 2021-04-07 | SRS/5.0.3 | 10000 | publishers | ~90% x 32 | 28GB | 33 | [#2188](https://github.com/ossrs/srs/issues/2188#issuecomment-816309097) | +| 2021-04-20 | Janus/0.11.1 | 4000 | publishers | ~90% x 32 | 790MB | 51 | [#2629](https://github.com/meetecho/janus-gateway/pull/2629#issuecomment-822914989) | +| 2021-04-07 | SRS/5.0.3 | 3400 | publishers | ~95% x 8 | 6.3GB | 12 | [#2188](https://github.com/ossrs/srs/issues/2188#issuecomment-816309097) | +| 2021-04-20 | Janus/0.11.1 | 1500 | publishers | ~95% x 8 | 276MB | 26 | [#2629](https://github.com/meetecho/janus-gateway/pull/2629#issuecomment-822914989) | +| 2021-04-07 | SRS/5.0.3 | 2000 | publishers | ~95% x 4 | 4.1GB | 8 | [#2188](https://github.com/ossrs/srs/issues/2188#issuecomment-816309097) | +| 2021-03-31 | SRS/5.0.2 | 1400 | publishers | ~90% x 4 | 3.1GB | 6 | [#2188](https://github.com/ossrs/srs/issues/2188#issuecomment-812499542) | +| 2021-03-31 | SRS/5.0.2 | 1400 | players | ~93% x 4 | 1.0GB | 6 | [#2188](https://github.com/ossrs/srs/issues/2188#issuecomment-812499542) | +| 2021-04-20 | Janus/0.11.1 | 750 | publishers | ~90% x 4 | 142MB | 23 | [#2629](https://github.com/meetecho/janus-gateway/pull/2629#issuecomment-822914989) | +| 2021-04-20 | Janus/0.11.1 | 750 | players | ~92% x 4 | 283MB | 23 | [#2629](https://github.com/meetecho/janus-gateway/pull/2629#issuecomment-822914989) | +| 2021-03-31 | SRS/4.0.87 | 550 | publishers | ~86% x 1 | 1.3GB | 1 | | +| 2021-03-31 | SRS/4.0.87 | 800 | players | ~94% x 1 | 444MB | 1 | | + +> Note: The benchmark tool for Janus is [srs-bench](https://github.com/ossrs/srs-bench/tree/feature/rtc#janus), and startup script by [janus-docker](https://github.com/winlinvip/janus-docker#usage). **Latency benchmark** The latency between encoder and player with realtime config([CN][v4_CN_LowLatency], [EN][v4_EN_LowLatency]): -| -| Update | SRS | VP6 | H.264 | VP6+MP3 | H.264+MP3 | -| ------------- | --------- | --------- | --------- | --------- | -------- | -| 2014-12-16 | 2.0.72 | 0.1s | 0.4s |[0.8s][p15]|[0.6s][p16]| -| 2014-12-12 | 2.0.70 |[0.1s][p13]|[0.4s][p14]| 1.0s | 0.9s | -| 2014-12-03 | 1.0.10 | 0.4s | 0.4s | 0.9s | 1.2s | +| Update | SRS | Protocol | VP6 | H.264 | VP6+MP3 | H.264+MP3 | +| ------------- | --------- | --------- | --------- | --------- | --------- | -------- | +| 2014-12-16 | 2.0.72 | RTMP | 0.1s | 0.4s |[0.8s][p15]|[0.6s][p16]| +| 2014-12-12 | 2.0.70 | RTMP |[0.1s][p13]|[0.4s][p14]| 1.0s | 0.9s | +| 2014-12-03 | 1.0.10 | RTMP | 0.4s | 0.4s | 0.9s | 1.2s | +| 2021-04-02 | 4.0.87 | WebRTC | x | 80ms | x | x | > 2018-08-05, [c45f72e](https://github.com/ossrs/srs/commit/c45f72ef7bac9c7cf85b9125fc9e3aafd53f396f), Refine HTTP-FLV latency, support realtime mode. 2.0.252 @@ -1159,35 +1168,6 @@ We used FMLE as encoder for benchmark. The latency of server was 0.1s+, and the bottleneck was the encoder. For more information, read [bug #257][bug #257-c0]. - -**HLS overhead** - -About the overhead of HLS overhead, we compared FFMPEG and SRS. - -| Bitrate | Duration | FLV(KB) | HLS(KB) | Overhead | -| ------- | -------- | ------- | -------- | --------- | -| 275kbps | 600s | 11144 | 12756 | 14.46% | -| 260kbps | 1860s | 59344 | 68004 | 14.59% | -| 697kbps | 60s | 5116 | 5476 | 7.03% | -| 565kbps | 453s | 31316 | 33544 | 7.11% | -| 565kbps | 1813s | 125224 | 134140 | 7.12% | -| 861kbps | 497s | 52316 | 54924 | 4.98% | -| 857kbps | 1862s | 195008 | 204768 | 5.00% | -| 1301kbps | 505s | 80320 | 83676 | 4.17% | -| 1312kbps | 1915s | 306920 | 319680 | 4.15% | -| 2707kbps | 600s | 198356 | 204560 | 3.12% | -| 2814kbps | 1800s | 618456 | 637660 | 3.10% | -| 2828kbps | 60s | 20716 | 21356 | 3.08% | -| 2599kbps | 307s | 97580 | 100672 | 3.16% | -| 2640kbps | 1283s | 413880 | 426912 | 3.14% | -| 5254kbps | 71s | 45832 | 47056 | 2.67% | -| 5147kbps | 370s | 195040 | 200280 | 2.68% | -| 5158kbps | 1327s | 835664 | 858092 | 2.68% | - -The HLS overhead is calc by: (HLS - FLV) / FLV * 100%. - -The overhead should be larger than this benchmark(48kbps audio is best overhead), for we fix the [#512][bug #512]. - ## Architecture SRS always use the simplest architecture to solve complex domain problems. diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh index 5aae95c9a7..5ccffcbb21 100755 --- a/trunk/auto/depends.sh +++ b/trunk/auto/depends.sh @@ -488,7 +488,11 @@ fi # Affected users should upgrade to OpenSSL 1.1.0e. Users unable to immediately # upgrade can alternatively recompile OpenSSL with -DOPENSSL_NO_HEARTBEATS. if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL != YES ]]; then - OPENSSL_OPTIONS="-no-shared -no-threads -DOPENSSL_NO_HEARTBEATS" + # Should never disable threads by -no-threads, because we're now multiple threading. + # @see https://www.openssl.org/blog/blog/2017/02/21/threads/ + # @see https://github.com/openssl/openssl/issues/2165 + # @see https://curl.se/libcurl/c/opensslthreadlock.html + OPENSSL_OPTIONS="-no-shared -DOPENSSL_NO_HEARTBEATS" OPENSSL_CONFIG="./config" # https://stackoverflow.com/questions/15539062/cross-compiling-of-openssl-for-linux-arm-v5te-linux-gnueabi-toolchain if [[ $SRS_CROSS_BUILD == YES ]]; then @@ -527,7 +531,7 @@ if [[ $SRS_SSL == YES && $SRS_USE_SYS_SSL != YES ]]; then fi # # https://wiki.openssl.org/index.php/Compilation_and_Installation#Configure_Options - # Already defined: -no-shared -no-threads -no-asm + # Already defined: -no-shared -no-asm # Should enable: -no-dtls -no-dtls1 -no-ssl3 # Might able to disable: -no-ssl2 -no-comp -no-idea -no-hw -no-engine -no-dso -no-err -no-nextprotoneg -no-psk -no-srp -no-ec2m -no-weak-ssl-ciphers # Note that we do not disable more features, because no file could be removed. diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 99a2265f45..ec2f88bc78 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -3,10 +3,6 @@ ############################################################################################# # RTMP sections ############################################################################################# -# the rtmp listen ports, split by space, each listen entry is <[ip:]port> -# for example, 192.168.1.100:1935 10.10.10.100:1935 -# where the ip is optional, default to 0.0.0.0, that is 1935 equals to 0.0.0.0:1935 -listen 1935; # the pid file # to ensure only one process can use a pid file # and provides the current running process id, for script, @@ -41,10 +37,11 @@ srs_log_level trace; # when srs_log_tank is file, specifies the log file. # default: ./objs/srs.log srs_log_file ./objs/srs.log; -# the max connections. -# if exceed the max connections, server will drop the new connection. -# default: 1000 -max_connections 1000; +# The interval in ms, to flush async log. Generally, we flush from +# coroutine-queue to thread-queue, then from thread-queue to disk. +# So the delay of logs might be 2*srs_log_flush_interval. +# Default: 1300 +srs_log_flush_interval 1300; # whether start as daemon # @remark: do not support reload. # default: on @@ -111,6 +108,66 @@ auto_reload_for_docker on; # default: 0.8 tcmalloc_release_rate 0.8; +# For thread pool. +threads { + # The thread pool manager cycle interval, in seconds. + # Default: 5 + interval 5; + # The number of hybrid threads to use, MUST >=1. + # Note that there MUST be same number of stream sections. + # Max to 64 threads. + # Default: 1 + hybrids 1; + # Whether automatically generate stream config by hybrid. + # If off, user must create a number of streams, which is equal to the hybrids. + # Default: off + generate_streams off; + # CPU set for affinity, for example: + # 0 means CPU0 + # 0-3 means CPU0, CPU1, CPU2 + # 1-63 means all CPUs except CPU0 + # Default: 0-63 + cpu_affinity { + # For master thread manager. + master 0-63; + # For hybrid server or services. + hybrid 0-63; + # For log writing thread. + log 0-63; + } +} + +# For system circuit breaker. +circuit_breaker { + # Whether enable the circuit breaker. + # Default: on + enabled on; + # The CPU percent(0, 100) ever 1s, as system high water-level, which enable the circuit-break + # mechanism, for example, NACK will be disabled if high water-level. + # Default: 90 + high_threshold 90; + # Reset the high water-level, if number of pulse under high_threshold. + # @remark 0 to disable the high water-level. + # Default: 2 + high_pulse 2; + # The CPU percent(0, 100) ever 1s, as system critical water-level, which enable the circuit-break + # mechanism, for example, TWCC will be disabled if high water-level. + # @note All circuit-break mechanism of high-water-level scope are enabled in critical. + # Default: 95 + critical_threshold 95; + # Reset the critical water-level, if number of pulse under critical_threshold. + # @remark 0 to disable the critical water-level. + # Default: 1 + critical_pulse 1; + # If dying, also drop packets for players. + # Default: 99 + dying_threshold 99; + # If CPU exceed the dying_pulse times, enter dying. + # @remark 0 to disable the dying water-level. + # Default: 5 + dying_pulse 5; +} + ############################################################################################# # heartbeat/stats sections ############################################################################################# @@ -218,49 +275,219 @@ http_api { cert ./conf/server.crt; } } -# embedded http server in srs. -# the http streaming config, for HLS/HDS/DASH/HTTPProgressive -# global config for http streaming, user must config the http section for each vhost. -# the embed http server used to substitute nginx in ./objs/nginx, -# for example, srs running in arm, can provides RTMP and HTTP service, only with srs installed. -# user can access the http server pages, generally: -# curl http://192.168.1.170:80/srs.html -# which will show srs version and welcome to srs. -# @remark, the http embedded stream need to config the vhost, for instance, the __defaultVhost__ -# need to open the feature http of vhost. -http_server { - # whether http streaming service is enabled. - # default: off - enabled on; - # the http streaming listen entry is <[ip:]port> - # for example, 192.168.1.100:8080 - # where the ip is optional, default to 0.0.0.0, that is 8080 equals to 0.0.0.0:8080 - # @remark, if use lower port, for instance 80, user must start srs by root. - # default: 8080 - listen 8080; - # the default dir for http root. - # default: ./objs/nginx/html - dir ./objs/nginx/html; - # whether enable crossdomain request. - # for both http static and stream server and apply on all vhosts. - # default: on - crossdomain on; - # For https_server or HTTPS Streaming. - https { - # Whether enable HTTPS Streaming. + +############################################################################################# +# RTMP/HTTP/RTC Stream sections +############################################################################################# +stream { + # the rtmp listen ports, split by space, each listen entry is <[ip:]port> + # for example, 192.168.1.100:1935 10.10.10.100:1935 + # where the ip is optional, default to 0.0.0.0, that is 1935 equals to 0.0.0.0:1935 + listen 1935; + # the max connections. + # if exceed the max connections, server will drop the new connection. + # default: 1000 + max_connections 1000; + + # embedded http server in srs. + # the http streaming config, for HLS/HDS/DASH/HTTPProgressive + # global config for http streaming, user must config the http section for each vhost. + # the embed http server used to substitute nginx in ./objs/nginx, + # for example, srs running in arm, can provides RTMP and HTTP service, only with srs installed. + # user can access the http server pages, generally: + # curl http://192.168.1.170:80/srs.html + # which will show srs version and welcome to srs. + # @remark, the http embedded stream need to config the vhost, for instance, the __defaultVhost__ + # need to open the feature http of vhost. + http_server { + # whether http streaming service is enabled. + # default: off + enabled on; + # the http streaming listen entry is <[ip:]port> + # for example, 192.168.1.100:8080 + # where the ip is optional, default to 0.0.0.0, that is 8080 equals to 0.0.0.0:8080 + # @remark, if use lower port, for instance 80, user must start srs by root. + # default: 8080 + listen 8080; + # the default dir for http root. + # default: ./objs/nginx/html + dir ./objs/nginx/html; + # whether enable crossdomain request. + # for both http static and stream server and apply on all vhosts. + # default: on + crossdomain on; + # For https_server or HTTPS Streaming. + https { + # Whether enable HTTPS Streaming. + # default: off + enabled on; + # The listen endpoint for HTTPS Streaming. + # default: 8088 + listen 8088; + # The SSL private key file, generated by: + # openssl genrsa -out server.key 2048 + # default: ./conf/server.key + key ./conf/server.key; + # The SSL public cert file, generated by: + # openssl req -new -x509 -key server.key -out server.crt -days 3650 -subj "/C=CN/ST=Beijing/L=Beijing/O=Me/OU=Me/CN=ossrs.net" + # default: ./conf/server.crt + cert ./conf/server.crt; + } + } + + rtc_server { + # Whether enable WebRTC server. # default: off enabled on; - # The listen endpoint for HTTPS Streaming. - # default: 8088 - listen 8088; - # The SSL private key file, generated by: - # openssl genrsa -out server.key 2048 - # default: ./conf/server.key - key ./conf/server.key; - # The SSL public cert file, generated by: - # openssl req -new -x509 -key server.key -out server.crt -days 3650 -subj "/C=CN/ST=Beijing/L=Beijing/O=Me/OU=Me/CN=ossrs.net" - # default: ./conf/server.crt - cert ./conf/server.crt; + # The udp listen port, we will reuse it for connections. + # default: 8000 + listen 8000; + # The exposed candidate IPs, response in SDP candidate line. It can be: + # * Retrieve server IP automatically, from all network interfaces. + # eth0 Retrieve server IP by specified network interface name. # TODO: Implements it. + # $CANDIDATE Read the IP from ENV variable, use * if not set. + # x.x.x.x A specified IP address or DNS name, which can be access by client such as Chrome. + # You can specific more than one interface name: + # eth0 eth1 Use network interface eth0 and eth1. # TODO: Implements it. + # Also by IP or DNS names: + # 192.168.1.3 10.1.2.3 rtc.me # TODO: Implements it. + # And by multiple ENV variables: + # $CANDIDATE $EIP # TODO: Implements it. + # @remark For Firefox, the candidate MUST be IP, MUST NOT be DNS name. + # @see https://github.com/ossrs/srs/wiki/v4_CN_RTCWiki#config-candidate + # default: * + candidate *; + # The IP family filter for auto discover candidate, it can be: + # ipv4 Filter IP v4 candidates. + # ipv6 Filter IP v6 candidates. + # all Filter all IP v4 or v6 candidates. + # For example, if set to ipv4, we only use the IPv4 address as candidate. + # default: ipv4 + ip_family ipv4; + # Whether use ECDSA certificate. + # If not, use RSA certificate. + # default: on + ecdsa on; + # Whether encrypt RTP packet by SRTP. + # @remark Should always turn it on, or Chrome will fail. + # default: on + encrypt on; + # We listen multiple times at the same port, by REUSEPORT, to increase the UDP queue. + # Note that you can set to 1 and increase the system UDP buffer size by net.core.rmem_max + # and net.core.rmem_default or just increase this to get larger UDP recv and send buffer. + # default: 1 + reuseport 1; + # Whether merge multiple NALUs into one. + # @see https://github.com/ossrs/srs/issues/307#issuecomment-612806318 + # default: off + merge_nalus off; + # Whether enable the perf stat at http://localhost:1985/api/v1/perf + # TODO: FIXME: We should enable it when refined. + # default: off + perf_stat off; + # For RTP packet and its payload cache. + rtp_cache { + # Whether enable the RTP packet cache. + # default: on + enabled on; + # The cache size for rtp packet in MB, each object is about 300B.. + # default: 64 + pkt_size 64.0; + # The cache size for rtp payload in MB, each object is about 40B. + # default: 16 + payload_size 16.0; + } + # For RTP shared message and the large buffer cache. + rtp_msg_cache { + #Whether enable the RTP message(a large buffer) cache. + # default: on + enabled on; + # The cache size for message object in MB, each object is about 40B. + # default: 16 + msg_size 16.0; + # The cache size for message large buffer in MB, each object is about 1500B. + # default: 512 + buffer_size 512.0; + } + # The black-hole to copy packet to, for debugging. + # For example, when debugging Chrome publish stream, the received packets are encrypted cipher, + # we can set the publisher black-hole, SRS will copy the plaintext packets to black-hole, and + # we are able to capture the plaintext packets by wireshark. + black_hole { + # Whether enable the black-hole. + # default: off + enabled off; + # The black-hole address for session. + addr 127.0.0.1:10000; + } + } +} + +vhost rtc.vhost.srs.com { + rtc { + # Whether enable WebRTC server. + # default: off + enabled on; + # Whether support NACK. + # default: on + nack on; + # Whether directly use the packet, avoid copy. + # default: on + nack_no_copy on; + # Whether support TWCC. + # default: on + twcc on; + # The timeout in seconds for session timeout. + # Client will send ping(STUN binding request) to server, we use it as heartbeat. + # default: 30 + stun_timeout 30; + # The strict check when process stun. + # default: off + stun_strict_check on; + # The role of dtls when peer is actpass: passive or active + # default: passive + dtls_role passive; + # The version of dtls, support dtls1.0, dtls1.2, and auto + # default: auto + dtls_version auto; + # Drop the packet with the pt(payload type), 0 never drop. + # default: 0 + drop_for_pt 0; + ############################################################### + # For transmuxing RTMP to RTC, the strategy for bframe. + # keep Keep bframe, which may make browser with playing problems. + # discard Discard bframe, maybe cause browser with little problems. + # default: discard + bframe discard; + # For transmuxing RTMP to RTC, the strategy for aac audio. + # transcode Transcode aac to opus. + # discard Discard aac audio packet. + # default: transcode + aac transcode; + ############################################################### + # For transmuxing RTC to RTMP. + # Whether trans-mux RTC to RTMP streaming. + # Default: off + rtc_to_rtmp off; + # The PLI interval in seconds, for RTC to RTMP. + # Note the available range is [0.5, 30] + # Default: 6.0 + pli_for_rtmp 6.0; + } + ############################################################### + # For transmuxing RTMP to RTC, it will impact the default values if RTC is on. + # Whether enable min delay mode for vhost. + # default: on, for RTC. + min_latency on; + play { + # set the MW(merged-write) latency in ms. + # @remark For WebRTC, we enable pass-timestamp mode, so we ignore this config. + # default: 0 (For WebRTC) + mw_latency 0; + # Set the MW(merged-write) min messages. + # default: 0 (For Real-Time, that is min_latency on) + # default: 1 (For WebRTC, that is min_latency off) + mw_msgs 0; } } @@ -440,164 +667,6 @@ srt_server { default_app live; } -############################################################################################# -# WebRTC server section -############################################################################################# -rtc_server { - # Whether enable WebRTC server. - # default: off - enabled on; - # The udp listen port, we will reuse it for connections. - # default: 8000 - listen 8000; - # The exposed candidate IPs, response in SDP candidate line. It can be: - # * Retrieve server IP automatically, from all network interfaces. - # eth0 Retrieve server IP by specified network interface name. # TODO: Implements it. - # $CANDIDATE Read the IP from ENV variable, use * if not set. - # x.x.x.x A specified IP address or DNS name, which can be access by client such as Chrome. - # You can specific more than one interface name: - # eth0 eth1 Use network interface eth0 and eth1. # TODO: Implements it. - # Also by IP or DNS names: - # 192.168.1.3 10.1.2.3 rtc.me # TODO: Implements it. - # And by multiple ENV variables: - # $CANDIDATE $EIP # TODO: Implements it. - # @remark For Firefox, the candidate MUST be IP, MUST NOT be DNS name. - # @see https://github.com/ossrs/srs/wiki/v4_CN_RTCWiki#config-candidate - # default: * - candidate *; - # The IP family filter for auto discover candidate, it can be: - # ipv4 Filter IP v4 candidates. - # ipv6 Filter IP v6 candidates. - # all Filter all IP v4 or v6 candidates. - # For example, if set to ipv4, we only use the IPv4 address as candidate. - # default: ipv4 - ip_family ipv4; - # Whether use ECDSA certificate. - # If not, use RSA certificate. - # default: on - ecdsa on; - # Whether encrypt RTP packet by SRTP. - # @remark Should always turn it on, or Chrome will fail. - # default: on - encrypt on; - # We listen multiple times at the same port, by REUSEPORT, to increase the UDP queue. - # Note that you can set to 1 and increase the system UDP buffer size by net.core.rmem_max - # and net.core.rmem_default or just increase this to get larger UDP recv and send buffer. - # default: 1 - reuseport 1; - # Whether merge multiple NALUs into one. - # @see https://github.com/ossrs/srs/issues/307#issuecomment-612806318 - # default: off - merge_nalus off; - # Whether enable the perf stat at http://localhost:1985/api/v1/perf - # TODO: FIXME: We should enable it when refined. - # default: off - perf_stat off; - # For RTP packet and its payload cache. - rtp_cache { - # Whether enable the RTP packet cache. - # default: on - enabled on; - # The cache size for rtp packet in MB, each object is about 300B.. - # default: 64 - pkt_size 64.0; - # The cache size for rtp payload in MB, each object is about 40B. - # default: 16 - payload_size 16.0; - } - # For RTP shared message and the large buffer cache. - rtp_msg_cache { - #Whether enable the RTP message(a large buffer) cache. - # default: on - enabled on; - # The cache size for message object in MB, each object is about 40B. - # default: 16 - msg_size 16.0; - # The cache size for message large buffer in MB, each object is about 1500B. - # default: 512 - buffer_size 512.0; - } - # The black-hole to copy packet to, for debugging. - # For example, when debugging Chrome publish stream, the received packets are encrypted cipher, - # we can set the publisher black-hole, SRS will copy the plaintext packets to black-hole, and - # we are able to capture the plaintext packets by wireshark. - black_hole { - # Whether enable the black-hole. - # default: off - enabled off; - # The black-hole address for session. - addr 127.0.0.1:10000; - } -} - -vhost rtc.vhost.srs.com { - rtc { - # Whether enable WebRTC server. - # default: off - enabled on; - # Whether support NACK. - # default: on - nack on; - # Whether directly use the packet, avoid copy. - # default: on - nack_no_copy on; - # Whether support TWCC. - # default: on - twcc on; - # The timeout in seconds for session timeout. - # Client will send ping(STUN binding request) to server, we use it as heartbeat. - # default: 30 - stun_timeout 30; - # The strict check when process stun. - # default: off - stun_strict_check on; - # The role of dtls when peer is actpass: passive or active - # default: passive - dtls_role passive; - # The version of dtls, support dtls1.0, dtls1.2, and auto - # default: auto - dtls_version auto; - # Drop the packet with the pt(payload type), 0 never drop. - # default: 0 - drop_for_pt 0; - ############################################################### - # For transmuxing RTMP to RTC, the strategy for bframe. - # keep Keep bframe, which may make browser with playing problems. - # discard Discard bframe, maybe cause browser with little problems. - # default: discard - bframe discard; - # For transmuxing RTMP to RTC, the strategy for aac audio. - # transcode Transcode aac to opus. - # discard Discard aac audio packet. - # default: transcode - aac transcode; - ############################################################### - # For transmuxing RTC to RTMP. - # Whether trans-mux RTC to RTMP streaming. - # Default: off - rtc_to_rtmp off; - # The PLI interval in seconds, for RTC to RTMP. - # Note the available range is [0.5, 30] - # Default: 6.0 - pli_for_rtmp 6.0; - } - ############################################################### - # For transmuxing RTMP to RTC, it will impact the default values if RTC is on. - # Whether enable min delay mode for vhost. - # default: on, for RTC. - min_latency on; - play { - # set the MW(merged-write) latency in ms. - # @remark For WebRTC, we enable pass-timestamp mode, so we ignore this config. - # default: 0 (For WebRTC) - mw_latency 0; - # Set the MW(merged-write) min messages. - # default: 0 (For Real-Time, that is min_latency on) - # default: 1 (For WebRTC, that is min_latency off) - mw_msgs 0; - } -} - ############################################################################################# # RTMP/HTTP VHOST sections ############################################################################################# diff --git a/trunk/conf/threads.conf b/trunk/conf/threads.conf new file mode 100644 index 0000000000..630c6a9ee1 --- /dev/null +++ b/trunk/conf/threads.conf @@ -0,0 +1,37 @@ + +daemon off; +srs_log_tank console; + +http_api { + enabled on; + listen 1985; +} + +threads { + hybrids 2; + generate_streams on; +} + +stream { + listen 1935; + max_connections 1000; + http_server { + enabled on; + listen 8080; + } + rtc_server { + enabled on; + listen 8000; + } +} + +vhost __defaultVhost__ { + rtc { + enabled on; + } + http_remux { + enabled on; + mount [vhost]/[app]/[stream].flv; + } +} + diff --git a/trunk/configure b/trunk/configure index 8ae0b701b3..86c1ae204a 100755 --- a/trunk/configure +++ b/trunk/configure @@ -274,7 +274,7 @@ MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_sourc "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" "srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr" - "srs_app_coworkers" "srs_app_hybrid") + "srs_app_coworkers" "srs_app_hybrid" "srs_app_threads") if [[ $SRS_RTC == YES ]]; then MODULE_FILES+=("srs_app_rtc_conn" "srs_app_rtc_dtls" "srs_app_rtc_sdp" "srs_app_rtc_queue" "srs_app_rtc_server" "srs_app_rtc_source" "srs_app_rtc_api") diff --git a/trunk/research/thread-model/.gitignore b/trunk/research/thread-model/.gitignore index 1abd1fc862..5737fff3e8 100644 --- a/trunk/research/thread-model/.gitignore +++ b/trunk/research/thread-model/.gitignore @@ -1,3 +1,4 @@ thread-local udp-connect-client udp-connect-server +extern-main diff --git a/trunk/research/thread-model/extern-extra.cpp b/trunk/research/thread-model/extern-extra.cpp new file mode 100644 index 0000000000..f203776488 --- /dev/null +++ b/trunk/research/thread-model/extern-extra.cpp @@ -0,0 +1,13 @@ + +#include + +int __thread ga = 100; +int __thread gb = 200; + +void* pfn2(void* arg) +{ + printf("Thread2: ga=%d, gb=%d\n", ga, gb); + return NULL; +} + + diff --git a/trunk/research/thread-model/extern-main.cpp b/trunk/research/thread-model/extern-main.cpp new file mode 100644 index 0000000000..33c4f446f9 --- /dev/null +++ b/trunk/research/thread-model/extern-main.cpp @@ -0,0 +1,36 @@ +/* +g++ -std=c++11 -g -O0 extern-main.cpp extern-extra.cpp -o extern-main +*/ +#include +// @see https://linux.die.net/man/3/pthread_create +#include + +/* +Main: ga=100, gb=1867710016 +Thread1: ga=100, gb=1867710016 +Thread2: ga=100, gb=200 +*/ +extern __thread int ga; +extern int gb; + +void* pfn(void* arg) +{ + printf("Thread1: ga=%d, gb=%d\n", ga, gb); + return NULL; +} + +extern void* pfn2(void* arg); + +int main(int argc, char** argv) +{ + printf("Main: ga=%d, gb=%d\n", ga, gb); + + pthread_t trd = NULL; + pthread_create(&trd, NULL, pfn, NULL); + pthread_join(trd, NULL); + + pthread_t trd2 = NULL; + pthread_create(&trd2, NULL, pfn2, NULL); + pthread_join(trd2, NULL); + return 0; +} diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 33edb4ff2a..4038d4c5ce 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -56,6 +56,7 @@ using namespace std; #include #include #include +#include using namespace srs_internal; @@ -541,6 +542,151 @@ srs_error_t srs_config_transform_vhost(SrsConfDirective* root) return err; } +// To wrap directive temporally. +class SrsTempConfig : public SrsConfig +{ +public: + SrsTempConfig(SrsConfDirective* r) { + root = r; + } + virtual ~SrsTempConfig() { + root = NULL; + } +}; + +srs_error_t srs_config_transform_vhost2(SrsConfDirective* root) +{ + srs_error_t err = srs_success; + + // Transform streams for hybrids. + if (true) { + SrsConfDirective* stream = new SrsConfDirective(); + SrsAutoFree(SrsConfDirective, stream); + + // The number of stream directives. + int nn_streams = 0; + std::string old_style_stream_name; + + // Collect directives which should be moved to stream. + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* dir = root->directives.at(i); + + // SRS5.0, move listen/max_connections/http_server/rtc_server to stream. + // SRS1/2/3/4: + // listen; max_connections; + // http_server {} rtc_server{} + // SRS5+: + // stream { + // listen; max_connections; + // http_server {} rtc_server{} + // } + if (dir->name == "listen" || dir->name == "max_connections" + || dir->name == "http_server" || dir->name == "rtc_server") { + old_style_stream_name = dir->name; + stream->directives.push_back(dir); + } + + if (dir->name == "stream") { + nn_streams++; + } + } + + // Fail if config the stream and old style stream. + if (!stream->directives.empty() && nn_streams) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "config %d stream conflicts with %s", + nn_streams, old_style_stream_name.c_str()); + } + + // Ignore if no directives for stream. + if (!stream->directives.empty()) { + // Remove the stream directives from root. + for (int i = 0; i < (int)stream->directives.size(); i++) { + SrsConfDirective* dir = stream->directives.at(i); + root->remove(dir); + } + + // Push the stream to root. + stream->name = "stream"; + root->directives.push_back(stream); + stream = NULL; + } + } + + // Auto generate streams, if there is only one stream template. + SrsTempConfig config(root); + if (config.get_threads_generate_stream()) { + int nn_streams = 0; + SrsConfDirective* tmpl = NULL; + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* dir = root->directives.at(i); + if (dir->name == "stream") { + tmpl = dir; + nn_streams++; + } + } + + int nn_hybrids = config.get_threads_hybrids(); + if (tmpl && nn_streams == 1 && nn_hybrids > 1) { + if ((err = srs_config_generate_stream(root, tmpl, nn_hybrids - nn_streams)) != srs_success) { + return srs_error_wrap(err, "generate stream"); + } + } + } + + return err; +} + +srs_error_t srs_config_generate_stream(SrsConfDirective* root, SrsConfDirective* tmpl, int nn) +{ + srs_error_t err = srs_success; + + SrsConfDirective* p = NULL; + + // stream.listen for RTMP. + int rtmp_port = 0; + if ((p = tmpl->get("listen")) != NULL) { + rtmp_port = ::atoi(p->arg0().c_str()); + } + + // stream.http_server.listen for HTTP. + int http_port = 0; + if ((p = tmpl->get("http_server")) != NULL) { + if ((p = p->get("listen")) != NULL) { + http_port = ::atoi(p->arg0().c_str()); + } + } + + // stream.rtc_server.listen for RTC. + int rtc_port = 0; + if ((p = tmpl->get("rtc_server")) != NULL) { + if ((p = p->get("listen")) != NULL) { + rtc_port = ::atoi(p->arg0().c_str()); + } + } + + for (int i = 0; i < nn; i++) { + SrsConfDirective* stream = tmpl->copy(); + root->directives.push_back(stream); + + // stream.listen for RTMP. + if (rtmp_port) { + stream->get_or_create("listen")->set_arg0(srs_int2str(rtmp_port + i + 1)); + } + + // stream.http_server.listen for HTTP. + if (http_port) { + stream->get_or_create("http_server")->get_or_create("listen")->set_arg0(srs_int2str(http_port + i + 1)); + } + + // stream.rtc_server.listen for RTC. + if (http_port) { + stream->get_or_create("rtc_server")->get_or_create("listen")->set_arg0(srs_int2str(rtc_port + i + 1)); + } + } + + return err; +} + // LCOV_EXCL_START srs_error_t srs_config_dumps_engine(SrsConfDirective* dir, SrsJsonObject* engine) { @@ -720,10 +866,10 @@ string SrsConfDirective::arg3() return ""; } -SrsConfDirective* SrsConfDirective::at(int index) +SrsConfDirective* SrsConfDirective::at(int stream_index) { - srs_assert(index < (int)directives.size()); - return directives.at(index); + srs_assert(stream_index < (int)directives.size()); + return directives.at(stream_index); } SrsConfDirective* SrsConfDirective::get(string _name) @@ -1163,8 +1309,6 @@ srs_error_t SrsConfDirective::read_token(SrsConfigBuffer* buffer, vector SrsConfig::SrsConfig() { - dolphin = false; - show_help = false; show_version = false; test_conf = false; @@ -1173,20 +1317,20 @@ SrsConfig::SrsConfig() root = new SrsConfDirective(); root->conf_line = 0; root->name = "root"; + + lock_ = new SrsThreadMutex(); } SrsConfig::~SrsConfig() { + srs_freep(lock_); srs_freep(root); } -bool SrsConfig::is_dolphin() -{ - return dolphin; -} - void SrsConfig::subscribe(ISrsReloadHandler* handler) { + SrsThreadLocker(lock_); + std::vector::iterator it; it = std::find(subscribes.begin(), subscribes.end(), handler); @@ -1199,6 +1343,8 @@ void SrsConfig::subscribe(ISrsReloadHandler* handler) void SrsConfig::unsubscribe(ISrsReloadHandler* handler) { + SrsThreadLocker(lock_); + std::vector::iterator it; it = std::find(subscribes.begin(), subscribes.end(), handler); @@ -1225,6 +1371,9 @@ srs_error_t SrsConfig::reload() if ((err = srs_config_transform_vhost(conf.root)) != srs_success) { return srs_error_wrap(err, "transform config"); } + if ((err = srs_config_transform_vhost2(conf.root)) != srs_success) { + return srs_error_wrap(err, "transform config"); + } if ((err = conf.check_config()) != srs_success) { return srs_error_wrap(err, "check config"); @@ -1519,27 +1668,6 @@ srs_error_t SrsConfig::reload_conf(SrsConfig* conf) } } - // merge config: srs_log_tank - if (!srs_directive_equals(root->get("srs_log_tank"), old_root->get("srs_log_tank"))) { - if ((err = do_reload_srs_log_tank()) != srs_success) { - return srs_error_wrap(err, "log tank");; - } - } - - // merge config: srs_log_level - if (!srs_directive_equals(root->get("srs_log_level"), old_root->get("srs_log_level"))) { - if ((err = do_reload_srs_log_level()) != srs_success) { - return srs_error_wrap(err, "log level");; - } - } - - // merge config: srs_log_file - if (!srs_directive_equals(root->get("srs_log_file"), old_root->get("srs_log_file"))) { - if ((err = do_reload_srs_log_file()) != srs_success) { - return srs_error_wrap(err, "log file");; - } - } - // merge config: max_connections if (!srs_directive_equals(root->get("max_connections"), old_root->get("max_connections"))) { if ((err = do_reload_max_connections()) != srs_success) { @@ -1547,13 +1675,6 @@ srs_error_t SrsConfig::reload_conf(SrsConfig* conf) } } - // merge config: utc_time - if (!srs_directive_equals(root->get("utc_time"), old_root->get("utc_time"))) { - if ((err = do_reload_utc_time()) != srs_success) { - return srs_error_wrap(err, "utc time");; - } - } - // merge config: pithy_print_ms if (!srs_directive_equals(root->get("pithy_print_ms"), old_root->get("pithy_print_ms"))) { if ((err = do_reload_pithy_print_ms()) != srs_success) { @@ -1667,102 +1788,14 @@ srs_error_t SrsConfig::reload_http_api(SrsConfDirective* old_root) srs_error_t SrsConfig::reload_http_stream(SrsConfDirective* old_root) { srs_error_t err = srs_success; - - // merge config. - std::vector::iterator it; - - // state graph - // old_http_stream new_http_stream - // DISABLED => ENABLED - // ENABLED => DISABLED - // ENABLED => ENABLED (modified) - - SrsConfDirective* new_http_stream = root->get("http_server"); - SrsConfDirective* old_http_stream = old_root->get("http_server"); - - // DISABLED => ENABLED - if (!get_http_stream_enabled(old_http_stream) && get_http_stream_enabled(new_http_stream)) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((err = subscribe->on_reload_http_stream_enabled()) != srs_success) { - return srs_error_wrap(err, "http stream off=>on"); - } - } - srs_trace("reload http stream off=>on success."); - return err; - } - - // ENABLED => DISABLED - if (get_http_stream_enabled(old_http_stream) && !get_http_stream_enabled(new_http_stream)) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((err = subscribe->on_reload_http_stream_disabled()) != srs_success) { - return srs_error_wrap(err, "http stream on=>off"); - } - } - srs_trace("reload http stream on=>off success."); - return err; - } - - // ENABLED => ENABLED (modified) - if (get_http_stream_enabled(old_http_stream) && get_http_stream_enabled(new_http_stream) - && !srs_directive_equals(old_http_stream, new_http_stream) - ) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((err = subscribe->on_reload_http_stream_updated()) != srs_success) { - return srs_error_wrap(err, "http stream enabled"); - } - } - srs_trace("reload http stream enabled success."); - - if (!srs_directive_equals(old_http_stream->get("crossdomain"), new_http_stream->get("crossdomain"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((err = subscribe->on_reload_http_stream_crossdomain()) != srs_success) { - return srs_error_wrap(err, "http stream crossdomain"); - } - } - } - srs_trace("reload http stream crossdomain success."); - return err; - } - - srs_trace("reload http stream success, nothing changed."); + // TODO: FIXME: We never support reload HTTP stream. return err; } srs_error_t SrsConfig::reload_rtc_server(SrsConfDirective* old_root) { srs_error_t err = srs_success; - - // merge config. - std::vector::iterator it; - - // state graph - // old_rtc_server new_rtc_server - // ENABLED => ENABLED (modified) - - SrsConfDirective* new_rtc_server = root->get("rtc_server"); - SrsConfDirective* old_rtc_server = old_root->get("rtc_server"); - - // TODO: FIXME: Support disable or enable reloading. - - // ENABLED => ENABLED (modified) - if (get_rtc_server_enabled(old_rtc_server) && get_rtc_server_enabled(new_rtc_server) - && !srs_directive_equals(old_rtc_server, new_rtc_server) - ) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((err = subscribe->on_reload_rtc_server()) != srs_success) { - return srs_error_wrap(err, "rtc server enabled"); - } - } - srs_trace("reload rtc server success."); - return err; - } - - srs_trace("reload rtc server success, nothing changed."); + // TODO: FIXME: Do not support reloading RTC Server. return err; } @@ -1988,12 +2021,14 @@ srs_error_t SrsConfig::parse_options(int argc, char** argv) // the parse_file never check the config, // we check it when user requires check config file. if (err == srs_success && (err = srs_config_transform_vhost(root)) == srs_success) { - if (err == srs_success && (err = check_config()) == srs_success) { - srs_trace("config file is ok"); - exit(0); + if (err == srs_success && (err = srs_config_transform_vhost2(root)) == srs_success) { + if (err == srs_success && (err = check_config()) == srs_success) { + srs_trace("config file is ok"); + exit(0); + } } } - + srs_error("invalid config, %s", srs_error_desc(err).c_str()); int ret = srs_error_code(err); srs_freep(err); @@ -2004,6 +2039,9 @@ srs_error_t SrsConfig::parse_options(int argc, char** argv) if ((err = srs_config_transform_vhost(root)) != srs_success) { return srs_error_wrap(err, "transform"); } + if ((err = srs_config_transform_vhost2(root)) != srs_success) { + return srs_error_wrap(err, "transform"); + } //////////////////////////////////////////////////////////////////////// // check log name and level @@ -2102,9 +2140,7 @@ srs_error_t SrsConfig::global_to_json(SrsJsonObject* obj) continue; } - if (dir->name == "listen") { - obj->set(dir->name, dir->dumps_args()); - } else if (dir->name == "pid") { + if (dir->name == "pid") { obj->set(dir->name, dir->dumps_arg0_to_str()); } else if (dir->name == "chunk_size") { obj->set(dir->name, dir->dumps_arg0_to_integer()); @@ -2116,8 +2152,6 @@ srs_error_t SrsConfig::global_to_json(SrsJsonObject* obj) obj->set(dir->name, dir->dumps_arg0_to_str()); } else if (dir->name == "srs_log_file") { obj->set(dir->name, dir->dumps_arg0_to_str()); - } else if (dir->name == "max_connections") { - obj->set(dir->name, dir->dumps_arg0_to_integer()); } else if (dir->name == "daemon") { obj->set(dir->name, dir->dumps_arg0_to_boolean()); } else if (dir->name == "utc_time") { @@ -2181,19 +2215,6 @@ srs_error_t SrsConfig::global_to_json(SrsJsonObject* obj) } } obj->set(dir->name, sobj); - } else if (dir->name == "http_server") { - SrsJsonObject* sobj = SrsJsonAny::object(); - for (int j = 0; j < (int)dir->directives.size(); j++) { - SrsConfDirective* sdir = dir->directives.at(j); - if (sdir->name == "enabled") { - sobj->set(sdir->name, sdir->dumps_arg0_to_boolean()); - } else if (sdir->name == "listen") { - sobj->set(sdir->name, sdir->dumps_arg0_to_str()); - } else if (sdir->name == "dir") { - sobj->set(sdir->name, sdir->dumps_arg0_to_str()); - } - } - obj->set(dir->name, sobj); } else if (dir->name == "stream_caster") { SrsJsonObject* sobj = SrsJsonAny::object(); for (int j = 0; j < (int)dir->directives.size(); j++) { @@ -2973,78 +2994,6 @@ srs_error_t SrsConfig::raw_set_ff_log_dir(string ff_log_dir, bool& applied) return err; } -srs_error_t SrsConfig::raw_set_srs_log_tank(string srs_log_tank, bool& applied) -{ - srs_error_t err = srs_success; - - applied = false; - - SrsConfDirective* conf = root->get_or_create("srs_log_tank"); - - if (conf->arg0() == srs_log_tank) { - return err; - } - - conf->args.clear(); - conf->args.push_back(srs_log_tank); - - if ((err = do_reload_srs_log_tank()) != srs_success) { - return srs_error_wrap(err, "reload log tank"); - } - - applied = true; - - return err; -} - -srs_error_t SrsConfig::raw_set_srs_log_level(string srs_log_level, bool& applied) -{ - srs_error_t err = srs_success; - - applied = false; - - SrsConfDirective* conf = root->get_or_create("srs_log_level"); - - if (conf->arg0() == srs_log_level) { - return err; - } - - conf->args.clear(); - conf->args.push_back(srs_log_level); - - if ((err = do_reload_srs_log_level()) != srs_success) { - return srs_error_wrap(err, "reload log level"); - } - - applied = true; - - return err; -} - -srs_error_t SrsConfig::raw_set_srs_log_file(string srs_log_file, bool& applied) -{ - srs_error_t err = srs_success; - - applied = false; - - SrsConfDirective* conf = root->get_or_create("srs_log_file"); - - if (conf->arg0() == srs_log_file) { - return err; - } - - conf->args.clear(); - conf->args.push_back(srs_log_file); - - if ((err = do_reload_srs_log_file()) != srs_success) { - return srs_error_wrap(err, "reload log file"); - } - - applied = true; - - return err; -} - srs_error_t SrsConfig::raw_set_max_connections(string max_connections, bool& applied) { srs_error_t err = srs_success; @@ -3069,30 +3018,6 @@ srs_error_t SrsConfig::raw_set_max_connections(string max_connections, bool& app return err; } -srs_error_t SrsConfig::raw_set_utc_time(string utc_time, bool& applied) -{ - srs_error_t err = srs_success; - - applied = false; - - SrsConfDirective* conf = root->get_or_create("utc_time"); - - if (conf->arg0() == utc_time) { - return err; - } - - conf->args.clear(); - conf->args.push_back(utc_time); - - if ((err = do_reload_utc_time()) != srs_success) { - return srs_error_wrap(err, "reload"); - } - - applied = true; - - return err; -} - srs_error_t SrsConfig::raw_set_pithy_print_ms(string pithy_print_ms, bool& applied) { srs_error_t err = srs_success; @@ -3292,54 +3217,6 @@ srs_error_t SrsConfig::do_reload_pid() return err; } -srs_error_t SrsConfig::do_reload_srs_log_tank() -{ - srs_error_t err = srs_success; - - vector::iterator it; - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((err = subscribe->on_reload_log_tank()) != srs_success) { - return srs_error_wrap(err, "notify subscribes reload srs_log_tank failed"); - } - } - srs_trace("reload srs_log_tank success."); - - return err; -} - -srs_error_t SrsConfig::do_reload_srs_log_level() -{ - srs_error_t err = srs_success; - - vector::iterator it; - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((err = subscribe->on_reload_log_level()) != srs_success) { - return srs_error_wrap(err, "notify subscribes reload srs_log_level failed"); - } - } - srs_trace("reload srs_log_level success."); - - return err; -} - -srs_error_t SrsConfig::do_reload_srs_log_file() -{ - srs_error_t err = srs_success; - - vector::iterator it; - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((err = subscribe->on_reload_log_file()) != srs_success) { - return srs_error_wrap(err, "notify subscribes reload srs_log_file failed"); - } - } - srs_trace("reload srs_log_file success."); - - return err; -} - srs_error_t SrsConfig::do_reload_max_connections() { srs_error_t err = srs_success; @@ -3356,22 +3233,6 @@ srs_error_t SrsConfig::do_reload_max_connections() return err; } -srs_error_t SrsConfig::do_reload_utc_time() -{ - srs_error_t err = srs_success; - - vector::iterator it; - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - ISrsReloadHandler* subscribe = *it; - if ((err = subscribe->on_reload_utc_time()) != srs_success) { - return srs_error_wrap(err, "utc_time"); - } - } - srs_trace("reload utc_time success."); - - return err; -} - srs_error_t SrsConfig::do_reload_pithy_print_ms() { srs_error_t err = srs_success; @@ -3468,28 +3329,6 @@ srs_error_t SrsConfig::parse_argv(int& i, char** argv) show_help = false; test_conf = true; break; - case 'p': - dolphin = true; - if (*p) { - dolphin_rtmp_port = p; - continue; - } - if (argv[++i]) { - dolphin_rtmp_port = argv[i]; - continue; - } - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "-p requires params"); - case 'x': - dolphin = true; - if (*p) { - dolphin_http_port = p; - continue; - } - if (argv[++i]) { - dolphin_http_port = argv[i]; - continue; - } - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "-x requires params"); case 'v': case 'V': show_help = false; @@ -3576,6 +3415,10 @@ srs_error_t SrsConfig::check_config() if ((err = check_number_connections()) != srs_success) { return srs_error_wrap(err, "check connections"); } + + if ((err = check_hybrids()) != srs_success) { + return srs_error_wrap(err, "check hybrids"); + } return err; } @@ -3599,19 +3442,44 @@ srs_error_t SrsConfig::check_normal_config() for (int i = 0; i < (int)root->directives.size(); i++) { SrsConfDirective* conf = root->at(i); std::string n = conf->name; - if (n != "listen" && n != "pid" && n != "chunk_size" && n != "ff_log_dir" + if (n != "pid" && n != "chunk_size" && n != "ff_log_dir" && n != "srs_log_tank" && n != "srs_log_level" && n != "srs_log_file" - && n != "max_connections" && n != "daemon" && n != "heartbeat" + && n != "daemon" && n != "heartbeat" && n != "stream" && n != "http_api" && n != "stats" && n != "vhost" && n != "pithy_print_ms" - && n != "http_server" && n != "stream_caster" && n != "rtc_server" && n != "srt_server" + && n != "stream_caster" && n != "srt_server" && n != "utc_time" && n != "work_dir" && n != "asprocess" && n != "ff_log_level" && n != "grace_final_wait" && n != "force_grace_quit" && n != "grace_start_wait" && n != "empty_ip_ok" && n != "disable_daemon_for_docker" && n != "inotify_auto_reload" && n != "auto_reload_for_docker" && n != "tcmalloc_release_rate" - ) { + && n != "srs_log_flush_interval" && n != "threads" && n != "circuit_breaker") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal directive %s", n.c_str()); } + + for (int j = 0; conf && n == "stream" && j < (int)conf->directives.size(); j++) { + SrsConfDirective* obj = conf->at(j); + string m = obj->name; + if (m != "listen" && m != "max_connections" && m != "http_server" && m != "rtc_server") { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream directive %s", m.c_str()); + } + + for (int k = 0; obj && m == "http_server" && k < (int)obj->directives.size(); k++) { + string l = obj->at(k)->name; + if (l != "enabled" && l != "listen" && l != "dir" && l != "crossdomain" && l != "https") { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_stream.%s", l.c_str()); + } + } + + for (int k = 0; obj && m == "rtc_server" && k < (int)obj->directives.size(); k++) { + string l = obj->at(k)->name; + if (l != "enabled" && l != "listen" && l != "dir" && l != "candidate" && l != "ecdsa" + && l != "encrypt" && l != "reuseport" && l != "merge_nalus" && l != "perf_stat" && l != "black_hole" + && l != "ip_family" && l != "rtp_cache" && l != "rtp_msg_cache") { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal rtc_server.%s", l.c_str()); + } + } + } } + if (true) { SrsConfDirective* conf = root->get("http_api"); for (int i = 0; conf && i < (int)conf->directives.size(); i++) { @@ -3631,15 +3499,6 @@ srs_error_t SrsConfig::check_normal_config() } } } - if (true) { - SrsConfDirective* conf = root->get("http_server"); - for (int i = 0; conf && i < (int)conf->directives.size(); i++) { - string n = conf->at(i)->name; - if (n != "enabled" && n != "listen" && n != "dir" && n != "crossdomain" && n != "https") { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal http_stream.%s", n.c_str()); - } - } - } if (true) { SrsConfDirective* conf = root->get("srt_server"); for (int i = 0; conf && i < (int)conf->directives.size(); i++) { @@ -3672,17 +3531,6 @@ srs_error_t SrsConfig::check_normal_config() } } } - if (true) { - SrsConfDirective* conf = root->get("rtc_server"); - for (int i = 0; conf && i < (int)conf->directives.size(); i++) { - string n = conf->at(i)->name; - if (n != "enabled" && n != "listen" && n != "dir" && n != "candidate" && n != "ecdsa" - && n != "encrypt" && n != "reuseport" && n != "merge_nalus" && n != "perf_stat" && n != "black_hole" - && n != "ip_family" && n != "rtp_cache" && n != "rtp_msg_cache") { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal rtc_server.%s", n.c_str()); - } - } - } //////////////////////////////////////////////////////////////////////// // check listen for rtmp. @@ -4075,6 +3923,63 @@ srs_error_t SrsConfig::check_number_connections() } // LCOV_EXCL_STOP +srs_error_t SrsConfig::check_hybrids() +{ + srs_error_t err = srs_success; + + // There MUST be at least one stream/hybrid. + int hybrids = get_threads_hybrids(); + if (hybrids < 1) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "hybrids MUST >=1, actual %d", hybrids); + } + + // The number of hybrids MUST be equal to the streams. + vector streams; + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* conf = root->at(i); + if (conf->name == "stream") { + streams.push_back(conf); + } + } + + if (hybrids > (int)streams.size()) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "hybrids=%d requires %d streams, actual=%d", + hybrids, (int)streams.size(), (int)streams.size()); + } + + // For each stream, the UDP listen MUST not be the same. + vector udp_ports; + for (int i = 0; i < (int)streams.size(); i++) { + int port = get_rtc_server_listen(i); + if (std::find(udp_ports.begin(), udp_ports.end(), port) != udp_ports.end()) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "RTC port=%d duplicated", port); + } + udp_ports.push_back(port); + } + + // TODO: FIXME: For edge, the RTMP/HTTP port is OK to be the same, but it's too complex. + // For each stream, the TCP listen MUST not be the same. + vector tcp_ports; + for (int i = 0; i < (int)streams.size(); i++) { + string port = get_http_stream_listen(i); + if (std::find(tcp_ports.begin(), tcp_ports.end(), port) != tcp_ports.end()) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "HTTP port=%s duplicated", port.c_str()); + } + tcp_ports.push_back(port); + + vector ports = get_listens(i); + for (int j = 0; j < (int)ports.size(); j++) { + port = ports.at(j); + if (std::find(tcp_ports.begin(), tcp_ports.end(), port) != tcp_ports.end()) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "RTMP port=%s duplicated", port.c_str()); + } + tcp_ports.push_back(port); + } + } + + return err; +} + srs_error_t SrsConfig::parse_buffer(SrsConfigBuffer* buffer) { srs_error_t err = srs_success; @@ -4088,23 +3993,6 @@ srs_error_t SrsConfig::parse_buffer(SrsConfigBuffer* buffer) return srs_error_wrap(err, "root parse"); } - // mock by dolphin mode. - // for the dolphin will start srs with specified params. - if (dolphin) { - // for RTMP. - set_config_directive(root, "listen", dolphin_rtmp_port); - - // for HTTP - set_config_directive(root, "http_server", ""); - SrsConfDirective* http_server = root->get("http_server"); - set_config_directive(http_server, "enabled", "on"); - set_config_directive(http_server, "listen", dolphin_http_port); - - // others. - set_config_directive(root, "daemon", "off"); - set_config_directive(root, "srs_log_tank", "console"); - } - return err; } @@ -4133,11 +4021,36 @@ SrsConfDirective* SrsConfig::get_root() return root; } -int SrsConfig::get_max_connections() +SrsConfDirective* SrsConfig::get_stream_at(int stream_index) +{ + int matched = 0; + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* conf = root->at(i); + + if (conf->name != "stream") { + continue; + } + + if (matched++ != stream_index) { + continue; + } + + return conf; + } + + return NULL; +} + +int SrsConfig::get_max_connections(int stream_index) { static int DEFAULT = 1000; - SrsConfDirective* conf = root->get("max_connections"); + SrsConfDirective* conf = get_stream_at(stream_index); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("max_connections"); if (!conf || conf->arg0().empty()) { return DEFAULT; } @@ -4145,11 +4058,16 @@ int SrsConfig::get_max_connections() return ::atoi(conf->arg0().c_str()); } -vector SrsConfig::get_listens() +vector SrsConfig::get_listens(int stream_index) { std::vector ports; - SrsConfDirective* conf = root->get("listen"); + SrsConfDirective* conf = get_stream_at(stream_index); + if (!conf) { + return ports; + } + + conf = conf->get("listen"); if (!conf) { return ports; } @@ -4321,6 +4239,222 @@ double SrsConfig::tcmalloc_release_rate() return trr; } +srs_utime_t SrsConfig::get_threads_interval() +{ + static srs_utime_t DEFAULT = 5 * SRS_UTIME_SECONDS; + + SrsConfDirective* conf = root->get("threads"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("interval"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + int v = ::atoi(conf->arg0().c_str()); + if (v <= 0) { + return DEFAULT; + } + + return v * SRS_UTIME_SECONDS; +} + +int SrsConfig::get_threads_hybrids() +{ + static int DEFAULT = 1; + + SrsConfDirective* conf = root->get("threads"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("hybrids"); + if (!conf) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +bool SrsConfig::get_threads_generate_stream() +{ + static bool DEFAULT = false; + + SrsConfDirective* conf = root->get("threads"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("generate_streams"); + if (!conf) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); +} + +bool SrsConfig::get_threads_cpu_affinity(std::string label, int* start, int* end) +{ + static int DEFAULT_START = 0; + static int DEFAULT_END = 63; + + *start = DEFAULT_START; + *end = DEFAULT_END; + + SrsConfDirective* conf = root->get("threads"); + if (!conf) { + return false; + } + + conf = conf->get("cpu_affinity"); + if (!conf) { + return false; + } + + conf = conf->get(label); + if (!conf) { + return false; + } + + string v = conf->arg0(); + size_t pos = v.find("-"); + if (pos == string::npos) { + *start = *end = ::atoi(v.c_str()); + return true; + } + + string sv = v.substr(0, pos); + string ev = v.substr(pos + 1); + if (!sv.empty()) { + *start = ::atoi(sv.c_str()); + } + if (!ev.empty()) { + *end = ::atoi(ev.c_str()); + } + return true; +} + +bool SrsConfig::get_circuit_breaker() +{ + static bool DEFAULT = true; + + SrsConfDirective* conf = root->get("circuit_breaker"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("enabled"); + if (!conf) { + return DEFAULT; + } + + return SRS_CONF_PERFER_TRUE(conf->arg0()); +} + +int SrsConfig::get_high_threshold() +{ + static int DEFAULT = 90; + + SrsConfDirective* conf = root->get("circuit_breaker"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("high_threshold"); + if (!conf) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_high_pulse() +{ + static int DEFAULT = 2; + + SrsConfDirective* conf = root->get("circuit_breaker"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("high_pulse"); + if (!conf) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_critical_threshold() +{ + static int DEFAULT = 95; + + SrsConfDirective* conf = root->get("circuit_breaker"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("critical_threshold"); + if (!conf) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_critical_pulse() +{ + static int DEFAULT = 1; + + SrsConfDirective* conf = root->get("circuit_breaker"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("critical_pulse"); + if (!conf) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_dying_threshold() +{ + static int DEFAULT = 99; + + SrsConfDirective* conf = root->get("circuit_breaker"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("dying_threshold"); + if (!conf) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_dying_pulse() +{ + static int DEFAULT = 5; + + SrsConfDirective* conf = root->get("circuit_breaker"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("dying_pulse"); + if (!conf) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + vector SrsConfig::get_stream_casters() { srs_assert(root); @@ -4748,9 +4882,19 @@ srs_utime_t SrsConfig::get_stream_caster_gb28181_sip_query_catalog_interval(SrsC return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_SECONDS); } -bool SrsConfig::get_rtc_server_enabled() +SrsConfDirective* SrsConfig::get_rtc_server_at(int stream_index) +{ + SrsConfDirective* conf = get_stream_at(stream_index); + if (!conf) { + return NULL; + } + + return conf->get("rtc_server"); +} + +bool SrsConfig::get_rtc_server_enabled(int stream_index) { - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); return get_rtc_server_enabled(conf); } @@ -4770,11 +4914,11 @@ bool SrsConfig::get_rtc_server_enabled(SrsConfDirective* conf) return SRS_CONF_PERFER_FALSE(conf->arg0()); } -int SrsConfig::get_rtc_server_listen() +int SrsConfig::get_rtc_server_listen(int stream_index) { static int DEFAULT = 8000; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -4787,11 +4931,11 @@ int SrsConfig::get_rtc_server_listen() return ::atoi(conf->arg0().c_str()); } -std::string SrsConfig::get_rtc_server_candidates() +std::string SrsConfig::get_rtc_server_candidates(int stream_index) { static string DEFAULT = "*"; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -4814,11 +4958,11 @@ std::string SrsConfig::get_rtc_server_candidates() return conf->arg0(); } -std::string SrsConfig::get_rtc_server_ip_family() +std::string SrsConfig::get_rtc_server_ip_family(int stream_index) { static string DEFAULT = "ipv4"; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -4831,11 +4975,11 @@ std::string SrsConfig::get_rtc_server_ip_family() return conf->arg0(); } -bool SrsConfig::get_rtc_server_ecdsa() +bool SrsConfig::get_rtc_server_ecdsa(int stream_index) { static bool DEFAULT = true; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -4848,11 +4992,11 @@ bool SrsConfig::get_rtc_server_ecdsa() return SRS_CONF_PERFER_TRUE(conf->arg0()); } -bool SrsConfig::get_rtc_server_encrypt() +bool SrsConfig::get_rtc_server_encrypt(int stream_index) { static bool DEFAULT = true; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -4865,9 +5009,9 @@ bool SrsConfig::get_rtc_server_encrypt() return SRS_CONF_PERFER_TRUE(conf->arg0()); } -int SrsConfig::get_rtc_server_reuseport() +int SrsConfig::get_rtc_server_reuseport(int stream_index) { - int v = get_rtc_server_reuseport2(); + int v = get_rtc_server_reuseport2(stream_index); #if !defined(SO_REUSEPORT) if (v > 1) { @@ -4879,11 +5023,11 @@ int SrsConfig::get_rtc_server_reuseport() return v; } -int SrsConfig::get_rtc_server_reuseport2() +int SrsConfig::get_rtc_server_reuseport2(int stream_index) { static int DEFAULT = 1; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -4896,11 +5040,11 @@ int SrsConfig::get_rtc_server_reuseport2() return ::atoi(conf->arg0().c_str()); } -bool SrsConfig::get_rtc_server_merge_nalus() +bool SrsConfig::get_rtc_server_merge_nalus(int stream_index) { static int DEFAULT = false; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -4913,11 +5057,11 @@ bool SrsConfig::get_rtc_server_merge_nalus() return SRS_CONF_PERFER_TRUE(conf->arg0()); } -bool SrsConfig::get_rtc_server_perf_stat() +bool SrsConfig::get_rtc_server_perf_stat(int stream_index) { static bool DEFAULT = false; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -4930,9 +5074,9 @@ bool SrsConfig::get_rtc_server_perf_stat() return SRS_CONF_PERFER_FALSE(conf->arg0()); } -SrsConfDirective* SrsConfig::get_rtc_server_rtp_cache() +SrsConfDirective* SrsConfig::get_rtc_server_rtp_cache(int stream_index) { - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return NULL; } @@ -4945,11 +5089,11 @@ SrsConfDirective* SrsConfig::get_rtc_server_rtp_cache() return conf; } -bool SrsConfig::get_rtc_server_rtp_cache_enabled() +bool SrsConfig::get_rtc_server_rtp_cache_enabled(int stream_index) { static bool DEFAULT = true; - SrsConfDirective* conf = get_rtc_server_rtp_cache(); + SrsConfDirective* conf = get_rtc_server_rtp_cache(stream_index); if (!conf) { return DEFAULT; } @@ -4962,11 +5106,11 @@ bool SrsConfig::get_rtc_server_rtp_cache_enabled() return SRS_CONF_PERFER_TRUE(conf->arg0()); } -uint64_t SrsConfig::get_rtc_server_rtp_cache_pkt_size() +uint64_t SrsConfig::get_rtc_server_rtp_cache_pkt_size(int stream_index) { int DEFAULT = 64 * 1024 * 1024; - SrsConfDirective* conf = get_rtc_server_rtp_cache(); + SrsConfDirective* conf = get_rtc_server_rtp_cache(stream_index); if (!conf) { return DEFAULT; } @@ -4979,11 +5123,11 @@ uint64_t SrsConfig::get_rtc_server_rtp_cache_pkt_size() return 1024 * (uint64_t)(1024 * ::atof(conf->arg0().c_str())); } -uint64_t SrsConfig::get_rtc_server_rtp_cache_payload_size() +uint64_t SrsConfig::get_rtc_server_rtp_cache_payload_size(int stream_index) { int DEFAULT = 16 * 1024 * 1024; - SrsConfDirective* conf = get_rtc_server_rtp_cache(); + SrsConfDirective* conf = get_rtc_server_rtp_cache(stream_index); if (!conf) { return DEFAULT; } @@ -4996,9 +5140,9 @@ uint64_t SrsConfig::get_rtc_server_rtp_cache_payload_size() return 1024 * (uint64_t)(1024 * ::atof(conf->arg0().c_str())); } -SrsConfDirective* SrsConfig::get_rtc_server_rtp_msg_cache() +SrsConfDirective* SrsConfig::get_rtc_server_rtp_msg_cache(int stream_index) { - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return NULL; } @@ -5011,11 +5155,11 @@ SrsConfDirective* SrsConfig::get_rtc_server_rtp_msg_cache() return conf; } -bool SrsConfig::get_rtc_server_rtp_msg_cache_enabled() +bool SrsConfig::get_rtc_server_rtp_msg_cache_enabled(int stream_index) { static bool DEFAULT = true; - SrsConfDirective* conf = get_rtc_server_rtp_msg_cache(); + SrsConfDirective* conf = get_rtc_server_rtp_msg_cache(stream_index); if (!conf) { return DEFAULT; } @@ -5028,11 +5172,11 @@ bool SrsConfig::get_rtc_server_rtp_msg_cache_enabled() return SRS_CONF_PERFER_TRUE(conf->arg0()); } -uint64_t SrsConfig::get_rtc_server_rtp_msg_cache_msg_size() +uint64_t SrsConfig::get_rtc_server_rtp_msg_cache_msg_size(int stream_index) { int DEFAULT = 16 * 1024 * 1024; - SrsConfDirective* conf = get_rtc_server_rtp_msg_cache(); + SrsConfDirective* conf = get_rtc_server_rtp_msg_cache(stream_index); if (!conf) { return DEFAULT; } @@ -5045,11 +5189,11 @@ uint64_t SrsConfig::get_rtc_server_rtp_msg_cache_msg_size() return 1024 * (uint64_t)(1024 * ::atof(conf->arg0().c_str())); } -uint64_t SrsConfig::get_rtc_server_rtp_msg_cache_buffer_size() +uint64_t SrsConfig::get_rtc_server_rtp_msg_cache_buffer_size(int stream_index) { int DEFAULT = 512 * 1024 * 1024; - SrsConfDirective* conf = get_rtc_server_rtp_msg_cache(); + SrsConfDirective* conf = get_rtc_server_rtp_msg_cache(stream_index); if (!conf) { return DEFAULT; } @@ -5062,11 +5206,11 @@ uint64_t SrsConfig::get_rtc_server_rtp_msg_cache_buffer_size() return 1024 * (uint64_t)(1024 * ::atof(conf->arg0().c_str())); } -bool SrsConfig::get_rtc_server_black_hole() +bool SrsConfig::get_rtc_server_black_hole(int stream_index) { static bool DEFAULT = false; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -5084,11 +5228,11 @@ bool SrsConfig::get_rtc_server_black_hole() return SRS_CONF_PERFER_FALSE(conf->arg0()); } -std::string SrsConfig::get_rtc_server_black_hole_addr() +std::string SrsConfig::get_rtc_server_black_hole_addr(int stream_index) { static string DEFAULT = ""; - SrsConfDirective* conf = root->get("rtc_server"); + SrsConfDirective* conf = get_rtc_server_at(stream_index); if (!conf) { return DEFAULT; } @@ -7042,6 +7186,23 @@ string SrsConfig::get_log_file() return conf->arg0(); } +srs_utime_t SrsConfig::srs_log_flush_interval() +{ + srs_utime_t DEFAULT = 1300 * SRS_UTIME_MILLISECONDS; + + SrsConfDirective* conf = root->get("srs_log_flush_interval"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + srs_utime_t v = ::atoi(conf->arg0().c_str()) * SRS_UTIME_MILLISECONDS; + if (v <= 0) { + return DEFAULT; + } + + return v; +} + bool SrsConfig::get_ff_log_enabled() { string log = get_ff_log_dir(); @@ -8226,9 +8387,19 @@ string SrsConfig::get_default_app_name() { return conf->arg0(); } -bool SrsConfig::get_http_stream_enabled() +SrsConfDirective* SrsConfig::get_http_stream_at(int stream_index) +{ + SrsConfDirective* conf = get_stream_at(stream_index); + if (!conf) { + return NULL; + } + + return conf->get("http_server"); +} + +bool SrsConfig::get_http_stream_enabled(int stream_index) { - SrsConfDirective* conf = root->get("http_server"); + SrsConfDirective* conf = get_http_stream_at(stream_index); return get_http_stream_enabled(conf); } @@ -8248,11 +8419,11 @@ bool SrsConfig::get_http_stream_enabled(SrsConfDirective* conf) return SRS_CONF_PERFER_FALSE(conf->arg0()); } -string SrsConfig::get_http_stream_listen() +string SrsConfig::get_http_stream_listen(int stream_index) { static string DEFAULT = "8080"; - SrsConfDirective* conf = root->get("http_server"); + SrsConfDirective* conf = get_http_stream_at(stream_index); if (!conf) { return DEFAULT; } @@ -8265,11 +8436,11 @@ string SrsConfig::get_http_stream_listen() return conf->arg0(); } -string SrsConfig::get_http_stream_dir() +string SrsConfig::get_http_stream_dir(int stream_index) { static string DEFAULT = "./objs/nginx/html"; - SrsConfDirective* conf = root->get("http_server"); + SrsConfDirective* conf = get_http_stream_at(stream_index); if (!conf) { return DEFAULT; } @@ -8282,11 +8453,11 @@ string SrsConfig::get_http_stream_dir() return conf->arg0(); } -bool SrsConfig::get_http_stream_crossdomain() +bool SrsConfig::get_http_stream_crossdomain(int stream_index) { static bool DEFAULT = true; - SrsConfDirective* conf = root->get("http_server"); + SrsConfDirective* conf = get_http_stream_at(stream_index); if (!conf) { return DEFAULT; } @@ -8299,9 +8470,9 @@ bool SrsConfig::get_http_stream_crossdomain() return SRS_CONF_PERFER_TRUE(conf->arg0()); } -SrsConfDirective* SrsConfig::get_https_stream() +SrsConfDirective* SrsConfig::get_https_stream(int stream_index) { - SrsConfDirective* conf = root->get("http_server"); + SrsConfDirective* conf = get_http_stream_at(stream_index); if (!conf) { return NULL; } @@ -8309,11 +8480,11 @@ SrsConfDirective* SrsConfig::get_https_stream() return conf->get("https"); } -bool SrsConfig::get_https_stream_enabled() +bool SrsConfig::get_https_stream_enabled(int stream_index) { static bool DEFAULT = false; - SrsConfDirective* conf = get_https_stream(); + SrsConfDirective* conf = get_https_stream(stream_index); if (!conf) { return DEFAULT; } @@ -8326,11 +8497,11 @@ bool SrsConfig::get_https_stream_enabled() return SRS_CONF_PERFER_FALSE(conf->arg0()); } -string SrsConfig::get_https_stream_listen() +string SrsConfig::get_https_stream_listen(int stream_index) { static string DEFAULT = "8088"; - SrsConfDirective* conf = get_https_stream(); + SrsConfDirective* conf = get_https_stream(stream_index); if (!conf) { return DEFAULT; } @@ -8343,11 +8514,11 @@ string SrsConfig::get_https_stream_listen() return conf->arg0(); } -string SrsConfig::get_https_stream_ssl_key() +string SrsConfig::get_https_stream_ssl_key(int stream_index) { static string DEFAULT = "./conf/server.key"; - SrsConfDirective* conf = get_https_stream(); + SrsConfDirective* conf = get_https_stream(stream_index); if (!conf) { return DEFAULT; } @@ -8360,11 +8531,11 @@ string SrsConfig::get_https_stream_ssl_key() return conf->arg0(); } -string SrsConfig::get_https_stream_ssl_cert() +string SrsConfig::get_https_stream_ssl_cert(int stream_index) { static string DEFAULT = "./conf/server.crt"; - SrsConfDirective* conf = get_https_stream(); + SrsConfDirective* conf = get_https_stream(stream_index); if (!conf) { return DEFAULT; } diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 9e6d848807..27db20f986 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -46,6 +46,7 @@ class SrsConfig; class SrsRequest; class SrsJsonArray; class SrsConfDirective; +class SrsThreadMutex; /** * whether the two vector actual equals, for instance, @@ -137,8 +138,11 @@ extern std::string srs_config_bool2switch(std::string sbool); // so we must transform the vhost directive anytime load the config. // @param root the root directive to transform, in and out parameter. extern srs_error_t srs_config_transform_vhost(SrsConfDirective* root); +extern srs_error_t srs_config_transform_vhost2(SrsConfDirective* root); +extern srs_error_t srs_config_generate_stream(SrsConfDirective* root, SrsConfDirective* tmpl, int nn); -// @global config object. +// TODO: FIXME: It should be thread-local or thread-safe. +// TODO: FIXME: We should use channel to deliver changes of config. extern SrsConfig* _srs_config; // The config directive. @@ -271,11 +275,6 @@ class SrsConfig { // user command private: - // Whether srs is run in dolphin mode. - // @see https://github.com/ossrs/srs-dolphin - bool dolphin; - std::string dolphin_rtmp_port; - std::string dolphin_http_port; // Whether show help and exit. bool show_help; // Whether test config file and exit. @@ -303,13 +302,10 @@ class SrsConfig private: // The reload subscribers, when reload, callback all handlers. std::vector subscribes; + SrsThreadMutex* lock_; public: SrsConfig(); virtual ~SrsConfig(); - // dolphin -public: - // Whether srs is in dolphin mode. - virtual bool is_dolphin(); // Reload public: // For reload handler to register itself, @@ -367,16 +363,8 @@ class SrsConfig virtual srs_error_t raw_set_chunk_size(std::string chunk_size, bool& applied); // RAW set the global ffmpeg log dir. virtual srs_error_t raw_set_ff_log_dir(std::string ff_log_dir, bool& applied); - // RAW set the global log tank. - virtual srs_error_t raw_set_srs_log_tank(std::string srs_log_tank, bool& applied); - // RAW set the global log level. - virtual srs_error_t raw_set_srs_log_level(std::string srs_log_level, bool& applied); - // RAW set the global log file path for file tank. - virtual srs_error_t raw_set_srs_log_file(std::string srs_log_file, bool& applied); // RAW set the global max connections of srs. virtual srs_error_t raw_set_max_connections(std::string max_connections, bool& applied); - // RAW set the global whether use utc time. - virtual srs_error_t raw_set_utc_time(std::string utc_time, bool& applied); // RAW set the global pithy print interval in ms. virtual srs_error_t raw_set_pithy_print_ms(std::string pithy_print_ms, bool& applied); // RAW create the new vhost. @@ -396,11 +384,7 @@ class SrsConfig private: virtual srs_error_t do_reload_listen(); virtual srs_error_t do_reload_pid(); - virtual srs_error_t do_reload_srs_log_tank(); - virtual srs_error_t do_reload_srs_log_level(); - virtual srs_error_t do_reload_srs_log_file(); virtual srs_error_t do_reload_max_connections(); - virtual srs_error_t do_reload_utc_time(); virtual srs_error_t do_reload_pithy_print_ms(); virtual srs_error_t do_reload_vhost_added(std::string vhost); virtual srs_error_t do_reload_vhost_removed(std::string vhost); @@ -421,6 +405,7 @@ class SrsConfig protected: virtual srs_error_t check_normal_config(); virtual srs_error_t check_number_connections(); + virtual srs_error_t check_hybrids(); protected: // Parse config from the buffer. // @param buffer, the config buffer, user must delete it. @@ -438,6 +423,8 @@ class SrsConfig // The root directive, no name and args, contains directives. // All directive parsed can retrieve from root. virtual SrsConfDirective* get_root(); + // Get the stream config at index. + virtual SrsConfDirective* get_stream_at(int index); // Get the daemon config. // If true, SRS will run in daemon mode, fork and fork to reap the // grand-child process to init process. @@ -448,11 +435,11 @@ class SrsConfig // for example, when you need SRS to service 10000+ connections, // user must use "ulimit -HSn 10000" and config the max connections // of SRS. - virtual int get_max_connections(); + virtual int get_max_connections(int stream_index = 0); // Get the listen port of SRS. // user can specifies multiple listen ports, // each args of directive is a listen port. - virtual std::vector get_listens(); + virtual std::vector get_listens(int stream_index = 0); // Get the pid file path. // The pid file is used to save the pid of SRS, // use file lock to prevent multiple SRS starting. @@ -487,6 +474,19 @@ class SrsConfig virtual bool auto_reload_for_docker(); // For tcmalloc, get the release rate. virtual double tcmalloc_release_rate(); +// Thread pool section. +public: + virtual srs_utime_t get_threads_interval(); + virtual int get_threads_hybrids(); + virtual bool get_threads_generate_stream(); + virtual bool get_threads_cpu_affinity(std::string label, int* start, int* end); + virtual bool get_circuit_breaker(); + virtual int get_high_threshold(); + virtual int get_high_pulse(); + virtual int get_critical_threshold(); + virtual int get_critical_pulse(); + virtual int get_dying_threshold(); + virtual int get_dying_pulse(); // stream_caster section public: // Get all stream_caster in config file. @@ -523,34 +523,37 @@ class SrsConfig virtual srs_utime_t get_stream_caster_gb28181_sip_query_catalog_interval(SrsConfDirective* conf); // rtc section +private: + // Get the RTC server config at index. + SrsConfDirective* get_rtc_server_at(int index); public: - virtual bool get_rtc_server_enabled(); + virtual bool get_rtc_server_enabled(int stream_index = 0); virtual bool get_rtc_server_enabled(SrsConfDirective* conf); - virtual int get_rtc_server_listen(); - virtual std::string get_rtc_server_candidates(); - virtual std::string get_rtc_server_ip_family(); - virtual bool get_rtc_server_ecdsa(); - virtual bool get_rtc_server_encrypt(); - virtual int get_rtc_server_reuseport(); - virtual bool get_rtc_server_merge_nalus(); - virtual bool get_rtc_server_perf_stat(); + virtual int get_rtc_server_listen(int stream_index = 0); + virtual std::string get_rtc_server_candidates(int stream_index = 0); + virtual std::string get_rtc_server_ip_family(int stream_index = 0); + virtual bool get_rtc_server_ecdsa(int stream_index = 0); + virtual bool get_rtc_server_encrypt(int stream_index = 0); + virtual int get_rtc_server_reuseport(int stream_index = 0); + virtual bool get_rtc_server_merge_nalus(int stream_index = 0); + virtual bool get_rtc_server_perf_stat(int stream_index = 0); private: - SrsConfDirective* get_rtc_server_rtp_cache(); + SrsConfDirective* get_rtc_server_rtp_cache(int stream_index = 0); public: - virtual bool get_rtc_server_rtp_cache_enabled(); - virtual uint64_t get_rtc_server_rtp_cache_pkt_size(); - virtual uint64_t get_rtc_server_rtp_cache_payload_size(); + virtual bool get_rtc_server_rtp_cache_enabled(int stream_index = 0); + virtual uint64_t get_rtc_server_rtp_cache_pkt_size(int stream_index = 0); + virtual uint64_t get_rtc_server_rtp_cache_payload_size(int stream_index = 0); private: - virtual SrsConfDirective* get_rtc_server_rtp_msg_cache(); + virtual SrsConfDirective* get_rtc_server_rtp_msg_cache(int stream_index = 0); public: - virtual bool get_rtc_server_rtp_msg_cache_enabled(); - virtual uint64_t get_rtc_server_rtp_msg_cache_msg_size(); - virtual uint64_t get_rtc_server_rtp_msg_cache_buffer_size(); + virtual bool get_rtc_server_rtp_msg_cache_enabled(int stream_index = 0); + virtual uint64_t get_rtc_server_rtp_msg_cache_msg_size(int stream_index = 0); + virtual uint64_t get_rtc_server_rtp_msg_cache_buffer_size(int stream_index = 0); public: - virtual bool get_rtc_server_black_hole(); - virtual std::string get_rtc_server_black_hole_addr(); + virtual bool get_rtc_server_black_hole(int stream_index = 0); + virtual std::string get_rtc_server_black_hole_addr(int stream_index = 0); private: - virtual int get_rtc_server_reuseport2(); + virtual int get_rtc_server_reuseport2(int stream_index = 0); public: SrsConfDirective* get_rtc(std::string vhost); @@ -903,6 +906,8 @@ class SrsConfig virtual std::string get_log_level(); // Get the log file path. virtual std::string get_log_file(); + // Get the interval in ms to flush async log. + virtual srs_utime_t srs_log_flush_interval(); // Whether ffmpeg log enabled virtual bool get_ff_log_enabled(); // The ffmpeg log dir. @@ -1046,26 +1051,29 @@ class SrsConfig virtual std::string get_https_api_ssl_cert(); // http stream section private: + // Get the HTTP stream config at index. + SrsConfDirective* get_http_stream_at(int index); // Whether http stream enabled. virtual bool get_http_stream_enabled(SrsConfDirective* conf); public: // Whether http stream enabled. // TODO: FIXME: rename to http_static. - virtual bool get_http_stream_enabled(); + virtual bool get_http_stream_enabled(int stream_index = 0); // Get the http stream listen port. - virtual std::string get_http_stream_listen(); + virtual std::string get_http_stream_listen(int stream_index = 0); // Get the http stream root dir. - virtual std::string get_http_stream_dir(); + virtual std::string get_http_stream_dir(int stream_index = 0); // Whether enable crossdomain for http static and stream server. - virtual bool get_http_stream_crossdomain(); + virtual bool get_http_stream_crossdomain(int stream_index = 0); // https api section private: - SrsConfDirective* get_https_stream(); + // Get the HTTPS stream config at index. + SrsConfDirective* get_https_stream(int index); public: - virtual bool get_https_stream_enabled(); - virtual std::string get_https_stream_listen(); - virtual std::string get_https_stream_ssl_key(); - virtual std::string get_https_stream_ssl_cert(); + virtual bool get_https_stream_enabled(int stream_index = 0); + virtual std::string get_https_stream_listen(int stream_index = 0); + virtual std::string get_https_stream_ssl_key(int stream_index = 0); + virtual std::string get_https_stream_ssl_cert(int stream_index = 0); public: // Get whether vhost enabled http stream virtual bool get_vhost_http_enabled(std::string vhost); diff --git a/trunk/src/app/srs_app_conn.cpp b/trunk/src/app/srs_app_conn.cpp index 42534c0238..56fccd4e4b 100644 --- a/trunk/src/app/srs_app_conn.cpp +++ b/trunk/src/app/srs_app_conn.cpp @@ -38,10 +38,10 @@ using namespace std; #include -SrsPps* _srs_pps_ids = new SrsPps(); -SrsPps* _srs_pps_fids = new SrsPps(); -SrsPps* _srs_pps_fids_level0 = new SrsPps(); -SrsPps* _srs_pps_dispose = new SrsPps(); +__thread SrsPps* _srs_pps_ids = NULL; +__thread SrsPps* _srs_pps_fids = NULL; +__thread SrsPps* _srs_pps_fids_level0 = NULL; +__thread SrsPps* _srs_pps_dispose = NULL; ISrsDisposingHandler::ISrsDisposingHandler() { diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index fdc421fbeb..d09ddc8e20 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -501,7 +501,7 @@ class SrsGb28181StreamChannel }; -// Global singleton instance. +// TODO: FIXME: It should be thread-local or thread-safe. extern SrsGb28181Manger* _srs_gb28181; //gb28181 module management, management of all RTMP multiplexers, diff --git a/trunk/src/app/srs_app_hourglass.cpp b/trunk/src/app/srs_app_hourglass.cpp index 16bd200e65..c54f546e00 100644 --- a/trunk/src/app/srs_app_hourglass.cpp +++ b/trunk/src/app/srs_app_hourglass.cpp @@ -32,17 +32,17 @@ using namespace std; #include -SrsPps* _srs_pps_timer = new SrsPps(); - -extern SrsPps* _srs_pps_clock_15ms; -extern SrsPps* _srs_pps_clock_20ms; -extern SrsPps* _srs_pps_clock_25ms; -extern SrsPps* _srs_pps_clock_30ms; -extern SrsPps* _srs_pps_clock_35ms; -extern SrsPps* _srs_pps_clock_40ms; -extern SrsPps* _srs_pps_clock_80ms; -extern SrsPps* _srs_pps_clock_160ms; -extern SrsPps* _srs_pps_timer_s; +__thread SrsPps* _srs_pps_timer = NULL; + +extern __thread SrsPps* _srs_pps_clock_15ms; +extern __thread SrsPps* _srs_pps_clock_20ms; +extern __thread SrsPps* _srs_pps_clock_25ms; +extern __thread SrsPps* _srs_pps_clock_30ms; +extern __thread SrsPps* _srs_pps_clock_35ms; +extern __thread SrsPps* _srs_pps_clock_40ms; +extern __thread SrsPps* _srs_pps_clock_80ms; +extern __thread SrsPps* _srs_pps_clock_160ms; +extern __thread SrsPps* _srs_pps_timer_s; ISrsHourGlass::ISrsHourGlass() { diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 1a720d0f60..35619a46f7 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1083,36 +1083,6 @@ srs_error_t SrsGoApiRaw::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* srs_error_reset(err); return srs_api_response_code(w, r, code); } - } else if (scope == "srs_log_tank") { - if (value.empty() || (value != "file" && value != "console")) { - return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS); - } - - if ((err = _srs_config->raw_set_srs_log_tank(value, applied)) != srs_success) { - int code = srs_error_code(err); - srs_error_reset(err); - return srs_api_response_code(w, r, code); - } - } else if (scope == "srs_log_level") { - if (value != "verbose" && value != "info" && value != "trace" && value != "warn" && value != "error") { - return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS); - } - - if ((err = _srs_config->raw_set_srs_log_level(value, applied)) != srs_success) { - int code = srs_error_code(err); - srs_error_reset(err); - return srs_api_response_code(w, r, code); - } - } else if (scope == "srs_log_file") { - if (value.empty() || !srs_string_starts_with(value, "./", "/tmp/", "/var/") || !srs_string_ends_with(value, ".log")) { - return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS); - } - - if ((err = _srs_config->raw_set_srs_log_file(value, applied)) != srs_success) { - int code = srs_error_code(err); - srs_error_reset(err); - return srs_api_response_code(w, r, code); - } } else if (scope == "max_connections") { int mcv = ::atoi(value.c_str()); if (mcv < 10 || mcv > 65535 || !srs_is_digit_number(value)) { @@ -1124,14 +1094,6 @@ srs_error_t SrsGoApiRaw::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* srs_error_reset(err); return srs_api_response_code(w, r, code); } - } else if (scope == "utc_time") { - if (!srs_is_boolean(value)) { - return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS); - } - - if ((err = _srs_config->raw_set_utc_time(srs_config_bool2switch(value), applied)) != srs_success) { - return srs_api_response_code(w, r, srs_error_wrap(err, "raw api update utc_time=%s", value.c_str())); - } } else if (scope == "pithy_print_ms") { int ppmv = ::atoi(value.c_str()); if (ppmv < 100 || ppmv > 300000 || !srs_is_digit_number(value)) { diff --git a/trunk/src/app/srs_app_hybrid.cpp b/trunk/src/app/srs_app_hybrid.cpp index 0d878d6583..564d019057 100644 --- a/trunk/src/app/srs_app_hybrid.cpp +++ b/trunk/src/app/srs_app_hybrid.cpp @@ -28,106 +28,108 @@ #include #include #include +#include +#include using namespace std; -extern SrsPps* _srs_pps_cids_get; -extern SrsPps* _srs_pps_cids_set; +extern __thread SrsPps* _srs_pps_cids_get; +extern __thread SrsPps* _srs_pps_cids_set; -extern SrsPps* _srs_pps_timer; -extern SrsPps* _srs_pps_pub; -extern SrsPps* _srs_pps_conn; -extern SrsPps* _srs_pps_dispose; +extern __thread SrsPps* _srs_pps_timer; +extern __thread SrsPps* _srs_pps_pub; +extern __thread SrsPps* _srs_pps_conn; +extern __thread SrsPps* _srs_pps_dispose; #if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) -extern unsigned long long _st_stat_recvfrom; -extern unsigned long long _st_stat_recvfrom_eagain; -extern unsigned long long _st_stat_sendto; -extern unsigned long long _st_stat_sendto_eagain; -SrsPps* _srs_pps_recvfrom = new SrsPps(); -SrsPps* _srs_pps_recvfrom_eagain = new SrsPps(); -SrsPps* _srs_pps_sendto = new SrsPps(); -SrsPps* _srs_pps_sendto_eagain = new SrsPps(); - -extern unsigned long long _st_stat_read; -extern unsigned long long _st_stat_read_eagain; -extern unsigned long long _st_stat_readv; -extern unsigned long long _st_stat_readv_eagain; -extern unsigned long long _st_stat_writev; -extern unsigned long long _st_stat_writev_eagain; -SrsPps* _srs_pps_read = new SrsPps(); -SrsPps* _srs_pps_read_eagain = new SrsPps(); -SrsPps* _srs_pps_readv = new SrsPps(); -SrsPps* _srs_pps_readv_eagain = new SrsPps(); -SrsPps* _srs_pps_writev = new SrsPps(); -SrsPps* _srs_pps_writev_eagain = new SrsPps(); - -extern unsigned long long _st_stat_recvmsg; -extern unsigned long long _st_stat_recvmsg_eagain; -extern unsigned long long _st_stat_sendmsg; -extern unsigned long long _st_stat_sendmsg_eagain; -SrsPps* _srs_pps_recvmsg = new SrsPps(); -SrsPps* _srs_pps_recvmsg_eagain = new SrsPps(); -SrsPps* _srs_pps_sendmsg = new SrsPps(); -SrsPps* _srs_pps_sendmsg_eagain = new SrsPps(); - -extern unsigned long long _st_stat_epoll; -extern unsigned long long _st_stat_epoll_zero; -extern unsigned long long _st_stat_epoll_shake; -extern unsigned long long _st_stat_epoll_spin; -SrsPps* _srs_pps_epoll = new SrsPps(); -SrsPps* _srs_pps_epoll_zero = new SrsPps(); -SrsPps* _srs_pps_epoll_shake = new SrsPps(); -SrsPps* _srs_pps_epoll_spin = new SrsPps(); - -extern unsigned long long _st_stat_sched_15ms; -extern unsigned long long _st_stat_sched_20ms; -extern unsigned long long _st_stat_sched_25ms; -extern unsigned long long _st_stat_sched_30ms; -extern unsigned long long _st_stat_sched_35ms; -extern unsigned long long _st_stat_sched_40ms; -extern unsigned long long _st_stat_sched_80ms; -extern unsigned long long _st_stat_sched_160ms; -extern unsigned long long _st_stat_sched_s; -SrsPps* _srs_pps_sched_15ms = new SrsPps(); -SrsPps* _srs_pps_sched_20ms = new SrsPps(); -SrsPps* _srs_pps_sched_25ms = new SrsPps(); -SrsPps* _srs_pps_sched_30ms = new SrsPps(); -SrsPps* _srs_pps_sched_35ms = new SrsPps(); -SrsPps* _srs_pps_sched_40ms = new SrsPps(); -SrsPps* _srs_pps_sched_80ms = new SrsPps(); -SrsPps* _srs_pps_sched_160ms = new SrsPps(); -SrsPps* _srs_pps_sched_s = new SrsPps(); +extern __thread unsigned long long _st_stat_recvfrom; +extern __thread unsigned long long _st_stat_recvfrom_eagain; +extern __thread unsigned long long _st_stat_sendto; +extern __thread unsigned long long _st_stat_sendto_eagain; +__thread SrsPps* _srs_pps_recvfrom = NULL; +__thread SrsPps* _srs_pps_recvfrom_eagain = NULL; +__thread SrsPps* _srs_pps_sendto = NULL; +__thread SrsPps* _srs_pps_sendto_eagain = NULL; + +extern __thread unsigned long long _st_stat_read; +extern __thread unsigned long long _st_stat_read_eagain; +extern __thread unsigned long long _st_stat_readv; +extern __thread unsigned long long _st_stat_readv_eagain; +extern __thread unsigned long long _st_stat_writev; +extern __thread unsigned long long _st_stat_writev_eagain; +__thread SrsPps* _srs_pps_read = NULL; +__thread SrsPps* _srs_pps_read_eagain = NULL; +__thread SrsPps* _srs_pps_readv = NULL; +__thread SrsPps* _srs_pps_readv_eagain = NULL; +__thread SrsPps* _srs_pps_writev = NULL; +__thread SrsPps* _srs_pps_writev_eagain = NULL; + +extern __thread unsigned long long _st_stat_recvmsg; +extern __thread unsigned long long _st_stat_recvmsg_eagain; +extern __thread unsigned long long _st_stat_sendmsg; +extern __thread unsigned long long _st_stat_sendmsg_eagain; +__thread SrsPps* _srs_pps_recvmsg = NULL; +__thread SrsPps* _srs_pps_recvmsg_eagain = NULL; +__thread SrsPps* _srs_pps_sendmsg = NULL; +__thread SrsPps* _srs_pps_sendmsg_eagain = NULL; + +extern __thread unsigned long long _st_stat_epoll; +extern __thread unsigned long long _st_stat_epoll_zero; +extern __thread unsigned long long _st_stat_epoll_shake; +extern __thread unsigned long long _st_stat_epoll_spin; +__thread SrsPps* _srs_pps_epoll = NULL; +__thread SrsPps* _srs_pps_epoll_zero = NULL; +__thread SrsPps* _srs_pps_epoll_shake = NULL; +__thread SrsPps* _srs_pps_epoll_spin = NULL; + +extern __thread unsigned long long _st_stat_sched_15ms; +extern __thread unsigned long long _st_stat_sched_20ms; +extern __thread unsigned long long _st_stat_sched_25ms; +extern __thread unsigned long long _st_stat_sched_30ms; +extern __thread unsigned long long _st_stat_sched_35ms; +extern __thread unsigned long long _st_stat_sched_40ms; +extern __thread unsigned long long _st_stat_sched_80ms; +extern __thread unsigned long long _st_stat_sched_160ms; +extern __thread unsigned long long _st_stat_sched_s; +__thread SrsPps* _srs_pps_sched_15ms = NULL; +__thread SrsPps* _srs_pps_sched_20ms = NULL; +__thread SrsPps* _srs_pps_sched_25ms = NULL; +__thread SrsPps* _srs_pps_sched_30ms = NULL; +__thread SrsPps* _srs_pps_sched_35ms = NULL; +__thread SrsPps* _srs_pps_sched_40ms = NULL; +__thread SrsPps* _srs_pps_sched_80ms = NULL; +__thread SrsPps* _srs_pps_sched_160ms = NULL; +__thread SrsPps* _srs_pps_sched_s = NULL; #endif -SrsPps* _srs_pps_clock_15ms = new SrsPps(); -SrsPps* _srs_pps_clock_20ms = new SrsPps(); -SrsPps* _srs_pps_clock_25ms = new SrsPps(); -SrsPps* _srs_pps_clock_30ms = new SrsPps(); -SrsPps* _srs_pps_clock_35ms = new SrsPps(); -SrsPps* _srs_pps_clock_40ms = new SrsPps(); -SrsPps* _srs_pps_clock_80ms = new SrsPps(); -SrsPps* _srs_pps_clock_160ms = new SrsPps(); -SrsPps* _srs_pps_timer_s = new SrsPps(); +__thread SrsPps* _srs_pps_clock_15ms = NULL; +__thread SrsPps* _srs_pps_clock_20ms = NULL; +__thread SrsPps* _srs_pps_clock_25ms = NULL; +__thread SrsPps* _srs_pps_clock_30ms = NULL; +__thread SrsPps* _srs_pps_clock_35ms = NULL; +__thread SrsPps* _srs_pps_clock_40ms = NULL; +__thread SrsPps* _srs_pps_clock_80ms = NULL; +__thread SrsPps* _srs_pps_clock_160ms = NULL; +__thread SrsPps* _srs_pps_timer_s = NULL; #if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) -extern int _st_active_count; -extern unsigned long long _st_stat_thread_run; -extern unsigned long long _st_stat_thread_idle; -extern unsigned long long _st_stat_thread_yield; -extern unsigned long long _st_stat_thread_yield2; -SrsPps* _srs_pps_thread_run = new SrsPps(); -SrsPps* _srs_pps_thread_idle = new SrsPps(); -SrsPps* _srs_pps_thread_yield = new SrsPps(); -SrsPps* _srs_pps_thread_yield2 = new SrsPps(); +extern __thread int _st_active_count; +extern __thread unsigned long long _st_stat_thread_run; +extern __thread unsigned long long _st_stat_thread_idle; +extern __thread unsigned long long _st_stat_thread_yield; +extern __thread unsigned long long _st_stat_thread_yield2; +__thread SrsPps* _srs_pps_thread_run = NULL; +__thread SrsPps* _srs_pps_thread_idle = NULL; +__thread SrsPps* _srs_pps_thread_yield = NULL; +__thread SrsPps* _srs_pps_thread_yield2 = NULL; #endif -extern SrsPps* _srs_pps_objs_rtps; -extern SrsPps* _srs_pps_objs_rraw; -extern SrsPps* _srs_pps_objs_rfua; -extern SrsPps* _srs_pps_objs_rbuf; -extern SrsPps* _srs_pps_objs_msgs; -extern SrsPps* _srs_pps_objs_rothers; +extern __thread SrsPps* _srs_pps_objs_rtps; +extern __thread SrsPps* _srs_pps_objs_rraw; +extern __thread SrsPps* _srs_pps_objs_rfua; +extern __thread SrsPps* _srs_pps_objs_rbuf; +extern __thread SrsPps* _srs_pps_objs_msgs; +extern __thread SrsPps* _srs_pps_objs_rothers; ISrsHybridServer::ISrsHybridServer() { @@ -145,6 +147,8 @@ SrsHybridServer::SrsHybridServer() timer5s_ = NULL; clock_monitor_ = new SrsClockWallMonitor(); + + stream_index_ = -1; } SrsHybridServer::~SrsHybridServer() @@ -170,11 +174,6 @@ srs_error_t SrsHybridServer::initialize() { srs_error_t err = srs_success; - // init st - if ((err = srs_st_init()) != srs_success) { - return srs_error_wrap(err, "initialize st failed"); - } - // Create global shared timer. timer20ms_ = new SrsFastTimer("hybrid", 20 * SRS_UTIME_MILLISECONDS); timer100ms_ = new SrsFastTimer("hybrid", 100 * SRS_UTIME_MILLISECONDS); @@ -212,6 +211,19 @@ srs_error_t SrsHybridServer::initialize() } } + // Create slots for other threads to communicate with us. + SrsThreadEntry* self = _srs_thread_pool->self(); + + self->slot_ = new SrsThreadPipeSlot(1); + + if ((err = self->slot_->initialize()) != srs_success) { + return srs_error_wrap(err, "init slot"); + } + + if ((err = self->slot_->open_responder(this)) != srs_success) { + return srs_error_wrap(err, "init slot"); + } + return err; } @@ -219,6 +231,7 @@ srs_error_t SrsHybridServer::run() { srs_error_t err = srs_success; + // Run all servers, which should never block. vector::iterator it; for (it = servers.begin(); it != servers.end(); ++it) { ISrsHybridServer* server = *it; @@ -228,7 +241,7 @@ srs_error_t SrsHybridServer::run() } } - // Wait for all server to quit. + // Wait util quit. srs_usleep(SRS_UTIME_NO_TIMEOUT); return err; @@ -409,5 +422,57 @@ srs_error_t SrsHybridServer::on_timer(srs_utime_t interval) return err; } -SrsHybridServer* _srs_hybrid = new SrsHybridServer(); +srs_error_t SrsHybridServer::on_thread_message(SrsThreadMessage* msg, SrsThreadPipeChannel* channel) +{ + srs_error_t err = srs_success; + + RtcServerAdapter* adapter = NULL; + if (true) { + vector servers = _srs_hybrid->servers; + for (vector::iterator it = servers.begin(); it != servers.end(); ++it) { + RtcServerAdapter* server = dynamic_cast(*it); + if (server) { + adapter = server; + break; + } + } + } + + if (!adapter) { + // TODO: FIXME: Response with error information? + srs_error_t r0 = channel->responder()->write(msg, sizeof(SrsThreadMessage), NULL); + srs_freep(r0); // Always response it, ignore any error. + + return err; + } + + if (msg->id == (uint64_t)SrsThreadMessageIDRtcCreateSession) { + SrsThreadMessageRtcCreateSession* s = (SrsThreadMessageRtcCreateSession*)msg->ptr; + err = adapter->rtc->create_session(s->ruc, *s->local_sdp, &s->session); + + if (err != srs_success) { + // TODO: FIXME: Response with error information? + srs_error_t r0 = channel->responder()->write(msg, sizeof(SrsThreadMessage), NULL); + srs_freep(r0); // Always response it, ignore any error. + + return srs_error_wrap(err, "create session"); + } + + // TODO: FIXME: Response timeout if error? + // TODO: FIXME: Response a different message? With trace ID? + // We're responder, write response to responder. + err = channel->responder()->write(msg, sizeof(SrsThreadMessage), NULL); + if (err != srs_success) { + return srs_error_wrap(err, "response"); + } + } else { + // TODO: FIXME: Response with error information? + srs_error_t r0 = channel->responder()->write(msg, sizeof(SrsThreadMessage), NULL); + srs_freep(r0); // Always response it, ignore any error. + } + + return err; +} + + __thread SrsHybridServer* _srs_hybrid = NULL; diff --git a/trunk/src/app/srs_app_hybrid.hpp b/trunk/src/app/srs_app_hybrid.hpp index 729f00016d..514b5f4529 100644 --- a/trunk/src/app/srs_app_hybrid.hpp +++ b/trunk/src/app/srs_app_hybrid.hpp @@ -29,6 +29,7 @@ #include #include +#include class SrsServer; class SrsServerAdapter; @@ -49,7 +50,7 @@ class ISrsHybridServer }; // The hybrid server manager. -class SrsHybridServer : public ISrsFastTimer +class SrsHybridServer : public ISrsFastTimer, public ISrsThreadResponder { private: std::vector servers; @@ -58,11 +59,17 @@ class SrsHybridServer : public ISrsFastTimer SrsFastTimer* timer1s_; SrsFastTimer* timer5s_; SrsClockWallMonitor* clock_monitor_; +private: + // The config index for hybrid/stream server. + int stream_index_; public: SrsHybridServer(); virtual ~SrsHybridServer(); public: virtual void register_server(ISrsHybridServer* svr); +public: + int stream_index() { return stream_index_; } // SrsHybridServer::stream_index() + void set_stream_index(int v) { stream_index_ = v; } // SrsHybridServer::set_stream_index() public: virtual srs_error_t initialize(); virtual srs_error_t run(); @@ -76,8 +83,10 @@ class SrsHybridServer : public ISrsFastTimer // interface ISrsFastTimer private: srs_error_t on_timer(srs_utime_t interval); +private: + srs_error_t on_thread_message(SrsThreadMessage* msg, SrsThreadPipeChannel* channel); }; -extern SrsHybridServer* _srs_hybrid; +extern __thread SrsHybridServer* _srs_hybrid; #endif diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index 0037a6d9c7..fb38d5bae2 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -42,17 +42,19 @@ using namespace std; #include #include #include +#include +#include #include -SrsPps* _srs_pps_rpkts = new SrsPps(); -SrsPps* _srs_pps_addrs = new SrsPps(); -SrsPps* _srs_pps_fast_addrs = new SrsPps(); +__thread SrsPps* _srs_pps_rpkts = NULL; +__thread SrsPps* _srs_pps_addrs = NULL; +__thread SrsPps* _srs_pps_fast_addrs = NULL; -SrsPps* _srs_pps_spkts = new SrsPps(); +__thread SrsPps* _srs_pps_spkts = NULL; // set the max packet size. -#define SRS_UDP_MAX_PACKET_SIZE 65535 +const int SRS_UDP_MAX_PACKET_SIZE = 1500; // sleep in srs_utime_t for udp recv packet. #define SrsUdpPacketRecvCycleInterval 0 @@ -300,6 +302,7 @@ SrsUdpMuxSocket::SrsUdpMuxSocket(srs_netfd_t fd) nread = 0; lfd = fd; + handler_ = NULL; fromlen = 0; peer_port = 0; @@ -323,6 +326,11 @@ int SrsUdpMuxSocket::recvfrom(srs_utime_t timeout) return nread; } + return on_recvfrom(); +} + +int SrsUdpMuxSocket::on_recvfrom() +{ // Reset the fast cache buffer size. cache_buffer_->set_size(nread); cache_buffer_->skip(-1 * cache_buffer_->pos()); @@ -361,8 +369,8 @@ srs_error_t SrsUdpMuxSocket::sendto(void* data, int size, srs_utime_t timeout) if (nb_write <= 0) { if (nb_write < 0 && errno == ETIME) { return srs_error_new(ERROR_SOCKET_TIMEOUT, "sendto timeout %d ms", srsu2msi(timeout)); - } - + } + return srs_error_new(ERROR_SOCKET_WRITE, "sendto"); } @@ -491,9 +499,42 @@ SrsUdpMuxSocket* SrsUdpMuxSocket::copy_sendonly() sendonly->fast_id_ = fast_id_; sendonly->address_changed_ = address_changed_; + sendonly->handler_ = handler_; + return sendonly; } +SrsUdpMuxSocket* SrsUdpMuxSocket::copy() +{ + SrsUdpMuxSocket* cp = new SrsUdpMuxSocket(lfd); + + cp->nb_buf = nb_buf; + if (nread) { + memcpy(cp->buf, buf, nread); + } + cp->nread = nread; + cp->lfd = lfd; + cp->from = from; + cp->fromlen = fromlen; + cp->peer_ip = peer_ip; + cp->peer_port = peer_port; + + // Copy the fast id. + cp->peer_id_ = peer_id_; + cp->fast_id_ = fast_id_; + cp->address_changed_ = address_changed_; + + if (nread) { + // Reset the fast cache buffer size. + cp->cache_buffer_->set_size(nread); + cp->cache_buffer_->skip(-1 * cache_buffer_->pos()); + } + + cp->handler_ = handler_; + + return cp; +} + SrsUdpMuxListener::SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p) { handler = h; diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index e3ba3e975b..54de6b30e0 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -162,9 +162,20 @@ class SrsUdpMuxSocket public: SrsUdpMuxSocket(srs_netfd_t fd); virtual ~SrsUdpMuxSocket(); +private: + ISrsUdpMuxHandler* handler_; +public: + // SrsUdpMuxSocket::set_handler + void set_handler(ISrsUdpMuxHandler* h) { handler_ = h; } + // SrsUdpMuxSocket::handler + ISrsUdpMuxHandler* handler() { return handler_; } public: int recvfrom(srs_utime_t timeout); +private: + int on_recvfrom(); +public: srs_error_t sendto(void* data, int size, srs_utime_t timeout); +public: srs_netfd_t stfd(); sockaddr_in* peer_addr(); socklen_t peer_addrlen(); @@ -176,6 +187,8 @@ class SrsUdpMuxSocket uint64_t fast_id(); SrsBuffer* buffer(); SrsUdpMuxSocket* copy_sendonly(); +public: + SrsUdpMuxSocket* copy(); }; class SrsUdpMuxListener : public ISrsCoroutineHandler diff --git a/trunk/src/app/srs_app_log.cpp b/trunk/src/app/srs_app_log.cpp index 28ff755ef1..6e1a62e011 100644 --- a/trunk/src/app/srs_app_log.cpp +++ b/trunk/src/app/srs_app_log.cpp @@ -30,68 +30,64 @@ #include #include #include +#include #include #include #include #include +#include // the max size of a line of log. -#define LOG_MAX_SIZE 8192 +int LOG_MAX_SIZE = 8192; // the tail append to each log. #define LOG_TAIL '\n' // reserved for the end of log data, it must be strlen(LOG_TAIL) #define LOG_TAIL_SIZE 1 +// Thread local log cache. +__thread char* _srs_log_data = NULL; + SrsFileLog::SrsFileLog() { level = SrsLogLevelTrace; - log_data = new char[LOG_MAX_SIZE]; - - fd = -1; log_to_file_tank = false; utc = false; + + writer_ = NULL; } SrsFileLog::~SrsFileLog() { - srs_freepa(log_data); - - if (fd > 0) { - ::close(fd); - fd = -1; - } - - if (_srs_config) { - _srs_config->unsubscribe(this); - } } +// @remark Note that we should never write logs, because log is not ready not. srs_error_t SrsFileLog::initialize() { + srs_error_t err = srs_success; + if (_srs_config) { - _srs_config->subscribe(this); - log_to_file_tank = _srs_config->get_log_tank_file(); + filename_ = _srs_config->get_log_file(); level = srs_get_log_level(_srs_config->get_log_level()); utc = _srs_config->get_utc_time(); } - - return srs_success; -} -void SrsFileLog::reopen() -{ - if (fd > 0) { - ::close(fd); - } - if (!log_to_file_tank) { - return; + return err; + } + + if (filename_.empty()) { + return srs_error_new(ERROR_SYSTEM_LOGFILE, "no log filename"); + } + + // We only use the log writer, which is managed by another thread. + if ((err = _srs_async_log->create_writer(filename_, &writer_)) != srs_success) { + return srs_error_wrap(err, "create async writer for %s", filename_.c_str()); } - open_log_file(); + return err; } void SrsFileLog::verbose(const char* tag, SrsContextId context_id, const char* fmt, ...) @@ -101,17 +97,17 @@ void SrsFileLog::verbose(const char* tag, SrsContextId context_id, const char* f } int size = 0; - if (!srs_log_header(log_data, LOG_MAX_SIZE, utc, false, tag, context_id, "Verb", &size)) { + if (!srs_log_header(_srs_log_data, LOG_MAX_SIZE, utc, false, tag, context_id, "Verb", &size)) { return; } va_list ap; va_start(ap, fmt); // we reserved 1 bytes for the new line. - size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap); + size += vsnprintf(_srs_log_data + size, LOG_MAX_SIZE - size, fmt, ap); va_end(ap); - write_log(fd, log_data, size, SrsLogLevelVerbose); + write_log(_srs_log_data, size, SrsLogLevelVerbose); } void SrsFileLog::info(const char* tag, SrsContextId context_id, const char* fmt, ...) @@ -121,17 +117,17 @@ void SrsFileLog::info(const char* tag, SrsContextId context_id, const char* fmt, } int size = 0; - if (!srs_log_header(log_data, LOG_MAX_SIZE, utc, false, tag, context_id, "Debug", &size)) { + if (!srs_log_header(_srs_log_data, LOG_MAX_SIZE, utc, false, tag, context_id, "Debug", &size)) { return; } va_list ap; va_start(ap, fmt); // we reserved 1 bytes for the new line. - size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap); + size += vsnprintf(_srs_log_data + size, LOG_MAX_SIZE - size, fmt, ap); va_end(ap); - write_log(fd, log_data, size, SrsLogLevelInfo); + write_log(_srs_log_data, size, SrsLogLevelInfo); } void SrsFileLog::trace(const char* tag, SrsContextId context_id, const char* fmt, ...) @@ -141,17 +137,17 @@ void SrsFileLog::trace(const char* tag, SrsContextId context_id, const char* fmt } int size = 0; - if (!srs_log_header(log_data, LOG_MAX_SIZE, utc, false, tag, context_id, "Trace", &size)) { + if (!srs_log_header(_srs_log_data, LOG_MAX_SIZE, utc, false, tag, context_id, "Trace", &size)) { return; } va_list ap; va_start(ap, fmt); // we reserved 1 bytes for the new line. - size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap); + size += vsnprintf(_srs_log_data + size, LOG_MAX_SIZE - size, fmt, ap); va_end(ap); - write_log(fd, log_data, size, SrsLogLevelTrace); + write_log(_srs_log_data, size, SrsLogLevelTrace); } void SrsFileLog::warn(const char* tag, SrsContextId context_id, const char* fmt, ...) @@ -161,17 +157,17 @@ void SrsFileLog::warn(const char* tag, SrsContextId context_id, const char* fmt, } int size = 0; - if (!srs_log_header(log_data, LOG_MAX_SIZE, utc, true, tag, context_id, "Warn", &size)) { + if (!srs_log_header(_srs_log_data, LOG_MAX_SIZE, utc, true, tag, context_id, "Warn", &size)) { return; } va_list ap; va_start(ap, fmt); // we reserved 1 bytes for the new line. - size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap); + size += vsnprintf(_srs_log_data + size, LOG_MAX_SIZE - size, fmt, ap); va_end(ap); - write_log(fd, log_data, size, SrsLogLevelWarn); + write_log(_srs_log_data, size, SrsLogLevelWarn); } void SrsFileLog::error(const char* tag, SrsContextId context_id, const char* fmt, ...) @@ -181,103 +177,40 @@ void SrsFileLog::error(const char* tag, SrsContextId context_id, const char* fmt } int size = 0; - if (!srs_log_header(log_data, LOG_MAX_SIZE, utc, true, tag, context_id, "Error", &size)) { + if (!srs_log_header(_srs_log_data, LOG_MAX_SIZE, utc, true, tag, context_id, "Error", &size)) { return; } va_list ap; va_start(ap, fmt); // we reserved 1 bytes for the new line. - size += vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, ap); + size += vsnprintf(_srs_log_data + size, LOG_MAX_SIZE - size, fmt, ap); va_end(ap); // add strerror() to error msg. // Check size to avoid security issue https://github.com/ossrs/srs/issues/1229 if (errno != 0 && size < LOG_MAX_SIZE) { - size += snprintf(log_data + size, LOG_MAX_SIZE - size, "(%s)", strerror(errno)); + size += snprintf(_srs_log_data + size, LOG_MAX_SIZE - size, "(%s)", strerror(errno)); } - write_log(fd, log_data, size, SrsLogLevelError); -} - -srs_error_t SrsFileLog::on_reload_utc_time() -{ - utc = _srs_config->get_utc_time(); - - return srs_success; + write_log(_srs_log_data, size, SrsLogLevelError); } -srs_error_t SrsFileLog::on_reload_log_tank() +void SrsFileLog::write_log(char *str_log, int size, int level) { srs_error_t err = srs_success; - - if (!_srs_config) { - return err; - } - - bool tank = log_to_file_tank; - log_to_file_tank = _srs_config->get_log_tank_file(); - - if (tank) { - return err; - } - - if (!log_to_file_tank) { - return err; - } - - if (fd > 0) { - ::close(fd); - } - open_log_file(); - - return err; -} -srs_error_t SrsFileLog::on_reload_log_level() -{ - srs_error_t err = srs_success; - - if (!_srs_config) { - return err; - } - - level = srs_get_log_level(_srs_config->get_log_level()); - - return err; -} - -srs_error_t SrsFileLog::on_reload_log_file() -{ - srs_error_t err = srs_success; - - if (!_srs_config) { - return err; - } - - if (!log_to_file_tank) { - return err; - } - - if (fd > 0) { - ::close(fd); - } - open_log_file(); - - return err; -} - -void SrsFileLog::write_log(int& fd, char *str_log, int size, int level) -{ // ensure the tail and EOF of string // LOG_TAIL_SIZE for the TAIL char. // 1 for the last char(0). - size = srs_min(LOG_MAX_SIZE - 1 - LOG_TAIL_SIZE, size); + size = srs_min(LOG_MAX_SIZE - 2 - LOG_TAIL_SIZE, size); // add some to the end of char. str_log[size++] = LOG_TAIL; - + str_log[size] = 0; + // if not to file, to console and return. + // @remark Its value changes, because there is some log before config loaded. if (!log_to_file_tank) { // if is error msg, then print color msg. // \033[31m : red text code in shell @@ -295,33 +228,10 @@ void SrsFileLog::write_log(int& fd, char *str_log, int size, int level) return; } - - // open log file. if specified - if (fd < 0) { - open_log_file(); - } - - // write log to file. - if (fd > 0) { - ::write(fd, str_log, size); - } -} -void SrsFileLog::open_log_file() -{ - if (!_srs_config) { - return; - } - - std::string filename = _srs_config->get_log_file(); - - if (filename.empty()) { - return; + // write log to file. + if ((err = writer_->write(str_log, size, NULL)) != srs_success) { + srs_error_reset(err); // Ignore any error for log writing. } - - fd = ::open(filename.c_str(), - O_RDWR | O_CREAT | O_APPEND, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH - ); } diff --git a/trunk/src/app/srs_app_log.hpp b/trunk/src/app/srs_app_log.hpp index 85907e29a2..f349b9e3ed 100644 --- a/trunk/src/app/srs_app_log.hpp +++ b/trunk/src/app/srs_app_log.hpp @@ -32,6 +32,8 @@ #include #include +class SrsAsyncFileWriter; + // For log TAGs. #define TAG_MAIN "MAIN" #define TAG_MAYBE "MAYBE" @@ -45,15 +47,16 @@ // when you want to use different level, override this classs, set the protected _level. class SrsFileLog : public ISrsLog, public ISrsReloadHandler { +private: + // Async file writer. + SrsAsyncFileWriter* writer_; private: // Defined in SrsLogLevel. SrsLogLevel level; -private: - char* log_data; - // Log to file if specified srs_log_file - int fd; // Whether log to file tank bool log_to_file_tank; + // If log to file, the log filename. + std::string filename_; // Whether use utc time. bool utc; public: @@ -62,21 +65,13 @@ class SrsFileLog : public ISrsLog, public ISrsReloadHandler // Interface ISrsLog public: virtual srs_error_t initialize(); - virtual void reopen(); virtual void verbose(const char* tag, SrsContextId context_id, const char* fmt, ...); virtual void info(const char* tag, SrsContextId context_id, const char* fmt, ...); virtual void trace(const char* tag, SrsContextId context_id, const char* fmt, ...); virtual void warn(const char* tag, SrsContextId context_id, const char* fmt, ...); virtual void error(const char* tag, SrsContextId context_id, const char* fmt, ...); -// Interface ISrsReloadHandler. -public: - virtual srs_error_t on_reload_utc_time(); - virtual srs_error_t on_reload_log_tank(); - virtual srs_error_t on_reload_log_level(); - virtual srs_error_t on_reload_log_file(); private: - virtual void write_log(int& fd, char* str_log, int size, int level); - virtual void open_log_file(); + virtual void write_log(char* str_log, int size, int level); }; #endif diff --git a/trunk/src/app/srs_app_pithy_print.cpp b/trunk/src/app/srs_app_pithy_print.cpp index 98137326b6..d344857b9b 100644 --- a/trunk/src/app/srs_app_pithy_print.cpp +++ b/trunk/src/app/srs_app_pithy_print.cpp @@ -192,8 +192,8 @@ bool SrsAlonePithyPrint::can_print() return info_.can_print(); } -// The global stage manager for pithy print, multiple stages. -static SrsStageManager* _srs_stages = new SrsStageManager(); +// It MUST be thread-local, by design. +__thread SrsStageManager* _srs_stages = NULL; SrsPithyPrint::SrsPithyPrint(int _stage_id) { diff --git a/trunk/src/app/srs_app_reload.cpp b/trunk/src/app/srs_app_reload.cpp index 28c4dfc858..4c9256b6b5 100644 --- a/trunk/src/app/srs_app_reload.cpp +++ b/trunk/src/app/srs_app_reload.cpp @@ -40,11 +40,6 @@ srs_error_t ISrsReloadHandler::on_reload_listen() return srs_success; } -srs_error_t ISrsReloadHandler::on_reload_utc_time() -{ - return srs_success; -} - srs_error_t ISrsReloadHandler::on_reload_max_conns() { return srs_success; @@ -55,21 +50,6 @@ srs_error_t ISrsReloadHandler::on_reload_pid() return srs_success; } -srs_error_t ISrsReloadHandler::on_reload_log_tank() -{ - return srs_success; -} - -srs_error_t ISrsReloadHandler::on_reload_log_level() -{ - return srs_success; -} - -srs_error_t ISrsReloadHandler::on_reload_log_file() -{ - return srs_success; -} - srs_error_t ISrsReloadHandler::on_reload_pithy_print() { return srs_success; diff --git a/trunk/src/app/srs_app_reload.hpp b/trunk/src/app/srs_app_reload.hpp index 9a1668d8e1..a63e2b5d83 100644 --- a/trunk/src/app/srs_app_reload.hpp +++ b/trunk/src/app/srs_app_reload.hpp @@ -39,13 +39,9 @@ class ISrsReloadHandler ISrsReloadHandler(); virtual ~ISrsReloadHandler(); public: - virtual srs_error_t on_reload_utc_time(); virtual srs_error_t on_reload_max_conns(); virtual srs_error_t on_reload_listen(); virtual srs_error_t on_reload_pid(); - virtual srs_error_t on_reload_log_tank(); - virtual srs_error_t on_reload_log_level(); - virtual srs_error_t on_reload_log_file(); virtual srs_error_t on_reload_pithy_print(); virtual srs_error_t on_reload_http_api_enabled(); virtual srs_error_t on_reload_http_api_disabled(); diff --git a/trunk/src/app/srs_app_rtc_api.cpp b/trunk/src/app/srs_app_rtc_api.cpp index e183604035..67daf8ab32 100644 --- a/trunk/src/app/srs_app_rtc_api.cpp +++ b/trunk/src/app/srs_app_rtc_api.cpp @@ -38,7 +38,15 @@ using namespace std; uint32_t SrsGoApiRtcPlay::ssrc_num = 0; -SrsGoApiRtcPlay::SrsGoApiRtcPlay(SrsRtcServer* server) +ISrsRtcServer::ISrsRtcServer() +{ +} + +ISrsRtcServer::~ISrsRtcServer() +{ +} + +SrsGoApiRtcPlay::SrsGoApiRtcPlay(ISrsRtcServer* server) { server_ = server; } @@ -413,7 +421,7 @@ srs_error_t SrsGoApiRtcPlay::exchange_sdp(SrsRequest* req, const SrsSdp& remote_ uint32_t SrsGoApiRtcPublish::ssrc_num = 0; -SrsGoApiRtcPublish::SrsGoApiRtcPublish(SrsRtcServer* server) +SrsGoApiRtcPublish::SrsGoApiRtcPublish(ISrsRtcServer* server) { server_ = server; } diff --git a/trunk/src/app/srs_app_rtc_api.hpp b/trunk/src/app/srs_app_rtc_api.hpp index c7bc6596ba..df3aca6f9c 100644 --- a/trunk/src/app/srs_app_rtc_api.hpp +++ b/trunk/src/app/srs_app_rtc_api.hpp @@ -26,20 +26,33 @@ #include +#include + #include class SrsRtcServer; class SrsRequest; class SrsSdp; +class SrsRtcConnection; +class SrsRtcUserConfig; + +class ISrsRtcServer +{ +public: + ISrsRtcServer(); + virtual ~ISrsRtcServer(); +public: + virtual srs_error_t create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sdp, SrsRtcConnection** psession) = 0; +}; class SrsGoApiRtcPlay : public ISrsHttpHandler { public: static uint32_t ssrc_num; private: - SrsRtcServer* server_; + ISrsRtcServer* server_; public: - SrsGoApiRtcPlay(SrsRtcServer* server); + SrsGoApiRtcPlay(ISrsRtcServer* server); virtual ~SrsGoApiRtcPlay(); public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); @@ -54,9 +67,9 @@ class SrsGoApiRtcPublish : public ISrsHttpHandler public: static uint32_t ssrc_num; private: - SrsRtcServer* server_; + ISrsRtcServer* server_; public: - SrsGoApiRtcPublish(SrsRtcServer* server); + SrsGoApiRtcPublish(ISrsRtcServer* server); virtual ~SrsGoApiRtcPublish(); public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 57ddb76f8a..4f032eb142 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -57,24 +57,26 @@ using namespace std; #include #include #include +#include #include -SrsPps* _srs_pps_sstuns = new SrsPps(); -SrsPps* _srs_pps_srtcps = new SrsPps(); -SrsPps* _srs_pps_srtps = new SrsPps(); +__thread SrsPps* _srs_pps_sstuns = NULL; +__thread SrsPps* _srs_pps_srtcps = NULL; +__thread SrsPps* _srs_pps_srtps = NULL; -SrsPps* _srs_pps_pli = new SrsPps(); -SrsPps* _srs_pps_twcc = new SrsPps(); -SrsPps* _srs_pps_rr = new SrsPps(); -SrsPps* _srs_pps_pub = new SrsPps(); -SrsPps* _srs_pps_conn = new SrsPps(); +__thread SrsPps* _srs_pps_pli = NULL; +__thread SrsPps* _srs_pps_twcc = NULL; +__thread SrsPps* _srs_pps_rr = NULL; +__thread SrsPps* _srs_pps_pub = NULL; +__thread SrsPps* _srs_pps_conn = NULL; -extern SrsPps* _srs_pps_snack; -extern SrsPps* _srs_pps_snack2; +extern __thread SrsPps* _srs_pps_snack; +extern __thread SrsPps* _srs_pps_snack2; -extern SrsPps* _srs_pps_rnack; -extern SrsPps* _srs_pps_rnack2; +extern __thread SrsPps* _srs_pps_rnack; +extern __thread SrsPps* _srs_pps_rnack2; +extern __thread SrsPps* _srs_pps_snack4; ISrsRtcTransport::ISrsRtcTransport() { @@ -256,7 +258,7 @@ srs_error_t SrsPlaintextTransport::on_dtls_alert(std::string type, std::string d srs_error_t SrsPlaintextTransport::on_dtls_handshake_done() { - srs_trace("RTC: DTLS handshake done."); + srs_trace("RTC: DTLS plaintext handshake done."); return session_->on_connection_established(); } @@ -593,6 +595,9 @@ srs_error_t SrsRtcPlayStream::cycle() } } + // How many messages to run a yield. + uint32_t nn_msgs_for_yield = 0; + while (true) { if ((err = trd_->pull()) != srs_success) { return srs_error_wrap(err, "rtc sender thread"); @@ -620,6 +625,13 @@ srs_error_t SrsRtcPlayStream::cycle() // Release the packet to cache. // @remark Note that the pkt might be set to NULL. _srs_rtp_cache->recycle(pkt); + + // Yield to another coroutines. + // @see https://github.com/ossrs/srs/issues/2194#issuecomment-777485531 + if (++nn_msgs_for_yield > 10) { + nn_msgs_for_yield = 0; + srs_thread_yield(); + } } } @@ -1217,6 +1229,12 @@ srs_error_t SrsRtcPublishStream::on_rtp(char* data, int nb_data) return err; } + // For async SRTP, the nb_plaintext might be zero, which means we do not got the plaintext + // right now, and it will callback if get one. + if (nb_plaintext == 0) { + return err; + } + // Handle the plaintext RTP packet. if ((err = on_rtp_plaintext(plaintext, nb_plaintext)) != srs_success) { // We try to decode the RTP header for more detail error informations. @@ -1299,6 +1317,12 @@ srs_error_t SrsRtcPublishStream::do_on_rtp_plaintext(SrsRtpPacket2*& pkt, SrsBuf } } + // If circuit-breaker is enabled, disable nack. + if (_srs_circuit_breaker->hybrid_critical_water_level()) { + ++_srs_pps_snack4->sugar; + return err; + } + // For NACK to handle packet. // @remark Note that the pkt might be set to NULL. if (nack_enabled_) { @@ -1547,6 +1571,12 @@ srs_error_t SrsRtcPublishStream::on_timer(srs_utime_t interval) if (twcc_enabled_) { ++_srs_pps_twcc->sugar; + // If circuit-breaker is dropping packet, disable TWCC. + if (_srs_circuit_breaker->hybrid_critical_water_level()) { + ++_srs_pps_snack4->sugar; + return err; + } + // We should not depends on the received packet, // instead we should send feedback every Nms. if ((err = send_periodic_twcc()) != srs_success) { @@ -2004,6 +2034,23 @@ srs_error_t SrsRtcConnection::on_rtcp(char* data, int nb_data) return srs_error_wrap(err, "rtcp unprotect"); } + // For async SRTP, the nb_unprotected_buf might be zero, which means we do not got the plaintext + // right now, and it will callback if get one. + if (nb_unprotected_buf == 0) { + return err; + } + + if ((err = on_rtcp_plaintext(data, nb_unprotected_buf)) != srs_success) { + return srs_error_wrap(err, "cipher=%d", nb_data); + } + + return err; +} + +srs_error_t SrsRtcConnection::on_rtcp_plaintext(char* data, int nb_unprotected_buf) +{ + srs_error_t err = srs_success; + char* unprotected_buf = data; if (_srs_blackhole->blackhole) { _srs_blackhole->sendto(unprotected_buf, nb_unprotected_buf); @@ -2025,7 +2072,7 @@ srs_error_t SrsRtcConnection::on_rtcp(char* data, int nb_data) SrsAutoFree(SrsRtcpCommon, rtcp); if(srs_success != err) { - return srs_error_wrap(err, "cipher=%u, plaintext=%u, bytes=[%s], rtcp=(%u,%u,%u,%u)", nb_data, nb_unprotected_buf, + return srs_error_wrap(err, "plaintext=%u, bytes=[%s], rtcp=(%u,%u,%u,%u)", nb_unprotected_buf, srs_string_dumps_hex(rtcp->data(), rtcp->size(), rtcp->size()).c_str(), rtcp->get_rc(), rtcp->type(), rtcp->get_ssrc(), rtcp->size()); } @@ -2034,6 +2081,28 @@ srs_error_t SrsRtcConnection::on_rtcp(char* data, int nb_data) return err; } +srs_error_t SrsRtcConnection::on_rtp_cipher(char* cipher, int size) +{ + srs_error_t err = srs_success; + + if ((err = sendonly_skt->sendto(cipher, size, 0)) != srs_success) { + srs_error_reset(err); // Ignore any error. + } + + return err; +} + +srs_error_t SrsRtcConnection::on_rtcp_cipher(char* cipher, int size) +{ + srs_error_t err = srs_success; + + if ((err = sendonly_skt->sendto(cipher, size, 0)) != srs_success) { + srs_error_reset(err); // Ignore any error. + } + + return err; +} + srs_error_t SrsRtcConnection::dispatch_rtcp(SrsRtcpCommon* rtcp) { srs_error_t err = srs_success; @@ -2146,6 +2215,22 @@ srs_error_t SrsRtcConnection::on_rtp(char* data, int nb_data) return publisher->on_rtp(data, nb_data); } +srs_error_t SrsRtcConnection::on_rtp_plaintext(char* plaintext, int nb_plaintext) +{ + srs_error_t err = srs_success; + + // We should keep alive here, for tunnel is enabled. + alive(); + + SrsRtcPublishStream* publisher = NULL; + if ((err = find_publisher(plaintext, nb_plaintext, &publisher)) != srs_success) { + return srs_error_wrap(err, "find"); + } + srs_assert(publisher); + + return publisher->on_rtp_plaintext(plaintext, nb_plaintext); +} + srs_error_t SrsRtcConnection::find_publisher(char* buf, int size, SrsRtcPublishStream** ppublisher) { srs_error_t err = srs_success; @@ -2334,10 +2419,13 @@ srs_error_t SrsRtcConnection::on_timer(srs_utime_t interval) ++_srs_pps_conn->sugar; - // For publisher to send NACK. - // TODO: FIXME: Merge with hybrid system clock. - srs_update_system_time(); + // If circuit-breaker is enabled, disable nack. + if (_srs_circuit_breaker->hybrid_critical_water_level()) { + ++_srs_pps_snack4->sugar; + return err; + } + // For publisher to send NACK. std::map::iterator it; for (it = publishers_.begin(); it != publishers_.end(); it++) { SrsRtcPublishStream* publisher = it->second; @@ -2362,11 +2450,12 @@ srs_error_t SrsRtcConnection::send_rtcp(char *data, int nb_data) return srs_error_wrap(err, "protect rtcp"); } - if ((err = sendonly_skt->sendto(data, nb_buf, 0)) != srs_success) { - return srs_error_wrap(err, "send"); + // Async SRTP encrypt. + if (nb_buf <= 0) { + return err; } - return err; + return on_rtcp_cipher(data, nb_buf); } void SrsRtcConnection::check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc, uint32_t& sent_nacks, uint32_t& timeout_nacks) @@ -2552,6 +2641,11 @@ srs_error_t SrsRtcConnection::do_send_packet(SrsRtpPacket2* pkt) iov->iov_len = (size_t)nn_encrypt; } + // Async SRTP encrypt. + if (iov->iov_len <= 0) { + return err; + } + // For NACK simulator, drop packet. if (nn_simulate_player_nack_drop) { simulate_player_drop_packet(&pkt->header, (int)iov->iov_len); @@ -2561,14 +2655,11 @@ srs_error_t SrsRtcConnection::do_send_packet(SrsRtpPacket2* pkt) ++_srs_pps_srtps->sugar; - // TODO: FIXME: Handle error. - sendonly_skt->sendto(iov->iov_base, iov->iov_len, 0); - // Detail log, should disable it in release version. srs_info("RTC: SEND PT=%u, SSRC=%#x, SEQ=%u, Time=%u, %u/%u bytes", pkt->header.get_payload_type(), pkt->header.get_ssrc(), pkt->header.get_sequence(), pkt->header.get_timestamp(), pkt->nb_bytes(), iov->iov_len); - return err; + return on_rtp_cipher((char*)iov->iov_base, iov->iov_len); } void SrsRtcConnection::set_all_tracks_status(std::string stream_uri, bool is_publish, bool status) diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index ab3d0a4e08..495816273b 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -334,7 +333,7 @@ class SrsRtcPublishStream : public ISrsFastTimer, public ISrsRtpPacketDecodeHand srs_error_t send_rtcp_xr_rrtr(); public: srs_error_t on_rtp(char* buf, int nb_buf); -private: +public: // @remark We copy the plaintext, user should free it. srs_error_t on_rtp_plaintext(char* plaintext, int nb_plaintext); private: @@ -492,10 +491,16 @@ class SrsRtcConnection : public ISrsResource, public ISrsFastTimer, public ISrsD srs_error_t on_dtls(char* data, int nb_data); srs_error_t on_rtp(char* data, int nb_data); private: + srs_error_t on_rtp_plaintext(char* plaintext, int size); // Decode the RTP header from buf, find the publisher by SSRC. srs_error_t find_publisher(char* buf, int size, SrsRtcPublishStream** ppublisher); public: srs_error_t on_rtcp(char* data, int nb_data); +private: + srs_error_t on_rtcp_plaintext(char* plaintext, int size); +private: + srs_error_t on_rtp_cipher(char* cipher, int size); + srs_error_t on_rtcp_cipher(char* cipher, int size); private: srs_error_t dispatch_rtcp(SrsRtcpCommon* rtcp); public: @@ -568,6 +573,8 @@ class ISrsRtcHijacker virtual srs_error_t on_start_consume(SrsRtcConnection* session, SrsRtcPlayStream* player, SrsRequest* req, SrsRtcConsumer* consumer) = 0; }; +// TODO: FIXME: It should be thread-local or thread-safe. +// TODO: FIXME: It seems thread-local make sense. extern ISrsRtcHijacker* _srs_rtc_hijacker; #endif diff --git a/trunk/src/app/srs_app_rtc_dtls.hpp b/trunk/src/app/srs_app_rtc_dtls.hpp index 0938f30c66..9236a41188 100644 --- a/trunk/src/app/srs_app_rtc_dtls.hpp +++ b/trunk/src/app/srs_app_rtc_dtls.hpp @@ -35,6 +35,7 @@ #include class SrsRequest; +class SrsUdpMuxSocket; class SrsDtlsCertificate { @@ -62,7 +63,7 @@ class SrsDtlsCertificate bool is_ecdsa(); }; -// @global config object. +// It's shared global object, MUST be thread-safe. extern SrsDtlsCertificate* _srs_rtc_dtls_certificate; // @remark: play the role of DTLS_CLIENT, will send handshake @@ -231,20 +232,20 @@ class SrsDtls class SrsSRTP { -private: +protected: srtp_t recv_ctx_; srtp_t send_ctx_; public: SrsSRTP(); virtual ~SrsSRTP(); public: - // Intialize srtp context with recv_key and send_key. - srs_error_t initialize(std::string recv_key, std::string send_key); + // Initialize srtp context with recv_key and send_key. + virtual srs_error_t initialize(std::string recv_key, std::string send_key); public: - srs_error_t protect_rtp(void* packet, int* nb_cipher); - srs_error_t protect_rtcp(void* packet, int* nb_cipher); - srs_error_t unprotect_rtp(void* packet, int* nb_plaintext); - srs_error_t unprotect_rtcp(void* packet, int* nb_plaintext); + virtual srs_error_t protect_rtp(void* packet, int* nb_cipher); + virtual srs_error_t protect_rtcp(void* packet, int* nb_cipher); + virtual srs_error_t unprotect_rtp(void* packet, int* nb_plaintext); + virtual srs_error_t unprotect_rtcp(void* packet, int* nb_plaintext); }; #endif diff --git a/trunk/src/app/srs_app_rtc_queue.cpp b/trunk/src/app/srs_app_rtc_queue.cpp index 7a19f2b579..9bdf246fc9 100644 --- a/trunk/src/app/srs_app_rtc_queue.cpp +++ b/trunk/src/app/srs_app_rtc_queue.cpp @@ -33,6 +33,12 @@ using namespace std; #include #include #include +#include + +#include + +extern __thread SrsPps* _srs_pps_snack3; +extern __thread SrsPps* _srs_pps_snack4; SrsRtpRingBuffer::SrsRtpRingBuffer(int capacity) { @@ -228,6 +234,12 @@ SrsRtpNackForReceiver::~SrsRtpNackForReceiver() void SrsRtpNackForReceiver::insert(uint16_t first, uint16_t last) { + // If circuit-breaker is enabled, disable nack. + if (_srs_circuit_breaker->hybrid_high_water_level()) { + ++_srs_pps_snack4->sugar; + return; + } + for (uint16_t s = first; s != last; ++s) { queue_[s] = SrsRtpNackInfo(); } @@ -259,6 +271,13 @@ void SrsRtpNackForReceiver::check_queue_size() void SrsRtpNackForReceiver::get_nack_seqs(SrsRtcpNack& seqs, uint32_t& timeout_nacks) { + // If circuit-breaker is enabled, disable nack. + if (_srs_circuit_breaker->hybrid_high_water_level()) { + queue_.clear(); + ++_srs_pps_snack4->sugar; + return; + } + srs_utime_t now = srs_get_system_time(); srs_utime_t interval = now - pre_check_time_; @@ -294,6 +313,8 @@ void SrsRtpNackForReceiver::get_nack_seqs(SrsRtcpNack& seqs, uint32_t& timeout_n ++nack_info.req_nack_count_; nack_info.pre_req_nack_time_ = now; seqs.add_lost_sn(seq); + + ++_srs_pps_snack3->sugar; } ++iter; diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp index 1fa107b600..2deca9cd68 100644 --- a/trunk/src/app/srs_app_rtc_server.cpp +++ b/trunk/src/app/srs_app_rtc_server.cpp @@ -45,35 +45,42 @@ using namespace std; #include #include -extern SrsPps* _srs_pps_rpkts; -SrsPps* _srs_pps_rstuns = new SrsPps(); -SrsPps* _srs_pps_rrtps = new SrsPps(); -SrsPps* _srs_pps_rrtcps = new SrsPps(); -extern SrsPps* _srs_pps_addrs; -extern SrsPps* _srs_pps_fast_addrs; - -extern SrsPps* _srs_pps_spkts; -extern SrsPps* _srs_pps_sstuns; -extern SrsPps* _srs_pps_srtcps; -extern SrsPps* _srs_pps_srtps; - -extern SrsPps* _srs_pps_ids; -extern SrsPps* _srs_pps_fids; -extern SrsPps* _srs_pps_fids_level0; - -extern SrsPps* _srs_pps_pli; -extern SrsPps* _srs_pps_twcc; -extern SrsPps* _srs_pps_rr; - -extern SrsPps* _srs_pps_snack; -extern SrsPps* _srs_pps_snack2; -extern SrsPps* _srs_pps_sanack; -extern SrsPps* _srs_pps_svnack; - -extern SrsPps* _srs_pps_rnack; -extern SrsPps* _srs_pps_rnack2; -extern SrsPps* _srs_pps_rhnack; -extern SrsPps* _srs_pps_rmnack; +extern __thread SrsPps* _srs_pps_rpkts; +__thread SrsPps* _srs_pps_rstuns = NULL; +__thread SrsPps* _srs_pps_rrtps = NULL; +__thread SrsPps* _srs_pps_rrtcps = NULL; +extern __thread SrsPps* _srs_pps_addrs; +extern __thread SrsPps* _srs_pps_fast_addrs; + +extern __thread SrsPps* _srs_pps_spkts; +extern __thread SrsPps* _srs_pps_sstuns; +extern __thread SrsPps* _srs_pps_srtcps; +extern __thread SrsPps* _srs_pps_srtps; + +extern __thread SrsPps* _srs_pps_ids; +extern __thread SrsPps* _srs_pps_fids; +extern __thread SrsPps* _srs_pps_fids_level0; + +extern __thread SrsPps* _srs_pps_pli; +extern __thread SrsPps* _srs_pps_twcc; +extern __thread SrsPps* _srs_pps_rr; + +extern __thread SrsPps* _srs_pps_snack; +extern __thread SrsPps* _srs_pps_snack2; +extern __thread SrsPps* _srs_pps_snack3; +extern __thread SrsPps* _srs_pps_snack4; +extern __thread SrsPps* _srs_pps_sanack; +extern __thread SrsPps* _srs_pps_svnack; + +extern __thread SrsPps* _srs_pps_rnack; +extern __thread SrsPps* _srs_pps_rnack2; +extern __thread SrsPps* _srs_pps_rhnack; +extern __thread SrsPps* _srs_pps_rmnack; + +extern __thread SrsPps* _srs_pps_rloss; +extern __thread SrsPps* _srs_pps_sloss; +extern __thread SrsPps* _srs_pps_aloss; +extern __thread SrsPps* _srs_pps_aloss2; SrsRtcBlackhole::SrsRtcBlackhole() { @@ -132,7 +139,7 @@ void SrsRtcBlackhole::sendto(void* data, int len) srs_sendto(blackhole_stfd, data, len, (sockaddr*)blackhole_addr, sizeof(sockaddr_in), SRS_UTIME_NO_TIMEOUT); } -SrsRtcBlackhole* _srs_blackhole = new SrsRtcBlackhole(); +__thread SrsRtcBlackhole* _srs_blackhole = NULL; // @global dtls certficate for rtc module. SrsDtlsCertificate* _srs_rtc_dtls_certificate = new SrsDtlsCertificate(); @@ -284,6 +291,7 @@ srs_error_t SrsRtcServer::initialize() _srs_hybrid->timer5s()->subscribe(this); // Initialize the black hole. + // TODO: FIXME: It should be thread-local or thread-safe. if ((err = _srs_blackhole->initialize()) != srs_success) { return srs_error_wrap(err, "black hole"); } @@ -305,7 +313,8 @@ srs_error_t SrsRtcServer::initialize() rtp_cache_enabled, (int)(rtp_cache_pkt_size/1024/1024), _srs_rtp_cache->capacity()/10000, (int)(rtp_cache_payload_size/1024/1024), _srs_rtp_raw_cache->capacity()/10000, _srs_rtp_fua_cache->capacity()/10000, rtp_msg_cache_enabled, (int)(rtp_msg_cache_msg_size/1024/1024), _srs_rtp_msg_cache_objs->capacity()/10000, - (int)(rtp_msg_cache_buffer_size/1024/1024), _srs_rtp_msg_cache_buffers->capacity()/10000); + (int)(rtp_msg_cache_buffer_size/1024/1024), _srs_rtp_msg_cache_buffers->capacity()/10000 + ); return err; } @@ -364,7 +373,7 @@ srs_error_t SrsRtcServer::listen_udp() return err; } - int port = _srs_config->get_rtc_server_listen(); + int port = _srs_config->get_rtc_server_listen(_srs_hybrid->stream_index()); if (port <= 0) { return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port); } @@ -481,33 +490,11 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt) if (srs_is_dtls((uint8_t*)data, size)) { ++_srs_pps_rstuns->sugar; - return session->on_dtls(data, size); - } - return srs_error_new(ERROR_RTC_UDP, "unknown packet"); -} - -srs_error_t SrsRtcServer::listen_api() -{ - srs_error_t err = srs_success; - - // TODO: FIXME: Fetch api from hybrid manager, not from SRS. - SrsHttpServeMux* http_api_mux = _srs_hybrid->srs()->instance()->api_server(); - - if ((err = http_api_mux->handle("/rtc/v1/play/", new SrsGoApiRtcPlay(this))) != srs_success) { - return srs_error_wrap(err, "handle play"); - } - - if ((err = http_api_mux->handle("/rtc/v1/publish/", new SrsGoApiRtcPublish(this))) != srs_success) { - return srs_error_wrap(err, "handle publish"); - } + err = session->on_dtls(data, size); -#ifdef SRS_SIMULATOR - if ((err = http_api_mux->handle("/rtc/v1/nack/", new SrsGoApiRtcNACK(this))) != srs_success) { - return srs_error_wrap(err, "handle nack"); + return err; } -#endif - - return err; + return srs_error_new(ERROR_RTC_UDP, "unknown packet"); } srs_error_t SrsRtcServer::create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sdp, SrsRtcConnection** psession) @@ -580,7 +567,7 @@ srs_error_t SrsRtcServer::do_create_session(SrsRtcUserConfig* ruc, SrsSdp& local // We allows to mock the eip of server. if (!ruc->eip_.empty()) { string host; - int port = _srs_config->get_rtc_server_listen(); + int port = _srs_config->get_rtc_server_listen(_srs_hybrid->stream_index()); srs_parse_hostport(ruc->eip_, host, port); local_sdp.add_candidate(host, port, "host"); @@ -588,7 +575,7 @@ srs_error_t SrsRtcServer::do_create_session(SrsRtcUserConfig* ruc, SrsSdp& local } else { std::vector candidate_ips = get_candidate_ips(); for (int i = 0; i < (int)candidate_ips.size(); ++i) { - local_sdp.add_candidate(candidate_ips[i], _srs_config->get_rtc_server_listen(), "host"); + local_sdp.add_candidate(candidate_ips[i], _srs_config->get_rtc_server_listen(_srs_hybrid->stream_index()), "host"); } srs_trace("RTC: Use candidates %s", srs_join_vector_string(candidate_ips, ", ").c_str()); } @@ -693,9 +680,9 @@ srs_error_t SrsRtcServer::on_timer(srs_utime_t interval) } string snk_desc; - _srs_pps_snack->update(); _srs_pps_snack2->update(); _srs_pps_sanack->update(); _srs_pps_svnack->update(); - if (_srs_pps_snack->r10s() || _srs_pps_sanack->r10s() || _srs_pps_svnack->r10s() || _srs_pps_snack2->r10s()) { - snprintf(buf, sizeof(buf), ", snk=(%d,a:%d,v:%d,h:%d)", _srs_pps_snack->r10s(), _srs_pps_sanack->r10s(), _srs_pps_svnack->r10s(), _srs_pps_snack2->r10s()); + _srs_pps_snack->update(); _srs_pps_snack2->update(); _srs_pps_snack3->update(); _srs_pps_snack4->update(); _srs_pps_sanack->update(); _srs_pps_svnack->update(); + if (_srs_pps_snack->r10s() || _srs_pps_sanack->r10s() || _srs_pps_svnack->r10s() || _srs_pps_snack2->r10s() || _srs_pps_snack3->r10s() || _srs_pps_snack4->r10s()) { + snprintf(buf, sizeof(buf), ", snk=(%d,a:%d,v:%d,h:%d,h3:%d,h4:%d)", _srs_pps_snack->r10s(), _srs_pps_sanack->r10s(), _srs_pps_svnack->r10s(), _srs_pps_snack2->r10s(), _srs_pps_snack3->r10s(), _srs_pps_snack4->r10s()); snk_desc = buf; } @@ -706,10 +693,11 @@ srs_error_t SrsRtcServer::on_timer(srs_utime_t interval) rnk_desc = buf; } + // TODO: FIXME: Should move to Hybrid server stat. string loss_desc; - SrsSnmpUdpStat* s = srs_get_udp_snmp_stat(); - if (s->rcv_buf_errors_delta || s->snd_buf_errors_delta) { - snprintf(buf, sizeof(buf), ", loss=(r:%d,s:%d)", s->rcv_buf_errors_delta, s->snd_buf_errors_delta); + _srs_pps_aloss->update(); _srs_pps_aloss2->update(); + if (_srs_pps_rloss->r1s() || _srs_pps_rloss->r10s() || _srs_pps_sloss->r10s() || _srs_pps_aloss->r10s() || _srs_pps_aloss2->r10s()) { + snprintf(buf, sizeof(buf), ", loss=(r:%d/%d,s:%d,a:%d/%d)", _srs_pps_rloss->r1s(), _srs_pps_rloss->r10s(), _srs_pps_sloss->r10s(), _srs_pps_aloss->r10s(), _srs_pps_aloss2->r10s()); loss_desc = buf; } @@ -742,10 +730,6 @@ srs_error_t RtcServerAdapter::initialize() { srs_error_t err = srs_success; - if ((err = _srs_rtc_dtls_certificate->initialize()) != srs_success) { - return srs_error_wrap(err, "rtc dtls certificate initialize"); - } - if ((err = rtc->initialize()) != srs_success) { return srs_error_wrap(err, "rtc server initialize"); } @@ -761,10 +745,6 @@ srs_error_t RtcServerAdapter::run() return srs_error_wrap(err, "listen udp"); } - if ((err = rtc->listen_api()) != srs_success) { - return srs_error_wrap(err, "listen api"); - } - if ((err = _srs_rtc_manager->start()) != srs_success) { return srs_error_wrap(err, "start manager"); } @@ -776,5 +756,5 @@ void RtcServerAdapter::stop() { } -SrsResourceManager* _srs_rtc_manager = new SrsResourceManager("RTC", true); +__thread SrsResourceManager* _srs_rtc_manager = NULL; diff --git a/trunk/src/app/srs_app_rtc_server.hpp b/trunk/src/app/srs_app_rtc_server.hpp index d302ad64be..ac9c727cb2 100644 --- a/trunk/src/app/srs_app_rtc_server.hpp +++ b/trunk/src/app/srs_app_rtc_server.hpp @@ -61,7 +61,8 @@ class SrsRtcBlackhole void sendto(void* data, int len); }; -extern SrsRtcBlackhole* _srs_blackhole; +// It MUST be thread-local, because it create ST socket. +extern __thread SrsRtcBlackhole* _srs_blackhole; // The handler for RTC server to call. class ISrsRtcServerHandler @@ -128,7 +129,6 @@ class SrsRtcServer : public ISrsUdpMuxHandler, public ISrsFastTimer, public ISrs // TODO: FIXME: Support reload. srs_error_t listen_udp(); virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* skt); - srs_error_t listen_api(); public: // Peer start offering, we answer it. srs_error_t create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sdp, SrsRtcConnection** psession); @@ -144,6 +144,7 @@ class SrsRtcServer : public ISrsUdpMuxHandler, public ISrsFastTimer, public ISrs // The RTC server adapter. class RtcServerAdapter : public ISrsHybridServer { + friend class SrsHybridServer; private: SrsRtcServer* rtc; public: @@ -155,8 +156,8 @@ class RtcServerAdapter : public ISrsHybridServer virtual void stop(); }; -// Manager for RTC connections. -extern SrsResourceManager* _srs_rtc_manager; +// It SHOULD be thread-local, because used to find connection for each UDP packet. +extern __thread SrsResourceManager* _srs_rtc_manager; #endif diff --git a/trunk/src/app/srs_app_rtc_source.cpp b/trunk/src/app/srs_app_rtc_source.cpp index 8ecabee05e..e055f88ae1 100644 --- a/trunk/src/app/srs_app_rtc_source.cpp +++ b/trunk/src/app/srs_app_rtc_source.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #ifdef SRS_FFMPEG_FIT #include @@ -51,15 +53,19 @@ #include // The NACK sent by us(SFU). -SrsPps* _srs_pps_snack = new SrsPps(); -SrsPps* _srs_pps_snack2 = new SrsPps(); -SrsPps* _srs_pps_sanack = new SrsPps(); -SrsPps* _srs_pps_svnack = new SrsPps(); +__thread SrsPps* _srs_pps_snack = NULL; +__thread SrsPps* _srs_pps_snack2 = NULL; +__thread SrsPps* _srs_pps_snack3 = NULL; +__thread SrsPps* _srs_pps_snack4 = NULL; +__thread SrsPps* _srs_pps_sanack = NULL; +__thread SrsPps* _srs_pps_svnack = NULL; -SrsPps* _srs_pps_rnack = new SrsPps(); -SrsPps* _srs_pps_rnack2 = new SrsPps(); -SrsPps* _srs_pps_rhnack = new SrsPps(); -SrsPps* _srs_pps_rmnack = new SrsPps(); +__thread SrsPps* _srs_pps_rnack = NULL; +__thread SrsPps* _srs_pps_rnack2 = NULL; +__thread SrsPps* _srs_pps_rhnack = NULL; +__thread SrsPps* _srs_pps_rmnack = NULL; + +extern __thread SrsPps* _srs_pps_aloss2; // Firefox defaults as 109, Chrome is 111. const int kAudioPayloadType = 111; @@ -310,7 +316,7 @@ SrsRtcStream* SrsRtcStreamManager::fetch(SrsRequest* r) return source; } -SrsRtcStreamManager* _srs_rtc_sources = new SrsRtcStreamManager(); +__thread SrsRtcStreamManager* _srs_rtc_sources = NULL; ISrsRtcPublishStream::ISrsRtcPublishStream() { @@ -576,6 +582,12 @@ srs_error_t SrsRtcStream::on_rtp(SrsRtpPacket2* pkt) { srs_error_t err = srs_success; + // If circuit-breaker is dying, drop packet. + if (_srs_circuit_breaker->hybrid_dying_water_level()) { + _srs_pps_aloss2->sugar += (int64_t)consumers.size(); + return err; + } + for (int i = 0; i < (int)consumers.size(); i++) { SrsRtcConsumer* consumer = consumers.at(i); if ((err = consumer->enqueue(pkt->copy())) != srs_success) { diff --git a/trunk/src/app/srs_app_rtc_source.hpp b/trunk/src/app/srs_app_rtc_source.hpp index 19172304b8..8a2da97368 100644 --- a/trunk/src/app/srs_app_rtc_source.hpp +++ b/trunk/src/app/srs_app_rtc_source.hpp @@ -137,8 +137,8 @@ class SrsRtcStreamManager virtual SrsRtcStream* fetch(SrsRequest* r); }; -// Global singleton instance. -extern SrsRtcStreamManager* _srs_rtc_sources; +// It SHOULD be thread-local, because stream source is isolated by threads. +extern __thread SrsRtcStreamManager* _srs_rtc_sources; // A publish stream interface, for source to callback with. class ISrsRtcPublishStream diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index da93d1c4f8..a8b940d5d0 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -40,6 +40,7 @@ using namespace std; #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ using namespace std; #include #include #include +#include std::string srs_listener_type2string(SrsListenerType type) { @@ -83,7 +85,15 @@ std::string srs_listener_type2string(SrsListenerType type) } } -SrsListener::SrsListener(SrsServer* svr, SrsListenerType t) +ISrsTcpMuxHandler::ISrsTcpMuxHandler() +{ +} + +ISrsTcpMuxHandler::~ISrsTcpMuxHandler() +{ +} + +SrsListener::SrsListener(ISrsTcpMuxHandler* svr, SrsListenerType t) { port = 0; server = svr; @@ -99,7 +109,7 @@ SrsListenerType SrsListener::listen_type() return type; } -SrsBufferListener::SrsBufferListener(SrsServer* svr, SrsListenerType t) : SrsListener(svr, t) +SrsBufferListener::SrsBufferListener(ISrsTcpMuxHandler* svr, SrsListenerType t) : SrsListener(svr, t) { listener = NULL; } @@ -131,7 +141,7 @@ srs_error_t SrsBufferListener::listen(string i, int p) srs_error_t SrsBufferListener::on_tcp_client(srs_netfd_t stfd) { - srs_error_t err = server->accept_client(type, stfd); + srs_error_t err = server->accept_tcp_client(type, stfd); if (err != srs_success) { srs_warn("accept client failed, err is %s", srs_error_desc(err).c_str()); srs_freep(err); @@ -394,39 +404,42 @@ SrsSignalManager* SrsSignalManager::instance = NULL; SrsSignalManager::SrsSignalManager(SrsServer* s) { SrsSignalManager::instance = this; - + + pipe_ = new SrsThreadPipePair(); + server = s; - sig_pipe[0] = sig_pipe[1] = -1; trd = new SrsSTCoroutine("signal", this, _srs_context->get_id()); - signal_read_stfd = NULL; } SrsSignalManager::~SrsSignalManager() { - srs_close_stfd(signal_read_stfd); - - if (sig_pipe[0] > 0) { - ::close(sig_pipe[0]); - } - if (sig_pipe[1] > 0) { - ::close(sig_pipe[1]); - } - srs_freep(trd); + + // Note that it's optional, because the read/write pair is in the same thread. + pipe_->close_read(); + pipe_->close_write(); + + // If in the same thread, we could directly free the pipe, which will close all FDs. + srs_freep(pipe_); } srs_error_t SrsSignalManager::initialize() { - /* Create signal pipe */ - if (pipe(sig_pipe) < 0) { - return srs_error_new(ERROR_SYSTEM_CREATE_PIPE, "create pipe"); + srs_error_t err = srs_success; + + if ((err = pipe_->initialize()) != srs_success) { + return srs_error_wrap(err, "init pipe"); } - - if ((signal_read_stfd = srs_netfd_open(sig_pipe[0])) == NULL) { - return srs_error_new(ERROR_SYSTEM_CREATE_PIPE, "open pipe"); + + if ((err = pipe_->open_read()) != srs_success) { + return srs_error_wrap(err, "init read pipe"); } - - return srs_success; + + if ((err = pipe_->open_write()) != srs_success) { + return srs_error_wrap(err, "init write pipe"); + } + + return err; } srs_error_t SrsSignalManager::start() @@ -486,11 +499,12 @@ srs_error_t SrsSignalManager::cycle() } int signo; + // Read the next signal from the pipe + if ((err = pipe_->read(&signo, sizeof(int), NULL)) != srs_success) { + srs_freep(err); // Ignore any error. + } - /* Read the next signal from the pipe */ - srs_read(signal_read_stfd, &signo, sizeof(int), SRS_UTIME_NO_TIMEOUT); - - /* Process signal synchronously */ + // Process signal synchronously server->on_signal(signo); } @@ -501,13 +515,15 @@ void SrsSignalManager::sig_catcher(int signo) { int err; - /* Save errno to restore it after the write() */ + // Save errno to restore it after the write() err = errno; - - /* write() is reentrant/async-safe */ - int fd = SrsSignalManager::instance->sig_pipe[1]; - write(fd, &signo, sizeof(int)); - + + // write() is reentrant/async-safe + srs_error_t r0 = SrsSignalManager::instance->pipe_->write(&signo, sizeof(int), NULL); + if (r0 != srs_success) { + srs_freep(r0); // Ignore any error. + } + errno = err; } @@ -671,7 +687,6 @@ SrsServer::SrsServer() signal_gmc_stop = false; signal_fast_quit = false; signal_gracefully_quit = false; - pid_fd = -1; signal_manager = new SrsSignalManager(this); conn_manager = new SrsResourceManager("TCP", true); @@ -682,7 +697,6 @@ SrsServer::SrsServer() // donot new object in constructor, // for some global instance is not ready now, // new these objects in initialize instead. - http_api_mux = new SrsHttpServeMux(); http_server = new SrsHttpServer(this); http_heartbeat = new SrsHttpHeartbeat(); ingester = new SrsIngester(); @@ -703,17 +717,11 @@ void SrsServer::destroy() srs_freep(timer_); dispose(); - - srs_freep(http_api_mux); + srs_freep(http_server); srs_freep(http_heartbeat); srs_freep(ingester); - if (pid_fd > 0) { - ::close(pid_fd); - pid_fd = -1; - } - srs_freep(signal_manager); srs_freep(conn_manager); @@ -798,16 +806,13 @@ srs_error_t SrsServer::initialize(ISrsServerCycle* ch) // instead, subscribe handler in initialize method. srs_assert(_srs_config); _srs_config->subscribe(this); - + + // TODO: FIXME: It should be thread-local or thread-safe. handler = ch; if(handler && (err = handler->initialize()) != srs_success){ return srs_error_wrap(err, "handler initialize"); } - if ((err = http_api_mux->initialize()) != srs_success) { - return srs_error_wrap(err, "http api initialize"); - } - if ((err = http_server->initialize()) != srs_success) { return srs_error_wrap(err, "http server initialize"); } @@ -836,69 +841,6 @@ srs_error_t SrsServer::initialize_signal() return signal_manager->initialize(); } -srs_error_t SrsServer::acquire_pid_file() -{ - // when srs in dolphin mode, no need the pid file. - if (_srs_config->is_dolphin()) { - return srs_success; - } - - std::string pid_file = _srs_config->get_pid_file(); - - // -rw-r--r-- - // 644 - int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - - int fd; - // open pid file - if ((fd = ::open(pid_file.c_str(), O_WRONLY | O_CREAT, mode)) == -1) { - return srs_error_new(ERROR_SYSTEM_PID_ACQUIRE, "open pid file=%s", pid_file.c_str()); - } - - // require write lock - struct flock lock; - - lock.l_type = F_WRLCK; // F_RDLCK, F_WRLCK, F_UNLCK - lock.l_start = 0; // type offset, relative to l_whence - lock.l_whence = SEEK_SET; // SEEK_SET, SEEK_CUR, SEEK_END - lock.l_len = 0; - - if (fcntl(fd, F_SETLK, &lock) == -1) { - if(errno == EACCES || errno == EAGAIN) { - ::close(fd); - srs_error("srs is already running!"); - return srs_error_new(ERROR_SYSTEM_PID_ALREADY_RUNNING, "srs is already running"); - } - return srs_error_new(ERROR_SYSTEM_PID_LOCK, "access to pid=%s", pid_file.c_str()); - } - - // truncate file - if (ftruncate(fd, 0) != 0) { - return srs_error_new(ERROR_SYSTEM_PID_TRUNCATE_FILE, "truncate pid file=%s", pid_file.c_str()); - } - - // write the pid - string pid = srs_int2str(getpid()); - if (write(fd, pid.c_str(), pid.length()) != (int)pid.length()) { - return srs_error_new(ERROR_SYSTEM_PID_WRITE_FILE, "write pid=%s to file=%s", pid.c_str(), pid_file.c_str()); - } - - // auto close when fork child process. - int val; - if ((val = fcntl(fd, F_GETFD, 0)) < 0) { - return srs_error_new(ERROR_SYSTEM_PID_GET_FILE_INFO, "fcntl fd=%d", fd); - } - val |= FD_CLOEXEC; - if (fcntl(fd, F_SETFD, val) < 0) { - return srs_error_new(ERROR_SYSTEM_PID_SET_FILE_INFO, "lock file=%s fd=%d", pid_file.c_str(), fd); - } - - srs_trace("write pid=%s to %s success!", pid.c_str(), pid_file.c_str()); - pid_fd = fd; - - return srs_success; -} - srs_error_t SrsServer::listen() { srs_error_t err = srs_success; @@ -907,14 +849,6 @@ srs_error_t SrsServer::listen() return srs_error_wrap(err, "rtmp listen"); } - if ((err = listen_http_api()) != srs_success) { - return srs_error_wrap(err, "http api listen"); - } - - if ((err = listen_https_api()) != srs_success) { - return srs_error_wrap(err, "https api listen"); - } - if ((err = listen_http_stream()) != srs_success) { return srs_error_wrap(err, "http stream listen"); } @@ -945,107 +879,10 @@ srs_error_t SrsServer::register_signal() return err; } -srs_error_t SrsServer::http_handle() -{ - srs_error_t err = srs_success; - - if ((err = http_api_mux->handle("/", new SrsGoApiRoot())) != srs_success) { - return srs_error_wrap(err, "handle /"); - } - if ((err = http_api_mux->handle("/api/", new SrsGoApiApi())) != srs_success) { - return srs_error_wrap(err, "handle api"); - } - if ((err = http_api_mux->handle("/api/v1/", new SrsGoApiV1())) != srs_success) { - return srs_error_wrap(err, "handle v1"); - } - if ((err = http_api_mux->handle("/api/v1/versions", new SrsGoApiVersion())) != srs_success) { - return srs_error_wrap(err, "handle versions"); - } - if ((err = http_api_mux->handle("/api/v1/summaries", new SrsGoApiSummaries())) != srs_success) { - return srs_error_wrap(err, "handle summaries"); - } - if ((err = http_api_mux->handle("/api/v1/rusages", new SrsGoApiRusages())) != srs_success) { - return srs_error_wrap(err, "handle rusages"); - } - if ((err = http_api_mux->handle("/api/v1/self_proc_stats", new SrsGoApiSelfProcStats())) != srs_success) { - return srs_error_wrap(err, "handle self proc stats"); - } - if ((err = http_api_mux->handle("/api/v1/system_proc_stats", new SrsGoApiSystemProcStats())) != srs_success) { - return srs_error_wrap(err, "handle system proc stats"); - } - if ((err = http_api_mux->handle("/api/v1/meminfos", new SrsGoApiMemInfos())) != srs_success) { - return srs_error_wrap(err, "handle meminfos"); - } - if ((err = http_api_mux->handle("/api/v1/authors", new SrsGoApiAuthors())) != srs_success) { - return srs_error_wrap(err, "handle authors"); - } - if ((err = http_api_mux->handle("/api/v1/features", new SrsGoApiFeatures())) != srs_success) { - return srs_error_wrap(err, "handle features"); - } - if ((err = http_api_mux->handle("/api/v1/vhosts/", new SrsGoApiVhosts())) != srs_success) { - return srs_error_wrap(err, "handle vhosts"); - } - if ((err = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != srs_success) { - return srs_error_wrap(err, "handle streams"); - } - if ((err = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != srs_success) { - return srs_error_wrap(err, "handle clients"); - } - if ((err = http_api_mux->handle("/api/v1/raw", new SrsGoApiRaw(this))) != srs_success) { - return srs_error_wrap(err, "handle raw"); - } - if ((err = http_api_mux->handle("/api/v1/clusters", new SrsGoApiClusters())) != srs_success) { - return srs_error_wrap(err, "handle clusters"); - } - if ((err = http_api_mux->handle("/api/v1/perf", new SrsGoApiPerf())) != srs_success) { - return srs_error_wrap(err, "handle perf"); - } -#ifdef SRS_GB28181 - if ((err = http_api_mux->handle("/api/v1/gb28181", new SrsGoApiGb28181())) != srs_success) { - return srs_error_wrap(err, "handle raw"); - } -#endif - - // test the request info. - if ((err = http_api_mux->handle("/api/v1/tests/requests", new SrsGoApiRequests())) != srs_success) { - return srs_error_wrap(err, "handle tests requests"); - } - // test the error code response. - if ((err = http_api_mux->handle("/api/v1/tests/errors", new SrsGoApiError())) != srs_success) { - return srs_error_wrap(err, "handle tests errors"); - } - // test the redirect mechenism. - if ((err = http_api_mux->handle("/api/v1/tests/redirects", new SrsHttpRedirectHandler("/api/v1/tests/errors", SRS_CONSTS_HTTP_MovedPermanently))) != srs_success) { - return srs_error_wrap(err, "handle tests redirects"); - } - // test the http vhost. - if ((err = http_api_mux->handle("error.srs.com/api/v1/tests/errors", new SrsGoApiError())) != srs_success) { - return srs_error_wrap(err, "handle tests errors for error.srs.com"); - } - -#ifdef SRS_GPERF - // The test api for get tcmalloc stats. - // @see Memory Introspection in https://gperftools.github.io/gperftools/tcmalloc.html - if ((err = http_api_mux->handle("/api/v1/tcmalloc", new SrsGoApiTcmalloc())) != srs_success) { - return srs_error_wrap(err, "handle tests errors"); - } -#endif - - // TODO: FIXME: for console. - // TODO: FIXME: support reload. - std::string dir = _srs_config->get_http_stream_dir() + "/console"; - if ((err = http_api_mux->handle("/console/", new SrsHttpFileServer(dir))) != srs_success) { - return srs_error_wrap(err, "handle console at %s", dir.c_str()); - } - srs_trace("http: api mount /console to %s", dir.c_str()); - - return err; -} - srs_error_t SrsServer::ingest() { srs_error_t err = srs_success; - + if ((err = ingester->start()) != srs_success) { return srs_error_wrap(err, "ingest start"); } @@ -1076,12 +913,14 @@ srs_error_t SrsServer::cycle() { srs_error_t err = srs_success; + // TODO: FIXME: It should be thread-local or thread-safe. // Start the inotify auto reload by watching config file. SrsInotifyWorker inotify(this); if ((err = inotify.start()) != srs_success) { return srs_error_wrap(err, "start inotify"); } + // TODO: FIXME: It should be thread-local or thread-safe. // Do server main cycle. err = do_cycle(); @@ -1112,11 +951,12 @@ srs_error_t SrsServer::cycle() } srs_trace("srs terminated"); - + // for valgrind to detect. srs_freep(_srs_config); srs_freep(_srs_log); + // TODO: FIXME: Should return to exit the thread, and quit by thread pool manager. exit(0); return err; @@ -1132,7 +972,7 @@ void SrsServer::on_signal(int signo) #ifndef SRS_GPERF_MC if (signo == SRS_SIGNAL_REOPEN_LOG) { - _srs_log->reopen(); + _srs_async_log->reopen(); if (handler) { handler->on_logrotate(); @@ -1280,10 +1120,6 @@ srs_error_t SrsServer::setup_ticks() if ((err = timer_->tick(8, 3 * SRS_UTIME_SECONDS)) != srs_success) { return srs_error_wrap(err, "tick"); } - - if ((err = timer_->tick(10, 9 * SRS_UTIME_SECONDS)) != srs_success) { - return srs_error_wrap(err, "tick"); - } } if (_srs_config->get_heartbeat_enabled()) { @@ -1305,14 +1141,16 @@ srs_error_t SrsServer::notify(int event, srs_utime_t interval, srs_utime_t tick) switch (event) { case 2: srs_update_system_rusage(); break; - case 3: srs_update_proc_stat(); break; + case 3: + srs_update_system_proc_stat(); + srs_update_self_proc_stat(); + break; case 4: srs_update_disk_stat(); break; case 5: srs_update_meminfo(); break; case 6: srs_update_platform_info(); break; case 7: srs_update_network_devices(); break; case 8: resample_kbps(); break; case 9: http_heartbeat->heartbeat(); break; - case 10: srs_update_udp_snmp_statistic(); break; } return err; @@ -1343,52 +1181,6 @@ srs_error_t SrsServer::listen_rtmp() return err; } -srs_error_t SrsServer::listen_http_api() -{ - srs_error_t err = srs_success; - - close_listeners(SrsListenerHttpApi); - if (_srs_config->get_http_api_enabled()) { - SrsListener* listener = new SrsBufferListener(this, SrsListenerHttpApi); - listeners.push_back(listener); - - std::string ep = _srs_config->get_http_api_listen(); - - std::string ip; - int port; - srs_parse_endpoint(ep, ip, port); - - if ((err = listener->listen(ip, port)) != srs_success) { - return srs_error_wrap(err, "http api listen %s:%d", ip.c_str(), port); - } - } - - return err; -} - -srs_error_t SrsServer::listen_https_api() -{ - srs_error_t err = srs_success; - - close_listeners(SrsListenerHttpsApi); - if (_srs_config->get_https_api_enabled()) { - SrsListener* listener = new SrsBufferListener(this, SrsListenerHttpsApi); - listeners.push_back(listener); - - std::string ep = _srs_config->get_https_api_listen(); - - std::string ip; - int port; - srs_parse_endpoint(ep, ip, port); - - if ((err = listener->listen(ip, port)) != srs_success) { - return srs_error_wrap(err, "https api listen %s:%d", ip.c_str(), port); - } - } - - return err; -} - srs_error_t SrsServer::listen_http_stream() { srs_error_t err = srs_success; @@ -1570,7 +1362,7 @@ void SrsServer::resample_kbps() srs_update_rtmp_server((int)conn_manager->size(), kbps); } -srs_error_t SrsServer::accept_client(SrsListenerType type, srs_netfd_t stfd) +srs_error_t SrsServer::accept_tcp_client(SrsListenerType type, srs_netfd_t stfd) { srs_error_t err = srs_success; @@ -1595,11 +1387,6 @@ srs_error_t SrsServer::accept_client(SrsListenerType type, srs_netfd_t stfd) return err; } -SrsHttpServeMux* SrsServer::api_server() -{ - return http_api_mux; -} - srs_error_t SrsServer::fd_to_resource(SrsListenerType type, srs_netfd_t stfd, ISrsStartableConneciton** pr) { srs_error_t err = srs_success; @@ -1644,10 +1431,6 @@ srs_error_t SrsServer::fd_to_resource(SrsListenerType type, srs_netfd_t stfd, IS if (type == SrsListenerRtmpStream) { *pr = new SrsRtmpConn(this, stfd, ip, port); - } else if (type == SrsListenerHttpApi) { - *pr = new SrsHttpApi(false, this, stfd, http_api_mux, ip, port); - } else if (type == SrsListenerHttpsApi) { - *pr = new SrsHttpApi(true, this, stfd, http_api_mux, ip, port); } else if (type == SrsListenerHttpStream) { *pr = new SrsResponseOnlyHttpConn(false, this, stfd, http_server, ip, port); } else if (type == SrsListenerHttpsStream) { @@ -1687,16 +1470,7 @@ srs_error_t SrsServer::on_reload_listen() srs_error_t SrsServer::on_reload_pid() { srs_error_t err = srs_success; - - if (pid_fd > 0) { - ::close(pid_fd); - pid_fd = -1; - } - - if ((err = acquire_pid_file()) != srs_success) { - return srs_error_wrap(err, "reload pid"); - } - + // TODO: FIXME: Do not support reload pid. return err; } @@ -1731,15 +1505,7 @@ srs_error_t SrsServer::on_reload_vhost_removed(std::string /*vhost*/) srs_error_t SrsServer::on_reload_http_api_enabled() { srs_error_t err = srs_success; - - if ((err = listen_http_api()) != srs_success) { - return srs_error_wrap(err, "reload http_api"); - } - - if ((err = listen_https_api()) != srs_success) { - return srs_error_wrap(err, "reload https_api"); - } - + // TODO: FIXME: Remove support for reloading HTTP API. return err; } @@ -1838,14 +1604,12 @@ srs_error_t SrsServerAdapter::run() return srs_error_wrap(err, "server initialize"); } + // TODO: FIXME: It should be thread-local or thread-safe. if ((err = srs->initialize_st()) != srs_success) { return srs_error_wrap(err, "initialize st"); } - if ((err = srs->acquire_pid_file()) != srs_success) { - return srs_error_wrap(err, "acquire pid file"); - } - + // TODO: FIXME: It should be thread-local or thread-safe. if ((err = srs->initialize_signal()) != srs_success) { return srs_error_wrap(err, "initialize signal"); } @@ -1854,14 +1618,12 @@ srs_error_t SrsServerAdapter::run() return srs_error_wrap(err, "listen"); } + // TODO: FIXME: It should be thread-local or thread-safe. if ((err = srs->register_signal()) != srs_success) { return srs_error_wrap(err, "register signal"); } - if ((err = srs->http_handle()) != srs_success) { - return srs_error_wrap(err, "http handle"); - } - + // TODO: FIXME: It should be thread-local or thread-safe. if ((err = srs->ingest()) != srs_success) { return srs_error_wrap(err, "ingest"); } @@ -1882,3 +1644,372 @@ SrsServer* SrsServerAdapter::instance() return srs; } +SrsApiServer::SrsApiServer() +{ + http_api_mux_ = new SrsHttpServeMux(); + http_ = new SrsBufferListener(this, SrsListenerHttpApi); + https_ = new SrsBufferListener(this, SrsListenerHttpApi); + conn_manager_ = new SrsResourceManager("api"); + lock_ = srs_mutex_new(); +} + +SrsApiServer::~SrsApiServer() +{ + srs_freep(http_api_mux_); + srs_freep(http_); + srs_freep(https_); + srs_freep(conn_manager_); + srs_mutex_destroy(lock_); +} + +srs_error_t SrsApiServer::initialize() +{ + srs_error_t err = srs_success; + + if ((err = http_api_mux_->initialize()) != srs_success) { + return srs_error_wrap(err, "http api initialize"); + } + + if ((err = http_handle()) != srs_success) { + return srs_error_wrap(err, "http handle"); + } + + if ((err = listen_api()) != srs_success) { + return srs_error_wrap(err, "listen api"); + } + + if ((err = listen_http_api()) != srs_success) { + return srs_error_wrap(err, "http api listen"); + } + + if ((err = listen_https_api()) != srs_success) { + return srs_error_wrap(err, "https api listen"); + } + + if ((err = conn_manager_->start()) != srs_success) { + return srs_error_wrap(err, "connection manager"); + } + + return err; +} + +srs_error_t SrsApiServer::listen_http_api() +{ + srs_error_t err = srs_success; + + if (!_srs_config->get_http_api_enabled()) { + return err; + } + + std::string ep = _srs_config->get_http_api_listen(); + + std::string ip; + int port; + srs_parse_endpoint(ep, ip, port); + + if ((err = http_->listen(ip, port)) != srs_success) { + return srs_error_wrap(err, "http api listen %s:%d", ip.c_str(), port); + } + + return err; +} + +srs_error_t SrsApiServer::listen_https_api() +{ + srs_error_t err = srs_success; + + if (!_srs_config->get_https_api_enabled()) { + return err; + } + + std::string ep = _srs_config->get_https_api_listen(); + + std::string ip; + int port; + srs_parse_endpoint(ep, ip, port); + + if ((err = https_->listen(ip, port)) != srs_success) { + return srs_error_wrap(err, "https api listen %s:%d", ip.c_str(), port); + } + + return err; +} + +srs_error_t SrsApiServer::accept_tcp_client(SrsListenerType type, srs_netfd_t stfd) +{ + srs_error_t err = srs_success; + + ISrsStartableConneciton* conn = NULL; + + if ((err = fd_to_resource(type, stfd, &conn)) != srs_success) { + if (srs_error_code(err) == ERROR_SOCKET_GET_PEER_IP && _srs_config->empty_ip_ok()) { + srs_close_stfd(stfd); srs_error_reset(err); + return srs_success; + } + return srs_error_wrap(err, "fd to resource"); + } + srs_assert(conn); + + // directly enqueue, the cycle thread will remove the client. + conn_manager_->add(conn); + + if ((err = conn->start()) != srs_success) { + return srs_error_wrap(err, "start conn coroutine"); + } + + return err; +} + +srs_error_t SrsApiServer::fd_to_resource(SrsListenerType type, srs_netfd_t stfd, ISrsStartableConneciton** pr) +{ + srs_error_t err = srs_success; + + int fd = srs_netfd_fileno(stfd); + string ip = srs_get_peer_ip(fd); + int port = srs_get_peer_port(fd); + + // for some keep alive application, for example, the keepalived, + // will send some tcp packet which we cann't got the ip, + // we just ignore it. + if (ip.empty()) { + return srs_error_new(ERROR_SOCKET_GET_PEER_IP, "ignore empty ip, fd=%d", fd); + } + + // avoid fd leak when fork. + // @see https://github.com/ossrs/srs/issues/518 + if (true) { + int val; + if ((val = fcntl(fd, F_GETFD, 0)) < 0) { + return srs_error_new(ERROR_SYSTEM_PID_GET_FILE_INFO, "fnctl F_GETFD error! fd=%d", fd); + } + val |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, val) < 0) { + return srs_error_new(ERROR_SYSTEM_PID_SET_FILE_INFO, "fcntl F_SETFD error! fd=%d", fd); + } + } + + // The context id may change during creating the bellow objects. + SrsContextRestore(_srs_context->get_id()); + + if (type == SrsListenerHttpApi) { + *pr = new SrsHttpApi(false, this, stfd, http_api_mux_, ip, port); + } else if (type == SrsListenerHttpsApi) { + *pr = new SrsHttpApi(true, this, stfd, http_api_mux_, ip, port); + } else { + srs_warn("close for no service handler. fd=%d, ip=%s:%d", fd, ip.c_str(), port); + srs_close_stfd(stfd); + return err; + } + + return err; +} + +void SrsApiServer::remove(ISrsResource* c) +{ + conn_manager_->remove(c); +} + +srs_error_t SrsApiServer::http_handle() +{ + srs_error_t err = srs_success; + + if ((err = http_api_mux_->handle("/", new SrsGoApiRoot())) != srs_success) { + return srs_error_wrap(err, "handle /"); + } + if ((err = http_api_mux_->handle("/api/", new SrsGoApiApi())) != srs_success) { + return srs_error_wrap(err, "handle api"); + } + if ((err = http_api_mux_->handle("/api/v1/", new SrsGoApiV1())) != srs_success) { + return srs_error_wrap(err, "handle v1"); + } + if ((err = http_api_mux_->handle("/api/v1/versions", new SrsGoApiVersion())) != srs_success) { + return srs_error_wrap(err, "handle versions"); + } + if ((err = http_api_mux_->handle("/api/v1/summaries", new SrsGoApiSummaries())) != srs_success) { + return srs_error_wrap(err, "handle summaries"); + } + if ((err = http_api_mux_->handle("/api/v1/rusages", new SrsGoApiRusages())) != srs_success) { + return srs_error_wrap(err, "handle rusages"); + } + if ((err = http_api_mux_->handle("/api/v1/self_proc_stats", new SrsGoApiSelfProcStats())) != srs_success) { + return srs_error_wrap(err, "handle self proc stats"); + } + if ((err = http_api_mux_->handle("/api/v1/system_proc_stats", new SrsGoApiSystemProcStats())) != srs_success) { + return srs_error_wrap(err, "handle system proc stats"); + } + if ((err = http_api_mux_->handle("/api/v1/meminfos", new SrsGoApiMemInfos())) != srs_success) { + return srs_error_wrap(err, "handle meminfos"); + } + if ((err = http_api_mux_->handle("/api/v1/authors", new SrsGoApiAuthors())) != srs_success) { + return srs_error_wrap(err, "handle authors"); + } + if ((err = http_api_mux_->handle("/api/v1/features", new SrsGoApiFeatures())) != srs_success) { + return srs_error_wrap(err, "handle features"); + } + if ((err = http_api_mux_->handle("/api/v1/vhosts/", new SrsGoApiVhosts())) != srs_success) { + return srs_error_wrap(err, "handle vhosts"); + } + if ((err = http_api_mux_->handle("/api/v1/streams/", new SrsGoApiStreams())) != srs_success) { + return srs_error_wrap(err, "handle streams"); + } + if ((err = http_api_mux_->handle("/api/v1/clients/", new SrsGoApiClients())) != srs_success) { + return srs_error_wrap(err, "handle clients"); + } + // TODO: FIXME: Implements it. + //if ((err = http_api_mux_->handle("/api/v1/raw", new SrsGoApiRaw(this))) != srs_success) { + // return srs_error_wrap(err, "handle raw"); + //} + if ((err = http_api_mux_->handle("/api/v1/clusters", new SrsGoApiClusters())) != srs_success) { + return srs_error_wrap(err, "handle clusters"); + } + if ((err = http_api_mux_->handle("/api/v1/perf", new SrsGoApiPerf())) != srs_success) { + return srs_error_wrap(err, "handle perf"); + } +#ifdef SRS_GB28181 + if ((err = http_api_mux_->handle("/api/v1/gb28181", new SrsGoApiGb28181())) != srs_success) { + return srs_error_wrap(err, "handle raw"); + } +#endif + + // test the request info. + if ((err = http_api_mux_->handle("/api/v1/tests/requests", new SrsGoApiRequests())) != srs_success) { + return srs_error_wrap(err, "handle tests requests"); + } + // test the error code response. + if ((err = http_api_mux_->handle("/api/v1/tests/errors", new SrsGoApiError())) != srs_success) { + return srs_error_wrap(err, "handle tests errors"); + } + // test the redirect mechenism. + if ((err = http_api_mux_->handle("/api/v1/tests/redirects", new SrsHttpRedirectHandler("/api/v1/tests/errors", SRS_CONSTS_HTTP_MovedPermanently))) != srs_success) { + return srs_error_wrap(err, "handle tests redirects"); + } + // test the http vhost. + if ((err = http_api_mux_->handle("error.srs.com/api/v1/tests/errors", new SrsGoApiError())) != srs_success) { + return srs_error_wrap(err, "handle tests errors for error.srs.com"); + } + +#ifdef SRS_GPERF + // The test api for get tcmalloc stats. + // @see Memory Introspection in https://gperftools.github.io/gperftools/tcmalloc.html + if ((err = http_api_mux_->handle("/api/v1/tcmalloc", new SrsGoApiTcmalloc())) != srs_success) { + return srs_error_wrap(err, "handle tests errors"); + } +#endif + + // TODO: FIXME: for console. + // TODO: FIXME: support reload. + std::string dir = _srs_config->get_http_stream_dir() + "/console"; + if ((err = http_api_mux_->handle("/console/", new SrsHttpFileServer(dir))) != srs_success) { + return srs_error_wrap(err, "handle console at %s", dir.c_str()); + } + srs_trace("http: api mount /console to %s", dir.c_str()); + + return err; +} + +srs_error_t SrsApiServer::listen_api() +{ + srs_error_t err = srs_success; + + if ((err = http_api_mux_->handle("/rtc/v1/play/", new SrsGoApiRtcPlay(this))) != srs_success) { + return srs_error_wrap(err, "handle play"); + } + + if ((err = http_api_mux_->handle("/rtc/v1/publish/", new SrsGoApiRtcPublish(this))) != srs_success) { + return srs_error_wrap(err, "handle publish"); + } + +#ifdef SRS_SIMULATOR + // TODO: FIXME: Implements it. + //if ((err = http_api_mux_->handle("/rtc/v1/nack/", new SrsGoApiRtcNACK(this))) != srs_success) { + // return srs_error_wrap(err, "handle nack"); + //} +#endif + + return err; +} + +srs_error_t SrsApiServer::create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sdp, SrsRtcConnection** psession) { + srs_error_t err = srs_success; + + SrsRequest* req = ruc->req_; + + // TODO: FIXME: Should update the hybrids for RTMP streams. + // Serve all connections of a stream, which identified by url, by the same hybrid thread. + string url = req->get_stream_url(); + SrsThreadEntry* hybrid = NULL; + if (true) { + map::iterator it = hybrids_.find(url); + if (it == hybrids_.end()) { + static int index = 0; + vector hybrids = _srs_thread_pool->hybrids(); + hybrids_[url] = hybrid = hybrids[(index++) % (int)hybrids.size()]; + } else { + hybrid = it->second; + } + } + + // Allocate slot to communicate with hybrid thread. + SrsThreadEntry* self = _srs_thread_pool->self(); + srs_assert(self && hybrid); + + SrsThreadPipeChannel* channel = NULL; + if (true) { + map::iterator it = self->channels_.find(hybrid->trd); + if (it == self->channels_.end()) { + self->channels_[hybrid->trd] = channel = hybrid->slot_->allocate(); + } else { + channel = it->second; + } + } + srs_assert(channel); + + // We're initiator, write to initiator, read from responder. + if ((err = channel->initiator()->open_write()) != srs_success) { + return srs_error_wrap(err, "open write"); + } + if ((err = channel->responder()->open_read()) != srs_success) { + return srs_error_wrap(err, "open read"); + } + + SrsThreadMessageRtcCreateSession s; + s.ruc = ruc; + s.local_sdp = &local_sdp; + s.session = NULL; + + SrsThreadMessage m; + m.id = (uint64_t)SrsThreadMessageIDRtcCreateSession; + m.ptr = (uint64_t)&s; + + if (true) { + // Process api request one by one. + // TODO: FIXME: The lock too big? Write log and error? + SrsLocker(lock_); + + // We're initiator, write to initiator, read from responder. + // TODO: FIXME: Write important logs, and error response, and timeout? + if ((err = channel->initiator()->write(&m, sizeof(m), NULL)) != srs_success) { + return srs_error_wrap(err, "write"); + } + + // TODO: FIXME: Write important logs, and error response, and timeout? + // TODO: FIXME: If play a invalid stream, api will be blocked. + if ((err = channel->responder()->read(&m, sizeof(m), NULL)) != srs_success) { + return srs_error_wrap(err, "read"); + } + } + + // Covert to output params. + // TODO: FIMXE: Should never return it, for it's not thread-safe. + *psession = s.session; + + // TODO: FIXME: Shoule return detail error by channel. + if (!s.session) { + return srs_error_new(ERROR_PIPE_READ, "no session"); + } + + return err; +} + +SrsApiServer* _srs_api = new SrsApiServer(); + diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 4c3986a4e7..40426b936b 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -40,6 +41,8 @@ #include #include #include +#include +#include class SrsServer; class SrsHttpServeMux; @@ -56,7 +59,7 @@ class SrsAppCasterFlv; class SrsRtspCaster; class SrsResourceManager; class SrsGb28181Caster; - +class SrsThreadPipePair; // The listener type for server to identify the connection, // that is, use different type to process the connection. @@ -84,6 +87,17 @@ enum SrsListenerType SrsListenerHttpsStream = 9, }; +// To mux the tcp handler. +class ISrsTcpMuxHandler +{ +public: + ISrsTcpMuxHandler(); + virtual ~ISrsTcpMuxHandler(); +public: + // Accept the TCP client, which is identified by type. + virtual srs_error_t accept_tcp_client(SrsListenerType type, srs_netfd_t stfd) = 0; +}; + // A common tcp listener, for RTMP/HTTP server. class SrsListener { @@ -92,9 +106,9 @@ class SrsListener protected: std::string ip; int port; - SrsServer* server; + ISrsTcpMuxHandler* server; public: - SrsListener(SrsServer* svr, SrsListenerType t); + SrsListener(ISrsTcpMuxHandler* svr, SrsListenerType t); virtual ~SrsListener(); public: virtual SrsListenerType listen_type(); @@ -107,7 +121,7 @@ class SrsBufferListener : virtual public SrsListener, virtual public ISrsTcpHand private: SrsTcpListener* listener; public: - SrsBufferListener(SrsServer* server, SrsListenerType type); + SrsBufferListener(ISrsTcpMuxHandler* server, SrsListenerType type); virtual ~SrsBufferListener(); public: virtual srs_error_t listen(std::string ip, int port); @@ -203,8 +217,7 @@ class SrsSignalManager : public ISrsCoroutineHandler private: // Per-process pipe which is used as a signal queue. // Up to PIPE_BUF/sizeof(int) signals can be queued up. - int sig_pipe[2]; - srs_netfd_t signal_read_stfd; + SrsThreadPipePair* pipe_; private: SrsServer* server; SrsCoroutine* trd; @@ -264,11 +277,9 @@ class ISrsServerCycle // SRS RTMP server, initialize and listen, start connection service thread, destroy client. class SrsServer : virtual public ISrsReloadHandler, virtual public ISrsSourceHandler , virtual public ISrsResourceManager, virtual public ISrsCoroutineHandler - , virtual public ISrsHourGlass + , virtual public ISrsHourGlass, public ISrsTcpMuxHandler { private: - // TODO: FIXME: Extract an HttpApiServer. - SrsHttpServeMux* http_api_mux; SrsHttpServer* http_server; SrsHttpHeartbeat* http_heartbeat; SrsIngester* ingester; @@ -276,11 +287,6 @@ class SrsServer : virtual public ISrsReloadHandler, virtual public ISrsSourceHan SrsCoroutine* trd_; SrsHourGlass* timer_; private: - // The pid file fd, lock the file write when server is running. - // @remark the init.d script should cleanup the pid file, when stop service, - // for the server never delete the file; when system startup, the pid in pid file - // maybe valid but the process is not SRS, the init.d script will never start server. - int pid_fd; // All listners, listener manager. std::vector listeners; // Signal manager which convert gignal to io message. @@ -316,10 +322,8 @@ class SrsServer : virtual public ISrsReloadHandler, virtual public ISrsSourceHan virtual srs_error_t initialize(ISrsServerCycle* ch); virtual srs_error_t initialize_st(); virtual srs_error_t initialize_signal(); - virtual srs_error_t acquire_pid_file(); virtual srs_error_t listen(); virtual srs_error_t register_signal(); - virtual srs_error_t http_handle(); virtual srs_error_t ingest(); virtual srs_error_t start(); // interface ISrsCoroutineHandler @@ -353,8 +357,6 @@ class SrsServer : virtual public ISrsReloadHandler, virtual public ISrsSourceHan private: // listen at specified protocol. virtual srs_error_t listen_rtmp(); - virtual srs_error_t listen_http_api(); - virtual srs_error_t listen_https_api(); virtual srs_error_t listen_http_stream(); virtual srs_error_t listen_https_stream(); virtual srs_error_t listen_stream_caster(); @@ -366,19 +368,11 @@ class SrsServer : virtual public ISrsReloadHandler, virtual public ISrsSourceHan virtual void close_listeners(SrsListenerType type); // Resample the server kbs. virtual void resample_kbps(); -// For internal only -public: - // When listener got a fd, notice server to accept it. - // @param type, the client type, used to create concrete connection, - // for instance RTMP connection to serve client. - // @param stfd, the client fd in st boxed, the underlayer fd. - virtual srs_error_t accept_client(SrsListenerType type, srs_netfd_t stfd); - // TODO: FIXME: Fetch from hybrid server manager. - virtual SrsHttpServeMux* api_server(); private: + virtual srs_error_t accept_tcp_client(SrsListenerType type, srs_netfd_t stfd); virtual srs_error_t fd_to_resource(SrsListenerType type, srs_netfd_t stfd, ISrsStartableConneciton** pr); // Interface ISrsResourceManager -public: +private: // A callback for connection to remove itself. // When connection thread cycle terminated, callback this to delete connection. // @see SrsTcpConnection.on_thread_stop(). @@ -416,5 +410,53 @@ class SrsServerAdapter : public ISrsHybridServer virtual SrsServer* instance(); }; +// The HTTP API server. +class SrsApiServer : public ISrsTcpMuxHandler, public ISrsResourceManager, public ISrsRtcServer +{ +private: + SrsBufferListener* http_; + SrsBufferListener* https_; + SrsHttpServeMux* http_api_mux_; + SrsResourceManager* conn_manager_; +private: + // Key is stream url, value is hybrid thread entry. + std::map hybrids_; +private: + // To process api request one by one. + srs_mutex_t lock_; +public: + SrsApiServer(); + virtual ~SrsApiServer(); +public: + virtual srs_error_t initialize(); +private: + virtual srs_error_t listen_http_api(); + virtual srs_error_t listen_https_api(); +private: + virtual srs_error_t accept_tcp_client(SrsListenerType type, srs_netfd_t stfd); + virtual srs_error_t fd_to_resource(SrsListenerType type, srs_netfd_t stfd, ISrsStartableConneciton** pr); + virtual void remove(ISrsResource* c); +private: + virtual srs_error_t http_handle(); + srs_error_t listen_api(); +private: + virtual srs_error_t create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sdp, SrsRtcConnection** psession); +}; + +// It's only used in master/srs thread. +extern SrsApiServer* _srs_api; + +// The RTC create session information. +struct SrsThreadMessageRtcCreateSession +{ + // Input. + SrsRtcUserConfig* ruc; + + // Output. + SrsSdp* local_sdp; + // TODO: FIXME: It's not thread-safe. + SrsRtcConnection* session; +}; + #endif diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index ce09e66143..66811507af 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -1684,7 +1684,7 @@ srs_error_t SrsMetaCache::update_vsh(SrsSharedPtrMessage* msg) return vformat->on_video(msg); } -SrsSourceManager* _srs_sources = new SrsSourceManager(); +__thread SrsSourceManager* _srs_sources = NULL; SrsSourceManager::SrsSourceManager() { diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index 4a59058951..5d77cf3a8f 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -485,7 +485,7 @@ class SrsSourceManager : public ISrsHourGlass }; // Global singleton instance. -extern SrsSourceManager* _srs_sources; +extern __thread SrsSourceManager* _srs_sources; // For RTMP2RTC, bridge SrsSource to SrsRtcStream class ISrsSourceBridger diff --git a/trunk/src/app/srs_app_threads.cpp b/trunk/src/app/srs_app_threads.cpp new file mode 100644 index 0000000000..9dcd82365d --- /dev/null +++ b/trunk/src/app/srs_app_threads.cpp @@ -0,0 +1,1325 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2021 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef SRS_OSX + pid_t gettid() { + return 0; + } +#else + #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30 + #include + #define gettid() syscall(SYS_gettid) + #endif +#endif + +using namespace std; + +#include + +extern __thread SrsPps* _srs_pps_rloss; +extern __thread SrsPps* _srs_pps_aloss; +extern __thread SrsPps* _srs_pps_aloss2; + +extern __thread SrsPps* _srs_pps_snack2; +extern __thread SrsPps* _srs_pps_snack3; +extern __thread SrsPps* _srs_pps_snack4; + +extern bool srs_is_rtp_or_rtcp(const uint8_t* data, size_t len); +extern bool srs_is_rtcp(const uint8_t* data, size_t len); + +uint64_t srs_covert_cpuset(cpu_set_t v) +{ +#ifdef SRS_OSX + return v; +#else + uint64_t iv = 0; + for (int i = 0; i <= 63; i++) { + if (CPU_ISSET(i, &v)) { + iv |= uint64_t(1) << i; + } + } + return iv; +#endif +} + +SrsCircuitBreaker::SrsCircuitBreaker() +{ + hybrid_high_water_level_ = 0; + hybrid_critical_water_level_ = 0; + hybrid_dying_water_level_ = 0; + + enabled_ = false; + high_threshold_ = 0; + high_pulse_ = 0; + critical_threshold_ = 0; + critical_pulse_ = 0; + dying_threshold_ = 0; + dying_pulse_ = 0; +} + +SrsCircuitBreaker::~SrsCircuitBreaker() +{ +} + +srs_error_t SrsCircuitBreaker::initialize() +{ + srs_error_t err = srs_success; + + // Start a timer to stat the data for circuit breaker. + _srs_hybrid->timer()->subscribe(1 * SRS_UTIME_SECONDS, this); + + enabled_ = _srs_config->get_circuit_breaker(); + high_threshold_ = _srs_config->get_high_threshold(); + high_pulse_ = _srs_config->get_high_pulse(); + critical_threshold_ = _srs_config->get_critical_threshold(); + critical_pulse_ = _srs_config->get_critical_pulse(); + dying_threshold_ = _srs_config->get_dying_threshold(); + dying_pulse_ = _srs_config->get_dying_pulse(); + + srs_trace("CircuitBreaker: enabled=%d, high=%dx%d, critical=%dx%d, dying=%dx%d", enabled_, + high_pulse_, high_threshold_, critical_pulse_, critical_threshold_, dying_pulse_, dying_threshold_); + + return err; +} + +bool SrsCircuitBreaker::hybrid_high_water_level() +{ + return enabled_ && (hybrid_critical_water_level() || hybrid_high_water_level_); +} + +bool SrsCircuitBreaker::hybrid_critical_water_level() +{ + return enabled_ && (hybrid_dying_water_level() || hybrid_critical_water_level_); +} + +bool SrsCircuitBreaker::hybrid_dying_water_level() +{ + return enabled_ && (dying_pulse_ && hybrid_dying_water_level_ >= dying_pulse_); +} + +srs_error_t SrsCircuitBreaker::on_timer(srs_utime_t interval, srs_utime_t tick) +{ + srs_error_t err = srs_success; + + SrsThreadEntry* entry = _srs_thread_pool->self(); + if (!entry->stat) { + return err; + } + + // For Circuit-Breaker to update the SNMP, ASAP. + srs_update_udp_snmp_statistic(); + + // Update thread CPUs per 1s. + srs_update_thread_proc_stat(entry->stat, entry->tid); + + // Update the Circuit-Breaker by water-level. + // Reset the high water-level when CPU is low for N times. + if (entry->stat->percent * 100 > high_threshold_) { + hybrid_high_water_level_ = high_pulse_; + } else if (hybrid_high_water_level_ > 0) { + hybrid_high_water_level_--; + } + + // Reset the critical water-level when CPU is low for N times. + if (entry->stat->percent * 100 > critical_threshold_) { + hybrid_critical_water_level_ = critical_pulse_; + } else if (hybrid_critical_water_level_ > 0) { + hybrid_critical_water_level_--; + } + + // Reset the dying water-level when CPU is low for N times. + if (entry->stat->percent * 100 > dying_threshold_) { + hybrid_dying_water_level_ = srs_min(dying_pulse_ + 1, hybrid_dying_water_level_ + 1); + } else if (hybrid_dying_water_level_ > 0) { + hybrid_dying_water_level_ = 0; + } + + // Show statistics for RTC server. + SrsProcSelfStat* u = srs_get_self_proc_stat(); + // Resident Set Size: number of pages the process has in real memory. + int memory = (int)(u->rss * 4 / 1024); + + // The hybrid thread cpu and memory. + float thread_percent = entry->stat->percent * 100; + + string circuit_breaker; + if (enabled_ && (hybrid_high_water_level() || hybrid_critical_water_level() || _srs_pps_aloss->r1s() || _srs_pps_rloss->r1s() || _srs_pps_snack2->r10s())) { + srs_trace("CircuitBreaker: thread=%s,%.2f%%, sys=%.2f%%,%dMB, break=%d,%d,%d, cond=%d,%d,%.2f%%, snk=%d,%d,%d", + entry->label.c_str(), thread_percent, u->percent * 100, memory, + hybrid_high_water_level(), hybrid_critical_water_level(), hybrid_dying_water_level(), // Whether Circuit-Break is enable. + _srs_pps_rloss->r1s(), _srs_pps_aloss->r1s(), thread_percent, // The conditions to enable Circuit-Breaker. + _srs_pps_snack2->r10s(), _srs_pps_snack3->r10s(), // NACK packet,seqs sent. + _srs_pps_snack4->r10s() // NACK drop by Circuit-Break. + ); + } + + return err; +} + +__thread SrsCircuitBreaker* _srs_circuit_breaker = NULL; + +SrsPipe::SrsPipe() +{ + pipes_[0] = pipes_[1] = -1; +} + +SrsPipe::~SrsPipe() +{ + // Close the FDs because we might not open it as stfd. + if (pipes_[0] > 0) { + ::close(pipes_[0]); + } + if (pipes_[1] > 0) { + ::close(pipes_[1]); + } +} + +srs_error_t SrsPipe::initialize() +{ + srs_error_t err = srs_success; + + if (pipes_[0] > 0) { + return err; + } + + if (pipe(pipes_) < 0) { + return srs_error_new(ERROR_SYSTEM_CREATE_PIPE, "create pipe"); + } + + return err; +} + +int SrsPipe::read_fd() +{ + return pipes_[0]; +} + +int SrsPipe::write_fd() +{ + return pipes_[1]; +} + +SrsThreadPipe::SrsThreadPipe() +{ + stfd_ = NULL; +} + +SrsThreadPipe::~SrsThreadPipe() +{ + srs_close_stfd(stfd_); +} + +srs_error_t SrsThreadPipe::initialize(int fd) +{ + srs_error_t err = srs_success; + + if (stfd_) { + return err; + } + + if ((stfd_ = srs_netfd_open(fd)) == NULL) { + return srs_error_new(ERROR_PIPE_OPEN, "open pipe"); + } + + return err; +} + +srs_error_t SrsThreadPipe::read(void* buf, size_t size, ssize_t* nread) +{ + ssize_t nn = srs_read(stfd_, buf, size, SRS_UTIME_NO_TIMEOUT); + + if (nread) { + *nread = nn; + } + + if (nn < 0) { + return srs_error_new(ERROR_PIPE_READ, "read"); + } + + return srs_success; +} + +srs_error_t SrsThreadPipe::write(void* buf, size_t size, ssize_t* nwrite) +{ + ssize_t nn = srs_write(stfd_, buf, size, SRS_UTIME_NO_TIMEOUT); + + if (nwrite) { + *nwrite = nn; + } + + if (nn < 0) { + return srs_error_new(ERROR_PIPE_WRITE, "write"); + } + + return srs_success; +} + +SrsThreadPipePair::SrsThreadPipePair() +{ + pipe_ = new SrsPipe(); + rpipe_ = new SrsThreadPipe(); + wpipe_ = new SrsThreadPipe(); +} + +SrsThreadPipePair::~SrsThreadPipePair() +{ + close_read(); + close_write(); + srs_freep(pipe_); +} + +srs_error_t SrsThreadPipePair::initialize() +{ + return pipe_->initialize(); +} + +srs_error_t SrsThreadPipePair::open_read() +{ + return rpipe_->initialize(pipe_->read_fd()); +} + +srs_error_t SrsThreadPipePair::open_write() +{ + return wpipe_->initialize(pipe_->write_fd()); +} + +void SrsThreadPipePair::close_read() +{ + srs_freep(rpipe_); +} + +void SrsThreadPipePair::close_write() +{ + srs_freep(wpipe_); +} + +srs_error_t SrsThreadPipePair::read(void* buf, size_t size, ssize_t* nread) +{ + return rpipe_->read(buf, size, nread); +} + +srs_error_t SrsThreadPipePair::write(void* buf, size_t size, ssize_t* nwrite) +{ + return wpipe_->write(buf, size, nwrite); +} + +SrsThreadPipeChannel::SrsThreadPipeChannel() +{ + initiator_ = new SrsThreadPipePair(); + responder_ = new SrsThreadPipePair(); + + trd_ = new SrsFastCoroutine("chan", this); + handler_ = NULL; +} + +SrsThreadPipeChannel::~SrsThreadPipeChannel() +{ + srs_freep(trd_); + srs_freep(initiator_); + srs_freep(responder_); +} + +SrsThreadPipePair* SrsThreadPipeChannel::initiator() +{ + return initiator_; +} + +SrsThreadPipePair* SrsThreadPipeChannel::responder() +{ + return responder_; +} + +srs_error_t SrsThreadPipeChannel::start(ISrsThreadResponder* h) +{ + handler_ = h; + return trd_->start(); +} + +srs_error_t SrsThreadPipeChannel::cycle() +{ + srs_error_t err = srs_success; + + while (true) { + if ((err = trd_->pull()) != srs_success) { + return srs_error_wrap(err, "pull"); + } + + // Here we're responder, read from initiator. + SrsThreadMessage m; + if ((err = initiator_->read(&m, sizeof(m), NULL)) != srs_success) { + srs_warn("read err %s", srs_error_desc(err).c_str()); + srs_freep(err); // Ignore any error. + continue; + } + + // Consume the message, the responder can write response to responder. + if (handler_ && (err = handler_->on_thread_message(&m, this)) != srs_success) { + srs_warn("consume err %s", srs_error_desc(err).c_str()); + srs_freep(err); // Ignore any error. + continue; + } + } + + return err; +} + +SrsThreadPipeSlot::SrsThreadPipeSlot(int slots) +{ + nn_channels_ = slots; + channels_ = new SrsThreadPipeChannel[slots]; + + index_ = 0; + lock_ = new SrsThreadMutex(); +} + +SrsThreadPipeSlot::~SrsThreadPipeSlot() +{ + srs_freepa(channels_); + srs_freep(lock_); +} + +srs_error_t SrsThreadPipeSlot::initialize() +{ + srs_error_t err = srs_success; + + for (int i = 0; i < nn_channels_; i++) { + SrsThreadPipeChannel* channel = &channels_[i]; + + // Here we're responder, but it's ok to initialize the initiator. + if ((err = channel->initiator()->initialize()) != srs_success) { + return srs_error_wrap(err, "init %d initiator", i); + } + if ((err = channel->responder()->initialize()) != srs_success) { + return srs_error_wrap(err, "init %d responder", i); + } + } + + return err; +} + +srs_error_t SrsThreadPipeSlot::open_responder(ISrsThreadResponder* h) +{ + srs_error_t err = srs_success; + + for (int i = 0; i < nn_channels_; i++) { + SrsThreadPipeChannel* channel = &channels_[i]; + + // We're responder, read from initiator, write to responder. + if ((err = channel->initiator()->open_read()) != srs_success) { + return srs_error_wrap(err, "open read"); + } + if ((err = channel->responder()->open_write()) != srs_success) { + return srs_error_wrap(err, "open write"); + } + + // OK, we start the cycle coroutine for responder. + if ((err = channel->start(h)) != srs_success) { + return srs_error_wrap(err, "start %d consume coroutine", i); + } + } + + return err; +} + +SrsThreadPipeChannel* SrsThreadPipeSlot::allocate() +{ + SrsThreadLocker(lock_); + return index_ < nn_channels_? &channels_[index_++] : NULL; +} + +ISrsThreadResponder::ISrsThreadResponder() +{ +} + +ISrsThreadResponder::~ISrsThreadResponder() +{ +} + +SrsThreadMutex::SrsThreadMutex() +{ + // https://man7.org/linux/man-pages/man3/pthread_mutexattr_init.3.html + int r0 = pthread_mutexattr_init(&attr_); + srs_assert(!r0); + + // https://man7.org/linux/man-pages/man3/pthread_mutexattr_gettype.3p.html + r0 = pthread_mutexattr_settype(&attr_, PTHREAD_MUTEX_ERRORCHECK); + srs_assert(!r0); + + // https://michaelkerrisk.com/linux/man-pages/man3/pthread_mutex_init.3p.html + r0 = pthread_mutex_init(&lock_, &attr_); + srs_assert(!r0); +} + +SrsThreadMutex::~SrsThreadMutex() +{ + int r0 = pthread_mutex_destroy(&lock_); + srs_assert(!r0); + + r0 = pthread_mutexattr_destroy(&attr_); + srs_assert(!r0); +} + +void SrsThreadMutex::lock() +{ + // https://man7.org/linux/man-pages/man3/pthread_mutex_lock.3p.html + // EDEADLK + // The mutex type is PTHREAD_MUTEX_ERRORCHECK and the current + // thread already owns the mutex. + int r0 = pthread_mutex_lock(&lock_); + srs_assert(!r0); +} + +void SrsThreadMutex::unlock() +{ + int r0 = pthread_mutex_unlock(&lock_); + srs_assert(!r0); +} + +SrsThreadEntry::SrsThreadEntry() +{ + pool = NULL; + start = NULL; + arg = NULL; + num = 0; + tid = 0; + + err = srs_success; + + // Set affinity mask to include CPUs 0 to 7 + CPU_ZERO(&cpuset); + CPU_ZERO(&cpuset2); + cpuset_ok = false; + + stat = new SrsProcSelfStat(); + slot_ = NULL; +} + +SrsThreadEntry::~SrsThreadEntry() +{ + srs_freep(stat); + srs_freep(err); + + // TODO: FIXME: Before free slot, we MUST close pipes in threads that open them. + srs_freep(slot_); + + // TODO: FIXME: Should dispose trd. +} + +SrsThreadPool::SrsThreadPool() +{ + entry_ = NULL; + lock_ = new SrsThreadMutex(); + hybrid_ = NULL; + + // Add primordial thread, current thread itself. + SrsThreadEntry* entry = new SrsThreadEntry(); + threads_.push_back(entry); + entry_ = entry; + + entry->pool = this; + entry->label = "primordial"; + entry->start = NULL; + entry->arg = NULL; + entry->num = 1; + entry->trd = pthread_self(); + entry->tid = gettid(); + + char buf[256]; + snprintf(buf, sizeof(buf), "srs-master-%d", entry->num); + entry->name = buf; + + pid_fd = -1; +} + +// TODO: FIMXE: If free the pool, we should stop all threads. +SrsThreadPool::~SrsThreadPool() +{ + srs_freep(lock_); + + if (pid_fd > 0) { + ::close(pid_fd); + pid_fd = -1; + } +} + +// Thread local objects. +extern const int LOG_MAX_SIZE; +extern __thread char* _srs_log_data; +extern __thread SrsStageManager* _srs_stages; +extern __thread SrsPps* _srs_pps_ids; +extern __thread SrsPps* _srs_pps_fids; +extern __thread SrsPps* _srs_pps_fids_level0; +extern __thread SrsPps* _srs_pps_dispose; +extern __thread SrsPps* _srs_pps_timer; +extern __thread SrsPps* _srs_pps_clock_15ms; +extern __thread SrsPps* _srs_pps_clock_20ms; +extern __thread SrsPps* _srs_pps_clock_25ms; +extern __thread SrsPps* _srs_pps_clock_30ms; +extern __thread SrsPps* _srs_pps_clock_35ms; +extern __thread SrsPps* _srs_pps_clock_40ms; +extern __thread SrsPps* _srs_pps_clock_80ms; +extern __thread SrsPps* _srs_pps_clock_160ms; +extern __thread SrsPps* _srs_pps_timer_s; +extern __thread SrsPps* _srs_pps_rpkts; +extern __thread SrsPps* _srs_pps_addrs; +extern __thread SrsPps* _srs_pps_fast_addrs; +extern __thread SrsPps* _srs_pps_spkts; +extern __thread SrsPps* _srs_pps_sstuns; +extern __thread SrsPps* _srs_pps_srtcps; +extern __thread SrsPps* _srs_pps_srtps; +extern __thread SrsPps* _srs_pps_pli; +extern __thread SrsPps* _srs_pps_twcc; +extern __thread SrsPps* _srs_pps_rr; +extern __thread SrsPps* _srs_pps_pub; +extern __thread SrsPps* _srs_pps_conn; +extern __thread SrsPps* _srs_pps_rstuns; +extern __thread SrsPps* _srs_pps_rrtps; +extern __thread SrsPps* _srs_pps_rrtcps; +extern __thread SrsPps* _srs_pps_snack; +extern __thread SrsPps* _srs_pps_snack2; +extern __thread SrsPps* _srs_pps_snack3; +extern __thread SrsPps* _srs_pps_snack4; +extern __thread SrsPps* _srs_pps_sanack; +extern __thread SrsPps* _srs_pps_svnack; +extern __thread SrsPps* _srs_pps_rnack; +extern __thread SrsPps* _srs_pps_rnack2; +extern __thread SrsPps* _srs_pps_rhnack; +extern __thread SrsPps* _srs_pps_rmnack; +extern __thread SrsPps* _srs_pps_rloss; +extern __thread SrsPps* _srs_pps_sloss; +extern __thread SrsPps* _srs_pps_aloss; +extern __thread SrsPps* _srs_pps_aloss2; +extern __thread SrsPps* _srs_pps_objs_msgs; +extern __thread SrsPps* _srs_pps_objs_rtps; +extern __thread SrsPps* _srs_pps_objs_rraw; +extern __thread SrsPps* _srs_pps_objs_rfua; +extern __thread SrsPps* _srs_pps_objs_rbuf; +extern __thread SrsPps* _srs_pps_objs_rothers; +extern __thread SrsPps* _srs_pps_objs_drop; +extern __thread SrsPps* _srs_pps_cids_get; +extern __thread SrsPps* _srs_pps_cids_set; +#if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) +extern __thread SrsPps* _srs_pps_recvfrom; +extern __thread SrsPps* _srs_pps_recvfrom_eagain; +extern __thread SrsPps* _srs_pps_sendto; +extern __thread SrsPps* _srs_pps_sendto_eagain; +extern __thread SrsPps* _srs_pps_read; +extern __thread SrsPps* _srs_pps_read_eagain; +extern __thread SrsPps* _srs_pps_readv; +extern __thread SrsPps* _srs_pps_readv_eagain; +extern __thread SrsPps* _srs_pps_writev; +extern __thread SrsPps* _srs_pps_writev_eagain; +extern __thread SrsPps* _srs_pps_recvmsg; +extern __thread SrsPps* _srs_pps_recvmsg_eagain; +extern __thread SrsPps* _srs_pps_sendmsg; +extern __thread SrsPps* _srs_pps_sendmsg_eagain; +extern __thread SrsPps* _srs_pps_epoll; +extern __thread SrsPps* _srs_pps_epoll_zero; +extern __thread SrsPps* _srs_pps_epoll_shake; +extern __thread SrsPps* _srs_pps_epoll_spin; +extern __thread SrsPps* _srs_pps_sched_15ms; +extern __thread SrsPps* _srs_pps_sched_20ms; +extern __thread SrsPps* _srs_pps_sched_25ms; +extern __thread SrsPps* _srs_pps_sched_30ms; +extern __thread SrsPps* _srs_pps_sched_35ms; +extern __thread SrsPps* _srs_pps_sched_40ms; +extern __thread SrsPps* _srs_pps_sched_80ms; +extern __thread SrsPps* _srs_pps_sched_160ms; +extern __thread SrsPps* _srs_pps_sched_s; +extern __thread SrsPps* _srs_pps_thread_run; +extern __thread SrsPps* _srs_pps_thread_idle; +extern __thread SrsPps* _srs_pps_thread_yield; +extern __thread SrsPps* _srs_pps_thread_yield2; +#endif + +// Setup the thread-local variables, MUST call when each thread starting. +srs_error_t SrsThreadPool::setup() +{ + srs_error_t err = srs_success; + + // Initialize the log shared buffer for threads. + srs_assert(!_srs_log_data); + _srs_log_data = new char[LOG_MAX_SIZE]; + + // Create the hybrid RTMP/HTTP/RTC server. + _srs_hybrid = new SrsHybridServer(); + + // Create the circuit breaker for each thread. + _srs_circuit_breaker = new SrsCircuitBreaker(); + + // Create the source manager for server. + _srs_sources = new SrsSourceManager(); + + // The blackhole for RTC server. + _srs_blackhole = new SrsRtcBlackhole(); + + // The resource manager for RTC server. + _srs_rtc_manager = new SrsResourceManager("RTC", true); + + // The source manager for RTC streams. + _srs_rtc_sources = new SrsRtcStreamManager(); + + // The object cache for RTC server. + _srs_rtp_cache = new SrsRtpObjectCacheManager(sizeof(SrsRtpPacket2)); + _srs_rtp_raw_cache = new SrsRtpObjectCacheManager(sizeof(SrsRtpRawPayload)); + _srs_rtp_fua_cache = new SrsRtpObjectCacheManager(sizeof(SrsRtpFUAPayload2)); + _srs_rtp_msg_cache_buffers = new SrsRtpObjectCacheManager(sizeof(SrsSharedPtrMessage) + kRtpPacketSize); + _srs_rtp_msg_cache_objs = new SrsRtpObjectCacheManager(sizeof(SrsSharedPtrMessage)); + + // The pithy print for each thread. + _srs_stages = new SrsStageManager(); + + // The pps stat. + _srs_pps_ids = new SrsPps(); + _srs_pps_fids = new SrsPps(); + _srs_pps_fids_level0 = new SrsPps(); + _srs_pps_dispose = new SrsPps(); + _srs_pps_timer = new SrsPps(); + _srs_pps_clock_15ms = new SrsPps(); + _srs_pps_clock_20ms = new SrsPps(); + _srs_pps_clock_25ms = new SrsPps(); + _srs_pps_clock_30ms = new SrsPps(); + _srs_pps_clock_35ms = new SrsPps(); + _srs_pps_clock_40ms = new SrsPps(); + _srs_pps_clock_80ms = new SrsPps(); + _srs_pps_clock_160ms = new SrsPps(); + _srs_pps_timer_s = new SrsPps(); + _srs_pps_rpkts = new SrsPps(); + _srs_pps_addrs = new SrsPps(); + _srs_pps_fast_addrs = new SrsPps(); + _srs_pps_spkts = new SrsPps(); + _srs_pps_sstuns = new SrsPps(); + _srs_pps_srtcps = new SrsPps(); + _srs_pps_srtps = new SrsPps(); + _srs_pps_pli = new SrsPps(); + _srs_pps_twcc = new SrsPps(); + _srs_pps_rr = new SrsPps(); + _srs_pps_pub = new SrsPps(); + _srs_pps_conn = new SrsPps(); + _srs_pps_rstuns = new SrsPps(); + _srs_pps_rrtps = new SrsPps(); + _srs_pps_rrtcps = new SrsPps(); + _srs_pps_snack = new SrsPps(); + _srs_pps_snack2 = new SrsPps(); + _srs_pps_snack3 = new SrsPps(); + _srs_pps_snack4 = new SrsPps(); + _srs_pps_sanack = new SrsPps(); + _srs_pps_svnack = new SrsPps(); + _srs_pps_rnack = new SrsPps(); + _srs_pps_rnack2 = new SrsPps(); + _srs_pps_rhnack = new SrsPps(); + _srs_pps_rmnack = new SrsPps(); + _srs_pps_rloss = new SrsPps(); + _srs_pps_sloss = new SrsPps(); + _srs_pps_aloss = new SrsPps(); + _srs_pps_aloss2 = new SrsPps(); + _srs_pps_objs_msgs = new SrsPps(); + _srs_pps_objs_rtps = new SrsPps(); + _srs_pps_objs_rraw = new SrsPps(); + _srs_pps_objs_rfua = new SrsPps(); + _srs_pps_objs_rbuf = new SrsPps(); + _srs_pps_objs_rothers = new SrsPps(); + _srs_pps_objs_drop = new SrsPps(); + _srs_pps_cids_get = new SrsPps(); + _srs_pps_cids_set = new SrsPps(); + #if defined(SRS_DEBUG) && defined(SRS_DEBUG_STATS) + _srs_pps_recvfrom = new SrsPps(); + _srs_pps_recvfrom_eagain = new SrsPps(); + _srs_pps_sendto = new SrsPps(); + _srs_pps_sendto_eagain = new SrsPps(); + _srs_pps_read = new SrsPps(); + _srs_pps_read_eagain = new SrsPps(); + _srs_pps_readv = new SrsPps(); + _srs_pps_readv_eagain = new SrsPps(); + _srs_pps_writev = new SrsPps(); + _srs_pps_writev_eagain = new SrsPps(); + _srs_pps_recvmsg = new SrsPps(); + _srs_pps_recvmsg_eagain = new SrsPps(); + _srs_pps_sendmsg = new SrsPps(); + _srs_pps_sendmsg_eagain = new SrsPps(); + _srs_pps_epoll = new SrsPps(); + _srs_pps_epoll_zero = new SrsPps(); + _srs_pps_epoll_shake = new SrsPps(); + _srs_pps_epoll_spin = new SrsPps(); + _srs_pps_sched_15ms = new SrsPps(); + _srs_pps_sched_20ms = new SrsPps(); + _srs_pps_sched_25ms = new SrsPps(); + _srs_pps_sched_30ms = new SrsPps(); + _srs_pps_sched_35ms = new SrsPps(); + _srs_pps_sched_40ms = new SrsPps(); + _srs_pps_sched_80ms = new SrsPps(); + _srs_pps_sched_160ms = new SrsPps(); + _srs_pps_sched_s = new SrsPps(); + _srs_pps_thread_run = new SrsPps(); + _srs_pps_thread_idle = new SrsPps(); + _srs_pps_thread_yield = new SrsPps(); + _srs_pps_thread_yield2 = new SrsPps(); + #endif + + // MUST init ST for each thread, because ST is thread-local now. + if ((err = srs_st_init()) != srs_success) { + return srs_error_wrap(err, "init st"); + } + + return err; +} + +srs_error_t SrsThreadPool::initialize() +{ + srs_error_t err = srs_success; + + // Initialize global shared thread-safe objects once. + if ((err = _srs_rtc_dtls_certificate->initialize()) != srs_success) { + return srs_error_wrap(err, "rtc dtls certificate initialize"); + } + + if ((err = acquire_pid_file()) != srs_success) { + return srs_error_wrap(err, "acquire pid file"); + } + + if ((err = _srs_api->initialize()) != srs_success) { + return srs_error_wrap(err, "init api server"); + } + + // Initialize the master primordial thread. + SrsThreadEntry* entry = (SrsThreadEntry*)entry_; +#ifndef SRS_OSX + // Load CPU affinity from config. + int cpu_start = 0, cpu_end = 0; + entry->cpuset_ok = _srs_config->get_threads_cpu_affinity("master", &cpu_start, &cpu_end); + for (int i = cpu_start; entry->cpuset_ok && i <= cpu_end; i++) { + CPU_SET(i, &entry->cpuset); + } +#endif + + int r0 = 0, r1 = 0; +#ifndef SRS_OSX + if (entry->cpuset_ok) { + r0 = pthread_setaffinity_np(pthread_self(), sizeof(entry->cpuset), &entry->cpuset); + } + r1 = pthread_getaffinity_np(pthread_self(), sizeof(entry->cpuset2), &entry->cpuset2); +#endif + + interval_ = _srs_config->get_threads_interval(); + srs_trace("Thread #%d(%s): init name=%s, interval=%dms, cpuset=%d/%d-0x%" PRIx64 "/%d-0x%" PRIx64, + entry->num, entry->label.c_str(), entry->name.c_str(), srsu2msi(interval_), + entry->cpuset_ok, r0, srs_covert_cpuset(entry->cpuset), r1, srs_covert_cpuset(entry->cpuset2) + ); + + return err; +} + +srs_error_t SrsThreadPool::acquire_pid_file() +{ + std::string pid_file = _srs_config->get_pid_file(); + + // -rw-r--r-- + // 644 + int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + + int fd; + // open pid file + if ((fd = ::open(pid_file.c_str(), O_WRONLY | O_CREAT, mode)) == -1) { + return srs_error_new(ERROR_SYSTEM_PID_ACQUIRE, "open pid file=%s", pid_file.c_str()); + } + + // require write lock + struct flock lock; + + lock.l_type = F_WRLCK; // F_RDLCK, F_WRLCK, F_UNLCK + lock.l_start = 0; // type offset, relative to l_whence + lock.l_whence = SEEK_SET; // SEEK_SET, SEEK_CUR, SEEK_END + lock.l_len = 0; + + if (fcntl(fd, F_SETLK, &lock) == -1) { + if(errno == EACCES || errno == EAGAIN) { + ::close(fd); + srs_error("srs is already running!"); + return srs_error_new(ERROR_SYSTEM_PID_ALREADY_RUNNING, "srs is already running"); + } + return srs_error_new(ERROR_SYSTEM_PID_LOCK, "access to pid=%s", pid_file.c_str()); + } + + // truncate file + if (ftruncate(fd, 0) != 0) { + return srs_error_new(ERROR_SYSTEM_PID_TRUNCATE_FILE, "truncate pid file=%s", pid_file.c_str()); + } + + // write the pid + string pid = srs_int2str(getpid()); + if (write(fd, pid.c_str(), pid.length()) != (int)pid.length()) { + return srs_error_new(ERROR_SYSTEM_PID_WRITE_FILE, "write pid=%s to file=%s", pid.c_str(), pid_file.c_str()); + } + + // auto close when fork child process. + int val; + if ((val = fcntl(fd, F_GETFD, 0)) < 0) { + return srs_error_new(ERROR_SYSTEM_PID_GET_FILE_INFO, "fcntl fd=%d", fd); + } + val |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, val) < 0) { + return srs_error_new(ERROR_SYSTEM_PID_SET_FILE_INFO, "lock file=%s fd=%d", pid_file.c_str(), fd); + } + + srs_trace("write pid=%s to %s success!", pid.c_str(), pid_file.c_str()); + pid_fd = fd; + + return srs_success; +} + +srs_error_t SrsThreadPool::execute(string label, srs_error_t (*start)(void* arg), void* arg) +{ + srs_error_t err = srs_success; + + SrsThreadEntry* entry = new SrsThreadEntry(); + + // Update the hybrid thread entry for circuit breaker. + if (label == "hybrid") { + hybrid_ = entry; + hybrids_.push_back(entry); + } + + // To protect the threads_ for executing thread-safe. + if (true) { + SrsThreadLocker(lock_); + threads_.push_back(entry); + } + + entry->pool = this; + entry->label = label; + entry->start = start; + entry->arg = arg; + + // The id of thread, should equal to the debugger thread id. + // For gdb, it's: info threads + // For lldb, it's: thread list + static int num = entry_->num + 1; + entry->num = num++; + + char buf[256]; + snprintf(buf, sizeof(buf), "srs-%s-%d", entry->label.c_str(), entry->num); + entry->name = buf; + +#ifndef SRS_OSX + // Load CPU affinity from config. + int cpu_start = 0, cpu_end = 0; + entry->cpuset_ok = _srs_config->get_threads_cpu_affinity(label, &cpu_start, &cpu_end); + for (int i = cpu_start; entry->cpuset_ok && i <= cpu_end; i++) { + CPU_SET(i, &entry->cpuset); + } +#endif + + // https://man7.org/linux/man-pages/man3/pthread_create.3.html + pthread_t trd; + int r0 = pthread_create(&trd, NULL, SrsThreadPool::start, entry); + if (r0 != 0) { + entry->err = srs_error_new(ERROR_THREAD_CREATE, "create thread %s, r0=%d", label.c_str(), r0); + return srs_error_copy(entry->err); + } + + entry->trd = trd; + + return err; +} + +srs_error_t SrsThreadPool::run() +{ + srs_error_t err = srs_success; + + while (true) { + vector threads; + if (true) { + SrsThreadLocker(lock_); + threads = threads_; + } + + // Check the threads status fastly. + int loops = (int)(interval_ / SRS_UTIME_SECONDS); + for (int i = 0; i < loops; i++) { + for (int i = 0; i < (int)threads.size(); i++) { + SrsThreadEntry* entry = threads.at(i); + if (entry->err != srs_success) { + err = srs_error_copy(entry->err); + err = srs_error_wrap(err, "thread #%d(%s)", entry->num, entry->label.c_str()); + return err; + } + } + + srs_usleep(1 * SRS_UTIME_SECONDS); + } + + // In normal state, gather status and log it. + string async_logs = _srs_async_log->description(); + + // The hybrid thread cpu and memory. + float top_percent = 0.0f; + for (int i = 0; i < (int)threads.size(); i++) { + SrsThreadEntry* entry = threads.at(i); + if (!entry->stat || entry->stat->percent <= 0) { + continue; + } + top_percent = srs_max(top_percent, entry->stat->percent * 100); + } + + // Show statistics for RTC server. + SrsProcSelfStat* u = srs_get_self_proc_stat(); + // Resident Set Size: number of pages the process has in real memory. + int memory = (int)(u->rss * 4 / 1024); + + srs_trace("Process: cpu=%.2f%%,%dMB, threads=%d,%.2f%%%%s", + u->percent * 100, memory, (int)threads_.size(), top_percent, + async_logs.c_str()); + } + + return err; +} + +void SrsThreadPool::stop() +{ + // TODO: FIXME: Should notify other threads to do cleanup and quit. +} + +SrsThreadEntry* SrsThreadPool::self() +{ + std::vector threads; + + if (true) { + SrsThreadLocker(lock_); + threads = threads_; + } + + for (int i = 0; i < (int)threads.size(); i++) { + SrsThreadEntry* entry = threads.at(i); + if (entry->trd == pthread_self()) { + return entry; + } + } + + return NULL; +} + +SrsThreadEntry* SrsThreadPool::hybrid() +{ + return hybrid_; +} + +vector SrsThreadPool::hybrids() +{ + return hybrids_; +} + +void* SrsThreadPool::start(void* arg) +{ + srs_error_t err = srs_success; + + SrsThreadEntry* entry = (SrsThreadEntry*)arg; + + // Initialize thread-local variables. + if ((err = SrsThreadPool::setup()) != srs_success) { + entry->err = err; + return NULL; + } + + // Set the thread local fields. + entry->tid = gettid(); + + int r0 = 0, r1 = 0; +#ifndef SRS_OSX + // https://man7.org/linux/man-pages/man3/pthread_setname_np.3.html + pthread_setname_np(pthread_self(), entry->name.c_str()); + if (entry->cpuset_ok) { + r0 = pthread_setaffinity_np(pthread_self(), sizeof(entry->cpuset), &entry->cpuset); + } + r1 = pthread_getaffinity_np(pthread_self(), sizeof(entry->cpuset2), &entry->cpuset2); +#else + pthread_setname_np(entry->name.c_str()); +#endif + + srs_trace("Thread #%d: run with tid=%d, entry=%p, label=%s, name=%s, cpuset=%d/%d-0x%" PRIx64 "/%d-0x%" PRIx64, + entry->num, (int)entry->tid, entry, entry->label.c_str(), entry->name.c_str(), entry->cpuset_ok, + r0, srs_covert_cpuset(entry->cpuset), r1, srs_covert_cpuset(entry->cpuset2)); + + if ((err = entry->start(entry->arg)) != srs_success) { + entry->err = err; + } + + // We do not use the return value, the err has been set to entry->err. + return NULL; +} + +// It MUST be thread-safe, global and shared object. +SrsThreadPool* _srs_thread_pool = new SrsThreadPool(); + +SrsAsyncFileWriter::SrsAsyncFileWriter(std::string p) +{ + filename_ = p; + writer_ = new SrsFileWriter(); + chunks_ = new SrsThreadQueue(); +} + +// TODO: FIXME: Before free the writer, we must remove it from the manager. +SrsAsyncFileWriter::~SrsAsyncFileWriter() +{ + // TODO: FIXME: Should we flush dirty logs? + srs_freep(writer_); + srs_freep(chunks_); +} + +srs_error_t SrsAsyncFileWriter::open() +{ + return writer_->open(filename_); +} + +srs_error_t SrsAsyncFileWriter::open_append() +{ + return writer_->open_append(filename_); +} + +void SrsAsyncFileWriter::close() +{ + writer_->close(); +} + +srs_error_t SrsAsyncFileWriter::write(void* buf, size_t count, ssize_t* pnwrite) +{ + srs_error_t err = srs_success; + + if (count <= 0) { + return err; + } + + char* cp = new char[count]; + memcpy(cp, buf, count); + + SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); + msg->wrap(cp, count); + + chunks_->push_back(msg); + + if (pnwrite) { + *pnwrite = count; + } + + return err; +} + +srs_error_t SrsAsyncFileWriter::writev(const iovec* iov, int iovcnt, ssize_t* pnwrite) +{ + srs_error_t err = srs_success; + + for (int i = 0; i < iovcnt; i++) { + const iovec* p = iov + i; + + ssize_t nn = 0; + if ((err = write(p->iov_base, p->iov_len, &nn)) != srs_success) { + return srs_error_wrap(err, "write %d iov %d bytes", i, p->iov_len); + } + + if (pnwrite) { + *pnwrite += nn; + } + } + + return err; +} + +srs_error_t SrsAsyncFileWriter::flush() +{ + srs_error_t err = srs_success; + + vector flying_chunks; + if (true) { + chunks_->swap(flying_chunks); + } + + // Flush the chunks to disk. + for (int i = 0; i < (int)flying_chunks.size(); i++) { + SrsSharedPtrMessage* msg = flying_chunks.at(i); + + srs_error_t r0 = writer_->write(msg->payload, msg->size, NULL); + + // Choose a random error to return. + if (err == srs_success) { + err = r0; + } else { + srs_freep(r0); + } + + srs_freep(msg); + } + + return err; +} + +SrsAsyncLogManager::SrsAsyncLogManager() +{ + interval_ = 0; + + reopen_ = false; + lock_ = new SrsThreadMutex(); +} + +// TODO: FIXME: We should stop the thread first, then free the manager. +SrsAsyncLogManager::~SrsAsyncLogManager() +{ + srs_freep(lock_); + + for (int i = 0; i < (int)writers_.size(); i++) { + SrsAsyncFileWriter* writer = writers_.at(i); + srs_freep(writer); + } +} + +// @remark Note that we should never write logs, because log is not ready not. +srs_error_t SrsAsyncLogManager::initialize() +{ + srs_error_t err = srs_success; + + interval_ = _srs_config->srs_log_flush_interval(); + if (interval_ <= 0) { + return srs_error_new(ERROR_SYSTEM_LOGFILE, "invalid interval=%dms", srsu2msi(interval_)); + } + + return err; +} + +// @remark Now, log is ready, and we can print logs. +srs_error_t SrsAsyncLogManager::start(void* arg) +{ + SrsAsyncLogManager* log = (SrsAsyncLogManager*)arg; + return log->do_start(); +} + +srs_error_t SrsAsyncLogManager::create_writer(std::string filename, SrsAsyncFileWriter** ppwriter) +{ + srs_error_t err = srs_success; + + SrsAsyncFileWriter* writer = new SrsAsyncFileWriter(filename); + + if (true) { + SrsThreadLocker(lock_); + writers_.push_back(writer); + } + + if ((err = writer->open()) != srs_success) { + return srs_error_wrap(err, "open file %s fail", filename.c_str()); + } + + *ppwriter = writer; + return err; +} + +void SrsAsyncLogManager::reopen() +{ + SrsThreadLocker(lock_); + reopen_ = true; +} + +std::string SrsAsyncLogManager::description() +{ + SrsThreadLocker(lock_); + + int nn_logs = 0; + int max_logs = 0; + for (int i = 0; i < (int)writers_.size(); i++) { + SrsAsyncFileWriter* writer = writers_.at(i); + + int nn = (int)writer->chunks_->size(); + nn_logs += nn; + max_logs = srs_max(max_logs, nn); + } + + static char buf[128]; + snprintf(buf, sizeof(buf), ", logs=%d/%d/%d", (int)writers_.size(), nn_logs, max_logs); + + return buf; +} + +srs_error_t SrsAsyncLogManager::do_start() +{ + srs_error_t err = srs_success; + + // Never quit for this thread. + while (true) { + // Reopen all log files. + if (reopen_) { + SrsThreadLocker(lock_); + reopen_ = false; + + for (int i = 0; i < (int)writers_.size(); i++) { + SrsAsyncFileWriter* writer = writers_.at(i); + + writer->close(); + if ((err = writer->open()) != srs_success) { + srs_error_reset(err); // Ignore any error for reopen logs. + } + } + } + + // Flush all logs from cache to disk. + if (true) { + SrsThreadLocker(lock_); + + for (int i = 0; i < (int)writers_.size(); i++) { + SrsAsyncFileWriter* writer = writers_.at(i); + + if ((err = writer->flush()) != srs_success) { + srs_error_reset(err); // Ignore any error for flushing logs. + } + } + } + + // We use the system primordial sleep, not the ST sleep, because + // this is a system thread, not a coroutine. + timespec tv = {0}; + tv.tv_sec = interval_ / SRS_UTIME_SECONDS; + tv.tv_nsec = (interval_ % SRS_UTIME_SECONDS) * 1000; + nanosleep(&tv, NULL); + } + + return err; +} + +// It MUST be thread-safe, global shared object. +SrsAsyncLogManager* _srs_async_log = new SrsAsyncLogManager(); diff --git a/trunk/src/app/srs_app_threads.hpp b/trunk/src/app/srs_app_threads.hpp new file mode 100644 index 0000000000..7e29ea0e86 --- /dev/null +++ b/trunk/src/app/srs_app_threads.hpp @@ -0,0 +1,455 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2021 Winlin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_APP_THREADS_HPP +#define SRS_APP_THREADS_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +class SrsThreadPool; +class SrsProcSelfStat; +class SrsThreadMutex; +class ISrsThreadResponder; +struct SrsThreadMessage; + +// The circuit breaker to protect server. +class SrsCircuitBreaker : public ISrsFastTimer +{ +private: + // Reset the water-level when CPU is low for N times. + // @note To avoid the CPU change rapidly. + int hybrid_high_water_level_; + int hybrid_critical_water_level_; + int hybrid_dying_water_level_; +private: + // The config for high/critical water level. + bool enabled_; + int high_threshold_; + int high_pulse_; + int critical_threshold_; + int critical_pulse_; + int dying_threshold_; + int dying_pulse_; +public: + SrsCircuitBreaker(); + virtual ~SrsCircuitBreaker(); +public: + srs_error_t initialize(); +public: + // Whether hybrid server water-level is high. + bool hybrid_high_water_level(); + bool hybrid_critical_water_level(); + bool hybrid_dying_water_level(); +// interface ISrsFastTimer +private: + srs_error_t on_timer(srs_utime_t interval, srs_utime_t tick); +}; + +extern __thread SrsCircuitBreaker* _srs_circuit_breaker; + +// The pipe wraps the os pipes(fds). +class SrsPipe +{ +private: + // The max buffer size of pipe is PIPE_BUF, so if we used to transmit signals(int), + // up to PIPE_BUF/sizeof(int) signals can be queued up. + // @see https://man7.org/linux/man-pages/man2/pipe.2.html + int pipes_[2]; +public: + SrsPipe(); + virtual ~SrsPipe(); +public: + srs_error_t initialize(); +public: + int read_fd(); + int write_fd(); +}; + +// The pipe to communicate between thread-local ST of threads. +class SrsThreadPipe +{ +private: + srs_netfd_t stfd_; +public: + SrsThreadPipe(); + virtual ~SrsThreadPipe(); +public: + // Open fd by ST, should be free by the same thread. + srs_error_t initialize(int fd); +public: + // Note that the pipe is unidirectional data channel, so only one of + // read/write is available. + srs_error_t read(void* buf, size_t size, ssize_t* nread); + srs_error_t write(void* buf, size_t size, ssize_t* nwrite); +}; + +// A thread pipe pair, to communicate between threads. +// @remark If thread A open read, then it MUST close the read. +class SrsThreadPipePair +{ +private: + // Per-process pipe which is used as a signal queue. + // Up to PIPE_BUF/sizeof(int) signals can be queued up. + SrsPipe* pipe_; + SrsThreadPipe* rpipe_; + SrsThreadPipe* wpipe_; +public: + SrsThreadPipePair(); + virtual ~SrsThreadPipePair(); +public: + // It's ok to initialize pipe in another threads. + srs_error_t initialize(); +public: + // It's ok to open read/write in one or two threads. + srs_error_t open_read(); + srs_error_t open_write(); +public: + // For multiple-threading, if a thread open the pipe, it MUST close it, never close it by + // another thread which has not open it. + // If pair(read/write) alive in one thread, user can directly free the pair, without closing + // the read/write, because it's in the same thread. + void close_read(); + void close_write(); +public: + srs_error_t read(void* buf, size_t size, ssize_t* nread); + srs_error_t write(void* buf, size_t size, ssize_t* nwrite); +}; + +// A thread pipe channel, bidirectional data channel, between two threads. +class SrsThreadPipeChannel : public ISrsCoroutineHandler +{ +private: + // ThreadA write initiator, read by ThreadB. + SrsThreadPipePair* initiator_; + // ThreadB write responder, read by ThreadA. + SrsThreadPipePair* responder_; +private: + // Coroutine for responder. + SrsFastCoroutine* trd_; + // The callback handler of responder. + ISrsThreadResponder* handler_; +public: + SrsThreadPipeChannel(); + virtual ~SrsThreadPipeChannel(); +public: + SrsThreadPipePair* initiator(); + SrsThreadPipePair* responder(); +public: + // For responder, start a coroutine to read messages from initiator. + srs_error_t start(ISrsThreadResponder* h); +private: + srs_error_t cycle(); +}; + +// A slot contains a fixed number of channels to communicate with threads. +class SrsThreadPipeSlot +{ +private: + SrsThreadPipeChannel* channels_; + int nn_channels_; +private: + // Current allocated index of slot for channels. + int index_; + SrsThreadMutex* lock_; +public: + SrsThreadPipeSlot(int slots); + virtual ~SrsThreadPipeSlot(); +public: + srs_error_t initialize(); + // Should only call by responder. + srs_error_t open_responder(ISrsThreadResponder* h); +public: + // Allocate channel for initiator. + SrsThreadPipeChannel* allocate(); +}; + +// The handler for responder, which got message from initiator. +class ISrsThreadResponder +{ +public: + ISrsThreadResponder(); + virtual ~ISrsThreadResponder(); +public: + // Got a thread message msg from channel. + virtual srs_error_t on_thread_message(SrsThreadMessage* msg, SrsThreadPipeChannel* channel) = 0; +}; + +// The ID for messages between threads. +enum SrsThreadMessageID +{ + // For SrsThreadMessageRtcCreateSession + SrsThreadMessageIDRtcCreateSession = 0x00000001, +}; + +// The message to marshal/unmarshal between threads. +struct SrsThreadMessage +{ + // Convert with SrsThreadMessageID. + uint64_t id; + // Convert with struct pointers. + uint64_t ptr; + // TODO: FIXME: Add a trace ID? +}; + +// The thread mutex wrapper, without error. +class SrsThreadMutex +{ +private: + pthread_mutex_t lock_; + pthread_mutexattr_t attr_; +public: + SrsThreadMutex(); + virtual ~SrsThreadMutex(); +public: + void lock(); + void unlock(); +}; + +// The thread mutex locker. +// TODO: FIXME: Rename _SRS to _srs +#define SrsThreadLocker(instance) \ + impl__SrsThreadLocker _SRS_free_##instance(instance) + +class impl__SrsThreadLocker +{ +private: + SrsThreadMutex* lock; +public: + impl__SrsThreadLocker(SrsThreadMutex* l) { + lock = l; + lock->lock(); + } + virtual ~impl__SrsThreadLocker() { + lock->unlock(); + } +}; + +// Thread-safe queue. +template +class SrsThreadQueue +{ +private: + std::vector dirty_; + SrsThreadMutex* lock_; +public: + // SrsThreadQueue::SrsThreadQueue + SrsThreadQueue() { + lock_ = new SrsThreadMutex(); + } + // SrsThreadQueue::~SrsThreadQueue + virtual ~SrsThreadQueue() { + srs_freep(lock_); + for (int i = 0; i < (int)dirty_.size(); i++) { + T* msg = dirty_.at(i); + srs_freep(msg); + } + } +public: + // SrsThreadQueue::push_back + void push_back(T* msg) { + SrsThreadLocker(lock_); + dirty_.push_back(msg); + } + // SrsThreadQueue::push_back + void push_back(std::vector& flying) { + SrsThreadLocker(lock_); + dirty_.insert(dirty_.end(), flying.begin(), flying.end()); + } + // SrsThreadQueue::swap + void swap(std::vector& flying) { + SrsThreadLocker(lock_); + dirty_.swap(flying); + } + // SrsThreadQueue::size + size_t size() { + SrsThreadLocker(lock_); + return dirty_.size(); + } +}; + +#ifdef SRS_OSX + typedef uint64_t cpu_set_t; + #define CPU_ZERO(p) *p = 0 +#endif + +// The information for a thread. +class SrsThreadEntry +{ +public: + SrsThreadPool* pool; + std::string label; + std::string name; + srs_error_t (*start)(void* arg); + void* arg; + int num; + // @see https://man7.org/linux/man-pages/man2/gettid.2.html + pid_t tid; +public: + // The thread object. + pthread_t trd; + // The exit error of thread. + srs_error_t err; +public: + // @see https://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html + cpu_set_t cpuset; // Config value. + cpu_set_t cpuset2; // Actual value. + bool cpuset_ok; +public: + SrsProcSelfStat* stat; + // The slot for other threads to communicate with this thread. + SrsThreadPipeSlot* slot_; + // The channels to communicate with other threads. + std::map channels_; +public: + SrsThreadEntry(); + virtual ~SrsThreadEntry(); +}; + +// Allocate a(or almost) fixed thread poll to execute tasks, +// so that we can take the advantage of multiple CPUs. +class SrsThreadPool +{ +private: + SrsThreadEntry* entry_; + srs_utime_t interval_; +private: + SrsThreadMutex* lock_; + std::vector threads_; +private: + // The hybrid server entry, the cpu percent used for circuit breaker. + SrsThreadEntry* hybrid_; + std::vector hybrids_; +private: + // The pid file fd, lock the file write when server is running. + // @remark the init.d script should cleanup the pid file, when stop service, + // for the server never delete the file; when system startup, the pid in pid file + // maybe valid but the process is not SRS, the init.d script will never start server. + int pid_fd; +public: + SrsThreadPool(); + virtual ~SrsThreadPool(); +public: + // Setup the thread-local variables. + static srs_error_t setup(); + // Initialize the thread pool. + srs_error_t initialize(); +private: + // Require the PID file for the whole process. + virtual srs_error_t acquire_pid_file(); +public: + // Execute start function with label in thread. + srs_error_t execute(std::string label, srs_error_t (*start)(void* arg), void* arg); + // Run in the primordial thread, util stop or quit. + srs_error_t run(); + // Stop the thread pool and quit the primordial thread. + void stop(); +public: + SrsThreadEntry* self(); + SrsThreadEntry* hybrid(); + std::vector hybrids(); +private: + static void* start(void* arg); +}; + +// It MUST be thread-safe, global and shared object. +extern SrsThreadPool* _srs_thread_pool; + +// Async file writer, it's thread safe. +class SrsAsyncFileWriter : public ISrsWriter +{ + friend class SrsAsyncLogManager; +private: + std::string filename_; + SrsFileWriter* writer_; +private: + // The thread-queue, to flush to disk by dedicated thread. + SrsThreadQueue* chunks_; +private: + SrsAsyncFileWriter(std::string p); + virtual ~SrsAsyncFileWriter(); +public: + // Open file writer, in truncate mode. + virtual srs_error_t open(); + // Open file writer, in append mode. + virtual srs_error_t open_append(); + // Close current writer. + virtual void close(); +// Interface ISrsWriteSeeker +public: + virtual srs_error_t write(void* buf, size_t count, ssize_t* pnwrite); + virtual srs_error_t writev(const iovec* iov, int iovcnt, ssize_t* pnwrite); +public: + // Flush thread-queue to disk, generally by dedicated thread. + srs_error_t flush(); +}; + +// The async log file writer manager, use a thread to flush multiple writers, +// and reopen all log files when got LOGROTATE signal. +class SrsAsyncLogManager +{ +private: + // The async flush interval. + srs_utime_t interval_; +private: + // The async reopen event. + bool reopen_; +private: + SrsThreadMutex* lock_; + std::vector writers_; +public: + SrsAsyncLogManager(); + virtual ~SrsAsyncLogManager(); +public: + // Initialize the async log manager. + srs_error_t initialize(); + // Run the async log manager thread. + static srs_error_t start(void* arg); + // Create a managed writer, user should never free it. + srs_error_t create_writer(std::string filename, SrsAsyncFileWriter** ppwriter); + // Reopen all log files, asynchronously. + virtual void reopen(); +public: + // Get the summary of this manager. + std::string description(); +private: + srs_error_t do_start(); +}; + +// It MUST be thread-safe, global shared object. +extern SrsAsyncLogManager* _srs_async_log; + +#endif diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index a221cbbbb2..dc05027148 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -50,6 +50,13 @@ using namespace std; #include #include +#include + +__thread SrsPps* _srs_pps_rloss = NULL; +__thread SrsPps* _srs_pps_sloss = NULL; +__thread SrsPps* _srs_pps_aloss = NULL; +__thread SrsPps* _srs_pps_aloss2 = NULL; + // the longest time to wait for a process to quit. #define SRS_PROCESS_QUIT_TIMEOUT_MS 1000 @@ -214,6 +221,7 @@ srs_error_t srs_kill_forced(int& pid) return err; } +// TODO: FIXME: It should be thread-local or thread-safe. static SrsRusage _srs_system_rusage; SrsRusage::SrsRusage() @@ -240,6 +248,7 @@ void srs_update_system_rusage() _srs_system_rusage.ok = true; } +// TODO: FIXME: It should be thread-local or thread-safe. static SrsProcSelfStat _srs_system_cpu_self_stat; static SrsProcSystemStat _srs_system_cpu_system_stat; @@ -327,7 +336,7 @@ SrsProcSystemStat* srs_get_system_proc_stat() return &_srs_system_cpu_system_stat; } -bool get_proc_system_stat(SrsProcSystemStat& r) +bool read_proc_system_stat(SrsProcSystemStat& r) { #ifndef SRS_OSX FILE* f = fopen("/proc/stat", "r"); @@ -366,15 +375,16 @@ bool get_proc_system_stat(SrsProcSystemStat& r) return true; } -bool get_proc_self_stat(SrsProcSelfStat& r) +bool read_proc_self_stat(const char* path, SrsProcSelfStat& r) { #ifndef SRS_OSX - FILE* f = fopen("/proc/self/stat", "r"); + FILE* f = fopen(path, "r"); if (f == NULL) { srs_warn("open self cpu stat failed, ignore"); return false; } - + + // Please read /proc/[pid]/stat of @doc https://man7.org/linux/man-pages/man5/procfs.5.html fscanf(f, "%d %32s %c %d %d %d %d " "%d %u %lu %lu %lu %lu " "%lu %lu %ld %ld %ld %ld " @@ -402,24 +412,16 @@ bool get_proc_self_stat(SrsProcSelfStat& r) return true; } -void srs_update_proc_stat() +void srs_update_system_proc_stat() { - // @see: http://stackoverflow.com/questions/7298646/calculating-user-nice-sys-idle-iowait-irq-and-sirq-from-proc-stat/7298711 - // @see https://github.com/ossrs/srs/issues/397 - static int user_hz = 0; - if (user_hz <= 0) { - user_hz = (int)sysconf(_SC_CLK_TCK); - srs_info("USER_HZ=%d", user_hz); - srs_assert(user_hz > 0); - } - // system cpu stat if (true) { SrsProcSystemStat r; - if (!get_proc_system_stat(r)) { + if (!read_proc_system_stat(r)) { return; } - + + // TODO: FIXME: Use system time cache. r.sample_time = srsu2ms(srs_update_system_time()); // calc usage in percent @@ -438,29 +440,70 @@ void srs_update_proc_stat() // upate cache. _srs_system_cpu_system_stat = r; } +} + +void srs_update_self_proc_stat() +{ + srs_update_thread_proc_stat(&_srs_system_cpu_self_stat, 0); +} + +void srs_update_thread_proc_stat(SrsProcSelfStat* stat, pid_t tid) +{ + // @see: http://stackoverflow.com/questions/7298646/calculating-user-nice-sys-idle-iowait-irq-and-sirq-from-proc-stat/7298711 + // @see https://github.com/ossrs/srs/issues/397 + int user_hz = srs_user_hz(); - // self cpu stat - if (true) { - SrsProcSelfStat r; - if (!get_proc_self_stat(r)) { + // self process cpu stat + SrsProcSelfStat r; + + // @see https://man7.org/linux/man-pages/man5/procfs.5.html + if (tid == 0) { + if (!read_proc_self_stat("/proc/self/stat", r)) { return; } - - r.sample_time = srsu2ms(srs_update_system_time()); - - // calc usage in percent - SrsProcSelfStat& o = _srs_system_cpu_self_stat; - - // @see: http://stackoverflow.com/questions/16011677/calculating-cpu-usage-using-proc-files - int64_t total = r.sample_time - o.sample_time; - int64_t usage = (r.utime + r.stime) - (o.utime + o.stime); - if (total > 0) { - r.percent = (float)(usage * 1000 / (double)total / user_hz); + } else { + char buf[128]; + snprintf(buf, sizeof(buf), "/proc/self/task/%d/stat", (int)tid); + if (!read_proc_self_stat(buf, r)) { + return; } - - // upate cache. - _srs_system_cpu_self_stat = r; } + + // calc usage in percent + SrsProcSelfStat* o = stat; + + // Never update in 1s. + if (srs_update_system_time() - o->sample_time <= 1 * SRS_UTIME_SECONDS) { + return; + } + + // TODO: FIXME: Use system time cache. + r.sample_time = srsu2ms(srs_update_system_time()); + + // @see: http://stackoverflow.com/questions/16011677/calculating-cpu-usage-using-proc-files + int64_t total = r.sample_time - o->sample_time; + int64_t usage = (r.utime + r.stime) - (o->utime + o->stime); + if (total > 0) { + r.percent = (float)(usage * 1000 / (double)total / user_hz); + } + + // update cache. + *stat = r; +} + +int srs_user_hz() +{ + // @see: http://stackoverflow.com/questions/7298646/calculating-user-nice-sys-idle-iowait-irq-and-sirq-from-proc-stat/7298711 + // @see https://github.com/ossrs/srs/issues/397 + static int user_hz = 0; + + if (user_hz <= 0) { + user_hz = (int)sysconf(_SC_CLK_TCK); + srs_info("USER_HZ=%d", user_hz); + srs_assert(user_hz > 0); + } + + return user_hz; } SrsDiskStat::SrsDiskStat() @@ -482,6 +525,7 @@ SrsDiskStat::SrsDiskStat() wr_ticks = nb_current = ticks = aveq = 0; } +// TODO: FIXME: It should be thread-local or thread-safe. static SrsDiskStat _srs_disk_stat; SrsDiskStat* srs_get_disk_stat() @@ -610,7 +654,7 @@ void srs_update_disk_stat() if (!srs_get_disk_diskstats_stat(r)) { return; } - if (!get_proc_system_stat(r.cpu)) { + if (!read_proc_system_stat(r.cpu)) { return; } @@ -676,6 +720,7 @@ SrsMemInfo::SrsMemInfo() SwapFree = 0; } +// TODO: FIXME: It should be thread-local or thread-safe. static SrsMemInfo _srs_system_meminfo; SrsMemInfo* srs_get_meminfo() @@ -768,6 +813,7 @@ SrsPlatformInfo::SrsPlatformInfo() load_fifteen_minutes = 0; } +// TODO: FIXME: It should be thread-local or thread-safe. static SrsPlatformInfo _srs_system_platform_info; SrsPlatformInfo* srs_get_platform_info() @@ -862,57 +908,58 @@ SrsSnmpUdpStat::SrsSnmpUdpStat() rcv_buf_errors = 0; snd_buf_errors = 0; in_csum_errors = 0; - - rcv_buf_errors_delta = 0; - snd_buf_errors_delta = 0; } SrsSnmpUdpStat::~SrsSnmpUdpStat() { } +// TODO: FIXME: It should be thread-local or thread-safe. static SrsSnmpUdpStat _srs_snmp_udp_stat; -bool get_udp_snmp_statistic(SrsSnmpUdpStat& r) +void srs_update_udp_snmp_statistic() { #ifndef SRS_OSX - if (true) { - FILE* f = fopen("/proc/net/snmp", "r"); - if (f == NULL) { - srs_warn("open proc network snmp failed, ignore"); - return false; - } + SrsSnmpUdpStat& r = _srs_snmp_udp_stat; - // ignore title. - static char buf[1024]; - fgets(buf, sizeof(buf), f); + FILE* f = fopen("/proc/net/snmp", "r"); + if (f == NULL) { + return; + } - while (fgets(buf, sizeof(buf), f)) { - // udp stat title - if (strncmp(buf, "Udp: ", 5) == 0) { - // read tcp stat data - if (!fgets(buf, sizeof(buf), f)) { - break; - } - // parse tcp stat data - if (strncmp(buf, "Udp: ", 5) == 0) { - sscanf(buf + 5, "%llu %llu %llu %llu %llu %llu %llu\n", - &r.in_datagrams, - &r.no_ports, - &r.in_errors, - &r.out_datagrams, - &r.rcv_buf_errors, - &r.snd_buf_errors, - &r.in_csum_errors); - } - } + static char buf[1024]; + while (fgets(buf, sizeof(buf), f)) { + // Ignore lines except UDP. + if (strncmp(buf, "Udp: ", 5) != 0) { + continue; } - fclose(f); + + // Ignore UDP stat title. + // Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors + if (strncmp(buf, "Udp: InDatagrams", 16) == 0) { + continue; + } + + // Parse the UDP stat messages. + // Udp: 22000790151 77826210 229174183 24889592909 229174182 420017 1 + sscanf(buf + 5, "%llu %llu %llu %llu %llu %llu %llu\n", + &r.in_datagrams, + &r.no_ports, + &r.in_errors, + &r.out_datagrams, + &r.rcv_buf_errors, + &r.snd_buf_errors, + &r.in_csum_errors); + break; } -#endif - r.ok = true; + fclose(f); - return true; + // Update the pps for recv/send loss. + _srs_pps_rloss->update(r.rcv_buf_errors); + _srs_pps_sloss->update(r.snd_buf_errors); + + r.ok = true; +#endif } SrsSnmpUdpStat* srs_get_udp_snmp_stat() @@ -920,25 +967,6 @@ SrsSnmpUdpStat* srs_get_udp_snmp_stat() return &_srs_snmp_udp_stat; } -void srs_update_udp_snmp_statistic() -{ - SrsSnmpUdpStat r; - if (!get_udp_snmp_statistic(r)) { - return; - } - - SrsSnmpUdpStat& o = _srs_snmp_udp_stat; - if (o.rcv_buf_errors > 0) { - r.rcv_buf_errors_delta = int(r.rcv_buf_errors - o.rcv_buf_errors); - } - - if (o.snd_buf_errors > 0) { - r.snd_buf_errors_delta = int(r.snd_buf_errors - o.snd_buf_errors); - } - - _srs_snmp_udp_stat = r; -} - SrsNetworkDevices::SrsNetworkDevices() { ok = false; @@ -965,6 +993,7 @@ SrsNetworkDevices::SrsNetworkDevices() scompressed = 0; } +// TODO: FIXME: It should be thread-local or thread-safe. #define MAX_NETWORK_DEVICES_COUNT 16 static SrsNetworkDevices _srs_system_network_devices[MAX_NETWORK_DEVICES_COUNT]; static int _nb_srs_system_network_devices = -1; @@ -1033,6 +1062,7 @@ SrsNetworkRtmpServer::SrsNetworkRtmpServer() rkbps_5m = skbps_5m = 0; } +// TODO: FIXME: It should be thread-local or thread-safe. static SrsNetworkRtmpServer _srs_network_rtmp_server; SrsNetworkRtmpServer* srs_get_network_rtmp_server() diff --git a/trunk/src/app/srs_app_utility.hpp b/trunk/src/app/srs_app_utility.hpp index 1c343746fc..26a92a17e7 100644 --- a/trunk/src/app/srs_app_utility.hpp +++ b/trunk/src/app/srs_app_utility.hpp @@ -341,7 +341,12 @@ extern SrsProcSelfStat* srs_get_self_proc_stat(); // Get system cpu stat, use cache to avoid performance problem. extern SrsProcSystemStat* srs_get_system_proc_stat(); // The daemon st-thread will update it. -extern void srs_update_proc_stat(); +extern void srs_update_system_proc_stat(); +extern void srs_update_self_proc_stat(); +// Get the thread stat of this process. +extern void srs_update_thread_proc_stat(SrsProcSelfStat* r, pid_t tid); +// Get the system config USER_HZ. +extern int srs_user_hz(); // Stat disk iops // @see: http://stackoverflow.com/questions/4458183/how-the-util-of-iostat-is-computed @@ -552,12 +557,9 @@ class SrsSnmpUdpStat public: // Whether the data is ok. bool ok; - // send and recv buffer error delta - int rcv_buf_errors_delta; - int snd_buf_errors_delta; public: - // @see: cat /proc/uptimecat /proc/net/snmp|grep 'Udp:' + // @see: cat /proc/uptime && cat /proc/net/snmp|grep 'Udp:' // @see: https://blog.packagecloud.io/eng/2017/02/06/monitoring-tuning-linux-networking-stack-sending-data/#procnetsnmp // InDatagrams: incremented when recvmsg was used by a userland program to read datagram. // also incremented when a UDP packet is encapsulated and sent back for processing. diff --git a/trunk/src/core/srs_core_version5.cpp b/trunk/src/core/srs_core_version5.cpp index e5071d6ef8..a8ca9a3a44 100644 --- a/trunk/src/core/srs_core_version5.cpp +++ b/trunk/src/core/srs_core_version5.cpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2021 Winlin * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp index 7c598a58e1..5639f04efd 100644 --- a/trunk/src/core/srs_core_version5.hpp +++ b/trunk/src/core/srs_core_version5.hpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2021 Winlin * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -26,6 +26,6 @@ #define VERSION_MAJOR 5 #define VERSION_MINOR 0 -#define VERSION_REVISION 1 +#define VERSION_REVISION 3 #endif diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 2d97327bf4..7558c00ac2 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -118,6 +118,11 @@ #define ERROR_SOCKET_SETREUSEADDR 1079 #define ERROR_SOCKET_SETCLOSEEXEC 1080 #define ERROR_SOCKET_ACCEPT 1081 +#define ERROR_THREAD_CREATE 1082 +#define ERROR_SYSTEM_LOGFILE 1083 +#define ERROR_PIPE_OPEN 1084 +#define ERROR_PIPE_READ 1085 +#define ERROR_PIPE_WRITE 1086 /////////////////////////////////////////////////////// // RTMP protocol error. diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index d66816a798..9d2f7fbbe6 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -43,7 +43,7 @@ using namespace std; #include -SrsPps* _srs_pps_objs_msgs = new SrsPps(); +__thread SrsPps* _srs_pps_objs_msgs = NULL; SrsMessageHeader::SrsMessageHeader() { diff --git a/trunk/src/kernel/srs_kernel_kbps.cpp b/trunk/src/kernel/srs_kernel_kbps.cpp index fbaf2074a6..c0aabeca87 100644 --- a/trunk/src/kernel/srs_kernel_kbps.cpp +++ b/trunk/src/kernel/srs_kernel_kbps.cpp @@ -88,12 +88,16 @@ void SrsPps::update(int64_t nn) srs_utime_t now = clk_->now(); + srs_pps_init(sample_1s_, nn, now); srs_pps_init(sample_10s_, nn, now); srs_pps_init(sample_30s_, nn, now); srs_pps_init(sample_1m_, nn, now); srs_pps_init(sample_5m_, nn, now); srs_pps_init(sample_60m_, nn, now); + if (now - sample_1s_.time >= 1 * SRS_UTIME_SECONDS) { + srs_pps_update(sample_1s_, nn, now); + } if (now - sample_10s_.time >= 10 * SRS_UTIME_SECONDS) { srs_pps_update(sample_10s_, nn, now); } @@ -116,6 +120,11 @@ int SrsPps::r10s() return sample_10s_.rate; } +int SrsPps::r1s() +{ + return sample_1s_.rate; +} + SrsWallClock::SrsWallClock() { } @@ -129,5 +138,6 @@ srs_utime_t SrsWallClock::now() return srs_get_system_time(); } +// TODO: FIXME: It should be thread-local or thread-safe. SrsWallClock* _srs_clock = new SrsWallClock(); diff --git a/trunk/src/kernel/srs_kernel_kbps.hpp b/trunk/src/kernel/srs_kernel_kbps.hpp index 7b90ce1716..8c23e68926 100644 --- a/trunk/src/kernel/srs_kernel_kbps.hpp +++ b/trunk/src/kernel/srs_kernel_kbps.hpp @@ -52,6 +52,7 @@ class SrsPps SrsWallClock* clk_; private: // samples + SrsRateSample sample_1s_; SrsRateSample sample_10s_; SrsRateSample sample_30s_; SrsRateSample sample_1m_; @@ -72,6 +73,8 @@ class SrsPps void update(int64_t nn); // Get the 10s average stat. int r10s(); + // Get the 1s average stat. + int r1s(); }; /** @@ -89,7 +92,7 @@ class SrsWallClock virtual srs_utime_t now(); }; -// The global clock. +// TODO: FIXME: It should be thread-local or thread-safe. extern SrsWallClock* _srs_clock; #endif diff --git a/trunk/src/kernel/srs_kernel_log.hpp b/trunk/src/kernel/srs_kernel_log.hpp index 98692afb69..8cf27a3ebf 100644 --- a/trunk/src/kernel/srs_kernel_log.hpp +++ b/trunk/src/kernel/srs_kernel_log.hpp @@ -61,8 +61,6 @@ class ISrsLog public: // Initialize log utilities. virtual srs_error_t initialize() = 0; - // Reopen the log file for log rotate. - virtual void reopen() = 0; public: // The log for verbose, very verbose information. virtual void verbose(const char* tag, SrsContextId context_id, const char* fmt, ...) = 0; @@ -99,10 +97,10 @@ class ISrsContext virtual const SrsContextId& set_id(const SrsContextId& v) = 0; }; -// @global User must provides a log object +// It SHOULD be thread-safe, because it use async log and thread-local buffer. extern ISrsLog* _srs_log; -// @global User must implements the LogContext and define a global instance. +// It SHOULD be thread-safe, because it use thread-local thread private data. extern ISrsContext* _srs_context; // Log style. diff --git a/trunk/src/kernel/srs_kernel_rtc_rtp.cpp b/trunk/src/kernel/srs_kernel_rtc_rtp.cpp index 5474d9106f..9746987d8f 100644 --- a/trunk/src/kernel/srs_kernel_rtc_rtp.cpp +++ b/trunk/src/kernel/srs_kernel_rtc_rtp.cpp @@ -36,12 +36,12 @@ using namespace std; #include -SrsPps* _srs_pps_objs_rtps = new SrsPps(); -SrsPps* _srs_pps_objs_rraw = new SrsPps(); -SrsPps* _srs_pps_objs_rfua = new SrsPps(); -SrsPps* _srs_pps_objs_rbuf = new SrsPps(); -SrsPps* _srs_pps_objs_rothers = new SrsPps(); -SrsPps* _srs_pps_objs_drop = new SrsPps(); +__thread SrsPps* _srs_pps_objs_rtps = NULL; +__thread SrsPps* _srs_pps_objs_rraw = NULL; +__thread SrsPps* _srs_pps_objs_rfua = NULL; +__thread SrsPps* _srs_pps_objs_rbuf = NULL; +__thread SrsPps* _srs_pps_objs_rothers = NULL; +__thread SrsPps* _srs_pps_objs_drop = NULL; /* @see https://tools.ietf.org/html/rfc1889#section-5.1 0 1 2 3 @@ -1081,12 +1081,14 @@ bool SrsRtpPacket2::is_keyframe() return false; } -SrsRtpObjectCacheManager* _srs_rtp_cache = new SrsRtpObjectCacheManager(sizeof(SrsRtpPacket2)); -SrsRtpObjectCacheManager* _srs_rtp_raw_cache = new SrsRtpObjectCacheManager(sizeof(SrsRtpRawPayload)); -SrsRtpObjectCacheManager* _srs_rtp_fua_cache = new SrsRtpObjectCacheManager(sizeof(SrsRtpFUAPayload2)); +// It SHOULD be thread-local, because it's safe to exchange objects between threads. +__thread SrsRtpObjectCacheManager* _srs_rtp_cache = NULL; +__thread SrsRtpObjectCacheManager* _srs_rtp_raw_cache = NULL; +__thread SrsRtpObjectCacheManager* _srs_rtp_fua_cache = NULL; -SrsRtpObjectCacheManager* _srs_rtp_msg_cache_buffers = new SrsRtpObjectCacheManager(sizeof(SrsSharedPtrMessage) + kRtpPacketSize); -SrsRtpObjectCacheManager* _srs_rtp_msg_cache_objs = new SrsRtpObjectCacheManager(sizeof(SrsSharedPtrMessage)); +// It SHOULD be thread-local, because it's safe to exchange objects between threads. +__thread SrsRtpObjectCacheManager* _srs_rtp_msg_cache_buffers = NULL; +__thread SrsRtpObjectCacheManager* _srs_rtp_msg_cache_objs = NULL; SrsRtpRawPayload::SrsRtpRawPayload() { diff --git a/trunk/src/kernel/srs_kernel_rtc_rtp.hpp b/trunk/src/kernel/srs_kernel_rtc_rtp.hpp index b00135e858..95961561d4 100644 --- a/trunk/src/kernel/srs_kernel_rtc_rtp.hpp +++ b/trunk/src/kernel/srs_kernel_rtc_rtp.hpp @@ -359,7 +359,7 @@ class SrsRtpPacket2 // For object cache manager to stat the object dropped. #include -extern SrsPps* _srs_pps_objs_drop; +extern __thread SrsPps* _srs_pps_objs_drop; // The RTP packet or message cache manager. template @@ -585,14 +585,16 @@ class SrsRtpFUAPayload2 : public ISrsRtpPayloader }; // For RTP packets cache. -extern SrsRtpObjectCacheManager* _srs_rtp_cache; -extern SrsRtpObjectCacheManager* _srs_rtp_raw_cache; -extern SrsRtpObjectCacheManager* _srs_rtp_fua_cache; +// It SHOULD be thread-local, because it's safe to exchange objects between threads. +extern __thread SrsRtpObjectCacheManager* _srs_rtp_cache; +extern __thread SrsRtpObjectCacheManager* _srs_rtp_raw_cache; +extern __thread SrsRtpObjectCacheManager* _srs_rtp_fua_cache; // For shared message cache, with payload. -extern SrsRtpObjectCacheManager* _srs_rtp_msg_cache_buffers; +// It SHOULD be thread-local, because it's safe to exchange objects between threads. +extern __thread SrsRtpObjectCacheManager* _srs_rtp_msg_cache_buffers; // For shared message cache, without payload. // Note that user must unwrap the shared message, before recycle it. -extern SrsRtpObjectCacheManager* _srs_rtp_msg_cache_objs; +extern __thread SrsRtpObjectCacheManager* _srs_rtp_msg_cache_objs; #endif diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 3282675129..1d16464fbd 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -128,6 +128,7 @@ srs_utime_t srs_get_system_startup_time() srs_gettimeofday_t _srs_gettimeofday = (srs_gettimeofday_t)::gettimeofday; #endif +// TODO: FIXME: It should be thread-local or thread-safe. srs_utime_t srs_update_system_time() { timeval now; diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index 9325e6e5e8..dfa92c8db9 100644 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -54,6 +54,7 @@ using namespace std; #include #include #include +#include #ifdef SRS_RTC #include #include @@ -65,28 +66,36 @@ using namespace std; // pre-declare srs_error_t run_directly_or_daemon(); -srs_error_t run_hybrid_server(); +srs_error_t run_in_thread_pool(); void show_macro_features(); // @global log and context. +// It SHOULD be thread-safe, because it use async log and thread-local buffer. ISrsLog* _srs_log = new SrsFileLog(); +// It SHOULD be thread-safe, because it use thread-local thread private data. ISrsContext* _srs_context = new SrsThreadContext(); // @global config object for app module. +// TODO: FIXME: It should be thread-local or thread-safe. SrsConfig* _srs_config = new SrsConfig(); // @global version of srs, which can grep keyword "XCORE" extern const char* _srs_version; -// @global main SRS server, for debugging -SrsServer* _srs_server = NULL; - /** * main entrance. */ srs_error_t do_main(int argc, char** argv) { srs_error_t err = srs_success; - + + // Initialize thread-local variables. + if ((err = SrsThreadPool::setup()) != srs_success) { + return srs_error_wrap(err, "thread init"); + } + + // For background context id. + _srs_context->set_id(_srs_context->generate_id()); + // TODO: support both little and big endian. srs_assert(srs_is_little_endian()); @@ -130,6 +139,11 @@ srs_error_t do_main(int argc, char** argv) if ((err = _srs_config->initialize_cwd()) != srs_success) { return srs_error_wrap(err, "config cwd"); } + + // We must initialize the async log manager before log init. + if ((err = _srs_async_log->initialize()) != srs_success) { + return srs_error_wrap(err, "init async log"); + } // config parsed, initialize log. if ((err = _srs_log->initialize()) != srs_success) { @@ -214,14 +228,16 @@ srs_error_t do_main(int argc, char** argv) return err; } -int main(int argc, char** argv) { - // For background context id. - _srs_context->set_id(_srs_context->generate_id()); - +int main(int argc, char** argv) +{ srs_error_t err = do_main(argc, argv); + // Because we are exiting, and it's impossible to notify the async log thread + // to write the error log, so we print to stderr instead. + // TODO: FIXME: Should we flush the async log cache? if (err != srs_success) { - srs_error("Failed, %s", srs_error_desc(err).c_str()); + fprintf(stderr, "Failed, ts=%" PRId64 ", err is %s\n", srs_update_system_time(), + srs_error_desc(err).c_str()); } int ret = srs_error_code(err); @@ -415,7 +431,7 @@ srs_error_t run_directly_or_daemon() // If not daemon, directly run hybrid server. if (!run_as_daemon) { - if ((err = run_hybrid_server()) != srs_success) { + if ((err = run_in_thread_pool()) != srs_success) { return srs_error_wrap(err, "run hybrid"); } return srs_success; @@ -452,17 +468,53 @@ srs_error_t run_directly_or_daemon() // son srs_trace("son(daemon) process running."); - if ((err = run_hybrid_server()) != srs_success) { + if ((err = run_in_thread_pool()) != srs_success) { return srs_error_wrap(err, "daemon run hybrid"); } return err; } -srs_error_t run_hybrid_server() +srs_error_t run_hybrid_server(void* arg); +srs_error_t run_in_thread_pool() +{ + srs_error_t err = srs_success; + + if ((err = _srs_thread_pool->initialize()) != srs_success) { + return srs_error_wrap(err, "init thread pool"); + } + + // After all init(log, async log manager, thread pool), now we can start to + // run the log manager thread. + if ((err = _srs_thread_pool->execute("log", SrsAsyncLogManager::start, _srs_async_log)) != srs_success) { + return srs_error_wrap(err, "start async log thread"); + } + + // Start a number of hybrid service threads. + int hybrids = _srs_config->get_threads_hybrids(); + for (int stream_index = 0; stream_index < hybrids; stream_index++) { + // TODO: FIXME: Change the thread name for debugging? + // Start the hybrid service worker thread, for RTMP and RTC server, etc. + if ((err = _srs_thread_pool->execute("hybrid", run_hybrid_server, (void*)(uint64_t)stream_index)) != srs_success) { + return srs_error_wrap(err, "start hybrid server %d thread", stream_index); + } + } + + srs_trace("Pool: Start threads hybrids=%d", hybrids); + + return _srs_thread_pool->run(); +} + +// TODO: FIXME: Extract to hybrid server. +srs_error_t run_hybrid_server(void* arg) { srs_error_t err = srs_success; + // The config index for hybrid/stream server. + int stream_index = (int)(uint64_t)arg; + _srs_hybrid->set_stream_index(stream_index); + srs_assert(_srs_hybrid->stream_index() >= 0); + // Create servers and register them. _srs_hybrid->register_server(new SrsServerAdapter()); @@ -475,15 +527,23 @@ srs_error_t run_hybrid_server() #endif // Do some system initialize. + // TODO: FIXME: If fail, for example, acquire pid fail, should exit. if ((err = _srs_hybrid->initialize()) != srs_success) { return srs_error_wrap(err, "hybrid initialize"); } + // Initialize the circuit breaker, which depends on hybrid timer. + // TODO: Enable the circuit breaker for API and LOG threads. + if ((err = _srs_circuit_breaker->initialize()) != srs_success) { + return srs_error_wrap(err, "circuit breaker init"); + } + // Should run util hybrid servers all done. if ((err = _srs_hybrid->run()) != srs_success) { return srs_error_wrap(err, "hybrid run"); } + // TODO: FIXME: Crash if hybrid run fail, for example, listen failed. // After all done, stop and cleanup. _srs_hybrid->stop(); diff --git a/trunk/src/protocol/srs_service_log.cpp b/trunk/src/protocol/srs_service_log.cpp index ec52e2b5ae..f5c4e42df9 100644 --- a/trunk/src/protocol/srs_service_log.cpp +++ b/trunk/src/protocol/srs_service_log.cpp @@ -35,8 +35,8 @@ using namespace std; #include -SrsPps* _srs_pps_cids_get = new SrsPps(); -SrsPps* _srs_pps_cids_set = new SrsPps(); +__thread SrsPps* _srs_pps_cids_get = NULL; +__thread SrsPps* _srs_pps_cids_set = NULL; #define SRS_BASIC_LOG_SIZE 8192 @@ -54,6 +54,7 @@ SrsContextId SrsThreadContext::generate_id() return cid.set_value(srs_random_str(8)); } +// TODO: FIXME: It should be thread-local or thread-safe. static SrsContextId _srs_context_default; static int _srs_context_key = -1; void _srs_context_destructor(void* arg) @@ -134,10 +135,6 @@ srs_error_t SrsConsoleLog::initialize() return srs_success; } -void SrsConsoleLog::reopen() -{ -} - void SrsConsoleLog::verbose(const char* tag, SrsContextId context_id, const char* fmt, ...) { if (level > SrsLogLevelVerbose) { @@ -248,9 +245,9 @@ bool srs_log_header(char* buffer, int size, bool utc, bool dangerous, const char { // clock time timeval tv; - if (gettimeofday(&tv, NULL) == -1) { - return false; - } + srs_utime_t now = srs_update_system_time(); + tv.tv_sec = now / SRS_UTIME_SECONDS; + tv.tv_usec = now % SRS_UTIME_SECONDS; // to calendar time struct tm* tm; diff --git a/trunk/src/protocol/srs_service_log.hpp b/trunk/src/protocol/srs_service_log.hpp index 0a11979d74..74ec715eb3 100644 --- a/trunk/src/protocol/srs_service_log.hpp +++ b/trunk/src/protocol/srs_service_log.hpp @@ -36,8 +36,6 @@ // which identify the client. class SrsThreadContext : public ISrsContext { -private: - std::map cache; public: SrsThreadContext(); virtual ~SrsThreadContext(); @@ -76,7 +74,6 @@ class SrsConsoleLog : public ISrsLog // Interface ISrsLog public: virtual srs_error_t initialize(); - virtual void reopen(); virtual void verbose(const char* tag, SrsContextId context_id, const char* fmt, ...); virtual void info(const char* tag, SrsContextId context_id, const char* fmt, ...); virtual void trace(const char* tag, SrsContextId context_id, const char* fmt, ...); diff --git a/trunk/src/protocol/srs_service_st.cpp b/trunk/src/protocol/srs_service_st.cpp index 61f2ce36ff..1c206e965a 100644 --- a/trunk/src/protocol/srs_service_st.cpp +++ b/trunk/src/protocol/srs_service_st.cpp @@ -70,6 +70,7 @@ srs_error_t srs_st_init() return srs_error_new(ERROR_ST_SET_EPOLL, "st enable st failed, current is %s", st_get_eventsys_name()); } + // TODO: FIXME: Pass the cid of thread. // Before ST init, we might have already initialized the background cid. SrsContextId cid = _srs_context->get_id(); if (cid.empty()) { @@ -456,6 +457,11 @@ ssize_t srs_read(srs_netfd_t stfd, void *buf, size_t nbyte, srs_utime_t timeout) return st_read((st_netfd_t)stfd, buf, nbyte, (st_utime_t)timeout); } +ssize_t srs_write(srs_netfd_t stfd, const void *buf, size_t nbyte, srs_utime_t timeout) +{ + return st_write((st_netfd_t)stfd, buf, nbyte, (st_utime_t)timeout); +} + bool srs_is_never_timeout(srs_utime_t tm) { return tm == SRS_UTIME_NO_TIMEOUT; diff --git a/trunk/src/protocol/srs_service_st.hpp b/trunk/src/protocol/srs_service_st.hpp index a82aa41d1f..f5de3cd0c7 100644 --- a/trunk/src/protocol/srs_service_st.hpp +++ b/trunk/src/protocol/srs_service_st.hpp @@ -102,10 +102,12 @@ extern int srs_sendmsg(srs_netfd_t stfd, const struct msghdr *msg, int flags, sr extern srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout); extern ssize_t srs_read(srs_netfd_t stfd, void *buf, size_t nbyte, srs_utime_t timeout); +extern ssize_t srs_write(srs_netfd_t stfd, const void *buf, size_t nbyte, srs_utime_t timeout); extern bool srs_is_never_timeout(srs_utime_t tm); // The mutex locker. +// TODO: FIXME: Rename _SRS to _srs #define SrsLocker(instance) \ impl__SrsLocker _SRS_free_##instance(&instance) diff --git a/trunk/src/protocol/srs_service_utility.cpp b/trunk/src/protocol/srs_service_utility.cpp index 0069a64bc5..8bea8d2570 100644 --- a/trunk/src/protocol/srs_service_utility.cpp +++ b/trunk/src/protocol/srs_service_utility.cpp @@ -73,6 +73,7 @@ bool srs_is_digit_number(string str) return v / powv >= 1 && v / powv <= 9; } +// TODO: FIXME: It should be thread-local or thread-safe. // we detect all network device as internet or intranet device, by its ip address. // key is device name, for instance, eth0 // value is whether internet, for instance, true. diff --git a/trunk/src/utest/srs_utest.cpp b/trunk/src/utest/srs_utest.cpp index 4110aa6092..8770d892d3 100644 --- a/trunk/src/utest/srs_utest.cpp +++ b/trunk/src/utest/srs_utest.cpp @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #include using namespace std; @@ -51,11 +52,13 @@ bool _srs_in_docker = false; #include // Initialize global settings. -srs_error_t prepare_main() { +srs_error_t prepare_main() +{ srs_error_t err = srs_success; - if ((err = srs_st_init()) != srs_success) { - return srs_error_wrap(err, "init st"); + // Initialize thread-local variables. + if ((err = SrsThreadPool::setup()) != srs_success) { + return srs_error_wrap(err, "thread init"); } if ((err = _srs_rtc_dtls_certificate->initialize()) != srs_success) { @@ -70,7 +73,8 @@ srs_error_t prepare_main() { // 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) { +GTEST_API_ int main(int argc, char **argv) +{ srs_error_t err = srs_success; if ((err = prepare_main()) != srs_success) { diff --git a/trunk/src/utest/srs_utest_reload.cpp b/trunk/src/utest/srs_utest_reload.cpp index 08c5dada96..d3779ba821 100644 --- a/trunk/src/utest/srs_utest_reload.cpp +++ b/trunk/src/utest/srs_utest_reload.cpp @@ -372,69 +372,6 @@ VOID TEST(ConfigReloadTest, ReloadPid) handler.reset(); } -VOID TEST(ConfigReloadTest, ReloadLogTank) -{ - MockReloadHandler handler; - MockSrsReloadConfig conf; - - conf.subscribe(&handler); - EXPECT_TRUE(ERROR_SUCCESS == conf.parse(_MIN_OK_CONF"srs_log_tank console;")); - EXPECT_TRUE(ERROR_SUCCESS == conf.do_reload(_MIN_OK_CONF"srs_log_tank console;")); - EXPECT_TRUE(handler.all_false()); - handler.reset(); - - EXPECT_TRUE(ERROR_SUCCESS == conf.do_reload(_MIN_OK_CONF"srs_log_tank file;")); - EXPECT_TRUE(handler.log_tank_reloaded); - EXPECT_EQ(1, handler.count_true()); - handler.reset(); - - EXPECT_TRUE(ERROR_SUCCESS == conf.do_reload(_MIN_OK_CONF"srs_log_tank console;")); - EXPECT_EQ(1, handler.count_true()); - handler.reset(); -} - -VOID TEST(ConfigReloadTest, ReloadLogLevel) -{ - MockReloadHandler handler; - MockSrsReloadConfig conf; - - conf.subscribe(&handler); - EXPECT_TRUE(ERROR_SUCCESS == conf.parse(_MIN_OK_CONF"srs_log_level trace;")); - EXPECT_TRUE(ERROR_SUCCESS == conf.do_reload(_MIN_OK_CONF"srs_log_level trace;")); - EXPECT_TRUE(handler.all_false()); - handler.reset(); - - EXPECT_TRUE(ERROR_SUCCESS == conf.do_reload(_MIN_OK_CONF"srs_log_level warn;")); - EXPECT_TRUE(handler.log_level_reloaded); - EXPECT_EQ(1, handler.count_true()); - handler.reset(); - - EXPECT_TRUE(ERROR_SUCCESS == conf.do_reload(_MIN_OK_CONF"srs_log_level trace;")); - EXPECT_EQ(1, handler.count_true()); - handler.reset(); -} - -VOID TEST(ConfigReloadTest, ReloadLogFile) -{ - MockReloadHandler handler; - MockSrsReloadConfig conf; - - conf.subscribe(&handler); - EXPECT_TRUE(ERROR_SUCCESS == conf.parse(_MIN_OK_CONF"srs_log_file srs.log;")); - EXPECT_TRUE(ERROR_SUCCESS == conf.do_reload(_MIN_OK_CONF"srs_log_file srs.log;")); - EXPECT_TRUE(handler.all_false()); - handler.reset(); - - EXPECT_TRUE(ERROR_SUCCESS == conf.do_reload(_MIN_OK_CONF"srs_log_file srs1.log;")); - EXPECT_TRUE(handler.log_file_reloaded); - EXPECT_EQ(1, handler.count_true()); - handler.reset(); - - EXPECT_TRUE(ERROR_SUCCESS == conf.do_reload(_MIN_OK_CONF"srs_log_file srs.log;")); - EXPECT_EQ(1, handler.count_true()); - handler.reset(); -} - VOID TEST(ConfigReloadTest, ReloadPithyPrint) { MockReloadHandler handler;