mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
* Optimizations 1) Some headers simplified for better compilation time 2) Some templates simplified for smaller executable size 3) Eliminate std::future to fix compilation for mingw64 4) PKG installation can be cancelled now 5) cellGame fixes 6) XAudio2 fix for mingw64 7) PPUInterpreter bug fixed (Clang) * any_pod<> implemented Aliases: any16, any32, any64 rsx::make_command fixed
159 lines
2.8 KiB
C++
159 lines
2.8 KiB
C++
#include "stdafx.h"
|
|
#include "Emu/System.h"
|
|
#include "vm.h"
|
|
#include "wait_engine.h"
|
|
|
|
#include "Utilities/Thread.h"
|
|
#include "Utilities/SharedMutex.h"
|
|
|
|
extern std::condition_variable& get_current_thread_cv();
|
|
extern std::mutex& get_current_thread_mutex();
|
|
|
|
namespace vm
|
|
{
|
|
static shared_mutex s_mutex;
|
|
|
|
static std::unordered_set<waiter*> s_waiters(256);
|
|
|
|
bool waiter::try_notify()
|
|
{
|
|
{
|
|
std::lock_guard<mutex_t> lock(*mutex);
|
|
|
|
try
|
|
{
|
|
// Test predicate
|
|
if (!pred || !pred())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Clear predicate
|
|
pred = nullptr;
|
|
}
|
|
catch (...)
|
|
{
|
|
// Capture any exception possibly thrown by predicate
|
|
pred = [exception = std::current_exception()]() -> bool
|
|
{
|
|
// New predicate will throw the captured exception from the original thread
|
|
std::rethrow_exception(exception);
|
|
};
|
|
}
|
|
|
|
// Set addr and mask to invalid values to prevent further polling
|
|
addr = 0;
|
|
mask = ~0;
|
|
}
|
|
|
|
// Signal thread
|
|
cond->notify_one();
|
|
return true;
|
|
}
|
|
|
|
waiter::~waiter()
|
|
{
|
|
}
|
|
|
|
waiter_lock::waiter_lock(u32 addr, u32 size)
|
|
: m_lock(get_current_thread_mutex(), std::defer_lock)
|
|
{
|
|
Expects(addr && (size & (~size + 1)) == size && (addr & (size - 1)) == 0);
|
|
|
|
m_waiter.mutex = m_lock.mutex();
|
|
m_waiter.cond = &get_current_thread_cv();
|
|
m_waiter.addr = addr;
|
|
m_waiter.mask = ~(size - 1);
|
|
|
|
{
|
|
writer_lock lock(s_mutex);
|
|
|
|
s_waiters.emplace(&m_waiter);
|
|
}
|
|
|
|
m_lock.lock();
|
|
}
|
|
|
|
void waiter_lock::wait()
|
|
{
|
|
// If another thread successfully called pred(), it must be set to null
|
|
while (m_waiter.pred)
|
|
{
|
|
// If pred() called by another thread threw an exception, it'll be rethrown
|
|
if (m_waiter.pred())
|
|
{
|
|
return;
|
|
}
|
|
|
|
CHECK_EMU_STATUS;
|
|
|
|
m_waiter.cond->wait(m_lock);
|
|
}
|
|
}
|
|
|
|
waiter_lock::~waiter_lock()
|
|
{
|
|
if (m_lock) m_lock.unlock();
|
|
|
|
writer_lock lock(s_mutex);
|
|
|
|
s_waiters.erase(&m_waiter);
|
|
}
|
|
|
|
void notify_at(u32 addr, u32 size)
|
|
{
|
|
reader_lock lock(s_mutex);
|
|
|
|
for (const auto _w : s_waiters)
|
|
{
|
|
// Check address range overlapping using masks generated from size (power of 2)
|
|
if (((_w->addr ^ addr) & (_w->mask & ~(size - 1))) == 0)
|
|
{
|
|
_w->try_notify();
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool notify_all()
|
|
{
|
|
reader_lock lock(s_mutex);
|
|
|
|
std::size_t waiters = 0;
|
|
std::size_t signaled = 0;
|
|
|
|
for (const auto _w : s_waiters)
|
|
{
|
|
if (_w->addr)
|
|
{
|
|
waiters++;
|
|
|
|
if (_w->try_notify())
|
|
{
|
|
signaled++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// return true if waiter list is empty or all available waiters were signaled
|
|
return waiters == signaled;
|
|
}
|
|
|
|
void start()
|
|
{
|
|
// start notification thread
|
|
thread_ctrl::spawn("vm::start thread", []()
|
|
{
|
|
while (!Emu.IsStopped())
|
|
{
|
|
// poll waiters periodically (TODO)
|
|
while (!notify_all() && !Emu.IsPaused())
|
|
{
|
|
std::this_thread::yield();
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
}
|
|
});
|
|
}
|
|
}
|