[PATCH rtems v1] Add the Regulator Interface and test

Joel Sherrill joel at rtems.org
Fri Jul 14 19:00:42 UTC 2023


While breaking for lunch, I made a couple of decisions. More inline. Sorry.

On Fri, Jul 14, 2023 at 11:00 AM Joel Sherrill <joel at rtems.org> wrote:

> Comments embedded. V2 coming. If your comments arrive in time, I might get
> them in without a V3.
>
>
> On Sun, Jul 9, 2023 at 7:48 PM Chris Johns <chrisj at rtems.org> wrote:
>
>> Hi,
>>
>> Thanks for posting this code. My comments are below and I hope they help.
>>
>> Thanks
>> Chris
>>
>> On 8/7/2023 6:55 am, Joel Sherrill wrote:
>> > 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 +++++++
>>
>> I prefer we do not add pieces of functionality to cpukit at this level.
>> You
>> mention this is application support so would `libapp` work? It would
>> allow for
>> growth, eg logging. We have separate libraries for libdl etc so should
>> this be
>> separate or placed in libcpu? We also have libmisc and it could live
>> there?
>>
>
> Showing your age. libcpu is gone. :)
>
> I will move it under libmisc for now. libapp would just be another dumping
> ground. I wonder why shell is under libmisc other than history. lol
>
>
>>
>> FYI and unrelated I also think moving dtc, ftpd, mghttpd, compression and
>> telnetd somewhere would be good.
>>
>
> Compression is better than the hodge podge of locations that that code was
> in before. :)
>
> The network libraries should be on a path to move to rtems-net-services.
>
> But dtc is core functionality for some BSPs so I'm not sure what to do
> with it.
>
> My main concern in this area has not been to push things into external
> repos or libraries as much as make sure the directory organization of
> what we have makes sense.  Top of my list is a few things in score/ that
> I think don't belong.
>
> But you are right. This is a topic for another thread.
>
>
>>
>> >  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
>>
>> ... bursts of input ...
>>
>
> Changed in three places
>
>>
>> > + * 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
>>
>> How is the maximum burst defined?
>>
>
> I updated the main page description. The regulator buffers maximum_messages
> which is the amount of buffering the delivery thread is allowed to get
> behind. This
> could be one burst or multiple as they are delivered.
>
> I think it was covered but later. I also added more to
> rtems_regulator_create()
> about the attributes structure.
>
>
>> > + * is in messages per second. If the sender produces data too fast, the
>> > + * regulator will buffer the configured number of messages.
>>
>> What happens to the input data when the "configured number of messages"
>> limit is
>> reached?
>>
>
> The rtems_regulator_send() returns RTEMS_TOO_MANY which is from the
> underlying
> rtems_message_queue_send().
>
> I will be submitting a Classic API Guide chapter for the regulator after
> the code is merged. Each error code will be listed as is practice. The
> Doxygen
> doesn't list every error code.
>
>
>> There is no discussion about error, error responiblity and recovery.
>>
>
> I added some to rtems_regulator_send() but my recommendation to User #1
> was to do a systems analysis on the source and sink and ensure the maximum
> number of messages buffered was sufficient.
>
> FWIW this is just a message queue under the hood and I don't think we
> have guidance for sizing that. :)
>
>
>>
>> > + * 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.
>>
>> Does the regulator provide any stats, latched states or alarms to help a
>> user
>> integrate and prove the data flows are operating as designed? I have
>> successfully used high or low water marks as a way to help audit this
>> type of
>> functionality in systems. The marks are set at the system level and can be
>> manually checked or monitored to make sure no system limits are beached.
>> Slient
>> drops or overflows can be difficult to resolve.
>>
>
> User #1 had no requirements for any of that. We can discuss adding that as
> user
> requirements arise. At this point, I wouldn't know what would be
> interesting for
> statistics. I have a few ideas -- like maximum messages queued (0 would be
> the
> low mark), reflecting the period statistics, and perhaps tracking number
> of messages
> delivered per period.
>
> Message queues do not track the high water mark or if they returned too
> many.
> It would seem that a lot of what would be interesting could be information
> from the
> underlying RTEMS objects.
>
> Can we file a ticket for this as an enhancement? I can assure you that it
> won't happen
> on this round of submission but it is an interesting thing to consider
> adding.
>
>
>>
>> > + *
>> > + * 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.
>>
>> What is the context this handler is called in? Are there any contriants,
>> eg tack
>> size, performance side effects, ...?
>>
>
>  Added. There is a dedicated thread whose stack size, period, and priority
> are specified as part of the attributes passed to rtems_regulator_create().
> I added some here and tidied up the information with
> rtems_regulator_create().
>
>
>> > 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);
>>
>> What is `regulator release()`?
>>
>
> Should have been rtems_regulator_release_buffer(). Looks like I tried
> to write English rather than the method name for some reason.
>
>
>>
>> > + *     }
>> > + * @endcode
>> > + *
>> > + * The following sequence diagram shows the behavior of the Delivery
>> thread
>>
>> behaviour
>>
>
> I thought we preferred US spellings but were not particularly picky. As
> long as the
> flavor and color are correct. :)
>
>
>>
>> > + * 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.
>>
>> Could this paragraph be worded in a simpler way? It has `are that` and
>> `and that
>> during each up to the instance...`.
>>
>
> I changed it to a lead-in and numbered bullets.
>
>>
>> > + * 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.
>>
>> What can a user do with a message pointer? Can they hold it, queue it or
>> take
>> any references to it? The example is not clear because a socket write is
>> a copy
>> by value and a release implies a separate action is required and the
>> implemention automatically returns the buffer to the parition?
>>
>
> The regulator is designed to not add copies of buffers. They obtain
> buffers,
> fill them in, feed them to the regulator, and eventually process each via a
> delivery function.
>
> The use of a partition and a message queue which is only ptr/length is
> intentional
> to avoid message buffer copies.  My thinking was that the input device
> would directly
> fill in the buffer and it would not be copied until the delivery function
> handed it to
> the sink.
>
> I've added Doxygen comments on the lifespan and ownership of the buffer.
> There was already some in a couple of places and I added to it.
>
>
>> > + */
>> > +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 */
>>
>> What happens if set to 0 or 1?
>>
>
> 1 is legal per RTEMS.
>
> Good catch on 0. That is actually period status. I added a check to
> rtems_regulator_create() for this and return RTEMS_INVALID_NUMBER.
>
>
>> > +  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.
>>
>> I see a message queue create in the code?
>>
>> Is the memory worksapce of heap?
>>
>
> Currently workspace. Do you think it should be changed to construct
> and allocate this memory from the heap?  That would be V3. :)
>

I am switching from rtems_message_queue_create() to
rtems_message_queue_construct(). This frees the user from a bit of
configuration.


> I added documentation to rtems_regulator_create() to list all the resources
> it allocates.
>
>
>
>>
>> > + */
>> > +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().
>>
>> It is a requirement the send call only passes memory from the
>> rtems_regulator_obtain_buffer() call. Maybe the wording should clearly
>> state this.
>>
>
>
> Strengthened and split into two paragraphs.
>
>
>>
>> > + * 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.
>>
>> This is not true. The memory is not automatically returned to the pool by
>> the
>> send call. If the send is successful it is placed in an internal handle
>> and
>> queued on a message queue (I assume is sized to hold all buffers) and it
>> is only
>> released after the message has be read from the message queue and the
>> delivery
>> handler has returned.
>>
>
> I've cleaned up the description.
>
>
>>
>> I find the automatic and manual interactions of the API confusing. I see a
>> number of places a user can step on their own toes. It seems this code as
>> evolved because the example shows a release call in delivery handler and
>> yet
>> there is an automatic release. I wonder if the automatic release is a
>> good idea
>> as the user needs to handle a few other cases manually? The automatic
>> release
>> also implies the handling of the buffer in the delivery handler is
>> sychronous
>> and that may not suite all applications requiring buffer buffering and
>> copies.
>>
>
> The API did not evolve other than to add rtems_ naming conventions when
> User #1 asked for it to be submitted. It was conceived to not introduce
> yet another
> buffer copy. Yes, that puts some responsibility on the user but that's the
> cost of
> not copying. I thought I kept it pretty simple to use. User #1 didn't have
> any issues
> incorporating it into their application.
>
> The automatic release does assume that the delivery to the sink is by
> value and
> the buffer is safe to release back to the regulator.
>
> Do you think the delivery function should return a bool to indicate if the
> release
> should happen or not? If it returns say false, the delivery code for the
> user would
> have to release it on its own.
>

I'm going to make this change. This way there can be zero copy on the
source side
and the sink side if the delivery function is calling a method that needs
to hold the
buffer.

The use case I thought of should have been obvious to me from the beginning:

Source Side (zero copy)
===================
+ obtain buffer from regulator
+ device DMAs into buffer
+ send buffer to regulator
....
Sink Side (zero copy)
================
+ delivery method sets up DMA to output the buffer to another device
   returns false
+ When DMA is complete, releases buffer to regulator.

Sink Side (copy)
=============
+ delivery method calls something like write() which copies the buffer
   returns true
+ Delivery thread releases buffer to regulator

>
>
>
>> > + *
>> > + * @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;
>>
>> I am confused because the example has:
>>
>>   regulator release(message);
>>
>
> Changed to rtems_regulator_release_buffer() where that occurred.
>
>>
>> and I do not know what that is doing and yet here the buffer is being
>> automatically released?
>>
>
> Hopefully clearer now. Buffers are managed by a partition. I added some to
> the comment to help tie it together.
>
>>
>> There is no explanation on why the return status of the partition return
>> call
>> can be ignored?
>>
>
> I have no idea what to do with a non-successful status. I suppose the user
> could provide a function to call back but generically we have no way to
> do anything.
>

With this going into RTEMS, I can make it a debug assertion. I didn't have
that option before.

>
>
>>
>> > +    }
>> > +  }
>> > +
>> > +}
>> > +
>> > +/**
>> > + * @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);
>>
>> The task holds and uses output_thread_period_id. Is this OK? Could the
>> task end
>> up spinning, locking things up?
>>
>
> Periods have to be used by the creating thread. My experience using them is
> that this ends up being the create/use pattern.
>
>
>>
>> I think it is wrong to delete the task like this. What if the deliver
>> function
>> is being called and that code in turn has called a socket write and with
>> that a
>> bunch of states are being held when the task context is killed?
>>
>
> The user has to know it is safe to delete the instance. They likely need
> to close
> the socket before deleting the regulator. I added a bit to
> rtems_regulator_delete()
> to note that the user has to clean up for resources used by the delivery
> thread and
> may need to delete them before calling this.
>
>
>>
>> > +
>> > +  (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;
>>
>> Not needed if you free the memory.
>>
>
> The regulator instance is in the user's space. We don't free it.
>
> I will consider adding a RTEMS_REGULATOR_INITIALIZER.
>
>
>> > +  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;
>>
>> Huh?
>>
>
>
> How would it fail? I can add a check and return it but it can't fail if the
> code is correct.
>
>
>>
>> > +  /**
>> > +   * The regulator is successfully initialized. Set the initialized
>> field
>> > +   * to reflect this and return the instance pointer.
>> > +   */
>> > +  the_regulator->initialized = REGULATOR_INITIALIZED;
>>
>> I am a bit lost on the value of this variable and state?
>>
>
> regulator instance is memory allocated by the called. Just making sure it
> has been through create.
>
>
>>
>> > +
>> > +  *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;
>>
>> These repeasted checks calls could be a single check type function.
>>
>
> Maybe. Not sure it is worth it. The test had 100% statement and branch
> coverage.
>
>>
>> > +  }
>> > +
>> > +  /**
>> > +   * 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;
>>
>> I would consider making this a handle the user also need to use. I would
>> allocate a handle per buffer and change send, deliver etc to use handles.
>> I have
>> found the message pointer confusing to follow in each interface.
>>
>
> Handle to what? They obtain a buffer of a known maximum size. Providing the
> size is just to support cases where the entire buffer isn't needed.
>
>
>
>>
>> > +
>> > +  /**
>> > +   * 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.
>>
>
> This file was a bogus copy of another test. Fixed.
>
>
>> > 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>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/devel/attachments/20230714/0b56cc2c/attachment-0001.htm>


More information about the devel mailing list