From 2a10f978cabaa76517aa890cd1f8cd8fd961afc9 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 26 Apr 2021 09:39:38 +0800 Subject: [PATCH] Threads: Support Hybrid+API+LOG threads commit Remove SEND/RECV/SRTP threads. Enable generate_streams by default. Support disable circuit breaker. commit 6c83e89ca634ad91e6a4db9aee480759f74f2ceb Author: winlin Date: Mon Apr 26 09:38:19 2021 +0800 Threads: Refine circuit breaker commit 614a78110eb7b723f0081c6409d8e54c151b4e79 Author: winlin Date: Sun Apr 25 19:52:13 2021 +0800 Threads-Hybrid: Fix rebase bugs commit 9c845c5af1b28158c9047e7733b0528fb7f34394 Author: winlin Date: Wed Apr 7 15:53:14 2021 +0800 Threads-Hybrid: Support multiple hybrid threads, 5.0.3 1. Support multiple hybrid threads. 2. Update benchmark data for 1/4/8/32 CPUs. 3. Update benchmark for Janus. commit 10edbb5d00e28c52c9d5bcd79ca9a91ac139b835 Author: winlin Date: Fri Apr 9 11:45:59 2021 +0800 Threads-Hybrid: Always response channel with error information commit 5074a4d444735644093e36fe9aaa9c7faf7b5856 Author: winlin Date: Fri Apr 9 11:21:45 2021 +0800 Threads-Hybrid: Use ST wait and IO for RECV/SEND thread. commit c8f2ff0b532f819c03189c1bf3d4ced67af22e62 Author: winlin Date: Fri Apr 9 10:21:05 2021 +0800 Threads-Hybrid: Merge api to master thread. commit 50f03ad7723c267b6082aaeaba8ee545729281b3 Author: winlin Date: Wed Apr 7 13:26:04 2021 +0800 Threads-Hybrid: Config auto generate stream config for hybrids. commit 11aada14a243e2a12405aa26395bb33fce676184 Author: winlin Date: Tue Apr 6 20:00:16 2021 +0800 Threads-Hybrid: Process api request one by one commit 963a0fa4b0b80b841a218ca95099dea744912502 Author: winlin Date: Tue Apr 6 19:46:53 2021 +0800 Threads-Hybrid: Change pps stat to thread-local. commit 9eb9b195f0dcbc7be21fd5dbc7c8cec440d0aaa3 Author: winlin Date: Tue Apr 6 15:21:48 2021 +0800 Threads-Hybrid: Move acquire pid file from hybrid to pool. commit 97f6684647a2634eeba8445c9a4b728084617d24 Author: winlin Date: Tue Apr 6 12:53:17 2021 +0800 Threads-Hybrid: Schedule connection to the sample hybrid by url commit eab4f8974c33a38dfde610db19b04ae0174a068e Author: winlin Date: Tue Apr 6 12:20:38 2021 +0800 Threads-Hybrid: Change global variables to thread-local or with lock 1. Thread-Safe: Config subscribes, subscribe or unsubscribe. 2. Global-Shared: Async SRTP/RECV/SEND/Log use thread-safe objects. 3. Global-Shared: SRTP and DTLS certificate, without critical data. 4. Thread-Local: Blackhole, ResourceManager, StreamManager, ObjectCache, by design. 5. Global-Shared: Log and context, which use thread-safe objects. 6. Thread-Local: Pithy print for each thread. commit 7dbac15156b3948667518d566fa7339e74b7dbbf Author: winlin Date: Tue Apr 6 09:10:16 2021 +0800 Threads-Hybrid: Add TODO as be thread-local commit babe8e67876ac734e1020c3949fda778335033d1 Author: winlin Date: Tue Apr 6 08:22:48 2021 +0800 Threads-Hybrid: Start multiple hybrid threads commit 336795604c734874372f040cd247337e9602bd9e Author: winlin Date: Tue Apr 6 07:59:37 2021 +0800 Threads-Hybrid: Support mulitple hybrid/stream servers 1. For hybrid RTMP/HTTP/RTC servers. 2. Config hybrids in threads. 3. Get hybrid server config with index. commit 6e2de90d54095827014b75f15c2b18a2d2cb04bb Author: winlin Date: Mon Apr 5 21:48:25 2021 +0800 Threads-Hybrid: Support communicate between threads by chan and slot 1. Hybrid thread is responder, API thread is initiator. 2. Responder read message from initiator-slot, write message to responder-slot. 3. Initiator write message to initiator-slot, read message from responder-slot. 4. Responder start a coroutine to consume requests and response it. commit 281350da03bbafc268f5d3c7e26598a6d28d00fa Author: winlin Date: Mon Apr 5 18:53:32 2021 +0800 Threads-Hybrid: Extract pipe and pair to communicate between threads 1. Pipe can be open by any threads, because it's only os FDs. 2. If pipe is open read/write, it's associated by ST, so we MUST free it by the same thread. 3. If open pipe in one thread, it's ok to free it directly, without close pipe. 4. If open read in a thread, then open write in another thread, user MUST close it correctly. commit 40e59ef77db9306fe4690eb6b2cec4c43d788911 Author: winlin Date: Mon Apr 5 16:16:23 2021 +0800 Threads-Hybrid: Enable RTC play API with bugs commit 9f4fbdbd26be1ac3415aaa0008dca776a93f8b2e Author: winlin Date: Mon Apr 5 12:39:28 2021 +0800 Threads-Hybrid: Init ST for each threads 1. ST is thread-local now. 2. MUST init st for each threads. 3. Do it as early as possible. commit 2fa3aca436b6d6912e8d5308cdcb87c93ad42d5c Author: winlin Date: Mon Apr 5 10:22:13 2021 +0800 Threads-Hybrid: Extract API server and threads. commit dd7c7ad131415610b6684e020d6c6b8cd2c782a8 Author: winlin Date: Sun Apr 4 20:44:07 2021 +0800 Threads-Hybrid: Refine conf file commit 246d7f5883f580c673ed285f356dba75e992481c Author: winlin Date: Mon Apr 5 11:55:10 2021 +0800 Threads-Hybrid: Research for extern and __thread commit 1a0456a06bf7d0046d656a700a99e7ab016acbae Author: winlin Date: Fri Apr 9 22:44:56 2021 +0800 Threads: TODO: Sort the packets, to avoid NACK commit b11f958c6b433456bbe72def379b23948c3333b2 Author: winlin Date: Wed Mar 31 18:37:39 2021 +0800 Threads: Support multiple threads with locks, #2188. 5.0.2 1. Threads-Log: Use thread to write and reopen logs. 2. Threads-SRTP: Support decrypt RTP by async SRTP. 3. Threads-RECV: Support dedicate thread to recv UDP packets. 4. Threads: Support cpu affinity for threads. 5. Threads-RECV: Drop received packet if exceed max queue size. 6. Threads: Use coroutine to consume recv/srtp packets. 7. Threads: Support Circuit-Breaker to work in storms. 8. Threads-SRTP: Support async decrypt RTCP 9. Threads-SEND: Support async send UDP packets 10. Threads-SRTP: Use async encrypt SRTP packet 11. Threads-SEND/RECV: Bind handler to listener to support multiple ports. 12. Threads-RECV: Support tunnel for recv-srtp. 13. Threads-SEND: Support tunnel for srtp-send. 14. Threads: Support circuit-breaker dying threshold commit 8cf7ead19cc563e2458054c064ae332ff03ce896 Author: winlin Date: Tue Apr 6 20:54:36 2021 +0800 Threads: Enable threads support for openssl. commit 28d8f1a72c916147e84367592977454815b96797 Author: winlin Date: Wed Apr 7 10:29:18 2021 +0800 Threads: Support multiple SRTP/SEND/RECV threads. 1. Move received and cooked packets queue to thread entry, that is, each thread has its own queue. 2. In RECV thread, push received packet to queue of source thread, in thread listener. 3. In SRTP thread, push cooked packet to queue of source thread, in asyn SRTP task. 4. In hybrid thread, directly and only consume the packets of self thread. 5. Sync between SRTP task by lock. commit a505b6bcd12da67e15bc27d51afba182a87f11d3 Author: winlin Date: Mon Apr 5 16:17:08 2021 +0800 Threads: Directly use hybrid thread to consume messages. commit 14bc7bca27f2bfccfd34552049ae1fe974b3fbea Author: winlin Date: Tue Mar 30 16:38:30 2021 +0800 Threads: Keep alive when got RTP plaintext commit e15737fdf47172ee6a0ce9877bd78c8392ebdca5 Author: winlin Date: Mon Mar 29 17:18:04 2021 +0800 Threads: Support circuit-breaker dying threshold commit d6a92cbe34717b1ee81028ea8a306be27301609d Author: winlin Date: Tue Mar 30 14:14:26 2021 +0800 Threads-SEND: Support tunnel for srtp-send. commit d282ccd2480497ac0769374c9975ccaa6f240d11 Author: winlin Date: Mon Mar 29 12:02:41 2021 +0800 Threads-RECV: Support tunnel for recv-srtp. commit eb7ce7ff2cb929d95ef9f18ba486e3f09283cef7 Author: winlin Date: Mon Mar 22 19:09:04 2021 +0800 Threads-RECV: Reset the cache buffer when copy commit 1235b338a843a39454f8303076e1ca065006f01d Author: winlin Date: Mon Mar 22 18:45:12 2021 +0800 Threads-SEND/RECV: Bind handler to listener to support multiple ports. commit 615da26521a76a12dca488d5d98824cdabf339c9 Author: winlin Date: Fri Mar 19 18:52:11 2021 +0800 Threads-SRTP: Use async encrypt SRTP packet 1. Async SRTP support protect RTCP. 2. Send packet ignore when encrypt size is 0. 3. Callback to send packet if encrypt done. commit a26b9260a960302e3c053d31f3b4bad6f67c697a Author: winlin Date: Fri Mar 19 18:43:08 2021 +0800 Threads-SRTP: Use async encrypt SRTP packet 1. Async SRTP support protect RTP. 2. Send packet ignore when encrypt size is 0. 3. Callback to send packet if encrypt done. commit 56ffc289565b64243ca68d9c7c05898922a2db31 Author: winlin Date: Fri Mar 19 18:03:40 2021 +0800 Threads-SEND: Support async send UDP packets 1. Support async send UDP by SrsAsyncSendManager. 2. Copy UDP packet by SrsAsyncUdpPacket. 3. Support SrsUdpMuxSocket raw sendto. 4. Config the async send by async_send. commit b0800f546394c119c23c3ec4c27e71993af625b2 Author: winlin Date: Fri Mar 19 16:16:09 2021 +0800 Threads-SRTP: Support async decrypt RTCP 1. SrsAsyncSRTP support unprotect_rtcp packet. 2. Extract on_rtcp_plaintext from on_rtcp. commit 949c80ec55d5ba1e0a91a6476128852c96a48806 Author: winlin Date: Fri Mar 19 14:58:34 2021 +0800 Threads-RECV: Change UDP recv max size from 6k to 1500 bytes. commit 198dca589d55e1ea56ed4d75c52d802e2d064bf6 Author: winlin Date: Fri Mar 19 13:48:36 2021 +0800 Threads: Merge recv and srtp consume to one timer. commit 57b771a4ae15e541dc235e0adaf56f5102081176 Author: winlin Date: Fri Mar 19 11:30:46 2021 +0800 Threads: Refine variables and do dispose 1. Rename packets to srtp or received packets. 2. Add task dispose API, cleanup in future. 3. If got packets before init AsyncSRTP, return error. 4. Never free the SRTPTask, dispose it instead. commit 9a05d241b8700855e13de1cf70be503767bd6ae3 Author: winlin Date: Thu Mar 18 17:33:51 2021 +0800 Threads: Support Circuit-Breaker to work in storms. 1. Config the recv queue, drop packet if exceed. 2. Config the high and critical threshold and pulse of water level. 3. If critical water level, disable NACK and TWCC. 4. If high water level, ignore for NACK insert and send. 5. Support read the CPU of thread. 6. Refine SrsPps to support r1s sample. commit 1c90497707568700167cb3b1929330cb1d460df5 Author: winlin Date: Wed Mar 17 13:53:55 2021 +0800 Threads: Use coroutine to consume recv/srtp packets. commit 957034ebeed8694fb61f4a3537c2ddaa5dac9acb Author: winlin Date: Wed Mar 17 08:08:17 2021 +0800 Threads: Use thread-local buffer for log 1. Call SrsThreadPool::setup() in main(),or each thread starting. 2. Initialize the thread-local object in SrsThreadPool::setup(). 3. Change shared log buffer to thread-local. commit 185359f7114b22dcee5314bbd27d5bb426bc91be Author: winlin Date: Tue Mar 16 20:29:35 2021 +0800 Threads-RECV: Show the dropped packets pps. commit 6fca41130d8823d7aa5867292ad674a70b6c8116 Author: winlin Date: Tue Mar 16 20:26:41 2021 +0800 Threads-RECV: Drop received packet if exceed max queue size. 1. Print the number of recv/srtp queue packets. 2. Drop packet if exceed max recv queue size. commit 9e554ca06095b573583b78e6d8b49c30fa3bb09a Author: winlin Date: Tue Mar 16 19:26:44 2021 +0800 Threads: Support cpu affinity for threads. 1. Config cpu_affinity in threads. 2. Default to not set the cpu affinity. 3. Support set by cpu range 0-63. commit fa4c9f9bfbccb8909a5393a96cf368d5ff065c4e Author: winlin Date: Tue Mar 16 11:42:32 2021 +0800 Threads: Set the threads name display in top. commit 1c6e941bf98de736028f2ca1d7d33015c3bf813c Author: winlin Date: Tue Mar 16 08:30:21 2021 +0800 Threads-RECV: Refine the stat for SNMP UDP recv/error 1. Remove the delta of _srs_snmp_udp_stat. 2. Use _srs_pps_rloss for receive loss rate. 3. Use _srs_pps_sloss for send loss rate. commit 85ea39d397b715ca6667a86e919d767eb9708a7d Author: winlin Date: Mon Mar 15 22:51:58 2021 +0800 Threads-RECV: Support dedicate thread to recv UDP packets. 1. Use SrsUdpMuxSocket::raw_recvfrom to read, without ST. 2. Start a UDP recv thread, to recv packets. 3. Consume UDP packets in RTC server timer. commit e3686a4b2cefb376104b8650df57b823faff54b9 Author: winlin Date: Mon Mar 15 21:15:28 2021 +0800 Threads: Fix bug for SRTP and Log thread nanosleep. commit 88165ba30bb1c3829237c67b82383c85f85f5e15 Author: winlin Date: Mon Mar 15 20:36:20 2021 +0800 Threads-SRTP: Support decrypt RTP by async SRTP. 1. Create dedicate thread for async srtp. 2. SrsSecurityTransport use async srtp if config on. 3. Cook SRTP packets in async srtp. 4. Consume cooked SRTP packets in RTC server timer. commit f068540eedaea43b0f74930b5034e37a3df2f6b3 Author: winlin Date: Sun Mar 14 21:39:36 2021 +0800 Threads-SRTP: Config and add files for the async-srtp 1. If configed the async srtp, use a new object. 2. Allow sync and async srtp, by config. commit 2367483e2ac24ed440cb914ebbc21c3f25e9c2ac Author: winlin Date: Sun Mar 14 22:11:02 2021 +0800 Threads-Log: Refine stat for sync wait, in log thread. commit 6a1d6a0cc91214087eb084fd4f3e7db99153a0bb Author: winlin Date: Sun Mar 14 18:43:54 2021 +0800 Threads-Log: Remove dual queue for sys logs. 1. It exists delay for multiple threads. 2. There is overlay for cache of coroutine queues. 3. Risk when other threads write logs. commit 6ddbc5fe611b3a15ab83c2eb09dc1cdef4d75d12 Author: winlin Date: Sun Mar 14 18:30:23 2021 +0800 Threads-Log: Refine comments for global variable. 1. Dual queue for async logs, exists risk. 2. Flush the logs is too slow, because it depends on logs and interval. commit 6718c3843e10bc747f1b67957b17ddfa1bf78189 Author: winlin Date: Sun Mar 14 10:42:09 2021 +0800 Threads-Log: Refine dual queue for log thread. 1. App/User controls the interval to flush coroutine-queue. 2. Use srs_update_system_time to get time for log. 3. Stat the thread sync in us, in SrsThreadPool. 4. Change default interval for thread to 5s. commit 37aee44d0800347f75d2a2b58cf31a55082e197f Author: winlin Date: Sun Mar 14 08:55:17 2021 +0800 Threads-Log: Support dual queue cache for async logs. 1. Create dual queue, the coroutine queue and thread queue. 2. The coroutine queue cache logs does not require lock. 3. When need to flush, flush the logs from coroutine-queue to thread-queue. 4. Finally, flush thread-queue to disk. commit 4918160d476d11adde8a5ad2a81b904769c9a467 Author: winlin Date: Sat Mar 13 07:09:56 2021 +0800 Threads-Log: Support thread-safe queue SrsThreadQueue. 1. Wrap std::vector to thread-safe queue. 2. Keep API compatible with std::vector. 3. SrsAsyncFileWriter use thread-safe queue instead. commit 3810f5ff484826d646fa1ed5b1de2dfc345746bf Author: winlin Date: Fri Mar 12 18:59:14 2021 +0800 Threads-Log: Remove utest for reload log configs. commit bf95fdfa8e338b89ec677dfa3b13615fcab85f8a Author: winlin Date: Fri Mar 12 18:48:03 2021 +0800 Threads-Log: Use thread to write and reopen logs. 1. Remove support for reload log configs. 2. Add config for thread pool cycle interval. 3. Add config for log flush interval. 4. Create a SrsAsyncLogManager to create writers. 5. Create a SrsAsyncFileWriter to write file async. commit 0cea382e114f22ae5ce00ca15076065fcf75bc4d Author: winlin Date: Fri Mar 12 12:02:36 2021 +0800 Threads-Log: Refine thread lock type to ERRORCHECK. 1. Set lock attribute type to PTHREAD_MUTEX_ERRORCHECK. 2. If dead lock, the pthread_mutex_lock return EDEADLK. 3. We assert fail if lock failed. commit 668c9c13c86d4f16a0db46e5d244cfb67da9011c Author: winlin Date: Fri Mar 12 08:45:10 2021 +0800 Threads-Log: Run hybrid server in thread. 1. Create thread when execute by thread pool. 2. The primordial thread check all threads status. 3. Have not complete the cleanup and stop. commit 831b77bc8d9d7b7659fc51d53cc4f573185afcce Author: winlin Date: Thu Mar 11 18:06:16 2021 +0800 Threads-Log: Refine API and main workflow. 1. Use SrsThreadPool to execute the hybrid server. 2. Right now, directly run in primordial thread, that is no threads. 3. Define the main APIs of SrsThreadPool. --- README.md | 66 +- trunk/auto/depends.sh | 8 +- trunk/conf/full.conf | 483 ++++--- trunk/conf/threads.conf | 37 + trunk/configure | 2 +- trunk/research/thread-model/.gitignore | 1 + trunk/research/thread-model/extern-extra.cpp | 13 + trunk/research/thread-model/extern-main.cpp | 36 + trunk/src/app/srs_app_config.cpp | 1041 ++++++++------ trunk/src/app/srs_app_config.hpp | 114 +- trunk/src/app/srs_app_conn.cpp | 8 +- trunk/src/app/srs_app_gb28181.hpp | 2 +- trunk/src/app/srs_app_hourglass.cpp | 22 +- trunk/src/app/srs_app_http_api.cpp | 38 - trunk/src/app/srs_app_hybrid.cpp | 255 ++-- trunk/src/app/srs_app_hybrid.hpp | 13 +- trunk/src/app/srs_app_listener.cpp | 55 +- trunk/src/app/srs_app_listener.hpp | 13 + trunk/src/app/srs_app_log.cpp | 184 +-- trunk/src/app/srs_app_log.hpp | 21 +- trunk/src/app/srs_app_pithy_print.cpp | 4 +- trunk/src/app/srs_app_reload.cpp | 20 - trunk/src/app/srs_app_reload.hpp | 4 - trunk/src/app/srs_app_rtc_api.cpp | 12 +- trunk/src/app/srs_app_rtc_api.hpp | 21 +- trunk/src/app/srs_app_rtc_conn.cpp | 139 +- trunk/src/app/srs_app_rtc_conn.hpp | 11 +- trunk/src/app/srs_app_rtc_dtls.hpp | 17 +- trunk/src/app/srs_app_rtc_queue.cpp | 21 + trunk/src/app/srs_app_rtc_server.cpp | 128 +- trunk/src/app/srs_app_rtc_server.hpp | 9 +- trunk/src/app/srs_app_rtc_source.cpp | 30 +- trunk/src/app/srs_app_rtc_source.hpp | 4 +- trunk/src/app/srs_app_server.cpp | 745 ++++++---- trunk/src/app/srs_app_server.hpp | 98 +- trunk/src/app/srs_app_source.cpp | 2 +- trunk/src/app/srs_app_source.hpp | 2 +- trunk/src/app/srs_app_threads.cpp | 1325 ++++++++++++++++++ trunk/src/app/srs_app_threads.hpp | 455 ++++++ trunk/src/app/srs_app_utility.cpp | 210 +-- trunk/src/app/srs_app_utility.hpp | 12 +- trunk/src/core/srs_core_version5.cpp | 2 +- trunk/src/core/srs_core_version5.hpp | 4 +- trunk/src/kernel/srs_kernel_error.hpp | 5 + trunk/src/kernel/srs_kernel_flv.cpp | 2 +- trunk/src/kernel/srs_kernel_kbps.cpp | 10 + trunk/src/kernel/srs_kernel_kbps.hpp | 5 +- trunk/src/kernel/srs_kernel_log.hpp | 6 +- trunk/src/kernel/srs_kernel_rtc_rtp.cpp | 24 +- trunk/src/kernel/srs_kernel_rtc_rtp.hpp | 14 +- trunk/src/kernel/srs_kernel_utility.cpp | 1 + trunk/src/main/srs_main_server.cpp | 86 +- trunk/src/protocol/srs_service_log.cpp | 15 +- trunk/src/protocol/srs_service_log.hpp | 3 - trunk/src/protocol/srs_service_st.cpp | 6 + trunk/src/protocol/srs_service_st.hpp | 2 + trunk/src/protocol/srs_service_utility.cpp | 1 + trunk/src/utest/srs_utest.cpp | 12 +- trunk/src/utest/srs_utest_reload.cpp | 63 - 59 files changed, 4188 insertions(+), 1754 deletions(-) create mode 100644 trunk/conf/threads.conf create mode 100644 trunk/research/thread-model/extern-extra.cpp create mode 100644 trunk/research/thread-model/extern-main.cpp create mode 100644 trunk/src/app/srs_app_threads.cpp create mode 100644 trunk/src/app/srs_app_threads.hpp 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;