ETLCPP / etl

Embedded Template Library
https://www.etlcpp.com
MIT License
2.05k stars 373 forks source link

Unable to add delegate to vector #768

Closed briandilley closed 2 months ago

briandilley commented 9 months ago

I have the following class:


#ifndef HALAL_H
#define HALAL_H

#include <etl/delegate.h>
#include <etl/vector.h>

#include <stm32f4xx_hal.h>
#include <stm32f4xx_hal_tim.h>

#ifndef HALAL_CONF_MAX_INTERRUPT_CALLBACKS
#define HALAL_CONF_MAX_INTERRUPT_CALLBACKS 10
#endif

typedef etl::delegate<void(CAN_HandleTypeDef*, uint32_t)> HALALCanRxFifoMsgPendingCallback;
typedef etl::delegate<void(uint16_t)> HALALGpioInterruptCallback;

class HALAL {
public:

  static void setEnabled(bool enabled);

  static void registerGpioInterruptCallback(HALALGpioInterruptCallback callback);
  static void invokeGpioInterrupt(uint16_t gpioPin);

  static void registerCanRxFifoMsgPendingCallback(HALALCanRxFifoMsgPendingCallback callback);
  static void invokeCanRxFifoMsgPending(CAN_HandleTypeDef* hcan, uint32_t RxFifo);

private:
  static volatile bool enabled;
  static etl::vector<HALALGpioInterruptCallback, HALAL_CONF_MAX_INTERRUPT_CALLBACKS> gpioInterruptCallbacks;
  static etl::vector<HALALCanRxFifoMsgPendingCallback, HALAL_CONF_MAX_INTERRUPT_CALLBACKS> canRxFifoMsgPendingCallbacks;
};

#endif

with the following implementation:


etl::vector<HALALGpioInterruptCallback, HALAL_CONF_MAX_INTERRUPT_CALLBACKS> HALAL::gpioInterruptCallbacks;
etl::vector<HALALCanRxFifoMsgPendingCallback, HALAL_CONF_MAX_INTERRUPT_CALLBACKS> HALAL::canRxFifoMsgPendingCallbacks;
volatile bool HALAL::enabled = false;

void HALAL::registerGpioInterruptCallback(HALALGpioInterruptCallback callback) {
  gpioInterruptCallbacks.push_back(callback);
}

void HALAL::invokeGpioInterrupt(uint16_t gpioPin) {
  if (!enabled) {
    return;
  }
  for (auto& cb : gpioInterruptCallbacks) {
    cb(gpioPin);
  }
}

void HALAL::registerCanRxFifoMsgPendingCallback(HALALCanRxFifoMsgPendingCallback callback) {
  canRxFifoMsgPendingCallbacks.push_back(callback);
}

void HALAL::invokeCanRxFifoMsgPending(CAN_HandleTypeDef *hcan, uint32_t RxFifo) {
  if (!enabled) {
    return;
  }
  for (auto& cb : canRxFifoMsgPendingCallbacks) {
    cb(hcan, RxFifo);
  }
}

I use it in the constructor of some "System" objects to register callbacks for interrupts like so:

using Singleton = etl::singleton<DINSystem>;

DINSystem::DINSystem():
  states(),
  changedPins() {

  HALAL::registerGpioInterruptCallback(
    HALALGpioInterruptCallback::create<DINSystem, &DINSystem::gpioInterruptCallback>(*this));
}

DINSystem& DINSystem::getInstance() {
  if (!Singleton::is_valid()) {
    Singleton::create();
  }
  return Singleton::instance();
}

void DINSystem::gpioInterruptCallback(uint16_t gpioPin) {
  this->changedPins.set(gpioPin);
}

For whatever reason that registerGpioInterruptCallback call fails in memory.h line 1806 where the vector is using it to create a copy of the delegate for storage within the vector (I think):

  template <typename T>
  void create_copy_at(T* p, const T& value)
  {
    ::new (p) T(value);  // <-- (fails here)
  }

Here's the stack:

image

The weirdest thing is that the other registration method(s) work just fine in other objects using the same pattern (registration in the constructor). (There are actually a lot more registration methods, i just left them out for brevity). The value of p looks fine to me, it's the same value as the p_end member of the vector.... so I assume it's fine.

jwellbelove commented 9 months ago

Does it fail on the first push to the vector? If not, are you sure that you are not exceeding the vector's capacity?

jwellbelove commented 9 months ago

I wonder if you are falling foul of what is called the "Static Initialisation Fiasco" if the DINSystem instance is declared in a different translation unit to the gpioInterruptCallbacks vector.

Is it possible that the call to push_back is occurring before the vector is initialised?

briandilley commented 9 months ago

It occurs on the first push to that vector. Pushes to other static vectors on the same class happen before this one and are successful. Also, I call the HALAL::setEnabled(false) as the first thing in the program basically... so I would hope that after that call everything on that class is initialized. It's essentially this:

HALAL::setEnabled(false);

... STM32 Peripheral initialization ...

... call to a function to perform "System" initialization (where this problem is occurring) ...

HALAL::setEnabled(true);

... start FreeRTOS ...
briandilley commented 9 months ago

Ok - new information: At the top of the registration methods I query the size and capacity of the vectors and they're WAY off... even the ones that were "working" before. So I guess their memory has been corrupted some how? I'm fairly new to the embedded stuff.

void HALAL::registerGpioInterruptCallback(HALALGpioInterruptCallback callback) {
  auto size = HALAL::gpioInterruptCallbacks.size();
  auto capacity = HALAL::gpioInterruptCallbacks.capacity();
  HALAL::gpioInterruptCallbacks.push_back(callback);
}

void HALAL::registerTimerPeriodElapsedCallback(HALALTimerPeriodElapsedCallback callback) {
  auto size = HALAL::timerPeriodElapsedCallbacks.size(); // reports numbers like 4294967277
  auto capacity = HALAL::timerPeriodElapsedCallbacks.capacity();
  timerPeriodElapsedCallbacks.push_back(callback);
}
jwellbelove commented 8 months ago

Did you manage to resolve this?