1
0
Fork 0
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:
Jacob Su 2024-10-15 17:52:17 +08:00 committed by GitHub
parent 0de887d374
commit e7d78462fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 168 additions and 15 deletions

View file

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
#define VERSION_REVISION 16
#define VERSION_REVISION 17
#endif

View 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);
}
}

View 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