threadsafe_queue.h 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Copyright 2010 Dolphin Emulator Project
  2. // Licensed under GPLv2+
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. // a simple lockless thread-safe,
  6. // single reader, single writer queue
  7. #include <algorithm>
  8. #include <atomic>
  9. #include <cstddef>
  10. #include <mutex>
  11. #include "common/common_types.h"
  12. namespace Common {
  13. template <typename T, bool NeedSize = true>
  14. class SPSCQueue {
  15. public:
  16. SPSCQueue() : size(0) {
  17. write_ptr = read_ptr = new ElementPtr();
  18. }
  19. ~SPSCQueue() {
  20. // this will empty out the whole queue
  21. delete read_ptr;
  22. }
  23. u32 Size() const {
  24. static_assert(NeedSize, "using Size() on FifoQueue without NeedSize");
  25. return size.load();
  26. }
  27. bool Empty() const {
  28. return !read_ptr->next.load();
  29. }
  30. T& Front() const {
  31. return read_ptr->current;
  32. }
  33. template <typename Arg>
  34. void Push(Arg&& t) {
  35. // create the element, add it to the queue
  36. write_ptr->current = std::forward<Arg>(t);
  37. // set the next pointer to a new element ptr
  38. // then advance the write pointer
  39. ElementPtr* new_ptr = new ElementPtr();
  40. write_ptr->next.store(new_ptr, std::memory_order_release);
  41. write_ptr = new_ptr;
  42. if (NeedSize)
  43. size++;
  44. }
  45. void Pop() {
  46. if (NeedSize)
  47. size--;
  48. ElementPtr* tmpptr = read_ptr;
  49. // advance the read pointer
  50. read_ptr = tmpptr->next.load();
  51. // set the next element to nullptr to stop the recursive deletion
  52. tmpptr->next.store(nullptr);
  53. delete tmpptr; // this also deletes the element
  54. }
  55. bool Pop(T& t) {
  56. if (Empty())
  57. return false;
  58. if (NeedSize)
  59. size--;
  60. ElementPtr* tmpptr = read_ptr;
  61. read_ptr = tmpptr->next.load(std::memory_order_acquire);
  62. t = std::move(tmpptr->current);
  63. tmpptr->next.store(nullptr);
  64. delete tmpptr;
  65. return true;
  66. }
  67. // not thread-safe
  68. void Clear() {
  69. size.store(0);
  70. delete read_ptr;
  71. write_ptr = read_ptr = new ElementPtr();
  72. }
  73. private:
  74. // stores a pointer to element
  75. // and a pointer to the next ElementPtr
  76. class ElementPtr {
  77. public:
  78. ElementPtr() : next(nullptr) {}
  79. ~ElementPtr() {
  80. ElementPtr* next_ptr = next.load();
  81. if (next_ptr)
  82. delete next_ptr;
  83. }
  84. T current;
  85. std::atomic<ElementPtr*> next;
  86. };
  87. ElementPtr* write_ptr;
  88. ElementPtr* read_ptr;
  89. std::atomic<u32> size;
  90. };
  91. // a simple thread-safe,
  92. // single reader, multiple writer queue
  93. template <typename T, bool NeedSize = true>
  94. class MPSCQueue : public SPSCQueue<T, NeedSize> {
  95. public:
  96. template <typename Arg>
  97. void Push(Arg&& t) {
  98. std::lock_guard<std::mutex> lock(write_lock);
  99. SPSCQueue<T, NeedSize>::Push(t);
  100. }
  101. private:
  102. std::mutex write_lock;
  103. };
  104. } // namespace Common