[PATCH rtems v1] Add the Regulator Interface and test

Joel Sherrill joel at rtems.org
Fri Jul 7 20:55:39 UTC 2023


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.
---
 cpukit/include/rtems/regulator.h              |  360 +++++
 cpukit/include/rtems/regulatorimpl.h          |  100 ++
 cpukit/regulator/regulator.c                  |  487 +++++++
 spec/build/cpukit/librtemscpu.yml             |    3 +
 spec/build/testsuites/libtests/grp.yml        |    2 +
 .../build/testsuites/libtests/regulator01.yml |   21 +
 testsuites/libtests/regulator01/regulator01.c | 1156 +++++++++++++++++
 .../libtests/regulator01/regulator01.doc      |   37 +
 .../libtests/regulator01/rtems_config.c       |   59 +
 9 files changed, 2225 insertions(+)
 create mode 100644 cpukit/include/rtems/regulator.h
 create mode 100644 cpukit/include/rtems/regulatorimpl.h
 create mode 100644 cpukit/regulator/regulator.c
 create mode 100644 spec/build/testsuites/libtests/regulator01.yml
 create mode 100644 testsuites/libtests/regulator01/regulator01.c
 create mode 100644 testsuites/libtests/regulator01/regulator01.doc
 create mode 100644 testsuites/libtests/regulator01/rtems_config.c

diff --git a/cpukit/include/rtems/regulator.h b/cpukit/include/rtems/regulator.h
new file mode 100644
index 0000000000..7344ced2ae
--- /dev/null
+++ b/cpukit/include/rtems/regulator.h
@@ -0,0 +1,360 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @defgroup RegulatorAPI Regulator API
+ *
+ * @brief Regulator APIs
+ *
+ * The Regulator provides a set of APIs to manage bursty message traffic.
+ */
+
+/**
+ * @mainpage
+ *
+ * The regulator is designed to sit logically between two entities -- a
+ * source and a destination, where it limits the traffic sent to the
+ * destination to prevent it from being flooded with messages from the
+ * source. This can be used to accommodate bursty input from a source
+ * and meter it out to a destination.
+ *
+ * The regulator library accepts an input stream of messages from a
+ * source and delivers them to a destination. The regulator assumes that the
+ * input stream from the source contains sporadic bursts of data which can
+ * exceed the acceptable rate of the destination. By limiting the message rate,
+ * the regulator prevents an overflow of messages.
+ *
+ * The regulator can be configured for the input buffering required to manage
+ * the maximum burst and for the metering rate for the output. The output rate
+ * is in messages per second. If the sender produces data too fast, the
+ * regulator will buffer the configured number of messages.
+ *
+ * A configuration capability is provided to allow for adaptation to different
+ * message streams. The regulator can also support running multiple instances,
+ * which could be used on independent message streams.
+ *
+ * The regulator provides a simple interface to the application for avoiding
+ * bursty input from a fast source overflowing a slower output sink.
+ *
+ * It is assumed that the application has a design limit on the number of
+ * messages which may be buffered. All messages will eventually be output.
+ *
+ * A regulator instance is used as follows:
+ *
+ * @code
+ *   while (1)
+ *     use rtems_regulator_obtain_buffer to obtain a buffer
+ *     input operation to fetch data into the buffer
+ *     rtems_regulator_send(buffer, size of message)
+ *       // rtems_regulator_send() will release the buffer automatically when done
+ * @endcode
+ *
+ * The sequence diagram below shows the interaction between a message Source,
+ * a Regulator instance, and RTEMS, given the usage described in the above
+ * paragraphs.
+ *
+ * \startuml "Regulator Application Input Source Usage"
+ *   Source -> Regulator : rtems_regulator_obtain_buffer(regulator, buffer)
+ *   Regulator -> RTEMS : rtems_partition_get_buffer(id, buffer)
+ *   RTEMS --> Regulator : rtems_status_code
+ *   Regulator --> Source : rtems_status_code
+ *   Source -> Regulator : rtems_regulator_send(regulator, message, length)
+ *   Regulator -> RTEMS : rtems_message_queue_send(id, message, size)
+ *   RTEMS --> Regulator : rtems_status_code
+ *   Regulator --> Source : rtems_status_code
+ * \enduml
+ *
+ * As illustrated in the sequence diagram, the Source usually corresponds
+ * to application software reading a system input. The Source obtains a
+ * buffer from the Regulator instance and fills it with incoming data.
+ * The Source then sends the buffer to the Regulator instance. The
+ * Regulator implementation uses the RTEMS Classic API Partition Manager
+ * to manage the buffer pool and the RTEMS Classic API Message Queue
+ * Manager to send the buffer to the Delivery thread.
+ *
+ * After the Source has sent the message to the Regulator instance,
+ * the Source is free to process another input and the Regulator
+ * instance will ensure that the buffer is delivered to the ultimate
+ * Destination by the internal Delivery thread.
+ */
+
+/**
+ * @addtogroup RegulatorAPI
+ *
+ * @file
+ *
+ * @brief This header file defines the Regulator API.
+ *
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef REGULATOR_H
+#define REGULATOR_H
+
+#include <stdlib.h>
+
+#include <rtems.h>
+
+/**
+ * @ingroup RegulatorAPI
+ *
+ * @brief Regulator Delivery Function Type
+ *
+ * The user provides a function which is invoked to deliver a message
+ * to the output. It takes three parameters
+ *
+ * @param[in] context is an untyped pointer to a user context
+ * @param[in] message points to the message
+ * @param[in] length is the message size
+ *
+ * The following is an example deliverer function. It assumes that the
+ * application has defined the my_context_t structure and it has at least
+ * the socket field.
+ *
+ * @code
+ *   void my_deliverer(
+ *     void     *context,
+ *     void     *message,
+ *     size_t    length
+ *    )
+ *    {
+ *       my_context_t *my_context;
+ *
+ *       my_context = (my_context_t *)context;
+ *
+ *       write(my_context->socket, message, length);
+ *       regulator release(message);
+ *     }
+ * @endcode
+ *
+ * The following sequence diagram shows the behavior of the Delivery thread
+ * body and its interaction with the user-supplied deliverer() function.
+ *
+ * \startuml "Regulator Delivery Thread Body"
+ *   loop while (1)
+ *     "Delivery Thread" -> RTEMS : rtems_rate_monotonic_period(id, output_thread_period)
+ *     loop for 0 : maximum_to_dequeue_per_period
+ *       "Delivery Thread" -> RTEMS : rtems_message_queue_receive(id, message, size, wait, 0)
+ *       RTEMS --> "Delivery Thread" : rtems_status_code
+ *       group if [rtems_status_code != RTEMS_SUCCESSFUL]
+ *         RTEMS -> "Delivery Thread" : break
+ *       end
+ *       "Delivery Thread" -> Application : deliverer(context, buffer, length)
+ *       "Delivery Thread" -> RTEMS : rtems_partition_return_buffer(id, buffer)
+ *       RTEMS --> "Delivery Thread" : rtems_status_code
+ *     end
+ *   end
+ * \enduml
+ *
+ * In the above sequence diagram, the key points are that the Delivery Thread
+ * Body is periodically executed and that during each up to the instance
+ * configuration parameter maximum_to_dequeue_per_period can be dequeued and
+ * given to the application's deliverer method for processing.
+ *
+ * Note that the application explicitly obtains buffers from the regulator
+ * instance but that the Delivery Thread implicitly releases them to the
+ * internal pool when delivered.
+ */
+typedef void (*rtems_regulator_deliverer)(
+  void     *context,
+  void     *message,
+  size_t    length
+);
+
+/**
+ * @ingroup RegulatorAPI
+ *
+ * @brief Attributes for Regulator Instance
+ *
+ * An instance of this structure must be populated by the application
+ * before creating an instance of the regulator. These settings tailor
+ * the behavior of the regulator instance.
+ */
+typedef struct {
+  /** Application method to invoke to output a message to the destination*/
+  rtems_regulator_deliverer deliverer;
+
+  /** Context pointer to pass to deliver method */
+  void  *deliverer_context;
+
+  /** Maximum size message to process */
+  size_t  maximum_message_size;
+
+  /** Maximum number of messages to be able to buffer */
+  size_t  maximum_messages;
+
+  /** Priority of output thread */
+  rtems_task_priority output_thread_priority;
+
+  /** Stack size of output thread */
+  size_t output_thread_stack_size;
+
+  /** Period (in ticks) of output thread */
+  rtems_interval output_thread_period;
+
+  /** Maximum messages to dequeue per period */
+  size_t  maximum_to_dequeue_per_period;
+
+} rtems_regulator_attributes;
+
+/**
+ * @ingroup RegulatorAPI
+ *
+ * @brief Regulator Instance
+ */
+typedef void *rtems_regulator_instance;
+
+/**
+ * @ingroup RegulatorAPI
+ *
+ * @brief Create a regulator
+ *
+ * This function creates an instance of a regulator. It uses the provided
+ * @a attributes to create the instance return in @a regulator. This instance
+ * will allocate the buffers associated with the regulator instance as well
+ * as the output task.
+ *
+ * @param[in] attributes specify the regulator instance attributes
+ * @param[inout] regulator will point to the regulator instance
+ *
+ * @return an RTEMS status code indicating success or failure.
+ *
+ * @note This method allocates memory for the buffers holding messages,
+ *       an output task and an RTEMS partition. When it executes, the
+ *       output task will create an RTEMS rate monotonic period.
+ */
+rtems_status_code rtems_regulator_create(
+  rtems_regulator_attributes  *attributes,
+  rtems_regulator_instance   **regulator
+);
+
+/**
+ * @ingroup RegulatorAPI
+ *
+ * @brief Delete a regulator
+ *
+ * This method is used to delete an instance of a @a regulator.
+ *
+ * @param[in] regulator is the instance to delete
+ *
+ * @return an RTEMS status code indicating success or failure.
+ *
+ * @note This method deallocates the resources allocated during
+ *       rtems_regulator_create().
+ */
+rtems_status_code rtems_regulator_delete(
+  rtems_regulator_instance    *regulator
+);
+
+/**
+ * @ingroup RegulatorAPI
+ *
+ * @brief Obtain Buffer from Regulator
+ *
+ * This method is used to obtain a buffer from the regulator's pool. The
+ * @a buffer returned is assumed to be filled in with contents and used
+ * in a subsequent call to rtems_regulator_send(). If the @a buffer is not sent,
+ * then it should be returned using rtems_regulator_release_buffer().
+ *
+ * The buffer is of the maximum_message_size specified in the attributes
+ * passed in to rtems_regulator_create().
+ *
+ * @param[in] regulator is the regulator instance to operate upon
+ * @param[out] buffer will point to the allocated buffer
+ *
+ * @return an RTEMS status code indicating success or failure.
+ *
+ * @note This method does not perform dynamic allocation. It obtains a
+ *       buffer from the pool allocated during rtems_regulator_create().
+ *
+ * @note Any attempt to write outside the buffer area is undefined.
+ */
+rtems_status_code rtems_regulator_obtain_buffer(
+  rtems_regulator_instance   *regulator,
+  void                      **buffer
+);
+
+/**
+ * @ingroup RegulatorAPI
+ *
+ * @brief Release Previously Obtained Regulator Buffer
+ *
+ * This method is used to release a buffer from the regulator's pool. It is
+ * assumed that the @a buffer returned will not be used by the application
+ * anymore. The @a buffer must have previously been allocated by
+ * rtems_regulator_obtain_buffer().
+ *
+ * If the rtems_regulator_send() is successful, the buffer is automatically
+ * released to the pool. If unsuccessful or the application decides not
+ * to send the message, it is the application's responsibility to
+ * release the buffer.
+ *
+ * @param[in] regulator is the regulator instance to operate upon
+ * @param[out] buffer will point to the buffer to release
+ *
+ * @return an RTEMS status code indicating success or failure.
+ *
+ * @note This method does not perform dynamic deallocation. It releases a
+ *       buffer to the pool allocated during rtems_regulator_create().
+ */
+rtems_status_code rtems_regulator_release_buffer(
+  rtems_regulator_instance   *regulator,
+  void                       *buffer
+);
+
+/**
+ * @ingroup RegulatorAPI
+ *
+ * @brief Send to regulator instance
+ *
+ * This method is used by the producer to send a @a message to the
+ * @a regulator for later delivery by the output task. The message is
+ * contained in the memory pointed to by @a message and is @a length
+ * bytes in length.
+ *
+ * It is assumed that the @a message buffer has been filled in with
+ * application content to deliver and that the @a message buffer was
+ * obtained via rtems_regulator_obtain_buffer().
+ *
+ * If the rtems_regulator_send() is successful, the buffer is automatically
+ * released to the pool. If unsuccessful or the application decides not
+ * to send the message, it is the application's responsibility to
+ * release the buffer.
+ *
+ * @param[in] regulator is the regulator instance to operate upon
+ * @param[out] message points to the message to deliver
+ * @param[out] length is the size of the message in bytes
+ *
+ * @return an RTEMS status code indicating success or failure.
+ *
+ */
+rtems_status_code rtems_regulator_send(
+  rtems_regulator_instance  *regulator,
+  void                      *message,
+  size_t                     length
+);
+
+#endif /* REGULATOR_H */
diff --git a/cpukit/include/rtems/regulatorimpl.h b/cpukit/include/rtems/regulatorimpl.h
new file mode 100644
index 0000000000..dfbcb04214
--- /dev/null
+++ b/cpukit/include/rtems/regulatorimpl.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @defgroup RegulatorInternalAPI Regulator API Internals
+ *
+ * @brief Regulator Internal Information
+ *
+ * This concerns implementation information about the Regulator.
+ */
+
+/**
+ * @ingroup RegulatorInternalAPI
+ *
+ * @file
+ *
+ * @brief Regulator Library Implementation Support
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef RTEMS_REGULATORIMPL_H
+#define RTEMS_REGULATORIMPL_H
+
+#include <rtems/chain.h>
+
+
+/**
+ * @ingroup RegulatorInternalAPI
+ *
+ * This constant is used to indicate the regulator instance is initialized.
+ */
+#define REGULATOR_INITIALIZED 0xDeadF00d
+
+/**
+ * @ingroup RegulatorInternalAPI
+ *
+ * @brief Regulator Message Instance Management Structure
+ */
+typedef struct {
+  /** This points to the message contents. */
+  void   *buffer;
+  /** This is the length of the message. */
+  size_t  length;
+} _Regulator_Message_t;
+
+/**
+ * @ingroup RegulatorInternalAPI
+ *
+ * @brief Regulator Instance Private Structure
+ *
+ * An instance of this structure is allocated per regulator instance.
+ */
+typedef struct {
+  /** Has magic value when instance is usable */
+  uint32_t   initialized;
+
+  /** Attributes for this instance -- copied from user */
+  rtems_regulator_attributes  Attributes;
+
+  /** Pointer to allocated message memory */
+  void *message_memory;
+
+  /** Pool of unused messages */
+  rtems_id  messages_partition_id;
+
+  /** Message queue of pending outgoing messages */
+  rtems_id  queue_id;
+
+  /** Id of thread performing output */
+  rtems_id  output_thread_id;
+
+  /** Id of period used by output thread */
+  rtems_id  output_thread_period_id;
+
+} _Regulator_Control;
+
+#endif /* RTEMS_REGULATORIMPL_H */
diff --git a/cpukit/regulator/regulator.c b/cpukit/regulator/regulator.c
new file mode 100644
index 0000000000..33a6b33950
--- /dev/null
+++ b/cpukit/regulator/regulator.c
@@ -0,0 +1,487 @@
+/* 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/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
+ */
+static rtems_task _Regulator_Output_task_body(
+  rtems_task_argument arg
+)
+{
+  _Regulator_Control   *the_regulator;
+  rtems_status_code     sc;
+  size_t                to_dequeue;
+  _Regulator_Message_t  regulator_message;
+  size_t                regulator_message_size;
+
+  /*
+   * The argument passed in cannot be NULL if the regulator_create worked.
+   */
+  the_regulator = (_Regulator_Control *) arg;
+
+  /**
+   * 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->output_thread_period_id
+  );
+  if (sc != RTEMS_SUCCESSFUL) {
+    rtems_task_exit();
+  }
+
+  /**
+   * Loop on the rate_monotonic_period() based on the specified period.
+   */
+  while (1) {
+    sc = rtems_rate_monotonic_period(
+      the_regulator->output_thread_period_id,
+      the_regulator->Attributes.output_thread_period
+    );
+    (void) sc;
+
+    /**
+     * 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,
+        &regulator_message,
+        &regulator_message_size,
+        RTEMS_NO_WAIT,
+        0
+      );
+      if (sc != RTEMS_SUCCESSFUL) {
+        break;
+      }
+
+      the_regulator->Attributes.deliverer(
+        the_regulator->Attributes.deliverer_context,
+        regulator_message.buffer,
+        regulator_message.length
+      );
+
+      /**
+       * The message was successfully delivered so return the buffer to the pool.
+       */
+      sc = rtems_partition_return_buffer(
+	the_regulator->messages_partition_id,
+        regulator_message.buffer
+      );
+      (void) sc;
+    }
+  }
+
+}
+
+/**
+ * @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 rtems_regulator_create() and in
+ * rtems_regulator_delete().
+ *
+ * @param[in] the_regulator is the instance to operate upon
+ */
+static void _Regulator_Free_helper(
+  _Regulator_Control *the_regulator
+)
+{
+  (void) rtems_rate_monotonic_delete(the_regulator->output_thread_period_id);
+
+  (void) rtems_task_delete(the_regulator->output_thread_id);
+
+  (void) rtems_message_queue_delete(the_regulator->queue_id);
+
+  (void) rtems_partition_delete(the_regulator->messages_partition_id);
+
+  if (the_regulator->message_memory) {
+    free(the_regulator->message_memory);
+  }
+
+  the_regulator->initialized = 0;
+  free(the_regulator);
+}
+
+/**
+ * @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) {
+    return RTEMS_INVALID_ADDRESS;
+  }
+
+  if (!regulator) {
+    return RTEMS_INVALID_ADDRESS;
+  }
+
+  /**
+   * Verify attributes are OK. Some are checked by calls to object create
+   * methods. Specifically the following are not checked:
+   *
+   * - output_thread_priority by rtems_task_create()
+   * - output_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;
+  }
+
+  /**
+   * Allocate memory for regulator instance
+   */
+  the_regulator = (_Regulator_Control *) calloc(sizeof(_Regulator_Control), 1);
+  if (!the_regulator) {
+    return RTEMS_NO_MEMORY;
+  }
+
+  /**
+   * We do NOT want the output_thread_id field to be initialized to 0. If the
+   * 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->output_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) {
+    _Regulator_Free_helper(the_regulator);
+    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);
+    return sc;
+  }
+
+  /**
+   * Create the message queue between the sender and output thread
+   */
+  sc = rtems_message_queue_create(
+    rtems_build_name('S', 'N', 'D', 'Q'),
+    attributes->maximum_messages,
+    sizeof(_Regulator_Message_t),
+    RTEMS_DEFAULT_ATTRIBUTES,
+    &the_regulator->queue_id
+  );
+  if (sc != RTEMS_SUCCESSFUL) {
+    _Regulator_Free_helper(the_regulator);
+    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->output_thread_priority,
+    attributes->output_thread_stack_size,
+    RTEMS_DEFAULT_MODES,
+    RTEMS_DEFAULT_ATTRIBUTES,
+    &the_regulator->output_thread_id
+  );
+  if (sc != RTEMS_SUCCESSFUL) {
+    _Regulator_Free_helper(the_regulator);
+    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.
+   */
+  sc = rtems_task_start(
+    the_regulator->output_thread_id,
+    _Regulator_Output_task_body,
+    (rtems_task_argument) the_regulator
+  );
+  (void) sc;
+
+  /**
+   * 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;
+}
+
+/**
+ * @ingroup RegulatorInternalAPI
+ */
+rtems_status_code rtems_regulator_delete(
+  rtems_regulator_instance    *regulator
+)
+{
+  _Regulator_Control *the_regulator;
+
+  the_regulator = (_Regulator_Control *) regulator;
+
+  /**
+   * Validate the arguments and ensure the regulator was successfully
+   * initialized.
+   */
+  if (!the_regulator) {
+    return RTEMS_INVALID_ADDRESS;
+  }
+
+  if (the_regulator->initialized != REGULATOR_INITIALIZED) {
+    return RTEMS_INCORRECT_STATE;
+  }
+
+  /**
+   * Free the resources associated with this regulator instance.
+   */
+  _Regulator_Free_helper(the_regulator);
+
+  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  sc;
+
+  the_regulator = (_Regulator_Control *) regulator;
+
+  /**
+   * Validate the arguments and ensure the regulator was successfully
+   * initialized.
+   */
+  if (!the_regulator) {
+    return RTEMS_INVALID_ADDRESS;
+  }
+
+  if (the_regulator->initialized != REGULATOR_INITIALIZED) {
+    return RTEMS_INCORRECT_STATE;
+  }
+
+  /**
+   * Allocate a buffer for the user application from the buffer pool managed
+   * by an Classic API partition.
+   */
+  sc = rtems_partition_get_buffer(
+    the_regulator->messages_partition_id,
+    buffer
+  );
+
+  return sc;
+}
+
+/**
+ * @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  sc;
+
+  the_regulator = (_Regulator_Control *) regulator;
+
+  /**
+   * Validate the arguments and ensure the regulator was successfully
+   * initialized.
+   */
+  if (!the_regulator) {
+    return RTEMS_INVALID_ADDRESS;
+  }
+
+  if (the_regulator->initialized != REGULATOR_INITIALIZED) {
+    return RTEMS_INCORRECT_STATE;
+  }
+
+  /**
+   * Deallocate the buffer to the buffer pool managed by a Classic
+   * API partition.
+   */
+  sc = rtems_partition_return_buffer(
+    the_regulator->messages_partition_id,
+    buffer
+  );
+
+  return sc;
+}
+
+/**
+ * @ingroup RegulatorInternalAPI
+ */
+rtems_status_code rtems_regulator_send(
+  rtems_regulator_instance *regulator,
+  void                     *message,
+  size_t                    length
+)
+{
+  _Regulator_Control   *the_regulator;
+  rtems_status_code     sc;
+  _Regulator_Message_t  regulator_message;
+
+  the_regulator = (_Regulator_Control *) regulator;
+
+  /**
+   * Validate the arguments and ensure the regulator was successfully
+   * initialized.
+   */
+  if (!message) {
+    return RTEMS_INVALID_ADDRESS;
+  }
+
+  if (length == 0) {
+    return RTEMS_INVALID_NUMBER;
+  }
+
+  if (!the_regulator) {
+    return RTEMS_INVALID_ADDRESS;
+  }
+
+  if (the_regulator->initialized != REGULATOR_INITIALIZED) {
+    return RTEMS_INCORRECT_STATE;
+  }
+
+  /**
+   * 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.
+   */
+  sc = rtems_message_queue_send(
+    the_regulator->queue_id,
+    &regulator_message,
+    sizeof(_Regulator_Message_t)
+  );
+  if (sc != RTEMS_SUCCESSFUL) {
+    return sc;
+  }
+
+  return sc;
+}
diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml
index 3654e7f94a..670c3cd479 100644
--- a/spec/build/cpukit/librtemscpu.yml
+++ b/spec/build/cpukit/librtemscpu.yml
@@ -150,6 +150,8 @@ install:
   - cpukit/include/rtems/pty.h
   - cpukit/include/rtems/qreslib.h
   - cpukit/include/rtems/ramdisk.h
+  - cpukit/include/rtems/regulator.h
+  - cpukit/include/rtems/regulatorimpl.h
   - cpukit/include/rtems/rbheap.h
   - cpukit/include/rtems/rbtree.h
   - cpukit/include/rtems/record.h
@@ -1183,6 +1185,7 @@ source:
 - cpukit/posix/src/vfork.c
 - cpukit/posix/src/wait.c
 - cpukit/posix/src/waitpid.c
+- cpukit/regulator/regulator.c
 - cpukit/rtems/src/barrier.c
 - cpukit/rtems/src/barriercreate.c
 - cpukit/rtems/src/barrierdelete.c
diff --git a/spec/build/testsuites/libtests/grp.yml b/spec/build/testsuites/libtests/grp.yml
index be340c8ab6..7ca0f7dfa1 100644
--- a/spec/build/testsuites/libtests/grp.yml
+++ b/spec/build/testsuites/libtests/grp.yml
@@ -230,6 +230,8 @@ links:
   uid: record01
 - role: build-dependency
   uid: record02
+- role: build-dependency
+  uid: regulator01
 - role: build-dependency
   uid: rtmonuse
 - role: build-dependency
diff --git a/spec/build/testsuites/libtests/regulator01.yml b/spec/build/testsuites/libtests/regulator01.yml
new file mode 100644
index 0000000000..776d0ae34b
--- /dev/null
+++ b/spec/build/testsuites/libtests/regulator01.yml
@@ -0,0 +1,21 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+build-type: test-program
+cflags: []
+copyrights:
+- Copyright (C) 2023 OAR Corporation
+cppflags: []
+cxxflags: []
+enabled-by: true
+features: c cprogram
+includes: []
+ldflags:
+- -Wl,--wrap=malloc
+links: []
+source:
+- testsuites/libtests/regulator01/regulator01.c
+- testsuites/libtests/regulator01/rtems_config.c
+stlib: []
+target: testsuites/libtests/regulator01.exe
+type: build
+use-after: []
+use-before: []
diff --git a/testsuites/libtests/regulator01/regulator01.c b/testsuites/libtests/regulator01/regulator01.c
new file mode 100644
index 0000000000..408a22dd48
--- /dev/null
+++ b/testsuites/libtests/regulator01/regulator01.c
@@ -0,0 +1,1156 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @defgroup RegulatorTests Regulator Test Cases
+ *
+ * @brief Unit test cases for the Regulator
+ *
+ * This is a set of unit test cases for the regulator.
+ */
+
+/**
+ * @ingroup RegulatorTests
+ *
+ * @file
+ *
+ * @brief Test 01 for Regulator Library
+ */
+
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rtems.h>
+#include <rtems/test-info.h>
+#include <tmacros.h>
+
+#include <rtems/regulator.h>
+
+/**
+ * @brief Regulator Test Name
+ */
+const char rtems_test_name[] = "Regulator 1";
+
+/*
+ * Prototypes for wrapped functions
+ */
+void *__wrap_malloc(size_t size);
+void *__real_malloc(size_t size);
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Calloc Wrapper Trigger Count
+ */
+static int malloc_trigger_count;
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Calloc Wrapper Call Count
+ */
+static int malloc_call_count;
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Calloc Wrapper Trigger enable
+ */
+static bool malloc_trigger_enabled;
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Enable Calloc Wrapper Trigger
+ */
+static void malloc_trigger_enable(
+  int trigger_count
+)
+{
+  malloc_trigger_enabled = true;
+  malloc_trigger_count = trigger_count;
+  malloc_call_count = 0;
+}
+/**
+ * @ingroup RegulatorTests
+ * @brief Reset Calloc Wrapper Trigger and Count
+ */
+static void malloc_trigger_reset(void)
+{
+  malloc_trigger_enabled = 0;
+  malloc_trigger_count = 0;
+  malloc_call_count = 0;
+}
+/**
+ * @ingroup RegulatorTests
+ * @brief Calloc Wrapper to Trigger Allocation Errors
+ */
+void *__wrap_malloc(size_t size)
+{
+  if (malloc_trigger_enabled) {
+    malloc_call_count++;
+    if (malloc_call_count == malloc_trigger_count) {
+      return NULL;
+    }
+  }
+
+  return __real_malloc(size);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Empty Deliver Method for Testing
+ */
+static void test_regulator_deliverer(
+  void     *context,
+  void     *message,
+  size_t    length
+)
+{
+  (void) context;
+  (void) message;
+  (void) length;
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Maximum length of a test message that is delivered
+ */
+#define MAXIMUM_MESSAGE_LENGTH 32
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Maximum number of test messages to buffer
+ */
+#define MAXIMUM_MESSAGES_TO_BUFFER 10
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Structure for capturing messages as delivered
+ */
+typedef struct {
+  rtems_interval processed;
+  char           message[MAXIMUM_MESSAGE_LENGTH];
+} message_log_t;
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Set of Delivered messages
+ */
+message_log_t delivered_messages[MAXIMUM_MESSAGES_TO_BUFFER];
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Count of Delivered messages
+ */
+int delivered_message_count;
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Reset Delivered Message Set
+ *
+ * This is used at the beginning of a test case which is going to
+ * check that message contents and delivery times were as expected.
+ */
+static void delivered_messages_reset(void)
+{
+  delivered_message_count = 0;
+  memset(delivered_messages, 0xc5, sizeof(delivered_messages));
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Empty Deliver Method for Testing
+ *
+ * This deliverer method implementation logs the messages along with
+ * their time of arrival. This is used by the test cases to verify
+ * proper delivery.
+ */
+static void test_regulator_deliverer_logger(
+  void     *context,
+  void     *message,
+  size_t    length
+)
+{
+  (void) context;
+
+  size_t         len;
+  rtems_interval ticks;
+
+  len = strnlen(message, MAXIMUM_MESSAGE_LENGTH) + 1;
+  rtems_test_assert(len = length);
+
+  ticks = rtems_clock_get_ticks_since_boot();
+
+  delivered_messages[delivered_message_count].processed = ticks;
+
+  strcpy(delivered_messages[delivered_message_count].message, message);
+
+  delivered_message_count++;
+}
+
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Helper to create a Regulator instance
+ *
+ * This helper creates a regulator instance with some arbitrary attributes.
+ * This is used in multiple test cases to have a valie regulator instance to
+ * trigger error cases.
+ */
+static rtems_regulator_instance *test_regulator_create_regulator_OK(void)
+{
+  rtems_status_code     sc;
+  rtems_regulator_attributes  attributes = {
+    .deliverer = test_regulator_deliverer,
+    .deliverer_context = NULL,
+    .maximum_message_size = 16,
+    .maximum_messages = 10,
+    .output_thread_priority = 16,
+    .output_thread_stack_size = 0,
+    .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000),
+    .maximum_to_dequeue_per_period = 3
+  };
+  rtems_regulator_instance   *regulator;
+
+  regulator = NULL;
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  rtems_test_assert(regulator != NULL);
+
+  return regulator;
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create() maximum_to_dequeue_per_period
+ * attributes error
+ *
+ * This unit test verifies that rtems_regulator_create() returns an error when
+ * the maximum_to_dequeue_per_period attribute is zero.
+ */
+static void test_regulator_create_max_dequeue_zero(void)
+{
+  rtems_status_code     sc;
+  rtems_regulator_attributes  attributes = {
+    .deliverer = test_regulator_deliverer,
+    .deliverer_context = NULL,
+    .maximum_message_size = 16,
+    .maximum_messages = 10,
+    .output_thread_priority = 16,
+    .output_thread_stack_size = 0,
+    .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000),
+    .maximum_to_dequeue_per_period = 0
+  };
+  rtems_regulator_instance   *regulator;
+
+  regulator = NULL;
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_INVALID_NUMBER);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create() NULL attributes error
+ *
+ * This unit test verifies that rtems_regulator_create() returns an error when
+ * the attributes argument is NULL.
+ */
+static void test_regulator_create_null_attributes(void)
+{
+  rtems_status_code          sc;
+  rtems_regulator_instance  *regulator;
+
+  sc = rtems_regulator_create(NULL, &regulator);
+  rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create NULL regulator error
+ *
+ * This unit test verifies that rtems_regulator_create() returns an error when
+ * the regulator argument is NULL.
+ */
+static void test_regulator_create_null_regulator(void)
+{
+  rtems_status_code           sc;
+  rtems_regulator_attributes  attributes;
+
+  sc = rtems_regulator_create(&attributes, NULL);
+  rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create deliverer is NULL
+ *
+ * This unit test verifies that rtems_regulator_create() returns an error when
+ * the the attributes deliverer field is NULL.
+ */
+static void test_regulator_create_deliverer_is_null(void)
+{
+  rtems_status_code           sc;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance   *regulator;
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = NULL;
+  attributes.maximum_messages              = 0;
+  attributes.maximum_message_size          = 16;
+  attributes.maximum_to_dequeue_per_period = 1;
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create maximum_messages is 0 error
+ *
+ * This unit test verifies that rtems_regulator_create() returns an error when
+ * the the attributes maximum_messages field is 0.
+ */
+static void test_regulator_create_maximum_messages_is_zero(void)
+{
+  rtems_status_code           sc;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance   *regulator;
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = test_regulator_deliverer;
+  attributes.maximum_messages              = 0;
+  attributes.maximum_message_size          = 16;
+  attributes.maximum_to_dequeue_per_period = 1;
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_INVALID_NUMBER);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create maximum_message_size is 0 error
+ *
+ * This unit test verifies that rtems_regulator_create() returns an error when
+ * the the attributes maximum_message_size field is 0.
+ */
+static void test_regulator_create_maximum_message_size_is_zero(void)
+{
+  rtems_status_code           sc;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance   *regulator;
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = test_regulator_deliverer;
+  attributes.maximum_messages              = 10;
+  attributes.maximum_message_size          = 0;
+  attributes.maximum_to_dequeue_per_period = 1;
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_INVALID_SIZE);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create maximum_to_dequeue_per_period is 0 error
+ *
+ * This unit test verifies that rtems_regulator_create() returns an error when
+ * the the attributes maximum_to_dequeue_per_period field is 0.
+ */
+static void test_regulator_create_maximum_to_dequeue_per_period_is_zero(void)
+{
+  rtems_status_code           sc;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance   *regulator;
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer            = test_regulator_deliverer;
+  attributes.maximum_messages     = 10;
+  attributes.maximum_message_size = 0;
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_INVALID_SIZE);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create returns error on failure to allocate regulator
+ *
+ * This unit test verifies that rtems_regulator_create() returns an error when
+ * it is unable to allocate the mmemory for the regulator instance.
+ */
+static void test_regulator_create_malloc_regulator_fails(void)
+{
+  rtems_status_code           sc;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance         *regulator;
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = test_regulator_deliverer;
+  attributes.maximum_messages              = 10;
+  attributes.maximum_message_size          = 16;
+  attributes.output_thread_priority        = 32;
+  attributes.maximum_to_dequeue_per_period = 1;
+  attributes.output_thread_period          = RTEMS_MILLISECONDS_TO_TICKS(1000);
+
+  malloc_trigger_enable(1);
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_NO_MEMORY);
+
+  malloc_trigger_reset();
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create returns error on failure to allocate buffers
+ *
+ * This unit test verifies that rtems_regulator_create() returns an error when
+ * it is unable to allocate the mmemory for the regulator buffers.
+ */
+static void test_regulator_create_malloc_buffers_fails(void)
+{
+  rtems_status_code           sc;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance         *regulator;
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = test_regulator_deliverer;
+  attributes.maximum_messages              = 10;
+  attributes.maximum_message_size          = 16;
+  attributes.output_thread_priority        = 32;
+  attributes.maximum_to_dequeue_per_period = 1;
+  attributes.output_thread_period          = RTEMS_MILLISECONDS_TO_TICKS(1000);
+
+  malloc_trigger_enable(2);
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_NO_MEMORY);
+
+  malloc_trigger_reset();
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create and delete work
+ *
+ * This unit test verifies that rtems_regulator_create() can successfully create
+ * the the attributes output_thread_priority field is 0.
+ */
+static void test_regulator_create_output_thread_priority_is_zero(void)
+{
+  rtems_status_code           sc;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance         *regulator;
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = test_regulator_deliverer;
+  attributes.maximum_messages              = 10;
+  attributes.maximum_message_size          = 16;
+  attributes.output_thread_priority        = 0;
+  attributes.maximum_to_dequeue_per_period = 1;
+  attributes.output_thread_period          = RTEMS_MILLISECONDS_TO_TICKS(1000);
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_INVALID_PRIORITY);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create rtems_partition_create error
+ *
+ * This unit test verifies that rtems_regulator_create() correctly returns an
+ * error when the call to rtems_partition_create() fails.
+ */
+static void test_regulator_create_partition_create_fails(void)
+{
+  rtems_status_code           sc;
+  rtems_id                    partition_id;
+  unsigned long               partition_area[16];
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance   *regulator;
+
+  sc = rtems_partition_create(
+    rtems_build_name('T', 'P', 'T', 'P'),
+    partition_area,
+    16 * sizeof(unsigned long),
+    2 * sizeof(unsigned long),
+    RTEMS_DEFAULT_ATTRIBUTES,
+    &partition_id
+  );
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = test_regulator_deliverer;
+  attributes.maximum_messages              = 10;
+  attributes.maximum_message_size          = 16;
+  attributes.output_thread_priority        = 8;
+  attributes.maximum_to_dequeue_per_period = 1;
+  attributes.output_thread_period          = RTEMS_MILLISECONDS_TO_TICKS(1000);
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_TOO_MANY);
+
+  sc = rtems_partition_delete(partition_id);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create rtems_message_queue_create error
+ *
+ * This unit test verifies that rtems_regulator_create() correctly returns an
+ * error when the call to rtems_message_queue_create() fails.
+ */
+static void test_regulator_create_message_queue_create_fails(void)
+{
+  rtems_status_code           sc;
+  rtems_id                    queue_id;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance   *regulator;
+
+  sc = rtems_message_queue_create(
+    rtems_build_name('T', 'Q', 'T', 'Q'),
+    4,
+    sizeof(unsigned long),
+    RTEMS_DEFAULT_ATTRIBUTES,
+    &queue_id
+  );
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = test_regulator_deliverer;
+  attributes.maximum_messages              = 10;
+  attributes.maximum_message_size          = 16;
+  attributes.output_thread_priority        = 8;
+  attributes.maximum_to_dequeue_per_period = 1;
+  attributes.output_thread_period          = RTEMS_MILLISECONDS_TO_TICKS(1000);
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_TOO_MANY);
+
+  sc = rtems_message_queue_delete(queue_id);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_create rtems_task_create error
+ *
+ * This unit test verifies that rtems_regulator_create() correctly returns an
+ * error when the call to rtems_task_create() fails.
+ */
+static void test_regulator_create_task_create_fails(void)
+{
+  rtems_status_code           sc;
+  rtems_id                    task_id;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance   *regulator;
+
+  sc = rtems_task_create(
+    rtems_build_name('T', 'T', 'T', 'T'),
+    80,
+    0,
+    RTEMS_DEFAULT_MODES,
+    RTEMS_DEFAULT_ATTRIBUTES,
+    &task_id
+  );
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = test_regulator_deliverer;
+  attributes.maximum_messages              = 10;
+  attributes.maximum_message_size          = 16;
+  attributes.output_thread_priority        = 8;
+  attributes.maximum_to_dequeue_per_period = 1;
+  attributes.output_thread_period          = RTEMS_MILLISECONDS_TO_TICKS(1000);
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_TOO_MANY);
+
+  sc = rtems_task_delete(task_id);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify Regulator Output Thread Handles Error on Period Create
+ *
+ * This unit test verifies that regulator output thread correctly exits
+ * when the call to rtems_rate_monotonic_create() fails.
+ *
+ * This error condition/path cannot be directly detected via a return code,
+ * It is verified via a debugger or code coverage reports.
+ */
+static void test_regulator_create_rate_monotonic_create_fails(void)
+{
+  rtems_status_code           sc;
+  rtems_id                    period_id;
+  rtems_regulator_attributes  attributes;
+  rtems_regulator_instance   *regulator;
+
+  sc = rtems_rate_monotonic_create(
+    rtems_build_name('T', 'S', 'T', 'P'),
+    &period_id
+  );
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  (void) memset(&attributes, 0, sizeof(rtems_regulator_attributes));
+
+  attributes.deliverer                     = test_regulator_deliverer;
+  attributes.maximum_messages              = 10;
+  attributes.maximum_message_size          = 16;
+  attributes.output_thread_priority        = 8;
+  attributes.maximum_to_dequeue_per_period = 1;
+  attributes.output_thread_period          = RTEMS_MILLISECONDS_TO_TICKS(1000);
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  /*
+   * Let the output thread execute and encounter the create error.
+   */
+
+  sleep(1);
+
+  /*
+   * Now deallocate the resources allocated earlier
+   */
+  sc = rtems_regulator_delete(regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  sc = rtems_rate_monotonic_delete(period_id);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_delete NULL regulator error
+ *
+ * This unit test verifies that rtems_regulator_delete() returns an error when
+ * the regulator argument is NULL.
+ */
+static void test_regulator_delete_null_regulator(void)
+{
+  rtems_status_code     sc;
+
+  sc = rtems_regulator_delete(NULL);
+  rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_delete uninitialized regulator error
+ *
+ * This unit test verifies that rtems_regulator_delete() returns an error when
+ * the regulator argument is uninitialized.
+ */
+static void test_regulator_delete_uninitialized_regulator(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance  regulator;
+
+  (void) memset(&regulator, 0, sizeof(regulator));
+
+  sc = rtems_regulator_delete(&regulator);
+  rtems_test_assert(sc == RTEMS_INCORRECT_STATE);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_delete successful case
+ *
+ * This unit test verifies that rtems_regulator_delete() can be successfully
+ * deleted.
+ */
+static void test_regulator_delete_OK(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance *regulator;
+
+  regulator = test_regulator_create_regulator_OK();
+  rtems_test_assert(regulator != NULL);
+
+  sc = rtems_regulator_delete(regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_obtain_buffer NULL regulator error
+ *
+ * This unit test verifies that rtems_regulator_obtain_buffer() returns an error when
+ * the regulator argument is NULL.
+ */
+static void test_regulator_obtain_buffer_null_regulator(void)
+{
+  rtems_status_code   sc;
+  void               *buffer;
+
+  sc = rtems_regulator_obtain_buffer(NULL, &buffer);
+  rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_obtain_buffer uninitialized regulator error
+ *
+ * This unit test verifies that rtems_regulator_obtain_buffer() returns an error when
+ * the regulator argument is uninitialized.
+ */
+static void test_regulator_obtain_buffer_uninitialized_regulator(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance  regulator;
+  void                     *buffer;
+
+  (void) memset(&regulator, 0, sizeof(regulator));
+
+  sc = rtems_regulator_obtain_buffer(&regulator, &buffer);
+  rtems_test_assert(sc == RTEMS_INCORRECT_STATE);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_obtain_buffer successful case
+ *
+ * This unit test verifies that rtems_regulator_obtain_buffer() can be successfully
+ * obtained from an initialized regulator.
+ */
+static void test_regulator_obtain_buffer_OK(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance *regulator;
+  void                     *buffer;
+
+  regulator = test_regulator_create_regulator_OK();
+  rtems_test_assert(regulator != NULL);
+
+  sc = rtems_regulator_obtain_buffer(regulator, &buffer);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  rtems_test_assert(buffer != NULL);
+
+  /*
+   * Not really testing this here but cannot delete underlying partition
+   * if there are buffers outstanding.
+   */
+  sc = rtems_regulator_release_buffer(regulator, buffer);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  rtems_test_assert(buffer != NULL);
+
+  sc = rtems_regulator_delete(regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_release_buffer NULL regulator error
+ *
+ * This unit test verifies that rtems_regulator_release_buffer() returns an error when
+ * the regulator argument is NULL.
+ */
+static void test_regulator_release_buffer_null_regulator(void)
+{
+  rtems_status_code   sc;
+  void               *buffer;
+
+  sc = rtems_regulator_release_buffer(NULL, &buffer);
+  rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_release_buffer uninitialized regulator error
+ *
+ * This unit test verifies that rtems_regulator_release_buffer() returns an
+ * error when the regulator argument is uninitialized.
+ */
+static void test_regulator_release_buffer_uninitialized_regulator(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance  regulator;
+  void                     *buffer;
+
+  (void) memset(&regulator, 0, sizeof(regulator));
+
+  sc = rtems_regulator_release_buffer(&regulator, &buffer);
+  rtems_test_assert(sc == RTEMS_INCORRECT_STATE);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_release_buffer successful case
+ *
+ * This unit test verifies that rtems_regulator_release_buffer() can be successfully
+ * invoked with a buffer previously allocated by rtems_regulator_obtain_buffer().
+ */
+static void test_regulator_release_buffer_OK(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance *regulator;
+  void                     *buffer;
+
+  regulator = test_regulator_create_regulator_OK();
+  rtems_test_assert(regulator != NULL);
+
+  sc = rtems_regulator_obtain_buffer(regulator, &buffer);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  rtems_test_assert(buffer != NULL);
+
+  sc = rtems_regulator_release_buffer(regulator, buffer);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  sc = rtems_regulator_delete(regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_send NULL regulator error
+ *
+ * This unit test verifies that rtems_regulator_send() returns an error when
+ * the regulator argument is NULL.
+ */
+static void test_regulator_send_null_regulator(void)
+{
+  rtems_status_code   sc;
+  void               *buffer;
+  size_t              length;
+
+  buffer = &length;
+  length = sizeof(size_t);
+
+  sc = rtems_regulator_send(NULL, buffer, length);
+  rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_send NULL message error
+ *
+ * This unit test verifies that rtems_regulator_send() returns an error when
+ * the message argument is NULL.
+ */
+static void test_regulator_send_null_message(void)
+{
+  rtems_status_code         sc;
+  size_t                    length;
+  rtems_regulator_instance  regulator;
+
+  length = sizeof(size_t);
+
+  sc = rtems_regulator_send(&regulator, NULL, length);
+  rtems_test_assert(sc == RTEMS_INVALID_ADDRESS);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_send zero length message error
+ *
+ * This unit test verifies that rtems_regulator_send() returns an
+ * error when the message length is 0.
+ */
+static void test_regulator_send_length_is_zero(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance  regulator;
+  void                     *buffer;
+  size_t                    length;
+
+  buffer = &length;
+  length = 0;
+
+  sc = rtems_regulator_send(&regulator, buffer, length);
+  rtems_test_assert(sc == RTEMS_INVALID_NUMBER);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_send uninitialized regulator error
+ *
+ * This unit test verifies that rtems_regulator_send() returns an
+ * error when the regulator argument is uninitialized.
+ */
+static void test_regulator_send_uninitialized_regulator(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance  regulator;
+  void                     *buffer;
+  size_t                    length;
+
+  buffer = &length;
+  length = sizeof(size_t);
+
+  (void) memset(&regulator, 0, sizeof(regulator));
+
+  sc = rtems_regulator_send(&regulator, buffer, length);
+  rtems_test_assert(sc == RTEMS_INCORRECT_STATE);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_send and output thread delivers message
+ *
+ * This unit test verifies that when the regulator is successfully initialized
+ * and used as expected, a message sent via rtems_regulator_send() is delivered as
+ * expected.
+ */
+static void test_regulator_send_one_message_OK(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance *regulator;
+  char                      message[MAXIMUM_MESSAGE_LENGTH];
+  void                     *buffer;
+  size_t                    length;
+  int                       match;
+  rtems_regulator_attributes  attributes = {
+    .deliverer = test_regulator_deliverer_logger,
+    .deliverer_context = NULL,
+    .maximum_message_size = 16,
+    .maximum_messages = 10,
+    .output_thread_priority = 16,
+    .output_thread_stack_size = 0,
+    .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(250),
+    .maximum_to_dequeue_per_period = 3
+  };
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  rtems_test_assert(regulator != NULL);
+
+  delivered_messages_reset();
+
+  sc = rtems_regulator_obtain_buffer(regulator, &buffer);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  rtems_test_assert(buffer != NULL);
+
+  length = snprintf(message, MAXIMUM_MESSAGE_LENGTH, "message %d", 1024) + 1;
+  strcpy(buffer, message);
+
+  sc = rtems_regulator_send(regulator, buffer, length);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  sleep(1);
+
+  rtems_test_assert(delivered_message_count == 1);
+  match = strncmp(
+    delivered_messages[0].message,
+    message,
+    MAXIMUM_MESSAGE_LENGTH
+  );
+  rtems_test_assert(match == 0);
+
+  sc = rtems_regulator_delete(regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @ingroup RegulatorTests
+ * @brief Verify rtems_regulator_send and output thread delivers messages
+ *
+ * This unit test verifies that when the regulator is successfully initialized
+ * and used as expected, and multiple messages are sent via rtems_regulator_send()
+ * that they are delivered as expected.
+ */
+#include <stdio.h>
+static void test_regulator_send_multiple_messages_OK(void)
+{
+  rtems_status_code         sc;
+  rtems_regulator_instance *regulator;
+  char                      message[MAXIMUM_MESSAGE_LENGTH];
+  void                     *buffer;
+  size_t                    length;
+  int                       match;
+  int                       i;
+  time_t                    base_time;
+  time_t                    tmp_time;
+  rtems_interval            base_ticks;
+  rtems_interval            ticks;
+  rtems_interval            ticks_per_second;
+
+  rtems_regulator_attributes  attributes = {
+    .deliverer = test_regulator_deliverer_logger,
+    .deliverer_context = NULL,
+    .maximum_message_size = MAXIMUM_MESSAGE_LENGTH,
+    .maximum_messages = 10,
+    .output_thread_priority = 16,
+    .output_thread_stack_size = 0,
+    .output_thread_period = RTEMS_MILLISECONDS_TO_TICKS(1000),
+    .maximum_to_dequeue_per_period = 2
+  };
+
+  delivered_messages_reset();
+
+  sc = rtems_regulator_create(&attributes, &regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  rtems_test_assert(regulator != NULL);
+
+  /*
+   * Ensure the messages are sent on a second boundary to ensure the
+   * output thread will process them as expected.
+   */
+  tmp_time = time(NULL);
+  do {
+    base_time = time(NULL);
+  } while (tmp_time == base_time);
+
+  /**
+   * Send five messages as a burst which will need to be smoothly sent at
+   * the configured rate.
+   */
+  for (i=1 ; i <= 5 ; i++) {
+    sc = rtems_regulator_obtain_buffer(regulator, &buffer);
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+    rtems_test_assert(buffer != NULL);
+
+    length = snprintf(message, MAXIMUM_MESSAGE_LENGTH, "message %d", i);
+    strcpy(buffer, message);
+
+    sc = rtems_regulator_send(regulator, buffer, length);
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  }
+
+  /*
+   * Let the output thread executed and deliver the messages.
+   */
+  sleep(5);
+
+  /**
+   * Ensure the five messages were delivered as follows:
+   *
+   *   - deliver all 5
+   *   - contents are "message N" where N is 1 to 5
+   *   - message 1 and 2 delivered during the first second
+   *   - message 3 and 4 delivered during the second second
+   *   - message 5 delivered during the third second
+   *   - no further messages delivered
+   */
+
+  rtems_test_assert(delivered_message_count == 5);
+
+  for (i=0 ; i < 5 ; i++) {
+    (void) snprintf(message, MAXIMUM_MESSAGE_LENGTH, "message %d", i+1);
+// printf("%d %s\n", i, delivered_messages[i].message);
+    match = strncmp(
+      delivered_messages[i].message,
+      message,
+      MAXIMUM_MESSAGE_LENGTH
+    );
+    rtems_test_assert(match == 0);
+  }
+
+  /**
+   * Verify that messages were delivered in the proper groups. Check that
+   * the delivery time matches expectations.
+   */
+  rtems_test_assert(delivered_messages[0].processed == delivered_messages[1].processed);
+  rtems_test_assert(delivered_messages[1].processed != delivered_messages[2].processed);
+  rtems_test_assert(delivered_messages[2].processed == delivered_messages[3].processed);
+  rtems_test_assert(delivered_messages[3].processed != delivered_messages[4].processed);
+
+  /**
+   * Verify that the message groups were properly spaced temporally. They
+   * should be one second apart.
+   */
+  ticks_per_second = rtems_clock_get_ticks_per_second();
+
+  base_ticks = delivered_messages[1].processed;
+  ticks      = delivered_messages[2].processed;
+  rtems_test_assert(ticks_per_second == ticks - base_ticks);
+
+  base_ticks = delivered_messages[3].processed;
+  ticks      = delivered_messages[4].processed;
+  rtems_test_assert(ticks_per_second == ticks - base_ticks);
+
+  sc = rtems_regulator_delete(regulator);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+/* Necessary prototype */
+rtems_task test_regulator(rtems_task_argument);
+
+/**
+ * @ingroup RegulatorTestshttps://devel.rtems.org/milestone/6.1
+ * @brief Test entry task which invokes test cases
+ */
+rtems_task test_regulator(rtems_task_argument arg)
+{
+  (void) arg;
+
+  TEST_BEGIN();
+
+  malloc_trigger_reset();
+
+  test_regulator_create_max_dequeue_zero();
+  test_regulator_create_null_attributes();
+  test_regulator_create_null_regulator();
+  test_regulator_create_deliverer_is_null();
+  test_regulator_create_maximum_messages_is_zero();
+  test_regulator_create_maximum_message_size_is_zero();
+  test_regulator_create_maximum_to_dequeue_per_period_is_zero();
+  test_regulator_create_malloc_regulator_fails();
+  test_regulator_create_malloc_buffers_fails();
+  test_regulator_create_output_thread_priority_is_zero();
+  test_regulator_create_partition_create_fails();
+  test_regulator_create_message_queue_create_fails();
+  test_regulator_create_task_create_fails();
+  test_regulator_create_rate_monotonic_create_fails();
+
+  test_regulator_delete_null_regulator();
+  test_regulator_delete_uninitialized_regulator();
+  test_regulator_delete_OK();
+
+  test_regulator_obtain_buffer_null_regulator();
+  test_regulator_obtain_buffer_uninitialized_regulator();
+  test_regulator_obtain_buffer_OK();
+
+  test_regulator_release_buffer_null_regulator();
+  test_regulator_release_buffer_uninitialized_regulator();
+  test_regulator_release_buffer_OK();
+
+  test_regulator_send_null_regulator();
+  test_regulator_send_null_message();
+  test_regulator_send_length_is_zero();
+  test_regulator_send_uninitialized_regulator();
+
+  test_regulator_send_one_message_OK();
+
+  test_regulator_send_multiple_messages_OK();
+
+  TEST_END();
+
+  rtems_test_exit(0);
+}
diff --git a/testsuites/libtests/regulator01/regulator01.doc b/testsuites/libtests/regulator01/regulator01.doc
new file mode 100644
index 0000000000..7c266dc406
--- /dev/null
+++ b/testsuites/libtests/regulator01/regulator01.doc
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+# Copyright (c) 2013 Daniel Ramirez <javamonn at gmail.com>
+#
+# 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.
+#
+
+This file describes the directives and concepts tested by this test set.
+
+test set name: uid01
+
+directives:
+  + uid_read_message
+
+concepts:
+  + Tests that uid_read_message when called with a timeout greater than 0
+    ticks but less than 1 tick will correctly round this up to a 1 tick
+    timeout.
diff --git a/testsuites/libtests/regulator01/rtems_config.c b/testsuites/libtests/regulator01/rtems_config.c
new file mode 100644
index 0000000000..ca96e1b1dd
--- /dev/null
+++ b/testsuites/libtests/regulator01/rtems_config.c
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @brief RTEMS Configuration for regulator tests
+ */
+
+/*
+ *  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 <rtems.h>
+
+rtems_task test_regulator(rtems_task_argument);
+
+#include <bsp.h> /* for device driver prototypes */
+
+/* NOTICE: the clock driver is explicitly disabled */
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+#define CONFIGURE_INIT_TASK_ENTRY_POINT test_regulator
+#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
+
+/* Use hard limits to make it easier to trip object creation errors */
+#define CONFIGURE_MAXIMUM_TASKS 2
+#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES 1
+#define CONFIGURE_MAXIMUM_PARTITIONS 1
+#define CONFIGURE_MAXIMUM_PERIODS 1
+
+#define CONFIGURE_UNIFIED_WORK_AREAS
+#define CONFIGURE_MINIMUM_TASK_STACK_SIZE (8 * 1024)
+
+#define CONFIGURE_INIT
+#include <rtems/confdefs.h>
-- 
2.24.4



More information about the devel mailing list