threadsafe_queue.h 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. [[nodiscard]] std::size_t Size() const {
  24. return size.load();
  25. }
  26. [[nodiscard]] bool Empty() const {
  27. return Size() == 0;
  28. }
  29. [[nodiscard]] 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. const size_t previous_size{size++};
  42. // Acquire the mutex and then immediately release it as a fence.
  43. // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
  44. // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
  45. if (previous_size == 0) {
  46. std::lock_guard lock{cv_mutex};
  47. }
  48. cv.notify_one();
  49. }
  50. void Pop() {
  51. --size;
  52. ElementPtr* tmpptr = read_ptr;
  53. // advance the read pointer
  54. read_ptr = tmpptr->next.load();
  55. // set the next element to nullptr to stop the recursive deletion
  56. tmpptr->next.store(nullptr);
  57. delete tmpptr; // this also deletes the element
  58. }
  59. bool Pop(T& t) {
  60. if (Empty())
  61. return false;
  62. --size;
  63. ElementPtr* tmpptr = read_ptr;
  64. read_ptr = tmpptr->next.load(std::memory_order_acquire);
  65. t = std::move(tmpptr->current);
  66. tmpptr->next.store(nullptr);
  67. delete tmpptr;
  68. return true;
  69. }
  70. void Wait() {
  71. if (Empty()) {
  72. std::unique_lock lock{cv_mutex};
  73. cv.wait(lock, [this]() { return !Empty(); });
  74. }
  75. }
  76. T PopWait() {
  77. Wait();
  78. T t;
  79. Pop(t);
  80. return t;
  81. }
  82. // not thread-safe
  83. void Clear() {
  84. size.store(0);
  85. delete read_ptr;
  86. write_ptr = read_ptr = new ElementPtr();
  87. }
  88. private:
  89. // stores a pointer to element
  90. // and a pointer to the next ElementPtr
  91. class ElementPtr {
  92. public:
  93. ElementPtr() {}
  94. ~ElementPtr() {
  95. ElementPtr* next_ptr = next.load();
  96. if (next_ptr)
  97. delete next_ptr;
  98. }
  99. T current;
  100. std::atomic<ElementPtr*> next{nullptr};
  101. };
  102. ElementPtr* write_ptr;
  103. ElementPtr* read_ptr;
  104. std::atomic_size_t size{0};
  105. std::mutex cv_mutex;
  106. std::condition_variable cv;
  107. };
  108. // a simple thread-safe,
  109. // single reader, multiple writer queue
  110. template <typename T>
  111. class MPSCQueue {
  112. public:
  113. [[nodiscard]] std::size_t Size() const {
  114. return spsc_queue.Size();
  115. }
  116. [[nodiscard]] bool Empty() const {
  117. return spsc_queue.Empty();
  118. }
  119. [[nodiscard]] T& Front() const {
  120. return spsc_queue.Front();
  121. }
  122. template <typename Arg>
  123. void Push(Arg&& t) {
  124. std::lock_guard lock{write_lock};
  125. spsc_queue.Push(t);
  126. }
  127. void Pop() {
  128. return spsc_queue.Pop();
  129. }
  130. bool Pop(T& t) {
  131. return spsc_queue.Pop(t);
  132. }
  133. void Wait() {
  134. spsc_queue.Wait();
  135. }
  136. T PopWait() {
  137. return spsc_queue.PopWait();
  138. }
  139. // not thread-safe
  140. void Clear() {
  141. spsc_queue.Clear();
  142. }
  143. private:
  144. SPSCQueue<T> spsc_queue;
  145. std::mutex write_lock;
  146. };
  147. } // namespace Common