// Copyright 2010 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #pragma once // a simple lockless thread-safe, // single reader, single writer queue #include #include #include #include #include "common/common_types.h" namespace Common { template class SPSCQueue { public: SPSCQueue() : size(0) { write_ptr = read_ptr = new ElementPtr(); } ~SPSCQueue() { // this will empty out the whole queue delete read_ptr; } u32 Size() const { static_assert(NeedSize, "using Size() on FifoQueue without NeedSize"); return size.load(); } bool Empty() const { return !read_ptr->next.load(); } T& Front() const { return read_ptr->current; } template void Push(Arg&& t) { // create the element, add it to the queue write_ptr->current = std::forward(t); // set the next pointer to a new element ptr // then advance the write pointer ElementPtr* new_ptr = new ElementPtr(); write_ptr->next.store(new_ptr, std::memory_order_release); write_ptr = new_ptr; if (NeedSize) size++; } void Pop() { if (NeedSize) size--; ElementPtr* tmpptr = read_ptr; // advance the read pointer read_ptr = tmpptr->next.load(); // set the next element to nullptr to stop the recursive deletion tmpptr->next.store(nullptr); delete tmpptr; // this also deletes the element } bool Pop(T& t) { if (Empty()) return false; if (NeedSize) size--; ElementPtr* tmpptr = read_ptr; read_ptr = tmpptr->next.load(std::memory_order_acquire); t = std::move(tmpptr->current); tmpptr->next.store(nullptr); delete tmpptr; return true; } // not thread-safe void Clear() { size.store(0); delete read_ptr; write_ptr = read_ptr = new ElementPtr(); } private: // stores a pointer to element // and a pointer to the next ElementPtr class ElementPtr { public: ElementPtr() : next(nullptr) {} ~ElementPtr() { ElementPtr* next_ptr = next.load(); if (next_ptr) delete next_ptr; } T current; std::atomic next; }; ElementPtr* write_ptr; ElementPtr* read_ptr; std::atomic size; }; // a simple thread-safe, // single reader, multiple writer queue template class MPSCQueue : public SPSCQueue { public: template void Push(Arg&& t) { std::lock_guard lock(write_lock); SPSCQueue::Push(t); } private: std::mutex write_lock; }; } // namespace Common