threadsafe_queue.h 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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 <atomic>
  8. #include <condition_variable>
  9. #include <cstddef>
  10. #include <mutex>
  11. #include <utility>
  12. namespace Common {
  13. template <typename T>
  14. class SPSCQueue {
  15. public:
  16. SPSCQueue() {
  17. write_ptr = read_ptr = new ElementPtr();
  18. }
  19. ~SPSCQueue() {
  20. // this will empty out the whole queue
  21. delete read_ptr;
  22. }
  23. std::size_t Size() const {
  24. return size.load();
  25. }
  26. bool Empty() const {
  27. return Size() == 0;
  28. }
  29. T& Front() const {
  30. return read_ptr->current;
  31. }
  32. template <typename Arg>
  33. void Push(Arg&& t) {
  34. // create the element, add it to the queue
  35. write_ptr->current = std::forward<Arg>(t);
  36. // set the next pointer to a new element ptr
  37. // then advance the write pointer
  38. ElementPtr* new_ptr = new ElementPtr();
  39. write_ptr->next.store(new_ptr, std::memory_order_release);
  40. write_ptr = new_ptr;
  41. cv.notify_one();
  42. ++size;
  43. }
  44. void Pop() {
  45. --size;
  46. ElementPtr* tmpptr = read_ptr;
  47. // advance the read pointer
  48. read_ptr = tmpptr->next.load();
  49. // set the next element to nullptr to stop the recursive deletion
  50. tmpptr->next.store(nullptr);
  51. delete tmpptr; // this also deletes the element
  52. }
  53. bool Pop(T& t) {
  54. if (Empty())
  55. return false;
  56. --size;
  57. ElementPtr* tmpptr = read_ptr;
  58. read_ptr = tmpptr->next.load(std::memory_order_acquire);
  59. t = std::move(tmpptr->current);
  60. tmpptr->next.store(nullptr);
  61. delete tmpptr;
  62. return true;
  63. }
  64. T PopWait() {
  65. if (Empty()) {
  66. std::unique_lock lock{cv_mutex};
  67. cv.wait(lock, [this]() { return !Empty(); });
  68. }
  69. T t;
  70. Pop(t);
  71. return t;
  72. }
  73. // not thread-safe
  74. void Clear() {
  75. size.store(0);
  76. delete read_ptr;
  77. write_ptr = read_ptr = new ElementPtr();
  78. }
  79. private:
  80. // stores a pointer to element
  81. // and a pointer to the next ElementPtr
  82. class ElementPtr {
  83. public:
  84. ElementPtr() {}
  85. ~ElementPtr() {
  86. ElementPtr* next_ptr = next.load();
  87. if (next_ptr)
  88. delete next_ptr;
  89. }
  90. T current;
  91. std::atomic<ElementPtr*> next{nullptr};
  92. };
  93. ElementPtr* write_ptr;
  94. ElementPtr* read_ptr;
  95. std::atomic_size_t size{0};
  96. std::mutex cv_mutex;
  97. std::condition_variable cv;
  98. };
  99. // a simple thread-safe,
  100. // single reader, multiple writer queue
  101. template <typename T>
  102. class MPSCQueue {
  103. public:
  104. std::size_t Size() const {
  105. return spsc_queue.Size();
  106. }
  107. bool Empty() const {
  108. return spsc_queue.Empty();
  109. }
  110. T& Front() const {
  111. return spsc_queue.Front();
  112. }
  113. template <typename Arg>
  114. void Push(Arg&& t) {
  115. std::lock_guard lock{write_lock};
  116. spsc_queue.Push(t);
  117. }
  118. void Pop() {
  119. return spsc_queue.Pop();
  120. }
  121. bool Pop(T& t) {
  122. return spsc_queue.Pop(t);
  123. }
  124. T PopWait() {
  125. return spsc_queue.PopWait();
  126. }
  127. // not thread-safe
  128. void Clear() {
  129. spsc_queue.Clear();
  130. }
  131. private:
  132. SPSCQueue<T> spsc_queue;
  133. std::mutex write_lock;
  134. };
  135. } // namespace Common