diff options
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 19 | ||||
-rw-r--r-- | src/core/hle/kernel/address_arbiter.h | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/archive.cpp | 429 | ||||
-rw-r--r-- | src/core/hle/kernel/archive.h | 85 | ||||
-rw-r--r-- | src/core/hle/kernel/event.cpp | 17 | ||||
-rw-r--r-- | src/core/hle/kernel/event.h | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.cpp | 139 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.h | 217 | ||||
-rw-r--r-- | src/core/hle/kernel/mutex.cpp | 108 | ||||
-rw-r--r-- | src/core/hle/kernel/mutex.h | 8 | ||||
-rw-r--r-- | src/core/hle/kernel/semaphore.cpp | 95 | ||||
-rw-r--r-- | src/core/hle/kernel/semaphore.h | 32 | ||||
-rw-r--r-- | src/core/hle/kernel/session.h | 58 | ||||
-rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 23 | ||||
-rw-r--r-- | src/core/hle/kernel/shared_memory.h | 16 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.cpp | 136 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 22 |
17 files changed, 604 insertions, 804 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index db571b895..38705e3cd 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/common_types.h" @@ -20,16 +20,10 @@ public: std::string GetTypeName() const override { return "Arbiter"; } std::string GetName() const override { return name; } - static Kernel::HandleType GetStaticHandleType() { return HandleType::AddressArbiter; } - Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; } + static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; + HandleType GetHandleType() const override { return HANDLE_TYPE; } std::string name; ///< Name of address arbiter object (optional) - - ResultVal<bool> WaitSynchronization() override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::OS); - } }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -53,13 +47,13 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); + Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); HLE::Reschedule(__func__); } break; default: - ERROR_LOG(KERNEL, "unknown type=%d", type); + LOG_ERROR(Kernel, "unknown type=%d", type); return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage); } return RESULT_SUCCESS; @@ -68,7 +62,8 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 /// Create an address arbiter AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) { AddressArbiter* address_arbiter = new AddressArbiter; - handle = Kernel::g_object_pool.Create(address_arbiter); + // TOOD(yuriks): Fix error reporting + handle = Kernel::g_handle_table.Create(address_arbiter).ValueOr(INVALID_HANDLE); address_arbiter->name = name; return address_arbiter; } diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 8a5fb10b4..030e7ad7b 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp deleted file mode 100644 index e273444c9..000000000 --- a/src/core/hle/kernel/archive.cpp +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#include "common/common_types.h" -#include "common/file_util.h" -#include "common/math_util.h" - -#include "core/file_sys/archive.h" -#include "core/file_sys/archive_sdmc.h" -#include "core/file_sys/directory.h" -#include "core/hle/kernel/archive.h" -#include "core/hle/result.h" -#include "core/hle/service/service.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -// Command to access archive file -enum class FileCommand : u32 { - Dummy1 = 0x000100C6, - Control = 0x040100C4, - OpenSubFile = 0x08010100, - Read = 0x080200C2, - Write = 0x08030102, - GetSize = 0x08040000, - SetSize = 0x08050080, - GetAttributes = 0x08060000, - SetAttributes = 0x08070040, - Close = 0x08080000, - Flush = 0x08090000, -}; - -// Command to access directory -enum class DirectoryCommand : u32 { - Dummy1 = 0x000100C6, - Control = 0x040100C4, - Read = 0x08010042, - Close = 0x08020000, -}; - -class Archive : public Object { -public: - std::string GetTypeName() const override { return "Archive"; } - std::string GetName() const override { return name; } - - static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; } - Kernel::HandleType GetHandleType() const override { return HandleType::Archive; } - - std::string name; ///< Name of archive (optional) - FileSys::Archive* backend; ///< Archive backend interface - - ResultVal<bool> SyncRequest() override { - u32* cmd_buff = Service::GetCommandBuffer(); - FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); - - switch (cmd) { - // Read from archive... - case FileCommand::Read: - { - u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - u32 length = cmd_buff[3]; - u32 address = cmd_buff[5]; - - // Number of bytes read - cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); - break; - } - // Write to archive... - case FileCommand::Write: - { - u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - u32 length = cmd_buff[3]; - u32 flush = cmd_buff[4]; - u32 address = cmd_buff[6]; - - // Number of bytes written - cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); - break; - } - case FileCommand::GetSize: - { - u64 filesize = (u64) backend->GetSize(); - cmd_buff[2] = (u32) filesize; // Lower word - cmd_buff[3] = (u32) (filesize >> 32); // Upper word - break; - } - case FileCommand::SetSize: - { - backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32)); - break; - } - case FileCommand::Close: - { - DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - CloseArchive(backend->GetIdCode()); - break; - } - // Unknown command... - default: - { - ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); - return UnimplementedFunction(ErrorModule::FS); - } - } - cmd_buff[1] = 0; // No error - return MakeResult<bool>(false); - } - - ResultVal<bool> WaitSynchronization() override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::FS); - } -}; - -class File : public Object { -public: - std::string GetTypeName() const override { return "File"; } - std::string GetName() const override { return path.DebugStr(); } - - static Kernel::HandleType GetStaticHandleType() { return HandleType::File; } - Kernel::HandleType GetHandleType() const override { return HandleType::File; } - - FileSys::Path path; ///< Path of the file - std::unique_ptr<FileSys::File> backend; ///< File backend interface - - ResultVal<bool> SyncRequest() override { - u32* cmd_buff = Service::GetCommandBuffer(); - FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); - switch (cmd) { - - // Read from file... - case FileCommand::Read: - { - u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; - u32 length = cmd_buff[3]; - u32 address = cmd_buff[5]; - DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x", - GetTypeName().c_str(), GetName().c_str(), offset, length, address); - cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); - break; - } - - // Write to file... - case FileCommand::Write: - { - u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; - u32 length = cmd_buff[3]; - u32 flush = cmd_buff[4]; - u32 address = cmd_buff[6]; - DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", - GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); - cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); - break; - } - - case FileCommand::GetSize: - { - DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); - u64 size = backend->GetSize(); - cmd_buff[2] = (u32)size; - cmd_buff[3] = size >> 32; - break; - } - - case FileCommand::SetSize: - { - u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", - GetTypeName().c_str(), GetName().c_str(), size); - backend->SetSize(size); - break; - } - - case FileCommand::Close: - { - DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - Kernel::g_object_pool.Destroy<File>(GetHandle()); - break; - } - - // Unknown command... - default: - ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); - ResultCode error = UnimplementedFunction(ErrorModule::FS); - cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. - return error; - } - cmd_buff[1] = 0; // No error - return MakeResult<bool>(false); - } - - ResultVal<bool> WaitSynchronization() override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::FS); - } -}; - -class Directory : public Object { -public: - std::string GetTypeName() const override { return "Directory"; } - std::string GetName() const override { return path.DebugStr(); } - - static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; } - Kernel::HandleType GetHandleType() const override { return HandleType::Directory; } - - FileSys::Path path; ///< Path of the directory - std::unique_ptr<FileSys::Directory> backend; ///< File backend interface - - ResultVal<bool> SyncRequest() override { - u32* cmd_buff = Service::GetCommandBuffer(); - DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); - switch (cmd) { - - // Read from directory... - case DirectoryCommand::Read: - { - u32 count = cmd_buff[1]; - u32 address = cmd_buff[3]; - auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); - DEBUG_LOG(KERNEL, "Read %s %s: count=%d", - GetTypeName().c_str(), GetName().c_str(), count); - - // Number of entries actually read - cmd_buff[2] = backend->Read(count, entries); - break; - } - - case DirectoryCommand::Close: - { - DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - Kernel::g_object_pool.Destroy<Directory>(GetHandle()); - break; - } - - // Unknown command... - default: - ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); - ResultCode error = UnimplementedFunction(ErrorModule::FS); - cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. - return error; - } - cmd_buff[1] = 0; // No error - return MakeResult<bool>(false); - } - - ResultVal<bool> WaitSynchronization() override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::FS); - } -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode - -ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code) { - auto itr = g_archive_map.find(id_code); - if (itr == g_archive_map.end()) { - return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Permanent); - } - - return MakeResult<Handle>(itr->second); -} - -ResultCode CloseArchive(FileSys::Archive::IdCode id_code) { - auto itr = g_archive_map.find(id_code); - if (itr == g_archive_map.end()) { - ERROR_LOG(KERNEL, "Cannot close archive %d, does not exist!", (int)id_code); - return InvalidHandle(ErrorModule::FS); - } - - INFO_LOG(KERNEL, "Closed archive %d", (int) id_code); - return RESULT_SUCCESS; -} - -/** - * Mounts an archive - * @param archive Pointer to the archive to mount - */ -ResultCode MountArchive(Archive* archive) { - FileSys::Archive::IdCode id_code = archive->backend->GetIdCode(); - ResultVal<Handle> archive_handle = OpenArchive(id_code); - if (archive_handle.Succeeded()) { - ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code); - return archive_handle.Code(); - } - g_archive_map[id_code] = archive->GetHandle(); - INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str()); - return RESULT_SUCCESS; -} - -ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name) { - Archive* archive = new Archive; - Handle handle = Kernel::g_object_pool.Create(archive); - archive->name = name; - archive->backend = backend; - - ResultCode result = MountArchive(archive); - if (result.IsError()) { - return result; - } - - return RESULT_SUCCESS; -} - -ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { - // TODO(bunnei): Binary type files get a raw file pointer to the archive. Currently, we create - // the archive file handles at app loading, and then keep them persistent throughout execution. - // Archives file handles are just reused and not actually freed until emulation shut down. - // Verify if real hardware works this way, or if new handles are created each time - if (path.GetType() == FileSys::Binary) - // TODO(bunnei): FixMe - this is a hack to compensate for an incorrect FileSys backend - // design. While the functionally of this is OK, our implementation decision to separate - // normal files from archive file pointers is very likely wrong. - // See https://github.com/citra-emu/citra/issues/205 - return MakeResult<Handle>(archive_handle); - - File* file = new File; - Handle handle = Kernel::g_object_pool.Create(file); - - Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); - if (archive == nullptr) { - return InvalidHandle(ErrorModule::FS); - } - file->path = path; - file->backend = archive->backend->OpenFile(path, mode); - - if (!file->backend) { - return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Permanent); - } - - return MakeResult<Handle>(handle); -} - -/** - * Delete a File from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the File inside of the Archive - * @return Whether deletion succeeded - */ -Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); - if (archive == nullptr) - return -1; - if (archive->backend->DeleteFile(path)) - return 0; - return -1; -} - -/** - * Delete a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Whether deletion succeeded - */ -Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); - if (archive == nullptr) - return -1; - if (archive->backend->DeleteDirectory(path)) - return 0; - return -1; -} - -/** - * Create a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Whether creation succeeded - */ -Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); - if (archive == nullptr) - return -1; - if (archive->backend->CreateDirectory(path)) - return 0; - return -1; -} - -/** - * Open a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Opened Directory object - */ -ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { - Directory* directory = new Directory; - Handle handle = Kernel::g_object_pool.Create(directory); - - Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle); - if (archive == nullptr) { - return InvalidHandle(ErrorModule::FS); - } - directory->path = path; - directory->backend = archive->backend->OpenDirectory(path); - - return MakeResult<Handle>(handle); -} - -/// Initialize archives -void ArchiveInit() { - g_archive_map.clear(); - - // TODO(Link Mauve): Add the other archive types (see here for the known types: - // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished - // archive type is SDMC, so it is the only one getting exposed. - - std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); - auto archive = new FileSys::Archive_SDMC(sdmc_directory); - if (archive->Initialize()) - CreateArchive(archive, "SDMC"); - else - ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); -} - -/// Shutdown archives -void ArchiveShutdown() { - g_archive_map.clear(); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h deleted file mode 100644 index 6fc4f0f25..000000000 --- a/src/core/hle/kernel/archive.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" - -#include "core/file_sys/archive.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/result.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -/** - * Opens an archive - * @param id_code IdCode of the archive to open - * @return Handle to the opened archive - */ -ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code); - -/** - * Closes an archive - * @param id_code IdCode of the archive to open - */ -ResultCode CloseArchive(FileSys::Archive::IdCode id_code); - -/** - * Creates an Archive - * @param backend File system backend interface to the archive - * @param name Name of Archive - */ -ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); - -/** - * Open a File from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the File inside of the Archive - * @param mode Mode under which to open the File - * @return Handle to the opened File object - */ -ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); - -/** - * Delete a File from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the File inside of the Archive - * @return Whether deletion succeeded - */ -Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path); - -/** - * Delete a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Whether deletion succeeded - */ -Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); - -/** - * Create a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Whether creation of directory succeeded - */ -Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); - -/** - * Open a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Handle to the opened File object - */ -ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); - -/// Initialize archives -void ArchiveInit(); - -/// Shutdown archives -void ArchiveShutdown(); - -} // namespace FileSys diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 288080209..e43c3ee4e 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include <map> @@ -19,8 +19,8 @@ public: std::string GetTypeName() const override { return "Event"; } std::string GetName() const override { return name; } - static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; } - Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Event; } + static const HandleType HANDLE_TYPE = HandleType::Event; + HandleType GetHandleType() const override { return HANDLE_TYPE; } ResetType intitial_reset_type; ///< ResetType specified at Event initialization ResetType reset_type; ///< Current ResetType @@ -53,7 +53,7 @@ public: * @return Result of operation, 0 on success, otherwise error code */ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { - Event* evt = g_object_pool.Get<Event>(handle); + Event* evt = g_handle_table.Get<Event>(handle); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); evt->permanent_locked = permanent_locked; @@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { * @return Result of operation, 0 on success, otherwise error code */ ResultCode SetEventLocked(const Handle handle, const bool locked) { - Event* evt = g_object_pool.Get<Event>(handle); + Event* evt = g_handle_table.Get<Event>(handle); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); if (!evt->permanent_locked) { @@ -82,7 +82,7 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) { * @return Result of operation, 0 on success, otherwise error code */ ResultCode SignalEvent(const Handle handle) { - Event* evt = g_object_pool.Get<Event>(handle); + Event* evt = g_handle_table.Get<Event>(handle); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); // Resume threads waiting for event to signal @@ -110,7 +110,7 @@ ResultCode SignalEvent(const Handle handle) { * @return Result of operation, 0 on success, otherwise error code */ ResultCode ClearEvent(Handle handle) { - Event* evt = g_object_pool.Get<Event>(handle); + Event* evt = g_handle_table.Get<Event>(handle); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); if (!evt->permanent_locked) { @@ -129,7 +129,8 @@ ResultCode ClearEvent(Handle handle) { Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { Event* evt = new Event; - handle = Kernel::g_object_pool.Create(evt); + // TOOD(yuriks): Fix error reporting + handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE); evt->locked = true; evt->permanent_locked = false; diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 73aec4e79..da793df1a 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 018000abd..e59ed1b57 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -1,106 +1,117 @@ -// Copyright 2014 Citra Emulator Project / PPSSPP Project -// Licensed under GPLv2 +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> + #include "common/common.h" #include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -#include "core/hle/kernel/archive.h" namespace Kernel { Handle g_main_thread = 0; -ObjectPool g_object_pool; +HandleTable g_handle_table; +u64 g_program_id = 0; -ObjectPool::ObjectPool() { - next_id = INITIAL_NEXT_ID; +HandleTable::HandleTable() { + next_generation = 1; + Clear(); } -Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) { - if (range_top > MAX_COUNT) { - range_top = MAX_COUNT; - } - if (next_id >= range_bottom && next_id < range_top) { - range_bottom = next_id++; - } - for (int i = range_bottom; i < range_top; i++) { - if (!occupied[i]) { - occupied[i] = true; - pool[i] = obj; - pool[i]->handle = i + HANDLE_OFFSET; - return i + HANDLE_OFFSET; - } +ResultVal<Handle> HandleTable::Create(Object* obj) { + _dbg_assert_(Kernel, obj != nullptr); + + u16 slot = next_free_slot; + if (slot >= generations.size()) { + LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); + return ERR_OUT_OF_HANDLES; } - ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); - return 0; -} + next_free_slot = generations[slot]; + + u16 generation = next_generation++; -bool ObjectPool::IsValid(Handle handle) { - int index = handle - HANDLE_OFFSET; - if (index < 0) - return false; - if (index >= MAX_COUNT) - return false; + // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. + // CTR-OS doesn't use generation 0, so skip straight to 1. + if (next_generation >= (1 << 15)) next_generation = 1; - return occupied[index]; + generations[slot] = generation; + intrusive_ptr_add_ref(obj); + objects[slot] = obj; + + Handle handle = generation | (slot << 15); + obj->handle = handle; + return MakeResult<Handle>(handle); } -void ObjectPool::Clear() { - for (int i = 0; i < MAX_COUNT; i++) { - //brutally clear everything, no validation - if (occupied[i]) - delete pool[i]; - occupied[i] = false; +ResultVal<Handle> HandleTable::Duplicate(Handle handle) { + Object* object = GetGeneric(handle); + if (object == nullptr) { + LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); + return ERR_INVALID_HANDLE; } - pool.fill(nullptr); - next_id = INITIAL_NEXT_ID; + return Create(object); +} + +ResultCode HandleTable::Close(Handle handle) { + if (!IsValid(handle)) + return ERR_INVALID_HANDLE; + + size_t slot = GetSlot(handle); + u16 generation = GetGeneration(handle); + + intrusive_ptr_release(objects[slot]); + objects[slot] = nullptr; + + generations[generation] = next_free_slot; + next_free_slot = slot; + return RESULT_SUCCESS; } -Object* &ObjectPool::operator [](Handle handle) -{ - _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); - return pool[handle - HANDLE_OFFSET]; +bool HandleTable::IsValid(Handle handle) const { + size_t slot = GetSlot(handle); + u16 generation = GetGeneration(handle); + + return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; } -void ObjectPool::List() { - for (int i = 0; i < MAX_COUNT; i++) { - if (occupied[i]) { - if (pool[i]) { - INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), - pool[i]->GetName().c_str()); - } - } +Object* HandleTable::GetGeneric(Handle handle) const { + if (handle == CurrentThread) { + // TODO(yuriks) Directly return the pointer once this is possible. + handle = GetCurrentThreadHandle(); + } else if (handle == CurrentProcess) { + LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess); + return nullptr; } -} -int ObjectPool::GetCount() { - int count = 0; - for (int i = 0; i < MAX_COUNT; i++) { - if (occupied[i]) - count++; + if (!IsValid(handle)) { + return nullptr; } - return count; + return objects[GetSlot(handle)]; } -Object* ObjectPool::CreateByIDType(int type) { - ERROR_LOG(COMMON, "Unimplemented: %d.", type); - return nullptr; +void HandleTable::Clear() { + for (size_t i = 0; i < MAX_COUNT; ++i) { + generations[i] = i + 1; + if (objects[i] != nullptr) + intrusive_ptr_release(objects[i]); + objects[i] = nullptr; + } + next_free_slot = 0; } /// Initialize the kernel void Init() { Kernel::ThreadingInit(); - Kernel::ArchiveInit(); } /// Shutdown the kernel void Shutdown() { Kernel::ThreadingShutdown(); - Kernel::ArchiveShutdown(); - g_object_pool.Clear(); // Free all kernel objects + g_handle_table.Clear(); // Free all kernel objects } /** @@ -109,8 +120,6 @@ void Shutdown() { * @return True on success, otherwise false */ bool LoadExec(u32 entry_point) { - Init(); - Core::g_app_core->SetPC(entry_point); // 0x30 is the typical main thread priority I've seen used so far diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 8d3937ce8..7f86fd07d 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project / PPSSPP Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once @@ -12,9 +12,17 @@ typedef u32 Handle; typedef s32 Result; +const Handle INVALID_HANDLE = 0; + namespace Kernel { -enum KernelHandle { +// TODO: Verify code +const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, + ErrorSummary::OutOfResource, ErrorLevel::Temporary); +// TOOD: Verify code +const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel); + +enum KernelHandle : Handle { CurrentThread = 0xFFFF8000, CurrentProcess = 0xFFFF8001, }; @@ -22,7 +30,7 @@ enum KernelHandle { enum class HandleType : u32 { Unknown = 0, Port = 1, - Service = 2, + Session = 2, Event = 3, Mutex = 4, SharedMemory = 5, @@ -30,20 +38,17 @@ enum class HandleType : u32 { Thread = 7, Process = 8, AddressArbiter = 9, - File = 10, - Semaphore = 11, - Archive = 12, - Directory = 13, + Semaphore = 10, }; enum { DEFAULT_STACK_SIZE = 0x4000, }; -class ObjectPool; +class HandleTable; class Object : NonCopyable { - friend class ObjectPool; + friend class HandleTable; u32 handle; public: virtual ~Object() {} @@ -53,113 +58,145 @@ public: virtual Kernel::HandleType GetHandleType() const = 0; /** - * Synchronize kernel object. - * @return True if the current thread should wait as a result of the sync + * Wait for kernel object to synchronize. + * @return True if the current thread should wait as a result of the wait */ - virtual ResultVal<bool> SyncRequest() { - ERROR_LOG(KERNEL, "(UNIMPLEMENTED)"); + virtual ResultVal<bool> WaitSynchronization() { + LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); return UnimplementedFunction(ErrorModule::Kernel); } - /** - * Wait for kernel object to synchronize. - * @return True if the current thread should wait as a result of the wait - */ - virtual ResultVal<bool> WaitSynchronization() = 0; +private: + friend void intrusive_ptr_add_ref(Object*); + friend void intrusive_ptr_release(Object*); + + unsigned int ref_count = 0; }; -class ObjectPool : NonCopyable { -public: - ObjectPool(); - ~ObjectPool() {} +// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting +inline void intrusive_ptr_add_ref(Object* object) { + ++object->ref_count; +} - // Allocates a handle within the range and inserts the object into the map. - Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); +inline void intrusive_ptr_release(Object* object) { + if (--object->ref_count == 0) { + delete object; + } +} - static Object* CreateByIDType(int type); +/** + * This class allows the creation of Handles, which are references to objects that can be tested + * for validity and looked up. Here they are used to pass references to kernel objects to/from the + * emulated process. it has been designed so that it follows the same handle format and has + * approximately the same restrictions as the handle manager in the CTR-OS. + * + * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). + * The slot index is used to index into the arrays in this class to access the data corresponding + * to the Handle. + * + * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter + * is kept and incremented every time a Handle is created. This is the Handle's "generation". The + * value of the counter is stored into the Handle as well as in the handle table (in the + * "generations" array). When looking up a handle, the Handle's generation must match with the + * value stored on the class, otherwise the Handle is considered invalid. + * + * To find free slots when allocating a Handle without needing to scan the entire object array, the + * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. + * When a Handle is created, an index is popped off the list and used for the new Handle. When it + * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is + * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been + * verified and isn't likely to cause any problems. + */ +class HandleTable final : NonCopyable { +public: + HandleTable(); - template <class T> - void Destroy(Handle handle) { - if (Get<T>(handle)) { - occupied[handle - HANDLE_OFFSET] = false; - delete pool[handle - HANDLE_OFFSET]; - } - } + /** + * Allocates a handle for the given object. + * @return The created Handle or one of the following errors: + * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. + */ + ResultVal<Handle> Create(Object* obj); - bool IsValid(Handle handle); + /** + * Returns a new handle that points to the same object as the passed in handle. + * @return The duplicated Handle or one of the following errors: + * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. + * - Any errors returned by `Create()`. + */ + ResultVal<Handle> Duplicate(Handle handle); - template <class T> - T* Get(Handle handle) { - if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { - if (handle != 0) { - WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); - } - return nullptr; - } else { - Object* t = pool[handle - HANDLE_OFFSET]; - if (t->GetHandleType() != T::GetStaticHandleType()) { - WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); - return nullptr; - } - return static_cast<T*>(t); - } - } + /** + * Closes a handle, removing it from the table and decreasing the object's ref-count. + * @return `RESULT_SUCCESS` or one of the following errors: + * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. + */ + ResultCode Close(Handle handle); - // ONLY use this when you know the handle is valid. - template <class T> - T *GetFast(Handle handle) { - const Handle realHandle = handle - HANDLE_OFFSET; - _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); - return static_cast<T*>(pool[realHandle]); - } + /// Checks if a handle is valid and points to an existing object. + bool IsValid(Handle handle) const; - template <class T, typename ArgT> - void Iterate(bool func(T*, ArgT), ArgT arg) { - int type = T::GetStaticIDType(); - for (int i = 0; i < MAX_COUNT; i++) - { - if (!occupied[i]) - continue; - T* t = static_cast<T*>(pool[i]); - if (t->GetIDType() == type) { - if (!func(t, arg)) - break; - } - } - } + /** + * Looks up a handle. + * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. + */ + Object* GetGeneric(Handle handle) const; - bool GetIDType(Handle handle, HandleType* type) const { - if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || - !occupied[handle - HANDLE_OFFSET]) { - ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); - return false; + /** + * Looks up a handle while verifying its type. + * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its + * type differs from the handle type `T::HANDLE_TYPE`. + */ + template <class T> + T* Get(Handle handle) const { + Object* object = GetGeneric(handle); + if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { + return static_cast<T*>(object); } - Object* t = pool[handle - HANDLE_OFFSET]; - *type = t->GetHandleType(); - return true; + return nullptr; } - Object* &operator [](Handle handle); - void List(); + /// Closes all handles held in this table. void Clear(); - int GetCount(); private: + /** + * This is the maximum limit of handles allowed per process in CTR-OS. It can be further + * reduced by ExHeader values, but this is not emulated here. + */ + static const size_t MAX_COUNT = 4096; - enum { - MAX_COUNT = 0x1000, - HANDLE_OFFSET = 0x100, - INITIAL_NEXT_ID = 0x10, - }; + static size_t GetSlot(Handle handle) { return handle >> 15; } + static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; } - std::array<Object*, MAX_COUNT> pool; - std::array<bool, MAX_COUNT> occupied; - int next_id; + /// Stores the Object referenced by the handle or null if the slot is empty. + std::array<Object*, MAX_COUNT> objects; + + /** + * The value of `next_generation` when the handle was created, used to check for validity. For + * empty slots, contains the index of the next free slot in the list. + */ + std::array<u16, MAX_COUNT> generations; + + /** + * Global counter of the number of created handles. Stored in `generations` when a handle is + * created, and wraps around to 1 when it hits 0x8000. + */ + u16 next_generation; + + /// Head of the free slots linked list. + u16 next_free_slot; }; -extern ObjectPool g_object_pool; +extern HandleTable g_handle_table; extern Handle g_main_thread; +/// The ID code of the currently running game +/// TODO(Subv): This variable should not be here, +/// we need a way to store information about the currently loaded application +/// for later query during runtime, maybe using the LDR service? +extern u64 g_program_id; + /// Initialize the kernel void Init(); diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index b303ba128..558068c79 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include <map> @@ -18,8 +18,8 @@ public: std::string GetTypeName() const override { return "Mutex"; } std::string GetName() const override { return name; } - static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } - Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Mutex; } + 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 @@ -27,21 +27,7 @@ public: std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex std::string name; ///< Name of mutex (optional) - ResultVal<bool> SyncRequest() override { - // TODO(bunnei): ImplementMe - locked = true; - return MakeResult<bool>(false); - } - - ResultVal<bool> WaitSynchronization() override { - // TODO(bunnei): ImplementMe - bool wait = locked; - if (locked) { - Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); - } - - return MakeResult<bool>(wait); - } + ResultVal<bool> WaitSynchronization() override; }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -49,21 +35,46 @@ public: typedef std::multimap<Handle, Handle> MutexMap; static MutexMap g_mutex_held_locks; -void MutexAcquireLock(Mutex* mutex, Handle thread) { +/** + * Acquires the specified mutex for the specified thread + * @param mutex Mutex that is to be acquired + * @param thread Thread that will acquired + */ +void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); mutex->lock_thread = thread; } -void MutexAcquireLock(Mutex* mutex) { - Handle thread = GetCurrentThreadHandle(); +bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { MutexAcquireLock(mutex, thread); + Kernel::ResumeThreadFromWait(thread); + return true; +} + +/** + * Resumes a thread waiting for the specified mutex + * @param mutex The mutex that some thread is waiting on + */ +void ResumeWaitingThread(Mutex* mutex) { + // Find the next waiting thread for the mutex... + if (mutex->waiting_threads.empty()) { + // Reset mutex lock thread handle, nothing is waiting + mutex->locked = false; + mutex->lock_thread = -1; + } + else { + // Resume the next waiting thread and re-lock the mutex + std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); + ReleaseMutexForThread(mutex, *iter); + mutex->waiting_threads.erase(iter); + } } void MutexEraseLock(Mutex* mutex) { Handle handle = mutex->GetHandle(); auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - if ((*iter).second == handle) { + if (iter->second == handle) { g_mutex_held_locks.erase(iter); break; } @@ -71,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) { mutex->lock_thread = -1; } +void ReleaseThreadMutexes(Handle thread) { + auto locked = g_mutex_held_locks.equal_range(thread); + + // Release every mutex that the thread holds, and resume execution on the waiting threads + for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { + Mutex* mutex = g_handle_table.Get<Mutex>(iter->second); + ResumeWaitingThread(mutex); + } + + // Erase all the locks that this thread holds + g_mutex_held_locks.erase(thread); +} + bool LockMutex(Mutex* mutex) { // Mutex alread locked? if (mutex->locked) { @@ -80,28 +104,10 @@ bool LockMutex(Mutex* mutex) { return true; } -bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { - MutexAcquireLock(mutex, thread); - Kernel::ResumeThreadFromWait(thread); - return true; -} - bool ReleaseMutex(Mutex* mutex) { MutexEraseLock(mutex); - bool woke_threads = false; - - // Find the next waiting thread for the mutex... - while (!woke_threads && !mutex->waiting_threads.empty()) { - std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); - woke_threads |= ReleaseMutexForThread(mutex, *iter); - mutex->waiting_threads.erase(iter); - } - // Reset mutex lock thread handle, nothing is waiting - if (!woke_threads) { - mutex->locked = false; - mutex->lock_thread = -1; - } - return woke_threads; + ResumeWaitingThread(mutex); + return true; } /** @@ -109,7 +115,7 @@ bool ReleaseMutex(Mutex* mutex) { * @param handle Handle to mutex to release */ ResultCode ReleaseMutex(Handle handle) { - Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle); + Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle); if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); if (!ReleaseMutex(mutex)) { @@ -130,7 +136,8 @@ ResultCode ReleaseMutex(Handle handle) { */ Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { Mutex* mutex = new Mutex; - handle = Kernel::g_object_pool.Create(mutex); + // TODO(yuriks): Fix error reporting + handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE); mutex->locked = mutex->initial_locked = initial_locked; mutex->name = name; @@ -158,4 +165,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { return handle; } +ResultVal<bool> Mutex::WaitSynchronization() { + bool wait = locked; + if (locked) { + Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); + } + else { + // Lock the mutex when the first thread accesses it + locked = true; + MutexAcquireLock(this); + } + + return MakeResult<bool>(wait); +} } // namespace diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 155449f95..a8ca97014 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once @@ -24,4 +24,10 @@ ResultCode ReleaseMutex(Handle handle); */ Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); +/** + * Releases all the mutexes held by the specified thread + * @param thread Thread that is holding the mutexes + */ +void ReleaseThreadMutexes(Handle thread); + } // namespace diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp new file mode 100644 index 000000000..6bc8066a6 --- /dev/null +++ b/src/core/hle/kernel/semaphore.cpp @@ -0,0 +1,95 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <queue> + +#include "common/common.h" + +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/semaphore.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +class Semaphore : public Object { +public: + std::string GetTypeName() const override { return "Semaphore"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Semaphore; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have + s32 available_count; ///< Number of free slots left in the semaphore + std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore + std::string name; ///< Name of semaphore (optional) + + /** + * Tests whether a semaphore still has free slots + * @return Whether the semaphore is available + */ + bool IsAvailable() const { + return available_count > 0; + } + + ResultVal<bool> WaitSynchronization() override { + bool wait = !IsAvailable(); + + if (wait) { + Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle()); + waiting_threads.push(GetCurrentThreadHandle()); + } else { + --available_count; + } + + return MakeResult<bool>(wait); + } +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ResultCode CreateSemaphore(Handle* handle, s32 initial_count, + s32 max_count, const std::string& name) { + + if (initial_count > max_count) + return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, + ErrorSummary::WrongArgument, ErrorLevel::Permanent); + + Semaphore* semaphore = new Semaphore; + // TOOD(yuriks): Fix error reporting + *handle = g_handle_table.Create(semaphore).ValueOr(INVALID_HANDLE); + + // When the semaphore is created, some slots are reserved for other threads, + // and the rest is reserved for the caller thread + semaphore->max_count = max_count; + semaphore->available_count = initial_count; + semaphore->name = name; + + return RESULT_SUCCESS; +} + +ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { + Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle); + if (semaphore == nullptr) + return InvalidHandle(ErrorModule::Kernel); + + if (semaphore->max_count - semaphore->available_count < release_count) + return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + + *count = semaphore->available_count; + semaphore->available_count += release_count; + + // Notify some of the threads that the semaphore has been released + // stop once the semaphore is full again or there are no more waiting threads + while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { + Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front()); + semaphore->waiting_threads.pop(); + --semaphore->available_count; + } + + return RESULT_SUCCESS; +} + +} // namespace diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h new file mode 100644 index 000000000..8644ecf0c --- /dev/null +++ b/src/core/hle/kernel/semaphore.h @@ -0,0 +1,32 @@ +// 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" + +namespace Kernel { + +/** + * Creates a semaphore. + * @param handle Pointer to the handle of the newly created object + * @param initial_count Number of slots reserved for other threads + * @param max_count Maximum number of slots the semaphore can have + * @param name Optional name of semaphore + * @return ResultCode of the error + */ +ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown"); + +/** + * Releases a certain number of slots from a semaphore. + * @param count The number of free slots the semaphore had before this call + * @param handle The handle of the semaphore to release + * @param release_count The number of slots to release + * @return ResultCode of the error + */ +ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count); + +} // namespace diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h new file mode 100644 index 000000000..91f3ffc2c --- /dev/null +++ b/src/core/hle/kernel/session.h @@ -0,0 +1,58 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header + +/** + * Returns a pointer to the command buffer in kernel memory + * @param offset Optional offset into command buffer + * @return Pointer to command buffer + */ +inline static u32* GetCommandBuffer(const int offset=0) { + return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); +} + +/** + * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS + * primitive for communication between different processes, and are used to implement service calls + * to the various system services. + * + * To make a service call, the client must write the command header and parameters to the buffer + * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest + * SVC call with its Session handle. The kernel will read the command header, using it to marshall + * the parameters to the process at the server endpoint of the session. After the server replies to + * the request, the response is marshalled back to the caller's TLS buffer and control is + * transferred back to it. + * + * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC + * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called + * with the session handle, this class's SyncRequest method is called, which should read the TLS + * buffer and emulate the call accordingly. Since the code can directly read the emulated memory, + * no parameter marshalling is done. + * + * In the long term, this should be turned into the full-fledged IPC mechanism implemented by + * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as + * opposed to HLE simulations. + */ +class Session : public Object { +public: + std::string GetTypeName() const override { return "Session"; } + + static const HandleType HANDLE_TYPE = HandleType::Session; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + /** + * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls + * aren't supported yet. + */ + virtual ResultVal<bool> SyncRequest() = 0; +}; + +} diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index cfcc0e0b7..cea1f6fa1 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/common.h" @@ -13,14 +13,8 @@ class SharedMemory : public Object { public: std::string GetTypeName() const override { return "SharedMemory"; } - static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; } - Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; } - - ResultVal<bool> WaitSynchronization() override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::OS); - } + static const HandleType HANDLE_TYPE = HandleType::SharedMemory; + HandleType GetHandleType() const override { return HANDLE_TYPE; } u32 base_address; ///< Address of shared memory block in RAM MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) @@ -38,7 +32,8 @@ public: */ SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { SharedMemory* shared_memory = new SharedMemory; - handle = Kernel::g_object_pool.Create(shared_memory); + // TOOD(yuriks): Fix error reporting + handle = Kernel::g_handle_table.Create(shared_memory).ValueOr(INVALID_HANDLE); shared_memory->name = name; return shared_memory; } @@ -61,12 +56,12 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions MemoryPermission other_permissions) { if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { - ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", + LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", handle, address); return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } - SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); + SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); shared_memory->base_address = address; @@ -77,13 +72,13 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions } ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { - SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); + SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); if (0 != shared_memory->base_address) return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); - ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle); + LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle); // 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 304cf5b67..bb65c7ccd 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once @@ -12,11 +12,15 @@ namespace Kernel { /// Permissions for mapped shared memory blocks enum class MemoryPermission : u32 { - None = 0, - Read = (1u << 0), - Write = (1u << 1), - ReadWrite = (Read | Write), - DontCare = (1u << 28) + None = 0, + Read = (1u << 0), + Write = (1u << 1), + ReadWrite = (Read | Write), + Execute = (1u << 2), + ReadExecute = (Read | Execute), + WriteExecute = (Write | Execute), + ReadWriteExecute = (Read | Write | Execute), + DontCare = (1u << 28) }; /** diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index f3f54a4e9..872df2d14 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project / PPSSPP Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include <algorithm> @@ -14,6 +14,7 @@ #include "core/hle/hle.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/mutex.h" #include "core/hle/result.h" #include "core/mem_map.h" @@ -25,8 +26,8 @@ public: std::string GetName() const override { return name; } std::string GetTypeName() const override { return "Thread"; } - static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } - Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Thread; } + 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; } @@ -49,6 +50,8 @@ public: ThreadContext context; + u32 thread_id; + u32 status; u32 entry_point; u32 stack_top; @@ -61,6 +64,7 @@ public: WaitType wait_type; Handle wait_handle; + VAddr wait_address; std::vector<Handle> waiting_threads; @@ -76,8 +80,10 @@ static Common::ThreadQueueList<Handle> thread_ready_queue; static Handle current_thread_handle; static Thread* current_thread; -/// Gets the current thread -inline Thread* GetCurrentThread() { +static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup +static u32 next_thread_id; ///< The next available thread id + +Thread* GetCurrentThread() { return current_thread; } @@ -121,6 +127,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { } t->wait_type = WAITTYPE_NONE; t->wait_handle = 0; + t->wait_address = 0; } /// Change a thread to "ready" state @@ -140,30 +147,43 @@ void ChangeReadyState(Thread* t, bool ready) { } } -/// Verify that a thread has not been released from waiting -inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { - _dbg_assert_(KERNEL, thread != nullptr); - return type == thread->wait_type && wait_handle == thread->wait_handle; +/// Check if a thread is blocking on a specified wait type +static bool CheckWaitType(const Thread* thread, WaitType type) { + return (type == thread->wait_type) && (thread->IsWaiting()); +} + +/// Check if a thread is blocking on a specified wait type with a specified handle +static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { + return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle); +} + +/// Check if a thread is blocking on a specified wait type with a specified handle and address +static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { + return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address); } /// Stops the current thread ResultCode StopThread(Handle handle, const char* reason) { - Thread* thread = g_object_pool.Get<Thread>(handle); + Thread* thread = g_handle_table.Get<Thread>(handle); if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); + // Release all the mutexes that this thread holds + ReleaseThreadMutexes(handle); + ChangeReadyState(thread, false); thread->status = THREADSTATUS_DORMANT; for (Handle waiting_handle : thread->waiting_threads) { - Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); - if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { + Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle); + + if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle)) ResumeThreadFromWait(waiting_handle); - } } thread->waiting_threads.clear(); // Stopped threads are never waiting. thread->wait_type = WAITTYPE_NONE; thread->wait_handle = 0; + thread->wait_address = 0; return RESULT_SUCCESS; } @@ -178,7 +198,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { if (new_status == THREADSTATUS_WAIT) { if (t->wait_type == WAITTYPE_NONE) { - ERROR_LOG(KERNEL, "Waittype none not allowed"); + LOG_ERROR(Kernel, "Waittype none not allowed"); } } } @@ -190,14 +210,14 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (Handle handle : thread_queue) { - Thread* thread = g_object_pool.Get<Thread>(handle); + Thread* thread = g_handle_table.Get<Thread>(handle); - // TODO(bunnei): Verify arbiter address... - if (!VerifyWait(thread, WAITTYPE_ARB, arbiter)) + if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) continue; if (thread == nullptr) continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. + if(thread->current_priority <= priority) { highest_priority_thread = handle; priority = thread->current_priority; @@ -215,10 +235,9 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) { // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (Handle handle : thread_queue) { - Thread* thread = g_object_pool.Get<Thread>(handle); + Thread* thread = g_handle_table.Get<Thread>(handle); - // TODO(bunnei): Verify arbiter address... - if (VerifyWait(thread, WAITTYPE_ARB, arbiter)) + if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) ResumeThreadFromWait(handle); } } @@ -269,14 +288,9 @@ Thread* NextThread() { if (next == 0) { return nullptr; } - return Kernel::g_object_pool.Get<Thread>(next); + return Kernel::g_handle_table.Get<Thread>(next); } -/** - * Puts the current thread in the wait state for the given type - * @param wait_type Type of wait - * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread - */ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { Thread* thread = GetCurrentThread(); thread->wait_type = wait_type; @@ -284,11 +298,18 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } +void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { + WaitCurrentThread(wait_type, wait_handle); + GetCurrentThread()->wait_address = wait_address; +} + /// Resumes a thread from waiting by marking it as "ready" void ResumeThreadFromWait(Handle handle) { - Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); + Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); if (thread) { thread->status &= ~THREADSTATUS_WAIT; + thread->wait_handle = 0; + thread->wait_type = WAITTYPE_NONE; if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { ChangeReadyState(thread, true); } @@ -301,12 +322,12 @@ void DebugThreadQueue() { if (!thread) { return; } - INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); + LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); for (u32 i = 0; i < thread_queue.size(); i++) { Handle handle = thread_queue[i]; s32 priority = thread_ready_queue.contains(handle); if (priority != -1) { - INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); + LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle); } } } @@ -316,15 +337,17 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio s32 processor_id, u32 stack_top, int stack_size) { _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), - "CreateThread priority=%d, outside of allowable range!", priority) + "priority=%d, outside of allowable range!", priority) Thread* thread = new Thread; - handle = Kernel::g_object_pool.Create(thread); + // TOOD(yuriks): Fix error reporting + handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE); thread_queue.push_back(handle); thread_ready_queue.prepare(priority); + thread->thread_id = next_thread_id++; thread->status = THREADSTATUS_DORMANT; thread->entry_point = entry_point; thread->stack_top = stack_top; @@ -333,6 +356,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio thread->processor_id = processor_id; thread->wait_type = WAITTYPE_NONE; thread->wait_handle = 0; + thread->wait_address = 0; thread->name = name; return thread; @@ -343,24 +367,24 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 u32 stack_top, int stack_size) { if (name == nullptr) { - ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); + LOG_ERROR(Kernel_SVC, "nullptr name"); return -1; } if ((u32)stack_size < 0x200) { - ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, + LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name, stack_size); return -1; } if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); - WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", + LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", name, priority, new_priority); // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm // validity of this priority = new_priority; } if (!Memory::GetPointer(entry_point)) { - ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); + LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); return -1; } Handle handle; @@ -375,7 +399,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 /// Get the priority of the thread specified by handle ResultVal<u32> GetThreadPriority(const Handle handle) { - Thread* thread = g_object_pool.Get<Thread>(handle); + Thread* thread = g_handle_table.Get<Thread>(handle); if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); return MakeResult<u32>(thread->current_priority); @@ -387,7 +411,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { if (!handle) { thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? } else { - thread = g_object_pool.Get<Thread>(handle); + thread = g_handle_table.Get<Thread>(handle); if (thread == nullptr) { return InvalidHandle(ErrorModule::Kernel); } @@ -397,7 +421,7 @@ ResultCode SetThreadPriority(Handle handle, 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); - WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); + LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority); // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm // validity of this priority = new_priority; @@ -450,24 +474,44 @@ void Reschedule() { Thread* prev = GetCurrentThread(); Thread* next = NextThread(); HLE::g_reschedule = false; - if (next > 0) { - INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); + if (next != nullptr) { + LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); SwitchContext(next); + } else { + LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); - // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep - // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. - // This results in the current thread yielding on a VBLANK once, and then it will be - // immediately placed back in the queue for execution. - if (prev->wait_type == WAITTYPE_VBLANK) { - ResumeThreadFromWait(prev->GetHandle()); + for (Handle handle : thread_queue) { + Thread* thread = g_handle_table.Get<Thread>(handle); + LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", + thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); } } + + // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put + // to sleep. So, we'll just immediately set it to "ready" again after an attempted context + // switch has occurred. This results in the current thread yielding on a sleep once, and then it + // will immediately be placed back in the queue for execution. + + if (CheckWaitType(prev, WAITTYPE_SLEEP)) + ResumeThreadFromWait(prev->GetHandle()); +} + +ResultCode GetThreadId(u32* thread_id, Handle handle) { + Thread* thread = g_handle_table.Get<Thread>(handle); + if (thread == nullptr) + return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, + ErrorSummary::WrongArgument, ErrorLevel::Permanent); + + *thread_id = thread->thread_id; + + return RESULT_SUCCESS; } //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { + next_thread_id = INITIAL_THREAD_ID; } void ThreadingShutdown() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index ce63a70d3..0e1397cd9 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -1,10 +1,13 @@ // Copyright 2014 Citra Emulator Project / PPSSPP Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include "common/common_types.h" + +#include "core/mem_map.h" + #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" @@ -37,7 +40,6 @@ enum WaitType { WAITTYPE_SEMA, WAITTYPE_EVENT, WAITTYPE_THREADEND, - WAITTYPE_VBLANK, WAITTYPE_MUTEX, WAITTYPE_SYNCH, WAITTYPE_ARB, @@ -58,6 +60,14 @@ void Reschedule(); /// Stops the current thread ResultCode StopThread(Handle thread, const char* reason); +/** + * Retrieves the ID of the specified thread handle + * @param thread_id Will contain the output thread id + * @param handle Handle to the thread we want + * @return Whether the function was successful or not + */ +ResultCode GetThreadId(u32* thread_id, Handle handle); + /// Resumes a thread from waiting by marking it as "ready" void ResumeThreadFromWait(Handle handle); @@ -77,6 +87,14 @@ Handle GetCurrentThreadHandle(); */ void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); +/** + * Puts the current thread in the wait state for the given type + * @param wait_type Type of wait + * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread + * @param wait_address Arbitration address used to resume from wait + */ +void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); + /// Put current thread in a wait state - on WaitSynchronization void WaitThread_Synchronization(); |