Skip to content

Commit

Permalink
[resumable] Implement mutex locking in fiber mode
Browse files Browse the repository at this point in the history
  • Loading branch information
salkinium committed Oct 27, 2024
1 parent 6a4aeaf commit 4ecdcfb
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 12 deletions.
14 changes: 14 additions & 0 deletions src/modm/processing/fiber/mutex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
#include <atomic>
#include <mutex>

/// @cond
namespace modm
{
template< uint8_t Functions > class Resumable;
template< uint8_t Levels > class NestedResumable;
}
/// @endcond

namespace modm::fiber
{

Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
55 changes: 54 additions & 1 deletion src/modm/processing/resumable/macros_fiber.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,24 @@
#define MODM_RF_MACROS_FIBER_HPP

#include <modm/processing/fiber.hpp>
#include <modm/processing/fiber/mutex.hpp>

/// @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


/**
Expand Down Expand Up @@ -94,4 +105,46 @@

/// @}

#ifndef __DOXYGEN__

#define RF_BEGIN_1() \
this->template checkRfType<true>(); \
modm::fiber::lock_guard rfLockGuardState{rfState};

#define RF_BEGIN_0(index) \
this->template checkRfType<false>(); \
this->template checkRfFunctions<index>(); \
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
81 changes: 70 additions & 11 deletions src/modm/processing/resumable/resumable_fiber.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#pragma once

#include "macros.hpp"
#include <modm/processing/fiber.hpp>
#include <modm/processing/fiber/mutex.hpp>
#include <stdint.h>

#define MODM_RESUMABLE_IS_FIBER
Expand All @@ -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<uint8_t> ids) const
{
for (uint8_t id : ids) if (isResumableRunning(id)) return true;
return false;
}
bool areAllResumablesRunning(std::initializer_list<uint8_t> ids) const
{
for (uint8_t id : ids) if (not isResumableRunning(id)) return false;
return true;
}
bool joinResumables(std::initializer_list<uint8_t> 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<uint8_t> ids) const;
bool areAllResumablesRunning(std::initializer_list<uint8_t> ids) const;
bool joinResumables(std::initializer_list<uint8_t> ids) const;

protected:
/// @cond
template<uint8_t index>
static void
checkRfFunctions()
{
static_assert(index < Functions,
"Index out of bounds! Increase the `Functions` template argument of your Resumable class.");
}
template<bool isNested>
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<bool isNested>
static void
checkRfType()
{
static_assert(isNested == false, "You must declare an index for this resumable function!");
}

modm::fiber::recursive_mutex rfState;
/// @endcond
};

/// @}
Expand Down

0 comments on commit 4ecdcfb

Please sign in to comment.