Skip to content

Commit

Permalink
Fix #429, update OS_time_t definition to 64-bit ticks
Browse files Browse the repository at this point in the history
Use a single 64-bit tick counter as OS_time_t, rather than
a split 32 bit seconds + 32 bit microseconds counter.

This benefits in several ways:

- increases the timing precision by 10x (0.1us ticks)
- increases the representable range by 400x (+/-14000 yrs)
- simplifies addition/subtraction (no carry over)
- avoids "year 2038" bug w/32-bit timestamps
  • Loading branch information
jphickey committed Jan 14, 2021
1 parent bfca5b2 commit 7e34a2c
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 43 deletions.
78 changes: 41 additions & 37 deletions src/os/inc/osapi-clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,29 @@
*/
typedef struct
{
uint32 seconds;
uint32 microsecs;
int64 ticks; /**< Ticks elapsed since reference point */
} OS_time_t;


/**
* @brief Multipliers/divisors to convert ticks into standardized units
*
* Various fixed conversion factor constants used by the conversion routines
*
* A 100ns tick time allows max intervals of about +/- 14000 years in
* a 64-bit signed integer value.
*
* @note Applications should not directly use these values, but rather use
* conversion routines below to obtain standardized units (seconds/microseconds/etc).
*/
enum
{
OS_TIME_TICK_RESOLUTION_NS = 100,
OS_TIME_TICKS_PER_SECOND = 1000000000 / OS_TIME_TICK_RESOLUTION_NS,
OS_TIME_TICKS_PER_MSEC = 1000000 / OS_TIME_TICK_RESOLUTION_NS,
OS_TIME_TICKS_PER_USEC = 1000 / OS_TIME_TICK_RESOLUTION_NS
};

/** @defgroup OSAPIClock OSAL Real Time Clock APIs
* @{
*/
Expand Down Expand Up @@ -108,7 +127,7 @@ int32 OS_SetLocalTime(const OS_time_t *time_struct);
*/
static inline int64 OS_TimeGetTotalSeconds(OS_time_t tm)
{
return (tm.seconds);
return (tm.ticks / OS_TIME_TICKS_PER_SECOND);
}

/*-------------------------------------------------------------------------------------*/
Expand All @@ -122,7 +141,7 @@ static inline int64 OS_TimeGetTotalSeconds(OS_time_t tm)
*/
static inline int64 OS_TimeGetTotalMilliseconds(OS_time_t tm)
{
return (((int64)tm.seconds * 1000) + (tm.microsecs / 1000));
return (tm.ticks / OS_TIME_TICKS_PER_MSEC);
}

/*-------------------------------------------------------------------------------------*/
Expand All @@ -136,7 +155,7 @@ static inline int64 OS_TimeGetTotalMilliseconds(OS_time_t tm)
*/
static inline int64 OS_TimeGetTotalMicroseconds(OS_time_t tm)
{
return (((int64)tm.seconds * 1000000) + tm.microsecs);
return (tm.ticks / OS_TIME_TICKS_PER_USEC);
}

/*-------------------------------------------------------------------------------------*/
Expand All @@ -154,7 +173,7 @@ static inline int64 OS_TimeGetTotalMicroseconds(OS_time_t tm)
*/
static inline int64 OS_TimeGetTotalNanoseconds(OS_time_t tm)
{
return (((int64)tm.seconds * 1000000000) + (tm.microsecs * 1000));
return (tm.ticks * OS_TIME_TICK_RESOLUTION_NS);
}

/*-------------------------------------------------------------------------------------*/
Expand All @@ -169,7 +188,7 @@ static inline int64 OS_TimeGetTotalNanoseconds(OS_time_t tm)
*/
static inline int64 OS_TimeGetFractionalPart(OS_time_t tm)
{
return (tm.microsecs);
return (tm.ticks % OS_TIME_TICKS_PER_SECOND);
}

/*-------------------------------------------------------------------------------------*/
Expand All @@ -194,7 +213,8 @@ static inline uint32 OS_TimeGetSubsecondsPart(OS_time_t tm)
* It also must round up, otherwise this may result in a value one
* less than the original when converted back to usec again.
*/
return (((OS_TimeGetFractionalPart(tm) << 26) + 15624) / 15625);
int64 frac = (OS_TimeGetFractionalPart(tm) << 30) + (OS_TIME_TICKS_PER_SECOND >> 2);
return (uint32)((frac - 1) / (OS_TIME_TICKS_PER_SECOND >> 2));
}


Expand All @@ -212,7 +232,7 @@ static inline uint32 OS_TimeGetSubsecondsPart(OS_time_t tm)
*/
static inline uint32 OS_TimeGetMillisecondsPart(OS_time_t tm)
{
return OS_TimeGetFractionalPart(tm) / 1000;
return (uint32)OS_TimeGetFractionalPart(tm) / OS_TIME_TICKS_PER_MSEC;
}

/*-------------------------------------------------------------------------------------*/
Expand All @@ -237,7 +257,7 @@ static inline uint32 OS_TimeGetMillisecondsPart(OS_time_t tm)
*/
static inline uint32 OS_TimeGetMicrosecondsPart(OS_time_t tm)
{
return OS_TimeGetFractionalPart(tm);
return (uint32)OS_TimeGetFractionalPart(tm) / OS_TIME_TICKS_PER_USEC;
}

/*-------------------------------------------------------------------------------------*/
Expand All @@ -256,7 +276,7 @@ static inline uint32 OS_TimeGetMicrosecondsPart(OS_time_t tm)
*/
static inline uint32 OS_TimeGetNanosecondsPart(OS_time_t tm)
{
return OS_TimeGetFractionalPart(tm) * 1000;
return (uint32)OS_TimeGetFractionalPart(tm) * OS_TIME_TICK_RESOLUTION_NS;
}

/*-------------------------------------------------------------------------------------*/
Expand All @@ -278,8 +298,8 @@ static inline uint32 OS_TimeGetNanosecondsPart(OS_time_t tm)
static inline OS_time_t OS_TimeAssembleFromNanoseconds(int64 seconds, uint32 nanoseconds)
{
OS_time_t result;
result.seconds = seconds;
result.microsecs = nanoseconds / 1000;
result.ticks = seconds * OS_TIME_TICKS_PER_SECOND;
result.ticks += nanoseconds / OS_TIME_TICK_RESOLUTION_NS;
return result;
}

Expand All @@ -302,8 +322,8 @@ static inline OS_time_t OS_TimeAssembleFromNanoseconds(int64 seconds, uint32 nan
static inline OS_time_t OS_TimeAssembleFromMicroseconds(int64 seconds, uint32 microseconds)
{
OS_time_t result;
result.seconds = seconds;
result.microsecs = microseconds;
result.ticks = seconds * OS_TIME_TICKS_PER_SECOND;
result.ticks += microseconds * OS_TIME_TICKS_PER_USEC;
return result;
}

Expand All @@ -326,8 +346,8 @@ static inline OS_time_t OS_TimeAssembleFromMicroseconds(int64 seconds, uint32 mi
static inline OS_time_t OS_TimeAssembleFromMilliseconds(int64 seconds, uint32 milliseconds)
{
OS_time_t result;
result.seconds = seconds;
result.microsecs = milliseconds * 1000;
result.ticks = seconds * OS_TIME_TICKS_PER_SECOND;
result.ticks += milliseconds * OS_TIME_TICKS_PER_MSEC;
return result;
}

Expand All @@ -350,9 +370,9 @@ static inline OS_time_t OS_TimeAssembleFromMilliseconds(int64 seconds, uint32 mi
static inline OS_time_t OS_TimeAssembleFromSubseconds(int64 seconds, uint32 subseconds)
{
OS_time_t result;
result.seconds = seconds;
result.ticks = seconds * OS_TIME_TICKS_PER_SECOND;
/* this should not round in any way, as the 32-bit input value has higher precision */
result.microsecs = ((int64)subseconds * 15625) >> 26;
result.ticks += ((int64)subseconds * (OS_TIME_TICKS_PER_SECOND >> 2)) >> 30;
return result;
}

Expand All @@ -367,15 +387,7 @@ static inline OS_time_t OS_TimeAssembleFromSubseconds(int64 seconds, uint32 subs
*/
static inline OS_time_t OS_TimeAdd(OS_time_t time1, OS_time_t time2)
{
OS_time_t result = time1;
result.seconds += time2.seconds;
result.microsecs += time2.microsecs;
if (result.microsecs >= 1000000)
{
++result.seconds;
result.microsecs -= 1000000;
}
return result;
return ((OS_time_t) { time1.ticks + time2.ticks });
}

/*-------------------------------------------------------------------------------------*/
Expand All @@ -389,15 +401,7 @@ static inline OS_time_t OS_TimeAdd(OS_time_t time1, OS_time_t time2)
*/
static inline OS_time_t OS_TimeSubtract(OS_time_t time1, OS_time_t time2)
{
OS_time_t result = time1;
result.seconds -= time2.seconds;
result.microsecs -= time2.microsecs;
if (result.microsecs >= 1000000)
{
--result.seconds;
result.microsecs += 1000000;
}
return result;
return ((OS_time_t) { time1.ticks - time2.ticks });
}


Expand Down
12 changes: 6 additions & 6 deletions src/unit-test-coverage/shared/src/coveragetest-clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,14 @@ void Test_OS_TimeAccessConversions(void)
UtAssert_UINT32_EQ(OS_TimeGetTotalMicroseconds(t4), 1901000);

/* Note: Nanoseconds/Subseconds may not be exact due to limitations of OS_time_t resolution */
UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t1), 1234567000);
UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t2), 2528888000);
UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t1), 1234567800);
UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t2), 2528888800);
UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t3), 45678000);
UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t4), 1901000000);

/* These functions only return the fractional part, not the whole part */
UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t1), 0x3c0c953a);
UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t2), 0x87653438);
UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t1), 0x3c0ca2a6);
UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t2), 0x876541a4);
UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t3), 0x0bb18dad);
UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t4), 0xe6a7ef9e);

Expand All @@ -139,8 +139,8 @@ void Test_OS_TimeAccessConversions(void)
UtAssert_UINT32_EQ(OS_TimeGetMicrosecondsPart(t3), 45678);
UtAssert_UINT32_EQ(OS_TimeGetMicrosecondsPart(t4), 901000);

UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t1), 234567000);
UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t2), 528888000);
UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t1), 234567800);
UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t2), 528888800);
UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t3), 45678000);
UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t4), 901000000);

Expand Down

0 comments on commit 7e34a2c

Please sign in to comment.