Merge pull request #370 from Subv/sync_primitives

Kernel: Reworked the new kernel synchronization primitives.
This commit is contained in:
bunnei 2018-04-23 16:33:00 -04:00 committed by GitHub
commit 0214351f4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 287 additions and 527 deletions

View file

@ -42,8 +42,6 @@ add_library(core STATIC
hle/kernel/client_port.h hle/kernel/client_port.h
hle/kernel/client_session.cpp hle/kernel/client_session.cpp
hle/kernel/client_session.h hle/kernel/client_session.h
hle/kernel/condition_variable.cpp
hle/kernel/condition_variable.h
hle/kernel/errors.h hle/kernel/errors.h
hle/kernel/event.cpp hle/kernel/event.cpp
hle/kernel/event.h hle/kernel/event.h

View file

@ -1,64 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hle/kernel/condition_variable.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object_address_table.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
ConditionVariable::ConditionVariable() {}
ConditionVariable::~ConditionVariable() {}
ResultVal<SharedPtr<ConditionVariable>> ConditionVariable::Create(VAddr guest_addr,
std::string name) {
SharedPtr<ConditionVariable> condition_variable(new ConditionVariable);
condition_variable->name = std::move(name);
condition_variable->guest_addr = guest_addr;
condition_variable->mutex_addr = 0;
// Condition variables are referenced by guest address, so track this in the kernel
g_object_address_table.Insert(guest_addr, condition_variable);
return MakeResult<SharedPtr<ConditionVariable>>(std::move(condition_variable));
}
bool ConditionVariable::ShouldWait(Thread* thread) const {
return GetAvailableCount() <= 0;
}
void ConditionVariable::Acquire(Thread* thread) {
if (GetAvailableCount() <= 0)
return;
SetAvailableCount(GetAvailableCount() - 1);
}
ResultCode ConditionVariable::Release(s32 target) {
if (target == -1) {
// When -1, wake up all waiting threads
SetAvailableCount(static_cast<s32>(GetWaitingThreads().size()));
WakeupAllWaitingThreads();
} else {
// Otherwise, wake up just a single thread
SetAvailableCount(target);
WakeupWaitingThread(GetHighestPriorityReadyThread());
}
return RESULT_SUCCESS;
}
s32 ConditionVariable::GetAvailableCount() const {
return Memory::Read32(guest_addr);
}
void ConditionVariable::SetAvailableCount(s32 value) const {
Memory::Write32(guest_addr, value);
}
} // namespace Kernel

View file

@ -1,63 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <queue>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
namespace Kernel {
class ConditionVariable final : public WaitObject {
public:
/**
* Creates a condition variable.
* @param guest_addr Address of the object tracking the condition variable in guest memory. If
* specified, this condition variable will update the guest object when its state changes.
* @param name Optional name of condition variable.
* @return The created condition variable.
*/
static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr,
std::string name = "Unknown");
std::string GetTypeName() const override {
return "ConditionVariable";
}
std::string GetName() const override {
return name;
}
static const HandleType HANDLE_TYPE = HandleType::ConditionVariable;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
s32 GetAvailableCount() const;
void SetAvailableCount(s32 value) const;
std::string name; ///< Name of condition variable (optional)
VAddr guest_addr; ///< Address of the guest condition variable value
VAddr mutex_addr; ///< (optional) Address of guest mutex value associated with this condition
///< variable, used for implementing events
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
/**
* Releases a slot from a condition variable.
* @param target The number of threads to wakeup, -1 is all.
* @return ResultCode indicating if the operation succeeded.
*/
ResultCode Release(s32 target);
private:
ConditionVariable();
~ConditionVariable() override;
};
} // namespace Kernel

View file

@ -20,6 +20,7 @@ enum {
MaxConnectionsReached = 52, MaxConnectionsReached = 52,
// Confirmed Switch OS error codes // Confirmed Switch OS error codes
MisalignedAddress = 102,
InvalidHandle = 114, InvalidHandle = 114,
Timeout = 117, Timeout = 117,
SynchronizationCanceled = 118, SynchronizationCanceled = 118,

View file

@ -18,12 +18,10 @@ using Handle = u32;
enum class HandleType : u32 { enum class HandleType : u32 {
Unknown, Unknown,
Event, Event,
Mutex,
SharedMemory, SharedMemory,
Thread, Thread,
Process, Process,
AddressArbiter, AddressArbiter,
ConditionVariable,
Timer, Timer,
ResourceLimit, ResourceLimit,
CodeSet, CodeSet,
@ -63,9 +61,7 @@ public:
bool IsWaitable() const { bool IsWaitable() const {
switch (GetHandleType()) { switch (GetHandleType()) {
case HandleType::Event: case HandleType::Event:
case HandleType::Mutex:
case HandleType::Thread: case HandleType::Thread:
case HandleType::ConditionVariable:
case HandleType::Timer: case HandleType::Timer:
case HandleType::ServerPort: case HandleType::ServerPort:
case HandleType::ServerSession: case HandleType::ServerSession:

View file

@ -7,6 +7,7 @@
#include <boost/range/algorithm_ext/erase.hpp> #include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h" #include "common/assert.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
@ -15,124 +16,120 @@
namespace Kernel { namespace Kernel {
void ReleaseThreadMutexes(Thread* thread) { /// Returns the number of threads that are waiting for a mutex, and the highest priority one among
for (auto& mtx : thread->held_mutexes) { /// those.
mtx->SetHasWaiters(false); static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
mtx->SetHoldingThread(nullptr); SharedPtr<Thread> current_thread, VAddr mutex_addr) {
mtx->WakeupAllWaitingThreads();
SharedPtr<Thread> highest_priority_thread;
u32 num_waiters = 0;
for (auto& thread : current_thread->wait_mutex_threads) {
if (thread->mutex_wait_address != mutex_addr)
continue;
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
++num_waiters;
if (highest_priority_thread == nullptr ||
thread->GetPriority() < highest_priority_thread->GetPriority()) {
highest_priority_thread = thread;
} }
thread->held_mutexes.clear();
} }
Mutex::Mutex() {} return {highest_priority_thread, num_waiters};
Mutex::~Mutex() {}
SharedPtr<Mutex> Mutex::Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr,
std::string name) {
SharedPtr<Mutex> mutex(new Mutex);
mutex->guest_addr = guest_addr;
mutex->name = std::move(name);
// If mutex was initialized with a holding thread, acquire it by the holding thread
if (holding_thread) {
mutex->Acquire(holding_thread.get());
} }
// Mutexes are referenced by guest address, so track this in the kernel /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
g_object_address_table.Insert(guest_addr, mutex); static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread,
SharedPtr<Thread> new_owner) {
auto threads = current_thread->wait_mutex_threads;
for (auto& thread : threads) {
if (thread->mutex_wait_address != mutex_addr)
continue;
return mutex; ASSERT(thread->lock_owner == current_thread);
current_thread->RemoveMutexWaiter(thread);
if (new_owner != thread)
new_owner->AddMutexWaiter(thread);
}
} }
bool Mutex::ShouldWait(Thread* thread) const { ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
auto holding_thread = GetHoldingThread(); Handle requesting_thread_handle) {
return holding_thread != nullptr && thread != holding_thread; // The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
} }
void Mutex::Acquire(Thread* thread) { SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
priority = thread->current_priority; // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
thread->held_mutexes.insert(this); // thread.
SetHoldingThread(thread); ASSERT(requesting_thread == GetCurrentThread());
thread->UpdatePriority();
Core::System::GetInstance().PrepareReschedule(); u32 addr_value = Memory::Read32(address);
// If the mutex isn't being held, just return success.
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
return RESULT_SUCCESS;
} }
ResultCode Mutex::Release(Thread* thread) { if (holding_thread == nullptr)
auto holding_thread = GetHoldingThread(); return ERR_INVALID_HANDLE;
ASSERT(holding_thread);
// We can only release the mutex if it's held by the calling thread. // Wait until the mutex is released
ASSERT(thread == holding_thread); GetCurrentThread()->mutex_wait_address = address;
GetCurrentThread()->wait_handle = requesting_thread_handle;
GetCurrentThread()->status = THREADSTATUS_WAIT_MUTEX;
GetCurrentThread()->wakeup_callback = nullptr;
// Update the lock holder thread's priority to prevent priority inversion.
holding_thread->AddMutexWaiter(GetCurrentThread());
holding_thread->held_mutexes.erase(this);
holding_thread->UpdatePriority();
SetHoldingThread(nullptr);
SetHasWaiters(!GetWaitingThreads().empty());
WakeupAllWaitingThreads();
Core::System::GetInstance().PrepareReschedule(); Core::System::GetInstance().PrepareReschedule();
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { ResultCode Mutex::Release(VAddr address) {
WaitObject::AddWaitingThread(thread); // The mutex address must be 4-byte aligned
thread->pending_mutexes.insert(this); if ((address % sizeof(u32)) != 0) {
SetHasWaiters(true); return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
UpdatePriority();
} }
void Mutex::RemoveWaitingThread(Thread* thread) { auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
WaitObject::RemoveWaitingThread(thread);
thread->pending_mutexes.erase(this); // There are no more threads waiting for the mutex, release it completely.
if (!GetHasWaiters()) if (thread == nullptr) {
SetHasWaiters(!GetWaitingThreads().empty()); ASSERT(GetCurrentThread()->wait_mutex_threads.empty());
UpdatePriority(); Memory::Write32(address, 0);
return RESULT_SUCCESS;
} }
void Mutex::UpdatePriority() { // Transfer the ownership of the mutex from the previous owner to the new one.
if (!GetHoldingThread()) TransferMutexOwnership(address, GetCurrentThread(), thread);
return;
u32 best_priority = THREADPRIO_LOWEST; u32 mutex_value = thread->wait_handle;
for (auto& waiter : GetWaitingThreads()) {
if (waiter->current_priority < best_priority) if (num_waiters >= 2) {
best_priority = waiter->current_priority; // Notify the guest that there are still some threads waiting for the mutex
mutex_value |= Mutex::MutexHasWaitersFlag;
} }
if (best_priority != priority) { // Grant the mutex to the next waiting thread and resume it.
priority = best_priority; Memory::Write32(address, mutex_value);
GetHoldingThread()->UpdatePriority();
}
}
Handle Mutex::GetOwnerHandle() const { ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
GuestState guest_state{Memory::Read32(guest_addr)}; thread->ResumeFromWait();
return guest_state.holding_thread_handle;
}
SharedPtr<Thread> Mutex::GetHoldingThread() const { thread->lock_owner = nullptr;
GuestState guest_state{Memory::Read32(guest_addr)}; thread->condvar_wait_address = 0;
return g_handle_table.Get<Thread>(guest_state.holding_thread_handle); thread->mutex_wait_address = 0;
} thread->wait_handle = 0;
void Mutex::SetHoldingThread(SharedPtr<Thread> thread) { return RESULT_SUCCESS;
GuestState guest_state{Memory::Read32(guest_addr)};
guest_state.holding_thread_handle.Assign(thread ? thread->guest_handle : 0);
Memory::Write32(guest_addr, guest_state.raw);
} }
bool Mutex::GetHasWaiters() const {
GuestState guest_state{Memory::Read32(guest_addr)};
return guest_state.has_waiters != 0;
}
void Mutex::SetHasWaiters(bool has_waiters) {
GuestState guest_state{Memory::Read32(guest_addr)};
guest_state.has_waiters.Assign(has_waiters ? 1 : 0);
Memory::Write32(guest_addr, guest_state.raw);
}
} // namespace Kernel } // namespace Kernel

View file

@ -15,87 +15,23 @@ namespace Kernel {
class Thread; class Thread;
class Mutex final : public WaitObject { class Mutex final {
public: public:
/** /// Flag that indicates that a mutex still has threads waiting for it.
* Creates a mutex. static constexpr u32 MutexHasWaitersFlag = 0x40000000;
* @param holding_thread Specifies a thread already holding the mutex. If not nullptr, this /// Mask of the bits in a mutex address value that contain the mutex owner.
* thread will acquire the mutex. static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
* @param guest_addr Address of the object tracking the mutex in guest memory. If specified,
* this mutex will update the guest object when its state changes.
* @param name Optional name of mutex
* @return Pointer to new Mutex object
*/
static SharedPtr<Mutex> Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr = 0,
std::string name = "Unknown");
std::string GetTypeName() const override { /// Attempts to acquire a mutex at the specified address.
return "Mutex"; static ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
} Handle requesting_thread_handle);
std::string GetName() const override {
return name;
}
static const HandleType HANDLE_TYPE = HandleType::Mutex; /// Releases the mutex at the specified address.
HandleType GetHandleType() const override { static ResultCode Release(VAddr address);
return HANDLE_TYPE;
}
u32 priority; ///< The priority of the mutex, used for priority inheritance.
std::string name; ///< Name of mutex (optional)
VAddr guest_addr; ///< Address of the guest mutex value
/**
* Elevate the mutex priority to the best priority
* among the priorities of all its waiting threads.
*/
void UpdatePriority();
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
void AddWaitingThread(SharedPtr<Thread> thread) override;
void RemoveWaitingThread(Thread* thread) override;
/**
* Attempts to release the mutex from the specified thread.
* @param thread Thread that wants to release the mutex.
* @returns The result code of the operation.
*/
ResultCode Release(Thread* thread);
/// Gets the handle to the holding process stored in the guest state.
Handle GetOwnerHandle() const;
/// Gets the Thread pointed to by the owner handle
SharedPtr<Thread> GetHoldingThread() const;
/// Sets the holding process handle in the guest state.
void SetHoldingThread(SharedPtr<Thread> thread);
/// Returns the has_waiters bit in the guest state.
bool GetHasWaiters() const;
/// Sets the has_waiters bit in the guest state.
void SetHasWaiters(bool has_waiters);
private: private:
Mutex(); Mutex() = default;
~Mutex() override; ~Mutex() = default;
/// Object in guest memory used to track the mutex state
union GuestState {
u32_le raw;
/// Handle of the thread that currently holds the mutex, 0 if available
BitField<0, 30, u32_le> holding_thread_handle;
/// 1 when there are threads waiting for this mutex, otherwise 0
BitField<30, 1, u32_le> has_waiters;
}; };
static_assert(sizeof(GuestState) == 4, "GuestState size is incorrect");
};
/**
* Releases all the mutexes held by the specified thread
* @param thread Thread that is holding the mutexes
*/
void ReleaseThreadMutexes(Thread* thread);
} // namespace Kernel } // namespace Kernel

View file

@ -13,7 +13,6 @@
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/condition_variable.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
@ -262,32 +261,14 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
"requesting_current_thread_handle=0x%08X", "requesting_current_thread_handle=0x%08X",
holding_thread_handle, mutex_addr, requesting_thread_handle); holding_thread_handle, mutex_addr, requesting_thread_handle);
SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle); return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle);
SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
ASSERT(requesting_thread);
ASSERT(requesting_thread == GetCurrentThread());
SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr);
if (!mutex) {
// Create a new mutex for the specified address if one does not already exist
mutex = Mutex::Create(holding_thread, mutex_addr);
mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr);
}
ASSERT(holding_thread == mutex->GetHoldingThread());
return WaitSynchronization1(mutex, requesting_thread.get());
} }
/// Unlock a mutex /// Unlock a mutex
static ResultCode ArbitrateUnlock(VAddr mutex_addr) { static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr); LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr);
SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); return Mutex::Release(mutex_addr);
ASSERT(mutex);
return mutex->Release(GetCurrentThread());
} }
/// Break program execution /// Break program execution
@ -412,11 +393,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
} }
thread->SetPriority(priority); thread->SetPriority(priority);
thread->UpdatePriority();
// Update the mutexes that this thread is waiting for
for (auto& mutex : thread->pending_mutexes)
mutex->UpdatePriority();
Core::System::GetInstance().PrepareReschedule(); Core::System::GetInstance().PrepareReschedule();
return RESULT_SUCCESS; return RESULT_SUCCESS;
@ -634,77 +610,20 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
ASSERT(thread); ASSERT(thread);
SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); CASCADE_CODE(Mutex::Release(mutex_addr));
if (!mutex) {
// Create a new mutex for the specified address if one does not already exist
mutex = Mutex::Create(thread, mutex_addr);
mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr);
}
SharedPtr<ConditionVariable> condition_variable = SharedPtr<Thread> current_thread = GetCurrentThread();
g_object_address_table.Get<ConditionVariable>(condition_variable_addr); current_thread->condvar_wait_address = condition_variable_addr;
if (!condition_variable) { current_thread->mutex_wait_address = mutex_addr;
// Create a new condition_variable for the specified address if one does not already exist current_thread->wait_handle = thread_handle;
condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap(); current_thread->status = THREADSTATUS_WAIT_MUTEX;
condition_variable->name = current_thread->wakeup_callback = nullptr;
Common::StringFromFormat("condition-variable-%llx", condition_variable_addr);
}
if (condition_variable->mutex_addr) { current_thread->WakeAfterDelay(nano_seconds);
// Previously created the ConditionVariable using WaitProcessWideKeyAtomic, verify
// everything is correct
ASSERT(condition_variable->mutex_addr == mutex_addr);
} else {
// Previously created the ConditionVariable using SignalProcessWideKey, set the mutex
// associated with it
condition_variable->mutex_addr = mutex_addr;
}
if (mutex->GetOwnerHandle()) { // Note: Deliberately don't attempt to inherit the lock owner's priority.
// Release the mutex if the current thread is holding it
mutex->Release(thread.get());
}
auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason,
SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, size_t index) {
ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
return true;
}
ASSERT(reason == ThreadWakeupReason::Signal);
// Now try to acquire the mutex and don't resume if it's not available.
if (!mutex->ShouldWait(thread.get())) {
mutex->Acquire(thread.get());
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
return true;
}
if (nano_seconds == 0) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
return true;
}
thread->wait_objects = {mutex};
mutex->AddWaitingThread(thread);
thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
// Create an event to wake the thread up after the
// specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
thread->wakeup_callback = DefaultThreadWakeupCallback;
Core::System::GetInstance().PrepareReschedule(); Core::System::GetInstance().PrepareReschedule();
return false;
};
CASCADE_CODE(
WaitSynchronization1(condition_variable, thread.get(), nano_seconds, wakeup_callback));
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -713,24 +632,53 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x", LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x",
condition_variable_addr, target); condition_variable_addr, target);
// Wakeup all or one thread - Any other value is unimplemented u32 processed = 0;
ASSERT(target == -1 || target == 1); auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
SharedPtr<ConditionVariable> condition_variable = for (auto& thread : thread_list) {
g_object_address_table.Get<ConditionVariable>(condition_variable_addr); if (thread->condvar_wait_address != condition_variable_addr)
if (!condition_variable) { continue;
// Create a new condition_variable for the specified address if one does not already exist
condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap(); // Only process up to 'target' threads, unless 'target' is -1, in which case process
condition_variable->name = // them all.
Common::StringFromFormat("condition-variable-%llx", condition_variable_addr); if (target != -1 && processed >= target)
break;
// If the mutex is not yet acquired, acquire it.
u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
thread->ResumeFromWait();
auto lock_owner = thread->lock_owner;
if (lock_owner)
lock_owner->RemoveMutexWaiter(thread);
thread->lock_owner = nullptr;
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
} else {
// Couldn't acquire the mutex, block the thread.
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = g_handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->status != THREADSTATUS_RUNNING);
thread->status = THREADSTATUS_WAIT_MUTEX;
thread->wakeup_callback = nullptr;
// Signal that the mutex now has a waiting thread.
Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
owner->AddMutexWaiter(thread);
Core::System::GetInstance().PrepareReschedule();
} }
CASCADE_CODE(condition_variable->Release(target)); ++processed;
if (condition_variable->mutex_addr) {
// If a mutex was created for this condition_variable, wait the current thread on it
SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(condition_variable->mutex_addr);
return WaitSynchronization1(mutex, GetCurrentThread());
} }
return RESULT_SUCCESS; return RESULT_SUCCESS;

View file

@ -77,9 +77,6 @@ void Thread::Stop() {
} }
wait_objects.clear(); wait_objects.clear();
// Release all the mutexes that this thread holds
ReleaseThreadMutexes(this);
// Mark the TLS slot in the thread's page as free. // Mark the TLS slot in the thread's page as free.
u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
u64 tls_slot = u64 tls_slot =
@ -126,6 +123,19 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0); resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
} }
if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
thread->wait_handle) {
ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
auto lock_owner = thread->lock_owner;
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
// and don't have a lock owner.
ASSERT(lock_owner == nullptr);
}
if (resume) if (resume)
thread->ResumeFromWait(); thread->ResumeFromWait();
} }
@ -151,6 +161,7 @@ void Thread::ResumeFromWait() {
case THREADSTATUS_WAIT_HLE_EVENT: case THREADSTATUS_WAIT_HLE_EVENT:
case THREADSTATUS_WAIT_SLEEP: case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC: case THREADSTATUS_WAIT_IPC:
case THREADSTATUS_WAIT_MUTEX:
break; break;
case THREADSTATUS_READY: case THREADSTATUS_READY:
@ -256,7 +267,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->last_running_ticks = CoreTiming::GetTicks(); thread->last_running_ticks = CoreTiming::GetTicks();
thread->processor_id = processor_id; thread->processor_id = processor_id;
thread->wait_objects.clear(); thread->wait_objects.clear();
thread->wait_address = 0; thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
thread->name = std::move(name); thread->name = std::move(name);
thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
thread->owner_process = owner_process; thread->owner_process = owner_process;
@ -317,17 +330,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
void Thread::SetPriority(u32 priority) { void Thread::SetPriority(u32 priority) {
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
"Invalid priority value."); "Invalid priority value.");
Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); nominal_priority = priority;
nominal_priority = current_priority = priority; UpdatePriority();
}
void Thread::UpdatePriority() {
u32 best_priority = nominal_priority;
for (auto& mutex : held_mutexes) {
if (mutex->priority < best_priority)
best_priority = mutex->priority;
}
BoostPriority(best_priority);
} }
void Thread::BoostPriority(u32 priority) { void Thread::BoostPriority(u32 priority) {
@ -377,6 +381,38 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + CommandHeaderOffset; return GetTLSAddress() + CommandHeaderOffset;
} }
void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
thread->lock_owner = this;
wait_mutex_threads.emplace_back(std::move(thread));
UpdatePriority();
}
void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
boost::remove_erase(wait_mutex_threads, thread);
thread->lock_owner = nullptr;
UpdatePriority();
}
void Thread::UpdatePriority() {
// Find the highest priority among all the threads that are waiting for this thread's lock
u32 new_priority = nominal_priority;
for (const auto& thread : wait_mutex_threads) {
if (thread->nominal_priority < new_priority)
new_priority = thread->nominal_priority;
}
if (new_priority == current_priority)
return;
Core::System::GetInstance().Scheduler().SetThreadPriority(this, new_priority);
current_priority = new_priority;
// Recursively update the priority of the thread that depends on the priority of this one.
if (lock_owner)
lock_owner->UpdatePriority();
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
/** /**

View file

@ -18,7 +18,7 @@
enum ThreadPriority : u32 { enum ThreadPriority : u32 {
THREADPRIO_HIGHEST = 0, ///< Highest thread priority THREADPRIO_HIGHEST = 0, ///< Highest thread priority
THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
THREADPRIO_LOWEST = 63, ///< Lowest thread priority THREADPRIO_LOWEST = 63, ///< Lowest thread priority
}; };
@ -43,6 +43,7 @@ enum ThreadStatus {
THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
THREADSTATUS_DORMANT, ///< Created but not yet made ready THREADSTATUS_DORMANT, ///< Created but not yet made ready
THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
}; };
@ -54,7 +55,6 @@ enum class ThreadWakeupReason {
namespace Kernel { namespace Kernel {
class Mutex;
class Process; class Process;
class Thread final : public WaitObject { class Thread final : public WaitObject {
@ -103,18 +103,21 @@ public:
*/ */
void SetPriority(u32 priority); void SetPriority(u32 priority);
/**
* Boost's a thread's priority to the best priority among the thread's held mutexes.
* This prevents priority inversion via priority inheritance.
*/
void UpdatePriority();
/** /**
* Temporarily boosts the thread's priority until the next time it is scheduled * Temporarily boosts the thread's priority until the next time it is scheduled
* @param priority The new priority * @param priority The new priority
*/ */
void BoostPriority(u32 priority); void BoostPriority(u32 priority);
/// Adds a thread to the list of threads that are waiting for a lock held by this thread.
void AddMutexWaiter(SharedPtr<Thread> thread);
/// Removes a thread from the list of threads that are waiting for a lock held by this thread.
void RemoveMutexWaiter(SharedPtr<Thread> thread);
/// Recalculates the current priority taking into account priority inheritance.
void UpdatePriority();
/** /**
* Gets the thread's thread ID * Gets the thread's thread ID
* @return The thread's ID * @return The thread's ID
@ -205,19 +208,22 @@ public:
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
/// Mutexes currently held by this thread, which will be released when it exits.
boost::container::flat_set<SharedPtr<Mutex>> held_mutexes;
/// Mutexes that this thread is currently waiting for.
boost::container::flat_set<SharedPtr<Mutex>> pending_mutexes;
SharedPtr<Process> owner_process; ///< Process that owns this thread SharedPtr<Process> owner_process; ///< Process that owns this thread
/// Objects that the thread is waiting on, in the same order as they were /// Objects that the thread is waiting on, in the same order as they were
// passed to WaitSynchronization1/N. // passed to WaitSynchronization1/N.
std::vector<SharedPtr<WaitObject>> wait_objects; std::vector<SharedPtr<WaitObject>> wait_objects;
VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address /// List of threads that are waiting for a mutex that is held by this thread.
std::vector<SharedPtr<Thread>> wait_mutex_threads;
/// Thread that owns the lock that this thread is waiting for.
SharedPtr<Thread> lock_owner;
// If waiting on a ConditionVariable, this is the ConditionVariable address
VAddr condvar_wait_address;
VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
Handle wait_handle; ///< The handle used to wait for the mutex.
std::string name; std::string name;

View file

@ -9,7 +9,8 @@
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/buffer_queue.h"
namespace Service::NVFlinger { namespace Service {
namespace NVFlinger {
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle"); native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
@ -110,4 +111,5 @@ void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_eve
buffer_wait_event = std::move(wait_event); buffer_wait_event = std::move(wait_event);
} }
} // namespace Service::NVFlinger } // namespace NVFlinger
} // namespace Service

View file

@ -13,7 +13,8 @@ namespace CoreTiming {
struct EventType; struct EventType;
} }
namespace Service::NVFlinger { namespace Service {
namespace NVFlinger {
struct IGBPBuffer { struct IGBPBuffer {
u32_le magic; u32_le magic;
@ -97,4 +98,5 @@ private:
Kernel::SharedPtr<Kernel::Event> buffer_wait_event; Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
}; };
} // namespace Service::NVFlinger } // namespace NVFlinger
} // namespace Service

View file

@ -137,7 +137,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings; process->address_mappings = default_address_mappings;
process->resource_limit = process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(base_addr, 48, Memory::DEFAULT_STACK_SIZE); process->Run(base_addr, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
is_loaded = true; is_loaded = true;
return ResultStatus::Success; return ResultStatus::Success;

View file

@ -165,7 +165,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings; process->address_mappings = default_address_mappings;
process->resource_limit = process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::DEFAULT_STACK_SIZE); process->Run(Memory::PROCESS_IMAGE_VADDR, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
is_loaded = true; is_loaded = true;
return ResultStatus::Success; return ResultStatus::Success;

View file

@ -6,8 +6,8 @@
#include "yuzu/util/util.h" #include "yuzu/util/util.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/condition_variable.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h" #include "core/hle/kernel/timer.h"
@ -67,6 +67,29 @@ QString WaitTreeText::GetText() const {
return text; return text;
} }
WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) {
mutex_value = Memory::Read32(mutex_address);
owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask);
owner = Kernel::g_handle_table.Get<Kernel::Thread>(owner_handle);
}
QString WaitTreeMutexInfo::GetText() const {
return tr("waiting for mutex 0x%1").arg(mutex_address, 16, 16, QLatin1Char('0'));
}
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const {
std::vector<std::unique_ptr<WaitTreeItem>> list;
bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0;
list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters)));
list.push_back(std::make_unique<WaitTreeText>(
tr("owner handle: 0x%1").arg(owner_handle, 8, 16, QLatin1Char('0'))));
if (owner != nullptr)
list.push_back(std::make_unique<WaitTreeThread>(*owner));
return list;
}
WaitTreeWaitObject::WaitTreeWaitObject(const Kernel::WaitObject& o) : object(o) {} WaitTreeWaitObject::WaitTreeWaitObject(const Kernel::WaitObject& o) : object(o) {}
bool WaitTreeExpandableItem::IsExpandable() const { bool WaitTreeExpandableItem::IsExpandable() const {
@ -84,11 +107,6 @@ std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitO
switch (object.GetHandleType()) { switch (object.GetHandleType()) {
case Kernel::HandleType::Event: case Kernel::HandleType::Event:
return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::Event&>(object)); return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::Event&>(object));
case Kernel::HandleType::Mutex:
return std::make_unique<WaitTreeMutex>(static_cast<const Kernel::Mutex&>(object));
case Kernel::HandleType::ConditionVariable:
return std::make_unique<WaitTreeConditionVariable>(
static_cast<const Kernel::ConditionVariable&>(object));
case Kernel::HandleType::Timer: case Kernel::HandleType::Timer:
return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object)); return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object));
case Kernel::HandleType::Thread: case Kernel::HandleType::Thread:
@ -160,6 +178,9 @@ QString WaitTreeThread::GetText() const {
case THREADSTATUS_WAIT_SYNCH_ANY: case THREADSTATUS_WAIT_SYNCH_ANY:
status = tr("waiting for objects"); status = tr("waiting for objects");
break; break;
case THREADSTATUS_WAIT_MUTEX:
status = tr("waiting for mutex");
break;
case THREADSTATUS_DORMANT: case THREADSTATUS_DORMANT:
status = tr("dormant"); status = tr("dormant");
break; break;
@ -186,6 +207,7 @@ QColor WaitTreeThread::GetColor() const {
return QColor(Qt::GlobalColor::darkYellow); return QColor(Qt::GlobalColor::darkYellow);
case THREADSTATUS_WAIT_SYNCH_ALL: case THREADSTATUS_WAIT_SYNCH_ALL:
case THREADSTATUS_WAIT_SYNCH_ANY: case THREADSTATUS_WAIT_SYNCH_ANY:
case THREADSTATUS_WAIT_MUTEX:
return QColor(Qt::GlobalColor::red); return QColor(Qt::GlobalColor::red);
case THREADSTATUS_DORMANT: case THREADSTATUS_DORMANT:
return QColor(Qt::GlobalColor::darkCyan); return QColor(Qt::GlobalColor::darkCyan);
@ -225,11 +247,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
list.push_back(std::make_unique<WaitTreeText>( list.push_back(std::make_unique<WaitTreeText>(
tr("last running ticks = %1").arg(thread.last_running_ticks))); tr("last running ticks = %1").arg(thread.last_running_ticks)));
if (thread.held_mutexes.empty()) { if (thread.mutex_wait_address != 0)
list.push_back(std::make_unique<WaitTreeText>(tr("not holding mutex"))); list.push_back(std::make_unique<WaitTreeMutexInfo>(thread.mutex_wait_address));
} else { else
list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes)); list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
}
if (thread.status == THREADSTATUS_WAIT_SYNCH_ANY || if (thread.status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread.status == THREADSTATUS_WAIT_SYNCH_ALL) { thread.status == THREADSTATUS_WAIT_SYNCH_ALL) {
list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects,
@ -250,33 +272,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
return list; return list;
} }
WaitTreeMutex::WaitTreeMutex(const Kernel::Mutex& object) : WaitTreeWaitObject(object) {}
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutex::GetChildren() const {
std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
const auto& mutex = static_cast<const Kernel::Mutex&>(object);
if (mutex.GetHasWaiters()) {
list.push_back(std::make_unique<WaitTreeText>(tr("locked by thread:")));
list.push_back(std::make_unique<WaitTreeThread>(*mutex.GetHoldingThread()));
} else {
list.push_back(std::make_unique<WaitTreeText>(tr("free")));
}
return list;
}
WaitTreeConditionVariable::WaitTreeConditionVariable(const Kernel::ConditionVariable& object)
: WaitTreeWaitObject(object) {}
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeConditionVariable::GetChildren() const {
std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
const auto& condition_variable = static_cast<const Kernel::ConditionVariable&>(object);
list.push_back(std::make_unique<WaitTreeText>(
tr("available count = %1").arg(condition_variable.GetAvailableCount())));
return list;
}
WaitTreeTimer::WaitTreeTimer(const Kernel::Timer& object) : WaitTreeWaitObject(object) {} WaitTreeTimer::WaitTreeTimer(const Kernel::Timer& object) : WaitTreeWaitObject(object) {}
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const { std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const {
@ -293,21 +288,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const {
return list; return list;
} }
WaitTreeMutexList::WaitTreeMutexList(
const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list)
: mutex_list(list) {}
QString WaitTreeMutexList::GetText() const {
return tr("holding mutexes");
}
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexList::GetChildren() const {
std::vector<std::unique_ptr<WaitTreeItem>> list(mutex_list.size());
std::transform(mutex_list.begin(), mutex_list.end(), list.begin(),
[](const auto& t) { return std::make_unique<WaitTreeMutex>(*t); });
return list;
}
WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list) WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list)
: thread_list(list) {} : thread_list(list) {}

View file

@ -16,8 +16,6 @@ class EmuThread;
namespace Kernel { namespace Kernel {
class WaitObject; class WaitObject;
class Event; class Event;
class Mutex;
class ConditionVariable;
class Thread; class Thread;
class Timer; class Timer;
} // namespace Kernel } // namespace Kernel
@ -61,6 +59,20 @@ public:
bool IsExpandable() const override; bool IsExpandable() const override;
}; };
class WaitTreeMutexInfo : public WaitTreeExpandableItem {
Q_OBJECT
public:
explicit WaitTreeMutexInfo(VAddr mutex_address);
QString GetText() const override;
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
private:
VAddr mutex_address;
u32 mutex_value;
Kernel::Handle owner_handle;
Kernel::SharedPtr<Kernel::Thread> owner;
};
class WaitTreeWaitObject : public WaitTreeExpandableItem { class WaitTreeWaitObject : public WaitTreeExpandableItem {
Q_OBJECT Q_OBJECT
public: public:
@ -104,20 +116,6 @@ public:
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
}; };
class WaitTreeMutex : public WaitTreeWaitObject {
Q_OBJECT
public:
explicit WaitTreeMutex(const Kernel::Mutex& object);
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
};
class WaitTreeConditionVariable : public WaitTreeWaitObject {
Q_OBJECT
public:
explicit WaitTreeConditionVariable(const Kernel::ConditionVariable& object);
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
};
class WaitTreeTimer : public WaitTreeWaitObject { class WaitTreeTimer : public WaitTreeWaitObject {
Q_OBJECT Q_OBJECT
public: public:
@ -125,19 +123,6 @@ public:
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
}; };
class WaitTreeMutexList : public WaitTreeExpandableItem {
Q_OBJECT
public:
explicit WaitTreeMutexList(
const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list);
QString GetText() const override;
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
private:
const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& mutex_list;
};
class WaitTreeThreadList : public WaitTreeExpandableItem { class WaitTreeThreadList : public WaitTreeExpandableItem {
Q_OBJECT Q_OBJECT
public: public: