[PATCH] c-user: Add self-contained objects chapter

Gedare Bloom gedare at rtems.org
Thu Feb 2 15:56:30 UTC 2017


On Thu, Feb 2, 2017 at 10:30 AM, Sebastian Huber
<sebastian.huber at embedded-brains.de> wrote:
>
>
> On 02/02/17 16:26, Gedare Bloom wrote:
>>
>> On Wed, Feb 1, 2017 at 5:17 AM, Sebastian Huber
>> <sebastian.huber at embedded-brains.de> wrote:
>>>
>>> Update #2843.
>>> ---
>>>   c-user/index.rst                  |   1 +
>>>   c-user/self_contained_objects.rst | 337
>>> ++++++++++++++++++++++++++++++++++++++
>>>   2 files changed, 338 insertions(+)
>>>   create mode 100644 c-user/self_contained_objects.rst
>>>
>>> diff --git a/c-user/index.rst b/c-user/index.rst
>>> index 7937042..4b7fe22 100644
>>> --- a/c-user/index.rst
>>> +++ b/c-user/index.rst
>>> @@ -62,6 +62,7 @@ to the Community Project hosted at
>>> http://www.rtems.org/.
>>>          board_support_packages
>>>          user_extensions
>>>          configuring_a_system
>>> +       self_contained_objects
>>>          multiprocessing
>>>          symmetric_multiprocessing_services
>>>          pci_library
>>> diff --git a/c-user/self_contained_objects.rst
>>> b/c-user/self_contained_objects.rst
>>> new file mode 100644
>>> index 0000000..76c2a64
>>> --- /dev/null
>>> +++ b/c-user/self_contained_objects.rst
>>> @@ -0,0 +1,337 @@
>>> +.. comment SPDX-License-Identifier: CC-BY-SA-4.0
>>> +
>>> +.. COMMENT: Copyright (c) 2014, 2017.
>>> +.. COMMENT: embedded brains GmbH.
>>> +.. COMMENT: All rights reserved.
>>> +
>>> +Self-Contained Objects
>>> +**********************
>>> +
>>> +Introduction
>>> +============
>>> +
>>> +One of the original design goals of RTEMS was the support for
>>> heterogeneous
>>> +computing based on message passing.  This was realized by
>>> synchronization
>>> +objects with an architecture-independent identifier provided by the
>>> system
>>> +during object creation (a 32-bit unsigned integer used as a bitfield)
>>> and a
>>> +user-defined four character name.  This approach in the so called
>>> Classic API
>>> +has some weaknesses:
>>> +
>>> +* Dynamic memory (the workspace) is used to allocate object pools.  This
>>> +  requires a complex configuration with heavy use of the C
>>> pre-processor.
>>> +  Dynamic memory is forbidden by some coding standards, e.g. MISRA
>>> C:2012
>>> +  :cite:`MISRA:2012:C`.
>>> +
>>> +* Objects are created via function calls which return an object
>>> identifier.
>>> +  The object operations use this identifier and map it internally to an
>>> object
>>> +  representation.
>>> +
>>> +* The object identifier is only known at run-time.  This hinders
>>> compiler
>>> +  optimizations and static analysis.
>>> +
>>> +* The objects reside in a table, e.g. they are suspect to false sharing
>>> of
>>> +  cache lines :cite:`Drepper:2007:Memory`.
>>> +
>>> +* The object operations use a rich set of options and attributes.  For
>>> each
>>> +  object operation these parameters must be evaluated and validated at
>>> run-time
>>> +  to figure out what to do exactly for this operation.
>>> +
>>> +For applications that use fine grained locking the mapping of the
>>> identifier to
>>> +the object representation and the parameter evaluation are a significant
>>> +overhead that may degrade the performance dramatically.  An example is
>>> the `new
>>> +network stack (libbsd) <https://git.rtems.org/rtems-libbsd>`_ which uses
>>> +hundreds of locks in a basic setup and the OpenMP support.
>>> +
>>> +To overcome these issues new self-contained objects are available in
>>> RTEMS 4.12
>>> +via the Newlib supplied :file:`<threads.h>`, :file:`<pthread.h>` and
>>> +:file:`<sys/lock.h>` header files.  The following synchronization
>>> objects are
>>> +provided
>>> +
>>> +* POSIX spinlocks,
>>> +
>>> +* mutexes,
>>> +
>>> +* recursive mutexes,
>>> +
>>> +* condition variables,
>>> +
>>> +* counting semaphores,
>>> +
>>> +* binary semaphores, and
>>> +
>>> +* Futex synchronization :cite:`Franke:2002:Futex`.
>>> +
>>> +This allows much better performance in SMP configurations as well as in
>>> +uni-processor configurations.  The application configuration is
>>> significantly
>>> +simplified, since it is no longer necessary to account for lock objects
>>> used by
>>> +Newlib and GCC.  The Newlib defined self-contained objects can be a
>>> statically
>>> +initialized and reside in the ``.bss`` section.  Destruction is a
>>> no-operation.
>>> +
>>> +Self-Contained Objects API
>>> +==========================
>>> +
>>> +To give the RTEMS users access to self-contained objects an API is
>>> necessary.
>>> +One option is to use the POSIX threads API (pthreads) and change its
>>> +implementation to use self-contained objects.  However, there are some
>>> other
>>> +things to consider.  Users of the Classic API may want to run their
>>> +applications with different RTEMS versions.  Since the POSIX threads API
>>> is
>>> +optional it may be not available at all, for example in a stripped down
>>> space
>>> +qualified version.  The POSIX threads API has a lot of options that need
>>> +run-time evaluation, so that optimal performance is not achievable.
>>> There are
>>> +a variety of error conditions.  This is a problem in combination with
>>> some
>>> +coding standards, e.g.  MISRA C:2012.  APIs used by Linux (e.g.
>>> +`<linux/mutex.h>
>>> +<http://lxr.free-electrons.com/source/include/linux/mutex.h>`_) or the
>>> FreeBSD
>>> +kernel (e.g.
>>> +`MUTEX(9) <https://www.freebsd.org/cgi/man.cgi?query=mutex&sektion=9>`_)
>>> are
>>> +better suited as a template for high-performance synchronization
>>> objects.
>>> +
>>> +The API defined in the following sections should be implementable via
>>> the
>>> +Classic API, the self-contained objects present in RTEMS 4.12 and the
>>> POSIX
>>> +API.  Timed operations are not available since the timeout semantics of
>>> the
>>> +Classic API and the POSIX threads API are quite different.
>>> +
>>> +Mutual Exclusion
>>> +================
>>> +
>>> +The :c:type:`rtems_mutex` provides mutual-exclusion synchronization
>>> using the
>>> +:ref:`PriorityInheritance` in uni-processor configurations or the
>>> :ref:`OMIP`
>>> +in SMP configurations.  Recursive locking is not supported
>>> +:cite:`Williams:2012:CA`.
>>> +
>>> +.. c:type:: rtems_mutex
>>> +
>>> +The ``rtems_mutex`` is the type of a mutual-exclusion synchronization
>>> object.
>>> +The storage space for this object must be provided by the user.  There
>>> are no
>>> +defined comparison or assignment operators for this type.  Only the
>>> object
>>> +itself may be used for performing synchronization.  The result of
>>> referring to
>>> +copies of the object in calls to :c:func:`rtems_mutex_lock`,
>>> +:c:func:`rtems_mutex_unlock`, and :c:func:`rtems_mutex_destroy` is
>>> undefined.
>>> +Objects of this type must be initialized via
>>> :c:func:`RTEMS_MUTEX_INITIALIZER`
>>> +or :c:func:`rtems_mutex_init` and must be destroyed via
>>> +:c:func:`rtems_mutex_destroy`.
>>> +
>>> +.. c:function:: RTEMS_MUTEX_INITIALIZER(name)
>>> +
>>> +An initializer for static initialization.  It is equivalent to a call to
>>> +:c:func:`rtems_mutex_init`.  Global mutexes without a name may reside in
>>> the
>>> +``.bss`` section.
>>> +
>>> +.. c:function:: void rtems_mutex_init(rtems_mutex *mutex, const char
>>> *name)
>>> +
>>> +Initializes the ``mutex`` with the specified ``name``.  The ``name``
>>> must be
>>> +persistent throughout the life-time of the mutex.  The mutex is unlocked
>>> after
>>> +initialization.
>>> +
>>> +.. c:function:: void rtems_mutex_lock(rtems_mutex *mutex)
>>> +
>>> +Locks the ``mutex``.  In case the mutex is currently locked, then the
>>> thread is
>>> +blocked until it becomes the mutex owner.  Threads wait in priority
>>> order.
>>> +
>>> +This function must be called from thread context with interrupts
>>> enabled.
>>> +
>>> +.. c:function:: void rtems_mutex_unlock(rtems_mutex *mutex)
>>> +
>>> +Unlocks the ``mutex``.  In case the currently executing thread is not
>>> the owner
>>> +of the ``mutex``, then the result is unpredictable.
>>> +
>>> +This function must be called from thread context with interrupts
>>> enabled.
>>> +
>>> +.. c:function:: void rtems_mutex_destroy(rtems_mutex *mutex)
>>> +
>>> +Destroys the ``mutex``.  In case the ``mutex`` is locked or still in
>>> use, then
>>> +the result is unpredictable.
>>> +
>>> +Condition Variables
>>> +===================
>>> +
>>> +The :c:type:`rtems_condition` provides a condition variable
>>> synchronization
>>> +object.
>>> +
>>> +.. c:type:: rtems_condition
>>> +
I missed saying this. Maybe 'rtems_condition_variable', or
'rtems_condvar', or 'rtems_condition_var'. Condition is a bit too
generic of a term I think.

>>> +The ``rtems_condition`` is the type of a condition variable object.  The
>>> +storage space for this object must be provided by the user.  There are
>>> no
>>> +defined comparison or assignment operators for this type.  Only the
>>> object
>>> +itself may be used for performing synchronization.  The result of
>>> referring to
>>> +copies of the object in calls to :c:func:`rtems_condition_wait`,
>>> +:c:func:`rtems_condition_signal`, :c:func:`rtems_condition_broadcast`,
>>> and
>>> +:c:func:`rtems_condition_destroy` is undefined.  Objects of this type
>>> must be
>>> +initialized via :c:func:`RTEMS_CONDITION_INITIALIZER` or
>>> +:c:func:`rtems_condition_init` and must be destroyed via
>>> +:c:func:`rtems_condition_destroy`.
>>> +
>>> +.. c:function:: RTEMS_CONDITION_INITIALIZER(name)
>>> +
>>> +An initializer for static initialization.  It is equivalent to a call to
>>> +:c:func:`rtems_condition_init`.  Global condition variables without a
>>> name may
>>> +reside in the ``.bss`` section.
>>> +
>>> +.. c:function:: void rtems_condition_init(rtems_condition *condition,
>>> const char *name)
>>> +
>>> +Initializes the ``condition`` with the specified ``name``.  The ``name``
>>> must
>>> +be persistent throughout the life-time of the condition variable.
>>> +
>>> +.. c:function:: void rtems_condition_wait(rtems_condition *condition,
>>> rtems_mutex *mutex)
>>> +
>>> +Atomically waits for the condition and unlocks the mutex.  Once the
>>> condition
>>> +is signalled to the thread it wakes up and locks the mutex.  Threads
>>> wait in
>>> +priority order.
>>> +
>>> +This function must be called from thread context with interrupts
>>> enabled.
>>> +
>>> +.. c:function:: void rtems_condition_signal(rtems_condition *condition)
>>> +
>>> +Signals the condition to the highest priority waiting thread.  If no
>>> threads
>>> +wait currently for the condition, then nothing happens.
>>> +
>>> +.. c:function:: void rtems_condition_broadcast(rtems_condition
>>> *condition)
>>> +
>>> +Signals the condition to all waiting threads.  If no threads wait
>>> currently for
>>> +the condition, then nothing happens.
>>> +
>>> +.. c:function:: void rtems_condition_destroy(rtems_condition *condition)
>>> +
>>> +Destroys the ``condition``.  In case the ``condition`` still in use,
>>> then the
>>> +result is unpredictable.
>>> +
>>> +Counting Semaphores
>>> +===================
>>> +
>>> +The :c:type:`rtems_counting_semaphore` provides a counting semaphore
>>> +synchronization object.
>>> +
>>> +.. c:type:: rtems_counting_semaphore
>>> +
>>> +The ``rtems_counting_semaphore`` is the type of a counting semaphore
>>> object.
>>> +The storage space for this object must be provided by the user.  There
>>> are no
>>> +defined comparison or assignment operators for this type.  Only the
>>> object
>>> +itself may be used for performing synchronization.  The result of
>>> referring to
>>> +copies of the object in calls to
>>> :c:func:`rtems_counting_semaphore_wait`,
>>> +:c:func:`rtems_counting_semaphore_post`, and
>>> +:c:func:`rtems_counting_semaphore_destroy` is undefined.  Objects of
>>> this type
>>> +must be initialized via :c:func:`RTEMS_COUNTING_SEMAPHORE_INITIALIZER`
>>> or
>>> +:c:func:`rtems_counting_semaphore_init` and must be destroyed via
>>> +:c:func:`rtems_counting_semaphore_destroy`.
>>> +
>>> +.. c:function:: RTEMS_COUNTING_SEMAPHORE_INITIALIZER(name, value)
>>> +
>>> +An initializer for static initialization.  It is equivalent to a call to
>>> +:c:func:`rtems_counting_semaphore_init`.  Global counting semaphores
>>> without a
>>> +name may reside in the ``.bss`` section.
>>> +
>>> +.. c:function:: void
>>> rtems_counting_semaphore_init(rtems_counting_semaphore *semaphore, const
>>> char *name, unsigned int count)
>>> +
>>> +Initializes the ``semaphore`` with the specified ``name`` and ``count``.
>>> The
>>> +``name`` must be persistent throughout the life-time of the counting
>>> semaphore.
>>> +The initial value is set to ``count``.
>>> +
>>> +.. c:function:: void
>>> rtems_counting_semaphore_wait(rtems_counting_semaphore *semaphore)
>>> +
>>> +Waits for the counting semaphore.  In case the current semaphore count
>>> is
>>> +positive, then the count is decremented and the function returns
>>> immediately,
>>> +otherwise the thread is blocked waiting for a semaphore post.  Threads
>>> wait in
>>> +priority order.
>>> +
>>> +This function must be called from thread context with interrupts
>>> enabled.
>>> +
>>> +.. c:function:: void
>>> rtems_counting_semaphore_post(rtems_counting_semaphore *semaphore)
>>> +
>>> +Posts the counting semaphore.  In case at least one thread is waiting on
>>> the
>>> +counting semaphore, then the highest priority thread is woken up,
>>> otherwise the
>>> +current count is incremented.
>>> +
>>
>> Is there a reason not to use "signal" here?
>>
>> Typically we will see pairs of wait-signal, pend-post, lock-unlock,
>> acquire-release, down-up. It is a little unusual to mix between these.
>
>
> I used the POSIX nomenclature:
>
> http://pubs.opengroup.org/onlinepubs/009695399/basedefs/semaphore.h.html
>
OK.

>
>>
>>> +This function may be called from interrupt context.  In case it is
>>> called from
>>> +thread context, then interrupts must be enabled.
>>> +
>>> +.. c:function:: void
>>> rtems_counting_semaphore_destroy(rtems_counting_semaphore *semaphore)
>>> +
>>> +Destroys the ``semaphore``.  In case the ``semaphore`` still in use,
>>> then the
>>> +result is unpredictable.
>>> +
>>> +Binary Semaphores
>>> +=================
>>> +
>>> +The :c:type:`rtems_binary_semaphore` provides a binary semaphore
>>> +synchronization object.
>>> +
>>> +.. c:type:: rtems_binary_semaphore
>>> +
>>> +The ``rtems_binary_semaphore`` is the type of a binary semaphore object.
>>> The
>>> +storage space for this object must be provided by the user.  There are
>>> no
>>> +defined comparison or assignment operators for this type.  Only the
>>> object
>>> +itself may be used for performing synchronization.  The result of
>>> referring to
>>> +copies of the object in calls to :c:func:`rtems_binary_semaphore_wait`,
>>> +:c:func:`rtems_binary_semaphore_post`, and
>>> +:c:func:`rtems_binary_semaphore_destroy` is undefined.  Objects of this
>>> type
>>> +must be initialized via :c:func:`RTEMS_BINARY_SEMAPHORE_INITIALIZER` or
>>> +:c:func:`rtems_binary_semaphore_init` and must be destroyed via
>>> +:c:func:`rtems_binary_semaphore_destroy`.
>>> +
>>> +.. c:function:: RTEMS_BINARY_SEMAPHORE_INITIALIZER(name)
>>> +
>>> +An initializer for static initialization.  It is equivalent to a call to
>>> +:c:func:`rtems_binary_semaphore_init`.  Global binary semaphores without
>>> a name
>>> +may reside in the ``.bss`` section.
>>> +
>>> +.. c:function:: void rtems_binary_semaphore_init(rtems_binary_semaphore
>>> *semaphore, const char *name)
>>> +
>>> +Initializes the ``semaphore`` with the specified ``name``.  The ``name``
>>> must
>>> +be persistent throughout the life-time of the binary semaphore.  The
>>> initial
>>> +value is set to zero.
>>> +
>>> +.. c:function:: void rtems_binary_semaphore_wait(rtems_binary_semaphore
>>> *semaphore)
>>> +
>>> +Waits for the binary semaphore.  In case the current semaphore count is
>>> +positive, then the count is decremented and the function returns
>>> immediately,
>>> +otherwise the thread is blocked waiting for a semaphore post.  Threads
>>> wait in
>>> +priority order.
>>> +
>>> +This function must be called from thread context with interrupts
>>> enabled.
>>> +
>>> +.. c:function:: void rtems_binary_semaphore_post(rtems_binary_semaphore
>>> *semaphore)
>>> +
>>> +Posts the binary semaphore.  In case at least one thread is waiting on
>>> the
>>> +binary semaphore, then the highest priority thread is woken up,
>>> otherwise the
>>> +current count is set to one.
>>> +
>>> +This function may be called from interrupt context.  In case it is
>>> called from
>>> +thread context, then interrupts must be enabled.
>>>
>> What is the difference between mutex and binary semaphore?
>
>
> Yes, there is a big difference. A binary semaphore is a counting semaphore
> those values are restricted to 0 and 1. It has no owner.
>

Whichever thread passes 'wait()' is (implicitly) owning the binary
semaphore. Semantically there is very little difference to me.
Certainly based on the description here I don't know what the
difference is and why if I'm a user I should pick rtems_mutex or
rtems_binary_semaphore.

Do the counting/binary semaphores also use priority inheritance?

If the binary semaphore is identically equal to a counting semaphore
initialized to 1, then why even include it in the API?

>
>>
>> I'd prefer lock/unlock for mutexes and binary semaphores unless there
>> is a good reason for the wait-post/signal terminology.
>>
>>> +.. c:function:: void
>>> rtems_binary_semaphore_destroy(rtems_binary_semaphore *semaphore)
>>> +
>>> +Destroys the ``semaphore``.  In case the ``semaphore`` still in use,
>>> then the
>>> +result is unpredictable.
>>> +
>>> +Threads
>>> +=======
>>> +
>>> +.. warning::
>>> +
>>> +   The self-contained threads support is work in progress.  In contrast
>>> to the
>>> +   synchronization objects the self-contained thread support is not just
>>> an API
>>> +   glue layer to already existing implementations.
>>> +
>>> +The :c:type:`rtems_thread` provides a thread of execution.
>>> +
>>> +.. c:type:: rtems_thread
>>> +
>>> +The ``rtems_thread`` is the type of a thread object.
>>> +
>>> +.. c:function:: RTEMS_THREAD_INITIALIZER(name, stack_size, priority,
>>> flags, entry, arg)
>>> +
>>> +.. c:function:: void rtems_thread_start(rtems_thread *thread, const char
>>> *name, size_t stack_size, uint32_t priority, uint32_t flags, void
>>> (*entry)(void *), void *arg)
>>> +
>>> +.. c:function:: void rtems_thread_restart(rtems_thread *thread, void
>>> *arg)
>>> +
>>> +.. c:function:: void rtems_thread_event_send(rtems_thread *thread,
>>> uint32_t events)
>>> +
>>
>> non-blocking send only?
>
>
> Yes, just like rtems_event_send().
>
>>
>>> +.. c:function:: void rtems_thread_event_poll(rtems_thread *thread,
>>> uint32_t events_of_interest, uint32_t *actual_events)
>>> +
>>> +.. c:function:: void rtems_thread_event_wait_all(rtems_thread *thread,
>>> uint32_t events_of_interest, uint32_t *actual_events)
>>> +
>>> +.. c:function:: void rtems_thread_event_wait_any(rtems_thread *thread,
>>> uint32_t events_of_interest, uint32_t *actual_events)
>>> +
>>> +.. c:function:: void rtems_thread_destroy(rtems_thread *thread)
>>> +
>>> +.. c:function:: void rtems_thread_self_destroy(void)
>>> --
>>> 1.8.4.5
>>>
>>> _______________________________________________
>>> devel mailing list
>>> devel at rtems.org
>>> http://lists.rtems.org/mailman/listinfo/devel
>
>
> --
> Sebastian Huber, embedded brains GmbH
>
> Address : Dornierstr. 4, D-82178 Puchheim, Germany
> Phone   : +49 89 189 47 41-16
> Fax     : +49 89 189 47 41-09
> E-Mail  : sebastian.huber at embedded-brains.de
> PGP     : Public key available on request.
>
> Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG.
>



More information about the devel mailing list