Skip to content

Commit

Permalink
Merge pull request #1456 from jphickey:fix-1455-hires-timedops
Browse files Browse the repository at this point in the history
Fix #1455, High-res timed stream ops
  • Loading branch information
dzbaker committed Jul 2, 2024
2 parents 6483329 + ec30aa7 commit a456bc7
Show file tree
Hide file tree
Showing 30 changed files with 893 additions and 193 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_TimedReadAbs(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_TimedWriteAbs(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
94 changes: 94 additions & 0 deletions src/os/inc/osapi-select.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"

/**
* @brief An abstract structure capable of holding several OSAL IDs
Expand Down Expand Up @@ -83,6 +84,52 @@ typedef enum
* If the timeout occurs this returns an error code and all output sets
* should be empty.
*
* This API is identical to OS_SelectMultiple() 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_SelectMultiple().
*
* @note This does not lock or otherwise protect the file handles in the
* given sets. If a filehandle supplied via one of the FdSet arguments
* is closed or modified by another while this function is in progress,
* the results are undefined. Because of this limitation, it is recommended
* to use OS_SelectSingle() whenever possible.
*
* @param[in,out] ReadSet Set of handles to check/wait to become readable
* @param[in,out] WriteSet Set of handles to check/wait to become writable
* @param[in] abs_timeout The absolute time that the call may block until
*
* @sa OS_SelectMultiple()
*
* @return Execution status, see @ref OSReturnCodes
* @retval #OS_SUCCESS If any handle in the ReadSet or WriteSet is readable or writable, respectively
* @retval #OS_ERROR_TIMEOUT If no handles in the ReadSet or WriteSet became readable or writable within the timeout
* @retval #OS_ERR_OPERATION_NOT_SUPPORTED if a specified handle does not support select
* @retval #OS_ERR_INVALID_ID if no valid handles were contained in the ReadSet/WriteSet
*/
int32 OS_SelectMultipleAbs(OS_FdSet *ReadSet, OS_FdSet *WriteSet, OS_time_t abs_timeout);

/*-------------------------------------------------------------------------------------*/
/**
* @brief Wait for events across multiple file handles
*
* Wait for any of the given sets of IDs to become readable or writable
*
* This function will block until any of the following occurs:
* - At least one OSAL ID in the ReadSet is readable
* - At least one OSAL ID in the WriteSet is writable
* - The timeout has elapsed
*
* The sets are input/output parameters. On entry, these indicate the
* file handle(s) to wait for. On exit, these are set to the actual
* file handle(s) that have activity.
*
* If the timeout occurs this returns an error code and all output sets
* should be empty.
*
* The timeout is expressed in milliseconds, relative to the time that the API was
* invoked. Use OS_SelectMultipleAbs() for higher timing precision.
*
* @note This does not lock or otherwise protect the file handles in the
* given sets. If a filehandle supplied via one of the FdSet arguments
* is closed or modified by another while this function is in progress,
Expand All @@ -94,6 +141,8 @@ typedef enum
* @param[in] msecs Indicates the timeout. Positive values will wait up to that many milliseconds. Zero will not wait
* (poll). Negative values will wait forever (pend)
*
* @sa OS_SelectMultipleAbs()
*
* @return Execution status, see @ref OSReturnCodes
* @retval #OS_SUCCESS If any handle in the ReadSet or WriteSet is readable or writable, respectively
* @retval #OS_ERROR_TIMEOUT If no handles in the ReadSet or WriteSet became readable or writable within the timeout
Expand Down Expand Up @@ -123,11 +172,56 @@ int32 OS_SelectMultiple(OS_FdSet *ReadSet, OS_FdSet *WriteSet, int32 msecs);
* To mitigate this risk the application may prefer to use
* the OS_TimedRead/OS_TimedWrite calls.
*
* This API is identical to OS_SelectSingle() 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_SelectSingle().
*
* @param[in] objid The handle ID to select on
* @param[in,out] StateFlags State flag(s) (readable or writable) @nonnull
* @param[in] abs_timeout The absolute time that the call may block until
*
* @sa OS_SelectSingle()
*
* @return Execution status, see @ref OSReturnCodes
* @retval #OS_SUCCESS If the handle is readable and/or writable, as requested
* @retval #OS_ERROR_TIMEOUT If the handle did not become readable or writable within the timeout
* @retval #OS_INVALID_POINTER if argument is NULL
* @retval #OS_ERR_INVALID_ID if the objid is not a valid handle
*/
int32 OS_SelectSingleAbs(osal_id_t objid, uint32 *StateFlags, OS_time_t abs_timeout);

/*-------------------------------------------------------------------------------------*/
/**
* @brief Wait for events on a single file handle
*
* Wait for a single OSAL filehandle to change state
*
* This function can be used to wait for a single OSAL stream ID
* to become readable or writable. On entry, the "StateFlags"
* parameter should be set to the desired state (OS_STREAM_STATE_READABLE
* and/or OS_STREAM_STATE_WRITABLE) and upon return the flags
* will be set to the state actually detected.
*
* As this operates on a single ID, the filehandle is protected
* during this call, such that another thread accessing the same
* handle will return an error. However, it is important to note that
* once the call returns then other threads may then also read/write
* and affect the state before the current thread can service it.
*
* To mitigate this risk the application may prefer to use
* the OS_TimedRead/OS_TimedWrite calls.
*
* The timeout is expressed in milliseconds, relative to the time that the API was
* invoked. Use OS_SelectSingleAbs() for higher timing precision.
*
* @param[in] objid The handle ID to select on
* @param[in,out] StateFlags State flag(s) (readable or writable) @nonnull
* @param[in] msecs Indicates the timeout. Positive values will wait up to that many milliseconds. Zero will not wait
* (poll). Negative values will wait forever (pend)
*
* @sa OS_SelectSingleAbs()
*
* @return Execution status, see @ref OSReturnCodes
* @retval #OS_SUCCESS If the handle is readable and/or writable, as requested
* @retval #OS_ERROR_TIMEOUT If the handle did not become readable or writable within the timeout
Expand Down
Loading

0 comments on commit a456bc7

Please sign in to comment.