Program Listing for File nonlocking_queue.h

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

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

#ifndef _EMBERS_NONLOCKING_QUEUE_H_
#define _EMBERS_NONLOCKING_QUEUE_H_

#include "counters.h"
#include "embers/memory.h"
#include "embers/atomic.h"

namespace embers
{

/*
 * NonLockingQueue replaces the spinlock and producer/consumer indexes from
 * LockingQueue1P1C with two increasingly monotonic counters.
 *
 * QUEUE_EMPTY: head_.Value() == tail_.Value()
 * QUEUE_FULL: !EMPTY() && (Index(head_.Value()) == Index(tail_.Value()))
 */

template <typename T,
          typename MonCntType = MonotonicCounter<MemoryScope::SYSTEM, std::memory_order_seq_cst>,
          MemoryScope scope_ = MemoryScope::SYSTEM>
class NonLockingQueue
{
 public:
  class Entry
  {
    enum state : int { INVALID = 0, LOCKED, VALID };
    atomic<int> flag;
    T data;

   public:
    Entry() : flag(0) {}

   private:
    friend class NonLockingQueue;

    __host__ __device__ inline bool IsValid()
    {
      return (VALID == flag.load(std::memory_order_relaxed));
    }

    __host__ inline void AcquireFence() { std::atomic_thread_fence(std::memory_order_acquire); };

    __device__ inline void AcquireFence() { flag.load(std::memory_order_acquire); };

    __host__ __device__ inline void SetValueAndRelease(enum state val)
    {
      flag.store(val, std::memory_order_release);
    }

    __host__ __device__ inline bool LockEntry(enum state expected)
    {
      return flag.compare_exchange_strong(*reinterpret_cast<int *>(&expected), LOCKED,
                                          std::memory_order_relaxed, std::memory_order_relaxed);
    }

    __host__ __device__ inline void SetValidAndRelease() { SetValueAndRelease(VALID); }

    __host__ __device__ inline void InvalidateAndRelease() { SetValueAndRelease(INVALID); }
  };

 private:
  int32_t num_slots_;
  MonCntType head_;
  MonCntType tail_;
  unique_ptr<Entry[]> contents_;

  __host__ __device__ bool Empty();
  __host__ __device__ bool Full();

  __host__ __device__ typename MonCntType::counter_int_type QIDX(
      typename MonCntType::counter_int_type val);

  __host__ __device__ Entry *GetEntry(typename MonCntType::counter_int_type index);

 public:
  class Contents
  {
   private:
    typename MonCntType::counter_int_type log2_size;
    unique_ptr<Entry[]> data;
    Contents(typename MonCntType::counter_int_type log2_size, unique_ptr<Entry[]> data)
        : log2_size(log2_size), data(std::move(data))
    {
    }
    friend class NonLockingQueue;
  };
  __host__ static Contents MakeQueueContents(int hip_dev, unsigned int queue_flags,
                                             typename MonCntType::counter_int_type log2_size);
  __host__ static Contents MakeQueueContentsHost(unsigned int queue_flags,
                                                 typename MonCntType::counter_int_type log2_size);
  __host__ NonLockingQueue() = default;
  __host__ ~NonLockingQueue() = default;
  __host__ NonLockingQueue(const NonLockingQueue &) = delete;
  __host__ NonLockingQueue &operator=(const NonLockingQueue &) = delete;
  __host__ NonLockingQueue(NonLockingQueue &&) = default;
  __host__ NonLockingQueue(Contents contents);
  __host__ __device__ void Enqueue(T item);
  __host__ __device__ T Dequeue();
  __host__ __device__ void Reset();
};
}  // namespace embers

#include "nonlocking_queue_impl.h"

#endif  // _EMBERS_NONLOCKING_QUEUE_H_