diff options
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 13 | ||||
| -rw-r--r-- | src/core/hle/kernel/address_arbiter.h | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/event.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/kernel/event.h | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 24 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 21 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 64 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.h | 10 | ||||
| -rw-r--r-- | src/core/hle/kernel/semaphore.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/semaphore.h | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/session.cpp | 13 | ||||
| -rw-r--r-- | src/core/hle/kernel/session.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 23 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.h | 7 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 407 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 142 | ||||
| -rw-r--r-- | src/core/hle/kernel/timer.cpp | 28 | ||||
| -rw-r--r-- | src/core/hle/kernel/timer.h | 8 |
18 files changed, 430 insertions, 361 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 2d01e2ef5..42f8ce2d9 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -15,14 +15,15 @@ namespace Kernel { -ResultVal<SharedPtr<AddressArbiter>> AddressArbiter::Create(std::string name) { +AddressArbiter::AddressArbiter() {} +AddressArbiter::~AddressArbiter() {} + +SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) { SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter); - // TOOD(yuriks): Don't create Handle (see Thread::Create()) - CASCADE_RESULT(auto unused, Kernel::g_handle_table.Create(address_arbiter)); address_arbiter->name = std::move(name); - return MakeResult<SharedPtr<AddressArbiter>>(std::move(address_arbiter)); + return address_arbiter; } ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, @@ -51,7 +52,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, case ArbitrationType::WaitIfLessThanWithTimeout: if ((s32)Memory::Read32(address) <= value) { Kernel::WaitCurrentThread_ArbitrateAddress(address); - Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); + GetCurrentThread()->WakeAfterDelay(nanoseconds); HLE::Reschedule(__func__); } break; @@ -71,7 +72,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, Memory::Write32(address, memory_value); if (memory_value <= value) { Kernel::WaitCurrentThread_ArbitrateAddress(address); - Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); + GetCurrentThread()->WakeAfterDelay(nanoseconds); HLE::Reschedule(__func__); } break; diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 638afff9e..8f6a1a8df 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -34,7 +34,7 @@ public: * @param name Optional name used for debugging. * @returns The created AddressArbiter. */ - static ResultVal<SharedPtr<AddressArbiter>> Create(std::string name = "Unknown"); + static SharedPtr<AddressArbiter> Create(std::string name = "Unknown"); std::string GetTypeName() const override { return "Arbiter"; } std::string GetName() const override { return name; } @@ -47,7 +47,8 @@ public: ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds); private: - AddressArbiter() = default; + AddressArbiter(); + ~AddressArbiter() override; }; } // namespace FileSys diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index d9ad40c6a..898e1c98f 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -14,16 +14,17 @@ namespace Kernel { -ResultVal<SharedPtr<Event>> Event::Create(ResetType reset_type, std::string name) { +Event::Event() {} +Event::~Event() {} + +SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { SharedPtr<Event> evt(new Event); - // TOOD(yuriks): Don't create Handle (see Thread::Create()) - CASCADE_RESULT(auto unused, Kernel::g_handle_table.Create(evt)); evt->signaled = false; evt->reset_type = evt->intitial_reset_type = reset_type; evt->name = std::move(name); - return MakeResult<SharedPtr<Event>>(evt); + return evt; } bool Event::ShouldWait() { diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 2c3e6b14e..fba960d2a 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -18,7 +18,7 @@ public: * @param reset_type ResetType describing how to create event * @param name Optional name of event */ - static ResultVal<SharedPtr<Event>> Create(ResetType reset_type, std::string name = "Unknown"); + static SharedPtr<Event> Create(ResetType reset_type, std::string name = "Unknown"); std::string GetTypeName() const override { return "Event"; } std::string GetName() const override { return name; } @@ -39,7 +39,8 @@ public: void Clear(); private: - Event() = default; + Event(); + ~Event() override; }; } // namespace diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d7fa4dcea..a2ffbcdb7 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -14,14 +14,16 @@ namespace Kernel { +unsigned int Object::next_object_id = 0; + SharedPtr<Thread> g_main_thread = nullptr; HandleTable g_handle_table; u64 g_program_id = 0; -void WaitObject::AddWaitingThread(Thread* thread) { +void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); if (itr == waiting_threads.end()) - waiting_threads.push_back(thread); + waiting_threads.push_back(std::move(thread)); } void WaitObject::RemoveWaitingThread(Thread* thread) { @@ -30,11 +32,11 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { waiting_threads.erase(itr); } -Thread* WaitObject::WakeupNextThread() { +SharedPtr<Thread> WaitObject::WakeupNextThread() { if (waiting_threads.empty()) return nullptr; - auto next_thread = waiting_threads.front(); + auto next_thread = std::move(waiting_threads.front()); waiting_threads.erase(waiting_threads.begin()); next_thread->ReleaseWaitObject(this); @@ -74,13 +76,10 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { // CTR-OS doesn't use generation 0, so skip straight to 1. if (next_generation >= (1 << 15)) next_generation = 1; - Handle handle = generation | (slot << 15); - if (obj->handle == INVALID_HANDLE) - obj->handle = handle; - generations[slot] = generation; objects[slot] = std::move(obj); + Handle handle = generation | (slot << 15); return MakeResult<Handle>(handle); } @@ -98,11 +97,10 @@ ResultCode HandleTable::Close(Handle handle) { return ERR_INVALID_HANDLE; size_t slot = GetSlot(handle); - u16 generation = GetGeneration(handle); objects[slot] = nullptr; - generations[generation] = next_free_slot; + generations[slot] = next_free_slot; next_free_slot = slot; return RESULT_SUCCESS; } @@ -155,12 +153,8 @@ void Shutdown() { * @return True on success, otherwise false */ bool LoadExec(u32 entry_point) { - Core::g_app_core->SetPC(entry_point); - // 0x30 is the typical main thread priority I've seen used so far - g_main_thread = Kernel::SetupMainThread(0x30, Kernel::DEFAULT_STACK_SIZE); - // Setup the idle thread - Kernel::SetupIdleThread(); + g_main_thread = Kernel::SetupMainThread(Kernel::DEFAULT_STACK_SIZE, entry_point, 0x30); return true; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 9860479ac..4d8e388b6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -58,14 +58,12 @@ enum { DEFAULT_STACK_SIZE = 0x4000, }; -class HandleTable; - class Object : NonCopyable { - friend class HandleTable; - u32 handle = INVALID_HANDLE; public: virtual ~Object() {} - Handle GetHandle() const { return handle; } + + /// Returns a unique identifier for the object. For debugging purposes only. + unsigned int GetObjectId() const { return object_id; } virtual std::string GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; } virtual std::string GetName() const { return "[UNKNOWN KERNEL OBJECT]"; } @@ -101,7 +99,10 @@ private: friend void intrusive_ptr_add_ref(Object*); friend void intrusive_ptr_release(Object*); + static unsigned int next_object_id; + unsigned int ref_count = 0; + unsigned int object_id = next_object_id++; }; // Special functions used by boost::instrusive_ptr to do automatic ref-counting @@ -135,25 +136,26 @@ public: * Add a thread to wait on this object * @param thread Pointer to thread to add */ - void AddWaitingThread(Thread* thread); + void AddWaitingThread(SharedPtr<Thread> thread); /** * Removes a thread from waiting on this object (e.g. if it was resumed already) * @param thread Pointer to thread to remove */ - void RemoveWaitingThread(Thread* thead); + void RemoveWaitingThread(Thread* thread); /** * Wake up the next thread waiting on this object * @return Pointer to the thread that was resumed, nullptr if no threads are waiting */ - Thread* WakeupNextThread(); + SharedPtr<Thread> WakeupNextThread(); /// Wake up all threads waiting on this object void WakeupAllWaitingThreads(); private: - std::vector<Thread*> waiting_threads; ///< Threads waiting for this object to become available + /// Threads waiting for this object to become available + std::vector<SharedPtr<Thread>> waiting_threads; }; /** @@ -274,7 +276,6 @@ private: }; extern HandleTable g_handle_table; -extern SharedPtr<Thread> g_main_thread; /// The ID code of the currently running game /// TODO(Subv): This variable should not be here, diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index acf484659..a811db392 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -5,6 +5,8 @@ #include <map> #include <vector> +#include <boost/range/algorithm_ext/erase.hpp> + #include "common/common.h" #include "core/hle/kernel/kernel.h" @@ -13,16 +15,13 @@ namespace Kernel { -typedef std::multimap<SharedPtr<Thread>, SharedPtr<Mutex>> MutexMap; -static MutexMap g_mutex_held_locks; - /** * Resumes a thread waiting for the specified mutex * @param mutex The mutex that some thread is waiting on */ static void ResumeWaitingThread(Mutex* mutex) { // Reset mutex lock thread handle, nothing is waiting - mutex->locked = false; + mutex->lock_count = 0; mutex->holding_thread = nullptr; // Find the next waiting thread for the mutex... @@ -33,24 +32,19 @@ static void ResumeWaitingThread(Mutex* mutex) { } void ReleaseThreadMutexes(Thread* thread) { - auto locked_range = g_mutex_held_locks.equal_range(thread); - - // Release every mutex that the thread holds, and resume execution on the waiting threads - for (auto iter = locked_range.first; iter != locked_range.second; ++iter) { - ResumeWaitingThread(iter->second.get()); + for (auto& mtx : thread->held_mutexes) { + ResumeWaitingThread(mtx.get()); } - - // Erase all the locks that this thread holds - g_mutex_held_locks.erase(thread); + thread->held_mutexes.clear(); } -ResultVal<SharedPtr<Mutex>> Mutex::Create(bool initial_locked, std::string name) { +Mutex::Mutex() {} +Mutex::~Mutex() {} + +SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { SharedPtr<Mutex> mutex(new Mutex); - // TOOD(yuriks): Don't create Handle (see Thread::Create()) - CASCADE_RESULT(auto unused, Kernel::g_handle_table.Create(mutex)); - mutex->initial_locked = initial_locked; - mutex->locked = false; + mutex->lock_count = 0; mutex->name = std::move(name); mutex->holding_thread = nullptr; @@ -58,42 +52,40 @@ ResultVal<SharedPtr<Mutex>> Mutex::Create(bool initial_locked, std::string name) if (initial_locked) mutex->Acquire(); - return MakeResult<SharedPtr<Mutex>>(mutex); + return mutex; } bool Mutex::ShouldWait() { - return locked && holding_thread != GetCurrentThread(); + return lock_count > 0 && holding_thread != GetCurrentThread();; } void Mutex::Acquire() { Acquire(GetCurrentThread()); } -void Mutex::Acquire(Thread* thread) { +void Mutex::Acquire(SharedPtr<Thread> thread) { _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); - if (locked) - return; - locked = true; + // Actually "acquire" the mutex only if we don't already have it... + if (lock_count == 0) { + thread->held_mutexes.insert(this); + holding_thread = std::move(thread); + } - g_mutex_held_locks.insert(std::make_pair(thread, this)); - holding_thread = thread; + lock_count++; } void Mutex::Release() { - if (!locked) - return; - - auto locked_range = g_mutex_held_locks.equal_range(holding_thread); - - for (MutexMap::iterator iter = locked_range.first; iter != locked_range.second; ++iter) { - if (iter->second == this) { - g_mutex_held_locks.erase(iter); - break; + // Only release if the mutex is held... + if (lock_count > 0) { + lock_count--; + + // Yield to the next thread only if we've fully released the mutex... + if (lock_count == 0) { + holding_thread->held_mutexes.erase(this); + ResumeWaitingThread(this); } } - - ResumeWaitingThread(this); } } // namespace diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 1e69528f1..d6d5328be 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -22,7 +22,7 @@ public: * @param name Optional name of mutex * @return Pointer to new Mutex object */ - static ResultVal<SharedPtr<Mutex>> Create(bool initial_locked, std::string name = "Unknown"); + static SharedPtr<Mutex> Create(bool initial_locked, std::string name = "Unknown"); std::string GetTypeName() const override { return "Mutex"; } std::string GetName() const override { return name; } @@ -30,8 +30,7 @@ public: static const HandleType HANDLE_TYPE = HandleType::Mutex; HandleType GetHandleType() const override { return HANDLE_TYPE; } - bool initial_locked; ///< Initial lock state when mutex was created - bool locked; ///< Current locked state + int lock_count; ///< Number of times the mutex has been acquired std::string name; ///< Name of mutex (optional) SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex @@ -43,11 +42,12 @@ public: * @param mutex Mutex that is to be acquired * @param thread Thread that will acquire the mutex */ - void Acquire(Thread* thread); + void Acquire(SharedPtr<Thread> thread); void Release(); private: - Mutex() = default; + Mutex(); + ~Mutex() override; }; /** diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index a9e406ef4..c8cf8b9a2 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -10,6 +10,9 @@ namespace Kernel { +Semaphore::Semaphore() {} +Semaphore::~Semaphore() {} + ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, std::string name) { @@ -18,8 +21,6 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou ErrorSummary::WrongArgument, ErrorLevel::Permanent); SharedPtr<Semaphore> semaphore(new Semaphore); - // TOOD(yuriks): Don't create Handle (see Thread::Create()) - CASCADE_RESULT(auto unused, Kernel::g_handle_table.Create(semaphore)); // When the semaphore is created, some slots are reserved for other threads, // and the rest is reserved for the caller thread diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index 9bb404ab6..d8dc1fd78 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h @@ -47,7 +47,8 @@ public: ResultVal<s32> Release(s32 release_count); private: - Semaphore() = default; + Semaphore(); + ~Semaphore() override; }; } // namespace diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp new file mode 100644 index 000000000..0594967f8 --- /dev/null +++ b/src/core/hle/kernel/session.cpp @@ -0,0 +1,13 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/kernel/session.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +Session::Session() {} +Session::~Session() {} + +} diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 1788e4375..7cc9332c9 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -5,6 +5,7 @@ #pragma once #include "core/hle/kernel/kernel.h" +#include "core/mem_map.h" namespace Kernel { @@ -43,6 +44,9 @@ inline static u32* GetCommandBuffer(const int offset=0) { */ class Session : public WaitObject { public: + Session(); + ~Session() override; + std::string GetTypeName() const override { return "Session"; } static const HandleType HANDLE_TYPE = HandleType::Session; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 536d134b0..4211fcf04 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -9,30 +9,31 @@ namespace Kernel { -ResultVal<SharedPtr<SharedMemory>> SharedMemory::Create(std::string name) { - SharedPtr<SharedMemory> shared_memory(new SharedMemory); +SharedMemory::SharedMemory() {} +SharedMemory::~SharedMemory() {} - // TOOD(yuriks): Don't create Handle (see Thread::Create()) - CASCADE_RESULT(auto unused, Kernel::g_handle_table.Create(shared_memory)); +SharedPtr<SharedMemory> SharedMemory::Create(std::string name) { + SharedPtr<SharedMemory> shared_memory(new SharedMemory); shared_memory->name = std::move(name); - return MakeResult<SharedPtr<SharedMemory>>(std::move(shared_memory)); + + return shared_memory; } ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions) { if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { - LOG_ERROR(Kernel, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", - GetHandle(), address); + LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X outside of shared mem bounds!", + GetObjectId(), address); // TODO: Verify error code with hardware return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } - base_address = address; - permissions = permissions; - other_permissions = other_permissions; + this->base_address = address; + this->permissions = permissions; + this->other_permissions = other_permissions; return RESULT_SUCCESS; } @@ -41,7 +42,7 @@ ResultVal<u8*> SharedMemory::GetPointer(u32 offset) { if (base_address != 0) return MakeResult<u8*>(Memory::GetPointer(base_address + offset)); - LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", GetHandle()); + LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId()); // TODO(yuriks): Verify error code. return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, ErrorSummary::InvalidState, ErrorLevel::Permanent); diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index f9ae23e93..5833b411c 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -29,7 +29,7 @@ public: * Creates a shared memory object * @param name Optional object name, used only for debugging purposes. */ - static ResultVal<SharedPtr<SharedMemory>> Create(std::string name = "Unknown"); + static SharedPtr<SharedMemory> Create(std::string name = "Unknown"); std::string GetTypeName() const override { return "SharedMemory"; } @@ -51,13 +51,14 @@ public: */ ResultVal<u8*> GetPointer(u32 offset = 0); - VAddr base_address; ///< Address of shared memory block in RAM + VAddr base_address; ///< Address of shared memory block in RAM MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) std::string name; ///< Name of shared memory object (optional) private: - SharedMemory() = default; + SharedMemory(); + ~SharedMemory() override; }; } // namespace diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 03b492c75..7f629c20e 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -4,7 +4,6 @@ #include <algorithm> #include <list> -#include <map> #include <vector> #include "common/common.h" @@ -22,8 +21,11 @@ namespace Kernel { +/// Event type for the thread wake up event +static int ThreadWakeupEventType = -1; + bool Thread::ShouldWait() { - return status != THREADSTATUS_DORMANT; + return status != THREADSTATUS_DEAD; } void Thread::Acquire() { @@ -34,94 +36,75 @@ void Thread::Acquire() { static std::vector<SharedPtr<Thread>> thread_list; // Lists only ready thread ids. -static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue; +static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> ready_queue; static Thread* current_thread; -static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup -static u32 next_thread_id; ///< The next available thread id +// The first available thread id at startup +static u32 next_thread_id = 1; -Thread* GetCurrentThread() { - return current_thread; +/** + * Creates a new thread ID + * @return The new thread ID + */ +inline static u32 const NewThreadId() { + return next_thread_id++; } -/// Resets a thread -static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { - memset(&t->context, 0, sizeof(Core::ThreadContext)); +Thread::Thread() {} +Thread::~Thread() {} - t->context.cpu_registers[0] = arg; - t->context.pc = t->context.reg_15 = t->entry_point; - t->context.sp = t->stack_top; - t->context.cpsr = 0x1F; // Usermode - - // TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a - // thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be - // agnostic of the CPU core. - t->context.mode = 8; - - if (t->current_priority < lowest_priority) { - t->current_priority = t->initial_priority; - } - - t->wait_objects.clear(); - t->wait_address = 0; -} - -/// Change a thread to "ready" state -static void ChangeReadyState(Thread* t, bool ready) { - if (t->IsReady()) { - if (!ready) { - thread_ready_queue.remove(t->current_priority, t); - } - } else if (ready) { - if (t->IsRunning()) { - thread_ready_queue.push_front(t->current_priority, t); - } else { - thread_ready_queue.push_back(t->current_priority, t); - } - t->status = THREADSTATUS_READY; - } +Thread* GetCurrentThread() { + return current_thread; } -/// Check if a thread is waiting on a the specified wait object +/** + * Check if a thread is waiting on the specified wait object + * @param thread The thread to test + * @param wait_object The object to test against + * @return True if the thread is waiting, false otherwise + */ static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { - auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); + if (thread->status != THREADSTATUS_WAIT_SYNCH) + return false; - if (itr != thread->wait_objects.end()) - return thread->IsWaiting(); - - return false; + auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); + return itr != thread->wait_objects.end(); } -/// Check if the specified thread is waiting on the specified address to be arbitrated +/** + * Check if the specified thread is waiting on the specified address to be arbitrated + * @param thread The thread to test + * @param wait_address The address to test against + * @return True if the thread is waiting, false otherwise + */ static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { - return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address; + return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address; } -/// Stops the current thread -void Thread::Stop(const char* reason) { +void Thread::Stop() { // Release all the mutexes that this thread holds ReleaseThreadMutexes(this); - ChangeReadyState(this, false); - status = THREADSTATUS_DORMANT; - WakeupAllWaitingThreads(); + // Cancel any outstanding wakeup events for this thread + CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); - // Stopped threads are never waiting. - wait_objects.clear(); - wait_address = 0; -} + // Clean up thread from ready queue + // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) + if (status == THREADSTATUS_READY){ + ready_queue.remove(current_priority, this); + } -/// Changes a threads state -static void ChangeThreadState(Thread* t, ThreadStatus new_status) { - if (!t || t->status == new_status) { - return; + status = THREADSTATUS_DEAD; + + WakeupAllWaitingThreads(); + + // Clean up any dangling references in objects that this thread was waiting for + for (auto& wait_object : wait_objects) { + wait_object->RemoveWaitingThread(this); } - ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); - t->status = new_status; } -/// Arbitrate the highest priority thread that is waiting Thread* ArbitrateHighestPriorityThread(u32 address) { Thread* highest_priority_thread = nullptr; s32 priority = THREADPRIO_LOWEST; @@ -148,118 +131,124 @@ Thread* ArbitrateHighestPriorityThread(u32 address) { return highest_priority_thread; } -/// Arbitrate all threads currently waiting void ArbitrateAllThreads(u32 address) { - - // Iterate through threads, find highest priority thread that is waiting to be arbitrated... + // Resume all threads found to be waiting on the address for (auto& thread : thread_list) { if (CheckWait_AddressArbiter(thread.get(), address)) thread->ResumeFromWait(); } } -/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -static void CallThread(Thread* t) { - // Stop waiting - ChangeThreadState(t, THREADSTATUS_READY); -} +/** + * Switches the CPU's active thread context to that of the specified thread + * @param new_thread The thread to switch to + */ +static void SwitchContext(Thread* new_thread) { + _dbg_assert_msg_(Kernel, new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); -/// Switches CPU context to that of the specified thread -static void SwitchContext(Thread* t) { - Thread* cur = GetCurrentThread(); + Thread* previous_thread = GetCurrentThread(); - // Save context for current thread - if (cur) { - Core::g_app_core->SaveContext(cur->context); + // Save context for previous thread + if (previous_thread) { + Core::g_app_core->SaveContext(previous_thread->context); - if (cur->IsRunning()) { - ChangeReadyState(cur, true); + 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 (t) { - current_thread = t; - ChangeReadyState(t, false); - t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; - Core::g_app_core->LoadContext(t->context); + if (new_thread) { + current_thread = new_thread; + + ready_queue.remove(new_thread->current_priority, new_thread); + new_thread->status = THREADSTATUS_RUNNING; + + Core::g_app_core->LoadContext(new_thread->context); } else { current_thread = nullptr; } } -/// Gets the next thread that is ready to be run by priority -static Thread* NextThread() { +/** + * Pops and returns the next thread from the thread queue + * @return A pointer to the next ready thread + */ +static Thread* PopNextReadyThread() { Thread* next; - Thread* cur = GetCurrentThread(); + Thread* thread = GetCurrentThread(); - if (cur && cur->IsRunning()) { - next = thread_ready_queue.pop_first_better(cur->current_priority); + 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); } else { - next = thread_ready_queue.pop_first(); - } - if (next == 0) { - return nullptr; + next = ready_queue.pop_first(); } + return next; } void WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); - ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); + thread->status = THREADSTATUS_WAIT_SLEEP; } -void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { +void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, bool wait_set_output, bool wait_all) { Thread* thread = GetCurrentThread(); thread->wait_set_output = wait_set_output; thread->wait_all = wait_all; - - // It's possible to call WaitSynchronizationN without any objects passed in... - if (wait_object != nullptr) - thread->wait_objects.push_back(wait_object); - - ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); + thread->wait_objects = std::move(wait_objects); + thread->status = THREADSTATUS_WAIT_SYNCH; } void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { Thread* thread = GetCurrentThread(); thread->wait_address = wait_address; - ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); + thread->status = THREADSTATUS_WAIT_ARB; } -/// Event type for the thread wake up event -static int ThreadWakeupEventType = -1; - -/// Callback that will wake up the thread it was scheduled for -static void ThreadWakeupCallback(u64 parameter, int cycles_late) { - Handle handle = static_cast<Handle>(parameter); - SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(handle); +// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing +// us to simply use a pool index or similar. +static Kernel::HandleTable wakeup_callback_handle_table; + +/** + * Callback that will wake up the thread it was scheduled for + * @param thread_handle The handle of the thread that's been awoken + * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time + */ +static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { + SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle); if (thread == nullptr) { - LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", (Handle)thread_handle); return; } - thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, - ErrorSummary::StatusChanged, ErrorLevel::Info)); + if (thread->status == THREADSTATUS_WAIT_SYNCH) { + thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info)); - if (thread->wait_set_output) - thread->SetWaitSynchronizationOutput(-1); + if (thread->wait_set_output) + thread->SetWaitSynchronizationOutput(-1); + } thread->ResumeFromWait(); } - -void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { +void Thread::WakeAfterDelay(s64 nanoseconds) { // Don't schedule a wakeup if the thread wants to wait forever if (nanoseconds == -1) return; - _dbg_assert_(Kernel, thread != nullptr); u64 microseconds = nanoseconds / 1000; - CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); + CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); } void Thread::ReleaseWaitObject(WaitObject* wait_object) { - if (wait_objects.empty()) { + if (status != THREADSTATUS_WAIT_SYNCH || wait_objects.empty()) { LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); return; } @@ -301,49 +290,56 @@ void Thread::ReleaseWaitObject(WaitObject* wait_object) { } void Thread::ResumeFromWait() { - // Cancel any outstanding wakeup events - CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); - - status &= ~THREADSTATUS_WAIT; - - // Remove this thread from all other WaitObjects - for (auto wait_object : wait_objects) - wait_object->RemoveWaitingThread(this); - - wait_objects.clear(); - wait_set_output = false; - wait_all = false; - wait_address = 0; - - if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { - ChangeReadyState(this, true); + // Cancel any outstanding wakeup events for this thread + CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); + + switch (status) { + case THREADSTATUS_WAIT_SYNCH: + // Remove this thread from all other WaitObjects + for (auto wait_object : wait_objects) + wait_object->RemoveWaitingThread(this); + break; + case THREADSTATUS_WAIT_ARB: + case THREADSTATUS_WAIT_SLEEP: + break; + case THREADSTATUS_RUNNING: + case THREADSTATUS_READY: + LOG_ERROR(Kernel, "Thread with object id %u has already resumed.", GetObjectId()); + _dbg_assert_(Kernel, false); + return; + case THREADSTATUS_DEAD: + // This should never happen, as threads must complete before being stopped. + LOG_CRITICAL(Kernel, "Thread with object id %u cannot be resumed because it's DEAD.", + GetObjectId()); + _dbg_assert_(Kernel, false); + return; } + + ready_queue.push_back(current_priority, this); + status = THREADSTATUS_READY; } -/// Prints the thread queue for debugging purposes +/** + * Prints the thread queue for debugging purposes + */ static void DebugThreadQueue() { Thread* thread = GetCurrentThread(); if (!thread) { - return; + LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD"); + } else { + LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId()); } - LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle()); + for (auto& t : thread_list) { - s32 priority = thread_ready_queue.contains(t.get()); + s32 priority = ready_queue.contains(t.get()); if (priority != -1) { - LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle()); + LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); } } } ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, - u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size) { - if (stack_size < 0x200) { - LOG_ERROR(Kernel, "(name=%s): invalid stack_size=0x%08X", name.c_str(), stack_size); - // TODO: Verify error - return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Kernel, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); - } - + u32 arg, s32 processor_id, VAddr stack_top) { if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", @@ -362,22 +358,13 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, SharedPtr<Thread> thread(new Thread); - // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for - // the time being. Create a handle here, it will be copied to the handle field in - // the object and use by the rest of the code. This should be removed when other - // code doesn't rely on the handle anymore. - ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread); - if (handle.Failed()) - return handle.Code(); - thread_list.push_back(thread); - thread_ready_queue.prepare(priority); + ready_queue.prepare(priority); - thread->thread_id = next_thread_id++; + thread->thread_id = NewThreadId(); thread->status = THREADSTATUS_DORMANT; thread->entry_point = entry_point; thread->stack_top = stack_top; - thread->stack_size = stack_size; thread->initial_priority = thread->current_priority = priority; thread->processor_id = processor_id; thread->wait_set_output = false; @@ -385,88 +372,86 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); + thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); + + // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used + // to initialize the context + Core::g_app_core->ResetContext(thread->context, stack_top, entry_point, arg); - ResetThread(thread.get(), arg, 0); - CallThread(thread.get()); + ready_queue.push_back(thread->current_priority, thread.get()); + thread->status = THREADSTATUS_READY; return MakeResult<SharedPtr<Thread>>(std::move(thread)); } -/// Set the priority of the thread specified by handle -void Thread::SetPriority(s32 priority) { - // If priority is invalid, clamp to valid range - if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { - s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); - LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority); +// TODO(peachum): Remove this. Range checking should be done, and an appropriate error should be returned. +static void ClampPriority(const Thread* thread, s32* priority) { + if (*priority < THREADPRIO_HIGHEST || *priority > THREADPRIO_LOWEST) { + _dbg_assert_msg_(Kernel, false, "Application passed an out of range priority. An error should be returned."); + + s32 new_priority = CLAMP(*priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); + LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", + thread->name.c_str(), *priority, new_priority); // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm // validity of this - priority = new_priority; + *priority = new_priority; } +} - // Change thread priority - s32 old = current_priority; - thread_ready_queue.remove(old, this); - current_priority = priority; - thread_ready_queue.prepare(current_priority); +void Thread::SetPriority(s32 priority) { + ClampPriority(this, &priority); - // Change thread status to "ready" and push to ready queue - if (IsRunning()) { - status = (status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; + if (current_priority == priority) { + return; } - if (IsReady()) { - thread_ready_queue.push_back(current_priority, this); + + if (status == THREADSTATUS_READY) { + // If thread was ready, adjust queues + ready_queue.remove(current_priority, this); + ready_queue.prepare(priority); + ready_queue.push_back(priority, this); } + + current_priority = priority; } -Handle SetupIdleThread() { +SharedPtr<Thread> SetupIdleThread() { // We need to pass a few valid values to get around parameter checking in Thread::Create. - auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, - THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE); - _dbg_assert_(Kernel, thread_res.Succeeded()); - SharedPtr<Thread> thread = std::move(*thread_res); + auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, + THREADPROCESSORID_0, 0).MoveFrom(); thread->idle = true; - CallThread(thread.get()); - return thread->GetHandle(); + return thread; } -SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { +SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority) { + _dbg_assert_(Kernel, !GetCurrentThread()); + // Initialize new "main" thread - auto thread_res = Thread::Create("main", Core::g_app_core->GetPC(), priority, 0, - THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - // TODO(yuriks): Propagate error - _dbg_assert_(Kernel, thread_res.Succeeded()); - SharedPtr<Thread> thread = std::move(*thread_res); - - // If running another thread already, set it to "ready" state - Thread* cur = GetCurrentThread(); - if (cur && cur->IsRunning()) { - ChangeReadyState(cur, true); - } + auto thread_res = Thread::Create("main", entry_point, priority, 0, + THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END); + + SharedPtr<Thread> thread = thread_res.MoveFrom(); // Run new "main" thread - current_thread = thread.get(); - thread->status = THREADSTATUS_RUNNING; - Core::g_app_core->LoadContext(thread->context); + SwitchContext(thread.get()); return thread; } - -/// Reschedules to the next available thread (call after current thread is suspended) void Reschedule() { Thread* prev = GetCurrentThread(); - Thread* next = NextThread(); + Thread* next = PopNextReadyThread(); HLE::g_reschedule = false; if (next != nullptr) { - LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); + LOG_TRACE(Kernel, "context switch %u -> %u", prev->GetObjectId(), next->GetObjectId()); SwitchContext(next); } else { - LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); + LOG_TRACE(Kernel, "cannot context switch from %u, no higher priority thread!", prev->GetObjectId()); for (auto& thread : thread_list) { - LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X", thread->GetHandle(), + LOG_TRACE(Kernel, "\tid=%u prio=0x%02X, status=0x%08X", thread->GetObjectId(), thread->current_priority, thread->status); } } @@ -483,8 +468,10 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { - next_thread_id = INITIAL_THREAD_ID; ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); + + // Setup the idle thread + SetupIdleThread(); } void ThreadingShutdown() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index d6299364a..cfd073a70 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <boost/container/flat_set.hpp> + #include "common/common_types.h" #include "core/core.h" @@ -29,21 +31,34 @@ enum ThreadProcessorId { }; enum ThreadStatus { - THREADSTATUS_RUNNING = 1, - THREADSTATUS_READY = 2, - THREADSTATUS_WAIT = 4, - THREADSTATUS_SUSPEND = 8, - THREADSTATUS_DORMANT = 16, - THREADSTATUS_DEAD = 32, - THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND + THREADSTATUS_RUNNING, ///< Currently running + THREADSTATUS_READY, ///< Ready to run + THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter + THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC + THREADSTATUS_WAIT_SYNCH, ///< Waiting due to a WaitSynchronization SVC + THREADSTATUS_DORMANT, ///< Created but not yet made ready + THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated }; namespace Kernel { +class Mutex; + class Thread final : public WaitObject { public: + /** + * Creates and returns a new thread. The new thread is immediately scheduled + * @param name The friendly name desired for the thread + * @param entry_point The address at which the thread should start execution + * @param priority The thread's priority + * @param arg User data to pass to the thread + * @param processor_id The ID(s) of the processors on which the thread is desired to be run + * @param stack_top The address of the thread's stack top + * @param stack_size The size of the thread's stack + * @return A shared pointer to the newly created thread + */ static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, - u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); + u32 arg, s32 processor_id, VAddr stack_top); std::string GetName() const override { return name; } std::string GetTypeName() const override { return "Thread"; } @@ -51,22 +66,32 @@ public: static const HandleType HANDLE_TYPE = HandleType::Thread; HandleType GetHandleType() const override { return HANDLE_TYPE; } - inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } - inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } - inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } - inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } - inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } - inline bool IsIdle() const { return idle; } - bool ShouldWait() override; void Acquire() override; + /** + * Checks if the thread is an idle (stub) thread + * @return True if the thread is an idle (stub) thread, false otherwise + */ + inline bool IsIdle() const { return idle; } + + /** + * Gets the thread's current priority + * @return The current thread's priority + */ s32 GetPriority() const { return current_priority; } + + /** + * Sets the thread's current priority + * @param priority The new priority + */ void SetPriority(s32 priority); + /** + * Gets the thread's thread ID + * @return The thread's ID + */ u32 GetThreadId() const { return thread_id; } - - void Stop(const char* reason); /** * Release an acquired wait object @@ -74,10 +99,18 @@ public: */ void ReleaseWaitObject(WaitObject* wait_object); - /// Resumes a thread from waiting by marking it as "ready" + /** + * Resumes a thread from waiting + */ void ResumeFromWait(); /** + * Schedules an event to wake up the specified thread after the specified delay + * @param nanoseconds The time this thread will be allowed to sleep for + */ + void WakeAfterDelay(s64 nanoseconds); + + /** * Sets the result after the thread awakens (from either WaitSynchronization SVC) * @param result Value to set to the returned result */ @@ -89,6 +122,11 @@ public: */ void SetWaitSynchronizationOutput(s32 output); + /** + * Stops a thread, invalidating it from further use + */ + void Stop(); + Core::ThreadContext context; u32 thread_id; @@ -96,15 +134,16 @@ public: u32 status; u32 entry_point; u32 stack_top; - u32 stack_size; s32 initial_priority; s32 current_priority; s32 processor_id; - std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on + /// Mutexes currently held by this thread, which will be released when it exits. + boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; + std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address bool wait_all; ///< True if the thread is waiting on all objects before resuming bool wait_set_output; ///< True if the output parameter should be set on thread wakeup @@ -115,34 +154,58 @@ public: bool idle = false; private: - Thread() = default; + Thread(); + ~Thread() override; + + /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. + Handle callback_handle; }; -/// Sets up the primary application thread -SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); +extern SharedPtr<Thread> g_main_thread; + +/** + * Sets up the primary application thread + * @param stack_size The size of the thread's stack + * @param entry_point The address at which the thread should start execution + * @param priority The priority to give the main thread + * @return A shared pointer to the main thread + */ +SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority); -/// Reschedules to the next available thread (call after current thread is suspended) +/** + * Reschedules to the next available thread (call after current thread is suspended) + */ void Reschedule(); -/// Arbitrate the highest priority thread that is waiting +/** + * Arbitrate the highest priority thread that is waiting + * @param address The address for which waiting threads should be arbitrated + */ Thread* ArbitrateHighestPriorityThread(u32 address); -/// Arbitrate all threads currently waiting... +/** + * Arbitrate all threads currently waiting. + * @param address The address for which waiting threads should be arbitrated + */ void ArbitrateAllThreads(u32 address); -/// Gets the current thread +/** + * Gets the current thread + */ Thread* GetCurrentThread(); -/// Waits the current thread on a sleep +/** + * Waits the current thread on a sleep + */ void WaitCurrentThread_Sleep(); /** * Waits the current thread from a WaitSynchronization call - * @param wait_object Kernel object that we are waiting on + * @param wait_objects Kernel objects that we are waiting on * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only) * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) */ -void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all); +void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, bool wait_set_output, bool wait_all); /** * Waits the current thread from an ArbitrateAddress call @@ -151,24 +214,21 @@ void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bo void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); /** - * Schedules an event to wake up the specified thread after the specified delay. - * @param handle The thread handle. - * @param nanoseconds The time this thread will be allowed to sleep for. - */ -void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); - -/** * Sets up the idle thread, this is a thread that is intended to never execute instructions, * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue * and will try to yield on every call. - * @returns The handle of the idle thread + * @return The handle of the idle thread */ -Handle SetupIdleThread(); +SharedPtr<Thread> SetupIdleThread(); -/// Initialize threading +/** + * Initialize threading + */ void ThreadingInit(); -/// Shutdown threading +/** + * Shutdown threading + */ void ThreadingShutdown(); } // namespace diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 503a5d2ce..4352fc99c 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <set> - #include "common/common.h" #include "core/core_timing.h" @@ -15,18 +13,24 @@ namespace Kernel { /// The event type of the generic timer callback event static int timer_callback_event_type = -1; +// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing +// us to simply use a pool index or similar. +static Kernel::HandleTable timer_callback_handle_table; + +Timer::Timer() {} +Timer::~Timer() {} -ResultVal<SharedPtr<Timer>> Timer::Create(ResetType reset_type, std::string name) { +SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) { SharedPtr<Timer> timer(new Timer); - // TOOD(yuriks): Don't create Handle (see Thread::Create()) - CASCADE_RESULT(auto unused, Kernel::g_handle_table.Create(timer)); timer->reset_type = reset_type; timer->signaled = false; timer->name = std::move(name); timer->initial_delay = 0; timer->interval_delay = 0; - return MakeResult<SharedPtr<Timer>>(timer); + timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); + + return timer; } bool Timer::ShouldWait() { @@ -38,17 +42,19 @@ void Timer::Acquire() { } void Timer::Set(s64 initial, s64 interval) { + // Ensure we get rid of any previous scheduled event + Cancel(); + initial_delay = initial; interval_delay = interval; u64 initial_microseconds = initial / 1000; - // TODO(yuriks): Figure out a replacement for GetHandle here - CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type, - GetHandle()); + CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), + timer_callback_event_type, callback_handle); } void Timer::Cancel() { - CoreTiming::UnscheduleEvent(timer_callback_event_type, GetHandle()); + CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle); } void Timer::Clear() { @@ -57,7 +63,7 @@ void Timer::Clear() { /// The timer callback event, called when a timer is fired static void TimerCallback(u64 timer_handle, int cycles_late) { - SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(timer_handle); + SharedPtr<Timer> timer = timer_callback_handle_table.Get<Timer>(timer_handle); if (timer == nullptr) { LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08X", timer_handle); diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index c45e79954..540e4e187 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -19,7 +19,7 @@ public: * @param name Optional name of timer * @return The created Timer */ - static ResultVal<SharedPtr<Timer>> Create(ResetType reset_type, std::string name = "Unknown"); + static SharedPtr<Timer> Create(ResetType reset_type, std::string name = "Unknown"); std::string GetTypeName() const override { return "Timer"; } std::string GetName() const override { return name; } @@ -49,7 +49,11 @@ public: void Clear(); private: - Timer() = default; + Timer(); + ~Timer() override; + + /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. + Handle callback_handle; }; /// Initializes the required variables for timers |
