mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	SVC: Correct WaitSynchronization, WaitProcessWideKey, SignalProcessWideKey.
This commit is contained in:
		
							parent
							
								
									5b6a67f849
								
							
						
					
					
						commit
						d4ebb510a0
					
				@ -187,7 +187,6 @@ void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) {
 | 
			
		||||
        }
 | 
			
		||||
        ++it;
 | 
			
		||||
    }
 | 
			
		||||
    UNREACHABLE();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads(
 | 
			
		||||
 | 
			
		||||
@ -632,7 +632,7 @@ void Scheduler::SwitchContext() {
 | 
			
		||||
            cpu_core.SaveContext(previous_thread->GetContext64());
 | 
			
		||||
            // Save the TPIDR_EL0 system register in case it was modified.
 | 
			
		||||
            previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
 | 
			
		||||
 | 
			
		||||
            cpu_core.ClearExclusiveState();
 | 
			
		||||
        }
 | 
			
		||||
        if (previous_thread->GetStatus() == ThreadStatus::Running) {
 | 
			
		||||
            previous_thread->SetStatus(ThreadStatus::Ready);
 | 
			
		||||
 | 
			
		||||
@ -1541,33 +1541,50 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
 | 
			
		||||
        return ERR_INVALID_ADDRESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    UNIMPLEMENTED();
 | 
			
		||||
 | 
			
		||||
    ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
 | 
			
		||||
 | 
			
		||||
    auto& kernel = system.Kernel();
 | 
			
		||||
    Handle event_handle;
 | 
			
		||||
    Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
 | 
			
		||||
    auto* const current_process = system.Kernel().CurrentProcess();
 | 
			
		||||
    const auto& handle_table = current_process->GetHandleTable();
 | 
			
		||||
    std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
 | 
			
		||||
    ASSERT(thread);
 | 
			
		||||
    {
 | 
			
		||||
        SchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds);
 | 
			
		||||
        const auto& handle_table = current_process->GetHandleTable();
 | 
			
		||||
        std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
 | 
			
		||||
        ASSERT(thread);
 | 
			
		||||
 | 
			
		||||
    const auto release_result = current_process->GetMutex().Release(mutex_addr);
 | 
			
		||||
    if (release_result.IsError()) {
 | 
			
		||||
        return release_result;
 | 
			
		||||
        current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
 | 
			
		||||
 | 
			
		||||
        const auto release_result = current_process->GetMutex().Release(mutex_addr);
 | 
			
		||||
        if (release_result.IsError()) {
 | 
			
		||||
            lock.CancelSleep();
 | 
			
		||||
            return release_result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (nano_seconds == 0) {
 | 
			
		||||
            lock.CancelSleep();
 | 
			
		||||
            return RESULT_TIMEOUT;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        current_thread->SetCondVarWaitAddress(condition_variable_addr);
 | 
			
		||||
        current_thread->SetMutexWaitAddress(mutex_addr);
 | 
			
		||||
        current_thread->SetWaitHandle(thread_handle);
 | 
			
		||||
        current_thread->SetStatus(ThreadStatus::WaitCondVar);
 | 
			
		||||
        current_process->InsertConditionVariableThread(SharedFrom(current_thread));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
 | 
			
		||||
    current_thread->SetCondVarWaitAddress(condition_variable_addr);
 | 
			
		||||
    current_thread->SetMutexWaitAddress(mutex_addr);
 | 
			
		||||
    current_thread->SetWaitHandle(thread_handle);
 | 
			
		||||
    current_thread->SetStatus(ThreadStatus::WaitCondVar);
 | 
			
		||||
    current_thread->InvalidateWakeupCallback();
 | 
			
		||||
    current_process->InsertConditionVariableThread(SharedFrom(current_thread));
 | 
			
		||||
    if (event_handle != InvalidHandle) {
 | 
			
		||||
        auto& time_manager = kernel.TimeManager();
 | 
			
		||||
        time_manager.UnscheduleTimeEvent(event_handle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    current_thread->WakeAfterDelay(nano_seconds);
 | 
			
		||||
    {
 | 
			
		||||
        SchedulerLock lock(kernel);
 | 
			
		||||
 | 
			
		||||
        current_process->RemoveConditionVariableThread(SharedFrom(current_thread));
 | 
			
		||||
    }
 | 
			
		||||
    // Note: Deliberately don't attempt to inherit the lock owner's priority.
 | 
			
		||||
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
    return current_thread->GetSignalingResult();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Signal process wide key
 | 
			
		||||
@ -1577,10 +1594,10 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
 | 
			
		||||
 | 
			
		||||
    ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
 | 
			
		||||
 | 
			
		||||
    UNIMPLEMENTED();
 | 
			
		||||
 | 
			
		||||
    // Retrieve a list of all threads that are waiting for this condition variable.
 | 
			
		||||
    auto* const current_process = system.Kernel().CurrentProcess();
 | 
			
		||||
    auto& kernel = system.Kernel();
 | 
			
		||||
    SchedulerLock lock(kernel);
 | 
			
		||||
    auto* const current_process = kernel.CurrentProcess();
 | 
			
		||||
    std::vector<std::shared_ptr<Thread>> waiting_threads =
 | 
			
		||||
        current_process->GetConditionVariableThreads(condition_variable_addr);
 | 
			
		||||
 | 
			
		||||
@ -1589,10 +1606,18 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
 | 
			
		||||
    std::size_t last = waiting_threads.size();
 | 
			
		||||
    if (target > 0)
 | 
			
		||||
        last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
 | 
			
		||||
 | 
			
		||||
    auto& time_manager = kernel.TimeManager();
 | 
			
		||||
    for (std::size_t index = 0; index < last; ++index) {
 | 
			
		||||
        auto& thread = waiting_threads[index];
 | 
			
		||||
 | 
			
		||||
        if (thread->GetStatus() != ThreadStatus::WaitCondVar) {
 | 
			
		||||
            last++;
 | 
			
		||||
            last = std::min(waiting_threads.size(), last);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        time_manager.CancelTimeEvent(thread.get());
 | 
			
		||||
 | 
			
		||||
        ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
 | 
			
		||||
 | 
			
		||||
        // liberate Cond Var Thread.
 | 
			
		||||
@ -1630,17 +1655,13 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            thread->SetLockOwner(nullptr);
 | 
			
		||||
            thread->SetMutexWaitAddress(0);
 | 
			
		||||
            thread->SetWaitHandle(0);
 | 
			
		||||
            thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
 | 
			
		||||
            thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
 | 
			
		||||
        } else {
 | 
			
		||||
            // The mutex is already owned by some other thread, make this thread wait on it.
 | 
			
		||||
            const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
 | 
			
		||||
            const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
 | 
			
		||||
            auto owner = handle_table.Get<Thread>(owner_handle);
 | 
			
		||||
            ASSERT(owner);
 | 
			
		||||
            ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
 | 
			
		||||
            thread->InvalidateWakeupCallback();
 | 
			
		||||
            thread->SetStatus(ThreadStatus::WaitMutex);
 | 
			
		||||
 | 
			
		||||
            owner->AddMutexWaiter(thread);
 | 
			
		||||
 | 
			
		||||
@ -17,12 +17,15 @@ namespace Kernel {
 | 
			
		||||
Synchronization::Synchronization(Core::System& system) : system{system} {}
 | 
			
		||||
 | 
			
		||||
void Synchronization::SignalObject(SynchronizationObject& obj) const {
 | 
			
		||||
    SchedulerLock lock(system.Kernel());
 | 
			
		||||
    auto& kernel = system.Kernel();
 | 
			
		||||
    SchedulerLock lock(kernel);
 | 
			
		||||
    auto& time_manager = kernel.TimeManager();
 | 
			
		||||
    if (obj.IsSignaled()) {
 | 
			
		||||
        for (auto thread : obj.GetWaitingThreads()) {
 | 
			
		||||
            if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) {
 | 
			
		||||
                thread->SetSynchronizationResults(&obj, RESULT_SUCCESS);
 | 
			
		||||
                thread->ResumeFromWait();
 | 
			
		||||
                time_manager.CancelTimeEvent(thread.get());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -79,6 +82,9 @@ std::pair<ResultCode, Handle> Synchronization::WaitFor(
 | 
			
		||||
        SchedulerLock lock(kernel);
 | 
			
		||||
        ResultCode signaling_result = thread->GetSignalingResult();
 | 
			
		||||
        SynchronizationObject* signaling_object = thread->GetSignalingObject();
 | 
			
		||||
        for (auto& obj : sync_objects) {
 | 
			
		||||
            obj->RemoveWaitingThread(SharedFrom(thread));
 | 
			
		||||
        }
 | 
			
		||||
        if (signaling_result == RESULT_SUCCESS) {
 | 
			
		||||
            const auto itr = std::find_if(
 | 
			
		||||
                sync_objects.begin(), sync_objects.end(),
 | 
			
		||||
 | 
			
		||||
@ -102,6 +102,10 @@ void SynchronizationObject::WakeupAllWaitingThreads() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SynchronizationObject::ClearWaitingThreads() {
 | 
			
		||||
    waiting_threads.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const {
 | 
			
		||||
    return waiting_threads;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -68,6 +68,8 @@ public:
 | 
			
		||||
    /// Get a const reference to the waiting threads list for debug use
 | 
			
		||||
    const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const;
 | 
			
		||||
 | 
			
		||||
    void ClearWaitingThreads();
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    bool is_signaled{}; // Tells if this sync object is signalled;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -49,12 +49,12 @@ Thread::~Thread() = default;
 | 
			
		||||
void Thread::Stop() {
 | 
			
		||||
    SchedulerLock lock(kernel);
 | 
			
		||||
    // Cancel any outstanding wakeup events for this thread
 | 
			
		||||
    Signal();
 | 
			
		||||
    Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
 | 
			
		||||
                                                             global_handle);
 | 
			
		||||
    kernel.GlobalHandleTable().Close(global_handle);
 | 
			
		||||
    global_handle = 0;
 | 
			
		||||
    SetStatus(ThreadStatus::Dead);
 | 
			
		||||
    Signal();
 | 
			
		||||
 | 
			
		||||
    owner_process->UnregisterThread(this);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,15 +8,21 @@
 | 
			
		||||
#include "core/core_timing_util.h"
 | 
			
		||||
#include "core/hle/kernel/handle_table.h"
 | 
			
		||||
#include "core/hle/kernel/kernel.h"
 | 
			
		||||
#include "core/hle/kernel/scheduler.h"
 | 
			
		||||
#include "core/hle/kernel/thread.h"
 | 
			
		||||
#include "core/hle/kernel/time_manager.h"
 | 
			
		||||
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
 | 
			
		||||
TimeManager::TimeManager(Core::System& system) : system{system} {
 | 
			
		||||
TimeManager::TimeManager(Core::System& system_) : system{system_} {
 | 
			
		||||
    time_manager_event_type = Core::Timing::CreateEvent(
 | 
			
		||||
        "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
 | 
			
		||||
            SchedulerLock lock(system.Kernel());
 | 
			
		||||
            Handle proper_handle = static_cast<Handle>(thread_handle);
 | 
			
		||||
            if (cancelled_events[proper_handle]) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            event_fired[proper_handle] = true;
 | 
			
		||||
            std::shared_ptr<Thread> thread =
 | 
			
		||||
                this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
 | 
			
		||||
            thread->OnWakeUp();
 | 
			
		||||
@ -24,14 +30,16 @@ TimeManager::TimeManager(Core::System& system) : system{system} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) {
 | 
			
		||||
    event_handle = timetask->GetGlobalHandle();
 | 
			
		||||
    if (nanoseconds > 0) {
 | 
			
		||||
        ASSERT(timetask);
 | 
			
		||||
        event_handle = timetask->GetGlobalHandle();
 | 
			
		||||
        const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
 | 
			
		||||
        system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle);
 | 
			
		||||
    } else {
 | 
			
		||||
        event_handle = InvalidHandle;
 | 
			
		||||
    }
 | 
			
		||||
    cancelled_events[event_handle] = false;
 | 
			
		||||
    event_fired[event_handle] = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
 | 
			
		||||
@ -39,6 +47,12 @@ void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle);
 | 
			
		||||
    cancelled_events[event_handle] = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TimeManager::CancelTimeEvent(Thread* time_task) {
 | 
			
		||||
    Handle event_handle = time_task->GetGlobalHandle();
 | 
			
		||||
    UnscheduleTimeEvent(event_handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Kernel
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
#include "core/hle/kernel/object.h"
 | 
			
		||||
 | 
			
		||||
@ -35,9 +36,13 @@ public:
 | 
			
		||||
    /// Unschedule an existing time event
 | 
			
		||||
    void UnscheduleTimeEvent(Handle event_handle);
 | 
			
		||||
 | 
			
		||||
    void CancelTimeEvent(Thread* time_task);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Core::System& system;
 | 
			
		||||
    std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
 | 
			
		||||
    std::unordered_map<Handle, bool> cancelled_events;
 | 
			
		||||
    std::unordered_map<Handle, bool> event_fired;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Kernel
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user