mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
ST: Use clock_gettime to prevent time jumping backwards. v7.0.17 (#3979)
try to fix #3978 **Background** check #3978 **Research** I referred the Android platform's solution, because I have android background, and there is a loop to handle message inside android.ff007a03c0/core/java/android/os/Handler.java (L701-L706C6)
``` public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } ```59d9dc1f50/libutils/SystemClock.cpp (L37-L51)
``` /* * native public static long uptimeMillis(); */ int64_t uptimeMillis() { return nanoseconds_to_milliseconds(uptimeNanos()); } /* * public static native long uptimeNanos(); */ int64_t uptimeNanos() { return systemTime(SYSTEM_TIME_MONOTONIC); } ```59d9dc1f50/libutils/Timers.cpp (L32-L55)
``` #if defined(__linux__) nsecs_t systemTime(int clock) { checkClockId(clock); static constexpr clockid_t clocks[] = {CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID, CLOCK_BOOTTIME}; static_assert(clock_id_max == arraysize(clocks)); timespec t = {}; clock_gettime(clocks[clock], &t); return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec; } #else nsecs_t systemTime(int clock) { // TODO: is this ever called with anything but REALTIME on mac/windows? checkClockId(clock); // Clock support varies widely across hosts. Mac OS doesn't support // CLOCK_BOOTTIME (and doesn't even have clock_gettime until 10.12). // Windows is windows. timeval t = {}; gettimeofday(&t, nullptr); return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL; } #endif ``` For Linux system, we can use `clock_gettime` api, but it's first appeared in Mac OSX 10.12. `man clock_gettime` The requirement is to find an alternative way to get the timestamp in microsecond unit, but the `clock_gettime` get nanoseconds, the math formula is the nanoseconds / 1000 = microsecond. Then I check the performance of this api + math division. I used those code to check the `clock_gettime` performance. ``` #include <sys/time.h> #include <time.h> #include <stdio.h> #include <unistd.h> int main() { struct timeval tv; struct timespec ts; clock_t start; clock_t end; long t; while (1) { start = clock(); gettimeofday(&tv, NULL); end = clock(); printf("gettimeofday clock is %lu\n", end - start); printf("gettimeofday is %lld\n", (tv.tv_sec * 1000000LL + tv.tv_usec)); start = clock(); clock_gettime(CLOCK_MONOTONIC, &ts); t = ts.tv_sec * 1000000L + ts.tv_nsec / 1000L; end = clock(); printf("clock_monotonic clock is %lu\n", end - start); printf("clock_monotonic: seconds is %ld, nanoseconds is %ld, sum is %ld\n", ts.tv_sec, ts.tv_nsec, t); start = clock(); clock_gettime(CLOCK_MONOTONIC_RAW, &ts); t = ts.tv_sec * 1000000L + ts.tv_nsec / 1000L; end = clock(); printf("clock_monotonic_raw clock is %lu\n", end - start); printf("clock_monotonic_raw: nanoseconds is %ld, sum is %ld\n", ts.tv_nsec, t); sleep(3); } return 0; } ``` Here is output: env: Mac OS M2 chip. ``` gettimeofday clock is 11 gettimeofday is 1709775727153949 clock_monotonic clock is 2 clock_monotonic: seconds is 1525204, nanoseconds is 409453000, sum is 1525204409453 clock_monotonic_raw clock is 2 clock_monotonic_raw: nanoseconds is 770493000, sum is 1525222770493 ``` We can see the `clock_gettime` is faster than `gettimeofday`, so there are no performance risks. **MacOS solution** `clock_gettime` api only available until mac os 10.12, for the mac os older than 10.12, just keep the `gettimeofday`. check osx version in `auto/options.sh`, then add MACRO in `auto/depends.sh`, the MACRO is `MD_OSX_HAS_NO_CLOCK_GETTIME`. **CYGWIN** According to google search, it seems the `clock_gettime(CLOCK_MONOTONIC)` is not support well at least 10 years ago, but I didn't own an windows machine, so can't verify it. so keep win's solution. --------- Co-authored-by: winlin <winlinvip@gmail.com>
This commit is contained in:
parent
0de887d374
commit
e7d78462fe
9 changed files with 168 additions and 15 deletions
5
trunk/3rdparty/st-srs/Makefile
vendored
5
trunk/3rdparty/st-srs/Makefile
vendored
|
@ -185,11 +185,9 @@ endif
|
|||
# make EXTRA_CFLAGS=-UMD_HAVE_EPOLL <target>
|
||||
#
|
||||
# or to enable sendmmsg(2) support:
|
||||
#
|
||||
# make EXTRA_CFLAGS="-DMD_HAVE_SENDMMSG -D_GNU_SOURCE"
|
||||
#
|
||||
# or to enable stats for ST:
|
||||
#
|
||||
# make EXTRA_CFLAGS=-DDEBUG_STATS
|
||||
#
|
||||
# or cache the stack and reuse it:
|
||||
|
@ -201,6 +199,9 @@ endif
|
|||
# or enable support for asan:
|
||||
# make EXTRA_CFLAGS="-DMD_ASAN -fsanitize=address -fno-omit-frame-pointer"
|
||||
#
|
||||
# or to disable the clock_gettime for MacOS before 10.12, see https://github.com/ossrs/srs/issues/3978
|
||||
# make EXTRA_CFLAGS=-DMD_OSX_NO_CLOCK_GETTIME
|
||||
#
|
||||
# or enable the coverage for utest:
|
||||
# make UTEST_FLAGS="-fprofile-arcs -ftest-coverage"
|
||||
#
|
||||
|
|
31
trunk/3rdparty/st-srs/md.h
vendored
31
trunk/3rdparty/st-srs/md.h
vendored
|
@ -101,10 +101,21 @@ extern void _st_md_cxt_restore(_st_jmp_buf_t env, int val);
|
|||
#error Unknown CPU architecture
|
||||
#endif
|
||||
|
||||
#define MD_GET_UTIME() \
|
||||
struct timeval tv; \
|
||||
(void) gettimeofday(&tv, NULL); \
|
||||
return (tv.tv_sec * 1000000LL + tv.tv_usec)
|
||||
#if defined (MD_OSX_NO_CLOCK_GETTIME)
|
||||
#define MD_GET_UTIME() \
|
||||
struct timeval tv; \
|
||||
(void) gettimeofday(&tv, NULL); \
|
||||
return (tv.tv_sec * 1000000LL + tv.tv_usec)
|
||||
#else
|
||||
/*
|
||||
* https://github.com/ossrs/srs/issues/3978
|
||||
* use clock_gettime to get the timestamp in microseconds.
|
||||
*/
|
||||
#define MD_GET_UTIME() \
|
||||
struct timespec ts; \
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts); \
|
||||
return (ts.tv_sec * 1000000LL + ts.tv_nsec / 1000)
|
||||
#endif
|
||||
|
||||
#elif defined (LINUX)
|
||||
|
||||
|
@ -120,13 +131,13 @@ extern void _st_md_cxt_restore(_st_jmp_buf_t env, int val);
|
|||
#define MD_HAVE_SOCKLEN_T
|
||||
|
||||
/*
|
||||
* All architectures and flavors of linux have the gettimeofday
|
||||
* function but if you know of a faster way, use it.
|
||||
* https://github.com/ossrs/srs/issues/3978
|
||||
* use clock_gettime to get the timestamp in microseconds.
|
||||
*/
|
||||
#define MD_GET_UTIME() \
|
||||
struct timeval tv; \
|
||||
(void) gettimeofday(&tv, NULL); \
|
||||
return (tv.tv_sec * 1000000LL + tv.tv_usec)
|
||||
#define MD_GET_UTIME() \
|
||||
struct timespec ts; \
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts); \
|
||||
return (ts.tv_sec * 1000000LL + ts.tv_nsec / 1000)
|
||||
|
||||
#if defined(__i386__)
|
||||
#define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[4]))
|
||||
|
|
|
@ -266,6 +266,9 @@ fi
|
|||
# for osx, use darwin for st, donot use epoll.
|
||||
if [[ $SRS_OSX == YES ]]; then
|
||||
_ST_MAKE=darwin-debug && _ST_OBJ="DARWIN_`uname -r`_DBG"
|
||||
if [[ $SRS_OSX_HAS_CLOCK_GETTIME != YES ]]; then
|
||||
_ST_EXTRA_CFLAGS="$_ST_EXTRA_CFLAGS -DMD_OSX_NO_CLOCK_GETTIME"
|
||||
fi
|
||||
fi
|
||||
# for windows/cygwin
|
||||
if [[ $SRS_CYGWIN64 = YES ]]; then
|
||||
|
|
|
@ -112,6 +112,8 @@ SRS_CROSS_BUILD_HOST=
|
|||
SRS_CROSS_BUILD_PREFIX=
|
||||
# For cache build
|
||||
SRS_BUILD_CACHE=YES
|
||||
# Only support MacOS 10.12+ for clock_gettime, see https://github.com/ossrs/srs/issues/3978
|
||||
SRS_OSX_HAS_CLOCK_GETTIME=YES
|
||||
#
|
||||
#####################################################################################
|
||||
# Toolchain for cross-build on Ubuntu for ARM or MIPS.
|
||||
|
@ -150,7 +152,9 @@ function apply_system_options() {
|
|||
OS_IS_RISCV=$(gcc -dM -E - </dev/null |grep -q '#define __riscv 1' && echo YES)
|
||||
|
||||
# Set the os option automatically.
|
||||
if [[ $OS_IS_OSX == YES ]]; then SRS_OSX=YES; fi
|
||||
if [[ $OS_IS_OSX == YES ]]; then
|
||||
SRS_OSX=YES;
|
||||
fi
|
||||
if [[ $OS_IS_CYGWIN == YES ]]; then SRS_CYGWIN64=YES; fi
|
||||
|
||||
if [[ $OS_IS_OSX == YES ]]; then SRS_JOBS=$(sysctl -n hw.ncpu 2>/dev/null || echo 1); fi
|
||||
|
|
3
trunk/configure
vendored
3
trunk/configure
vendored
|
@ -464,7 +464,8 @@ if [[ $SRS_UTEST == YES ]]; then
|
|||
MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_kernel" "srs_utest_core"
|
||||
"srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload"
|
||||
"srs_utest_mp4" "srs_utest_service" "srs_utest_app" "srs_utest_rtc" "srs_utest_config2"
|
||||
"srs_utest_protocol" "srs_utest_protocol2" "srs_utest_kernel2" "srs_utest_protocol3")
|
||||
"srs_utest_protocol" "srs_utest_protocol2" "srs_utest_kernel2" "srs_utest_protocol3"
|
||||
"srs_utest_st")
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
MODULE_FILES+=("srs_utest_srt")
|
||||
fi
|
||||
|
|
|
@ -7,6 +7,7 @@ The changelog for SRS.
|
|||
<a name="v7-changes"></a>
|
||||
|
||||
## SRS 7.0 Changelog
|
||||
* v7.0, 2024-10-15, Merge [#3979](https://github.com/ossrs/srs/pull/3979): ST: Use clock_gettime to prevent time jumping backwards. v7.0.17 (#3979)
|
||||
* v7.0, 2024-09-09, Merge [#4158](https://github.com/ossrs/srs/pull/4158): Proxy: Support proxy server for SRS. v7.0.16 (#4158)
|
||||
* v7.0, 2024-09-09, Merge [#4171](https://github.com/ossrs/srs/pull/4171): Heartbeat: Report ports for proxy server. v7.0.15 (#4171)
|
||||
* v7.0, 2024-09-01, Merge [#4165](https://github.com/ossrs/srs/pull/4165): FLV: Refine source and http handler. v7.0.14 (#4165)
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
|
||||
#define VERSION_MAJOR 7
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 16
|
||||
#define VERSION_REVISION 17
|
||||
|
||||
#endif
|
117
trunk/src/utest/srs_utest_st.cpp
Normal file
117
trunk/src/utest/srs_utest_st.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// Copyright (c) 2013-2024 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
#include <srs_utest_st.hpp>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
VOID TEST(StTest, StUtimeInMicroseconds)
|
||||
{
|
||||
st_utime_t st_time_1 = st_utime();
|
||||
// sleep 1 microsecond
|
||||
#if !defined(SRS_CYGWIN64)
|
||||
usleep(1);
|
||||
#endif
|
||||
st_utime_t st_time_2 = st_utime();
|
||||
|
||||
EXPECT_GT(st_time_1, 0);
|
||||
EXPECT_GT(st_time_2, 0);
|
||||
EXPECT_GE(st_time_2, st_time_1);
|
||||
// st_time_2 - st_time_1 should be in range of [1, 100] microseconds
|
||||
EXPECT_GE(st_time_2 - st_time_1, 0);
|
||||
EXPECT_LE(st_time_2 - st_time_1, 100);
|
||||
}
|
||||
|
||||
static inline st_utime_t time_gettimeofday() {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (tv.tv_sec * 1000000LL + tv.tv_usec);
|
||||
}
|
||||
|
||||
VOID TEST(StTest, StUtimePerformance)
|
||||
{
|
||||
clock_t start;
|
||||
int gettimeofday_elapsed_time = 0;
|
||||
int st_utime_elapsed_time = 0;
|
||||
|
||||
// Both the st_utime(clock_gettime or gettimeofday) and gettimeofday's
|
||||
// elpased time to execute is dependence on whether it is the first time be called.
|
||||
// In general, the gettimeofday has better performance, but the gap between
|
||||
// them is really small, maybe less than 10 clock ~ 10 microseconds.
|
||||
|
||||
// check st_utime first, then gettimeofday
|
||||
{
|
||||
start = clock();
|
||||
st_utime_t t2 = st_utime();
|
||||
int elapsed_time = clock() - start;
|
||||
st_utime_elapsed_time += elapsed_time;
|
||||
EXPECT_GT(t2, 0);
|
||||
|
||||
start = clock();
|
||||
st_utime_t t1 = time_gettimeofday();
|
||||
elapsed_time = clock() - start;
|
||||
gettimeofday_elapsed_time += elapsed_time;
|
||||
EXPECT_GT(t1, 0);
|
||||
|
||||
|
||||
EXPECT_GE(gettimeofday_elapsed_time, 0);
|
||||
EXPECT_GE(st_utime_elapsed_time, 0);
|
||||
|
||||
// pass the test, if
|
||||
EXPECT_LT(gettimeofday_elapsed_time > st_utime_elapsed_time ?
|
||||
gettimeofday_elapsed_time - st_utime_elapsed_time :
|
||||
st_utime_elapsed_time - gettimeofday_elapsed_time, 10);
|
||||
}
|
||||
|
||||
// check gettimeofday first, then st_utime
|
||||
{
|
||||
start = clock();
|
||||
st_utime_t t1 = time_gettimeofday();
|
||||
int elapsed_time = clock() - start;
|
||||
gettimeofday_elapsed_time += elapsed_time;
|
||||
EXPECT_GT(t1, 0);
|
||||
|
||||
start = clock();
|
||||
st_utime_t t2 = st_utime();
|
||||
elapsed_time = clock() - start;
|
||||
st_utime_elapsed_time += elapsed_time;
|
||||
EXPECT_GT(t2, 0);
|
||||
|
||||
EXPECT_GE(gettimeofday_elapsed_time, 0);
|
||||
EXPECT_GE(st_utime_elapsed_time, 0);
|
||||
|
||||
EXPECT_LT(gettimeofday_elapsed_time > st_utime_elapsed_time ?
|
||||
gettimeofday_elapsed_time - st_utime_elapsed_time :
|
||||
st_utime_elapsed_time - gettimeofday_elapsed_time, 10);
|
||||
}
|
||||
|
||||
// compare st_utime & gettimeofday in a loop
|
||||
for (int i = 0; i < 100; i++) {
|
||||
start = clock();
|
||||
st_utime_t t2 = st_utime();
|
||||
int elapsed_time = clock() - start;
|
||||
st_utime_elapsed_time = elapsed_time;
|
||||
EXPECT_GT(t2, 0);
|
||||
usleep(1);
|
||||
|
||||
start = clock();
|
||||
st_utime_t t1 = time_gettimeofday();
|
||||
elapsed_time = clock() - start;
|
||||
gettimeofday_elapsed_time = elapsed_time;
|
||||
EXPECT_GT(t1, 0);
|
||||
usleep(1);
|
||||
|
||||
EXPECT_GE(gettimeofday_elapsed_time, 0);
|
||||
EXPECT_GE(st_utime_elapsed_time, 0);
|
||||
|
||||
EXPECT_LT(gettimeofday_elapsed_time > st_utime_elapsed_time ?
|
||||
gettimeofday_elapsed_time - st_utime_elapsed_time :
|
||||
st_utime_elapsed_time - gettimeofday_elapsed_time, 10);
|
||||
|
||||
}
|
||||
}
|
15
trunk/src/utest/srs_utest_st.hpp
Normal file
15
trunk/src/utest/srs_utest_st.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// Copyright (c) 2013-2024 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
|
||||
#ifndef SRS_UTEST_ST_HPP
|
||||
#define SRS_UTEST_ST_HPP
|
||||
|
||||
#include <srs_utest.hpp>
|
||||
|
||||
#include <st.h>
|
||||
|
||||
#endif // SRS_UTEST_ST_HPP
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue