
#ifndef PLS_BACKOFF_H_
#define PLS_BACKOFF_H_

#include "pls/internal/base/system_details.h"
#include "pls/internal/helpers/profiler.h"
#include "pls/internal/base/thread.h"

#include <random>
#include <math.h>

namespace pls {
namespace internal {
namespace base {

class backoff {
  const unsigned long INITIAL_SPIN_ITERS = 2u << 4u;
  const unsigned long MAX_SPIN_ITERS = 2u << 8u;
  const unsigned long MAX_ITERS = 2u << 10u;
  const unsigned long YELD_ITERS = 2u << 10u;

  unsigned long current_ = INITIAL_SPIN_ITERS;
  std::minstd_rand random_;

  static void spin(unsigned long iterations) {
    for (volatile unsigned long i = 0; i < iterations; i++)
      system_details::relax_cpu(); // Spin
  }

 public:
  backoff() : current_{INITIAL_SPIN_ITERS}, random_{std::random_device{}()} {}

  void do_backoff() {
    PROFILE_LOCK("Backoff")
    spin(random_() % std::min(current_, MAX_SPIN_ITERS));

    if (current_ >= YELD_ITERS) {
      PROFILE_LOCK("Yield")
      this_thread::yield();
    }

    current_ = std::min(current_ * 2, MAX_ITERS);
  }

  void reset() {
    current_ = INITIAL_SPIN_ITERS;
  }
};

}
}
}

#endif //PLS_BACKOFF_H_
