threadsafe_queue.h 3.9 KB

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