mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-27 14:45:30 +03:00
Address CR Comments (#132) + Change Core Migration API
This addresses all CR comments including more codebase-wide changes arising from certain review comments like proper usage of its/it's and consistent contraction of it is into it's. An overhaul was made to the presentation and formatting of `KThread.h` and `LoadBalance` works has been superseded by `GetOptimalCoreForThread` which can be used alongside `InsertThread` or `MigrateToCore`. It makes the API far more atomic and neater. This was a major point of contention for the design prior, it's simplified some code and potentially improved performance.
This commit is contained in:
parent
0ea02f2d56
commit
fe5061a8e0
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
@ -171,7 +171,7 @@
|
||||
</inspection_tool>
|
||||
<inspection_tool class="CheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ClangTidy" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="clangTidyChecks" value="-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bad-signal-to-kill-thread,bugprone-branch-clone,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-dynamic-static-initializers,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-incorrect-roundings,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-parentheses,bugprone-macro-repeated-side-effects,bugprone-misplaced-operator-in-strlen-in-alloc,bugprone-misplaced-pointer-arithmetic-in-alloc,bugprone-misplaced-widening-cast,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-no-escape,bugprone-not-null-terminated-result,bugprone-parent-virtual-call,bugprone-posix-return,bugprone-reserved-identifier,bugprone-sizeof-container,bugprone-sizeof-expression,bugprone-spuriously-wake-up-functions,bugprone-string-constructor,bugprone-string-integer-assignment,bugprone-string-literal-with-embedded-nul,bugprone-suspicious-enum-usage,bugprone-suspicious-include,bugprone-suspicious-memset-usage,bugprone-suspicious-missing-comma,bugprone-suspicious-semicolon,bugprone-suspicious-string-compare,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-throw-keyword-missing,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-raii,bugprone-unused-return-value,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err52-cpp,cert-err58-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,misc-misplaced-const,misc-new-delete-overloads,misc-no-recursion,misc-non-copyable-objects,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,modernize-avoid-bind,modernize-concat-nested-namespaces,modernize-deprecated-headers,modernize-deprecated-ios-base-aliases,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-replace-disallow-copy-and-assign-macro,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,mpi-buffer-deref,mpi-type-mismatch,openmp-use-default-none,performance-faster-string-find,performance-for-range-copy,performance-implicit-conversion-in-loop,performance-inefficient-algorithm,performance-inefficient-string-concatenation,performance-inefficient-vector-operation,performance-move-const-arg,performance-move-constructor-init,performance-no-automatic-move,performance-noexcept-move-constructor,performance-trivially-destructible,performance-type-promotion-in-math-fn,performance-unnecessary-copy-initialization,performance-unnecessary-value-param,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-make-member-function-const,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-redundant-string-cstr,readability-redundant-string-init,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof" />
|
||||
<option name="clangTidyChecks" value="-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bad-signal-to-kill-thread,bugprone-branch-clone,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-dynamic-static-initializers,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-incorrect-roundings,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-parentheses,bugprone-macro-repeated-side-effects,bugprone-misplaced-operator-in-strlen-in-alloc,bugprone-misplaced-pointer-arithmetic-in-alloc,bugprone-misplaced-widening-cast,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-no-escape,bugprone-not-null-terminated-result,bugprone-parent-virtual-call,bugprone-posix-return,bugprone-reserved-identifier,bugprone-sizeof-container,bugprone-sizeof-expression,bugprone-spuriously-wake-up-functions,bugprone-string-constructor,bugprone-string-integer-assignment,bugprone-string-literal-with-embedded-nul,bugprone-suspicious-enum-usage,bugprone-suspicious-include,bugprone-suspicious-memset-usage,bugprone-suspicious-missing-comma,bugprone-suspicious-semicolon,bugprone-suspicious-string-compare,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-throw-keyword-missing,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-raii,bugprone-unused-return-value,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err58-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cert-str34-c,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,misc-misplaced-const,misc-new-delete-overloads,misc-no-recursion,misc-non-copyable-objects,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,modernize-avoid-bind,modernize-concat-nested-namespaces,modernize-deprecated-ios-base-aliases,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-replace-disallow-copy-and-assign-macro,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,mpi-buffer-deref,mpi-type-mismatch,openmp-use-default-none,performance-faster-string-find,performance-for-range-copy,performance-implicit-conversion-in-loop,performance-inefficient-algorithm,performance-inefficient-string-concatenation,performance-inefficient-vector-operation,performance-move-const-arg,performance-move-constructor-init,performance-no-automatic-move,performance-noexcept-move-constructor,performance-trivially-destructible,performance-type-promotion-in-math-fn,performance-unnecessary-copy-initialization,performance-unnecessary-value-param,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-make-member-function-const,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-redundant-string-cstr,readability-redundant-string-init,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ClassComplexity" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_limit" value="80" />
|
||||
|
@ -22,7 +22,7 @@ std::weak_ptr<skyline::gpu::GPU> GpuWeak;
|
||||
std::weak_ptr<skyline::input::Input> InputWeak;
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring) {
|
||||
skyline::signal::ScopedStackBlocker stackBlocker;
|
||||
skyline::signal::ScopedStackBlocker stackBlocker; // We do not want anything to unwind past JNI code as there are invalid stack frames which can lead to a segmentation fault
|
||||
Fps = FrameTime = 0;
|
||||
|
||||
pthread_setname_np(pthread_self(), "EmuMain");
|
||||
|
@ -41,7 +41,7 @@ namespace skyline {
|
||||
|
||||
void Logger::Write(LogLevel level, const std::string& str) {
|
||||
constexpr std::array<char, 5> levelCharacter{'E', 'W', 'I', 'D', 'V'}; // The LogLevel as written out to a file
|
||||
constexpr std::array<int, 5> levelAlog{ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, ANDROID_LOG_DEBUG, ANDROID_LOG_VERBOSE}; // This corresponds to LogLevel and provides it's equivalent for NDK Logging
|
||||
constexpr std::array<int, 5> levelAlog{ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, ANDROID_LOG_DEBUG, ANDROID_LOG_VERBOSE}; // This corresponds to LogLevel and provides its equivalent for NDK Logging
|
||||
|
||||
if (logTag.empty())
|
||||
UpdateTag();
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <compare>
|
||||
#include <sys/mman.h>
|
||||
#include <fmt/format.h>
|
||||
#include <frozen/unordered_map.h>
|
||||
@ -68,7 +69,7 @@ namespace skyline {
|
||||
};
|
||||
|
||||
/**
|
||||
* @note Success is 0, 0 - it is the only error that's not specific to a module
|
||||
* @note Success is 0, it's the only result that's not specific to a module
|
||||
*/
|
||||
Result() = default;
|
||||
|
||||
@ -299,7 +300,7 @@ namespace skyline {
|
||||
constexpr span(const std::span<T, Extent> &spn) : std::span<T, Extent>(spn) {}
|
||||
|
||||
/**
|
||||
* @brief We want to support implicitly casting from std::string_view -> span as it is just a specialization of a data view which span is a generic form of, the opposite doesn't hold true as not all data held by a span is string data therefore the conversion isn't implicit there
|
||||
* @brief We want to support implicitly casting from std::string_view -> span as it's just a specialization of a data view which span is a generic form of, the opposite doesn't hold true as not all data held by a span is string data therefore the conversion isn't implicit there
|
||||
*/
|
||||
template<typename Traits>
|
||||
constexpr span(const std::basic_string_view<T, Traits> &string) : std::span<T, Extent>(const_cast<T *>(string.data()), string.size()) {}
|
||||
|
@ -21,6 +21,8 @@ namespace skyline {
|
||||
PREF_ELEM("log_level", logLevel, static_cast<Logger::LogLevel>(element.text().as_uint(static_cast<unsigned int>(Logger::LogLevel::Info)))),
|
||||
};
|
||||
|
||||
#undef PREF_ELEM
|
||||
|
||||
std::bitset<std::tuple_size_v<typeof(preferences)>> preferencesSet{}; // A bitfield to keep track of all the preferences we've set
|
||||
for (auto element{document.last_child().first_child()}; element; element = element.next_sibling()) {
|
||||
std::string_view name{element.attribute("name").value()};
|
||||
|
@ -10,10 +10,7 @@ namespace skyline::signal {
|
||||
thread_local std::exception_ptr SignalExceptionPtr;
|
||||
|
||||
void ExceptionThrow() {
|
||||
// We need the compiler to not remove the asm at the end of 'std::rethrow_exception' which is a noreturn function
|
||||
volatile bool alwaysTrue{true};
|
||||
if (alwaysTrue)
|
||||
std::rethrow_exception(SignalExceptionPtr);
|
||||
std::rethrow_exception(SignalExceptionPtr);
|
||||
}
|
||||
|
||||
std::terminate_handler terminateHandler{};
|
||||
@ -41,6 +38,7 @@ namespace skyline::signal {
|
||||
|
||||
static void *exceptionThrowEnd{};
|
||||
if (!exceptionThrowEnd) {
|
||||
// We need to find the function bounds for ExceptionThrow, if we haven't already
|
||||
u32 *it{reinterpret_cast<u32 *>(&ExceptionThrow) + 1};
|
||||
while (_Unwind_FindEnclosingFunction(it) == &ExceptionThrow)
|
||||
it++;
|
||||
@ -51,11 +49,13 @@ namespace skyline::signal {
|
||||
bool hasAdvanced{};
|
||||
while (lookupFrame && lookupFrame->lr) {
|
||||
if (lookupFrame->lr >= reinterpret_cast<void *>(&ExceptionThrow) && lookupFrame->lr < exceptionThrowEnd) {
|
||||
// We need to check if the current stack frame is from ExceptionThrow
|
||||
// As we need to skip past it (2 frames) and be able to recognize when we're in an infinite loop
|
||||
if (!hasAdvanced) {
|
||||
frame = SafeFrameRecurse(2, lookupFrame);
|
||||
hasAdvanced = true;
|
||||
} else {
|
||||
terminateHandler(); // We have no handler to consume the exception, it's time to quit
|
||||
terminateHandler(); // We presumably have no exception handlers left on the stack to consume the exception, it's time to quit
|
||||
}
|
||||
}
|
||||
lookupFrame = lookupFrame->next;
|
||||
|
@ -45,9 +45,9 @@ namespace skyline::signal {
|
||||
class SignalException {
|
||||
public:
|
||||
int signal{};
|
||||
void* pc{};
|
||||
void *pc{};
|
||||
void *fault{};
|
||||
std::vector<void*> frames; //!< A vector of all stack frame entries prior to the signal occuring
|
||||
std::vector<void *> frames; //!< A vector of all stack frame entries prior to the signal occuring
|
||||
|
||||
inline std::string what() const {
|
||||
if (!fault)
|
||||
|
@ -150,7 +150,7 @@ namespace skyline {
|
||||
|
||||
public:
|
||||
std::vector<u8> backing; //!< The object that holds a host copy of the guest texture (Will be replaced with a vk::Image)
|
||||
std::shared_ptr<GuestTexture> guest; //!< The guest texture from which this was created, it is required for syncing
|
||||
std::shared_ptr<GuestTexture> guest; //!< The guest texture from which this was created, it's required for syncing
|
||||
texture::Dimensions dimensions;
|
||||
texture::Format format;
|
||||
texture::Swizzle swizzle;
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
namespace skyline::input {
|
||||
/**
|
||||
* @brief A controller equivalent to a physical one connected to the Switch, it's translation into a Player (NpadDevice) is also encapsulated here
|
||||
* @brief A controller equivalent to a physical one connected to the Switch, its translation into a Player (NpadDevice) is also encapsulated here
|
||||
*/
|
||||
struct GuestController {
|
||||
NpadControllerType type{};
|
||||
@ -26,7 +26,7 @@ namespace skyline::input {
|
||||
friend NpadDevice;
|
||||
|
||||
/**
|
||||
* @brief Translates an NPad's ID into it's index in the array
|
||||
* @brief Translates an NPad's ID into its index in the array
|
||||
* @param id The ID of the NPad to translate
|
||||
* @return The corresponding index of the NPad in the array
|
||||
*/
|
||||
|
@ -65,7 +65,7 @@ namespace skyline::input {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A handle to a specific device addressed by it's ID and type
|
||||
* @brief A handle to a specific device addressed by its ID and type
|
||||
* @note This is used by both Six-Axis and Vibration
|
||||
*/
|
||||
union __attribute__((__packed__)) NpadDeviceHandle {
|
||||
@ -130,7 +130,7 @@ namespace skyline::input {
|
||||
NpadControllerState &GetNextEntry(NpadControllerInfo &info);
|
||||
|
||||
/**
|
||||
* @return The NpadControllerInfo for this controller based on it's type
|
||||
* @return The NpadControllerInfo for this controller based on its type
|
||||
*/
|
||||
NpadControllerInfo &GetControllerInfo();
|
||||
|
||||
|
@ -243,7 +243,7 @@ namespace skyline::input {
|
||||
NpadControllerInfo leftController; //!< The Left Joy-Con controller data (Only in Single Mode, no input rotation based on rotation)
|
||||
NpadControllerInfo rightController; //!< The Right Joy-Con controller data (Only in Single Mode, no input rotation based on rotation)
|
||||
NpadControllerInfo palmaController; //!< The Poké Ball Plus controller data
|
||||
NpadControllerInfo defaultController; //!< The Default controller data (Inputs are rotated based on orientation and SL/SR are mapped to L/R incase it is a single JC)
|
||||
NpadControllerInfo defaultController; //!< The Default controller data (Inputs are rotated based on orientation and SL/SR are mapped to L/R incase it's a single JC)
|
||||
|
||||
NpadSixAxisInfo fullKeySixAxis; //!< The Pro/GC IMU data
|
||||
NpadSixAxisInfo handheldSixAxis; //!< The Handheld IMU data
|
||||
|
@ -8,7 +8,7 @@
|
||||
namespace skyline::input {
|
||||
/*
|
||||
* @brief A description of a point being touched on the screen
|
||||
* @note All members are jint as it is treated as a jint array in Kotlin
|
||||
* @note All members are jint as it's treated as a jint array in Kotlin
|
||||
* @note This structure corresponds to TouchScreenStateData, see that for details
|
||||
*/
|
||||
struct TouchScreenPoint {
|
||||
|
@ -40,15 +40,15 @@ namespace skyline {
|
||||
* @url https://switchbrew.org/wiki/IPC_Marshalling#IPC_Command_Structure
|
||||
*/
|
||||
struct CommandHeader {
|
||||
CommandType type : 16;
|
||||
u8 xNo : 4;
|
||||
u8 aNo : 4;
|
||||
u8 bNo : 4;
|
||||
u8 wNo : 4;
|
||||
u32 rawSize : 10;
|
||||
CommandType type : 16;
|
||||
u8 xNo : 4;
|
||||
u8 aNo : 4;
|
||||
u8 bNo : 4;
|
||||
u8 wNo : 4;
|
||||
u32 rawSize : 10;
|
||||
BufferCFlag cFlag : 4;
|
||||
u32 : 17;
|
||||
bool handleDesc : 1;
|
||||
bool handleDesc : 1;
|
||||
};
|
||||
static_assert(sizeof(CommandHeader) == 8);
|
||||
|
||||
@ -56,7 +56,7 @@ namespace skyline {
|
||||
* @url https://switchbrew.org/wiki/IPC_Marshalling#Handle_descriptor
|
||||
*/
|
||||
struct HandleDescriptor {
|
||||
bool sendPid : 1;
|
||||
bool sendPid : 1;
|
||||
u32 copyCount : 4;
|
||||
u32 moveCount : 4;
|
||||
u32 : 23;
|
||||
@ -263,7 +263,7 @@ namespace skyline {
|
||||
std::vector<u8> payload; //!< The contents to be pushed to the data payload
|
||||
|
||||
public:
|
||||
Result errorCode{}; //!< The error code to respond with, it is 0 (Success) by default
|
||||
Result errorCode{}; //!< The error code to respond with, it's 0 (Success) by default
|
||||
std::vector<KHandle> copyHandles;
|
||||
std::vector<KHandle> moveHandles;
|
||||
std::vector<KHandle> domainObjects;
|
||||
|
@ -209,7 +209,7 @@ namespace skyline {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief MemoryManager keeps track of guest virtual memory and it's related attributes
|
||||
* @brief MemoryManager keeps track of guest virtual memory and its related attributes
|
||||
*/
|
||||
class MemoryManager {
|
||||
private:
|
||||
@ -225,7 +225,7 @@ namespace skyline {
|
||||
memory::Region stack{};
|
||||
memory::Region tlsIo{}; //!< TLS/IO
|
||||
|
||||
std::shared_mutex mutex; //!< Synchronizes any operations done on the VMM, it is locked in shared mode by readers and exclusive mode by writers
|
||||
std::shared_mutex mutex; //!< Synchronizes any operations done on the VMM, it's locked in shared mode by readers and exclusive mode by writers
|
||||
|
||||
MemoryManager(const DeviceState &state);
|
||||
|
||||
|
@ -22,8 +22,7 @@ namespace skyline::kernel {
|
||||
}
|
||||
}
|
||||
|
||||
Scheduler::CoreContext &Scheduler::LoadBalance(const std::shared_ptr<type::KThread> &thread, bool alwaysInsert) {
|
||||
std::lock_guard migrationLock(thread->coreMigrationMutex);
|
||||
Scheduler::CoreContext &Scheduler::GetOptimalCoreForThread(const std::shared_ptr<type::KThread> &thread) {
|
||||
auto *currentCore{&cores.at(thread->coreId)};
|
||||
|
||||
if (!currentCore->queue.empty() && thread->affinityMask.count() != 1) {
|
||||
@ -36,12 +35,19 @@ namespace skyline::kernel {
|
||||
u64 timeslice{};
|
||||
|
||||
if (!candidateCore.queue.empty()) {
|
||||
std::unique_lock coreLock(candidateCore.mutex);
|
||||
std::lock_guard coreLock(candidateCore.mutex);
|
||||
|
||||
auto threadIterator{candidateCore.queue.cbegin()};
|
||||
if (threadIterator != candidateCore.queue.cend()) {
|
||||
const auto &runningThread{*threadIterator};
|
||||
timeslice += runningThread->averageTimeslice ? std::min(runningThread->averageTimeslice - (util::GetTimeTicks() - runningThread->timesliceStart), 1UL) : runningThread->timesliceStart ? util::GetTimeTicks() - runningThread->timesliceStart : 1UL;
|
||||
timeslice += [&]() {
|
||||
if (runningThread->averageTimeslice)
|
||||
return std::min(runningThread->averageTimeslice - (util::GetTimeTicks() - runningThread->timesliceStart), 1UL);
|
||||
else if (runningThread->timesliceStart)
|
||||
return util::GetTimeTicks() - runningThread->timesliceStart;
|
||||
else
|
||||
return 1UL;
|
||||
}();
|
||||
|
||||
while (++threadIterator != candidateCore.queue.cend()) {
|
||||
const auto &residentThread{*threadIterator};
|
||||
@ -59,25 +65,36 @@ namespace skyline::kernel {
|
||||
}
|
||||
|
||||
if (optimalCore != currentCore) {
|
||||
if (!alwaysInsert && thread == state.thread)
|
||||
RemoveThread();
|
||||
else if (!alwaysInsert && thread != state.thread)
|
||||
[[unlikely]]
|
||||
throw exception("Migrating an external thread (T{}) without 'alwaysInsert' isn't supported", thread->id);
|
||||
/*
|
||||
bool isInserted;
|
||||
if (conditionalInsert) {
|
||||
auto it{std::find(currentCore->queue.begin(), currentCore->queue.end(), thread)};
|
||||
isInserted = it != currentCore->queue.end();
|
||||
if (isInserted && thread == state.thread) {
|
||||
// If the thread is in it's current core's queue
|
||||
// We need to remove the thread from the current core's queue
|
||||
it = currentCore->queue.erase(it);
|
||||
if (it == currentCore->queue.begin() && it != currentCore->queue.end())
|
||||
(*it)->scheduleCondition.notify_one();
|
||||
} else if (isInserted && thread != state.thread) [[unlikely]] {
|
||||
// It's not very useful to insert an external thread on the core optimal for it
|
||||
// We leave any sort of load balancing to a thread to do on it's own
|
||||
// Our systems can support it but it's pointless to do and would waste precious CPU cycles
|
||||
throw exception("Migrating an external thread (T{}) which is potentially inserted in its resident core's queue isn't supported", thread->id);
|
||||
}
|
||||
} else {
|
||||
isInserted = false;
|
||||
}
|
||||
thread->coreId = optimalCore->id;
|
||||
InsertThread(thread);
|
||||
*/
|
||||
state.logger->Debug("Load Balancing T{}: C{} -> C{}", thread->id, currentCore->id, optimalCore->id);
|
||||
} else {
|
||||
if (alwaysInsert)
|
||||
InsertThread(thread);
|
||||
state.logger->Debug("Load Balancing T{}: C{} (Late)", thread->id, currentCore->id);
|
||||
}
|
||||
|
||||
return *optimalCore;
|
||||
}
|
||||
|
||||
if (alwaysInsert)
|
||||
InsertThread(thread);
|
||||
state.logger->Debug("Load Balancing T{}: C{} (Early)", thread->id, currentCore->id);
|
||||
|
||||
return *currentCore;
|
||||
@ -114,31 +131,31 @@ namespace skyline::kernel {
|
||||
core.queue.push_front(thread);
|
||||
}
|
||||
if (thread != state.thread)
|
||||
thread->wakeCondition.notify_one(); // We only want to trigger the conditional variable if the current thread isn't inserting itself
|
||||
thread->scheduleCondition.notify_one(); // We only want to trigger the conditional variable if the current thread isn't inserting itself
|
||||
} else {
|
||||
core.queue.insert(nextThread, thread);
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::MigrateToIdealCore(const std::shared_ptr<type::KThread> &thread, CoreContext *&core, std::unique_lock<std::mutex> &lock) {
|
||||
// We need to check if the thread was in it's resident core's queue
|
||||
void Scheduler::MigrateToCore(const std::shared_ptr<type::KThread> &thread, CoreContext *¤tCore, CoreContext *targetCore, std::unique_lock<std::mutex> &lock) {
|
||||
// We need to check if the thread was in its resident core's queue
|
||||
// If it was, we need to remove it from the queue
|
||||
auto it{std::find(core->queue.begin(), core->queue.end(), thread)};
|
||||
bool wasInserted{it != core->queue.end()};
|
||||
auto it{std::find(currentCore->queue.begin(), currentCore->queue.end(), thread)};
|
||||
bool wasInserted{it != currentCore->queue.end()};
|
||||
if (wasInserted) {
|
||||
it = core->queue.erase(it);
|
||||
if (it == core->queue.begin() && it != core->queue.end())
|
||||
(*it)->wakeCondition.notify_one();
|
||||
it = currentCore->queue.erase(it);
|
||||
if (it == currentCore->queue.begin() && it != currentCore->queue.end())
|
||||
(*it)->scheduleCondition.notify_one();
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
thread->coreId = thread->idealCore;
|
||||
thread->coreId = targetCore->id;
|
||||
if (wasInserted)
|
||||
// We need to add the thread to the ideal core queue, if it was previously it's resident core's queue
|
||||
// We need to add the thread to the ideal core queue, if it was previously its resident core's queue
|
||||
InsertThread(thread);
|
||||
|
||||
core = &cores.at(thread->coreId);
|
||||
lock = std::unique_lock(core->mutex);
|
||||
currentCore = targetCore;
|
||||
lock = std::unique_lock(targetCore->mutex);
|
||||
}
|
||||
|
||||
void Scheduler::WaitSchedule(bool loadBalance) {
|
||||
@ -148,30 +165,30 @@ namespace skyline::kernel {
|
||||
|
||||
auto wakeFunction{[&]() {
|
||||
if (!thread->affinityMask.test(thread->coreId)) [[unlikely]] {
|
||||
MigrateToIdealCore(thread, core, lock);
|
||||
std::lock_guard migrationLock(thread->coreMigrationMutex);
|
||||
MigrateToCore(thread, core, &cores.at(thread->idealCore), lock);
|
||||
}
|
||||
return !core->queue.empty() && core->queue.front() == thread;
|
||||
}};
|
||||
|
||||
if (loadBalance && thread->affinityMask.count() > 1) {
|
||||
std::chrono::milliseconds loadBalanceThreshold{PreemptiveTimeslice * 2}; //!< The amount of time that needs to pass unscheduled for a thread to attempt load balancing
|
||||
while (!thread->wakeCondition.wait_for(lock, loadBalanceThreshold, wakeFunction)) {
|
||||
lock.unlock();
|
||||
LoadBalance(state.thread);
|
||||
if (thread->coreId == core->id) {
|
||||
lock.lock();
|
||||
} else {
|
||||
core = &cores.at(thread->coreId);
|
||||
lock = std::unique_lock(core->mutex);
|
||||
}
|
||||
while (!thread->scheduleCondition.wait_for(lock, loadBalanceThreshold, wakeFunction)) {
|
||||
lock.unlock(); // We cannot call GetOptimalCoreForThread without relinquishing the core mutex
|
||||
std::lock_guard migrationLock(thread->coreMigrationMutex);
|
||||
auto newCore{&GetOptimalCoreForThread(state.thread)};
|
||||
lock.lock();
|
||||
if (core != newCore)
|
||||
MigrateToCore(thread, core, newCore, lock);
|
||||
|
||||
loadBalanceThreshold *= 2; // We double the duration required for future load balancing for this invocation to minimize pointless load balancing
|
||||
}
|
||||
} else {
|
||||
thread->wakeCondition.wait(lock, wakeFunction);
|
||||
thread->scheduleCondition.wait(lock, wakeFunction);
|
||||
}
|
||||
|
||||
if (thread->priority == core->preemptionPriority) {
|
||||
// If the thread needs to be preempted then arm its preemption timer
|
||||
struct itimerspec spec{.it_value = {.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(PreemptiveTimeslice).count()}};
|
||||
timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
|
||||
thread->isPreempted = true;
|
||||
@ -185,9 +202,10 @@ namespace skyline::kernel {
|
||||
auto *core{&cores.at(thread->coreId)};
|
||||
|
||||
std::unique_lock lock(core->mutex);
|
||||
if (thread->wakeCondition.wait_for(lock, timeout, [&]() {
|
||||
if (thread->scheduleCondition.wait_for(lock, timeout, [&]() {
|
||||
if (!thread->affinityMask.test(thread->coreId)) [[unlikely]] {
|
||||
MigrateToIdealCore(thread, core, lock);
|
||||
std::lock_guard migrationLock(thread->coreMigrationMutex);
|
||||
MigrateToCore(thread, core, &cores.at(thread->idealCore), lock);
|
||||
}
|
||||
return !core->queue.empty() && core->queue.front() == thread;
|
||||
})) {
|
||||
@ -212,13 +230,13 @@ namespace skyline::kernel {
|
||||
std::unique_lock lock(core.mutex);
|
||||
if (core.queue.front() == thread) {
|
||||
// If this thread is at the front of the thread queue then we need to rotate the thread
|
||||
// In the case where this thread was forcefully yielded, we don't need to do this as it's done by the thread which yielded us
|
||||
// Splice the linked element from the beginning of the queue to where it's priority is present
|
||||
// In the case where this thread was forcefully yielded, we don't need to do this as it's done by the thread which yielded to this thread
|
||||
// Splice the linked element from the beginning of the queue to where its priority is present
|
||||
core.queue.splice(std::upper_bound(core.queue.begin(), core.queue.end(), thread->priority.load(), type::KThread::IsHigherPriority), core.queue, core.queue.begin());
|
||||
|
||||
auto &front{core.queue.front()};
|
||||
if (front != thread)
|
||||
front->wakeCondition.notify_one(); // If we aren't at the front of the queue, only then should we wake the thread at the front up
|
||||
front->scheduleCondition.notify_one(); // If we aren't at the front of the queue, only then should we wake the thread at the front up
|
||||
} else if (!thread->forceYield) [[unlikely]] {
|
||||
throw exception("T{} called Rotate while not being in C{}'s queue", thread->id, thread->coreId);
|
||||
}
|
||||
@ -250,7 +268,7 @@ namespace skyline::kernel {
|
||||
thread->averageTimeslice = (thread->averageTimeslice / 4) + (3 * (util::GetTimeTicks() - thread->timesliceStart / 4));
|
||||
|
||||
if (it != core.queue.end())
|
||||
(*it)->wakeCondition.notify_one(); // We need to wake the thread at the front of the queue, if we were at the front previously
|
||||
(*it)->scheduleCondition.notify_one(); // We need to wake the thread at the front of the queue, if we were at the front previously
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -274,7 +292,7 @@ namespace skyline::kernel {
|
||||
// If the thread isn't in the queue then the new priority will be handled automatically on insertion
|
||||
return;
|
||||
if (currentIt == core->queue.begin()) {
|
||||
// Alternatively, if it's currently running then we'd just want to cause it to yield if it's priority is lower than the the thread behind it
|
||||
// Alternatively, if it's currently running then we'd just want to cause it to yield if its priority is lower than the the thread behind it
|
||||
auto nextIt{std::next(currentIt)};
|
||||
if (nextIt != core->queue.end() && (*nextIt)->priority < thread->priority) {
|
||||
if (!thread->pendingYield) {
|
||||
@ -282,7 +300,7 @@ namespace skyline::kernel {
|
||||
thread->pendingYield = true;
|
||||
}
|
||||
} else if (!thread->isPreempted && thread->priority == core->preemptionPriority) {
|
||||
// If the thread needs to be preempted due to the new priority then arm it's preemption timer
|
||||
// If the thread needs to be preempted due to the new priority then arm its preemption timer
|
||||
struct itimerspec spec{.it_value = {.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(PreemptiveTimeslice).count()}};
|
||||
timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
|
||||
thread->isPreempted = true;
|
||||
@ -318,11 +336,11 @@ namespace skyline::kernel {
|
||||
|
||||
void Scheduler::UpdateCore(const std::shared_ptr<type::KThread> &thread) {
|
||||
auto *core{&cores.at(thread->coreId)};
|
||||
std::unique_lock coreLock(core->mutex);
|
||||
std::lock_guard coreLock(core->mutex);
|
||||
if (core->queue.front() == thread)
|
||||
thread->SendSignal(YieldSignal);
|
||||
else
|
||||
thread->wakeCondition.notify_one();
|
||||
thread->scheduleCondition.notify_one();
|
||||
}
|
||||
|
||||
void Scheduler::ParkThread() {
|
||||
@ -339,7 +357,7 @@ namespace skyline::kernel {
|
||||
if (thread->coreId == constant::ParkedCoreId) {
|
||||
std::unique_lock lock(parkedMutex);
|
||||
parkedQueue.insert(std::upper_bound(parkedQueue.begin(), parkedQueue.end(), thread->priority.load(), type::KThread::IsHigherPriority), thread);
|
||||
thread->wakeCondition.wait(lock, [&]() { return parkedQueue.front() == thread && thread->coreId != constant::ParkedCoreId; });
|
||||
thread->scheduleCondition.wait(lock, [&]() { return parkedQueue.front() == thread && thread->coreId != constant::ParkedCoreId; });
|
||||
}
|
||||
|
||||
InsertThread(thread);
|
||||
@ -355,12 +373,12 @@ namespace skyline::kernel {
|
||||
nextThread = nextThread->priority == thread->priority ? nextThread : nullptr; // If the next thread doesn't have the same priority then it won't be scheduled next
|
||||
auto parkedThread{parkedQueue.front()};
|
||||
|
||||
// We need to be conservative about waking up a parked thread, it should only be done if it's priority is higher than the current thread
|
||||
// Alternatively, it should be done if it's priority is equivalent to the current thread's priority but the next thread had been scheduled prior or if there is no next thread (Current thread would be rescheduled)
|
||||
// We need to be conservative about waking up a parked thread, it should only be done if its priority is higher than the current thread
|
||||
// Alternatively, it should be done if its priority is equivalent to the current thread's priority but the next thread had been scheduled prior or if there is no next thread (Current thread would be rescheduled)
|
||||
if (parkedThread->priority < thread->priority || (parkedThread->priority == thread->priority && (!nextThread || parkedThread->timesliceStart < nextThread->timesliceStart))) {
|
||||
parkedThread->coreId = thread->coreId;
|
||||
parkedLock.unlock();
|
||||
parkedThread->wakeCondition.notify_one();
|
||||
parkedThread->scheduleCondition.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ namespace skyline {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
/**
|
||||
* @brief The Scheduler is responsible for determining which threads should run on which virtual cores and when they should be scheduled
|
||||
* @note We tend to stray a lot from HOS in our scheduler design as we've designed it around our 1 host thread per guest thread which leads to scheduling from the perspective of threads while the HOS scheduler deals with scheduling from the perspective of cores, not doing this would lead to missing out on key optimizations and serialization of scheduling
|
||||
*/
|
||||
@ -58,10 +58,11 @@ namespace skyline {
|
||||
std::list<std::shared_ptr<type::KThread>> parkedQueue; //!< A queue of threads which are parked and waiting on core migration
|
||||
|
||||
/**
|
||||
* @brief Migrate a thread from it's resident core to it's ideal core
|
||||
* @note This is used to handle non-cooperative core affinity mask changes where the resident core is not in it's new affinity mask
|
||||
* @brief Migrate a thread from its resident core to its ideal core
|
||||
* @note 'KThread::coreMigrationMutex' **must** be locked by the calling thread prior to calling this
|
||||
* @note This is used to handle non-cooperative core affinity mask changes where the resident core is not in its new affinity mask
|
||||
*/
|
||||
void MigrateToIdealCore(const std::shared_ptr<type::KThread>& thread, CoreContext*& core, std::unique_lock<std::mutex>& lock);
|
||||
void MigrateToCore(const std::shared_ptr<type::KThread> &thread, CoreContext *¤tCore, CoreContext* targetCore, std::unique_lock<std::mutex> &lock);
|
||||
|
||||
public:
|
||||
static constexpr std::chrono::milliseconds PreemptiveTimeslice{10}; //!< The duration of time a preemptive thread can run before yielding
|
||||
@ -76,79 +77,78 @@ namespace skyline {
|
||||
static void SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls);
|
||||
|
||||
/**
|
||||
* @brief Checks all cores and migrates the specified thread to the core where the calling thread should be scheduled the earliest
|
||||
* @param alwaysInsert If to insert the thread even if it hasn't migrated cores, this is used during thread creation
|
||||
* @return A reference to the CoreContext of the core which the calling thread is running on after load balancing
|
||||
* @note This inserts the thread into the migrated process's queue after load balancing, there is no need to call it redundantly
|
||||
* @note alwaysInsert makes the assumption that the thread isn't inserted in any core's queue currently
|
||||
* @brief Checks all cores and determines the core where the supplied thread should be scheduled the earliest
|
||||
* @note 'KThread::coreMigrationMutex' **must** be locked by the calling thread prior to calling this
|
||||
* @note No core mutexes should be held by the calling thread, that will cause a recursive lock and lead to a deadlock
|
||||
* @return A reference to the CoreContext of the optimal core
|
||||
*/
|
||||
CoreContext& LoadBalance(const std::shared_ptr<type::KThread> &thread, bool alwaysInsert = false);
|
||||
CoreContext &GetOptimalCoreForThread(const std::shared_ptr<type::KThread> &thread);
|
||||
|
||||
/**
|
||||
* @brief Inserts the specified thread into the scheduler queue at the appropriate location based on it's priority
|
||||
* @brief Inserts the specified thread into the scheduler queue at the appropriate location based on its priority
|
||||
*/
|
||||
void InsertThread(const std::shared_ptr<type::KThread>& thread);
|
||||
void InsertThread(const std::shared_ptr<type::KThread> &thread);
|
||||
|
||||
/**
|
||||
* @brief Wait for the current thread to be scheduled on it's resident core
|
||||
* @brief Wait for the calling thread to be scheduled on its resident core
|
||||
* @param loadBalance If the thread is appropriate for load balancing then if to load balance it occassionally or not
|
||||
* @note There is an assumption of the thread being on it's resident core queue, if it's not this'll never return
|
||||
* @note There is an assumption of the thread being on its resident core queue, if it's not this'll never return
|
||||
*/
|
||||
void WaitSchedule(bool loadBalance = true);
|
||||
|
||||
/**
|
||||
* @brief Wait for the current thread to be scheduled on it's resident core or for the timeout to expire
|
||||
* @brief Wait for the calling thread to be scheduled on its resident core or for the timeout to expire
|
||||
* @return If the thread has been scheduled (true) or if the timer expired before it could be (false)
|
||||
* @note This will never load balance as it uses the timeout itself as a result this shouldn't be used as a replacement for regular waits
|
||||
*/
|
||||
bool TimedWaitSchedule(std::chrono::nanoseconds timeout);
|
||||
|
||||
/**
|
||||
* @brief Rotates the calling thread's resident core queue, if it is at the front of it
|
||||
* @brief Rotates the calling thread's resident core queue, if it's at the front of it
|
||||
* @param cooperative If this was triggered by a cooperative yield as opposed to a preemptive one
|
||||
*/
|
||||
void Rotate(bool cooperative = true);
|
||||
|
||||
/**
|
||||
* @brief Removes the calling thread from it's resident core queue
|
||||
* @brief Removes the calling thread from its resident core queue
|
||||
*/
|
||||
void RemoveThread();
|
||||
|
||||
/**
|
||||
* @brief Updates the placement of the supplied thread in it's resident core's queue according to it's new priority
|
||||
* @brief Updates the placement of the supplied thread in its resident core's queue according to its new priority
|
||||
*/
|
||||
void UpdatePriority(const std::shared_ptr<type::KThread>& thread);
|
||||
void UpdatePriority(const std::shared_ptr<type::KThread> &thread);
|
||||
|
||||
/**
|
||||
* @brief Updates the core that the supplied thread is resident to according to it's new affinity mask and ideal core
|
||||
* @brief Updates the core that the supplied thread is resident to according to its new affinity mask and ideal core
|
||||
* @note This supports changing the core of a thread which is currently running
|
||||
*/
|
||||
void UpdateCore(const std::shared_ptr<type::KThread>& thread);
|
||||
void UpdateCore(const std::shared_ptr<type::KThread> &thread);
|
||||
|
||||
/**
|
||||
* @brief Parks the calling thread after removing it from it's resident core's queue and inserts it on the core it's been awoken on
|
||||
* @brief Parks the calling thread after removing it from its resident core's queue and inserts it on the core it's been awoken on
|
||||
* @note This will not handle waiting for the thread to be scheduled, this should be followed with a call to WaitSchedule/TimedWaitSchedule
|
||||
*/
|
||||
void ParkThread();
|
||||
|
||||
/**
|
||||
* @brief Wakes a single parked thread which may be appropriate for running next on this core
|
||||
* @note We will only wake a thread if it is determined to be a better pick than the thread which would be run on this core next
|
||||
* @note We will only wake a thread if it's determined to be a better pick than the thread which would be run on this core next
|
||||
*/
|
||||
void WakeParkedThread();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A lock which removes the calling thread from it's resident core's scheduler queue and adds it back when being destroyed
|
||||
* @note It also blocks till the thread has been rescheduled in it's destructor, this behavior might not be preferable in some cases
|
||||
* @note This is not an analogue to KScopedSchedulerLock on HOS, it is for handling thread state changes which we handle with Scheduler::YieldPending
|
||||
* @brief A lock which removes the calling thread from its resident core's scheduler queue and adds it back when being destroyed
|
||||
* @note It also blocks till the thread has been rescheduled in its destructor, this behavior might not be preferable in some cases
|
||||
* @note This is not an analogue to KScopedSchedulerLock on HOS, it's for handling thread state changes which we handle with Scheduler::YieldPending
|
||||
*/
|
||||
struct SchedulerScopedLock {
|
||||
private:
|
||||
const DeviceState& state;
|
||||
const DeviceState &state;
|
||||
|
||||
public:
|
||||
inline SchedulerScopedLock(const DeviceState& state) : state(state) {
|
||||
inline SchedulerScopedLock(const DeviceState &state) : state(state) {
|
||||
state.scheduler->RemoveThread();
|
||||
}
|
||||
|
||||
|
@ -352,7 +352,7 @@ namespace skyline::kernel::svc {
|
||||
thread->basePriority = priority;
|
||||
u8 newPriority{};
|
||||
do {
|
||||
// Try to CAS the priority of the thread with it's new base priority
|
||||
// Try to CAS the priority of the thread with its new base priority
|
||||
// If the new priority is equivalent to the current priority then we don't need to CAS
|
||||
newPriority = thread->priority.load();
|
||||
newPriority = std::min(newPriority, priority);
|
||||
@ -396,7 +396,7 @@ namespace skyline::kernel::svc {
|
||||
} else if (idealCore == IdealCoreNoUpdate) {
|
||||
idealCore = thread->idealCore;
|
||||
} else if (idealCore == IdealCoreDontCare) {
|
||||
idealCore = __builtin_ctzll(affinityMask.to_ullong()); // The first enabled core in the affinity mask
|
||||
idealCore = std::countr_zero(affinityMask.to_ullong()); // The first enabled core in the affinity mask
|
||||
}
|
||||
|
||||
auto processMask{state.process->npdm.threadInfo.coreMask};
|
||||
@ -1151,6 +1151,6 @@ namespace skyline::kernel::svc {
|
||||
else if (result == result::InvalidState)
|
||||
state.logger->Debug("svcSignalToAddress: The value at 0x{:X} did not satisfy the mutation condition", address);
|
||||
|
||||
state.ctx->gpr.w0 = Result{};
|
||||
state.ctx->gpr.w0 = result;
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ namespace skyline::kernel::svc {
|
||||
void GetCurrentProcessorNumber(const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief Clears a KEvent of it's signal
|
||||
* @brief Resets a KEvent to its unsignalled state
|
||||
* @url https://switchbrew.org/wiki/SVC#ClearEvent
|
||||
*/
|
||||
void ClearEvent(const DeviceState &state);
|
||||
|
@ -100,7 +100,7 @@ namespace skyline::kernel::type {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
constexpr u32 HandleWaitersBit{0b01000000000000000000000000000000}; //!< A bit which denotes if a mutex psuedo-handle has waiters or not
|
||||
constexpr u32 HandleWaitersBit{1UL << 30}; //!< A bit which denotes if a mutex psuedo-handle has waiters or not
|
||||
|
||||
Result KProcess::MutexLock(u32 *mutex, KHandle ownerHandle, KHandle tag) {
|
||||
std::shared_ptr<KThread> owner;
|
||||
@ -126,7 +126,7 @@ namespace skyline::kernel::type {
|
||||
isHighestPriority = waiters.insert(std::upper_bound(waiters.begin(), waiters.end(), state.thread->priority.load(), KThread::IsHigherPriority), state.thread) == waiters.begin();
|
||||
state.scheduler->RemoveThread();
|
||||
|
||||
std::atomic_store(&state.thread->waitThread, owner);
|
||||
state.thread->waitThread = owner;
|
||||
state.thread->waitKey = mutex;
|
||||
state.thread->waitTag = tag;
|
||||
}
|
||||
@ -143,15 +143,10 @@ namespace skyline::kernel::type {
|
||||
} while (ownerPriority != priority && owner->priority.compare_exchange_strong(ownerPriority, priority));
|
||||
|
||||
if (ownerPriority != priority) {
|
||||
auto waitThread{std::atomic_load(&owner->waitThread)};
|
||||
while (waitThread) {
|
||||
std::shared_ptr<KThread> waitThread;
|
||||
{
|
||||
std::lock_guard lock(waitThread->waiterMutex);
|
||||
|
||||
auto currentWaitThread{std::atomic_load(&owner->waitThread)};
|
||||
if (waitThread != currentWaitThread) {
|
||||
waitThread = currentWaitThread;
|
||||
continue;
|
||||
}
|
||||
waitThread = owner->waitThread;
|
||||
|
||||
// We need to update the location of the owner thread in the waiter queue of the thread it's waiting on
|
||||
auto &waiters{waitThread->waiters};
|
||||
@ -181,7 +176,7 @@ namespace skyline::kernel::type {
|
||||
if (nextOwnerIt != waiters.end()) {
|
||||
auto nextOwner{*nextOwnerIt};
|
||||
std::lock_guard nextLock(nextOwner->waiterMutex);
|
||||
std::atomic_store(&nextOwner->waitThread, std::shared_ptr<KThread>{nullptr});
|
||||
nextOwner->waitThread = std::shared_ptr<KThread>{nullptr};
|
||||
nextOwner->waitKey = nullptr;
|
||||
|
||||
// Move all threads waiting on this key to the next owner's waiter list
|
||||
@ -190,7 +185,7 @@ namespace skyline::kernel::type {
|
||||
auto thread{*it};
|
||||
if (thread->waitKey == mutex) {
|
||||
nextOwner->waiters.splice(std::upper_bound(nextOwner->waiters.begin(), nextOwner->waiters.end(), (*it)->priority.load(), KThread::IsHigherPriority), waiters, it);
|
||||
std::atomic_store(&thread->waitThread, nextOwner);
|
||||
thread->waitThread = nextOwner;
|
||||
if (!nextWaiter)
|
||||
nextWaiter = thread;
|
||||
}
|
||||
@ -216,7 +211,7 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
|
||||
if (nextWaiter) {
|
||||
// If there is a waiter on the new owner then try to inherit it's priority
|
||||
// If there is a waiter on the new owner then try to inherit its priority
|
||||
u8 priority, ownerPriority;
|
||||
do {
|
||||
ownerPriority = nextOwner->priority.load();
|
||||
@ -279,8 +274,8 @@ namespace skyline::kernel::type {
|
||||
auto queue{syncWaiters.equal_range(key)};
|
||||
|
||||
auto it{queue.first};
|
||||
for (i32 waiterCount{amount}; it != queue.second && (amount <= 0 || waiterCount); it = syncWaiters.erase(it), waiterCount--)
|
||||
state.scheduler->InsertThread(it->second);
|
||||
for (i32 waiterCount{amount}; it != queue.second && (amount <= 0 || waiterCount); it = syncWaiters.erase(it), waiterCount--)
|
||||
state.scheduler->InsertThread(it->second);
|
||||
|
||||
if (it == queue.second)
|
||||
__atomic_store_n(key, false, __ATOMIC_SEQ_CST); // We need to update the boolean flag denoting that there are no more threads waiting on this conditional variable
|
||||
@ -299,14 +294,15 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
|
||||
if (timeout > 0 && !state.scheduler->TimedWaitSchedule(std::chrono::nanoseconds(timeout))) {
|
||||
std::unique_lock lock(syncWaiterMutex);
|
||||
auto queue{syncWaiters.equal_range(address)};
|
||||
auto iterator{std::find(queue.first, queue.second, SyncWaiters::value_type{address, state.thread})};
|
||||
if (iterator != queue.second)
|
||||
if (syncWaiters.erase(iterator) == queue.second)
|
||||
__atomic_store_n(address, false, __ATOMIC_SEQ_CST);
|
||||
{
|
||||
std::lock_guard lock(syncWaiterMutex);
|
||||
auto queue{syncWaiters.equal_range(address)};
|
||||
auto iterator{std::find(queue.first, queue.second, SyncWaiters::value_type{address, state.thread})};
|
||||
if (iterator != queue.second)
|
||||
if (syncWaiters.erase(iterator) == queue.second)
|
||||
__atomic_store_n(address, false, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
state.scheduler->InsertThread(state.thread);
|
||||
state.scheduler->WaitSchedule();
|
||||
|
||||
|
@ -26,7 +26,7 @@ namespace skyline {
|
||||
|
||||
private:
|
||||
std::mutex threadMutex; //!< Synchronizes thread creation to prevent a race between thread creation and thread killing
|
||||
bool disableThreadCreation{}; //!< If to disable thread creation, we use this to prevent thread creation after all threads have been killed
|
||||
bool disableThreadCreation{}; //!< Whether to disable thread creation, we use this to prevent thread creation after all threads have been killed
|
||||
std::vector<std::shared_ptr<KThread>> threads;
|
||||
|
||||
using SyncWaiters = std::multimap<void *, std::shared_ptr<KThread>>;
|
||||
@ -47,13 +47,13 @@ namespace skyline {
|
||||
|
||||
/**
|
||||
* @return A non-null pointer to a TLS page slot on success, a nullptr will be returned if this page is full
|
||||
* @note This function is not thread-safe and should be called by exclusively one thread at a time
|
||||
* @note This function is not thread-safe and should be called exclusively by one thread at a time
|
||||
*/
|
||||
u8 *ReserveSlot();
|
||||
};
|
||||
|
||||
public:
|
||||
u8* tlsExceptionContext{}; //!< A pointer to the TLS Exception Handling Context slot
|
||||
u8 *tlsExceptionContext{}; //!< A pointer to the TLS exception handling context slot
|
||||
std::mutex tlsMutex; //!< A mutex to synchronize allocation of TLS pages to prevent extra pages from being created
|
||||
std::vector<std::shared_ptr<TlsPage>> tlsPages; //!< All TLS pages allocated by this process
|
||||
std::shared_ptr<KPrivateMemory> mainThreadStack; //!< The stack memory of the main thread stack is owned by the KProcess itself
|
||||
@ -72,8 +72,8 @@ namespace skyline {
|
||||
/**
|
||||
* @brief Kill the main thread/all running threads in the process in a graceful manner
|
||||
* @param join Return after the main thread has joined rather than instantly
|
||||
* @param all If to kill all running threads or just the main thread
|
||||
* @param disableCreation If to disable further thread creation
|
||||
* @param all Whether to kill all running threads or just the main thread
|
||||
* @param disableCreation Whether to disable further thread creation
|
||||
* @note If there are no threads then this will silently return
|
||||
* @note The main thread should eventually kill the rest of the threads itself
|
||||
*/
|
||||
@ -226,12 +226,12 @@ namespace skyline {
|
||||
/**
|
||||
* @brief Waits on the supplied address with the specified arbitration function
|
||||
*/
|
||||
Result WaitForAddress(u32 *address, u32 value, i64 timeout, bool(*arbitrationFunction)(u32* address, u32 value));
|
||||
Result WaitForAddress(u32 *address, u32 value, i64 timeout, bool(*arbitrationFunction)(u32 *address, u32 value));
|
||||
|
||||
/**
|
||||
* @brief Signals a variable amount of waiters at the supplied address
|
||||
*/
|
||||
Result SignalToAddress(u32 *address, u32 value, i32 amount, bool(*mutateFunction)(u32* address, u32 value, u32 waiterCount) = nullptr);
|
||||
Result SignalToAddress(u32 *address, u32 value, i32 amount, bool(*mutateFunction)(u32 *address, u32 value, u32 waiterCount) = nullptr);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace skyline::kernel::type {
|
||||
void KSyncObject::Signal() {
|
||||
std::lock_guard lock(syncObjectMutex);
|
||||
signalled = true;
|
||||
for (auto& waiter : syncObjectWaiters) {
|
||||
for (auto &waiter : syncObjectWaiters) {
|
||||
if (waiter->isCancellable) {
|
||||
waiter->isCancellable = false;
|
||||
waiter->wakeObject = this;
|
||||
@ -16,4 +16,13 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool KSyncObject::ResetSignal() {
|
||||
std::lock_guard lock(syncObjectMutex);
|
||||
if (signalled) [[likely]] {
|
||||
signalled = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -30,14 +30,7 @@ namespace skyline::kernel::type {
|
||||
* @brief Resets the object to an unsignalled state
|
||||
* @return If the signal was reset or not
|
||||
*/
|
||||
inline bool ResetSignal() {
|
||||
std::lock_guard lock(syncObjectMutex);
|
||||
if (signalled) {
|
||||
signalled = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool ResetSignal();
|
||||
|
||||
virtual ~KSyncObject() = default;
|
||||
};
|
||||
|
@ -39,7 +39,7 @@ namespace skyline::kernel::type {
|
||||
state.scheduler->RemoveThread();
|
||||
|
||||
{
|
||||
std::unique_lock lock(statusMutex);
|
||||
std::lock_guard lock(statusMutex);
|
||||
running = false;
|
||||
ready = false;
|
||||
statusCondition.notify_all();
|
||||
@ -89,7 +89,7 @@ namespace skyline::kernel::type {
|
||||
"MOV X0, SP\n\t"
|
||||
"STR X0, [%x0, #0x2A8]\n\t" // Write ThreadContext::hostSp
|
||||
"MOV SP, %x1\n\t" // Replace SP with guest stack
|
||||
"MOV LR, %x2\n\t" // Store entry in Link Register so it is jumped to on return
|
||||
"MOV LR, %x2\n\t" // Store entry in Link Register so it's jumped to on return
|
||||
"MOV X0, %x3\n\t" // Store the argument in X0
|
||||
"MOV X1, %x4\n\t" // Store the thread handle in X1, NCA applications require this
|
||||
"MOV X2, XZR\n\t" // Zero out other GP and SIMD registers, not doing this will break applications
|
||||
@ -185,7 +185,12 @@ namespace skyline::kernel::type {
|
||||
void KThread::Start(bool self) {
|
||||
std::unique_lock lock(statusMutex);
|
||||
if (!running) {
|
||||
state.scheduler->LoadBalance(shared_from_this(), true); // This will automatically insert the thread into the core queue after load balancing
|
||||
{
|
||||
std::lock_guard migrationLock(coreMigrationMutex);
|
||||
auto thisShared{shared_from_this()};
|
||||
coreId = state.scheduler->GetOptimalCoreForThread(thisShared).id;
|
||||
state.scheduler->InsertThread(thisShared);
|
||||
}
|
||||
|
||||
running = true;
|
||||
killed = false;
|
||||
|
@ -22,14 +22,18 @@ namespace skyline {
|
||||
std::thread thread; //!< If this KThread is backed by a host thread then this'll hold it
|
||||
pthread_t pthread{}; //!< The pthread_t for the host thread running this guest thread
|
||||
|
||||
/**
|
||||
* @brief Entry function any guest threads, sets up necessary context and jumps into guest code from the calling thread
|
||||
* @note This function also serves as the entry point for host threads created in StartThread
|
||||
*/
|
||||
void StartThread();
|
||||
|
||||
public:
|
||||
std::mutex statusMutex; //!< Synchronizes all thread state changes, running/ready
|
||||
std::condition_variable statusCondition; //!< A conditional variable signalled on the status of the thread changing
|
||||
std::mutex statusMutex; //!< Synchronizes all thread state changes (running/ready/killed)
|
||||
std::condition_variable statusCondition; //!< Signalled on the status of the thread changing
|
||||
bool running{false}; //!< If the host thread that corresponds to this thread is running, this doesn't reflect guest scheduling changes
|
||||
bool killed{false}; //!< If this thread was previously running and has been killed
|
||||
bool ready{false}; //!< If this thread is ready to recieve signals or not
|
||||
bool killed{false}; //!< If this thread was previously running and has been killed
|
||||
|
||||
KHandle handle;
|
||||
size_t id; //!< Index of thread in parent process's KThread vector
|
||||
@ -37,31 +41,36 @@ namespace skyline {
|
||||
nce::ThreadContext ctx{}; //!< The context of the guest thread during the last SVC
|
||||
jmp_buf originalCtx; //!< The context of the host thread prior to jumping into guest code
|
||||
|
||||
void *entry;
|
||||
u64 entryArgument;
|
||||
void *stackTop;
|
||||
void *entry; //!< A function pointer to the thread's entry
|
||||
u64 entryArgument; //!< An argument to provide with to the thread entry function
|
||||
void *stackTop; //!< The top of the guest's stack, this is set to the initial guest stack pointer
|
||||
|
||||
std::condition_variable wakeCondition; //!< A conditional variable which is signalled to wake the current thread while it's sleeping
|
||||
std::condition_variable scheduleCondition; //!< Signalled to wake the thread when it's scheduled or its resident core changes
|
||||
std::atomic<u8> basePriority; //!< The priority of the thread for the scheduler without any priority-inheritance
|
||||
std::atomic<u8> priority; //!< The priority of the thread for the scheduler
|
||||
std::atomic<u8> priority; //!< The priority of the thread for the scheduler including priority-inheritance
|
||||
|
||||
std::mutex coreMigrationMutex; //!< Synchronizes operations which depend on which core the thread is running on
|
||||
i8 idealCore; //!< The ideal CPU core for this thread to run on
|
||||
i8 coreId; //!< The CPU core on which this thread is running
|
||||
CoreMask affinityMask{}; //!< A mask of CPU cores this thread is allowed to run on
|
||||
std::mutex coreMigrationMutex; //!< Synchronizes operations which depend on which core the thread is running on
|
||||
u64 timesliceStart{}; //!< Start of the scheduler timeslice
|
||||
|
||||
u64 timesliceStart{}; //!< A timestamp in host CNTVCT ticks of when the thread's current timeslice started
|
||||
u64 averageTimeslice{}; //!< A weighted average of the timeslice duration for this thread
|
||||
timer_t preemptionTimer{}; //!< A kernel timer used for preemption interrupts
|
||||
|
||||
bool isPreempted{}; //!< If the preemption timer has been armed and will fire
|
||||
bool pendingYield{}; //!< If the current thread has been yielded and hasn't been acted upon it yet
|
||||
bool pendingYield{}; //!< If the thread has been yielded and hasn't been acted upon it yet
|
||||
bool forceYield{}; //!< If the thread has been forcefully yielded by another thread
|
||||
|
||||
std::mutex waiterMutex; //!< Synchronizes operations on mutation of the waiter members
|
||||
u32* waitKey; //!< The key of the mutex which this thread is waiting on
|
||||
u32 *waitKey; //!< The key of the mutex which this thread is waiting on
|
||||
KHandle waitTag; //!< The handle of the thread which requested the mutex lock
|
||||
std::shared_ptr<KThread> waitThread; //!< The thread which this thread is waiting on
|
||||
std::list<std::shared_ptr<type::KThread>> waiters; //!< A queue of threads waiting on this thread sorted by priority
|
||||
bool isCancellable{false}; //!< If the thread is currently in a position where it is cancellable
|
||||
bool cancelSync{false}; //!< If to cancel a SvcWaitSynchronization call this thread currently is in/the next one it joins
|
||||
type::KSyncObject* wakeObject{}; //!< A pointer to the synchronization object responsible for waking this thread up
|
||||
|
||||
bool isCancellable{false}; //!< If the thread is currently in a position where it's cancellable
|
||||
bool cancelSync{false}; //!< Whether to cancel the SvcWaitSynchronization call this thread currently is in/the next one it joins
|
||||
type::KSyncObject *wakeObject{}; //!< A pointer to the synchronization object responsible for waking this thread up
|
||||
|
||||
KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, u8 priority, i8 idealCore);
|
||||
|
||||
@ -85,7 +94,7 @@ namespace skyline {
|
||||
void SendSignal(int signal);
|
||||
|
||||
/**
|
||||
* @return If the supplied priority value is higher than the current thread
|
||||
* @return If the supplied priority value is higher than the supplied thread's priority value
|
||||
*/
|
||||
static constexpr bool IsHigherPriority(const i8 priority, const std::shared_ptr<type::KThread> &it) {
|
||||
return priority < it->priority;
|
||||
|
@ -50,16 +50,16 @@ namespace skyline::loader {
|
||||
class Loader {
|
||||
private:
|
||||
/**
|
||||
* @brief All data used to determine the corresponding symbol for an address
|
||||
* @brief All data used to determine the corresponding symbol for an address from an executable
|
||||
*/
|
||||
struct ExecutableSymbolicInfo {
|
||||
void* patchStart; //!< A pointer to the start of this executable's patch section
|
||||
void* programStart; //!< A pointer to the start of this executable
|
||||
void* programEnd; //!< A pointer to the end of this executable
|
||||
std::string name; //!< The name of the executable this belongs to
|
||||
std::string patchName; //!< The name of the executable this belongs to's patch section
|
||||
span<Elf64_Sym> symbols; //!< A span over the .dynsym section of this executable
|
||||
span<char> symbolStrings; //!< A span over the .dynstr section of this executable
|
||||
void *patchStart; //!< A pointer to the start of this executable's patch section
|
||||
void *programStart; //!< A pointer to the start of the executable
|
||||
void *programEnd; //!< A pointer to the end of the executable
|
||||
std::string name; //!< The name of the executable
|
||||
std::string patchName; //!< The name of the patch section
|
||||
span<Elf64_Sym> symbols; //!< A span over the .dynsym section
|
||||
span<char> symbolStrings; //!< A span over the .dynstr section
|
||||
};
|
||||
|
||||
std::vector<ExecutableSymbolicInfo> executables;
|
||||
@ -80,7 +80,7 @@ namespace skyline::loader {
|
||||
* @param name An optional name for the executable, used for symbol resolution
|
||||
* @return An ExecutableLoadInfo struct containing the load base and size
|
||||
*/
|
||||
ExecutableLoadInfo LoadExecutable(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state, Executable &executable, size_t offset = 0, const std::string& name = {});
|
||||
ExecutableLoadInfo LoadExecutable(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state, Executable &executable, size_t offset = 0, const std::string &name = {});
|
||||
|
||||
std::optional<vfs::NACP> nacp;
|
||||
std::shared_ptr<vfs::Backing> romFs;
|
||||
@ -96,8 +96,11 @@ namespace skyline::loader {
|
||||
*/
|
||||
virtual void *LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) = 0;
|
||||
|
||||
/**
|
||||
* @note The lifetime of the data contained within is tied to the lifetime of the Loader class it was obtained from (as this points to symbols from the executables loaded into memory directly)
|
||||
*/
|
||||
struct SymbolInfo {
|
||||
char* name; //!< The name of the symbol that was found
|
||||
char *name; //!< The name of the symbol that was found
|
||||
std::string_view executableName; //!< The executable that contained the symbol
|
||||
};
|
||||
|
||||
@ -111,11 +114,11 @@ namespace skyline::loader {
|
||||
* @param frame The initial stack frame or the calling function's stack frame by default
|
||||
* @return A string with the stack trace based on the supplied context
|
||||
*/
|
||||
std::string GetStackTrace(signal::StackFrame* frame = nullptr);
|
||||
std::string GetStackTrace(signal::StackFrame *frame = nullptr);
|
||||
|
||||
/**
|
||||
* @return A string with the stack trace based on the stack frames in the supplied vector
|
||||
*/
|
||||
std::string GetStackTrace(const std::vector<void*> frames);
|
||||
std::string GetStackTrace(const std::vector<void *> frames);
|
||||
};
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ namespace skyline::loader {
|
||||
|
||||
/**
|
||||
* @brief The asset section was created by homebrew developers to store additional data for their applications to use
|
||||
* @note This would actually be retrieved by NRO homebrew by reading the NRO file itself (reading it's own binary) but libnx homebrew wrongly detects the images to be running in NSO mode where the RomFS is handled by HOS, this allows us to just provide the parsed data from the asset section to it directly
|
||||
* @note This would actually be retrieved by NRO homebrew by reading the NRO file itself (reading its own binary) but libnx homebrew wrongly detects the images to be running in NSO mode where the RomFS is handled by HOS, this allows us to just provide the parsed data from the asset section to it directly
|
||||
*/
|
||||
struct NroAssetHeader {
|
||||
u32 magic; //!< "ASET"
|
||||
|
@ -29,7 +29,7 @@ namespace skyline::loader {
|
||||
return outputBuffer;
|
||||
}
|
||||
|
||||
Loader::ExecutableLoadInfo NsoLoader::LoadNso(Loader *loader, const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state, size_t offset, const std::string& name) {
|
||||
Loader::ExecutableLoadInfo NsoLoader::LoadNso(Loader *loader, const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state, size_t offset, const std::string &name) {
|
||||
auto header{backing->Read<NsoHeader>()};
|
||||
|
||||
if (header.magic != util::MakeMagic<u32>("NSO0"))
|
||||
|
@ -35,7 +35,7 @@ namespace skyline::loader {
|
||||
static_assert(sizeof(NsoSegmentHeader) == 0xC);
|
||||
|
||||
struct NsoRelativeSegmentHeader {
|
||||
u32 offset; //!< The offset of the segment into it's parent segment
|
||||
u32 offset; //!< The offset of the segment into its parent segment
|
||||
u32 size; //!< Size of the segment
|
||||
};
|
||||
static_assert(sizeof(NsoRelativeSegmentHeader) == 0x8);
|
||||
|
@ -35,7 +35,7 @@ namespace skyline {
|
||||
/**
|
||||
* @brief Writes a vector of 128-bit user IDs to an output buffer
|
||||
*/
|
||||
Result WriteUserList(span <u8> buffer, std::vector <UserId> userIds);
|
||||
Result WriteUserList(span <u8> buffer, std::vector<UserId> userIds);
|
||||
|
||||
public:
|
||||
IAccountServiceForApplication(const DeviceState &state, ServiceManager &manager);
|
||||
@ -76,16 +76,14 @@ namespace skyline {
|
||||
*/
|
||||
Result GetBaasAccountManagerForApplication(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL (
|
||||
SFUNC(
|
||||
|
||||
0x1, IAccountServiceForApplication, GetUserExistence),
|
||||
SFUNC(0x2, IAccountServiceForApplication, ListAllUsers),
|
||||
SFUNC(0x3, IAccountServiceForApplication, ListOpenUsers),
|
||||
SFUNC(0x4, IAccountServiceForApplication, GetLastOpenedUser),
|
||||
SFUNC(0x5, IAccountServiceForApplication, GetProfile),
|
||||
SFUNC(0x64, IAccountServiceForApplication, InitializeApplicationInfoV0),
|
||||
SFUNC(0x65, IAccountServiceForApplication, GetBaasAccountManagerForApplication)
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x1, IAccountServiceForApplication, GetUserExistence),
|
||||
SFUNC(0x2, IAccountServiceForApplication, ListAllUsers),
|
||||
SFUNC(0x3, IAccountServiceForApplication, ListOpenUsers),
|
||||
SFUNC(0x4, IAccountServiceForApplication, GetLastOpenedUser),
|
||||
SFUNC(0x5, IAccountServiceForApplication, GetProfile),
|
||||
SFUNC(0x64, IAccountServiceForApplication, InitializeApplicationInfoV0),
|
||||
SFUNC(0x65, IAccountServiceForApplication, GetBaasAccountManagerForApplication)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
namespace skyline::service::am {
|
||||
/**
|
||||
* @brief This is used to notify an application about it's own state
|
||||
* @brief This is used to notify an application about its own state
|
||||
* @url https://switchbrew.org/wiki/Applet_Manager_services#IApplicationFunctions
|
||||
*/
|
||||
class IApplicationFunctions : public BaseService {
|
||||
|
@ -31,7 +31,7 @@ namespace skyline::service {
|
||||
*/
|
||||
class BaseService {
|
||||
private:
|
||||
std::string name; //!< The name of the service, it is only assigned after GetName is called and shouldn't be used directly
|
||||
std::string name; //!< The name of the service, it's only assigned after GetName is called and shouldn't be used directly
|
||||
|
||||
protected:
|
||||
const DeviceState &state;
|
||||
|
@ -30,7 +30,7 @@ namespace skyline::service {
|
||||
/**
|
||||
* @brief This constructor fills in the Parcel object with data from a IPC buffer
|
||||
* @param buffer The buffer that contains the parcel
|
||||
* @param hasToken If the parcel starts with a token, it is skipped if this flag is true
|
||||
* @param hasToken If the parcel starts with a token, it's skipped if this flag is true
|
||||
*/
|
||||
Parcel(span<u8> buffer, const DeviceState &state, bool hasToken = false);
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace skyline::service::hid {
|
||||
Result ActivateDebugPad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Activates the touch screen (if it's disabled, it is enabled by default)
|
||||
* @brief Activates the touch screen (if it's disabled, it's enabled by default)
|
||||
*/
|
||||
Result ActivateTouchScreen(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace skyline::service::hosbinder {
|
||||
Parcel out(state);
|
||||
|
||||
// We opted for just supporting a single layer and display as it's what basically all games use and wasting cycles on it is pointless
|
||||
// If this was not done then we would need to maintain an array of GraphicBufferProducer objects for each layer and send the request it specifically
|
||||
// If this was not done then we would need to maintain an array of GraphicBufferProducer objects for each layer and send the request for it specifically
|
||||
// There would also need to be an external compositor which composites all the graphics buffers submitted to every GraphicBufferProducer
|
||||
|
||||
state.logger->Debug("TransactParcel: Layer ID: {}, Code: {}", layerId, code);
|
||||
|
@ -25,13 +25,13 @@ namespace skyline::service::hosbinder {
|
||||
Result TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Adjusts the reference counts to the underlying binder, it is stubbed as we aren't using the real symbols
|
||||
* @brief Adjusts the reference counts to the underlying binder, it's stubbed as we aren't using the real symbols
|
||||
* @url https://switchbrew.org/wiki/Nvnflinger_services#AdjustRefcount
|
||||
*/
|
||||
Result AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Adjusts the reference counts to the underlying binder, it is stubbed as we aren't using the real symbols
|
||||
* @brief Adjusts the reference counts to the underlying binder, it's stubbed as we aren't using the real symbols
|
||||
* @url https://switchbrew.org/wiki/Nvnflinger_services#GetNativeHandle
|
||||
*/
|
||||
Result GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
@ -55,7 +55,7 @@ namespace skyline {
|
||||
class NvHostCtrl : public NvDevice {
|
||||
private:
|
||||
/**
|
||||
* @brief Metadata about an event, it is used by QueryEvent and EventWait
|
||||
* @brief Metadata about an event, it's used by QueryEvent and EventWait
|
||||
*/
|
||||
union EventValue {
|
||||
u32 val;
|
||||
|
@ -56,7 +56,7 @@ namespace skyline::service::nvdrv::device {
|
||||
NvStatus Create(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
|
||||
/**
|
||||
* @brief Returns the handle of an NvMapObject from it's ID
|
||||
* @brief Returns the handle of an NvMapObject from its ID
|
||||
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FROM_ID
|
||||
*/
|
||||
NvStatus FromId(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
@ -80,7 +80,7 @@ namespace skyline::service::nvdrv::device {
|
||||
NvStatus Param(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
|
||||
/**
|
||||
* @brief Returns the ID of an NvMapObject from it's handle
|
||||
* @brief Returns the ID of an NvMapObject from its handle
|
||||
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_GET_ID
|
||||
*/
|
||||
NvStatus GetId(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
|
@ -67,7 +67,7 @@ namespace skyline::service::nvdrv {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Closes the specified device with it's file descriptor
|
||||
* @brief Closes the specified device with its file descriptor
|
||||
*/
|
||||
void CloseDevice(u32 fd);
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ namespace skyline::service {
|
||||
ServiceManager(const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief Creates a new service using it's type enum and writes it's handle or virtual handle (If it's a domain request) to IpcResponse
|
||||
* @brief Creates a new service using its type enum and writes its handle or virtual handle (If it's a domain request) to IpcResponse
|
||||
* @param name The service's name
|
||||
* @param session The session object of the command
|
||||
* @param response The response object to write the handle or virtual handle to
|
||||
@ -37,7 +37,7 @@ namespace skyline::service {
|
||||
std::shared_ptr<BaseService> NewService(ServiceName name, type::KSession &session, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Registers a service object in the manager and writes it's handle or virtual handle (If it's a domain request) to IpcResponse
|
||||
* @brief Registers a service object in the manager and writes its handle or virtual handle (If it's a domain request) to IpcResponse
|
||||
* @param serviceObject An instance of the service
|
||||
* @param session The session object of the command
|
||||
* @param response The response object to write the handle or virtual handle to
|
||||
|
@ -26,7 +26,7 @@ namespace skyline::service {
|
||||
Result GetAvailableLanguageCodes(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Converts a language code list index to it's corresponding language code
|
||||
* @brief Converts a language code list index to its corresponding language code
|
||||
*/
|
||||
Result MakeLanguageCode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace skyline::service::sm {
|
||||
Result Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Returns a handle to a service with it's name passed in as an argument
|
||||
* @brief Returns a handle to a service with its name passed in as an argument
|
||||
* @url https://switchbrew.org/wiki/Services_API#GetService
|
||||
*/
|
||||
Result GetService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
@ -39,13 +39,13 @@ namespace skyline::service::visrv {
|
||||
Result GetManagerDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Opens up a display using it's name as the input
|
||||
* @brief Opens up a display using its name as the input
|
||||
* @url https://switchbrew.org/wiki/Display_services#OpenDisplay
|
||||
*/
|
||||
Result OpenDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Closes an open display using it's ID
|
||||
* @brief Closes an open display using its ID
|
||||
* @url https://switchbrew.org/wiki/Display_services#CloseDisplay
|
||||
*/
|
||||
Result CloseDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
@ -33,7 +33,7 @@ namespace skyline::service::visrv {
|
||||
Result CreateStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Destroys a stray layer by it's ID
|
||||
* @brief Destroys a stray layer by its ID
|
||||
*/
|
||||
Result DestroyStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
};
|
||||
|
@ -51,7 +51,7 @@ namespace skyline::vfs {
|
||||
std::vector<NpdmKernelCapability> capabilities(aci0.kernelCapability.size / sizeof(NpdmKernelCapability));
|
||||
backing->Read(span(capabilities), meta.aci0.offset + aci0.kernelCapability.offset);
|
||||
for (auto capability : capabilities) {
|
||||
auto trailingOnes{__builtin_ctz(~capability.raw)};
|
||||
auto trailingOnes{std::countr_zero(~capability.raw)};
|
||||
switch (trailingOnes) {
|
||||
case 3:
|
||||
threadInfo.priority = kernel::Priority{capability.threadInfo.highestPriority, capability.threadInfo.lowestPriority};
|
||||
@ -65,6 +65,9 @@ namespace skyline::vfs {
|
||||
kernelVersion.minorVersion = capability.kernelVersion.minorVersion;
|
||||
kernelVersion.majorVersion = capability.kernelVersion.majorVersion;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
namespace skyline {
|
||||
namespace constant {
|
||||
constexpr u32 RomFsEmptyEntry{0xFFFFFFFF}; //!< The value a RomFS entry has it's offset set to if it is empty
|
||||
constexpr u32 RomFsEmptyEntry{0xFFFFFFFF}; //!< The value a RomFS entry has its offset set to, if it's empty
|
||||
}
|
||||
|
||||
namespace vfs {
|
||||
|
@ -241,6 +241,8 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
if (stopEmulation())
|
||||
emulationThread.join()
|
||||
|
||||
vibrators.forEach { (_, vibrator) -> vibrator.cancel() }
|
||||
|
||||
shouldFinish = true
|
||||
|
||||
executeApplication(intent?.data!!)
|
||||
@ -410,7 +412,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
|
||||
ButtonId.RightStick -> StickId.Right
|
||||
|
||||
else -> error("Invalid button id")
|
||||
else -> error("Invalid button ID")
|
||||
}
|
||||
setAxisValue(0, stickId.xAxis.ordinal, (position.x * Short.MAX_VALUE).toInt())
|
||||
setAxisValue(0, stickId.yAxis.ordinal, (-position.y * Short.MAX_VALUE).toInt()) // Y is inverted, since drawing starts from top left
|
||||
|
Loading…
Reference in New Issue
Block a user