Skip to content

Commit

Permalink
Fix #429, update OSAL code to use time accessors
Browse files Browse the repository at this point in the history
Do not access members of OS_time_t directly, instead
use conversion/accessor inline functions to get the
desired value.

Update the "stat" structure output by OS_stat to use
the OS_time_t struct instead of int32, and update
the OS_stat implemention to transfer the full resolution
if it supports it (POSIX.1-2008 or newer).
  • Loading branch information
jphickey committed Dec 31, 2020
1 parent d698a4d commit 9bd1055
Show file tree
Hide file tree
Showing 13 changed files with 331 additions and 66 deletions.
258 changes: 255 additions & 3 deletions src/os/inc/osapi-clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,26 @@
#include "osconfig.h"
#include "common_types.h"

/** @brief OSAL time */
/**
* @brief OSAL time interval structure
*
* This is used to represent a basic time interval.
*
* When used with OS_GetLocalTime/OS_SetLocalTime, this represents the
* interval from the OS's epoch point, typically 01 Jan 1970 00:00:00 UTC
* on systems that have a persistent real time clock (RTC), or the system
* boot time if there is no RTC available.
*
* Applications should not directly access fields within this structure,
* as the definition may change in future versions of OSAL. Instead,
* applications should use the accessor/conversion methods defined below.
*/
typedef struct
{
uint32 seconds;
uint32 microsecs;
} OS_time_t;


/** @defgroup OSAPIClock OSAL Real Time Clock APIs
* @{
*/
Expand Down Expand Up @@ -66,7 +78,247 @@ int32 OS_GetLocalTime(OS_time_t *time_struct);
*
* @return Set local time status, see @ref OSReturnCodes
*/
int32 OS_SetLocalTime(OS_time_t *time_struct);
int32 OS_SetLocalTime(const OS_time_t *time_struct);


/*-------------------------------------------------------------------------------------*/
/*
* Accessor / Unit Conversion routines for OS_time_t
*
* These routines allow the user to simply interpret OS_time_t intervals into
* in normalized units of whole seconds, milliseconds, microseconds, or nanoseconds.
*/
/*-------------------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------------------*/
/**
* @brief Get interval from an OS_time_t object normalized to whole number of seconds
*
* Extracts the number of whole seconds from a given OS_time_t object, discarding
* any fractional component.
*
* This may also replace a direct read of the "seconds" field from
* the OS_time_t object from previous versions of OSAL, where the
* structure was defined with separate seconds/microseconds fields.
*
* @sa OS_TimeGetMicrosecondsPart()
*
* @param[in] tm Time interval value
* @returns Whole number of seconds in time interval
*/
static inline int64 OS_TimeGetTotalSeconds(OS_time_t tm)
{
return (tm.seconds);
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Get interval from an OS_time_t object normalized to millisecond units
*
* Note this refers to the complete interval, not just the fractional part.
*
* @param[in] tm Time interval value
* @returns Whole number of milliseconds in time interval
*/
static inline int64 OS_TimeGetTotalMilliseconds(OS_time_t tm)
{
return (((int64)tm.seconds * 1000) + (tm.microsecs / 1000));
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Get interval from an OS_time_t object normalized to microsecond units
*
* Note this refers to the complete interval, not just the fractional part.
*
* @param[in] tm Time interval value
* @returns Whole number of microseconds in time interval
*/
static inline int64 OS_TimeGetTotalMicroseconds(OS_time_t tm)
{
return (((int64)tm.seconds * 1000000) + tm.microsecs);
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Get interval from an OS_time_t object normalized to nanosecond units
*
* Note this refers to the complete interval, not just the fractional part.
*
* @note There is no protection against overflow of the 64-bit return value.
* Applications must use caution to ensure that the interval does not exceed the
* representable range of a signed 64 bit integer - approximately 140 years.
*
* @param[in] tm Time interval value
* @returns Whole number of microseconds in time interval
*/
static inline int64 OS_TimeGetTotalNanoseconds(OS_time_t tm)
{
return (((int64)tm.seconds * 1000000000) + (tm.microsecs * 1000));
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Get subseconds portion (fractional part only) from an OS_time_t object
*
* Extracts the fractional part from a given OS_time_t object.
* Units returned are in ticks, not normalized to any standard time unit.
*
* @param[in] tm Time interval value
* @returns Fractional/subsecond portion of time interval in ticks
*/
static inline int64 OS_TimeGetFractionalPart(OS_time_t tm)
{
return (tm.microsecs);
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Get 32-bit normalized subseconds (fractional part only) from an OS_time_t object
*
* Extracts the fractional part from a given OS_time_t object normalized
* to units of (1 / (2^32)) sec. This is a base-2 fixed-point fractional value
* with the point left-justified in the 32-bit value (i.e. left of MSB).
*
* This is (mostly) compatible with the CFE "subseconds" value, where 0x80000000 represents
* exactly one half second, and 0 represents a full second.
*
* @param[in] tm Time interval value
* @returns Fractional/subsecond portion of time interval as 32-bit fixed point value
*/
static inline uint32 OS_TimeGetSubseconds32Part(OS_time_t tm)
{
/*
* This computation avoids a 32-bit left shift which may not be implemented.
*
* 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);
}


/*-------------------------------------------------------------------------------------*/
/**
* @brief Get milliseconds portion (fractional part only) from an OS_time_t object
*
* Extracts the fractional part from a given OS_time_t object normalized
* to units of milliseconds.
*
* @sa OS_TimeGetTotalSeconds()
*
* @param[in] tm Time interval value
* @returns Number of milliseconds in time interval
*/
static inline uint32 OS_TimeGetMillisecondsPart(OS_time_t tm)
{
return OS_TimeGetFractionalPart(tm) / 1000;
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Get microseconds portion (fractional part only) from an OS_time_t object
*
* Extracts the fractional part from a given OS_time_t object normalized
* to units of microseconds.
*
* This function may be used to adapt applications initially implemented
* using an older OSAL version where OS_time_t was a structure containing
* a "seconds" and "microsecs" field.
*
* This function will obtain a value that is compatible with the "microsecs" field of
* OS_time_t as it was defined in previous versions of OSAL, as well as the "tv_usec"
* field of POSIX-style "struct timeval" values.
*
* @sa OS_TimeGetTotalSeconds()
*
* @param[in] tm Time interval value
* @returns Number of microseconds in time interval
*/
static inline uint32 OS_TimeGetMicrosecondsPart(OS_time_t tm)
{
return OS_TimeGetFractionalPart(tm);
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Get nanoseconds portion (fractional part only) from an OS_time_t object
*
* Extracts the only number of nanoseconds from a given OS_time_t object.
*
* This function will obtain a value that is compatible with the "tv_nsec" field
* of POSIX-style "struct timespec" values.
*
* @sa OS_TimeGetTotalSeconds()
*
* @param[in] tm Time interval value
* @returns Number of nanoseconds in time interval
*/
static inline uint32 OS_TimeGetNanosecondsPart(OS_time_t tm)
{
return OS_TimeGetFractionalPart(tm) * 1000;
}

/**
* @brief Convert a number of seconds + nanoseconds into an OS_time_t interval
*
* @param[in] seconds Whole number of seconds
* @param[in] nanoseconds Number of nanoseconds (fractional part only)
* @returns The input arguments represented as an OS_time_t interval
*/
static inline OS_time_t OS_TimeInterval(int64 seconds, int32 nanoseconds)
{
OS_time_t result;
result.seconds = seconds;
result.microsecs = nanoseconds / 1000;
return result;
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Computes the sum of two time intervals
*
* @param[in] time1 The first interval
* @param[in] time2 The second interval
*
* @return The sum of the two intervals (time1 + time2)
*/
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;
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Computes the difference between two time intervals
*
* @param[in] time1 The first interval
* @param[in] time2 The second interval
*
* @return The difference of the two intervals (time1 - 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;
}


/**@}*/

#endif
11 changes: 6 additions & 5 deletions src/os/inc/osapi-file.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include "osconfig.h"
#include "common_types.h"
#include "osapi-clock.h"


/** @defgroup OSFileAccess OSAL File Access Option Defines
Expand Down Expand Up @@ -63,9 +64,9 @@ typedef struct
*/
typedef struct
{
uint32 FileModeBits;
int32 FileTime;
size_t FileSize;
uint32 FileModeBits;
OS_time_t FileTime;
size_t FileSize;
} os_fstat_t;

/**
Expand Down Expand Up @@ -96,8 +97,8 @@ enum
#define OS_FILESTAT_READ(x) ((x).FileModeBits & OS_FILESTAT_MODE_READ)
/** @brief Access file stat size field */
#define OS_FILESTAT_SIZE(x) ((x).FileSize)
/** @brief Access file stat time field */
#define OS_FILESTAT_TIME(x) ((x).FileTime)
/** @brief Access file stat time field as a whole number of seconds */
#define OS_FILESTAT_TIME(x) (OS_TimeGetTotalSeconds((x).FileTime))

/**
* @brief Flags that can be used with opening of a file (bitmask)
Expand Down
36 changes: 31 additions & 5 deletions src/os/portable/os-impl-posix-files.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#include "os-impl-files.h"
#include "os-shared-file.h"
Expand Down Expand Up @@ -133,18 +134,43 @@ int32 OS_FileOpen_Impl(const OS_object_token_t *token, const char *local_path, i
*-----------------------------------------------------------------*/
int32 OS_FileStat_Impl(const char *local_path, os_fstat_t *FileStats)
{
struct stat st;
mode_t readbits;
mode_t writebits;
mode_t execbits;
struct stat st;
mode_t readbits;
mode_t writebits;
mode_t execbits;
struct timespec filetime;

if (stat(local_path, &st) < 0)
{
return OS_ERROR;
}

FileStats->FileSize = st.st_size;
FileStats->FileTime = st.st_mtime;

/*
* NOTE: Traditional timestamps are only a whole number of seconds (time_t)
* POSIX.1-2008 expands this to have a full "struct timespec" with nanosecond
* resolution.
*
* GLIBC (and likely other C libraries that use similar feature selection)
* will expose this value based on _POSIX_C_SOURCE or _XOPEN_SOURCE minimum
* values. Otherwise this just falls back to standard 1-second resolution
* available via the "st_mtime" member.
*/
#if (_POSIX_C_SOURCE >= 200809L) || (_XOPEN_SOURCE >= 700)
/*
* Better - use the full resolution (seconds + nanoseconds) as specified in POSIX.1-2008
*/
filetime = st.st_mtim;
#else
/*
* Fallback - every POSIX-compliant implementation must expose "st_mtime" field.
*/
filetime.tv_sec = st.st_mtime;
filetime.tv_nsec = 0;
#endif

FileStats->FileTime = OS_TimeInterval(filetime.tv_sec, filetime.tv_nsec);

/* note that the "fst_mode" member is already zeroed by the caller */
if (S_ISDIR(st.st_mode))
Expand Down
9 changes: 4 additions & 5 deletions src/os/portable/os-impl-posix-gettime.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ int32 OS_GetLocalTime_Impl(OS_time_t *time_struct)

if (Status == 0)
{
time_struct->seconds = time.tv_sec;
time_struct->microsecs = time.tv_nsec / 1000;
ReturnCode = OS_SUCCESS;
*time_struct = OS_TimeInterval(time.tv_sec, time.tv_nsec);
ReturnCode = OS_SUCCESS;
}
else
{
Expand All @@ -100,8 +99,8 @@ int32 OS_SetLocalTime_Impl(const OS_time_t *time_struct)
int32 ReturnCode;
struct timespec time;

time.tv_sec = time_struct->seconds;
time.tv_nsec = (time_struct->microsecs * 1000);
time.tv_sec = OS_TimeGetTotalSeconds(*time_struct);
time.tv_nsec = OS_TimeGetNanosecondsPart(*time_struct);

Status = clock_settime(OSAL_GETTIME_SOURCE_CLOCK, &time);

Expand Down
2 changes: 1 addition & 1 deletion src/os/shared/src/osapi-clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ int32 OS_GetLocalTime(OS_time_t *time_struct)
* See description in API and header file for detail
*
*-----------------------------------------------------------------*/
int32 OS_SetLocalTime(OS_time_t *time_struct)
int32 OS_SetLocalTime(const OS_time_t *time_struct)
{
/* Check inputs */
OS_CHECK_POINTER(time_struct);
Expand Down
Loading

0 comments on commit 9bd1055

Please sign in to comment.