mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 22:15:29 +03:00
Improve Synchronization SVCs + Fix TLS Page Allocation Race + Fix KProcess::GetHandle<KObject>
This commit is contained in:
parent
ebadc1d1e1
commit
ef52e22cef
@ -48,6 +48,7 @@ add_library(skyline SHARED
|
|||||||
${source_DIR}/skyline/kernel/types/KThread.cpp
|
${source_DIR}/skyline/kernel/types/KThread.cpp
|
||||||
${source_DIR}/skyline/kernel/types/KSharedMemory.cpp
|
${source_DIR}/skyline/kernel/types/KSharedMemory.cpp
|
||||||
${source_DIR}/skyline/kernel/types/KPrivateMemory.cpp
|
${source_DIR}/skyline/kernel/types/KPrivateMemory.cpp
|
||||||
|
${source_DIR}/skyline/kernel/types/KSyncObject.cpp
|
||||||
${source_DIR}/skyline/audio.cpp
|
${source_DIR}/skyline/audio.cpp
|
||||||
${source_DIR}/skyline/audio/track.cpp
|
${source_DIR}/skyline/audio/track.cpp
|
||||||
${source_DIR}/skyline/audio/resampler.cpp
|
${source_DIR}/skyline/audio/resampler.cpp
|
||||||
|
@ -9,9 +9,7 @@ extern skyline::u16 Fps;
|
|||||||
extern skyline::u32 FrameTime;
|
extern skyline::u32 FrameTime;
|
||||||
|
|
||||||
namespace skyline::gpu {
|
namespace skyline::gpu {
|
||||||
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {
|
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state, true)), bufferEvent(std::make_shared<kernel::type::KEvent>(state, true)) {}
|
||||||
vsyncEvent->Signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
PresentationEngine::~PresentationEngine() {
|
PresentationEngine::~PresentationEngine() {
|
||||||
if (window)
|
if (window)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "npad.h"
|
#include "npad.h"
|
||||||
|
|
||||||
namespace skyline::input {
|
namespace skyline::input {
|
||||||
NpadDevice::NpadDevice(NpadManager &manager, NpadSection §ion, NpadId id) : manager(manager), section(section), id(id), updateEvent(std::make_shared<kernel::type::KEvent>(manager.state)) {}
|
NpadDevice::NpadDevice(NpadManager &manager, NpadSection §ion, NpadId id) : manager(manager), section(section), id(id), updateEvent(std::make_shared<kernel::type::KEvent>(manager.state, false)) {}
|
||||||
|
|
||||||
void NpadDevice::Connect(NpadControllerType newType) {
|
void NpadDevice::Connect(NpadControllerType newType) {
|
||||||
if (type == newType) {
|
if (type == newType) {
|
||||||
|
@ -524,11 +524,8 @@ namespace skyline::kernel::svc {
|
|||||||
auto object{state.process->GetHandle(handle)};
|
auto object{state.process->GetHandle(handle)};
|
||||||
switch (object->objectType) {
|
switch (object->objectType) {
|
||||||
case type::KType::KEvent:
|
case type::KType::KEvent:
|
||||||
std::static_pointer_cast<type::KEvent>(object)->ResetSignal();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case type::KType::KProcess:
|
case type::KType::KProcess:
|
||||||
std::static_pointer_cast<type::KProcess>(object)->ResetSignal();
|
std::static_pointer_cast<type::KSyncObject>(object)->ResetSignal();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
@ -552,17 +549,18 @@ namespace skyline::kernel::svc {
|
|||||||
|
|
||||||
u32 numHandles{state.ctx->gpr.w2};
|
u32 numHandles{state.ctx->gpr.w2};
|
||||||
if (numHandles > maxSyncHandles) {
|
if (numHandles > maxSyncHandles) {
|
||||||
state.ctx->gpr.w0 = result::OutOfHandles;
|
state.ctx->gpr.w0 = result::OutOfRange;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string handleStr;
|
|
||||||
std::vector<std::shared_ptr<type::KSyncObject>> objectTable;
|
|
||||||
span waitHandles(reinterpret_cast<KHandle *>(state.ctx->gpr.x1), numHandles);
|
span waitHandles(reinterpret_cast<KHandle *>(state.ctx->gpr.x1), numHandles);
|
||||||
|
std::vector<std::shared_ptr<type::KSyncObject>> objectTable;
|
||||||
|
objectTable.reserve(numHandles);
|
||||||
|
|
||||||
|
std::string handleString;
|
||||||
for (const auto &handle : waitHandles) {
|
for (const auto &handle : waitHandles) {
|
||||||
if (Logger::LogLevel::Debug <= state.logger->configLevel)
|
if (Logger::LogLevel::Debug <= state.logger->configLevel)
|
||||||
handleStr += fmt::format("* 0x{:X}\n", handle);
|
handleString += fmt::format("* 0x{:X}\n", handle);
|
||||||
|
|
||||||
auto object{state.process->GetHandle(handle)};
|
auto object{state.process->GetHandle(handle)};
|
||||||
switch (object->objectType) {
|
switch (object->objectType) {
|
||||||
@ -574,46 +572,99 @@ namespace skyline::kernel::svc {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
state.logger->Debug("svcWaitSynchronization: An invalid handle was supplied: 0x{:X}", handle);
|
||||||
state.ctx->gpr.w0 = result::InvalidHandle;
|
state.ctx->gpr.w0 = result::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 timeout{state.ctx->gpr.x3};
|
i64 timeout{static_cast<i64>(state.ctx->gpr.x3)};
|
||||||
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
|
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: {}ns", handleString, timeout);
|
||||||
|
|
||||||
SchedulerScopedLock schedulerLock(state);
|
std::unique_lock lock(type::KSyncObject::syncObjectMutex);
|
||||||
auto start{util::GetTimeNs()};
|
if (state.thread->cancelSync) {
|
||||||
while (true) {
|
state.thread->cancelSync = false;
|
||||||
if (state.thread->cancelSync) {
|
state.ctx->gpr.w0 = result::Cancelled;
|
||||||
state.thread->cancelSync = false;
|
return;
|
||||||
state.ctx->gpr.w0 = result::Cancelled;
|
}
|
||||||
|
|
||||||
|
u32 index{};
|
||||||
|
for (const auto &object : objectTable) {
|
||||||
|
if (object->signalled) {
|
||||||
|
state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", waitHandles[index]);
|
||||||
|
state.ctx->gpr.w0 = Result{};
|
||||||
|
state.ctx->gpr.w1 = index;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
u32 index{};
|
if (timeout == 0) {
|
||||||
for (const auto &object : objectTable) {
|
state.logger->Debug("svcWaitSynchronization: No handle is currently signalled");
|
||||||
if (object->signalled) {
|
state.ctx->gpr.w0 = result::TimedOut;
|
||||||
state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", waitHandles[index]);
|
return;
|
||||||
state.ctx->gpr.w0 = Result{};
|
}
|
||||||
state.ctx->gpr.w1 = index;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((util::GetTimeNs() - start) >= timeout) {
|
auto priority{state.thread->priority.load()};
|
||||||
state.logger->Debug("svcWaitSynchronization: Wait has timed out");
|
for (const auto &object : objectTable)
|
||||||
state.ctx->gpr.w0 = result::TimedOut;
|
object->syncObjectWaiters.insert(std::upper_bound(object->syncObjectWaiters.begin(), object->syncObjectWaiters.end(), priority, type::KThread::IsHigherPriority), state.thread);
|
||||||
return;
|
|
||||||
}
|
state.thread->isCancellable = true;
|
||||||
|
state.thread->wakeObject = nullptr;
|
||||||
|
state.scheduler->RemoveThread();
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
if (timeout > 0)
|
||||||
|
state.scheduler->TimedWaitSchedule(std::chrono::nanoseconds(timeout));
|
||||||
|
else
|
||||||
|
state.scheduler->WaitSchedule(false);
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
state.thread->isCancellable = false;
|
||||||
|
auto wakeObject{state.thread->wakeObject};
|
||||||
|
|
||||||
|
u32 wakeIndex{};
|
||||||
|
index = 0;
|
||||||
|
for (const auto &object : objectTable) {
|
||||||
|
if (object.get() == wakeObject)
|
||||||
|
wakeIndex = index;
|
||||||
|
|
||||||
|
auto it{std::find(object->syncObjectWaiters.begin(), object->syncObjectWaiters.end(), state.thread)};
|
||||||
|
if (it != object->syncObjectWaiters.end())
|
||||||
|
object->syncObjectWaiters.erase(it);
|
||||||
|
else
|
||||||
|
throw exception("svcWaitSynchronization: An object (0x{:X}) has been removed from the syncObjectWaiters queue incorrectly", waitHandles[index]);
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wakeObject) {
|
||||||
|
state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", waitHandles[index]);
|
||||||
|
state.ctx->gpr.w0 = Result{};
|
||||||
|
state.ctx->gpr.w1 = wakeIndex;
|
||||||
|
} else if (state.thread->cancelSync) {
|
||||||
|
state.thread->cancelSync = false;
|
||||||
|
state.logger->Debug("svcWaitSynchronization: Wait has been cancelled");
|
||||||
|
state.ctx->gpr.w0 = result::Cancelled;
|
||||||
|
} else {
|
||||||
|
state.logger->Debug("svcWaitSynchronization: Wait has timed out");
|
||||||
|
state.ctx->gpr.w0 = result::TimedOut;
|
||||||
|
lock.unlock();
|
||||||
|
state.scheduler->InsertThread(state.thread);
|
||||||
|
state.scheduler->WaitSchedule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CancelSynchronization(const DeviceState &state) {
|
void CancelSynchronization(const DeviceState &state) {
|
||||||
try {
|
try {
|
||||||
state.process->GetHandle<type::KThread>(state.ctx->gpr.w0)->cancelSync = true;
|
std::unique_lock lock(type::KSyncObject::syncObjectMutex);
|
||||||
|
auto thread{state.process->GetHandle<type::KThread>(state.ctx->gpr.w0)};
|
||||||
|
thread->cancelSync = true;
|
||||||
|
if (thread->isCancellable) {
|
||||||
|
thread->isCancellable = false;
|
||||||
|
state.scheduler->InsertThread(thread);
|
||||||
|
}
|
||||||
} catch (const std::out_of_range &) {
|
} catch (const std::out_of_range &) {
|
||||||
state.logger->Warn("svcCancelSynchronization: 'handle' invalid: 0x{:X}", static_cast<u32>(state.ctx->gpr.w0));
|
state.logger->Warn("svcCancelSynchronization: 'handle' invalid: 0x{:X}", static_cast<u32>(state.ctx->gpr.w0));
|
||||||
state.ctx->gpr.w0 = result::InvalidHandle;
|
state.ctx->gpr.w0 = result::InvalidHandle;
|
||||||
@ -878,7 +929,7 @@ namespace skyline::kernel::svc {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case InfoState::UserExceptionContextAddr:
|
case InfoState::UserExceptionContextAddr:
|
||||||
out = reinterpret_cast<u64>(state.process->tlsPages[0]->Get(0));
|
out = reinterpret_cast<u64>(state.process->tlsExceptionContext);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -12,13 +12,9 @@ namespace skyline::kernel::type {
|
|||||||
*/
|
*/
|
||||||
class KEvent : public KSyncObject {
|
class KEvent : public KSyncObject {
|
||||||
public:
|
public:
|
||||||
KEvent(const DeviceState &state) : KSyncObject(state, KType::KEvent) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Resets the KEvent to an unsignalled state
|
* @param presignalled If this object should be signalled initially or not
|
||||||
*/
|
*/
|
||||||
inline void ResetSignal() {
|
KEvent(const DeviceState &state, bool presignalled) : KSyncObject(state, KType::KEvent, presignalled) {}
|
||||||
signalled = false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -10,19 +10,9 @@ namespace skyline::kernel::type {
|
|||||||
KProcess::TlsPage::TlsPage(const std::shared_ptr<KPrivateMemory> &memory) : memory(memory) {}
|
KProcess::TlsPage::TlsPage(const std::shared_ptr<KPrivateMemory> &memory) : memory(memory) {}
|
||||||
|
|
||||||
u8 *KProcess::TlsPage::ReserveSlot() {
|
u8 *KProcess::TlsPage::ReserveSlot() {
|
||||||
if (Full())
|
if (index == constant::TlsSlots)
|
||||||
throw exception("Trying to reserve TLS slot in full page");
|
return nullptr;
|
||||||
return Get(index++);
|
return memory->ptr + (constant::TlsSlotSize * index++);
|
||||||
}
|
|
||||||
|
|
||||||
u8 *KProcess::TlsPage::Get(u8 slot) {
|
|
||||||
if (slot >= constant::TlsSlots)
|
|
||||||
throw exception("TLS slot is out of range");
|
|
||||||
return memory->ptr + (constant::TlsSlotSize * slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KProcess::TlsPage::Full() {
|
|
||||||
return index == constant::TlsSlots;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcess::KProcess(const DeviceState &state) : memory(state), KSyncObject(state, KType::KProcess) {}
|
KProcess::KProcess(const DeviceState &state) : memory(state), KSyncObject(state, KType::KProcess) {}
|
||||||
@ -52,22 +42,23 @@ namespace skyline::kernel::type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KProcess::InitializeHeap() {
|
void KProcess::InitializeHeapTls() {
|
||||||
constexpr size_t DefaultHeapSize{0x200000};
|
constexpr size_t DefaultHeapSize{0x200000};
|
||||||
heap = heap.make_shared(state, reinterpret_cast<u8 *>(state.process->memory.heap.address), DefaultHeapSize, memory::Permission{true, true, false}, memory::states::Heap);
|
heap = heap.make_shared(state, reinterpret_cast<u8 *>(state.process->memory.heap.address), DefaultHeapSize, memory::Permission{true, true, false}, memory::states::Heap);
|
||||||
InsertItem(heap); // Insert it into the handle table so GetMemoryObject will contain it
|
InsertItem(heap); // Insert it into the handle table so GetMemoryObject will contain it
|
||||||
|
tlsExceptionContext = AllocateTlsSlot();
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 *KProcess::AllocateTlsSlot() {
|
u8 *KProcess::AllocateTlsSlot() {
|
||||||
|
std::lock_guard lock(tlsMutex);
|
||||||
|
u8 *slot;
|
||||||
for (auto &tlsPage: tlsPages)
|
for (auto &tlsPage: tlsPages)
|
||||||
if (!tlsPage->Full())
|
if ((slot = tlsPage->ReserveSlot()))
|
||||||
return tlsPage->ReserveSlot();
|
return slot;
|
||||||
|
|
||||||
u8 *ptr = tlsPages.empty() ? reinterpret_cast<u8 *>(state.process->memory.tlsIo.address) : ((*(tlsPages.end() - 1))->memory->ptr + PAGE_SIZE);
|
slot = tlsPages.empty() ? reinterpret_cast<u8 *>(memory.tlsIo.address) : ((*(tlsPages.end() - 1))->memory->ptr + PAGE_SIZE);
|
||||||
auto tlsPage{std::make_shared<TlsPage>(std::make_shared<KPrivateMemory>(state, ptr, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal))};
|
auto tlsPage{std::make_shared<TlsPage>(std::make_shared<KPrivateMemory>(state, slot, PAGE_SIZE, memory::Permission(true, true, false), memory::states::ThreadLocal))};
|
||||||
tlsPages.push_back(tlsPage);
|
tlsPages.push_back(tlsPage);
|
||||||
|
|
||||||
tlsPage->ReserveSlot(); // User-mode exception handling
|
|
||||||
return tlsPage->ReserveSlot();
|
return tlsPage->ReserveSlot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <vfs/npdm.h>
|
#include <vfs/npdm.h>
|
||||||
#include "KThread.h"
|
#include "KThread.h"
|
||||||
#include "KTransferMemory.h"
|
#include "KTransferMemory.h"
|
||||||
@ -30,7 +29,7 @@ namespace skyline {
|
|||||||
bool disableThreadCreation{}; //!< If to disable thread creation, we use this to prevent thread creation after all threads have been killed
|
bool disableThreadCreation{}; //!< If to disable thread creation, we use this to prevent thread creation after all threads have been killed
|
||||||
std::vector<std::shared_ptr<KThread>> threads;
|
std::vector<std::shared_ptr<KThread>> threads;
|
||||||
|
|
||||||
using SyncWaiters = std::multimap<void*, std::shared_ptr<KThread>>;
|
using SyncWaiters = std::multimap<void *, std::shared_ptr<KThread>>;
|
||||||
std::mutex syncWaiterMutex; //!< Synchronizes all mutations to the map to prevent races
|
std::mutex syncWaiterMutex; //!< Synchronizes all mutations to the map to prevent races
|
||||||
SyncWaiters syncWaiters; //!< All threads waiting on process-wide synchronization primitives (Atomic keys + Address Arbiter)
|
SyncWaiters syncWaiters; //!< All threads waiting on process-wide synchronization primitives (Atomic keys + Address Arbiter)
|
||||||
|
|
||||||
@ -42,21 +41,23 @@ namespace skyline {
|
|||||||
*/
|
*/
|
||||||
struct TlsPage {
|
struct TlsPage {
|
||||||
u8 index{}; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved
|
u8 index{}; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved
|
||||||
std::shared_ptr<KPrivateMemory> memory;
|
std::shared_ptr<KPrivateMemory> memory; //!< A single page sized memory allocation for this TLS page
|
||||||
|
|
||||||
TlsPage(const std::shared_ptr<KPrivateMemory> &memory);
|
TlsPage(const std::shared_ptr<KPrivateMemory> &memory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
u8 *ReserveSlot();
|
u8 *ReserveSlot();
|
||||||
|
|
||||||
u8 *Get(u8 slot);
|
|
||||||
|
|
||||||
bool Full();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<KPrivateMemory> mainThreadStack;
|
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
|
||||||
std::shared_ptr<KPrivateMemory> heap;
|
std::shared_ptr<KPrivateMemory> heap;
|
||||||
std::vector<std::shared_ptr<TlsPage>> tlsPages;
|
|
||||||
vfs::NPDM npdm;
|
vfs::NPDM npdm;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -79,9 +80,10 @@ namespace skyline {
|
|||||||
void Kill(bool join, bool all = false, bool disableCreation = false);
|
void Kill(bool join, bool all = false, bool disableCreation = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @brief This initializes the process heap and TLS Error Context slot pointer, it should be called prior to creating the first thread
|
||||||
* @note This requires VMM regions to be initialized, it will map heap at an arbitrary location otherwise
|
* @note This requires VMM regions to be initialized, it will map heap at an arbitrary location otherwise
|
||||||
*/
|
*/
|
||||||
void InitializeHeap();
|
void InitializeHeapTls();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A 0x200 TLS slot allocated inside the TLS/IO region
|
* @return A 0x200 TLS slot allocated inside the TLS/IO region
|
||||||
@ -177,7 +179,12 @@ namespace skyline {
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
std::shared_ptr<KObject> GetHandle<KObject>(KHandle handle) {
|
std::shared_ptr<KObject> GetHandle<KObject>(KHandle handle) {
|
||||||
return handles.at(handle - constant::BaseHandleIndex);
|
std::shared_lock lock(handleMutex);
|
||||||
|
auto &item{handles.at(handle - constant::BaseHandleIndex)};
|
||||||
|
if (item != nullptr)
|
||||||
|
return item;
|
||||||
|
else
|
||||||
|
throw std::out_of_range(fmt::format("GetHandle was called with a deleted handle: 0x{:X}", handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -215,13 +222,6 @@ namespace skyline {
|
|||||||
* @brief Signals the conditional variable at the specified address
|
* @brief Signals the conditional variable at the specified address
|
||||||
*/
|
*/
|
||||||
void ConditionalVariableSignal(u32 *key, u64 amount);
|
void ConditionalVariableSignal(u32 *key, u64 amount);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Resets the object to an unsignalled state
|
|
||||||
*/
|
|
||||||
inline void ResetSignal() {
|
|
||||||
signalled = false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp
Normal file
19
app/src/main/cpp/skyline/kernel/types/KSyncObject.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include "KSyncObject.h"
|
||||||
|
#include "KThread.h"
|
||||||
|
|
||||||
|
namespace skyline::kernel::type {
|
||||||
|
void KSyncObject::Signal() {
|
||||||
|
std::lock_guard lock(syncObjectMutex);
|
||||||
|
signalled = true;
|
||||||
|
for (auto& waiter : syncObjectWaiters) {
|
||||||
|
if (waiter->isCancellable) {
|
||||||
|
waiter->isCancellable = false;
|
||||||
|
waiter->wakeObject = this;
|
||||||
|
state.scheduler->InsertThread(waiter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,19 +7,31 @@
|
|||||||
|
|
||||||
namespace skyline::kernel::type {
|
namespace skyline::kernel::type {
|
||||||
/**
|
/**
|
||||||
* @brief KSyncObject holds the state of a waitable object
|
* @brief KSyncObject is an abstract class which holds everything necessary for an object to be synchronizable
|
||||||
|
* @note This abstraction is roughly equivalent to KSynchronizationObject on HOS
|
||||||
*/
|
*/
|
||||||
class KSyncObject : public KObject {
|
class KSyncObject : public KObject {
|
||||||
public:
|
public:
|
||||||
std::atomic<bool> signalled{false}; //!< If the current object is signalled (Used as object stays signalled till the signal is consumed)
|
inline static std::mutex syncObjectMutex; //!< A global lock used for locking all signalling to avoid races
|
||||||
|
std::list<std::shared_ptr<KThread>> syncObjectWaiters; //!< A list of threads waiting on this object to be signalled
|
||||||
KSyncObject(const DeviceState &state, skyline::kernel::type::KType type) : KObject(state, type) {};
|
std::atomic<bool> signalled; //!< If the current object is signalled (An object stays signalled till the signal has been explicitly reset)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A function for calling when a particular KSyncObject is signalled
|
* @param presignalled If this object should be signalled initially or not
|
||||||
*/
|
*/
|
||||||
virtual void Signal() {
|
KSyncObject(const DeviceState &state, skyline::kernel::type::KType type, bool presignalled = false) : KObject(state, type), signalled(presignalled) {};
|
||||||
signalled = true;
|
|
||||||
|
/**
|
||||||
|
* @brief Wakes up any waiters on this object and flips the 'signalled' flag
|
||||||
|
*/
|
||||||
|
void Signal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resets the object to an unsignalled state
|
||||||
|
*/
|
||||||
|
inline void ResetSignal() {
|
||||||
|
std::lock_guard lock(syncObjectMutex);
|
||||||
|
signalled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~KSyncObject() = default;
|
virtual ~KSyncObject() = default;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <csetjmp>
|
#include <csetjmp>
|
||||||
#include <nce/guest.h>
|
#include <nce/guest.h>
|
||||||
#include <kernel/scheduler.h>
|
#include <kernel/scheduler.h>
|
||||||
@ -27,7 +26,7 @@ namespace skyline {
|
|||||||
public:
|
public:
|
||||||
std::mutex mutex; //!< Synchronizes all thread state changes
|
std::mutex mutex; //!< Synchronizes all thread state changes
|
||||||
bool running{false}; //!< If the host thread that corresponds to this thread is running, this doesn't reflect guest scheduling changes
|
bool running{false}; //!< If the host thread that corresponds to this thread is running, this doesn't reflect guest scheduling changes
|
||||||
std::atomic<bool> cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in
|
bool killed{false}; //!< If this thread was previously running and has been killed
|
||||||
|
|
||||||
KHandle handle;
|
KHandle handle;
|
||||||
size_t id; //!< Index of thread in parent process's KThread vector
|
size_t id; //!< Index of thread in parent process's KThread vector
|
||||||
@ -54,6 +53,9 @@ namespace skyline {
|
|||||||
KHandle waitTag; //!< The handle of the thread which requested the mutex lock
|
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::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
|
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
|
||||||
|
|
||||||
KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, u8 priority, i8 idealCore);
|
KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, u8 priority, i8 idealCore);
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ namespace skyline::kernel {
|
|||||||
auto &process{state.process};
|
auto &process{state.process};
|
||||||
process = std::make_shared<kernel::type::KProcess>(state);
|
process = std::make_shared<kernel::type::KProcess>(state);
|
||||||
auto entry{state.loader->LoadProcessData(process, state)};
|
auto entry{state.loader->LoadProcessData(process, state)};
|
||||||
process->InitializeHeap();
|
process->InitializeHeapTls();
|
||||||
auto thread{process->CreateThread(entry)};
|
auto thread{process->CreateThread(entry)};
|
||||||
if (thread) {
|
if (thread) {
|
||||||
state.logger->Debug("Starting main HOS thread");
|
state.logger->Debug("Starting main HOS thread");
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include "ILibraryAppletAccessor.h"
|
#include "ILibraryAppletAccessor.h"
|
||||||
|
|
||||||
namespace skyline::service::am {
|
namespace skyline::service::am {
|
||||||
ILibraryAppletAccessor::ILibraryAppletAccessor(const DeviceState &state, ServiceManager &manager) : stateChangeEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager) {}
|
ILibraryAppletAccessor::ILibraryAppletAccessor(const DeviceState &state, ServiceManager &manager) : stateChangeEvent(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {}
|
||||||
|
|
||||||
Result ILibraryAppletAccessor::GetAppletStateChangedEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result ILibraryAppletAccessor::GetAppletStateChangedEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
stateChangeEvent->Signal();
|
stateChangeEvent->Signal();
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include "IApplicationFunctions.h"
|
#include "IApplicationFunctions.h"
|
||||||
|
|
||||||
namespace skyline::service::am {
|
namespace skyline::service::am {
|
||||||
IApplicationFunctions::IApplicationFunctions(const DeviceState &state, ServiceManager &manager) : gpuErrorEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager) {}
|
IApplicationFunctions::IApplicationFunctions(const DeviceState &state, ServiceManager &manager) : gpuErrorEvent(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {}
|
||||||
|
|
||||||
Result IApplicationFunctions::PopLaunchParameter(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result IApplicationFunctions::PopLaunchParameter(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
constexpr u32 LaunchParameterMagic{0xC79497CA}; //!< The magic of the application launch parameters
|
constexpr u32 LaunchParameterMagic{0xC79497CA}; //!< The magic of the application launch parameters
|
||||||
|
@ -11,7 +11,7 @@ namespace skyline::service::am {
|
|||||||
messageEvent->Signal();
|
messageEvent->Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
ICommonStateGetter::ICommonStateGetter(const DeviceState &state, ServiceManager &manager) : messageEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager) {
|
ICommonStateGetter::ICommonStateGetter(const DeviceState &state, ServiceManager &manager) : messageEvent(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {
|
||||||
operationMode = static_cast<OperationMode>(state.settings->GetBool("operation_mode"));
|
operationMode = static_cast<OperationMode>(state.settings->GetBool("operation_mode"));
|
||||||
state.logger->Info("Switch to mode: {}", static_cast<bool>(operationMode) ? "Docked" : "Handheld");
|
state.logger->Info("Switch to mode: {}", static_cast<bool>(operationMode) ? "Docked" : "Handheld");
|
||||||
QueueMessage(Message::FocusStateChange);
|
QueueMessage(Message::FocusStateChange);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "ISelfController.h"
|
#include "ISelfController.h"
|
||||||
|
|
||||||
namespace skyline::service::am {
|
namespace skyline::service::am {
|
||||||
ISelfController::ISelfController(const DeviceState &state, ServiceManager &manager) : libraryAppletLaunchableEvent(std::make_shared<type::KEvent>(state)), accumulatedSuspendedTickChangedEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager) {}
|
ISelfController::ISelfController(const DeviceState &state, ServiceManager &manager) : libraryAppletLaunchableEvent(std::make_shared<type::KEvent>(state, false)), accumulatedSuspendedTickChangedEvent(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {}
|
||||||
|
|
||||||
Result ISelfController::LockExit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result ISelfController::LockExit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "IAudioDevice.h"
|
#include "IAudioDevice.h"
|
||||||
|
|
||||||
namespace skyline::service::audio {
|
namespace skyline::service::audio {
|
||||||
IAudioDevice::IAudioDevice(const DeviceState &state, ServiceManager &manager) : systemEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager) {}
|
IAudioDevice::IAudioDevice(const DeviceState &state, ServiceManager &manager) : systemEvent(std::make_shared<type::KEvent>(state, true)), BaseService(state, manager) {}
|
||||||
|
|
||||||
Result IAudioDevice::ListAudioDeviceName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result IAudioDevice::ListAudioDeviceName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
span buffer{request.outputBuf.at(0)};
|
span buffer{request.outputBuf.at(0)};
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "IAudioOut.h"
|
#include "IAudioOut.h"
|
||||||
|
|
||||||
namespace skyline::service::audio {
|
namespace skyline::service::audio {
|
||||||
IAudioOut::IAudioOut(const DeviceState &state, ServiceManager &manager, u8 channelCount, u32 sampleRate) : sampleRate(sampleRate), channelCount(channelCount), releaseEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager) {
|
IAudioOut::IAudioOut(const DeviceState &state, ServiceManager &manager, u8 channelCount, u32 sampleRate) : sampleRate(sampleRate), channelCount(channelCount), releaseEvent(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {
|
||||||
track = state.audio->OpenTrack(channelCount, constant::SampleRate, [this]() { this->releaseEvent->Signal(); });
|
track = state.audio->OpenTrack(channelCount, constant::SampleRate, [this]() { this->releaseEvent->Signal(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace skyline::service::audio::IAudioRenderer {
|
namespace skyline::service::audio::IAudioRenderer {
|
||||||
IAudioRenderer::IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParameters ¶meters)
|
IAudioRenderer::IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParameters ¶meters)
|
||||||
: systemEvent(std::make_shared<type::KEvent>(state)), parameters(parameters), BaseService(state, manager) {
|
: systemEvent(std::make_shared<type::KEvent>(state, true)), parameters(parameters), BaseService(state, manager) {
|
||||||
track = state.audio->OpenTrack(constant::ChannelCount, constant::SampleRate, []() {});
|
track = state.audio->OpenTrack(constant::ChannelCount, constant::SampleRate, []() {});
|
||||||
track->Start();
|
track->Start();
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "INotificationService.h"
|
#include "INotificationService.h"
|
||||||
|
|
||||||
namespace skyline::service::friends {
|
namespace skyline::service::friends {
|
||||||
INotificationService::INotificationService(const DeviceState &state, ServiceManager &manager) : notificationEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager) {}
|
INotificationService::INotificationService(const DeviceState &state, ServiceManager &manager) : notificationEvent(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {}
|
||||||
|
|
||||||
Result INotificationService::GetEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result INotificationService::GetEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
KHandle handle{state.process->InsertItem(notificationEvent)};
|
KHandle handle{state.process->InsertItem(notificationEvent)};
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "IRequest.h"
|
#include "IRequest.h"
|
||||||
|
|
||||||
namespace skyline::service::nifm {
|
namespace skyline::service::nifm {
|
||||||
IRequest::IRequest(const DeviceState &state, ServiceManager &manager) : event0(std::make_shared<type::KEvent>(state)), event1(std::make_shared<type::KEvent>(state)), BaseService(state, manager) {}
|
IRequest::IRequest(const DeviceState &state, ServiceManager &manager) : event0(std::make_shared<type::KEvent>(state, false)), event1(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {}
|
||||||
|
|
||||||
Result IRequest::GetRequestState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result IRequest::GetRequestState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
constexpr u32 Unsubmitted{1}; //!< The request has not been submitted
|
constexpr u32 Unsubmitted{1}; //!< The request has not been submitted
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include "nvhost_channel.h"
|
#include "nvhost_channel.h"
|
||||||
|
|
||||||
namespace skyline::service::nvdrv::device {
|
namespace skyline::service::nvdrv::device {
|
||||||
NvHostChannel::NvHostChannel(const DeviceState &state) : smExceptionBreakpointIntReportEvent(std::make_shared<type::KEvent>(state)), smExceptionBreakpointPauseReportEvent(std::make_shared<type::KEvent>(state)), errorNotifierEvent(std::make_shared<type::KEvent>(state)), NvDevice(state) {
|
NvHostChannel::NvHostChannel(const DeviceState &state) : smExceptionBreakpointIntReportEvent(std::make_shared<type::KEvent>(state, false)), smExceptionBreakpointPauseReportEvent(std::make_shared<type::KEvent>(state, false)), errorNotifierEvent(std::make_shared<type::KEvent>(state, false)), NvDevice(state) {
|
||||||
auto driver{nvdrv::driver.lock()};
|
auto driver{nvdrv::driver.lock()};
|
||||||
auto &hostSyncpoint{driver->hostSyncpoint};
|
auto &hostSyncpoint{driver->hostSyncpoint};
|
||||||
|
|
||||||
|
@ -7,17 +7,17 @@
|
|||||||
#include "nvhost_ctrl.h"
|
#include "nvhost_ctrl.h"
|
||||||
|
|
||||||
namespace skyline::service::nvdrv::device {
|
namespace skyline::service::nvdrv::device {
|
||||||
NvHostEvent::NvHostEvent(const DeviceState &state) : event(std::make_shared<type::KEvent>(state)) {}
|
NvHostEvent::NvHostEvent(const DeviceState &state) : event(std::make_shared<type::KEvent>(state, false)) {}
|
||||||
|
|
||||||
void NvHostEvent::Signal() {
|
void NvHostEvent::Signal() {
|
||||||
auto oldState{state};
|
auto oldState{state};
|
||||||
state = State::Signaling;
|
state = State::Signalling;
|
||||||
|
|
||||||
// This is to ensure that the HOS event isn't signalled when the nvhost event is cancelled
|
// This is to ensure that the HOS event isn't signalled when the nvhost event is cancelled
|
||||||
if (oldState == State::Waiting)
|
if (oldState == State::Waiting)
|
||||||
event->Signal();
|
event->Signal();
|
||||||
|
|
||||||
state = State::Signaled;
|
state = State::Signalled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvHostEvent::Cancel(const std::shared_ptr<gpu::GPU> &gpuState) {
|
void NvHostEvent::Cancel(const std::shared_ptr<gpu::GPU> &gpuState) {
|
||||||
@ -46,7 +46,7 @@ namespace skyline::service::nvdrv::device {
|
|||||||
if (events[i]) {
|
if (events[i]) {
|
||||||
const auto &event{*events[i]};
|
const auto &event{*events[i]};
|
||||||
|
|
||||||
if (event.state == NvHostEvent::State::Cancelled || event.state == NvHostEvent::State::Available || event.state == NvHostEvent::State::Signaled) {
|
if (event.state == NvHostEvent::State::Cancelled || event.state == NvHostEvent::State::Available || event.state == NvHostEvent::State::Signalled) {
|
||||||
eventIndex = i;
|
eventIndex = i;
|
||||||
|
|
||||||
// This event is already attached to the requested syncpoint, so use it
|
// This event is already attached to the requested syncpoint, so use it
|
||||||
@ -113,7 +113,7 @@ namespace skyline::service::nvdrv::device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto &event{*events.at(userEventId)};
|
auto &event{*events.at(userEventId)};
|
||||||
if (event.state == NvHostEvent::State::Cancelled || event.state == NvHostEvent::State::Available || event.state == NvHostEvent::State::Signaled) {
|
if (event.state == NvHostEvent::State::Cancelled || event.state == NvHostEvent::State::Available || event.state == NvHostEvent::State::Signalled) {
|
||||||
state.logger->Debug("Waiting on nvhost event: {} with fence: {}", userEventId, data.fence.id);
|
state.logger->Debug("Waiting on nvhost event: {} with fence: {}", userEventId, data.fence.id);
|
||||||
event.Wait(state.gpu, data.fence);
|
event.Wait(state.gpu, data.fence);
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ namespace skyline {
|
|||||||
Available = 0,
|
Available = 0,
|
||||||
Waiting = 1,
|
Waiting = 1,
|
||||||
Cancelling = 2,
|
Cancelling = 2,
|
||||||
Signaling = 3,
|
Signalling = 3,
|
||||||
Signaled = 4,
|
Signalled = 4,
|
||||||
Cancelled = 5,
|
Cancelled = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "nvhost_ctrl_gpu.h"
|
#include "nvhost_ctrl_gpu.h"
|
||||||
|
|
||||||
namespace skyline::service::nvdrv::device {
|
namespace skyline::service::nvdrv::device {
|
||||||
NvHostCtrlGpu::NvHostCtrlGpu(const DeviceState &state) : errorNotifierEvent(std::make_shared<type::KEvent>(state)), unknownEvent(std::make_shared<type::KEvent>(state)), NvDevice(state) {}
|
NvHostCtrlGpu::NvHostCtrlGpu(const DeviceState &state) : errorNotifierEvent(std::make_shared<type::KEvent>(state, false)), unknownEvent(std::make_shared<type::KEvent>(state, false)), NvDevice(state) {}
|
||||||
|
|
||||||
NvStatus NvHostCtrlGpu::ZCullGetCtxSize(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
NvStatus NvHostCtrlGpu::ZCullGetCtxSize(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
||||||
buffer.as<u32>() = 0x1;
|
buffer.as<u32>() = 0x1;
|
||||||
|
Loading…
Reference in New Issue
Block a user