forked from Imagelibrary/rtems
Updates #4924. The Regulator is an application support class which is used to deal with the scenario where there is a bursty input source which needs to be metered out to a destination sink. The maximum size of bursts needs to be known and the delivery method must be configured to deliver messages at a rate that allows the traffic to not overflow.
680 lines
19 KiB
C
680 lines
19 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* @brief Regulator Library Implementation
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2022 On-Line Applications Research Corporation (OAR)
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/regulator.h>
|
|
|
|
#include <rtems/regulatorimpl.h>
|
|
|
|
/**
|
|
* @ingroup RegulatorInternalAPI
|
|
*
|
|
* This method is the body for the task which delivers the output for
|
|
* this regulator instance at the configured rate.
|
|
*
|
|
* @param[in] arg points to the regulator instance this thread
|
|
* is associated with
|
|
*
|
|
* @note The argument passed in cannot be NULL if the
|
|
* rtems_regulator_create worked.
|
|
*/
|
|
static rtems_task _Regulator_Output_task_body(
|
|
rtems_task_argument arg
|
|
)
|
|
{
|
|
_Regulator_Control *the_regulator = (_Regulator_Control *)arg;
|
|
rtems_status_code sc;
|
|
size_t to_dequeue;
|
|
_Regulator_Message_t regulator_message;
|
|
size_t regulator_message_size;
|
|
bool release_it;
|
|
|
|
the_regulator->delivery_thread_is_running = true;
|
|
|
|
/**
|
|
* This thread uses a rate monotonic period object instance. A rate
|
|
* monotonic period object must be created by the thread using it.
|
|
* It can be deleted by any thread which simplifies clean up.
|
|
*
|
|
* The rate_monotonic_create() call can fail if the application
|
|
* is incorrectly configured. This thread has no way to report the
|
|
* failure. If it continues with an invalid id, then the thread will
|
|
* not block on the period and spin continuously consuming CPU. The only
|
|
* alternatives are to invoke rtems_fatal_error_occurred() or silently
|
|
* exit the thread.
|
|
*/
|
|
sc = rtems_rate_monotonic_create(
|
|
rtems_build_name('P', 'E', 'R', 'D'),
|
|
&the_regulator->delivery_thread_period_id
|
|
);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
goto exit_delivery_thread;
|
|
}
|
|
|
|
/**
|
|
* Loop on the rate_monotonic_period() based on the specified period.
|
|
*/
|
|
while (1) {
|
|
sc = rtems_rate_monotonic_period(
|
|
the_regulator->delivery_thread_period_id,
|
|
the_regulator->Attributes.delivery_thread_period
|
|
);
|
|
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
|
|
|
|
/**
|
|
* If the delivery thread has been requested to exit, then
|
|
* quit processing messages, break out of this loop, and exit
|
|
* this thread.
|
|
*/
|
|
if (the_regulator->delivery_thread_request_exit) {
|
|
break;
|
|
}
|
|
|
|
/**
|
|
* Loop for the configured number of messages to deliver per period.
|
|
* If we reach the point, there are no more messages, block for the
|
|
* rest of this period. If there are messages, deliver them.
|
|
*/
|
|
for (to_dequeue = 0;
|
|
to_dequeue < the_regulator->Attributes.maximum_to_dequeue_per_period;
|
|
to_dequeue++) {
|
|
regulator_message_size = sizeof(_Regulator_Message_t);
|
|
sc = rtems_message_queue_receive(
|
|
the_regulator->queue_id,
|
|
®ulator_message,
|
|
®ulator_message_size,
|
|
RTEMS_NO_WAIT,
|
|
0
|
|
);
|
|
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
break;
|
|
}
|
|
|
|
release_it = the_regulator->Attributes.deliverer(
|
|
the_regulator->Attributes.deliverer_context,
|
|
regulator_message.buffer,
|
|
regulator_message.length
|
|
);
|
|
|
|
the_regulator->Statistics.delivered++;
|
|
|
|
/**
|
|
* The message was successfully delivered. If the delivery function
|
|
* wants the buffer returned, do it now. The delivery to the Destination
|
|
* may involve handing the buffer off to something like DMA
|
|
* and need to wait for it to complete before releasing the buffer.
|
|
*
|
|
* Note that this is the underlying RTEMS service
|
|
* used by @a rtems_regulator_obtain_buffer() and @a
|
|
* rtems_regulator_release_buffer().
|
|
*/
|
|
if (release_it == true) {
|
|
the_regulator->Statistics.released++;
|
|
sc = rtems_partition_return_buffer(
|
|
the_regulator->messages_partition_id,
|
|
regulator_message.buffer
|
|
);
|
|
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This thread was requested to exit. Do so.
|
|
*/
|
|
exit_delivery_thread:
|
|
the_regulator->delivery_thread_is_running = false;
|
|
the_regulator->delivery_thread_has_exited = true;
|
|
|
|
(void) rtems_rate_monotonic_delete(the_regulator->delivery_thread_period_id);
|
|
|
|
rtems_task_exit();
|
|
}
|
|
|
|
/**
|
|
* @ingroup RegulatorInternalAPI
|
|
*
|
|
* This method frees the resources associated with a regulator instance.
|
|
* The resources are freed in the opposite of the order in which they are
|
|
* allocated. This is used on error cases in @a rtems_regulator_create() and in
|
|
* @a rtems_regulator_delete().
|
|
*
|
|
* @param[in] the_regulator is the instance to operate upon
|
|
* @param[in] ticks is the length of time to wait for the delivery thread
|
|
* to exit
|
|
*
|
|
* @return This method returns true is successful and false on timeout.
|
|
*/
|
|
static bool _Regulator_Free_helper(
|
|
_Regulator_Control *the_regulator,
|
|
rtems_interval ticks
|
|
)
|
|
{
|
|
rtems_status_code sc;
|
|
|
|
|
|
/*
|
|
* If the output thread has not started running, then we can just delete it.
|
|
*/
|
|
|
|
if (ticks == 0 || the_regulator->delivery_thread_is_running == false) {
|
|
sc = rtems_task_delete(the_regulator->delivery_thread_id);
|
|
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
|
|
} else {
|
|
rtems_interval remaining = ticks;
|
|
|
|
the_regulator->delivery_thread_request_exit = true;
|
|
|
|
while (1) {
|
|
if (the_regulator->delivery_thread_has_exited) {
|
|
break;
|
|
}
|
|
|
|
if (remaining == 0) {
|
|
return false;
|
|
}
|
|
|
|
(void) rtems_task_wake_after(1);
|
|
remaining--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The output thread deletes the rate monotonic period that it created.
|
|
*/
|
|
|
|
/*
|
|
* The regulator's message_queue_storage is implicitly freed by this call.
|
|
*/
|
|
sc = rtems_message_queue_delete(the_regulator->queue_id);
|
|
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
|
|
|
|
sc = rtems_partition_delete(the_regulator->messages_partition_id);
|
|
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
|
|
|
|
if (the_regulator->message_memory) {
|
|
free(the_regulator->message_memory);
|
|
}
|
|
|
|
the_regulator->initialized = 0;
|
|
free(the_regulator);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @ingroup RegulatorInternalAPI
|
|
*/
|
|
rtems_status_code rtems_regulator_create(
|
|
rtems_regulator_attributes *attributes,
|
|
rtems_regulator_instance **regulator
|
|
)
|
|
{
|
|
_Regulator_Control *the_regulator;
|
|
rtems_status_code sc;
|
|
size_t alloc_size;
|
|
|
|
/**
|
|
* Perform basic validation of parameters
|
|
*/
|
|
if (attributes == NULL) {
|
|
return RTEMS_INVALID_ADDRESS;
|
|
}
|
|
|
|
if (regulator == NULL) {
|
|
return RTEMS_INVALID_ADDRESS;
|
|
}
|
|
|
|
/**
|
|
* Verify attributes are OK. Some are checked by calls to object create
|
|
* methods. Specifically the following are not checked:
|
|
*
|
|
* - delivery_thread_priority by rtems_task_create()
|
|
* - delivery_thread_stack_size can be any value
|
|
*/
|
|
if (attributes->deliverer == NULL) {
|
|
return RTEMS_INVALID_ADDRESS;
|
|
}
|
|
|
|
if (attributes->maximum_messages == 0) {
|
|
return RTEMS_INVALID_NUMBER;
|
|
}
|
|
|
|
if (attributes->maximum_message_size == 0) {
|
|
return RTEMS_INVALID_SIZE;
|
|
}
|
|
|
|
if (attributes->maximum_to_dequeue_per_period == 0) {
|
|
return RTEMS_INVALID_NUMBER;
|
|
}
|
|
|
|
if (attributes->delivery_thread_period == 0) {
|
|
return RTEMS_INVALID_NUMBER;
|
|
}
|
|
|
|
/**
|
|
* Allocate memory for regulator instance
|
|
*/
|
|
the_regulator = (_Regulator_Control *) calloc(sizeof(_Regulator_Control), 1);
|
|
if (the_regulator == NULL) {
|
|
return RTEMS_NO_MEMORY;
|
|
}
|
|
|
|
/**
|
|
* We do NOT want the delivery_thread_id field to be initialized to 0. If the
|
|
* @a rtems_task_create() fails, then the field will not be overwritten.
|
|
* This results in an attempt to rtems_task_delete(0) during clean
|
|
* up. The thread ID of 0 is self which results in the calling thread
|
|
* accidentally deleting itself.
|
|
*/
|
|
the_regulator->delivery_thread_id = (rtems_id) -1;
|
|
|
|
/**
|
|
* Copy the attributes to an internal area for later use
|
|
*/
|
|
the_regulator->Attributes = *attributes;
|
|
|
|
/**
|
|
* Allocate memory for the messages. There is no need to zero out the
|
|
* message memory because the user should fill that in.
|
|
*/
|
|
alloc_size = attributes->maximum_message_size * attributes->maximum_messages;
|
|
the_regulator->message_memory = calloc(alloc_size, 1);
|
|
if (the_regulator->message_memory == NULL) {
|
|
_Regulator_Free_helper(the_regulator, 0);
|
|
return RTEMS_NO_MEMORY;
|
|
}
|
|
|
|
/**
|
|
* Associate message memory with a partition so allocations are atomic
|
|
*/
|
|
sc = rtems_partition_create(
|
|
rtems_build_name('P', 'O', 'O', 'L'),
|
|
the_regulator->message_memory,
|
|
alloc_size,
|
|
attributes->maximum_message_size,
|
|
RTEMS_DEFAULT_ATTRIBUTES,
|
|
&the_regulator->messages_partition_id
|
|
);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
_Regulator_Free_helper(the_regulator, 0);
|
|
return sc;
|
|
}
|
|
|
|
/**
|
|
* Create the message queue between the sender and output thread
|
|
*/
|
|
RTEMS_MESSAGE_QUEUE_BUFFER(sizeof(_Regulator_Message_t)) regulator_message_t;
|
|
|
|
size_t storage_size = sizeof(regulator_message_t) * attributes->maximum_messages;
|
|
|
|
the_regulator->message_queue_storage = malloc(storage_size);
|
|
if (the_regulator->message_queue_storage == NULL) {
|
|
_Regulator_Free_helper(the_regulator, 0);
|
|
return RTEMS_NO_MEMORY;
|
|
}
|
|
|
|
rtems_message_queue_config mq_config = {
|
|
.name = rtems_build_name('S', 'N', 'D', 'Q'),
|
|
.maximum_pending_messages = attributes->maximum_messages,
|
|
.maximum_message_size = sizeof(_Regulator_Message_t),
|
|
.storage_area = the_regulator->message_queue_storage,
|
|
.storage_size = storage_size,
|
|
.storage_free = free,
|
|
.attributes = RTEMS_DEFAULT_ATTRIBUTES
|
|
};
|
|
sc = rtems_message_queue_construct(
|
|
&mq_config,
|
|
&the_regulator->queue_id
|
|
);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
_Regulator_Free_helper(the_regulator, 0);
|
|
return sc;
|
|
}
|
|
|
|
/**
|
|
* @note A rate monotonic period object must be created by the thread
|
|
* using it. Thus that specific create operation is not included
|
|
* in this method. All other resources are allocated here.
|
|
*/
|
|
|
|
/**
|
|
* Create the output thread Using the priority and stack size attributes
|
|
* specified by the user.
|
|
*/
|
|
sc = rtems_task_create(
|
|
rtems_build_name('R', 'E', 'G', 'U'),
|
|
attributes->delivery_thread_priority,
|
|
attributes->delivery_thread_stack_size,
|
|
RTEMS_DEFAULT_MODES,
|
|
RTEMS_DEFAULT_ATTRIBUTES,
|
|
&the_regulator->delivery_thread_id
|
|
);
|
|
if (sc != RTEMS_SUCCESSFUL) {
|
|
_Regulator_Free_helper(the_regulator, 0);
|
|
return sc;
|
|
}
|
|
|
|
/**
|
|
* Start the output thread.
|
|
*
|
|
* @note There should be no way this call can fail. The task id is valid,
|
|
* the regulator output thread entry point is valid, and the argument
|
|
* is valid.
|
|
*/
|
|
the_regulator->delivery_thread_is_running = true;
|
|
the_regulator->delivery_thread_request_exit = false;
|
|
the_regulator->delivery_thread_has_exited = false;
|
|
|
|
sc = rtems_task_start(
|
|
the_regulator->delivery_thread_id,
|
|
_Regulator_Output_task_body,
|
|
(rtems_task_argument) the_regulator
|
|
);
|
|
_Assert_Unused_variable_equals(sc, RTEMS_SUCCESSFUL);
|
|
|
|
/**
|
|
* The regulator is successfully initialized. Set the initialized field
|
|
* to reflect this and return the instance pointer.
|
|
*/
|
|
the_regulator->initialized = REGULATOR_INITIALIZED;
|
|
|
|
*regulator = (void *)the_regulator;
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/**
|
|
* @brief Validate the regulator instance provided by the user
|
|
*
|
|
* Validate the regulator instance provided by the user
|
|
*
|
|
* @param[in] regulator is the instance provided by the user
|
|
* @param[inout] status will contain the RTEMS status for this check
|
|
*
|
|
* @return This method returns a @a _Regulator_Control instance pointer
|
|
* which is NULL if invalid or points to the internal regulator
|
|
* control structure if valid.
|
|
*/
|
|
static inline _Regulator_Control *_Regulator_Get(
|
|
rtems_regulator_instance *regulator,
|
|
rtems_status_code *status
|
|
)
|
|
{
|
|
_Regulator_Control *the_regulator = (_Regulator_Control *) regulator;
|
|
|
|
if (the_regulator == NULL) {
|
|
*status = RTEMS_INVALID_ADDRESS;
|
|
return NULL;
|
|
}
|
|
|
|
if (the_regulator->initialized != REGULATOR_INITIALIZED) {
|
|
*status = RTEMS_INCORRECT_STATE;
|
|
return NULL;
|
|
}
|
|
|
|
status = RTEMS_SUCCESSFUL;
|
|
return the_regulator;
|
|
}
|
|
|
|
/**
|
|
* @ingroup RegulatorInternalAPI
|
|
*/
|
|
rtems_status_code rtems_regulator_delete(
|
|
rtems_regulator_instance *regulator,
|
|
rtems_interval ticks
|
|
)
|
|
{
|
|
_Regulator_Control *the_regulator;
|
|
rtems_status_code status;
|
|
|
|
/**
|
|
* Convert external handle to internal instance pointer
|
|
*/
|
|
the_regulator = _Regulator_Get(regulator, &status);
|
|
if (the_regulator == NULL) {
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* There can be no buffers outstanding
|
|
*/
|
|
_Regulator_Statistics *stats = &the_regulator->Statistics;
|
|
|
|
if (stats->obtained != stats->released ) {
|
|
return RTEMS_RESOURCE_IN_USE;
|
|
}
|
|
|
|
/**
|
|
* Free the resources associated with this regulator instance.
|
|
*/
|
|
bool bc;
|
|
bc = _Regulator_Free_helper(the_regulator, ticks);
|
|
if (bc == false) {
|
|
return RTEMS_TIMEOUT;
|
|
}
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/**
|
|
* @ingroup RegulatorInternalAPI
|
|
*
|
|
* Allocate a buffer for the caller using the internal partition.
|
|
*/
|
|
rtems_status_code rtems_regulator_obtain_buffer(
|
|
rtems_regulator_instance *regulator,
|
|
void **buffer
|
|
)
|
|
{
|
|
_Regulator_Control *the_regulator;
|
|
rtems_status_code status;
|
|
|
|
/**
|
|
* Convert external handle to internal instance pointer
|
|
*/
|
|
the_regulator = _Regulator_Get(regulator, &status);
|
|
if (the_regulator == NULL) {
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Allocate a buffer for the user application from the buffer pool managed
|
|
* by an Classic API partition.
|
|
*/
|
|
status = rtems_partition_get_buffer(
|
|
the_regulator->messages_partition_id,
|
|
buffer
|
|
);
|
|
|
|
if (status == RTEMS_SUCCESSFUL) {
|
|
the_regulator->Statistics.obtained++;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @ingroup RegulatorInternalAPI
|
|
*
|
|
* Allocate a buffer for the caller using the internal partition.
|
|
*/
|
|
rtems_status_code rtems_regulator_release_buffer(
|
|
rtems_regulator_instance *regulator,
|
|
void *buffer
|
|
)
|
|
{
|
|
_Regulator_Control *the_regulator;
|
|
rtems_status_code status;
|
|
|
|
/**
|
|
* Convert external handle to internal instance pointer
|
|
*/
|
|
the_regulator = _Regulator_Get(regulator, &status);
|
|
if (the_regulator == NULL) {
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Deallocate the buffer to the buffer pool managed by a Classic
|
|
* API partition.
|
|
*/
|
|
status = rtems_partition_return_buffer(
|
|
the_regulator->messages_partition_id,
|
|
buffer
|
|
);
|
|
|
|
if (status == RTEMS_SUCCESSFUL) {
|
|
the_regulator->Statistics.released++;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @ingroup RegulatorInternalAPI
|
|
*/
|
|
rtems_status_code rtems_regulator_send(
|
|
rtems_regulator_instance *regulator,
|
|
void *message,
|
|
size_t length
|
|
)
|
|
{
|
|
_Regulator_Control *the_regulator;
|
|
rtems_status_code status;
|
|
_Regulator_Message_t regulator_message;
|
|
|
|
the_regulator = (_Regulator_Control *) regulator;
|
|
|
|
/**
|
|
* Validate the arguments and ensure the regulator was successfully
|
|
* initialized.
|
|
*/
|
|
if (message == NULL) {
|
|
return RTEMS_INVALID_ADDRESS;
|
|
}
|
|
|
|
if (length == 0) {
|
|
return RTEMS_INVALID_NUMBER;
|
|
}
|
|
|
|
/**
|
|
* Convert external handle to internal instance pointer
|
|
*/
|
|
the_regulator = _Regulator_Get(regulator, &status);
|
|
if (the_regulator == NULL) {
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Place the message pointer and length into a temporary structure. This
|
|
* lets the implementation internally send the message by reference and
|
|
* have a zero-copy implementation.
|
|
*/
|
|
regulator_message.buffer = message;
|
|
regulator_message.length = length;
|
|
|
|
/**
|
|
* Send the application message to the output thread for delivery using
|
|
* a Classic API message queue.
|
|
*/
|
|
status = rtems_message_queue_send(
|
|
the_regulator->queue_id,
|
|
®ulator_message,
|
|
sizeof(_Regulator_Message_t)
|
|
);
|
|
if (status != RTEMS_SUCCESSFUL) {
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @ingroup RegulatorInternalAPI
|
|
*/
|
|
rtems_status_code rtems_regulator_get_statistics(
|
|
rtems_regulator_instance *regulator,
|
|
rtems_regulator_statistics *statistics
|
|
)
|
|
{
|
|
_Regulator_Control *the_regulator;
|
|
rtems_status_code status;
|
|
|
|
/**
|
|
* Validate the arguments and ensure the regulator was successfully
|
|
* initialized.
|
|
*/
|
|
if (statistics == NULL) {
|
|
return RTEMS_INVALID_ADDRESS;
|
|
}
|
|
|
|
/**
|
|
* Convert external handle to internal instance pointer
|
|
*/
|
|
the_regulator = _Regulator_Get(regulator, &status);
|
|
if (the_regulator == NULL) {
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Zero out the statistics structure in case the get period statistics
|
|
* fails below.
|
|
*/
|
|
memset(statistics, 0, sizeof(rtems_regulator_statistics));
|
|
|
|
/**
|
|
* Fill in the caller's statistics structure from information
|
|
* maintained by the regulator instance about buffers processed.
|
|
*/
|
|
statistics->obtained = the_regulator->Statistics.obtained;
|
|
statistics->released = the_regulator->Statistics.released;
|
|
statistics->delivered = the_regulator->Statistics.delivered;
|
|
|
|
/**
|
|
* Attempt to retrieve the delivery thread's period's statistics.
|
|
*
|
|
* NOTE; If the Delivery Thread has not run yet, the period will not
|
|
* exist yet. We should not fail for this reason but it is why
|
|
* we zeroed out the entire structure above.
|
|
*/
|
|
(void) rtems_rate_monotonic_get_statistics(
|
|
the_regulator->delivery_thread_period_id,
|
|
&statistics->period_statistics
|
|
);
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|