mirror of https://github.com/yuzu-mirror/yuzu
commit
23fe6f5be3
@ -1,91 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Kernel namespace
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
AddressArbiter::AddressArbiter() {}
|
||||
AddressArbiter::~AddressArbiter() {}
|
||||
|
||||
SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) {
|
||||
SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter);
|
||||
|
||||
address_arbiter->name = std::move(name);
|
||||
|
||||
return address_arbiter;
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value,
|
||||
u64 nanoseconds) {
|
||||
switch (type) {
|
||||
|
||||
// Signal thread(s) waiting for arbitrate address...
|
||||
case ArbitrationType::Signal:
|
||||
// Negative value means resume all threads
|
||||
if (value < 0) {
|
||||
ArbitrateAllThreads(address);
|
||||
} else {
|
||||
// Resume first N threads
|
||||
for (int i = 0; i < value; i++)
|
||||
ArbitrateHighestPriorityThread(address);
|
||||
}
|
||||
break;
|
||||
|
||||
// Wait current thread (acquire the arbiter)...
|
||||
case ArbitrationType::WaitIfLessThan:
|
||||
if ((s32)Memory::Read32(address) < value) {
|
||||
Kernel::WaitCurrentThread_ArbitrateAddress(address);
|
||||
}
|
||||
break;
|
||||
case ArbitrationType::WaitIfLessThanWithTimeout:
|
||||
if ((s32)Memory::Read32(address) < value) {
|
||||
Kernel::WaitCurrentThread_ArbitrateAddress(address);
|
||||
GetCurrentThread()->WakeAfterDelay(nanoseconds);
|
||||
}
|
||||
break;
|
||||
case ArbitrationType::DecrementAndWaitIfLessThan: {
|
||||
s32 memory_value = Memory::Read32(address);
|
||||
if (memory_value < value) {
|
||||
// Only change the memory value if the thread should wait
|
||||
Memory::Write32(address, (s32)memory_value - 1);
|
||||
Kernel::WaitCurrentThread_ArbitrateAddress(address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: {
|
||||
s32 memory_value = Memory::Read32(address);
|
||||
if (memory_value < value) {
|
||||
// Only change the memory value if the thread should wait
|
||||
Memory::Write32(address, (s32)memory_value - 1);
|
||||
Kernel::WaitCurrentThread_ArbitrateAddress(address);
|
||||
GetCurrentThread()->WakeAfterDelay(nanoseconds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_ERROR(Kernel, "unknown type=%d", type);
|
||||
return ERR_INVALID_ENUM_VALUE_FND;
|
||||
}
|
||||
|
||||
// The calls that use a timeout seem to always return a Timeout error even if they did not put
|
||||
// the thread to sleep
|
||||
if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
|
||||
type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
|
||||
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
@ -1,60 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
// Address arbiters are an underlying kernel synchronization object that can be created/used via
|
||||
// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
|
||||
// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
|
||||
// semphores.
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Kernel namespace
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
enum class ArbitrationType : u32 {
|
||||
Signal,
|
||||
WaitIfLessThan,
|
||||
DecrementAndWaitIfLessThan,
|
||||
WaitIfLessThanWithTimeout,
|
||||
DecrementAndWaitIfLessThanWithTimeout,
|
||||
};
|
||||
|
||||
class AddressArbiter final : public Object {
|
||||
public:
|
||||
/**
|
||||
* Creates an address arbiter.
|
||||
*
|
||||
* @param name Optional name used for debugging.
|
||||
* @returns The created AddressArbiter.
|
||||
*/
|
||||
static SharedPtr<AddressArbiter> Create(std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Arbiter";
|
||||
}
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
std::string name; ///< Name of address arbiter object (optional)
|
||||
|
||||
ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds);
|
||||
|
||||
private:
|
||||
AddressArbiter();
|
||||
~AddressArbiter() override;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
@ -0,0 +1,134 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
|
||||
|
||||
Scheduler::~Scheduler() {
|
||||
for (auto& thread : thread_list) {
|
||||
thread->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool Scheduler::HaveReadyThreads() {
|
||||
return ready_queue.get_first() != nullptr;
|
||||
}
|
||||
|
||||
Thread* Scheduler::GetCurrentThread() const {
|
||||
return current_thread.get();
|
||||
}
|
||||
|
||||
Thread* Scheduler::PopNextReadyThread() {
|
||||
Thread* next = nullptr;
|
||||
Thread* thread = GetCurrentThread();
|
||||
|
||||
if (thread && thread->status == THREADSTATUS_RUNNING) {
|
||||
// We have to do better than the current thread.
|
||||
// This call returns null when that's not possible.
|
||||
next = ready_queue.pop_first_better(thread->current_priority);
|
||||
if (!next) {
|
||||
// Otherwise just keep going with the current thread
|
||||
next = thread;
|
||||
}
|
||||
} else {
|
||||
next = ready_queue.pop_first();
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
Thread* previous_thread = GetCurrentThread();
|
||||
|
||||
// Save context for previous thread
|
||||
if (previous_thread) {
|
||||
previous_thread->last_running_ticks = CoreTiming::GetTicks();
|
||||
cpu_core->SaveContext(previous_thread->context);
|
||||
|
||||
if (previous_thread->status == THREADSTATUS_RUNNING) {
|
||||
// This is only the case when a reschedule is triggered without the current thread
|
||||
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
|
||||
ready_queue.push_front(previous_thread->current_priority, previous_thread);
|
||||
previous_thread->status = THREADSTATUS_READY;
|
||||
}
|
||||
}
|
||||
|
||||
// Load context of new thread
|
||||
if (new_thread) {
|
||||
ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
|
||||
"Thread must be ready to become running.");
|
||||
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
new_thread->CancelWakeupTimer();
|
||||
|
||||
auto previous_process = Kernel::g_current_process;
|
||||
|
||||
current_thread = new_thread;
|
||||
|
||||
ready_queue.remove(new_thread->current_priority, new_thread);
|
||||
new_thread->status = THREADSTATUS_RUNNING;
|
||||
|
||||
if (previous_process != current_thread->owner_process) {
|
||||
Kernel::g_current_process = current_thread->owner_process;
|
||||
SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
|
||||
}
|
||||
|
||||
cpu_core->LoadContext(new_thread->context);
|
||||
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
|
||||
} else {
|
||||
current_thread = nullptr;
|
||||
// Note: We do not reset the current process and current page table when idling because
|
||||
// technically we haven't changed processes, our threads are just paused.
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::Reschedule() {
|
||||
Thread* cur = GetCurrentThread();
|
||||
Thread* next = PopNextReadyThread();
|
||||
|
||||
if (cur && next) {
|
||||
LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
|
||||
} else if (cur) {
|
||||
LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
|
||||
} else if (next) {
|
||||
LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
|
||||
}
|
||||
|
||||
SwitchContext(next);
|
||||
}
|
||||
|
||||
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
|
||||
thread_list.push_back(thread);
|
||||
ready_queue.prepare(priority);
|
||||
}
|
||||
|
||||
void Scheduler::RemoveThread(Thread* thread) {
|
||||
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
|
||||
thread_list.end());
|
||||
}
|
||||
|
||||
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
|
||||
ASSERT(thread->status == THREADSTATUS_READY);
|
||||
ready_queue.push_back(priority, thread);
|
||||
}
|
||||
|
||||
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
|
||||
ASSERT(thread->status == THREADSTATUS_READY);
|
||||
ready_queue.remove(priority, thread);
|
||||
}
|
||||
|
||||
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
|
||||
// If thread was ready, adjust queues
|
||||
if (thread->status == THREADSTATUS_READY)
|
||||
ready_queue.move(thread, thread->current_priority, priority);
|
||||
else
|
||||
ready_queue.prepare(priority);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
@ -0,0 +1,73 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Scheduler final {
|
||||
public:
|
||||
explicit Scheduler(ARM_Interface* cpu_core);
|
||||
~Scheduler();
|
||||
|
||||
/// Returns whether there are any threads that are ready to run.
|
||||
bool HaveReadyThreads();
|
||||
|
||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||
void Reschedule();
|
||||
|
||||
/// Gets the current running thread
|
||||
Thread* GetCurrentThread() const;
|
||||
|
||||
/// Adds a new thread to the scheduler
|
||||
void AddThread(SharedPtr<Thread> thread, u32 priority);
|
||||
|
||||
/// Removes a thread from the scheduler
|
||||
void RemoveThread(Thread* thread);
|
||||
|
||||
/// Schedules a thread that has become "ready"
|
||||
void ScheduleThread(Thread* thread, u32 priority);
|
||||
|
||||
/// Unschedules a thread that was already scheduled
|
||||
void UnscheduleThread(Thread* thread, u32 priority);
|
||||
|
||||
/// Sets the priority of a thread in the scheduler
|
||||
void SetThreadPriority(Thread* thread, u32 priority);
|
||||
|
||||
/// Returns a list of all threads managed by the scheduler
|
||||
const std::vector<SharedPtr<Thread>>& GetThreadList() const {
|
||||
return thread_list;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Pops and returns the next thread from the thread queue
|
||||
* @return A pointer to the next ready thread
|
||||
*/
|
||||
Thread* PopNextReadyThread();
|
||||
|
||||
/**
|
||||
* Switches the CPU's active thread context to that of the specified thread
|
||||
* @param new_thread The thread to switch to
|
||||
*/
|
||||
void SwitchContext(Thread* new_thread);
|
||||
|
||||
/// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<SharedPtr<Thread>> thread_list;
|
||||
|
||||
/// Lists only ready thread ids.
|
||||
Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
|
||||
|
||||
SharedPtr<Thread> current_thread = nullptr;
|
||||
|
||||
ARM_Interface* cpu_core;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
Loading…
Reference in New Issue