#pragma once

#include "public.h"

#include <atomic>

namespace NYT {

////////////////////////////////////////////////////////////////////////////////

//! Multiple producer single consumer lock-free queue.
/*!
 *  Internally implemented by a pair of lock-free stack (head) and
 *  linked-list of popped-but-not-yet-dequeued items (tail).
 *
 *  Additionally supports draining during shutdown.
 *
 *  The proper shutdown sequence is as follows:
 *  1) #DrainConsumer must be called by the consumer thread.
 *  2) #DrainProducer must be called by each producer thread
 *  that has just enqueued some items into the queue.
 */
template <class T>
class TMpscQueue final
{
public:
    TMpscQueue(const TMpscQueue&) = delete;
    void operator=(const TMpscQueue&) = delete;

    TMpscQueue() = default;
    ~TMpscQueue();

    void Enqueue(const T& value);
    void Enqueue(T&& value);

    bool TryDequeue(T* value);

    bool IsEmpty() const;

    void DrainConsumer();
    void DrainProducer();

private:
    struct TNode;

    std::atomic<TNode*> Head_ = nullptr;
    TNode* Tail_ = nullptr;

    void DoEnqueue(TNode* node);
    void DeleteNodeList(TNode* node);
};

/////////////////////////////////////////////////////////////////////////////

} // namespace NYT

#define MPSC_QUEUE_INL_H_
#include "mpsc_queue-inl.h"
#undef MPSC_QUEUE_INL_H_