diff --git a/doc/design/determinism.md b/doc/design/determinism.md deleted file mode 100644 index 203010cb54..0000000000 --- a/doc/design/determinism.md +++ /dev/null @@ -1,114 +0,0 @@ -# Determinism in iceoryx - -Deterministic execution is important for many application domains, especially real-time systems. - -However, the meaning of determinism differs between domains such as -computer science and real-time systems. - -## Deterministic result - -The function result only depends on the input and is entirely predictable in the sense that repeated -execution with the same input (and state of the environment) always leads to the same -output. - -The state of the environment refers to any global or member variables the function depends on. -In particular it may depend on the current time or a pseudo random number generator state. - -This is the (informal) definition used in computer science. - -## Deterministic runtime - -The runtime of a function is bounded and the function cannot block indefinitely under normal -conditions. Normal conditions mean that the preconditions of the functions hold and the system is -not already in some fatal error state (that may intentionally or unintentionally block), otherwise -the system is in an abnormal state. In an abnormal state no determinism guarantees can be given. - -Furthermore it must be guaranteed that the function execution is scheduled by the underlying OS scheduler. - -However, while such time-bounds are guaranteed to exist they are usually not known -and depend on hardware and generally the system state (i.e. other processes, context switches etc.). - -This definition is mostly used in real-time systems theory and in the -following referred to as time-determinism. - -## Influences on time-determinism - -1. Thread timing (including benign race conditions) -1. Context switches -1. Branch prediction and speculative execution -1. Caching -1. Exceptions -1. Dynamic memory allocation with a system allocator (`new`, `malloc`) -1. Blocking OS calls -1. Blocking logic or wait logic (e.g. loops in a spinlock) -1. Hardware and general physics - -Some of these such as thread-timing may also effect the determinism of the result, -depending on the algorithm (e.g. concurrent queues). - -## Deterministic annotation - -The `@deterministic` annotation guarantees that: - -1. No exceptions are used -1. No blocking OS calls are used unless the purpose of the function is to wait for some event -1. No dynamic memory allocation with the system allocator takes place - -It does **NOT** guarantee that: - -1. The function is properly scheduled or completes in any defined amount of time -1. The result itself is deterministic (e.g. concurrent lock-free queue push) -1. Any concrete time-bound is met - -Except for functions whose purpose is blocking, `@deterministic` guarantees time-determinism in the -above sense. This makes `@deterministic` functions suitable to be used in real-time applications -where a (somewhat) predictable runtime is required. - -We only consider public API for annotation, private functions are implementation details and do -not require annotation (it can be considered optionally where it is useful). - -## Conditional guarantees for templates - -iceoryx hoofs provides many template classes and functions that depend on user-defined -types `T`. Any determinism guarantee of a function `f` requires that functions defined -for the template type `T` that are called by `f` must provide sufficient determinism guarantees -themselves. - -For example, if `f` calls a member function of `T` that allocates dynamic memory or throws an -exception, this particular instantiation of `f` with `T` provides no determinism guarantees. - -In other words, the determinism guarantee is always conditional for templates. -It is sufficient that the relevant functions of `T` (those that are called by `f`) provide -the determinism guarantee themselves. - -Note that for example the `size` function of `vector` does not depend on the template type `T`, i.e. -the set of relevant functions above is empty. In this case the function is unconditionally -`@deterministic`. - -## Conditional guarantees for callbacks - -For callbacks specified by the user and provided as e.g. lambda expressions, function pointers or -function wrappers such as `cxx::function` or `std::function` the `@deterministic` guarantee is also -conditional. - -A function `f` that is `@deterministic` and invokes a callback only provides the determinism -guarantee if the callback itself is `@deterministic`. - -## Limitations - -Due to the effects of modern multi-processor CPUs `@deterministic` functions cannot guarantee -hard real-time constraints where a time-bound must always be met. An independent monitoring -mechanism can be used to check whether time bounds are met at runtime (not part of iceoryx). - -## Future considerations - -Currently this labeling is experimental due to interaction to gain experience with automatic -validation methods that verify whether a function is indeed `@determinisitic`. -Since the validation interferes with tests, the labeling is currently limited to a few headers only. -It will be expanded in the future if it proves useful and feasible. - -1. Reevaluate dynamic memory allocation guarantee for POSIX calls. -1. `@deterministic` functions could be more precisely annotated as time-bounded or real-time-safe. -1. Provide more detailed guarantees such as non-blocking or non-allocating. -1. Extend the guarantee for polymorphic classes (similar restrictions as with callbacks). - diff --git a/iceoryx_hoofs/buffer/include/iox/stack.hpp b/iceoryx_hoofs/buffer/include/iox/stack.hpp index ccb83f17f0..e2f7c0dae0 100644 --- a/iceoryx_hoofs/buffer/include/iox/stack.hpp +++ b/iceoryx_hoofs/buffer/include/iox/stack.hpp @@ -42,47 +42,31 @@ class stack final // NOLINT(cppcoreguidelines-pro-type-member-init, hicpp-member public: static_assert(is_storable_in_iox_stack_t::value, "T has to be move constructible"); - /// @deterministic stack() noexcept = default; - - /// @deterministic stack(const stack& rhs) noexcept; - - /// @deterministic stack(stack&& rhs) noexcept; - - /// @deterministic stack& operator=(const stack& rhs) noexcept; - - /// @deterministic stack& operator=(stack&& rhs) noexcept; - - /// @deterministic ~stack() noexcept; /// @brief returns the last pushed element when the stack contains elements /// otherwise a nullopt - /// @deterministic optional pop() noexcept; /// @brief pushed an element into the stack by forwarding all arguments /// to the constructor of T /// @param[in] args arguments which will be perfectly forwarded to the constructor of T /// @return true if the push was successful, otherwise false - /// @deterministic template bool push(Targs&&... args) noexcept; /// @brief calls the destructor of all contained elements in reverse creation order and empties the stack - /// @deterministic void clear() noexcept; /// @brief returns the stack size - /// @deterministic uint64_t size() const noexcept; /// @brief returns the stack capacity - /// @deterministic static constexpr uint64_t capacity() noexcept; private: diff --git a/iceoryx_hoofs/container/include/iox/uninitialized_array.hpp b/iceoryx_hoofs/container/include/iox/uninitialized_array.hpp index c191a422ce..fb4db576b0 100644 --- a/iceoryx_hoofs/container/include/iox/uninitialized_array.hpp +++ b/iceoryx_hoofs/container/include/iox/uninitialized_array.hpp @@ -84,7 +84,6 @@ class UninitializedArray final using iterator = ElementType*; using const_iterator = const ElementType*; - /// @deterministic // The (empty) user-defined constructor is required. // Use of "= default" leads to value-initialization of class members. // AXIVION Next Construct AutosarC++19_03-A12.6.1 : This is a low-level building block which is supposed to provide uninitialized memory @@ -99,34 +98,27 @@ class UninitializedArray final /// @param[in] index position of the element to return /// @return reference to the element /// @note out of bounds access leads to undefined behavior - /// @deterministic constexpr ElementType& operator[](const uint64_t index) noexcept; /// @brief returns a const reference to the element stored at index /// @param[in] index position of the element to return /// @return const reference to the element /// @note out of bounds access leads to undefined behavior - /// @deterministic constexpr const ElementType& operator[](const uint64_t index) const noexcept; /// @brief returns an iterator to the beginning of the UninitializedArray - /// @deterministic iterator begin() noexcept; /// @brief returns a const iterator to the beginning of the UninitializedArray - /// @deterministic const_iterator begin() const noexcept; /// @brief returns an iterator to the end of the UninitializedArray - /// @deterministic iterator end() noexcept; /// @brief returns a const iterator to the end of the UninitializedArray - /// @deterministic const_iterator end() const noexcept; /// @brief returns the array capacity - /// @deterministic static constexpr uint64_t capacity() noexcept; private: diff --git a/iceoryx_hoofs/container/include/iox/vector.hpp b/iceoryx_hoofs/container/include/iox/vector.hpp index 23454147ef..961af97ade 100644 --- a/iceoryx_hoofs/container/include/iox/vector.hpp +++ b/iceoryx_hoofs/container/include/iox/vector.hpp @@ -43,33 +43,27 @@ class vector final using const_iterator = const T*; /// @brief creates an empty vector - /// @deterministic vector() noexcept = default; /// @brief creates a vector with count copies of elements with value value /// @param [in] count is the number copies which are inserted into the vector /// @param [in] value is the value which is inserted into the vector - /// @deterministic vector(const uint64_t count, const T& value) noexcept; /// @brief creates a vector with count copies of elements constructed with the default constructor of T /// @param [in] count is the number copies which are inserted into the vector - /// @deterministic explicit vector(const uint64_t count) noexcept; /// @brief copy constructor to copy a vector of the same capacity /// @param[in] rhs is the copy origin - /// @deterministic vector(const vector& rhs) noexcept; /// @brief move constructor to move a vector of the same capacity /// @param[in] rhs is the move origin - /// @deterministic vector(vector&& rhs) noexcept; /// @brief destructs the vector and also calls the destructor of all /// contained elements in reverse construction order - /// @deterministic ~vector() noexcept; /// @brief copy assignment. if the destination vector contains more @@ -77,7 +71,6 @@ class vector final /// destructed /// @param[in] rhs is the copy origin /// @return reference to self - /// @deterministic vector& operator=(const vector& rhs) noexcept; /// @brief move assignment. if the destination vector contains more @@ -85,109 +78,90 @@ class vector final /// destructed /// @param[in] rhs is the move origin /// @return reference to self - /// @deterministic vector& operator=(vector&& rhs) noexcept; /// @brief returns an iterator to the first element of the vector, /// if the vector is empty it returns the same iterator as /// end (the first iterator which is outside of the vector) - /// @deterministic iterator begin() noexcept; /// @brief returns a const iterator to the first element of the vector, /// if the vector is empty it returns the same iterator as /// end (the first iterator which is outside of the vector) - /// @deterministic const_iterator begin() const noexcept; /// @brief returns an iterator to the element which comes after the last /// element (the first element which is outside of the vector) - /// @deterministic iterator end() noexcept; /// @brief returns a const iterator to the element which comes after the last /// element (the first element which is outside of the vector) - /// @deterministic const_iterator end() const noexcept; /// @brief return the pointer to the underlying array /// @return pointer to underlying array - /// @deterministic T* data() noexcept; /// @brief return the const pointer to the underlying array /// @return const pointer to underlying array - /// @deterministic const T* data() const noexcept; /// @brief returns a reference to the element stored at index. /// @param[in] index of the element to return /// @return reference to the element stored at index /// @attention Out of bounds access leads to a program termination! - /// @deterministic T& at(const uint64_t index) noexcept; /// @brief returns a const reference to the element stored at index. /// @param[in] index of the element to return /// @return const reference to the element stored at index /// @attention Out of bounds access leads to a program termination! - /// @deterministic const T& at(const uint64_t index) const noexcept; /// @brief returns a reference to the element stored at index. /// @param[in] index of the element to return /// @return reference to the element stored at index /// @attention Out of bounds access leads to a program termination! - /// @deterministic T& operator[](const uint64_t index) noexcept; /// @brief returns a const reference to the element stored at index. /// @param[in] index of the element to return /// @return const reference to the element stored at index /// @attention Out of bounds access leads to a program termination! - /// @deterministic const T& operator[](const uint64_t index) const noexcept; /// @brief returns a reference to the first element; terminates if the vector is empty /// @return reference to the first element /// @attention Accessing an empty vector leads to a program termination! - /// @deterministic T& front() noexcept; /// @brief returns a const reference to the first element; terminates if the vector is empty /// @return const reference to the first element /// @attention Accessing an empty vector leads to a program termination! - /// @deterministic const T& front() const noexcept; /// @brief returns a reference to the last element; terminates if the vector is empty /// @return reference to the last element /// @attention Accessing an empty vector leads to a program termination! - /// @deterministic T& back() noexcept; /// @brief returns a const reference to the last element; terminates if the vector is empty /// @return const reference to the last element /// @attention Accessing an empty vector leads to a program termination! - /// @deterministic const T& back() const noexcept; /// @brief returns the capacity of the vector which was given via the template /// argument - /// @deterministic static constexpr uint64_t capacity() noexcept; /// @brief returns the number of elements which are currently stored in the /// vector - /// @deterministic uint64_t size() const noexcept; /// @brief returns true if the vector is emtpy, otherwise false - /// @deterministic bool empty() const noexcept; /// @brief calls the destructor of all contained elements and removes them - /// @deterministic void clear() noexcept; /// @brief resizes the vector. If the vector size increases new elements will be constructed with the given @@ -199,7 +173,6 @@ class vector final /// @note perfect forwarded arguments are explicitly not wanted here. think of what happens if resize /// creates two new elements via move construction. The first one has a valid source but the second /// gets an already moved parameter. - /// @deterministic template bool resize(const uint64_t count, const Targs&... args) noexcept; @@ -208,7 +181,6 @@ class vector final /// @param[in] position the position where the element should be created /// @param[in] args arguments which are used by the constructor of the newly created argument /// @return true if successful, false if position is greater than size or the vector is already full - /// @deterministic template bool emplace(const uint64_t position, Targs&&... args) noexcept; @@ -216,25 +188,21 @@ class vector final /// and performs a placement new at the end /// @param[in] args arguments which are used by the constructor of the newly created argument /// @return true if successful, false if the vector is already full - /// @deterministic template bool emplace_back(Targs&&... args) noexcept; /// @brief appends the given element at the end of the vector /// @param[in] value to append to the vector /// @return true if successful, false if vector already full - /// @deterministic bool push_back(const T& value) noexcept; /// @brief appends the given element at the end of the vector /// @param[in] value to append to the vector /// @return true if successful, false if vector already full - /// @deterministic bool push_back(T&& value) noexcept; /// @brief removes the last element of the vector; calling pop_back on an empty container does nothing /// @return true if the last element was removed. If the vector is empty it returns false. - /// @deterministic bool pop_back() noexcept; /// @brief removes an element at the given position. if this element is in @@ -242,7 +210,6 @@ class vector final /// left to ensure that the elements are stored contiguously /// @param[in] position at which the element shall be removed /// @return true if the element was removed, i.e. begin() <= position < end(), otherwise false - /// @deterministic bool erase(iterator position) noexcept; private: