0

Please consider following code:

struct timer_s {
  timer_s(void (*func)(void *), void *data, uint64_t period = 0)
      : func(func), data(data), period(period) {}

  void (*func)(void *);
  void *data;
  uint64_t period;
};

static std::map<uint64_t, std::unique_ptr<timer_s>> TIMERS;

extern "C" void default_machine_timer_interrupt_handler(void) {
  auto mtime = interrupt_timer_mtime_get();

  if (TIMERS.empty()) {
    interrupt_timer_disable();
    return;
  }

  auto it = TIMERS.begin();

  it->second->func(it->second->data);

  if (it->second->period) std::swap(TIMERS[mtime + it->second->period], it->second);

  TIMERS.erase(it);

  if (!TIMERS.empty()) {
    interrupt_timer_cmp_set(TIMERS.begin()->first);
    return;
  }

  interrupt_timer_disable();
}

void create_timer(void (*fun)(void *), void *data, uint32_t usec, timer_type_t timer_type) {
  auto when = interrupt_timer_mtime_get() + usec;
  auto enable = false;
  if (TIMERS.empty()) enable = true;

  TIMERS.insert(std::make_pair(when, std::unique_ptr<timer_s>(new timer_s(
                                         fun, data, timer_type == TIMER_REPEAT ? usec : 0))));

  interrupt_timer_cmp_set(TIMERS.begin()->first);

  if (enable) interrupt_timer_enable();
}

My question is regarding using advanced solutions like STL in ISR. For the sake of discussion lets leave performance(complicated operations in interrupt context).

default_machine_timer_interrupt_handler is run from ISR and create_timer from main context. It works just fine. But why? Map TIMERS is not volatile and optimization is on. Is it just a coincidence? When I have to tag value as volatile?

Kazik
  • 45
  • 1
  • 5
  • The fundamental problem here is (that `volatile` will not fix) is that `create_timer` has a considerable amount of time where it relies on the internal state of the map not changing, but there is nothing to prevent the timer interrupt from changing that state (think if the `TIMERS.insert` call was pointing at a node that is then erased by the interrupt call). There is also the issue that freeing the memory in `TIMERS.erase` may not be possible if there is any other part of the program that could potentially be allocating or freeing memory at the time of the interrupt. – 1201ProgramAlarm Sep 16 '20 at 15:53
  • A rule of thumb for embedded systems: Don't use functions or structures that use dynamic memory. Memory allocations can lead to fragmentation and embedded systems don't have a lot of memory. Better to use a global array and work in that array. – Thomas Matthews Sep 16 '20 at 15:54
  • With the speed of modern day processors, a linear search on a table will be very quick, sometimes faster than a `std::map`. – Thomas Matthews Sep 16 '20 at 15:55
  • You may be able to read from the interrupt controller and find out which interrupt number called the ISR. Use this number as an index into a table of function pointers. – Thomas Matthews Sep 16 '20 at 15:56

0 Answers0