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 4, 2021
1 parent 779d3e3 commit 6a2ebed
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 39 deletions.
70 changes: 37 additions & 33 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 ((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 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 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 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 @@ -301,9 +321,9 @@ static inline OS_time_t OS_TimeAssembleFromNanoseconds(int64 seconds, uint32 nan
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 @@ -318,15 +338,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 @@ -340,15 +352,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 @@ -103,21 +103,21 @@ void Test_OS_TimeAccessConversions(void)
UtAssert_UINT32_EQ(OS_TimeGetTotalMicroseconds(t2), 2528888);

/* 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);

/* 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_TimeGetMillisecondsPart(t1), 234);
UtAssert_UINT32_EQ(OS_TimeGetMillisecondsPart(t2), 528);

UtAssert_UINT32_EQ(OS_TimeGetMicrosecondsPart(t1), 234567);
UtAssert_UINT32_EQ(OS_TimeGetMicrosecondsPart(t2), 528888);

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

/* Simple Add/Subtract */
t3 = OS_TimeAdd(t1, t2);
Expand Down

0 comments on commit 6a2ebed

Please sign in to comment.