2015-05-24 06:32:32 +02:00
|
|
|
// Copyright 2010 Dolphin Emulator Project
|
2021-07-05 03:22:19 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2015-05-24 06:32:32 +02:00
|
|
|
|
2014-02-10 13:54:46 -05:00
|
|
|
#pragma once
|
2010-07-29 10:21:48 +00:00
|
|
|
|
|
|
|
// a simple lockless thread-safe,
|
2017-08-23 16:45:42 -07:00
|
|
|
// single producer, single consumer queue
|
2010-07-29 10:21:48 +00:00
|
|
|
|
2015-05-25 18:52:04 -04:00
|
|
|
#include <atomic>
|
2025-03-12 03:21:44 -05:00
|
|
|
#include <cassert>
|
|
|
|
#include <memory>
|
2010-08-10 04:12:32 +00:00
|
|
|
|
2010-07-29 10:21:48 +00:00
|
|
|
namespace Common
|
|
|
|
{
|
2025-03-12 03:21:44 -05:00
|
|
|
|
|
|
|
namespace detail
|
|
|
|
{
|
|
|
|
template <typename T, bool IncludeWaitFunctionality>
|
|
|
|
class SPSCQueueBase final
|
2010-07-29 10:21:48 +00:00
|
|
|
{
|
|
|
|
public:
|
2025-03-12 03:21:44 -05:00
|
|
|
SPSCQueueBase() = default;
|
|
|
|
~SPSCQueueBase()
|
2010-07-29 10:21:48 +00:00
|
|
|
{
|
2025-03-12 03:21:44 -05:00
|
|
|
Clear();
|
2010-07-29 10:21:48 +00:00
|
|
|
delete m_read_ptr;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2025-03-12 03:21:44 -05:00
|
|
|
SPSCQueueBase(const SPSCQueueBase&) = delete;
|
|
|
|
SPSCQueueBase& operator=(const SPSCQueueBase&) = delete;
|
|
|
|
|
|
|
|
std::size_t Size() const { return m_size.load(std::memory_order_acquire); }
|
|
|
|
bool Empty() const { return Size() == 0; }
|
|
|
|
|
|
|
|
// The following are only safe from the "producer thread":
|
|
|
|
void Push(const T& arg) { Emplace(arg); }
|
|
|
|
void Push(T&& arg) { Emplace(std::move(arg)); }
|
|
|
|
template <typename... Args>
|
|
|
|
void Emplace(Args&&... args)
|
2010-07-29 10:21:48 +00:00
|
|
|
{
|
2025-03-12 03:21:44 -05:00
|
|
|
std::construct_at(&m_write_ptr->value.data, std::forward<Args>(args)...);
|
|
|
|
|
|
|
|
Node* const new_ptr = new Node;
|
|
|
|
m_write_ptr->next = new_ptr;
|
|
|
|
m_write_ptr = new_ptr;
|
|
|
|
|
|
|
|
AdjustSize(1);
|
2010-08-10 04:12:32 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2025-03-12 03:21:44 -05:00
|
|
|
void WaitForEmpty() requires(IncludeWaitFunctionality)
|
2010-07-29 10:21:48 +00:00
|
|
|
{
|
2025-03-12 03:21:44 -05:00
|
|
|
while (const std::size_t old_size = Size())
|
|
|
|
m_size.wait(old_size, std::memory_order_acquire);
|
2010-07-29 10:21:48 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2025-03-12 03:21:44 -05:00
|
|
|
// The following are only safe from the "consumer thread":
|
|
|
|
T& Front() { return m_read_ptr->value.data; }
|
|
|
|
const T& Front() const { return m_read_ptr->value.data; }
|
|
|
|
|
2010-08-10 04:12:32 +00:00
|
|
|
void Pop()
|
2010-07-29 10:21:48 +00:00
|
|
|
{
|
2025-03-12 03:21:44 -05:00
|
|
|
assert(!Empty());
|
|
|
|
|
|
|
|
std::destroy_at(&Front());
|
|
|
|
|
|
|
|
Node* const old_node = m_read_ptr;
|
|
|
|
m_read_ptr = old_node->next;
|
|
|
|
delete old_node;
|
|
|
|
|
|
|
|
AdjustSize(-1);
|
2010-07-29 10:21:48 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2025-03-12 03:21:44 -05:00
|
|
|
bool Pop(T& result)
|
2010-07-29 10:21:48 +00:00
|
|
|
{
|
2010-08-10 04:12:32 +00:00
|
|
|
if (Empty())
|
2010-07-29 10:21:48 +00:00
|
|
|
return false;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2025-03-12 03:21:44 -05:00
|
|
|
result = std::move(Front());
|
|
|
|
Pop();
|
2010-07-29 10:21:48 +00:00
|
|
|
return true;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2025-03-12 03:21:44 -05:00
|
|
|
void WaitForData() requires(IncludeWaitFunctionality)
|
|
|
|
{
|
|
|
|
m_size.wait(0, std::memory_order_acquire);
|
|
|
|
}
|
|
|
|
|
2010-08-10 04:12:32 +00:00
|
|
|
void Clear()
|
|
|
|
{
|
2025-03-12 03:21:44 -05:00
|
|
|
while (!Empty())
|
|
|
|
Pop();
|
2010-08-10 04:12:32 +00:00
|
|
|
}
|
|
|
|
|
2010-07-29 10:21:48 +00:00
|
|
|
private:
|
2025-03-12 03:21:44 -05:00
|
|
|
struct Node
|
2010-07-29 10:21:48 +00:00
|
|
|
{
|
2025-03-12 03:21:44 -05:00
|
|
|
// union allows value construction to be deferred until Push.
|
|
|
|
union Value
|
2010-07-29 10:21:48 +00:00
|
|
|
{
|
2025-03-12 03:21:44 -05:00
|
|
|
T data;
|
|
|
|
Value() {}
|
|
|
|
~Value() {}
|
|
|
|
} value;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2025-03-12 03:21:44 -05:00
|
|
|
Node* next;
|
2010-07-29 10:21:48 +00:00
|
|
|
};
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2025-03-12 03:21:44 -05:00
|
|
|
Node* m_write_ptr = new Node;
|
|
|
|
Node* m_read_ptr = m_write_ptr;
|
|
|
|
|
|
|
|
void AdjustSize(std::size_t value)
|
|
|
|
{
|
|
|
|
m_size.fetch_add(value, std::memory_order_release);
|
|
|
|
if constexpr (IncludeWaitFunctionality)
|
|
|
|
m_size.notify_one();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::atomic<std::size_t> m_size = 0;
|
2010-07-29 10:21:48 +00:00
|
|
|
};
|
2025-03-12 03:21:44 -05:00
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
using SPSCQueue = detail::SPSCQueueBase<T, false>;
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
using WaitableSPSCQueue = detail::SPSCQueueBase<T, true>;
|
|
|
|
|
2019-05-05 23:48:12 +00:00
|
|
|
} // namespace Common
|