mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 12:37:59 -10:00
Every std::mutex, lock_guard, unique_lock, scoped_lock, condition_variable, call_once and semaphore left in Source/Core is replaced by a lock-free design: - Input: the device list, control bindings, stick calibration and default device are published snapshots read under a quiesce; a rebind requested while another thread is rebinding is handed to that thread instead of waited for. - WiimoteReal: the pool thread owns the slots and runs commands from an MPSC queue; the active Wiimotes are atomic slots. - EXI/SI/IOS devices: socket and packet traffic goes through SPSC rings or MPSC handoffs owned by one thread; USB scanning publishes its device list, and libusb transfers drain through a CallGate. - Memory cards: the flush thread applies change records queued by the CPU thread. - Custom assets: loads are scheduled as a published batch with an atomic cursor, results come back over an MPSC stack, and busy assets are skipped and retried. - D3D state and GL program caches use insert-only atomic maps (Common/AtomicHashMap.h). - Logging appends with O_APPEND / FILE_APPEND_DATA; FatFs reports busy instead of waiting; per-socket WSAStartup/WSACleanup. Deleted as unused by the libretro core: the profiler, the FIFO recorder, the volume verifier, the touch InputOverrider and the GameCube adapter's libusb path.
174 lines
4.3 KiB
C++
174 lines
4.3 KiB
C++
// Copyright 2016 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "InputCommon/ControlReference/ControlReference.h"
|
|
|
|
#include <cstring>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
|
|
using namespace ciface::ExpressionParser;
|
|
|
|
static thread_local bool tls_input_gate = true;
|
|
|
|
void ControlReference::SetInputGate(bool enable)
|
|
{
|
|
tls_input_gate = enable;
|
|
}
|
|
|
|
bool ControlReference::GetInputGate()
|
|
{
|
|
return tls_input_gate;
|
|
}
|
|
|
|
//
|
|
// UpdateReference
|
|
//
|
|
// Updates a controlreference's binded devices/controls
|
|
// need to call this to re-bind a control reference after changing its expression
|
|
//
|
|
void ControlReference::FreeBinding(quiesce_node_t* node)
|
|
{
|
|
delete reinterpret_cast<Binding*>(node);
|
|
}
|
|
|
|
void ControlReference::Publish(Binding* binding)
|
|
{
|
|
retro_atomic_store_release_int(&m_has_expression, !binding->expression.empty());
|
|
if (void* old = retro_atomic_exchange_ptr(&m_binding, binding))
|
|
quiesce_retire(ciface::InputQuiesce(), &static_cast<Binding*>(old)->node, FreeBinding);
|
|
}
|
|
|
|
void ControlReference::UpdateReference(ciface::ExpressionParser::ControlEnvironment& env)
|
|
{
|
|
ciface::InputReadScope scope;
|
|
for (;;)
|
|
{
|
|
Binding* const cur = PeekBinding();
|
|
if (!cur || !cur->parsed)
|
|
return;
|
|
|
|
// A fresh parse to bind, leaving the published one untouched for its readers.
|
|
auto parse_result = ParseExpression(cur->expression);
|
|
auto* next = new Binding{{}, cur->expression, parse_result.status, std::move(parse_result.expr)};
|
|
if (next->parsed)
|
|
next->parsed->UpdateReferences(env);
|
|
|
|
// Lost to a SetExpression or another rebind meanwhile: bind what is there now.
|
|
if (retro_atomic_cas_ptr(&m_binding, cur, next))
|
|
{
|
|
quiesce_retire(ciface::InputQuiesce(), &cur->node, FreeBinding);
|
|
return;
|
|
}
|
|
delete next;
|
|
}
|
|
}
|
|
|
|
int ControlReference::BoundCount() const
|
|
{
|
|
ciface::InputReadScope scope;
|
|
const Binding* const b = PeekBinding();
|
|
return b && b->parsed ? b->parsed->CountNumControls() : 0;
|
|
}
|
|
|
|
ParseStatus ControlReference::GetParseStatus() const
|
|
{
|
|
ciface::InputReadScope scope;
|
|
const Binding* const b = PeekBinding();
|
|
return b ? b->status : ParseStatus::EmptyExpression;
|
|
}
|
|
|
|
std::string ControlReference::GetExpression() const
|
|
{
|
|
ciface::InputReadScope scope;
|
|
const Binding* const b = PeekBinding();
|
|
return b ? b->expression : std::string{};
|
|
}
|
|
|
|
bool ControlReference::HasExpression() const
|
|
{
|
|
return retro_atomic_load_acquire_int(&m_has_expression) != 0;
|
|
}
|
|
|
|
std::optional<std::string> ControlReference::SetExpression(std::string expr)
|
|
{
|
|
auto parse_result = ParseExpression(expr);
|
|
Publish(new Binding{{}, std::move(expr), parse_result.status, std::move(parse_result.expr)});
|
|
return parse_result.description;
|
|
}
|
|
|
|
ControlState ControlReference::GetRange() const
|
|
{
|
|
const u64 bits = static_cast<u64>(retro_atomic_load_acquire_64(&m_range_bits));
|
|
ControlState range;
|
|
std::memcpy(&range, &bits, sizeof(range));
|
|
return range;
|
|
}
|
|
|
|
void ControlReference::SetRange(ControlState range)
|
|
{
|
|
u64 bits;
|
|
std::memcpy(&bits, &range, sizeof(bits));
|
|
retro_atomic_store_release_64(&m_range_bits, static_cast<int64_t>(bits));
|
|
}
|
|
|
|
ControlReference::ControlReference()
|
|
{
|
|
SetRange(1.0);
|
|
}
|
|
|
|
ControlReference::~ControlReference()
|
|
{
|
|
delete static_cast<Binding*>(retro_atomic_exchange_ptr(&m_binding, nullptr));
|
|
}
|
|
|
|
|
|
InputReference::InputReference() : ControlReference()
|
|
{
|
|
}
|
|
|
|
OutputReference::OutputReference() : ControlReference()
|
|
{
|
|
}
|
|
|
|
bool InputReference::IsInput() const
|
|
{
|
|
return true;
|
|
}
|
|
bool OutputReference::IsInput() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// InputReference :: State
|
|
//
|
|
// Gets the state of an input reference
|
|
// override function for ControlReference::State ...
|
|
//
|
|
ControlState InputReference::State(const ControlState ignore)
|
|
{
|
|
if (!GetInputGate())
|
|
return 0.0;
|
|
ciface::InputReadScope scope;
|
|
const Binding* const b = PeekBinding();
|
|
return b && b->parsed ? b->parsed->GetValue() * GetRange() : 0.0;
|
|
}
|
|
|
|
//
|
|
// OutputReference :: State
|
|
//
|
|
// Set the state of all binded outputs
|
|
// overrides ControlReference::State .. combined them so I could make the GUI simple / inputs ==
|
|
// same as outputs one list
|
|
// I was lazy and it works so watever
|
|
//
|
|
ControlState OutputReference::State(const ControlState state)
|
|
{
|
|
ciface::InputReadScope scope;
|
|
const Binding* const b = PeekBinding();
|
|
if (b && b->parsed)
|
|
b->parsed->SetValue(state * GetRange());
|
|
return 0.0;
|
|
}
|