Program Listing for File barrier.h

Return to documentation for file (include/embers/primitives/barrier.h)

/* Copyright © 2020 Advanced Micro Devices, Inc. All rights reserved */

#ifndef _EMBERS_BARRIER_H_
#define _EMBERS_BARRIER_H_

#include <thread>

#include "embers/atomic.h"
#include "embers/primitives/backoff.h"

namespace embers
{

template <MemoryScope scope = MemoryScope::SYSTEM>
class Barrier
{
 private:
  const uint64_t num_participants_;
  atomic<uint64_t, scope> first_;
  atomic<uint64_t, scope> second_;

  __host__ void threadfence() const noexcept
  {
    std::atomic_thread_fence(std::memory_order_seq_cst);
  }
  __device__ void threadfence() const noexcept
  {
    if constexpr (scope == MemoryScope::AGENT) {
      __threadfence();
    } else {
      __threadfence_system();
    }
  }

 public:
  __host__ __device__ Barrier(uint64_t num_participants = 1)
      : num_participants_(num_participants),
        first_(atomic<uint64_t>(0)),
        second_(atomic<uint64_t>(0))
  {
  }

  __host__ __device__ Barrier(const Barrier &) = delete;
  __host__ ~Barrier() = default;

  __host__ __device__ void Sync(std::memory_order order = std::memory_order_relaxed) noexcept
  {
    // the incs and loads can be relaxed here because they are to the same variable (we want the inc
    // and the loads to be ordered with each other)
    first_.fetch_inc(order);
    while (first_.load(order) % num_participants_ != 0) {
      backoff();
    }

    // the threadfence with SEQ_CST ordering being in the middle ensures that Sync has SEQ_CST
    // semantics (all acquires will happen before you leave the lock, all releases will happen
    // before you leave the lock).
    threadfence();

    // because we want to re-use the barrier, we need a second lock so that "everyone has left the
    // previous lock before I let anyone move on"
    second_.fetch_inc(order);
    while (second_.load(order) % num_participants_ != 0) {
      backoff();
    }
  }
};

}  // namespace embers
#endif  // _EMBERS_BARRIER_H_