| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 | 
					
						
							|  |  |  | // SPDX-License-Identifier: GPL-2.0-or-later
 | 
					
						
							| 
									
										
										
										
											2022-06-14 08:57:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <atomic>
 | 
					
						
							|  |  |  | #include <condition_variable>
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  | #include <cstddef>
 | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  | #include <mutex>
 | 
					
						
							|  |  |  | #include <new>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-17 23:42:17 -07:00
										 |  |  | #include "common/polyfill_thread.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  | namespace Common { | 
					
						
							| 
									
										
										
										
											2022-06-14 08:57:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  | namespace detail { | 
					
						
							|  |  |  | constexpr size_t DefaultCapacity = 0x1000; | 
					
						
							|  |  |  | } // namespace detail
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename T, size_t Capacity = detail::DefaultCapacity> | 
					
						
							|  |  |  | class SPSCQueue { | 
					
						
							|  |  |  |     static_assert((Capacity & (Capacity - 1)) == 0, "Capacity must be a power of two."); | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     template <typename... Args> | 
					
						
							|  |  |  |     bool TryEmplace(Args&&... args) { | 
					
						
							|  |  |  |         return Emplace<PushMode::Try>(std::forward<Args>(args)...); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     template <typename... Args> | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     void EmplaceWait(Args&&... args) { | 
					
						
							|  |  |  |         Emplace<PushMode::Wait>(std::forward<Args>(args)...); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     bool TryPop(T& t) { | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         return Pop<PopMode::Try>(t); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void PopWait(T& t) { | 
					
						
							|  |  |  |         Pop<PopMode::Wait>(t); | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     void PopWait(T& t, std::stop_token stop_token) { | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         Pop<PopMode::WaitWithStopToken>(t, stop_token); | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     T PopWait() { | 
					
						
							| 
									
										
										
										
											2023-08-31 15:09:15 +01:00
										 |  |  |         T t{}; | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         Pop<PopMode::Wait>(t); | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |         return t; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     T PopWait(std::stop_token stop_token) { | 
					
						
							| 
									
										
										
										
											2023-08-31 15:09:15 +01:00
										 |  |  |         T t{}; | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         Pop<PopMode::WaitWithStopToken>(t, stop_token); | 
					
						
							|  |  |  |         return t; | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  | private: | 
					
						
							|  |  |  |     enum class PushMode { | 
					
						
							|  |  |  |         Try, | 
					
						
							|  |  |  |         Wait, | 
					
						
							|  |  |  |         Count, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     enum class PopMode { | 
					
						
							|  |  |  |         Try, | 
					
						
							|  |  |  |         Wait, | 
					
						
							|  |  |  |         WaitWithStopToken, | 
					
						
							|  |  |  |         Count, | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     template <PushMode Mode, typename... Args> | 
					
						
							|  |  |  |     bool Emplace(Args&&... args) { | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         const size_t write_index = m_write_index.load(std::memory_order::relaxed); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |         if constexpr (Mode == PushMode::Try) { | 
					
						
							|  |  |  |             // Check if we have free slots to write to.
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |             if ((write_index - m_read_index.load(std::memory_order::acquire)) == Capacity) { | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else if constexpr (Mode == PushMode::Wait) { | 
					
						
							|  |  |  |             // Wait until we have free slots to write to.
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:48:01 -04:00
										 |  |  |             std::unique_lock lock{producer_cv_mutex}; | 
					
						
							|  |  |  |             producer_cv.wait(lock, [this, write_index] { | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |                 return (write_index - m_read_index.load(std::memory_order::acquire)) < Capacity; | 
					
						
							| 
									
										
										
										
											2023-03-19 14:48:01 -04:00
										 |  |  |             }); | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             static_assert(Mode < PushMode::Count, "Invalid PushMode."); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Determine the position to write to.
 | 
					
						
							|  |  |  |         const size_t pos = write_index % Capacity; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Emplace into the queue.
 | 
					
						
							|  |  |  |         std::construct_at(std::addressof(m_data[pos]), std::forward<Args>(args)...); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Increment the write index.
 | 
					
						
							|  |  |  |         ++m_write_index; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Notify the consumer that we have pushed into the queue.
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:48:01 -04:00
										 |  |  |         std::scoped_lock lock{consumer_cv_mutex}; | 
					
						
							|  |  |  |         consumer_cv.notify_one(); | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     template <PopMode Mode> | 
					
						
							|  |  |  |     bool Pop(T& t, [[maybe_unused]] std::stop_token stop_token = {}) { | 
					
						
							|  |  |  |         const size_t read_index = m_read_index.load(std::memory_order::relaxed); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         if constexpr (Mode == PopMode::Try) { | 
					
						
							|  |  |  |             // Check if the queue is empty.
 | 
					
						
							|  |  |  |             if (read_index == m_write_index.load(std::memory_order::acquire)) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else if constexpr (Mode == PopMode::Wait) { | 
					
						
							|  |  |  |             // Wait until the queue is not empty.
 | 
					
						
							|  |  |  |             std::unique_lock lock{consumer_cv_mutex}; | 
					
						
							|  |  |  |             consumer_cv.wait(lock, [this, read_index] { | 
					
						
							|  |  |  |                 return read_index != m_write_index.load(std::memory_order::acquire); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         } else if constexpr (Mode == PopMode::WaitWithStopToken) { | 
					
						
							|  |  |  |             // Wait until the queue is not empty.
 | 
					
						
							|  |  |  |             std::unique_lock lock{consumer_cv_mutex}; | 
					
						
							|  |  |  |             Common::CondvarWait(consumer_cv, lock, stop_token, [this, read_index] { | 
					
						
							|  |  |  |                 return read_index != m_write_index.load(std::memory_order::acquire); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             if (stop_token.stop_requested()) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             static_assert(Mode < PopMode::Count, "Invalid PopMode."); | 
					
						
							| 
									
										
										
										
											2022-06-14 08:57:19 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         // Determine the position to read from.
 | 
					
						
							|  |  |  |         const size_t pos = read_index % Capacity; | 
					
						
							| 
									
										
										
										
											2022-06-14 08:57:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         // Pop the data off the queue, moving it.
 | 
					
						
							|  |  |  |         t = std::move(m_data[pos]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Increment the read index.
 | 
					
						
							|  |  |  |         ++m_read_index; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:48:01 -04:00
										 |  |  |         // Notify the producer that we have popped off the queue.
 | 
					
						
							|  |  |  |         std::scoped_lock lock{producer_cv_mutex}; | 
					
						
							|  |  |  |         producer_cv.notify_one(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     alignas(128) std::atomic_size_t m_read_index{0}; | 
					
						
							|  |  |  |     alignas(128) std::atomic_size_t m_write_index{0}; | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     std::array<T, Capacity> m_data; | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 14:48:01 -04:00
										 |  |  |     std::condition_variable_any producer_cv; | 
					
						
							|  |  |  |     std::mutex producer_cv_mutex; | 
					
						
							|  |  |  |     std::condition_variable_any consumer_cv; | 
					
						
							|  |  |  |     std::mutex consumer_cv_mutex; | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename T, size_t Capacity = detail::DefaultCapacity> | 
					
						
							|  |  |  | class MPSCQueue { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  |     template <typename... Args> | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     bool TryEmplace(Args&&... args) { | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  |         std::scoped_lock lock{write_mutex}; | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |         return spsc_queue.TryEmplace(std::forward<Args>(args)...); | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     template <typename... Args> | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     void EmplaceWait(Args&&... args) { | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         std::scoped_lock lock{write_mutex}; | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |         spsc_queue.EmplaceWait(std::forward<Args>(args)...); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool TryPop(T& t) { | 
					
						
							|  |  |  |         return spsc_queue.TryPop(t); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     void PopWait(T& t) { | 
					
						
							|  |  |  |         spsc_queue.PopWait(t); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     void PopWait(T& t, std::stop_token stop_token) { | 
					
						
							|  |  |  |         spsc_queue.PopWait(t, stop_token); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     T PopWait() { | 
					
						
							|  |  |  |         return spsc_queue.PopWait(); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     T PopWait(std::stop_token stop_token) { | 
					
						
							|  |  |  |         return spsc_queue.PopWait(stop_token); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     SPSCQueue<T, Capacity> spsc_queue; | 
					
						
							|  |  |  |     std::mutex write_mutex; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename T, size_t Capacity = detail::DefaultCapacity> | 
					
						
							|  |  |  | class MPMCQueue { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  |     template <typename... Args> | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     bool TryEmplace(Args&&... args) { | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  |         std::scoped_lock lock{write_mutex}; | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |         return spsc_queue.TryEmplace(std::forward<Args>(args)...); | 
					
						
							| 
									
										
										
										
											2023-03-19 04:01:47 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     template <typename... Args> | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |     void EmplaceWait(Args&&... args) { | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         std::scoped_lock lock{write_mutex}; | 
					
						
							| 
									
										
										
										
											2023-03-19 14:24:18 -04:00
										 |  |  |         spsc_queue.EmplaceWait(std::forward<Args>(args)...); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-14 08:57:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     bool TryPop(T& t) { | 
					
						
							|  |  |  |         std::scoped_lock lock{read_mutex}; | 
					
						
							|  |  |  |         return spsc_queue.TryPop(t); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     void PopWait(T& t) { | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         std::scoped_lock lock{read_mutex}; | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         spsc_queue.PopWait(t); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     void PopWait(T& t, std::stop_token stop_token) { | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         std::scoped_lock lock{read_mutex}; | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         spsc_queue.PopWait(t, stop_token); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     T PopWait() { | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         std::scoped_lock lock{read_mutex}; | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         return spsc_queue.PopWait(); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |     T PopWait(std::stop_token stop_token) { | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |         std::scoped_lock lock{read_mutex}; | 
					
						
							| 
									
										
										
										
											2023-03-19 15:17:21 -04:00
										 |  |  |         return spsc_queue.PopWait(stop_token); | 
					
						
							| 
									
										
										
										
											2023-03-19 03:19:25 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     SPSCQueue<T, Capacity> spsc_queue; | 
					
						
							|  |  |  |     std::mutex write_mutex; | 
					
						
							|  |  |  |     std::mutex read_mutex; | 
					
						
							| 
									
										
										
										
											2022-06-02 01:08:18 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-14 08:57:19 -04:00
										 |  |  | } // namespace Common
 |