threadsafe_queue.h 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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>
  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. u32 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. ++size;
  42. }
  43. void Pop() {
  44. --size;
  45. ElementPtr* tmpptr = read_ptr;
  46. // advance the read pointer
  47. read_ptr = tmpptr->next.load();
  48. // set the next element to nullptr to stop the recursive deletion
  49. tmpptr->next.store(nullptr);
  50. delete tmpptr; // this also deletes the element
  51. }
  52. bool Pop(T& t) {
  53. if (Empty())
  54. return false;
  55. --size;
  56. ElementPtr* tmpptr = read_ptr;
  57. read_ptr = tmpptr->next.load(std::memory_order_acquire);
  58. t = std::move(tmpptr->current);
  59. tmpptr->next.store(nullptr);
  60. delete tmpptr;
  61. return true;
  62. }
  63. // not thread-safe
  64. void Clear() {
  65. size.store(0);
  66. delete read_ptr;
  67. write_ptr = read_ptr = new ElementPtr();
  68. }
  69. private:
  70. // stores a pointer to element
  71. // and a pointer to the next ElementPtr
  72. class ElementPtr {
  73. public:
  74. ElementPtr() : next(nullptr) {}
  75. ~ElementPtr() {
  76. ElementPtr* next_ptr = next.load();
  77. if (next_ptr)
  78. delete next_ptr;
  79. }
  80. T current;
  81. std::atomic<ElementPtr*> next;
  82. };
  83. ElementPtr* write_ptr;
  84. ElementPtr* read_ptr;
  85. std::atomic<u32> size{0};
  86. };
  87. // a simple thread-safe,
  88. // single reader, multiple writer queue
  89. template <typename T>
  90. class MPSCQueue {
  91. public:
  92. u32 Size() const {
  93. return spsc_queue.Size();
  94. }
  95. bool Empty() const {
  96. return spsc_queue.Empty();
  97. }
  98. T& Front() const {
  99. return spsc_queue.Front();
  100. }
  101. template <typename Arg>
  102. void Push(Arg&& t) {
  103. std::lock_guard<std::mutex> lock(write_lock);
  104. spsc_queue.Push(t);
  105. }
  106. void Pop() {
  107. return spsc_queue.Pop();
  108. }
  109. bool Pop(T& t) {
  110. return spsc_queue.Pop(t);
  111. }
  112. // not thread-safe
  113. void Clear() {
  114. spsc_queue.Clear();
  115. }
  116. private:
  117. SPSCQueue<T> spsc_queue;
  118. std::mutex write_lock;
  119. };
  120. } // namespace Common