mirror of https://github.com/ossrs/srs.git
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.pull/4160/headff007a03c0/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>
parent
0de887d374
commit
e7d78462fe
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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…
Reference in New Issue