//  BlockyFroggy
//  Copyright © 2019 John Ryland.
//  All rights reserved.
#pragma once
#ifndef LOCK_FREE_H
#define LOCK_FREE_H

// Based on pseudo-code from here:
//   https://www.infoq.com/news/2014/10/cpp-lock-free-programming
// Which is based on a Herb Sutter presentation
template <typename ObjectT>
class LockFreeList
{
public:
  LockFreeList() = default;
  ~LockFreeList() = default;

  class NodeReference
  { 
  public:
    NodeReference(SharedNodePtr aNode) : mNode{aNode} {}
    ObjectT& operator*()  { return mNode->mObject;  }
    ObjectT* operator->() { return &mNode->mObject; }
  private:
    SharedNodePtr mNode;
  };

  auto find(ObjectT aObject) const
  {
    auto node = mHead.load();
    while (node && node->mObject != aObject)
      node = node->mNext;
    return NodeReference{std::move(node)};
  }

  void push_front(ObjectT aObject)
  {
    auto node = std::make_shared<Node>();
    node->mObject = aObject;
    node->mNext = mHead;
    while (mHead.compare_exchange_weak(node->mNext, node))
      /* do nothing */;
  }

  void pop_front()
  {
    auto node = mHead.load();
    while (node && !mHead.compare_exchange_weak(node, node->mNext))
      /* do nothing */;
  }

private:
  typedef std::shared_ptr<Node> SharedNodePtr;
  typedef std::atomic<std::shared_ptr<ObjectT>> SharedAtomicObjectT;
  struct Node
  {
    ObjectT       mObject;
    SharedNodePtr mNext;
  };
  std::atomic<SharedNodePtr> mHead;
};

#endif // LOCK_FREE_H

