rpcsx/rpcs3/Emu/Memory/wait_engine.cpp

130 lines
2.3 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Emu/System.h"
#include "vm.h"
#include "wait_engine.h"
#include "Utilities/Thread.h"
#include "Utilities/mutex.h"
2016-05-13 16:01:48 +02:00
#include <unordered_set>
namespace vm
{
static shared_mutex s_mutex;
2016-05-13 16:01:48 +02:00
static std::unordered_set<waiter_base*, pointer_hash<waiter_base>> s_waiters(256);
2016-05-13 16:01:48 +02:00
void waiter_base::initialize(u32 addr, u32 size)
{
2016-08-15 15:29:38 +02:00
verify(HERE), addr, (size & (~size + 1)) == size, (addr & (size - 1)) == 0;
2016-05-13 16:01:48 +02:00
this->addr = addr;
this->mask = ~(size - 1);
this->thread = thread_ctrl::get_current();
2016-05-24 00:59:39 +02:00
struct waiter final
2016-05-13 16:01:48 +02:00
{
2016-05-24 00:59:39 +02:00
waiter_base* m_ptr;
thread_ctrl* m_thread;
waiter(waiter_base* ptr)
: m_ptr(ptr)
, m_thread(ptr->thread)
{
// Initialize waiter
writer_lock lock(s_mutex);
s_waiters.emplace(m_ptr);
2016-05-24 00:59:39 +02:00
}
~waiter()
{
// Reset thread
m_ptr->thread = nullptr;
2016-05-24 00:59:39 +02:00
// Remove waiter
writer_lock lock(s_mutex);
s_waiters.erase(m_ptr);
2016-05-24 00:59:39 +02:00
}
};
2016-05-13 16:01:48 +02:00
// Wait until thread == nullptr
2016-08-15 16:11:45 +02:00
waiter{this}, thread_ctrl::wait([&] { return !thread || test(); });
}
2016-05-13 16:01:48 +02:00
bool waiter_base::try_notify()
{
const auto _t = thread.load();
2016-05-13 16:01:48 +02:00
try
{
2016-05-13 16:01:48 +02:00
// Test predicate
if (UNLIKELY(!_t || !test()))
{
2016-05-13 16:01:48 +02:00
return false;
}
}
2016-05-13 16:01:48 +02:00
catch (...)
{
// Capture any exception thrown by the predicate
_t->set_exception(std::current_exception());
}
// Signal the thread with nullptr
if (auto _t = thread.exchange(nullptr))
{
_t->notify();
}
2016-05-13 16:01:48 +02:00
return true;
}
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();
}
}
}
2016-05-13 16:01:48 +02:00
// Return amount of threads which are not notified
static std::size_t notify_all()
{
reader_lock lock(s_mutex);
std::size_t signaled = 0;
for (const auto _w : s_waiters)
{
2016-05-13 16:01:48 +02:00
if (_w->try_notify())
{
2016-05-13 16:01:48 +02:00
signaled++;
}
}
2016-05-13 16:01:48 +02:00
return s_waiters.size() - signaled;
}
void start()
{
2016-05-13 16:01:48 +02:00
thread_ctrl::spawn("vm::wait", []()
{
while (!Emu.IsStopped())
{
2016-05-13 16:01:48 +02:00
// Poll waiters periodically (TODO)
2016-05-24 00:59:39 +02:00
while (notify_all() && !Emu.IsPaused() && !Emu.IsStopped())
{
thread_ctrl::wait_for(50);
}
thread_ctrl::wait_for(1000);
}
});
}
}