text: /* timer_pool.cc
     Mathieu Stefani, 09 février 2016
     
     Implementation of the timer pool
  */
  
  #include <pistache/os.h>
  #include <pistache/timer_pool.h>
  
  #include <cassert>
  
  #include <sys/timerfd.h>
  
  namespace Pistache {
  
  TimerPool::Entry::Entry()
      : fd_(-1)
      , registered(false)
  {
      state.store(static_cast<uint32_t>(State::Idle));
  }
  
  TimerPool::Entry::~Entry()
  {
      if (fd_ != -1)
          close(fd_);
  }
  
  Fd TimerPool::Entry::fd() const
  {
      assert(fd_ != -1);
  
      return fd_;
  }
  
  void
  TimerPool::Entry::initialize() {
      if (fd_ == -1) {
          fd_ = TRY_RET(timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK));
      }
  }
  
  void TimerPool::Entry::disarm()
  {
      assert(fd_ != -1);
  
      itimerspec spec;
      spec.it_interval.tv_sec = 0;
      spec.it_interval.tv_nsec = 0;
  
      spec.it_value.tv_sec = 0;
      spec.it_value.tv_nsec = 0;
  
      TRY(timerfd_settime(fd_, 0, &spec, 0));
  }
  
  void TimerPool::Entry::registerReactor(const Aio::Reactor::Key& key, Aio::Reactor* reactor)
  {
      if (!registered)
      {
          reactor->registerFd(key, fd_, Polling::NotifyOn::Read);
          registered = true;
      }
  }
  
  void
  TimerPool::Entry::armMs(std::chrono::milliseconds value)
  {
      itimerspec spec;
      spec.it_interval.tv_sec = 0;
      spec.it_interval.tv_nsec = 0;
  
      if (value.count() < 1000) {
          spec.it_value.tv_sec = 0;
          spec.it_value.tv_nsec
              = std::chrono::duration_cast<std::chrono::nanoseconds>(value).count();
      } else {
          spec.it_value.tv_sec
              = std::chrono::duration_cast<std::chrono::seconds>(value).count();
          spec.it_value.tv_nsec = 0;
      }
      TRY(timerfd_settime(fd_, 0, &spec, 0));
  }
  
  TimerPool::TimerPool(size_t initialSize)
  {
      for (size_t i = 0; i < initialSize; ++i) {
          timers.push_back(std::make_shared<TimerPool::Entry>());
      }
  }
  
  std::shared_ptr<TimerPool::Entry>
  TimerPool::pickTimer() {
      for (auto& entry: timers) {
          auto curState = static_cast<uint32_t>(TimerPool::Entry::State::Idle);
          auto newState = static_cast<uint32_t>(TimerPool::Entry::State::Used);
          if (entry->state.compare_exchange_strong(curState, newState)) {
              entry->initialize();
              return entry;
          }
      }
  
      return nullptr;
  }
  
  void
  TimerPool::releaseTimer(const std::shared_ptr<Entry>& timer) {
      timer->state.store(static_cast<uint32_t>(TimerPool::Entry::State::Idle));
  }
  
  } // namespace Pistache
  