Skip to content

iceoryx_hoofs/posix_wrapper/timer.hpp🔗

Namespaces🔗

Name
iox
building block to easily create free function for logging in a library context
iox::posix
iox::units::duration_literals

Classes🔗

Name
class iox::posix::Timer
Interface for timers on POSIX operating systems.

Source code🔗

// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved.
// Copyright (c) 2021 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_HOOFS_POSIX_WRAPPER_TIMER_HPP
#define IOX_HOOFS_POSIX_WRAPPER_TIMER_HPP

#include "iceoryx_hoofs/cxx/optional.hpp"
#include "iceoryx_hoofs/cxx/vector.hpp"
#include "iceoryx_hoofs/design_pattern/creation.hpp"
#include "iceoryx_hoofs/internal/units/duration.hpp"
#include "iceoryx_hoofs/platform/signal.hpp"
#include "iceoryx_hoofs/platform/time.hpp"

#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <ctime>
#include <functional>
#include <limits>


namespace iox
{
namespace posix
{
enum class TimerError
{
    NO_ERROR,
    TIMER_NOT_INITIALIZED,
    NO_VALID_CALLBACK,
    KERNEL_ALLOC_FAILED,
    INVALID_ARGUMENTS,
    ALLOC_MEM_FAILED,
    NO_PERMISSION,
    INVALID_POINTER,
    NO_TIMER_TO_DELETE,
    TIMEOUT_IS_ZERO,
    INTERNAL_LOGIC_ERROR
};

using namespace iox::units::duration_literals;



class Timer
{
  public:
    enum class RunMode
    {
        ONCE,
        PERIODIC
    };

    enum class CatchUpPolicy
    {
        SKIP_TO_NEXT_BEAT,
        IMMEDIATE,
        TERMINATE
    };

  private:
    static constexpr size_t SIZE_OF_COMBINDED_INDEX_AND_DESCRIPTOR = sizeof(uint32_t);
    static constexpr size_t SIZE_OF_SIGVAL_INT = sizeof(int);
    static_assert(SIZE_OF_SIGVAL_INT >= SIZE_OF_COMBINDED_INDEX_AND_DESCRIPTOR, "size of sigval_int is to low");

    static constexpr uint32_t MAX_NUMBER_OF_CALLBACK_HANDLES = 100u;
    static_assert(MAX_NUMBER_OF_CALLBACK_HANDLES <= std::numeric_limits<uint8_t>::max(),
                  "number of callback handles exceeds max index value");
    class OsTimer;
    struct OsTimerCallbackHandle
    {
        static constexpr uint32_t MAX_DESCRIPTOR_VALUE{(1u << 24u) - 1u};
        static sigval indexAndDescriptorToSigval(uint8_t index, uint32_t descriptor) noexcept;
        static uint8_t sigvalToIndex(sigval intVal) noexcept;
        static uint32_t sigvalToDescriptor(sigval intVal) noexcept;

        void incrementDescriptor() noexcept;

        std::mutex m_accessMutex;

        std::atomic<uint32_t> m_descriptor{0u};
        // must be operator= otherwise it is undefined, see https://en.cppreference.com/w/cpp/atomic/ATOMIC_FLAG_INIT
        std::atomic_flag m_callbackIsAboutToBeExecuted = ATOMIC_FLAG_INIT;

        std::atomic<bool> m_inUse{false};
        std::atomic<bool> m_isTimerActive{false};
        std::atomic<uint64_t> m_timerInvocationCounter{0u};
        CatchUpPolicy m_catchUpPolicy{CatchUpPolicy::TERMINATE};
        OsTimer* m_timer{nullptr};
    };

    class OsTimer
    {
#ifdef __QNX__
        static constexpr timer_t INVALID_TIMER_ID = 0;
#else
        static constexpr timer_t INVALID_TIMER_ID = nullptr;
#endif
      public:
        static void callbackHelper(sigval data) noexcept;

        OsTimer(const units::Duration timeToWait, const std::function<void()>& callback) noexcept;

        OsTimer(const OsTimer&) = delete;
        OsTimer(OsTimer&&) = delete;
        OsTimer& operator=(const OsTimer&) = delete;
        OsTimer& operator=(OsTimer&&) = delete;

        virtual ~OsTimer() noexcept;

        cxx::expected<TimerError> start(const RunMode runMode, const CatchUpPolicy catchUpPolicy) noexcept;

        cxx::expected<TimerError> stop() noexcept;

        cxx::expected<TimerError>
        restart(const units::Duration timeToWait, const RunMode runMode, const CatchUpPolicy catchUpPolicy) noexcept;

        // @brief Returns the time until the timer expires the next time
        cxx::expected<units::Duration, TimerError> timeUntilExpiration() noexcept;

        cxx::expected<uint64_t, TimerError> getOverruns() noexcept;

        bool hasError() const noexcept;

        TimerError getError() const noexcept;

      private:
        void executeCallback() noexcept;

      private:
        units::Duration m_timeToWait;

        std::function<void()> m_callback;

        timer_t m_timerId{INVALID_TIMER_ID};

        uint8_t m_callbackHandleIndex{0u};

        bool m_isInitialized{false};

        TimerError m_errorValue{TimerError::NO_ERROR};

        static OsTimerCallbackHandle s_callbackHandlePool[MAX_NUMBER_OF_CALLBACK_HANDLES];
    };

  public:
    Timer(const units::Duration timeToWait) noexcept;

    Timer(const units::Duration timeToWait, const std::function<void()>& callback) noexcept;

    static cxx::expected<units::Duration, TimerError> now() noexcept;

    Timer(const Timer& other) = delete;

    Timer(Timer&& other) = delete;

    Timer& operator=(const Timer& other) = delete;

    Timer& operator=(Timer&& other) = delete;

    virtual ~Timer() noexcept = default;

    cxx::expected<TimerError> start(const RunMode runMode, const CatchUpPolicy catchUpPolicy) noexcept;

    cxx::expected<TimerError> stop() noexcept;

    cxx::expected<TimerError>
    restart(const units::Duration timeToWait, const RunMode runMode, const CatchUpPolicy catchUpPolicy) noexcept;

    // @brief Returns the time until the timer expires the next time
    cxx::expected<units::Duration, TimerError> timeUntilExpiration() noexcept;

    cxx::expected<uint64_t, TimerError> getOverruns() noexcept;

    bool hasError() const noexcept;

    TimerError getError() const noexcept;

  private:
    cxx::optional<OsTimer> m_osTimer;

    static cxx::error<TimerError> createErrorFromErrno(const int32_t errnum) noexcept;

    units::Duration m_timeToWait;

    units::Duration m_creationTime;

    TimerError m_errorValue{TimerError::NO_ERROR};
};

} // namespace posix
} // namespace iox

#endif // IOX_HOOFS_POSIX_WRAPPER_TIMER_HPP

Updated on 31 May 2022 at 11:34:55 CEST