Compare commits

...

6 Commits

Author SHA1 Message Date
Levi Behunin
a9de7a738f Forgot warning push 2021-10-23 02:27:10 -06:00
Levi Behunin
f7f1078e6d nit 2021-10-22 14:21:32 -06:00
Levi Behunin
d7d73fadb0 The real fix to 4324 -_- 2021-10-22 13:57:00 -06:00
Levi Behunin
a53343476c More pre c++20 removal and correcly disable 4324 2021-10-22 13:10:23 -06:00
Levi Behunin
c8f0fc00b0 Morph suggestion
Remove code used for pre c++20
2021-10-21 23:56:23 -06:00
Levi Behunin
564ecb71ec Move to MPMCQueue
from: https://github.com/rigtorp/MPMCQueue
2021-10-21 18:33:58 -06:00
14 changed files with 287 additions and 51 deletions

View File

@@ -0,0 +1,240 @@
/*
Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4324)
#endif
#include <atomic>
#include <cassert>
#include <cstddef> // offsetof
#include <memory>
#include <new>
#include <stdexcept>
namespace Common {
namespace mpmc {
static constexpr size_t hardwareInterferenceSize = 64;
template <typename T>
using AlignedAllocator = std::allocator<T>;
template <typename T>
struct Slot {
~Slot() noexcept {
if (turn & 1) {
destroy();
}
}
template <typename... Args>
void construct(Args&&... args) noexcept {
static_assert(std::is_nothrow_constructible<T, Args&&...>::value,
"T must be nothrow constructible with Args&&...");
new (&storage) T(std::forward<Args>(args)...);
}
void destroy() noexcept {
static_assert(std::is_nothrow_destructible<T>::value, "T must be nothrow destructible");
reinterpret_cast<T*>(&storage)->~T();
}
T&& move() noexcept {
return reinterpret_cast<T&&>(storage);
}
// Align to avoid false sharing between adjacent slots
alignas(hardwareInterferenceSize) std::atomic<size_t> turn = {0};
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage;
};
template <typename T, typename Allocator = AlignedAllocator<Slot<T>>>
class Queue {
private:
static_assert(std::is_nothrow_copy_assignable<T>::value ||
std::is_nothrow_move_assignable<T>::value,
"T must be nothrow copy or move assignable");
static_assert(std::is_nothrow_destructible<T>::value, "T must be nothrow destructible");
public:
explicit Queue(const size_t capacity, const Allocator& allocator = Allocator())
: capacity_(capacity), allocator_(allocator), head_(0), tail_(0) {
if (capacity_ < 1) {
throw std::invalid_argument("capacity < 1");
}
// Allocate one extra slot to prevent false sharing on the last slot
slots_ = allocator_.allocate(capacity_ + 1);
// Allocators are not required to honor alignment for over-aligned types
// (see http://eel.is/c++draft/allocator.requirements#10) so we verify
// alignment here
if (reinterpret_cast<size_t>(slots_) % alignof(Slot<T>) != 0) {
allocator_.deallocate(slots_, capacity_ + 1);
throw std::bad_alloc();
}
for (size_t i = 0; i < capacity_; ++i) {
new (&slots_[i]) Slot<T>();
}
static_assert(alignof(Slot<T>) == hardwareInterferenceSize,
"Slot must be aligned to cache line boundary to prevent false sharing");
static_assert(sizeof(Slot<T>) % hardwareInterferenceSize == 0,
"Slot size must be a multiple of cache line size to prevent "
"false sharing between adjacent slots");
static_assert(sizeof(Queue) % hardwareInterferenceSize == 0,
"Queue size must be a multiple of cache line size to "
"prevent false sharing between adjacent queues");
static_assert(offsetof(Queue, tail_) - offsetof(Queue, head_) ==
static_cast<std::ptrdiff_t>(hardwareInterferenceSize),
"head and tail must be a cache line apart to prevent false sharing");
}
~Queue() noexcept {
for (size_t i = 0; i < capacity_; ++i) {
slots_[i].~Slot();
}
allocator_.deallocate(slots_, capacity_ + 1);
}
// non-copyable and non-movable
Queue(const Queue&) = delete;
Queue& operator=(const Queue&) = delete;
template <typename... Args>
void emplace(Args&&... args) noexcept {
static_assert(std::is_nothrow_constructible<T, Args&&...>::value,
"T must be nothrow constructible with Args&&...");
auto const head = head_.fetch_add(1);
auto& slot = slots_[idx(head)];
while (turn(head) * 2 != slot.turn.load(std::memory_order_acquire))
;
slot.construct(std::forward<Args>(args)...);
slot.turn.store(turn(head) * 2 + 1, std::memory_order_release);
}
template <typename... Args>
bool try_emplace(Args&&... args) noexcept {
static_assert(std::is_nothrow_constructible<T, Args&&...>::value,
"T must be nothrow constructible with Args&&...");
auto head = head_.load(std::memory_order_acquire);
for (;;) {
auto& slot = slots_[idx(head)];
if (turn(head) * 2 == slot.turn.load(std::memory_order_acquire)) {
if (head_.compare_exchange_strong(head, head + 1)) {
slot.construct(std::forward<Args>(args)...);
slot.turn.store(turn(head) * 2 + 1, std::memory_order_release);
return true;
}
} else {
auto const prevHead = head;
head = head_.load(std::memory_order_acquire);
if (head == prevHead) {
return false;
}
}
}
}
void push(const T& v) noexcept {
static_assert(std::is_nothrow_copy_constructible<T>::value,
"T must be nothrow copy constructible");
emplace(v);
}
template <typename P, typename = typename std::enable_if<
std::is_nothrow_constructible<T, P&&>::value>::type>
void push(P&& v) noexcept {
emplace(std::forward<P>(v));
}
bool try_push(const T& v) noexcept {
static_assert(std::is_nothrow_copy_constructible<T>::value,
"T must be nothrow copy constructible");
return try_emplace(v);
}
template <typename P, typename = typename std::enable_if<
std::is_nothrow_constructible<T, P&&>::value>::type>
bool try_push(P&& v) noexcept {
return try_emplace(std::forward<P>(v));
}
void pop(T& v) noexcept {
auto const tail = tail_.fetch_add(1);
auto& slot = slots_[idx(tail)];
while (turn(tail) * 2 + 1 != slot.turn.load(std::memory_order_acquire))
;
v = slot.move();
slot.destroy();
slot.turn.store(turn(tail) * 2 + 2, std::memory_order_release);
}
bool try_pop(T& v) noexcept {
auto tail = tail_.load(std::memory_order_acquire);
for (;;) {
auto& slot = slots_[idx(tail)];
if (turn(tail) * 2 + 1 == slot.turn.load(std::memory_order_acquire)) {
if (tail_.compare_exchange_strong(tail, tail + 1)) {
v = slot.move();
slot.destroy();
slot.turn.store(turn(tail) * 2 + 2, std::memory_order_release);
return true;
}
} else {
auto const prevTail = tail;
tail = tail_.load(std::memory_order_acquire);
if (tail == prevTail) {
return false;
}
}
}
}
private:
constexpr size_t idx(size_t i) const noexcept {
return i % capacity_;
}
constexpr size_t turn(size_t i) const noexcept {
return i / capacity_;
}
private:
const size_t capacity_;
Slot<T>* slots_;
[[no_unique_address]] Allocator allocator_;
// Align to avoid false sharing between head_ and tail_
alignas(hardwareInterferenceSize) std::atomic<size_t> head_;
alignas(hardwareInterferenceSize) std::atomic<size_t> tail_;
};
} // namespace mpmc
template <typename T, typename Allocator = mpmc::AlignedAllocator<mpmc::Slot<T>>>
using MPMCQueue = mpmc::Queue<T, Allocator>;
} // namespace Common
#ifdef _MSC_VER
#pragma warning(pop)
#endif

View File

@@ -170,7 +170,7 @@ void Adapter::UpdateYuzuSettings(std::size_t port) {
if (pads[port].buttons != 0) {
pad_status.button = pads[port].last_button;
pad_queue.Push(pad_status);
pad_queue.push(pad_status);
}
// Accounting for a threshold here to ensure an intentional press
@@ -181,7 +181,7 @@ void Adapter::UpdateYuzuSettings(std::size_t port) {
pad_status.axis = static_cast<PadAxes>(i);
pad_status.axis_value = value;
pad_status.axis_threshold = axis_threshold;
pad_queue.Push(pad_status);
pad_queue.push(pad_status);
}
}
}
@@ -478,20 +478,18 @@ bool Adapter::DeviceConnected(std::size_t port) const {
}
void Adapter::BeginConfiguration() {
pad_queue.Clear();
configuring = true;
}
void Adapter::EndConfiguration() {
pad_queue.Clear();
configuring = false;
}
Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() {
Common::MPMCQueue<GCPadStatus>& Adapter::GetPadQueue() {
return pad_queue;
}
const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
const Common::MPMCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
return pad_queue;
}

View File

@@ -8,8 +8,8 @@
#include <mutex>
#include <thread>
#include <unordered_map>
#include "common/atomic_threadsafe_queue.h"
#include "common/common_types.h"
#include "common/threadsafe_queue.h"
#include "input_common/main.h"
struct libusb_context;
@@ -85,8 +85,8 @@ public:
void BeginConfiguration();
void EndConfiguration();
Common::SPSCQueue<GCPadStatus>& GetPadQueue();
const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
Common::MPMCQueue<GCPadStatus>& GetPadQueue();
const Common::MPMCQueue<GCPadStatus>& GetPadQueue() const;
GCController& GetPadState(std::size_t port);
const GCController& GetPadState(std::size_t port) const;
@@ -145,7 +145,7 @@ private:
libusb_device_handle* usb_adapter_handle = nullptr;
std::array<GCController, 4> pads;
Common::SPSCQueue<GCPadStatus> pad_queue;
Common::MPMCQueue<GCPadStatus> pad_queue{1024};
std::thread adapter_input_thread;
std::thread adapter_scan_thread;

View File

@@ -7,7 +7,7 @@
#include <mutex>
#include <utility>
#include "common/assert.h"
#include "common/threadsafe_queue.h"
#include "common/atomic_threadsafe_queue.h"
#include "input_common/gcadapter/gc_adapter.h"
#include "input_common/gcadapter/gc_poller.h"
@@ -103,7 +103,7 @@ Common::ParamPackage GCButtonFactory::GetNextInput() const {
Common::ParamPackage params;
GCAdapter::GCPadStatus pad;
auto& queue = adapter->GetPadQueue();
while (queue.Pop(pad)) {
while (queue.try_pop(pad)) {
// This while loop will break on the earliest detected button
params.Set("engine", "gcpad");
params.Set("port", static_cast<s32>(pad.port));
@@ -263,7 +263,7 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
GCAdapter::GCPadStatus pad;
Common::ParamPackage params;
auto& queue = adapter->GetPadQueue();
while (queue.Pop(pad)) {
while (queue.try_pop(pad)) {
if (pad.button != GCAdapter::PadButton::Undefined) {
params.Set("engine", "gcpad");
params.Set("port", static_cast<s32>(pad.port));

View File

@@ -52,7 +52,7 @@ void Mouse::UpdateYuzuSettings() {
return;
}
mouse_queue.Push(MouseStatus{
mouse_queue.push(MouseStatus{
.button = last_button,
});
}
@@ -153,7 +153,6 @@ void Mouse::ReleaseAllButtons() {
void Mouse::BeginConfiguration() {
buttons = 0;
last_button = MouseButton::Undefined;
mouse_queue.Clear();
configuring = true;
}
@@ -165,7 +164,6 @@ void Mouse::EndConfiguration() {
info.data.axis = {0, 0};
}
last_button = MouseButton::Undefined;
mouse_queue.Clear();
configuring = false;
}
@@ -205,11 +203,11 @@ bool Mouse::UnlockButton(std::size_t button_) {
return button_state;
}
Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
Common::MPMCQueue<MouseStatus>& Mouse::GetMouseQueue() {
return mouse_queue;
}
const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
const Common::MPMCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
return mouse_queue;
}

View File

@@ -9,8 +9,8 @@
#include <stop_token>
#include <thread>
#include "common/atomic_threadsafe_queue.h"
#include "common/common_types.h"
#include "common/threadsafe_queue.h"
#include "common/vector_math.h"
#include "core/frontend/input.h"
#include "input_common/motion_input.h"
@@ -79,8 +79,8 @@ public:
[[nodiscard]] bool ToggleButton(std::size_t button_);
[[nodiscard]] bool UnlockButton(std::size_t button_);
[[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
[[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
[[nodiscard]] Common::MPMCQueue<MouseStatus>& GetMouseQueue();
[[nodiscard]] const Common::MPMCQueue<MouseStatus>& GetMouseQueue() const;
[[nodiscard]] MouseData& GetMouseState(std::size_t button);
[[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
@@ -109,7 +109,7 @@ private:
std::jthread update_thread;
MouseButton last_button{MouseButton::Undefined};
std::array<MouseInfo, 7> mouse_info;
Common::SPSCQueue<MouseStatus> mouse_queue;
Common::MPMCQueue<MouseStatus> mouse_queue{1024};
bool configuring{false};
int mouse_panning_timout{};
};

View File

@@ -52,7 +52,7 @@ Common::ParamPackage MouseButtonFactory::GetNextInput() const {
MouseInput::MouseStatus pad;
Common::ParamPackage params;
auto& queue = mouse_input->GetMouseQueue();
while (queue.Pop(pad)) {
while (queue.try_pop(pad)) {
// This while loop will break on the earliest detected button
if (pad.button != MouseInput::MouseButton::Undefined) {
params.Set("engine", "mouse");
@@ -184,7 +184,7 @@ Common::ParamPackage MouseAnalogFactory::GetNextInput() const {
MouseInput::MouseStatus pad;
Common::ParamPackage params;
auto& queue = mouse_input->GetMouseQueue();
while (queue.Pop(pad)) {
while (queue.try_pop(pad)) {
// This while loop will break on the earliest detected button
if (pad.button != MouseInput::MouseButton::Undefined) {
params.Set("engine", "mouse");
@@ -227,7 +227,7 @@ Common::ParamPackage MouseMotionFactory::GetNextInput() const {
MouseInput::MouseStatus pad;
Common::ParamPackage params;
auto& queue = mouse_input->GetMouseQueue();
while (queue.Pop(pad)) {
while (queue.try_pop(pad)) {
// This while loop will break on the earliest detected button
if (pad.button != MouseInput::MouseButton::Undefined) {
params.Set("engine", "mouse");
@@ -275,7 +275,7 @@ Common::ParamPackage MouseTouchFactory::GetNextInput() const {
MouseInput::MouseStatus pad;
Common::ParamPackage params;
auto& queue = mouse_input->GetMouseQueue();
while (queue.Pop(pad)) {
while (queue.try_pop(pad)) {
// This while loop will break on the earliest detected button
if (pad.button != MouseInput::MouseButton::Undefined) {
params.Set("engine", "mouse");

View File

@@ -46,7 +46,7 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
// Don't handle the event if we are configuring
if (sdl_state->polling) {
sdl_state->event_queue.Push(*event);
sdl_state->event_queue.push(*event);
} else {
sdl_state->HandleGameControllerEvent(*event);
}
@@ -1460,7 +1460,6 @@ public:
explicit SDLPoller(SDLState& state_) : state(state_) {}
void Start([[maybe_unused]] const std::string& device_id) override {
state.event_queue.Clear();
state.polling = true;
}
@@ -1478,7 +1477,7 @@ public:
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
while (state.event_queue.try_pop(event)) {
const auto package = FromEvent(event);
if (package) {
return *package;
@@ -1550,7 +1549,7 @@ public:
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
while (state.event_queue.try_pop(event)) {
const auto package = FromEvent(event);
if (package) {
return *package;
@@ -1592,7 +1591,7 @@ public:
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
while (state.event_queue.try_pop(event)) {
if (event.type != SDL_JOYAXISMOTION) {
// Check for a button press
auto button_press = button_poller.FromEvent(event);

View File

@@ -12,8 +12,8 @@
#include <SDL.h>
#include "common/atomic_threadsafe_queue.h"
#include "common/common_types.h"
#include "common/threadsafe_queue.h"
#include "input_common/sdl/sdl.h"
union SDL_Event;
@@ -59,7 +59,7 @@ public:
/// Used by the Pollers during config
std::atomic<bool> polling = false;
Common::SPSCQueue<SDL_Event> event_queue;
Common::MPMCQueue<SDL_Event> event_queue{1024};
std::vector<Common::ParamPackage> GetInputDevices() override;

View File

@@ -338,7 +338,7 @@ void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index,
gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]);
}
UDPPadStatus pad{
.host = clients[client].host,
.host = clients[client].host.c_str(),
.port = clients[client].port,
.pad_index = pad_index,
};
@@ -346,12 +346,12 @@ void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index,
if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
pad.motion = static_cast<PadMotion>(i);
pad.motion_value = gyro[i];
pad_queue.Push(pad);
pad_queue.push(pad);
}
if (acc[i] > 1.75f || acc[i] < -1.75f) {
pad.motion = static_cast<PadMotion>(i + 3);
pad.motion_value = acc[i];
pad_queue.Push(pad);
pad_queue.push(pad);
}
}
}
@@ -401,12 +401,10 @@ void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client,
}
void Client::BeginConfiguration() {
pad_queue.Clear();
configuring = true;
}
void Client::EndConfiguration() {
pad_queue.Clear();
configuring = false;
}
@@ -434,11 +432,11 @@ const Input::TouchStatus& Client::GetTouchState() const {
return touch_status;
}
Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() {
Common::MPMCQueue<UDPPadStatus>& Client::GetPadQueue() {
return pad_queue;
}
const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
const Common::MPMCQueue<UDPPadStatus>& Client::GetPadQueue() const {
return pad_queue;
}

View File

@@ -11,10 +11,10 @@
#include <string>
#include <thread>
#include <tuple>
#include "common/atomic_threadsafe_queue.h"
#include "common/common_types.h"
#include "common/param_package.h"
#include "common/thread.h"
#include "common/threadsafe_queue.h"
#include "common/vector_math.h"
#include "core/frontend/input.h"
#include "input_common/motion_input.h"
@@ -46,7 +46,7 @@ enum class PadTouch {
};
struct UDPPadStatus {
std::string host{"127.0.0.1"};
const char* host{"127.0.0.1"};
u16 port{26760};
std::size_t pad_index{};
PadMotion motion{PadMotion::Undefined};
@@ -85,8 +85,8 @@ public:
bool DeviceConnected(std::size_t pad) const;
void ReloadSockets();
Common::SPSCQueue<UDPPadStatus>& GetPadQueue();
const Common::SPSCQueue<UDPPadStatus>& GetPadQueue() const;
Common::MPMCQueue<UDPPadStatus>& GetPadQueue();
const Common::MPMCQueue<UDPPadStatus>& GetPadQueue() const;
DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad);
const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const;
@@ -146,7 +146,7 @@ private:
static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2;
std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{};
std::array<ClientConnection, MAX_UDP_CLIENTS> clients{};
Common::SPSCQueue<UDPPadStatus> pad_queue{};
Common::MPMCQueue<UDPPadStatus> pad_queue{1024};
Input::TouchStatus touch_status{};
std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{};
};

View File

@@ -59,7 +59,7 @@ Common::ParamPackage UDPMotionFactory::GetNextInput() {
Common::ParamPackage params;
CemuhookUDP::UDPPadStatus pad;
auto& queue = client->GetPadQueue();
while (queue.Pop(pad)) {
while (queue.try_pop(pad)) {
if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
continue;
}

View File

@@ -32,7 +32,10 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
while (!stop_token.stop_requested()) {
CommandDataContainer next = state.queue.PopWait(stop_token);
CommandDataContainer next;
if (!state.queue.try_pop(next)) {
continue;
}
if (stop_token.stop_requested()) {
break;
}
@@ -119,7 +122,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
std::unique_lock lk(state.write_lock);
const u64 fence{++state.last_fence};
state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
state.queue.push(CommandDataContainer(std::move(command_data), fence, block));
if (block) {
state.cv.wait(lk, thread.get_stop_token(), [this, fence] {

View File

@@ -11,7 +11,7 @@
#include <thread>
#include <variant>
#include "common/threadsafe_queue.h"
#include "common/atomic_threadsafe_queue.h"
#include "video_core/framebuffer_config.h"
namespace Tegra {
@@ -97,9 +97,9 @@ struct CommandDataContainer {
/// Struct used to synchronize the GPU thread
struct SynchState final {
using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>;
using CommandQueue = Common::MPMCQueue<CommandDataContainer>;
std::mutex write_lock;
CommandQueue queue;
CommandQueue queue{100000};
u64 last_fence{};
std::atomic<u64> signaled_fence{};
std::condition_variable_any cv;