Skip to content

Commit

Permalink
Fix #1455, High-resolution timed ops
Browse files Browse the repository at this point in the history
Add API calls equivalent to existing calls that use an absolute timeout
as opposed to a relative timeout.  The absolute timeout can support
resolution of 0.1 usec in the default configuration.

Internally this primarily affects the underlying call to select().

Note that per the definition of select() in POSIX, it uses a
"struct timeval" which has a resolution of 1 usec.
  • Loading branch information
jphickey committed May 20, 2024
1 parent 372ea65 commit 8dfdbda
Show file tree
Hide file tree
Showing 29 changed files with 681 additions and 189 deletions.
108 changes: 108 additions & 0 deletions src/os/inc/osapi-clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,29 @@ typedef struct
int64 ticks; /**< Ticks elapsed since reference point */
} OS_time_t;

/**
* @brief The maximum value for OS_time_t
*
* This is the largest positive (future) time that is representable
* in an OS_time_t value.
*/
#define OS_TIME_MAX ((OS_time_t) {INT64_MAX})

/**
* @brief The zero value for OS_time_t
*
* This is a reasonable initializer/placeholder value for an OS_time_t
*/
#define OS_TIME_ZERO ((OS_time_t) {0})

/**
* @brief The minimum value for OS_time_t
*
* This is the largest negative (past) time that is representable
* in an OS_time_t value.
*/
#define OS_TIME_MIN ((OS_time_t) {INT64_MIN})

/**
* @brief Multipliers/divisors to convert ticks into standardized units
*
Expand Down Expand Up @@ -102,6 +125,45 @@ int32 OS_GetLocalTime(OS_time_t *time_struct);
*/
int32 OS_SetLocalTime(const OS_time_t *time_struct);

/*-------------------------------------------------------------------------------------*/
/**
* @brief Gets an absolute time value relative to the current time
*
* This function adds the given interval, expressed in milliseconds, to the
* current clock and returns the result.
*
* @note This is intended to ease transitioning from a relative timeout value to
* and absolute timeout value. The result can be passed to any function
* that accepts an absolute timeout, to mimic the behavior of a relative timeout.
*
* @param[in] relative_msec A relative time interval, in milliseconds
*
* @returns Absolute time value after adding interval
*/
OS_time_t OS_TimeFromRelativeMilliseconds(int32 relative_msec);

/*-------------------------------------------------------------------------------------*/
/**
* @brief Gets a relative time value from an absolute time
*
* This function computes the number of milliseconds until the given
* absolute time value is reached in the system clock.
*
* @note This is intended to ease transitioning from a relative timeout value to
* and absolute timeout value. The result can be passed to any function
* that accepts a relative timeout, to mimic the behavior of an absolute timeout.
*
* The return value of this function is intended to be compatible with the relative
* timeout parameter of various OSAL APIs e.g. OS_TimedRead() / OS_TimedWrite()
*
* @param[in] time An absolute time value
*
* @returns Milliseconds until time value will be reached
* @retval OS_CHECK (0) if time is the current time or is in the past
* @retval OS_PEND (-1) if time is far in the future (not expressable as an int32)
*/
int32 OS_TimeToRelativeMilliseconds(OS_time_t time);

/*-------------------------------------------------------------------------------------*/
/*
* Accessor / Unit Conversion routines for OS_time_t
Expand Down Expand Up @@ -485,6 +547,52 @@ static inline OS_time_t OS_TimeSubtract(OS_time_t time1, OS_time_t time2)
return ostm;
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Checks if two time values are equal
*
* @param[in] time1 The first time value
* @param[in] time2 The second time value
*
* @retval true if the two values are equal
* @retval false if the two values are not equal
*/
static inline bool OS_TimeEqual(OS_time_t time1, OS_time_t time2)
{
return (time1.ticks == time2.ticks);
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Checks the sign of the time value
*
* @param[in] time The time to check
*
* @retval -1 if the time value is negative / below 0
* @retval 0 if the time value is 0
* @retval 1 if the time value is positive / above 0
*/
static inline int8_t OS_TimeGetSign(OS_time_t time)
{
return (time.ticks > 0) - (time.ticks < 0);
}

/*-------------------------------------------------------------------------------------*/
/**
* @brief Compares two time values
*
* @param[in] time1 The first time
* @param[in] time2 The second time
*
* @retval -1 if the time1 < time2
* @retval 0 if the times are equal
* @retval 1 if the time1 > time2
*/
static inline int8_t OS_TimeCompare(OS_time_t time1, OS_time_t time2)
{
return OS_TimeGetSign(OS_TimeSubtract(time1, time2));
}

/**@}*/

#endif /* OSAPI_CLOCK_H */
81 changes: 79 additions & 2 deletions src/os/inc/osapi-file.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,49 @@ int32 OS_write(osal_id_t filedes, const void *buffer, size_t nbytes);
* @param[in] filedes The handle ID to operate on
* @param[out] buffer Storage location for file data @nonnull
* @param[in] nbytes Maximum number of bytes to read @nonzero
* @param[in] timeout Maximum time to wait, in milliseconds (OS_PEND = forever)
* @param[in] abstime Absolute time at which this function should return, if no data is readable
*
* @returns Byte count on success or appropriate error code, see @ref OSReturnCodes
* @retval #OS_ERROR_TIMEOUT if no data became available during timeout period
* @retval #OS_ERR_INVALID_ID if the file descriptor passed in is invalid
* @retval #OS_ERR_INVALID_SIZE if the passed-in size is not valid
* @retval #OS_INVALID_POINTER if the passed-in buffer is not valid
* @retval 0 if at end of file/stream data
*/
int32 OS_AbsTimedRead(osal_id_t filedes, void *buffer, size_t nbytes, OS_time_t abstime);

/*-------------------------------------------------------------------------------------*/
/**
* @brief File/Stream input read with a timeout
*
* This implements a time-limited read and is primarily intended for use with
* sockets but may also work with any other stream-like resource that the underlying
* OS supports, such as pipes or special devices.
*
* If data is immediately available on the file/socket, this will return that data
* along with the actual number of bytes that were immediately available. It will
* not block.
*
* If the file position is at the end of file or end of stream data (e.g. if the remote
* end has closed the connection), then this function will immediately return 0 without
* blocking for the timeout period.
*
* If no data is immediately available, but the underlying resource/stream is still
* connected to a peer, this will wait up to the given timeout for additional
* data to appear. If no data appears within the timeout period, then this returns
* the #OS_ERROR_TIMEOUT status code. This allows the caller to differentiate
* an open (but idle) socket connection from a connection which has been closed
* by the remote peer.
*
* In all cases this will return successfully as soon as at least 1 byte of actual
* data is available. It will not attempt to read the entire input buffer.
*
* If an EOF condition occurs prior to timeout, this function returns zero.
*
* @param[in] filedes The handle ID to operate on
* @param[out] buffer Storage location for file data @nonnull
* @param[in] nbytes Maximum number of bytes to read @nonzero
* @param[in] timeout Maximum time to wait, in milliseconds, relative to current time (OS_PEND = forever)
*
* @returns Byte count on success or appropriate error code, see @ref OSReturnCodes
* @retval #OS_ERROR_TIMEOUT if no data became available during timeout period
Expand Down Expand Up @@ -272,7 +314,42 @@ int32 OS_TimedRead(osal_id_t filedes, void *buffer, size_t nbytes, int32 timeout
* @param[in] filedes The handle ID to operate on
* @param[in] buffer Source location for file data @nonnull
* @param[in] nbytes Maximum number of bytes to read @nonzero
* @param[in] timeout Maximum time to wait, in milliseconds (OS_PEND = forever)
* @param[in] abstime Absolute time at which this function should return, if no data is readable
*
* @return A non-negative byte count or appropriate error code, see @ref OSReturnCodes
* @retval #OS_ERROR_TIMEOUT if no data became available during timeout period
* @retval #OS_ERR_INVALID_ID if the file descriptor passed in is invalid
* @retval #OS_ERR_INVALID_SIZE if the passed-in size is not valid
* @retval #OS_INVALID_POINTER if the passed-in buffer is not valid
* @retval 0 if file/stream cannot accept any more data
*/
int32 OS_AbsTimedWrite(osal_id_t filedes, const void *buffer, size_t nbytes, OS_time_t abstime);

/*-------------------------------------------------------------------------------------*/
/**
* @brief File/Stream output write with a timeout
*
* This implements a time-limited write and is primarily intended for use with
* sockets but may also work with any other stream-like resource that the underlying
* OS supports.
*
* If output buffer space is immediately available on the file/socket, this will
* place data into the buffer and return the actual number of bytes that were
* queued for output. It will not block.
*
* If no output buffer space is immediately available, this will wait up to the
* given timeout for space to become available. If no space becomes available within
* the timeout period, then this returns an error code (not zero).
*
* In all cases this will return successfully as soon as at least 1 byte of actual
* data is output. It will _not_ attempt to write the entire output buffer.
*
* If an EOF condition occurs prior to timeout, this function returns zero.
*
* @param[in] filedes The handle ID to operate on
* @param[in] buffer Source location for file data @nonnull
* @param[in] nbytes Maximum number of bytes to read @nonzero
* @param[in] timeout Maximum time to wait, in milliseconds, relative to current time (OS_PEND = forever)
*
* @return A non-negative byte count or appropriate error code, see @ref OSReturnCodes
* @retval #OS_ERROR_TIMEOUT if no data became available during timeout period
Expand Down
35 changes: 34 additions & 1 deletion src/os/inc/osapi-sockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
/* NOTE - osconfig.h may optionally specify the value for OS_SOCADDR_MAX_LEN */
#include "osconfig.h"
#include "common_types.h"
#include "osapi-clock.h"

/*
* The absolute maximum size of a network address
Expand Down Expand Up @@ -408,11 +409,43 @@ int32 OS_SocketAccept(osal_id_t sock_id, osal_id_t *connsock_id, OS_SockAddr_t *
* If a message is already available on the socket, this should immediately return
* that data without blocking. Otherwise, it may block up to the given timeout.
*
* This API is identical to OS_SocketRecvFrom() except for the timeout parameter. In
* this call, timeout is expressed as an absolute value of the OS clock, in the same
* time domain as obtained via OS_GetLocalTime(). This allows for a more precise
* timeout than what is possible via the normal OS_SocketRecvFrom().
*
* @param[in] sock_id The socket ID, previously bound using OS_SocketBind()
* @param[out] buffer Pointer to message data receive buffer @nonnull
* @param[in] buflen The maximum length of the message data to receive @nonzero
* @param[out] RemoteAddr Buffer to store the remote network address (may be NULL)
* @param[in] timeout The maximum amount of time to wait, or OS_PEND to wait forever
* @param[in] abs_timeout The absolute time at which the call should return if nothing received
*
* @return Count of actual bytes received or error status, see @ref OSReturnCodes
* @retval #OS_INVALID_POINTER if argument is NULL
* @retval #OS_ERR_INVALID_SIZE if passed-in buflen is not valid
* @retval #OS_ERR_INVALID_ID if the sock_id parameter is not valid
* @retval #OS_ERR_INCORRECT_OBJ_TYPE if the handle is not a socket
*/
int32 OS_AbsSocketRecvFrom(osal_id_t sock_id, void *buffer, size_t buflen, OS_SockAddr_t *RemoteAddr,
OS_time_t abs_timeout);

/*-------------------------------------------------------------------------------------*/
/**
* @brief Reads data from a message-oriented (datagram) socket
*
* If a message is already available on the socket, this should immediately return
* that data without blocking. Otherwise, it may block up to the given timeout.
*
* The timeout is expressed in milliseconds, relative to the time that the API was
* invoked. Use OS_AbsSocketRecvFrom() for higher timing precision.
*
* @param[in] sock_id The socket ID, previously bound using OS_SocketBind()
* @param[out] buffer Pointer to message data receive buffer @nonnull
* @param[in] buflen The maximum length of the message data to receive @nonzero
* @param[out] RemoteAddr Buffer to store the remote network address (may be NULL)
* @param[in] timeout The maximum amount of time to wait or OS_PEND to wait forever
*
* @sa OS_AbsSocketRecvFrom()
*
* @return Count of actual bytes received or error status, see @ref OSReturnCodes
* @retval #OS_INVALID_POINTER if argument is NULL
Expand Down
Loading

0 comments on commit 8dfdbda

Please sign in to comment.