diff --git a/src/modm/processing/fiber/mutex.hpp b/src/modm/processing/fiber/mutex.hpp index b681186486..bf9b9d3dc9 100644 --- a/src/modm/processing/fiber/mutex.hpp +++ b/src/modm/processing/fiber/mutex.hpp @@ -22,6 +22,14 @@ #include #include +/// @cond +namespace modm +{ +template< uint8_t Functions > class Resumable; +template< uint8_t Levels > class NestedResumable; +} +/// @endcond + namespace modm::fiber { @@ -32,6 +40,9 @@ namespace modm::fiber /// @see https://en.cppreference.com/w/cpp/thread/mutex class mutex { + template< uint8_t Functions > friend class ::modm::Resumable; + template< uint8_t Levels > friend class ::modm::NestedResumable; + mutex(const mutex&) = delete; mutex& operator=(const mutex&) = delete; @@ -87,6 +98,9 @@ class timed_mutex : public mutex /// @see https://en.cppreference.com/w/cpp/thread/recursive_mutex class recursive_mutex { + template< uint8_t Functions > friend class ::modm::Resumable; + template< uint8_t Levels > friend class ::modm::NestedResumable; + recursive_mutex(const recursive_mutex&) = delete; recursive_mutex& operator=(const recursive_mutex&) = delete; using count_t = uint16_t; diff --git a/src/modm/processing/resumable/macros_fiber.hpp b/src/modm/processing/resumable/macros_fiber.hpp index 60dde2c981..8c6168d685 100644 --- a/src/modm/processing/resumable/macros_fiber.hpp +++ b/src/modm/processing/resumable/macros_fiber.hpp @@ -13,13 +13,24 @@ #define MODM_RF_MACROS_FIBER_HPP #include +#include /// @ingroup modm_processing_resumable /// @{ +#ifdef __DOXYGEN__ /// Declare start of resumable function with index. /// @warning Use at start of the `resumable()` implementation! -#define RF_BEGIN(...) +#define RF_BEGIN(index) + +/** + * Declare start of a nested resumable function. + * This will immediately return if the nesting is too deep. + * + * @warning Use at start of the `resumable()` implementation! + */ +#define RF_BEGIN() +#endif /** @@ -94,4 +105,46 @@ /// @} +#ifndef __DOXYGEN__ + +#define RF_BEGIN_1() \ + this->template checkRfType(); \ + modm::fiber::lock_guard rfLockGuardState{rfState}; + +#define RF_BEGIN_0(index) \ + this->template checkRfType(); \ + this->template checkRfFunctions(); \ + modm::fiber::lock_guard rfLockGuardState{rfStateArray[index]}; + +// the following completely unreadable preprocessor macro magic is based on this +// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ +// http://efesx.com/2010/07/17/variadic-macro-to-count-number-of-arguments/#comment-255 +#define RF_ARG3(_0, _1, _2, ...) _2 +#define RF_HAS_COMMA(...) RF_ARG3(__VA_ARGS__, 1, 0) +#define RF_TRIGGER_PARENTHESIS_(...) , + +#define RF_ISEMPTY(...) _RF_ISEMPTY( \ + /* test if there is just one argument, eventually an empty one */ \ + RF_HAS_COMMA(__VA_ARGS__), \ + /* test if _TRIGGER_PARENTHESIS_ together with the argument adds a comma */ \ + RF_HAS_COMMA(RF_TRIGGER_PARENTHESIS_ __VA_ARGS__), \ + /* test if the argument together with a parenthesis adds a comma */ \ + RF_HAS_COMMA(__VA_ARGS__ (/*empty*/)), \ + /* test if placing it between _TRIGGER_PARENTHESIS_ and the parenthesis adds a comma */ \ + RF_HAS_COMMA(RF_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)) \ + ) + +#define RF_PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4 +#define _RF_ISEMPTY(_0, _1, _2, _3) RF_HAS_COMMA(RF_PASTE5(RF_IS_EMPTY_CASE_, _0, _1, _2, _3)) +#define RF_IS_EMPTY_CASE_0001 , + +// all we wanted is to call RF_BEGIN_0 for 1 argument +// and call RF_BEGIN_1 for 0 arguments. Makes total sense. +#define RF_GET_BEGIN_MACRO3(n) RF_BEGIN_ ## n +#define RF_GET_BEGIN_MACRO2(n) RF_GET_BEGIN_MACRO3(n) +#define RF_GET_BEGIN_MACRO(...) RF_GET_BEGIN_MACRO2(RF_ISEMPTY(__VA_ARGS__)) +#define RF_BEGIN(...) RF_GET_BEGIN_MACRO(__VA_ARGS__)(__VA_ARGS__) + +#endif + #endif // MODM_RF_MACROS_FIBER_HPP diff --git a/src/modm/processing/resumable/resumable_fiber.hpp b/src/modm/processing/resumable/resumable_fiber.hpp index fb7957da69..6f8de44006 100644 --- a/src/modm/processing/resumable/resumable_fiber.hpp +++ b/src/modm/processing/resumable/resumable_fiber.hpp @@ -12,6 +12,8 @@ #pragma once #include "macros.hpp" +#include +#include #include #define MODM_RESUMABLE_IS_FIBER @@ -27,27 +29,84 @@ template < typename T > using ResumableResult = T; /// Resumable functions implemented via fibers are normal functions -template< uint8_t Functions = 0 > -struct Resumable +template< uint8_t Functions = 1 > +class Resumable { +public: + bool isResumableRunning(uint8_t id) const + { + return id < Functions and rfStateArray[id].locked; + } + bool areAnyResumablesRunning() const + { + for (const auto &state : rfStateArray) if (state.locked) return true; + return false; + } + bool areAnyResumablesRunning(std::initializer_list ids) const + { + for (uint8_t id : ids) if (isResumableRunning(id)) return true; + return false; + } + bool areAllResumablesRunning(std::initializer_list ids) const + { + for (uint8_t id : ids) if (not isResumableRunning(id)) return false; + return true; + } + bool joinResumables(std::initializer_list ids) const + { + modm::this_fiber::poll([&]{ return not areAnyResumablesRunning(ids); }); + return true; + } // unimplementable with fibers, but may be stubbed by user application void stopAllResumables(); bool stopResumable(uint8_t id); - bool isResumableRunning(uint8_t id) const; - bool areAnyResumablesRunning() const; - bool areAnyResumablesRunning(std::initializer_list ids) const; - bool areAllResumablesRunning(std::initializer_list ids) const; - bool joinResumables(std::initializer_list ids) const; + +protected: + /// @cond + template + static void + checkRfFunctions() + { + static_assert(index < Functions, + "Index out of bounds! Increase the `Functions` template argument of your Resumable class."); + } + template + static void + checkRfType() + { + static_assert(isNested == false, "You must declare an index for this resumable function!"); + } + modm::fiber::mutex rfStateArray[Functions]; + /// @endcond }; /// Resumable functions implemented via fibers are normal functions -template< uint8_t Levels = 0 > -struct NestedResumable +template< uint8_t Levels = 1 > +class NestedResumable { +public: + bool isResumableRunning() const + { + return rfState.owner != rfState.NoOwner; + } + int8_t getResumableDepth() const + { + return isResumableRunning() ? rfState.count - 1 : -1; + } // unimplementable with fibers, but may be stubbed by user application void stopResumable(); - bool isResumableRunning() const; - int8_t getResumableDepth() const; + +protected: + /// @cond + template + static void + checkRfType() + { + static_assert(isNested == false, "You must declare an index for this resumable function!"); + } + + modm::fiber::recursive_mutex rfState; + /// @endcond }; /// @}