Files
decaf-emu/src/processor.cpp

242 lines
4.8 KiB
C++
Raw Normal View History

#include <algorithm>
#include "interpreter.h"
#include "log.h"
#include "processor.h"
#include "ppc.h"
#include "modules/coreinit/coreinit_thread.h"
#include "modules/coreinit/coreinit_scheduler.h"
Processor
gProcessor { 3 };
__declspec(thread) Core *
tCurrentCore = nullptr;
void
Fiber::fiberEntryPoint(void *param)
{
gProcessor.fiberEntryPoint(reinterpret_cast<Fiber*>(param));
}
Processor::Processor(size_t cores)
{
for (auto i = 0u; i < cores; ++i) {
mCores.push_back(new Core { i });
}
}
void
Processor::start()
{
mRunning = true;
for (auto core : mCores) {
core->thread = std::thread(std::bind(&Processor::coreEntryPoint, this, core));
}
}
void
Processor::join()
{
for (auto core : mCores) {
core->thread.join();
}
}
Fiber *
Processor::peekNextFiber(uint32_t core)
{
auto bit = 1 << core;
for (auto fiber : mFiberQueue) {
if (fiber->thread->state != OSThreadState::Ready) {
continue;
}
if (fiber->thread->suspendCounter > 0) {
continue;
}
if (fiber->thread->attr & bit) {
return fiber;
}
}
return nullptr;
}
void
Processor::coreEntryPoint(Core *core)
{
tCurrentCore = core;
core->primaryFiber = ConvertThreadToFiber(NULL);
while (mRunning) {
std::unique_lock<std::mutex> lock(mMutex);
if (auto fiber = peekNextFiber(core->id)) {
// Remove fiber from schedule queue
mFiberQueue.erase(std::remove(mFiberQueue.begin(), mFiberQueue.end(), fiber), mFiberQueue.end());
// Switch to fiber
core->currentFiber = fiber;
fiber->coreID = core->id;
fiber->parentFiber = core->primaryFiber;
fiber->thread->state = OSThreadState::Running;
lock.unlock();
gLog->trace("Core {} enter thread {}", core->id, fiber->thread->id);
SwitchToFiber(fiber->handle);
} else {
// Wait for a valid fiber
gLog->trace("Core {} wait for thread", core->id);
mCondition.wait(lock);
}
}
}
void
Processor::fiberEntryPoint(Fiber *fiber)
{
gInterpreter.execute(&fiber->state, fiber->state.cia);
OSExitThread(fiber->state.gpr[3]);
}
void
Processor::exit()
{
// Return to parent fiber
auto core = tCurrentCore;
auto fiber = tCurrentCore->currentFiber;
gLog->trace("Core {} exit thread {}", core->id, fiber->thread->id);
SwitchToFiber(fiber->parentFiber);
}
void
Processor::queue(Fiber *fiber)
{
std::lock_guard<std::mutex> lock(mMutex);
auto compare =
[](Fiber *lhs, Fiber *rhs) {
return lhs->thread->basePriority < rhs->thread->basePriority;
};
auto pos = std::upper_bound(mFiberQueue.begin(), mFiberQueue.end(), fiber, compare);
mFiberQueue.insert(pos, fiber);
mCondition.notify_all();
}
void
Processor::reschedule(bool hasSchedulerLock, bool yield)
{
auto core = tCurrentCore;
if (!core) {
// Ran from host thread
return;
}
auto fiber = core->currentFiber;
auto next = peekNextFiber(core->id);
auto thread = fiber->thread;
bool reschedule = true;
// Priority is 0 = highest, 31 = lowest
if (thread->suspendCounter <= 0 && thread->state == OSThreadState::Running) {
if (!next) {
// There is no thread to reschedule to
return;
}
if (yield) {
// Yield will transfer control to threads with equal or better priority
if (thread->basePriority < next->thread->basePriority) {
return;
}
} else {
// Only reschedule to more important threads
if (thread->basePriority <= next->thread->basePriority) {
return;
}
}
}
2015-07-09 01:15:27 +01:00
// Change state to ready, only if this thread is running
if (fiber->thread->state == OSThreadState::Running) {
fiber->thread->state = OSThreadState::Ready;
}
// Add this fiber to queue
queue(fiber);
if (hasSchedulerLock) {
OSUnlockScheduler();
}
gLog->trace("Core {} leave thread {}", core->id, fiber->thread->id);
2015-07-09 01:15:27 +01:00
// Return to main scheduler fiber
SwitchToFiber(core->primaryFiber);
if (hasSchedulerLock) {
OSLockScheduler();
}
}
void
Processor::yield()
{
reschedule(false, true);
}
Fiber *
Processor::createFiber()
{
auto fiber = new Fiber();
mFiberList.push_back(fiber);
return fiber;
}
Fiber *
Processor::getCurrentFiber()
{
2015-07-26 14:00:36 +01:00
return tCurrentCore ? tCurrentCore->currentFiber : nullptr;
}
uint32_t
Processor::getCoreID()
{
2015-07-26 14:00:36 +01:00
return tCurrentCore ? tCurrentCore->id : 4;
}
uint32_t
Processor::getCoreCount()
{
return static_cast<uint32_t>(mCores.size());
}
namespace spdlog
{
namespace details
{
namespace os
{
size_t thread_id()
{
size_t coreID = 0, threadID = 0;
if (tCurrentCore) {
coreID = tCurrentCore->id;
if (tCurrentCore->currentFiber) {
threadID = tCurrentCore->currentFiber->thread->id;
}
}
return (coreID << 8) | threadID;
}
}
}
}