Merge Heinz & Andrew's rtems-osd-event branch into 7.0

This commit is contained in:
Andrew Johnson
2022-01-24 12:44:17 -06:00
11 changed files with 221 additions and 41 deletions

View File

@@ -16,6 +16,17 @@ should also be read to understand what has changed since earlier releases.
<!-- Insert new items immediately below here ... -->
### Fix embedded implementations of `epicsEvent`
[GH:202](https://github.com/epics-base/epics-base/issues/202) and
[GH:206](https://github.com/epics-base/epics-base/pull/206)
Heinz Junkes provided a new implementation of the `epicsEvent` API suitable for
RTEMS Posix targets (RTEMS 5.1 and later). In review a few issues related to
overflow of timeout values surfaced in this and other embedded implementations,
and these were also been fixed in this Pull Request. The API documentation for
this and some other routines has also been updated.
### Breakpoint Table Names

View File

@@ -559,19 +559,19 @@ LIBCA_API chid epicsStdCall ca_evid_to_chid ( evid id );
/*
* ca_pend_event()
*
* timeOut R wait for this delay in seconds
* timeout R wait for this delay in seconds
*/
LIBCA_API int epicsStdCall ca_pend_event (ca_real timeOut);
LIBCA_API int epicsStdCall ca_pend_event (ca_real timeout);
#define ca_poll() ca_pend_event(1e-12)
/*
* ca_pend_io()
*
* timeOut R wait for this delay in seconds but return early
* timeout R wait for this delay in seconds but return early
* if all get requests (or search requests with null
* connection handler pointer have completed)
*/
LIBCA_API int epicsStdCall ca_pend_io (ca_real timeOut);
LIBCA_API int epicsStdCall ca_pend_io (ca_real timeout);
/* calls ca_pend_io() if early is true otherwise ca_pend_event() is called */
LIBCA_API int epicsStdCall ca_pend (ca_real timeout, int early);

View File

@@ -70,9 +70,9 @@ void epicsEvent::wait ()
}
}
bool epicsEvent::wait (double timeOut)
bool epicsEvent::wait (double timeout)
{
epicsEventStatus status = epicsEventWaitWithTimeout (this->id, timeOut);
epicsEventStatus status = epicsEventWaitWithTimeout (this->id, timeout);
if (status == epicsEventOK) {
return true;

View File

@@ -99,10 +99,12 @@ public:
**/
void wait ();
/**\brief Wait for the event or until the specified timeout.
* \param timeOut The timeout delay in seconds.
* \param timeout The timeout delay in seconds. A timeout of zero is
* equivalent to calling tryWait(); NaN or any value too large to be
* represented to the target OS is equivalent to no timeout.
* \return True if the event was triggered, False if it timed out.
**/
bool wait ( double timeOut );
bool wait ( double timeout );
/**\brief Similar to wait() except that if the event is currently empty the
* call will return immediately.
* \return True if the event was full (triggered), False if empty.
@@ -190,11 +192,13 @@ LIBCOM_API void epicsEventMustWait(epicsEventId id);
/**\brief Wait an the event or until the specified timeout period is over.
* \note Blocks until full or timeout.
* \param id The event identifier.
* \param timeOut The timeout delay in seconds.
* \param timeout The timeout delay in seconds. A timeout of zero is
* equivalent to calling epicsEventTryWait(); NaN or any value too large
* to be represented to the target OS is equivalent to no timeout.
* \return Status indicator.
**/
LIBCOM_API epicsEventStatus epicsEventWaitWithTimeout(
epicsEventId id, double timeOut);
epicsEventId id, double timeout);
/**\brief Similar to wait() except that if the event is currently empty the
* call will return immediately with status \c epicsEventWaitTimeout.

View File

@@ -84,6 +84,9 @@ public:
/**
* \brief Send a message or timeout.
* \param timeout The timeout delay in seconds. A timeout of zero is
* equivalent to calling trySend(); NaN or any value too large to be
* represented to the target OS is equivalent to no timeout.
* \returns 0 if the message was sent to a receiver or queued for
* future delivery.
* \returns -1 if the timeout was reached before the
@@ -124,12 +127,16 @@ public:
int receive ( void *message, unsigned int size );
/**
* \brief Wait for a message to be queued.
* Wait up to \p timeout seconds for a message to be sent if the queue
* is empty, then move the first message to the specified location.
* \brief Wait for and fetch the next message.
* \param timeout The timeout delay in seconds. A timeout of zero is
* equivalent to calling tryReceive(); NaN or any value too large to
* be represented to the target OS is equivalent to no timeout.
*
* If the received message is larger than the specified
* messageBufferSize it may either return -1, or truncate the
* Waits up to \p timeout seconds for a message to arrive if the queue
* is empty, then moves the first message to the specified location.
*
* If the received message is larger than the specified message size
* the implementation may either return -1, or truncate the
* message. It is most efficient if the messageBufferSize is equal
* to the maximumMessageSize with which the message queue was
* created.

View File

@@ -0,0 +1,116 @@
/*************************************************************************\
* Copyright (c) 2021 Fritz Haber Institute, Berlin
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* RTEMS osdEvent.c
* Author: H. Junkes
* junkes@fhi.mpg.de
*/
#include <stdio.h>
#include <malloc.h>
#include <stdint.h>
#include <rtems.h>
#include <rtems/error.h>
#include <rtems/thread.h>
#include "libComAPI.h"
#include "epicsEvent.h"
typedef struct epicsEventOSD {
rtems_binary_semaphore rbs;
} epicsEventOSD;
/*
* Create a simple binary semaphore
*/
LIBCOM_API epicsEventId
epicsEventCreate(epicsEventInitialState initialState)
{
epicsEventOSD *pSem = malloc (sizeof(*pSem));
if (pSem) {
rtems_binary_semaphore_init(&pSem->rbs, NULL);
if (initialState)
rtems_binary_semaphore_post(&pSem->rbs);
}
return pSem;
}
LIBCOM_API void
epicsEventDestroy(epicsEventId pSem)
{
rtems_binary_semaphore_destroy(&pSem->rbs);
}
LIBCOM_API epicsEventStatus
epicsEventTrigger(epicsEventId pSem)
{
rtems_binary_semaphore_post(&pSem->rbs);
return epicsEventOK;
}
LIBCOM_API epicsEventStatus
epicsEventWait(epicsEventId pSem)
{
rtems_binary_semaphore_wait(&pSem->rbs);
return epicsEventOK;
}
LIBCOM_API epicsEventStatus
epicsEventWaitWithTimeout(epicsEventId pSem, double timeout)
{
int sc;
rtems_interval delay;
rtems_interval rate = rtems_clock_get_ticks_per_second();
if (!rate)
return epicsEventError;
if (timeout <= 0.0) {
sc = rtems_binary_semaphore_try_wait(&pSem->rbs);
if (!sc)
return epicsEventOK;
else
return epicsEventWaitTimeout;
}
else if (timeout < (double) UINT32_MAX / rate) {
delay = timeout * rate;
if (delay == 0) {
/* 0 < timeout < 1/rate; round up */
delay = 1;
}
}
else {
/* timeout is NaN or too big to represent; wait forever */
delay = RTEMS_NO_TIMEOUT;
}
sc = rtems_binary_semaphore_wait_timed_ticks(&pSem->rbs, delay);
if (!sc)
return epicsEventOK;
else if (sc == ETIMEDOUT)
return epicsEventWaitTimeout;
else
return epicsEventError;
}
LIBCOM_API epicsEventStatus
epicsEventTryWait(epicsEventId pSem)
{
int sc = rtems_binary_semaphore_try_wait(&pSem->rbs);
if (!sc)
return epicsEventOK;
else
return epicsEventWaitTimeout;
}
LIBCOM_API void
epicsEventShow(epicsEventId pSem, unsigned int level)
{
}

View File

@@ -0,0 +1,8 @@
/*************************************************************************\
* Copyright (c) 2021 Fritz Haber Institute, Berlin
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* No definitions are needed for this implementation */

View File

@@ -20,6 +20,7 @@
#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1
#include <stdio.h>
#include <stdint.h>
#include <rtems.h>
#include <rtems/error.h>
@@ -122,19 +123,27 @@ epicsEventWait(epicsEventId id)
}
epicsEventStatus
epicsEventWaitWithTimeout(epicsEventId id, double timeOut)
epicsEventWaitWithTimeout(epicsEventId id, double timeout)
{
rtems_id sid = (rtems_id)id;
rtems_status_code sc;
rtems_interval delay;
extern double rtemsTicksPerSecond_double;
if (timeOut <= 0.0)
if (timeout <= 0.0)
return epicsEventTryWait(id);
SEMSTAT(1)
delay = timeOut * rtemsTicksPerSecond_double;
if (delay == 0)
delay++;
if (timeout < (double) UINT32_MAX / rtemsTicksPerSecond_double) {
delay = timeout * rtemsTicksPerSecond_double;
if (delay == 0) {
/* 0 < timeout < 1/rtemsTicksPerSecond, round up */
delay++;
}
}
else {
/* timeout is NaN or too big to represent in ticks */
delay = RTEMS_NO_TIMEOUT;
}
sc = rtems_semaphore_obtain (sid, RTEMS_WAIT, delay);
if (sc == RTEMS_SUCCESSFUL)
return epicsEventOK;

View File

@@ -90,7 +90,7 @@ LIBCOM_API epicsEventStatus epicsEventWait ( epicsEventId pSem )
* epicsEventWaitWithTimeout ()
*/
LIBCOM_API epicsEventStatus epicsEventWaitWithTimeout (
epicsEventId pSem, double timeOut )
epicsEventId pSem, double timeout )
{
/* waitable timers use 100 nanosecond intervals, like FILETIME */
static const unsigned ivalPerSec = 10000000u; /* number of 100ns intervals per second */
@@ -101,17 +101,17 @@ LIBCOM_API epicsEventStatus epicsEventWaitWithTimeout (
HANDLE timer;
LONGLONG nIvals; /* number of intervals */
if ( timeOut <= 0.0 ) {
if ( timeout <= 0.0 ) {
tmo.QuadPart = 0u;
}
else if ( timeOut >= INFINITE / mSecPerSec ) {
else if ( timeout >= INFINITE / mSecPerSec ) {
/* we need to apply a maximum wait time to stop an overflow. We choose (INFINITE - 1) milliseconds,
to be compatible with previous WaitForSingleObject() implementation */
nIvals = (LONGLONG)(INFINITE - 1) * (ivalPerSec / mSecPerSec);
tmo.QuadPart = -nIvals; /* negative value means a relative time offset for timer */
}
else {
nIvals = (LONGLONG)(timeOut * ivalPerSec + 0.999999);
nIvals = (LONGLONG)(timeout * ivalPerSec + 0.999999);
tmo.QuadPart = -nIvals;
}

View File

@@ -34,20 +34,25 @@ void epicsEventDestroy(epicsEventId id)
semDelete((SEM_ID)id);
}
epicsEventStatus epicsEventWaitWithTimeout(epicsEventId id, double timeOut)
epicsEventStatus epicsEventWaitWithTimeout(epicsEventId id, double timeout)
{
int rate = sysClkRateGet();
int status;
int ticks;
if (timeOut <= 0.0) {
if (timeout <= 0.0) {
ticks = 0;
} else if (timeOut >= (double) INT_MAX / rate) {
ticks = WAIT_FOREVER;
} else {
ticks = timeOut * rate;
if (ticks <= 0)
}
else if (timeout < (double) INT_MAX / rate) {
ticks = timeout * rate;
if (ticks == 0) {
/* 0 < timeout < 1/rate; round up */
ticks = 1;
}
}
else {
/* timeout is NaN or too big to represent in ticks */
ticks = WAIT_FOREVER;
}
status = semTake((SEM_ID)id, ticks);
if (status == OK)

View File

@@ -21,18 +21,28 @@ extern "C" int sysClkRateGet(void);
LIBCOM_API int epicsStdCall epicsMessageQueueSendWithTimeout(
epicsMessageQueueId id,
void *message,
unsigned int messageSize,
unsigned int size,
double timeout)
{
int ticks;
int rate = sysClkRateGet();
if (timeout<=0.0) {
if (timeout <= 0.0) {
ticks = 0;
} else {
ticks = (int)(timeout*sysClkRateGet());
if(ticks<=0) ticks = 1;
}
return msgQSend((MSG_Q_ID)id, (char *)message, messageSize, ticks, MSG_PRI_NORMAL);
else if (timeout < (double) INT_MAX / rate) {
ticks = timeout * rate;
if (ticks == 0) {
/* 0 < timeout < 1/rate; round up */
ticks = 1;
}
}
else {
/* timeout is NaN or too big to represent in ticks */
ticks = WAIT_FOREVER;
}
return msgQSend((MSG_Q_ID)id, (char *)message, size, ticks, MSG_PRI_NORMAL);
}
LIBCOM_API int epicsStdCall epicsMessageQueueReceiveWithTimeout(
@@ -42,12 +52,22 @@ LIBCOM_API int epicsStdCall epicsMessageQueueReceiveWithTimeout(
double timeout)
{
int ticks;
int rate = sysClkRateGet();
if (timeout<=0.0) {
if (timeout <= 0.0) {
ticks = 0;
} else {
ticks = (int)(timeout*sysClkRateGet());
if(ticks<=0) ticks = 1;
}
else if (timeout < (double) INT_MAX / rate) {
ticks = timeout * rate;
if (ticks == 0) {
/* 0 < timeout < 1/rate, round up */
ticks = 1;
}
}
else {
/* timeout is NaN or too big to represent in ticks */
ticks = WAIT_FOREVER;
}
return msgQReceive((MSG_Q_ID)id, (char *)message, size, ticks);
}