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

Gedare Bloom gedare at rtems.org
Thu Feb 2 15:26:31 UTC 2017


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
> +
> +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.

> +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?

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?

> +.. 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


More information about the devel mailing list