mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 07:05:40 +12:00
[Kernel] Implement thread sleeping
This commit is contained in:
parent
db0adc55c1
commit
2a4709dcfa
6 changed files with 60 additions and 7 deletions
|
@ -107,6 +107,8 @@ class CPU {
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static constexpr u64 ticksPerSec = 268111856;
|
||||||
|
|
||||||
CPU(Memory& mem, Kernel& kernel);
|
CPU(Memory& mem, Kernel& kernel);
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
@ -161,7 +163,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void runFrame() {
|
void runFrame() {
|
||||||
env.ticksLeft = 268111856 / 60;
|
env.ticksLeft = ticksPerSec / 60;
|
||||||
|
|
||||||
const auto exitReason = jit->Run();
|
const auto exitReason = jit->Run();
|
||||||
if (static_cast<u32>(exitReason) != 0) [[unlikely]] {
|
if (static_cast<u32>(exitReason) != 0) [[unlikely]] {
|
||||||
|
|
|
@ -66,6 +66,7 @@ class Kernel {
|
||||||
Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u32 arg,ThreadStatus status = ThreadStatus::Dormant);
|
Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u32 arg,ThreadStatus status = ThreadStatus::Dormant);
|
||||||
|
|
||||||
void signalArbiter(u32 waitingAddress, s32 threadCount);
|
void signalArbiter(u32 waitingAddress, s32 threadCount);
|
||||||
|
void sleepThread(s64 ns);
|
||||||
void sleepThreadOnArbiter(u32 waitingAddress);
|
void sleepThreadOnArbiter(u32 waitingAddress);
|
||||||
void switchThread(int newThreadIndex);
|
void switchThread(int newThreadIndex);
|
||||||
void sortThreads();
|
void sortThreads();
|
||||||
|
@ -116,6 +117,7 @@ class Kernel {
|
||||||
void sendSyncRequest();
|
void sendSyncRequest();
|
||||||
void signalEvent();
|
void signalEvent();
|
||||||
void svcCloseHandle();
|
void svcCloseHandle();
|
||||||
|
void svcSleepThread();
|
||||||
void connectToPort();
|
void connectToPort();
|
||||||
void outputDebugString();
|
void outputDebugString();
|
||||||
void waitSynchronization1();
|
void waitSynchronization1();
|
||||||
|
|
|
@ -116,6 +116,11 @@ struct Thread {
|
||||||
// The waiting address for threads that are waiting on an AddressArbiter
|
// The waiting address for threads that are waiting on an AddressArbiter
|
||||||
u32 waitingAddress;
|
u32 waitingAddress;
|
||||||
|
|
||||||
|
// The nanoseconds until a thread wakes up from being asleep or from timing out while waiting on an arbiter
|
||||||
|
s64 waitingNanoseconds;
|
||||||
|
// The tick this thread went to sleep on
|
||||||
|
u64 sleepTick;
|
||||||
|
|
||||||
// Thread context used for switching between threads
|
// Thread context used for switching between threads
|
||||||
std::array<u32, 16> gprs;
|
std::array<u32, 16> gprs;
|
||||||
std::array<u32, 32> fprs; // Stored as u32 because dynarmic does it
|
std::array<u32, 32> fprs; // Stored as u32 because dynarmic does it
|
||||||
|
|
|
@ -94,5 +94,14 @@ void Kernel::waitSynchronizationN() {
|
||||||
|
|
||||||
printf("Hacky WaitSync stuff for OoT triggered!!!\n");
|
printf("Hacky WaitSync stuff for OoT triggered!!!\n");
|
||||||
threads[currentThreadIndex].status = ThreadStatus::Ready;
|
threads[currentThreadIndex].status = ThreadStatus::Ready;
|
||||||
switchThread(rand() % threadCount);
|
|
||||||
|
while (1) {
|
||||||
|
auto index = rand() % threadCount;
|
||||||
|
auto& thread = threads[index];
|
||||||
|
|
||||||
|
if (canThreadRun(thread)) {
|
||||||
|
switchThread(rand() % threadCount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -23,6 +23,7 @@ void Kernel::serviceSVC(u32 svc) {
|
||||||
case 0x01: controlMemory(); break;
|
case 0x01: controlMemory(); break;
|
||||||
case 0x02: queryMemory(); break;
|
case 0x02: queryMemory(); break;
|
||||||
case 0x08: createThread(); break;
|
case 0x08: createThread(); break;
|
||||||
|
case 0x0A: svcSleepThread(); break;
|
||||||
case 0x14: releaseMutex(); break;
|
case 0x14: releaseMutex(); break;
|
||||||
case 0x17: createEvent(); break;
|
case 0x17: createEvent(); break;
|
||||||
case 0x18: signalEvent(); break;
|
case 0x18: signalEvent(); break;
|
||||||
|
@ -95,6 +96,10 @@ void Kernel::reset() {
|
||||||
arbiterCount = 0;
|
arbiterCount = 0;
|
||||||
threadCount = 0;
|
threadCount = 0;
|
||||||
|
|
||||||
|
for (auto& t : threads) {
|
||||||
|
t.status = ThreadStatus::Dead;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto& object : objects) {
|
for (auto& object : objects) {
|
||||||
deleteObjectData(object);
|
deleteObjectData(object);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,14 @@ void Kernel::sortThreads() {
|
||||||
bool Kernel::canThreadRun(const Thread& t) {
|
bool Kernel::canThreadRun(const Thread& t) {
|
||||||
if (t.status == ThreadStatus::Ready) {
|
if (t.status == ThreadStatus::Ready) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (t.status == ThreadStatus::WaitSleep) {
|
||||||
|
const u64 elapsedTicks = cpu.getTicks() - t.sleepTick;
|
||||||
|
|
||||||
|
constexpr double ticksPerSec = double(CPU::ticksPerSec);
|
||||||
|
constexpr double nsPerTick = ticksPerSec / 1000000000.0;
|
||||||
|
|
||||||
|
const s64 elapsedNs = s64(double(elapsedTicks) * nsPerTick);
|
||||||
|
return elapsedNs >= t.waitingNanoseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle timeouts and stuff here
|
// Handle timeouts and stuff here
|
||||||
|
@ -129,6 +137,28 @@ Handle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Kernel::sleepThreadOnArbiter(u32 waitingAddress) {
|
||||||
|
Thread& t = threads[currentThreadIndex];
|
||||||
|
t.status = ThreadStatus::WaitArbiter;
|
||||||
|
t.waitingAddress = waitingAddress;
|
||||||
|
|
||||||
|
switchToNextThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a thread sleep for a certain amount of nanoseconds at minimum
|
||||||
|
void Kernel::sleepThread(s64 ns) {
|
||||||
|
if (ns < 0) {
|
||||||
|
Helpers::panic("Sleeping a thread for a negative amount of ns");
|
||||||
|
} else if (ns == 0) { // Used when we want to force a thread switch
|
||||||
|
switchToNextThread();
|
||||||
|
} else { // If we're sleeping for > 0 ns
|
||||||
|
Thread& t = threads[currentThreadIndex];
|
||||||
|
t.status = ThreadStatus::WaitSleep;
|
||||||
|
t.waitingNanoseconds = ns;
|
||||||
|
t.sleepTick = cpu.getTicks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Result CreateThread(s32 priority, ThreadFunc entrypoint, u32 arg, u32 stacktop, s32 threadPriority, s32 processorID)
|
// Result CreateThread(s32 priority, ThreadFunc entrypoint, u32 arg, u32 stacktop, s32 threadPriority, s32 processorID)
|
||||||
void Kernel::createThread() {
|
void Kernel::createThread() {
|
||||||
u32 priority = regs[0];
|
u32 priority = regs[0];
|
||||||
|
@ -150,12 +180,12 @@ void Kernel::createThread() {
|
||||||
regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready);
|
regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Kernel::sleepThreadOnArbiter(u32 waitingAddress) {
|
// void SleepThread(s64 nanoseconds)
|
||||||
Thread& t = threads[currentThreadIndex];
|
void Kernel::svcSleepThread() {
|
||||||
t.status = ThreadStatus::WaitArbiter;
|
const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32));
|
||||||
t.waitingAddress = waitingAddress;
|
logSVC("SleepThread(ns = %lld)\n", ns);
|
||||||
|
|
||||||
switchToNextThread();
|
sleepThread(ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Kernel::getThreadID() {
|
void Kernel::getThreadID() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue