aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp21
-rw-r--r--src/core/hle/kernel/address_arbiter.h4
-rw-r--r--src/core/hle/kernel/archive.cpp436
-rw-r--r--src/core/hle/kernel/archive.h70
-rw-r--r--src/core/hle/kernel/event.cpp47
-rw-r--r--src/core/hle/kernel/event.h14
-rw-r--r--src/core/hle/kernel/kernel.cpp29
-rw-r--r--src/core/hle/kernel/kernel.h83
-rw-r--r--src/core/hle/kernel/mutex.cpp124
-rw-r--r--src/core/hle/kernel/mutex.h11
-rw-r--r--src/core/hle/kernel/semaphore.cpp94
-rw-r--r--src/core/hle/kernel/semaphore.h32
-rw-r--r--src/core/hle/kernel/session.h58
-rw-r--r--src/core/hle/kernel/shared_memory.cpp53
-rw-r--r--src/core/hle/kernel/shared_memory.h7
-rw-r--r--src/core/hle/kernel/thread.cpp215
-rw-r--r--src/core/hle/kernel/thread.h28
17 files changed, 505 insertions, 821 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 2b21657d..9a921108 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -24,23 +24,12 @@ public:
Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; }
std::string name; ///< Name of address arbiter object (optional)
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Arbitrate an address
-Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
switch (type) {
// Signal thread(s) waiting for arbitrate address...
@@ -58,16 +47,16 @@ Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 va
// 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);
- return -1;
+ LOG_ERROR(Kernel, "unknown type=%d", type);
+ return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Create an address arbiter
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index a483fe46..8a5fb10b 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -11,7 +11,7 @@
// Address arbiters are an underlying kernel synchronization object that can be created/used via
// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
-// semphores.
+// semphores.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
@@ -28,7 +28,7 @@ enum class ArbitrationType : u32 {
};
/// Arbitrate an address
-Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
/// Create an address arbiter
Handle CreateAddressArbiter(const std::string& name = "Unknown");
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
deleted file mode 100644
index 764082d7..00000000
--- a/src/core/hle/kernel/archive.cpp
+++ /dev/null
@@ -1,436 +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/service/service.h"
-#include "core/hle/kernel/archive.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
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) 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());
- Kernel::g_object_pool.Destroy<Archive>(GetHandle());
- CloseArchive(backend->GetIdCode());
- break;
- }
- // Unknown command...
- default:
- {
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- return -1;
- }
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-class File : public Object {
-public:
- std::string GetTypeName() const override { return "File"; }
- std::string GetName() const override { return path; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
- Kernel::HandleType GetHandleType() const override { return HandleType::File; }
-
- std::string path; ///< Path of the file
- std::unique_ptr<FileSys::File> backend; ///< File backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) 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);
- cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
- return -1;
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-class Directory : public Object {
-public:
- std::string GetTypeName() const override { return "Directory"; }
- std::string GetName() const override { return path; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
- Kernel::HandleType GetHandleType() const override { return HandleType::Directory; }
-
- std::string path; ///< Path of the directory
- std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) 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];
- FileSys::Entry* 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);
- cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
- return -1;
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
-
-/**
- * Opens an archive
- * @param id_code IdCode of the archive to open
- * @return Handle to archive if it exists, otherwise a null handle (0)
- */
-Handle OpenArchive(FileSys::Archive::IdCode id_code) {
- auto itr = g_archive_map.find(id_code);
- if (itr == g_archive_map.end()) {
- return 0;
- }
- return itr->second;
-}
-
-/**
- * Closes an archive
- * @param id_code IdCode of the archive to open
- * @return Result of operation, 0 on success, otherwise error code
- */
-Result CloseArchive(FileSys::Archive::IdCode id_code) {
- if (1 != g_archive_map.erase(id_code)) {
- ERROR_LOG(KERNEL, "Cannot close archive %d", (int) id_code);
- return -1;
- }
-
- INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
- return 0;
-}
-
-/**
- * Mounts an archive
- * @param archive Pointer to the archive to mount
- * @return Result of operation, 0 on success, otherwise error code
- */
-Result MountArchive(Archive* archive) {
- FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
- if (0 != OpenArchive(id_code)) {
- ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
- return -1;
- }
- g_archive_map[id_code] = archive->GetHandle();
- INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
- return 0;
-}
-
-/**
- * Creates an Archive
- * @param handle Handle to newly created archive object
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Newly created Archive object
- */
-Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
- Archive* archive = new Archive;
- handle = Kernel::g_object_pool.Create(archive);
- archive->name = name;
- archive->backend = backend;
-
- MountArchive(archive);
-
- return archive;
-}
-
-/**
- * Creates an Archive
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Handle to newly created Archive object
- */
-Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
- Handle handle;
- CreateArchive(handle, backend, name);
- return handle;
-}
-
-/**
- * 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 Opened File object
- */
-Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) {
- File* file = new File;
- Handle handle = Kernel::g_object_pool.Create(file);
-
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- file->path = path;
- file->backend = archive->backend->OpenFile(path, mode);
-
- if (!file->backend)
- return 0;
-
- return handle;
-}
-
-/**
- * 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 Opened Directory object
- */
-Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& 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
- */
-Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& path) {
- Directory* directory = new Directory;
- Handle handle = Kernel::g_object_pool.Create(directory);
-
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- directory->path = path;
- directory->backend = archive->backend->OpenDirectory(path);
-
- return 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 0230996b..00000000
--- a/src/core/hle/kernel/archive.h
+++ /dev/null
@@ -1,70 +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/hle/kernel/kernel.h"
-#include "core/file_sys/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Kernel namespace
-
-namespace Kernel {
-
-/**
- * Opens an archive
- * @param id_code IdCode of the archive to open
- * @return Handle to archive if it exists, otherwise a null handle (0)
- */
-Handle OpenArchive(FileSys::Archive::IdCode id_code);
-
-/**
- * Closes an archive
- * @param id_code IdCode of the archive to open
- * @return true if it worked fine
- */
-Result CloseArchive(FileSys::Archive::IdCode id_code);
-
-/**
- * Creates an Archive
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Handle to newly created Archive object
- */
-Handle 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 Opened File object
- */
-Handle OpenFileFromArchive(Handle archive_handle, const std::string& name, const FileSys::Mode mode);
-
-/**
- * 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 std::string& name);
-
-/**
- * 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
- */
-Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& name);
-
-/// 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 45ed79be..28808020 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
#include <algorithm>
@@ -30,13 +30,8 @@ public:
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
std::string name; ///< Name of event (optional)
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- *wait = locked;
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = locked;
if (locked) {
Handle thread = GetCurrentThreadHandle();
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
@@ -47,7 +42,7 @@ public:
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
locked = true;
}
- return 0;
+ return MakeResult<bool>(wait);
}
};
@@ -57,12 +52,12 @@ public:
* @param permanent_locked Boolean permanent locked value to set event
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SetPermanentLock(Handle handle, const bool permanent_locked) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
evt->permanent_locked = permanent_locked;
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -71,14 +66,14 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
* @param locked Boolean locked value to set event
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SetEventLocked(const Handle handle, const bool locked) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SetEventLocked(const Handle handle, const bool locked) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
evt->locked = locked;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -86,16 +81,16 @@ Result SetEventLocked(const Handle handle, const bool locked) {
* @param handle Handle to event to signal
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SignalEvent(const Handle handle) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SignalEvent(const Handle handle) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
// Resume threads waiting for event to signal
bool event_caught = false;
for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
ResumeThreadFromWait( evt->waiting_threads[i]);
- // If any thread is signalled awake by this event, assume the event was "caught" and reset
+ // If any thread is signalled awake by this event, assume the event was "caught" and reset
// the event. This will result in the next thread waiting on the event to block. Otherwise,
// the event will not be reset, and the next thread to call WaitSynchronization on it will
// not block. Not sure if this is correct behavior, but it seems to work.
@@ -106,7 +101,7 @@ Result SignalEvent(const Handle handle) {
if (!evt->permanent_locked) {
evt->locked = event_caught;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -114,14 +109,14 @@ Result SignalEvent(const Handle handle) {
* @param handle Handle to event to clear
* @return Result of operation, 0 on success, otherwise error code
*/
-Result ClearEvent(Handle handle) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode ClearEvent(Handle handle) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
evt->locked = true;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index c39b3318..73aec4e7 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -15,31 +15,27 @@ namespace Kernel {
* Changes whether an event is locked or not
* @param handle Handle to event to change
* @param locked Boolean locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SetEventLocked(const Handle handle, const bool locked);
+ResultCode SetEventLocked(const Handle handle, const bool locked);
/**
* Hackish function to set an events permanent lock state, used to pass through synch blocks
* @param handle Handle to event to change
* @param permanent_locked Boolean permanent locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SetPermanentLock(Handle handle, const bool permanent_locked);
+ResultCode SetPermanentLock(Handle handle, const bool permanent_locked);
/**
* Signals an event
* @param handle Handle to event to signal
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SignalEvent(const Handle handle);
+ResultCode SignalEvent(const Handle handle);
/**
* Clears an event
* @param handle Handle to event to clear
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result ClearEvent(Handle handle);
+ResultCode ClearEvent(Handle handle);
/**
* Creates an event
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 88cbc1af..6a690e91 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1,18 +1,20 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// 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;
+u64 g_program_id = 0;
ObjectPool::ObjectPool() {
next_id = INITIAL_NEXT_ID;
@@ -33,11 +35,11 @@ Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) {
return i + HANDLE_OFFSET;
}
}
- ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use.");
+ LOG_ERROR(Kernel, "Unable to allocate kernel object, too many objects slots in use.");
return 0;
}
-bool ObjectPool::IsValid(Handle handle) {
+bool ObjectPool::IsValid(Handle handle) const {
int index = handle - HANDLE_OFFSET;
if (index < 0)
return false;
@@ -60,7 +62,7 @@ void ObjectPool::Clear() {
Object* &ObjectPool::operator [](Handle handle)
{
- _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
+ _dbg_assert_msg_(Kernel, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
return pool[handle - HANDLE_OFFSET];
}
@@ -68,37 +70,30 @@ 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(),
+ LOG_DEBUG(Kernel, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(),
pool[i]->GetName().c_str());
}
}
}
}
-int ObjectPool::GetCount() {
- int count = 0;
- for (int i = 0; i < MAX_COUNT; i++) {
- if (occupied[i])
- count++;
- }
- return count;
+int ObjectPool::GetCount() const {
+ return std::count(occupied.begin(), occupied.end(), true);
}
Object* ObjectPool::CreateByIDType(int type) {
- ERROR_LOG(COMMON, "Unimplemented: %d.", type);
+ LOG_ERROR(Kernel, "Unimplemented: %d.", type);
return nullptr;
}
/// 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
}
@@ -109,8 +104,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 867d1b89..7123485b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -1,12 +1,13 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include "common/common.h"
+#include "core/hle/result.h"
typedef u32 Handle;
typedef s32 Result;
@@ -21,7 +22,7 @@ enum KernelHandle {
enum class HandleType : u32 {
Unknown = 0,
Port = 1,
- Service = 2,
+ Session = 2,
Event = 3,
Mutex = 4,
SharedMemory = 5,
@@ -29,12 +30,9 @@ 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,
};
@@ -52,21 +50,13 @@ public:
virtual Kernel::HandleType GetHandleType() const = 0;
/**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
+ * Wait for kernel object to synchronize.
+ * @return True if the current thread should wait as a result of the wait
*/
- virtual Result SyncRequest(bool* wait) {
- ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
- return -1;
+ virtual ResultVal<bool> WaitSynchronization() {
+ LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
+ return UnimplementedFunction(ErrorModule::Kernel);
}
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- virtual Result WaitSynchronization(bool* wait) = 0;
};
class ObjectPool : NonCopyable {
@@ -80,38 +70,29 @@ public:
static Object* CreateByIDType(int type);
template <class T>
- u32 Destroy(Handle handle) {
- u32 error;
- if (Get<T>(handle, error)) {
+ void Destroy(Handle handle) {
+ if (Get<T>(handle)) {
occupied[handle - HANDLE_OFFSET] = false;
delete pool[handle - HANDLE_OFFSET];
}
- return error;
- };
+ }
- bool IsValid(Handle handle);
+ bool IsValid(Handle handle) const;
template <class T>
- T* Get(Handle handle, u32& outError) {
+ T* Get(Handle handle) {
if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
- // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
- if (handle != 0 && (u32)handle != 0x80020001) {
- WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ if (handle != 0) {
+ LOG_ERROR(Kernel, "Bad object handle %08x", handle);
}
- outError = 0;//T::GetMissingErrorCode();
- return 0;
+ return nullptr;
} else {
- // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
- // it just acted as a static case and everything worked. This means that we will never
- // see the Wrong type object error below, but we'll just have to live with that danger.
- T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]);
- if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) {
- WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
- outError = 0;//T::GetMissingErrorCode();
- return 0;
+ Object* t = pool[handle - HANDLE_OFFSET];
+ if (t->GetHandleType() != T::GetStaticHandleType()) {
+ LOG_ERROR(Kernel, "Wrong object type for %08x", handle);
+ return nullptr;
}
- outError = 0;//SCE_KERNEL_ERROR_OK;
- return t;
+ return static_cast<T*>(t);
}
}
@@ -119,7 +100,7 @@ public:
template <class T>
T *GetFast(Handle handle) {
const Handle realHandle = handle - HANDLE_OFFSET;
- _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
+ _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
return static_cast<T*>(pool[realHandle]);
}
@@ -139,9 +120,9 @@ public:
}
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);
+ if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
+ !occupied[handle - HANDLE_OFFSET]) {
+ LOG_ERROR(Kernel, "Bad object handle %08X", handle);
return false;
}
Object* t = pool[handle - HANDLE_OFFSET];
@@ -152,10 +133,10 @@ public:
Object* &operator [](Handle handle);
void List();
void Clear();
- int GetCount();
+ int GetCount() const;
private:
-
+
enum {
MAX_COUNT = 0x1000,
HANDLE_OFFSET = 0x100,
@@ -170,6 +151,12 @@ private:
extern ObjectPool g_object_pool;
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 fcfd061a..5a173e12 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
#include <vector>
@@ -27,32 +27,7 @@ public:
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
std::string name; ///< Name of mutex (optional)
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- // TODO(bunnei): ImplementMe
- locked = true;
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- *wait = locked;
-
- if (locked) {
- Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
- }
-
- return 0;
- }
+ ResultVal<bool> WaitSynchronization() override;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -60,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;
}
@@ -82,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_object_pool.GetFast<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) {
@@ -91,43 +104,27 @@ 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;
}
/**
* Releases a mutex
* @param handle Handle to mutex to release
*/
-Result ReleaseMutex(Handle handle) {
- Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
-
- _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
+ResultCode ReleaseMutex(Handle handle) {
+ Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle);
+ if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!ReleaseMutex(mutex)) {
- return -1;
+ // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure
+ // what error condition this is supposed to be signaling.
+ return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel,
+ ErrorSummary::NothingHappened, ErrorLevel::Temporary);
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -167,4 +164,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 7d7b5137..7f4909a6 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -13,9 +13,8 @@ namespace Kernel {
/**
* Releases a mutex
* @param handle Handle to mutex to release
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result ReleaseMutex(Handle handle);
+ResultCode ReleaseMutex(Handle handle);
/**
* Creates a mutex
@@ -25,4 +24,10 @@ Result 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 00000000..6f56da8a
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -0,0 +1,94 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// 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 Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; }
+ Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; }
+
+ u32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
+ u32 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, u32 initial_count,
+ u32 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;
+ *handle = g_object_pool.Create(semaphore);
+
+ // 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_object_pool.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 00000000..f0075fdb
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.h
@@ -0,0 +1,32 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// 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, u32 initial_count, u32 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 00000000..06ae4bc3
--- /dev/null
+++ b/src/core/hle/kernel/session.h
@@ -0,0 +1,58 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// 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 Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Session; }
+ Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Session; }
+
+ /**
+ * 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 6bd5e272..3c8c502c 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "common/common.h"
@@ -16,17 +16,6 @@ public:
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; }
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-
u32 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)
@@ -48,11 +37,6 @@ SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
return shared_memory;
}
-/**
- * Creates a shared memory object
- * @param name Optional name of shared memory object
- * @return Handle of newly created shared memory object
- */
Handle CreateSharedMemory(const std::string& name) {
Handle handle;
CreateSharedMemory(handle, name);
@@ -67,39 +51,36 @@ Handle CreateSharedMemory(const std::string& name) {
* @param other_permissions Memory block map other permissions (specified by SVC field)
* @return Result of operation, 0 on success, otherwise error code
*/
-Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+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!",
- handle);
- return -1;
+ 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.GetFast<SharedMemory>(handle);
- _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+ SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
shared_memory->base_address = address;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
- return 0;
+ return RESULT_SUCCESS;
}
-/**
- * Gets a pointer to the shared memory block
- * @param handle Shared memory block handle
- * @param offset Offset from the start of the shared memory block to get pointer
- * @return Pointer to the shared memory block from the specified offset
- */
-u8* GetSharedMemoryPointer(Handle handle, u32 offset) {
- SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
- _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
+ SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (0 != shared_memory->base_address)
- return Memory::GetPointer(shared_memory->base_address + offset);
+ return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
- ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle);
- return nullptr;
+ 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);
}
} // namespace
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 6204d8a4..bb778ec2 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -36,9 +36,8 @@ Handle CreateSharedMemory(const std::string& name="Unknown");
* @param address Address in system memory to map shared memory block to
* @param permissions Memory block map permissions (specified by SVC field)
* @param other_permissions Memory block map other permissions (specified by SVC field)
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions,
MemoryPermission other_permissions);
/**
@@ -47,6 +46,6 @@ Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
* @param offset Offset from the start of the shared memory block to get pointer
* @return Pointer to the shared memory block from the specified offset
*/
-u8* GetSharedMemoryPointer(Handle handle, u32 offset);
+ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset);
} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index e15590c4..1c04701d 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <algorithm>
#include <list>
@@ -11,10 +11,12 @@
#include "common/thread_queue_list.h"
#include "core/core.h"
-#include "core/mem_map.h"
#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"
namespace Kernel {
@@ -33,25 +35,23 @@ public:
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- if (status != THREADSTATUS_DORMANT) {
+ ResultVal<bool> WaitSynchronization() override {
+ const bool wait = status != THREADSTATUS_DORMANT;
+ if (wait) {
Handle thread = GetCurrentThreadHandle();
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
waiting_threads.push_back(thread);
}
WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
- *wait = true;
}
- return 0;
+
+ return MakeResult<bool>(wait);
}
ThreadContext context;
+ u32 thread_id;
+
u32 status;
u32 entry_point;
u32 stack_top;
@@ -64,6 +64,7 @@ public:
WaitType wait_type;
Handle wait_handle;
+ VAddr wait_address;
std::vector<Handle> waiting_threads;
@@ -71,17 +72,20 @@ public:
};
// Lists all thread ids that aren't deleted/etc.
-std::vector<Handle> g_thread_queue;
+static std::vector<Handle> thread_queue;
// Lists only ready thread ids.
-Common::ThreadQueueList<Handle> g_thread_ready_queue;
+static Common::ThreadQueueList<Handle> thread_ready_queue;
+
+static Handle current_thread_handle;
+static Thread* current_thread;
-Handle g_current_thread_handle;
-Thread* g_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
/// Gets the current thread
inline Thread* GetCurrentThread() {
- return g_current_thread;
+ return current_thread;
}
/// Gets the current thread handle
@@ -91,8 +95,8 @@ Handle GetCurrentThreadHandle() {
/// Sets the current thread
inline void SetCurrentThread(Thread* t) {
- g_current_thread = t;
- g_current_thread_handle = t->GetHandle();
+ current_thread = t;
+ current_thread_handle = t->GetHandle();
}
/// Saves the current CPU context
@@ -113,7 +117,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
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.
@@ -124,6 +128,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
@@ -131,40 +136,44 @@ void ChangeReadyState(Thread* t, bool ready) {
Handle handle = t->GetHandle();
if (t->IsReady()) {
if (!ready) {
- g_thread_ready_queue.remove(t->current_priority, handle);
+ thread_ready_queue.remove(t->current_priority, handle);
}
} else if (ready) {
if (t->IsRunning()) {
- g_thread_ready_queue.push_front(t->current_priority, handle);
+ thread_ready_queue.push_front(t->current_priority, handle);
} else {
- g_thread_ready_queue.push_back(t->current_priority, handle);
+ thread_ready_queue.push_back(t->current_priority, handle);
}
t->status = THREADSTATUS_READY;
}
}
/// Verify that a thread has not been released from waiting
-inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
- if (type != thread->wait_type || wait_handle != thread->wait_handle)
- return false;
+static 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) && (thread->IsWaiting());
+}
- return true;
+/// Verify that a thread has not been released from waiting (with wait address)
+static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
+ _dbg_assert_(Kernel, thread != nullptr);
+ return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address);
}
/// Stops the current thread
-void StopThread(Handle handle, const char* reason) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
+ResultCode StopThread(Handle handle, const char* reason) {
+ Thread* thread = g_object_pool.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 (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
- const Handle waiting_thread = thread->waiting_threads[i];
+ for (Handle waiting_handle : thread->waiting_threads) {
+ Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle);
if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
- ResumeThreadFromWait(waiting_thread);
+ ResumeThreadFromWait(waiting_handle);
}
}
thread->waiting_threads.clear();
@@ -172,6 +181,9 @@ void StopThread(Handle handle, const char* reason) {
// Stopped threads are never waiting.
thread->wait_type = WAITTYPE_NONE;
thread->wait_handle = 0;
+ thread->wait_address = 0;
+
+ return RESULT_SUCCESS;
}
/// Changes a threads state
@@ -181,10 +193,10 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
}
ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
t->status = 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");
}
}
}
@@ -195,13 +207,15 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
s32 priority = THREADPRIO_LOWEST;
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (const auto& handle : g_thread_queue) {
+ for (Handle handle : thread_queue) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
- // TODO(bunnei): Verify arbiter address...
- if (!VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
continue;
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ 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;
@@ -216,12 +230,12 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
/// Arbitrate all threads currently waiting
void ArbitrateAllThreads(u32 arbiter, u32 address) {
-
+
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (const auto& handle : g_thread_queue) {
+ for (Handle handle : thread_queue) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
- // TODO(bunnei): Verify arbiter address...
- if (VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
ResumeThreadFromWait(handle);
}
}
@@ -238,11 +252,11 @@ void CallThread(Thread* t) {
/// Switches CPU context to that of the specified thread
void SwitchContext(Thread* t) {
Thread* cur = GetCurrentThread();
-
+
// Save context for current thread
if (cur) {
SaveContext(cur->context);
-
+
if (cur->IsRunning()) {
ChangeReadyState(cur, true);
}
@@ -263,23 +277,18 @@ void SwitchContext(Thread* t) {
Thread* NextThread() {
Handle next;
Thread* cur = GetCurrentThread();
-
+
if (cur && cur->IsRunning()) {
- next = g_thread_ready_queue.pop_first_better(cur->current_priority);
+ next = thread_ready_queue.pop_first_better(cur->current_priority);
} else {
- next = g_thread_ready_queue.pop_first();
+ next = thread_ready_queue.pop_first();
}
if (next == 0) {
return nullptr;
}
- return Kernel::g_object_pool.GetFast<Thread>(next);
+ return Kernel::g_object_pool.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;
@@ -287,10 +296,14 @@ 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) {
- u32 error;
- Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
+ Thread* thread = Kernel::g_object_pool.Get<Thread>(handle);
if (thread) {
thread->status &= ~THREADSTATUS_WAIT;
if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
@@ -305,12 +318,12 @@ void DebugThreadQueue() {
if (!thread) {
return;
}
- INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
- for (u32 i = 0; i < g_thread_queue.size(); i++) {
- Handle handle = g_thread_queue[i];
- s32 priority = g_thread_ready_queue.contains(handle);
+ 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);
}
}
}
@@ -319,16 +332,17 @@ void DebugThreadQueue() {
Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
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)
+ _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
+ "priority=%d, outside of allowable range!", priority)
Thread* thread = new Thread;
handle = Kernel::g_object_pool.Create(thread);
- g_thread_queue.push_back(handle);
- g_thread_ready_queue.prepare(priority);
+ 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;
@@ -337,6 +351,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;
@@ -347,28 +362,28 @@ 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;
- Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
+ Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
stack_size);
ResetThread(thread, arg, 0);
@@ -378,26 +393,30 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
}
/// Get the priority of the thread specified by handle
-u32 GetThreadPriority(const Handle handle) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
- return thread->current_priority;
+ResultVal<u32> GetThreadPriority(const Handle handle) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
+
+ return MakeResult<u32>(thread->current_priority);
}
/// Set the priority of the thread specified by handle
-Result SetThreadPriority(Handle handle, s32 priority) {
+ResultCode SetThreadPriority(Handle handle, s32 priority) {
Thread* thread = nullptr;
if (!handle) {
thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
} else {
- thread = g_object_pool.GetFast<Thread>(handle);
+ thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) {
+ return InvalidHandle(ErrorModule::Kernel);
+ }
}
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
// 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;
@@ -405,37 +424,37 @@ Result SetThreadPriority(Handle handle, s32 priority) {
// Change thread priority
s32 old = thread->current_priority;
- g_thread_ready_queue.remove(old, handle);
+ thread_ready_queue.remove(old, handle);
thread->current_priority = priority;
- g_thread_ready_queue.prepare(thread->current_priority);
+ thread_ready_queue.prepare(thread->current_priority);
// Change thread status to "ready" and push to ready queue
if (thread->IsRunning()) {
thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
}
if (thread->IsReady()) {
- g_thread_ready_queue.push_back(thread->current_priority, handle);
+ thread_ready_queue.push_back(thread->current_priority, handle);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Sets up the primary application thread
Handle SetupMainThread(s32 priority, int stack_size) {
Handle handle;
-
+
// Initialize new "main" thread
- Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
+ Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
-
+
ResetThread(thread, 0, 0);
-
+
// If running another thread already, set it to "ready" state
Thread* cur = GetCurrentThread();
if (cur && cur->IsRunning()) {
ChangeReadyState(cur, true);
}
-
+
// Run new "main" thread
SetCurrentThread(thread);
thread->status = THREADSTATUS_RUNNING;
@@ -451,13 +470,13 @@ void Reschedule() {
Thread* next = NextThread();
HLE::g_reschedule = false;
if (next > 0) {
- INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
-
+ LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
+
SwitchContext(next);
// 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
+ // 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());
@@ -465,9 +484,21 @@ void Reschedule() {
}
}
+ResultCode GetThreadId(u32* thread_id, Handle handle) {
+ Thread* thread = g_object_pool.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 39fa38b7..be7adfac 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -1,11 +1,15 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// 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"
enum ThreadPriority {
THREADPRIO_HIGHEST = 0, ///< Highest thread priority
@@ -55,7 +59,15 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
void Reschedule();
/// Stops the current thread
-void StopThread(Handle thread, const char* reason);
+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);
@@ -76,14 +88,22 @@ 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();
/// Get the priority of the thread specified by handle
-u32 GetThreadPriority(const Handle handle);
+ResultVal<u32> GetThreadPriority(const Handle handle);
/// Set the priority of the thread specified by handle
-Result SetThreadPriority(Handle handle, s32 priority);
+ResultCode SetThreadPriority(Handle handle, s32 priority);
/// Initialize threading
void ThreadingInit();