#include "pls/internal/base/swmr_spin_lock.h"
#include "pls/internal/base/system_details.h"

namespace pls {
namespace internal {
namespace base {

bool swmr_spin_lock::reader_try_lock() {
  PROFILE_LOCK("Try Acquire Read Lock")
  if (write_request_.load(std::memory_order_acquire) == 1) {
    return false;
  }
  // We think we can enter the region
  readers_.fetch_add(1, std::memory_order_acquire);
  if (write_request_.load(std::memory_order_acquire) == 1) {
    // Whoops, the writer acquires the lock, so we back off again
    readers_.fetch_add(-1, std::memory_order_release);
    return false;
  }

  return true;
}

void swmr_spin_lock::reader_unlock() {
  PROFILE_LOCK("Release Read Lock")
  readers_--;
}

void swmr_spin_lock::writer_lock() {
  PROFILE_LOCK("Acquire Write Lock")
  // Tell the readers that we would like to write
  write_request_ = 1;

  // Wait for all of them to exit the critical section
  while (readers_ > 0)
    system_details::relax_cpu(); // Spin, not expensive as relaxed load
}

void swmr_spin_lock::writer_unlock() {
  PROFILE_LOCK("Release Write Lock")
  write_request_ = 0;
}

}
}
}
