<commit_before>#include <kernel/timers.hpp>

#include <kernel/os.hpp>
#include <service>
#include <smp>
#include <statman>
#include <map>
#include <vector>

using namespace std::chrono;
typedef Timers::duration_t duration_t;
typedef Timers::handler_t  handler_t;

static void sched_timer(duration_t when, Timers::id_t id);

struct Timer
{
  Timer(duration_t p, handler_t cb)
    : period(p), callback(cb), deferred_destruct(false) {}

  bool is_alive() const noexcept {
    return deferred_destruct == false;
  }

  bool is_oneshot() const noexcept {
    return period.count() == 0;
  }

  void reset() {
    callback.reset();
    deferred_destruct = false;
  }

  duration_t period;
  handler_t  callback;
  bool deferred_destruct = false;
};

/**
 * 1. There are no restrictions on when timers can be started or stopped
 * 2. A period of 0 means start a one-shot timer
 * 3. Each timer is a separate object living in a "fixed" vector
 * 4. A dead timer is simply a timer which has its handler reset, as well as
 *     having been removed from schedule
 * 5. No timer may be scheduled more than once at a time, as that will needlessly
 *     inflate the schedule container, as well as complicate stopping timers
 * 6. Free timer IDs are retrieved from a stack of free timer IDs (or through
 *     expanding the "fixed" vector)
**/
static bool signal_ready = false;

struct alignas(SMP_ALIGN) timer_system
{
  bool     is_running  = false;
  uint32_t dead_timers = 0;
  Timers::start_func_t arch_start_func;
  Timers::stop_func_t  arch_stop_func;
  std::vector<Timer>        timers;
  std::vector<Timers::id_t> free_timers;
  // timers sorted by timestamp
  std::multimap<duration_t, Timers::id_t> scheduled;
  /** Stats */
  int64_t*  oneshot_started;
  int64_t*  oneshot_stopped;
  uint32_t* periodic_started;
  uint32_t* periodic_stopped;
};
static SMP_ARRAY<timer_system> systems;

static inline timer_system& get() {
  return PER_CPU(systems);
}

void Timers::init(const start_func_t& start, const stop_func_t& stop)
{
  auto& system = get();
  // architecture specific start and stop functions
  system.arch_start_func = start;
  system.arch_stop_func  = stop;

  std::string CPU = "cpu" + std::to_string(SMP::cpu_id());
  system.oneshot_started = (int64_t*) &Statman::get().create(Stat::UINT64, CPU + ".timers.oneshot_started").get_uint64();
  system.oneshot_stopped = (int64_t*) &Statman::get().create(Stat::UINT64, CPU + ".timers.oneshot_stopped").get_uint64();
  system.periodic_started = &Statman::get().create(Stat::UINT32, CPU + ".timers.periodic_started").get_uint32();
  system.periodic_stopped = &Statman::get().create(Stat::UINT32, CPU + ".timers.periodic_stopped").get_uint32();
}

bool Timers::is_ready()
{
  return signal_ready;
}
void Timers::ready()
{
  signal_ready = true;
  // begin processing timers if any are queued
  if (get().is_running == false) {
    timers_handler();
  }
  // call Service::ready(), because timer system is ready!
  Service::ready();
}

Timers::id_t Timers::periodic(duration_t when, duration_t period, handler_t handler)
{
  auto& system = get();
  Timers::id_t id;

  if (UNLIKELY(system.free_timers.empty()))
  {
    if (LIKELY(system.dead_timers))
    {
      // look for dead timer
      auto it = system.scheduled.begin();
      while (it != system.scheduled.end()) {
        // take over this timer, if dead
        Timers::id_t id = it->second;

        if (system.timers[id].deferred_destruct)
        {
          system.dead_timers--;
          // remove from schedule
          system.scheduled.erase(it);
          // reset timer
          system.timers[id].reset();
          // reuse timer
          new (&system.timers[id]) Timer(period, handler);

          // Stat increment timer started
          if (system.timers[id].is_oneshot()) {
            if (system.oneshot_started) (*system.oneshot_started)++;
          } else {
            if (system.periodic_started) (*system.periodic_started)++;
          }

          sched_timer(when, id);
          return id;
        }
        ++it;
      }
    }
    id = system.timers.size();
    // occupy new slot
    system.timers.emplace_back(period, handler);
  }
  else {
    // get free timer slot
    id = system.free_timers.back();
    system.free_timers.pop_back();

    // occupy free slot
    new (&system.timers[id]) Timer(period, handler);
  }

  // Stat increment timer started
  if (system.timers[id].is_oneshot()) {
    if (system.oneshot_started) (*system.oneshot_started)++;
  } else {
    if (system.periodic_started) (*system.periodic_started)++;
  }

  // immediately schedule timer
  sched_timer(when, id);
  return id;
}

void Timers::stop(Timers::id_t id)
{
  auto& system = get();<commit_msg>kernel: Workaround for internal Timer destructor sometimes being called by another outside class with the same name<commit_after>#include <kernel/timers.hpp>

#include <kernel/os.hpp>
#include <service>
#include <smp>
#include <statman>
#include <map>
#include <vector>

using namespace std::chrono;
typedef Timers::duration_t duration_t;
typedef Timers::handler_t  handler_t;

static void sched_timer(duration_t when, Timers::id_t id);

struct SystemTimer
{
  SystemTimer(duration_t p, handler_t cb)
    : period(p), callback(std::move(cb)), deferred_destruct(false) {}

  SystemTimer(SystemTimer&& other)
  : period(other.period),
    callback(std::move(other.callback)),
    deferred_destruct(other.deferred_destruct) {}

  bool is_alive() const noexcept {
    return deferred_destruct == false;
  }

  bool is_oneshot() const noexcept {
    return period.count() == 0;
  }

  void reset() {
    callback.reset();
    deferred_destruct = false;
  }

  duration_t period;
  handler_t  callback;
  bool deferred_destruct = false;
};

/**
 * 1. There are no restrictions on when timers can be started or stopped
 * 2. A period of 0 means start a one-shot timer
 * 3. Each timer is a separate object living in a "fixed" vector
 * 4. A dead timer is simply a timer which has its handler reset, as well as
 *     having been removed from schedule
 * 5. No timer may be scheduled more than once at a time, as that will needlessly
 *     inflate the schedule container, as well as complicate stopping timers
 * 6. Free timer IDs are retrieved from a stack of free timer IDs (or through
 *     expanding the "fixed" vector)
**/
static bool signal_ready = false;

struct alignas(SMP_ALIGN) timer_system
{
  bool     is_running  = false;
  uint32_t dead_timers = 0;
  Timers::start_func_t arch_start_func;
  Timers::stop_func_t  arch_stop_func;
  std::vector<SystemTimer>  timers;
  std::vector<Timers::id_t> free_timers;
  // timers sorted by timestamp
  std::multimap<duration_t, Timers::id_t> scheduled;
  /** Stats */
  int64_t*  oneshot_started;
  int64_t*  oneshot_stopped;
  uint32_t* periodic_started;
  uint32_t* periodic_stopped;
};
static SMP_ARRAY<timer_system> systems;

static inline timer_system& get() {
  return PER_CPU(systems);
}

void Timers::init(const start_func_t& start, const stop_func_t& stop)
{
  auto& system = get();
  // architecture specific start and stop functions
  system.arch_start_func = start;
  system.arch_stop_func  = stop;

  std::string CPU = "cpu" + std::to_string(SMP::cpu_id());
  system.oneshot_started = (int64_t*) &Statman::get().create(Stat::UINT64, CPU + ".timers.oneshot_started").get_uint64();
  system.oneshot_stopped = (int64_t*) &Statman::get().create(Stat::UINT64, CPU + ".timers.oneshot_stopped").get_uint64();
  system.periodic_started = &Statman::get().create(Stat::UINT32, CPU + ".timers.periodic_started").get_uint32();
  system.periodic_stopped = &Statman::get().create(Stat::UINT32, CPU + ".timers.periodic_stopped").get_uint32();
}

bool Timers::is_ready()
{
  return signal_ready;
}
void Timers::ready()
{
  signal_ready = true;
  // begin processing timers if any are queued
  if (get().is_running == false) {
    timers_handler();
  }
  // call Service::ready(), because timer system is ready!
  Service::ready();
}

Timers::id_t Timers::periodic(duration_t when, duration_t period, handler_t handler)
{
  auto& system = get();
  Timers::id_t id;

  if (UNLIKELY(system.free_timers.empty()))
  {
    if (LIKELY(system.dead_timers))
    {
      // look for dead timer
      auto it = system.scheduled.begin();
      while (it != system.scheduled.end()) {
        // take over this timer, if dead
        id = it->second;

        if (system.timers[id].deferred_destruct)
        {
          system.dead_timers--;
          // remove from schedule
          system.scheduled.erase(it);
          // reset timer
          system.timers[id].reset();
          // reuse timer
          new (&system.timers[id]) SystemTimer(period, handler);

          // Stat increment timer started
          if (system.timers[id].is_oneshot()) {
            if (system.oneshot_started) (*system.oneshot_started)++;
          } else {
            if (system.periodic_started) (*system.periodic_started)++;
          }

          sched_timer(when, id);
          return id;
        }
        ++it;
      }
    }
    id = system.timers.size();
    // occupy new slot
    system.timers.emplace_back(period, handler);
  }
  else {
    // get free timer slot
    id = system.free_timers.back();
    system.free_timers.pop_back();

    // occupy free slot
    new (&system.timers[id]) SystemTimer(period, handler);
  }

  // Stat increment timer started
  if (system.timers[id].is_oneshot()) {
    if (system.oneshot_started) (*system.oneshot_started)++;
  } else {
    if (system.periodic_started) (*system.periodic_started)++;
  }

  // immediately schedule timer
  sched_timer(when, id);
  return id;
}

void Timers::stop(Timers::id_t id)
{
  auto& system = get();
{'fraction_non_alphanumeric': 0.1251413881748072, 'fraction_numerical': 0.008020565552699229, 'mean_word_length': 3.4390689183021452, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4515256', 'n_tokens_mistral': 3307, 'n_tokens_neox': 3144, 'n_words': 813}