CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HLE/KernelWaitHelpers.h
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#pragma once1819#include <vector>20#include <map>21#include <algorithm>2223#include "Common/CommonTypes.h"24#include "Core/HLE/sceKernelThread.h"2526namespace HLEKernel27{2829// Should be called from the CoreTiming handler for the wait func.30template <typename KO, WaitType waitType>31inline void WaitExecTimeout(SceUID threadID) {32u32 error;33SceUID uid = __KernelGetWaitID(threadID, waitType, error);34u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);35KO *ko = uid == 0 ? NULL : kernelObjects.Get<KO>(uid, error);36if (ko)37{38if (timeoutPtr != 0)39Memory::Write_U32(0, timeoutPtr);4041// This thread isn't waiting anymore, but we'll remove it from waitingThreads later.42// The reason is, if it times out, but what it was waiting on is DELETED prior to it43// actually running, it will get a DELETE result instead of a TIMEOUT.44// So, we need to remember it or we won't be able to mark it DELETE instead later.45__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);46__KernelReSchedule("wait timed out");47}48}4950// Move a thread from the waiting thread list to the paused thread list.51// This version is for vectors which contain structs, which must have SceUID threadID and u64 pausedTimeout.52// Should not be called directly.53template <typename WaitInfoType, typename PauseType>54inline bool WaitPauseHelperUpdate(SceUID pauseKey, SceUID threadID, std::vector<WaitInfoType> &waitingThreads, std::map<SceUID, PauseType> &pausedWaits, u64 pauseTimeout) {55WaitInfoType waitData = {0};56for (size_t i = 0; i < waitingThreads.size(); i++) {57WaitInfoType *t = &waitingThreads[i];58if (t->threadID == threadID)59{60waitData = *t;61// TODO: Hmm, what about priority/fifo order? Does it lose its place in line?62waitingThreads.erase(waitingThreads.begin() + i);63break;64}65}6667if (waitData.threadID != threadID)68return false;6970waitData.pausedTimeout = pauseTimeout;71pausedWaits[pauseKey] = waitData;72return true;73}7475// Move a thread from the waiting thread list to the paused thread list.76// This version is for a simpler list of SceUIDs. The paused list is a std::map<SceUID, u64>.77// Should not be called directly.78template <>79inline bool WaitPauseHelperUpdate<SceUID, u64>(SceUID pauseKey, SceUID threadID, std::vector<SceUID> &waitingThreads, std::map<SceUID, u64> &pausedWaits, u64 pauseTimeout) {80// TODO: Hmm, what about priority/fifo order? Does it lose its place in line?81waitingThreads.erase(std::remove(waitingThreads.begin(), waitingThreads.end(), threadID), waitingThreads.end());82pausedWaits[pauseKey] = pauseTimeout;83return true;84}8586// Retrieve the paused wait info from the list, and pop it.87// Returns the pausedTimeout value.88// Should not be called directly.89template <typename WaitInfoType, typename PauseType>90inline u64 WaitPauseHelperGet(SceUID pauseKey, SceUID threadID, std::map<SceUID, PauseType> &pausedWaits, WaitInfoType &waitData) {91waitData = pausedWaits[pauseKey];92u64 waitDeadline = waitData.pausedTimeout;93pausedWaits.erase(pauseKey);94return waitDeadline;95}9697// Retrieve the paused wait info from the list, and pop it.98// This version is for a simple std::map paused list.99// Should not be called directly.100template <>101inline u64 WaitPauseHelperGet<SceUID, u64>(SceUID pauseKey, SceUID threadID, std::map<SceUID, u64> &pausedWaits, SceUID &waitData) {102waitData = threadID;103u64 waitDeadline = pausedWaits[pauseKey];104pausedWaits.erase(pauseKey);105return waitDeadline;106}107108enum WaitBeginEndCallbackResult {109// Returned when the thread cannot be found in the waiting threads list.110// Only returned for struct types, which have other data than the threadID.111WAIT_CB_BAD_WAIT_DATA = -2,112// Returned when the wait ID of the thread no longer matches the kernel object.113WAIT_CB_BAD_WAIT_ID = -1,114// Success, whether that means the wait was paused, deleted, etc.115WAIT_CB_SUCCESS = 0,116// Success, and resumed waiting. Useful for logging.117WAIT_CB_RESUMED_WAIT = 1,118// Success, but the wait timed out. Useful for logging.119WAIT_CB_TIMED_OUT = 2,120};121122// Meant to be called in a registered begin callback function for a wait type.123//124// The goal of this function is to pause the wait. While inside a callback, waits are released.125// Once the callback returns, the wait should be resumed (see WaitEndCallback.)126//127// This assumes the object has been validated already. The primary purpose is if you need128// to use a specific pausedWaits list (for example, sceMsgPipe has two types of waiting per object.)129//130// In most cases, use the other, simpler version of WaitBeginCallback().131template <typename WaitInfoType, typename PauseType>132WaitBeginEndCallbackResult WaitBeginCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, std::vector<WaitInfoType> &waitingThreads, std::map<SceUID, PauseType> &pausedWaits, bool doTimeout = true) {133SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;134135// This means two callbacks in a row. PSP crashes if the same callback waits inside itself (may need more testing.)136// TODO: Handle this better?137if (pausedWaits.find(pauseKey) != pausedWaits.end()) {138return WAIT_CB_SUCCESS;139}140141u64 pausedTimeout = 0;142if (doTimeout && waitTimer != -1) {143s64 cyclesLeft = CoreTiming::UnscheduleEvent(waitTimer, threadID);144pausedTimeout = CoreTiming::GetTicks() + cyclesLeft;145}146147if (!WaitPauseHelperUpdate(pauseKey, threadID, waitingThreads, pausedWaits, pausedTimeout)) {148return WAIT_CB_BAD_WAIT_DATA;149}150151return WAIT_CB_SUCCESS;152}153154// Meant to be called in a registered begin callback function for a wait type.155//156// The goal of this function is to pause the wait. While inside a callback, waits are released.157// Once the callback returns, the wait should be resumed (see WaitEndCallback.)158//159// In the majority of cases, calling this function is sufficient for the BeginCallback handler.160template <typename KO, WaitType waitType, typename WaitInfoType>161WaitBeginEndCallbackResult WaitBeginCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer) {162u32 error;163SceUID uid = __KernelGetWaitID(threadID, waitType, error);164u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);165KO *ko = uid == 0 ? NULL : kernelObjects.Get<KO>(uid, error);166if (ko) {167return WaitBeginCallback(threadID, prevCallbackId, waitTimer, ko->waitingThreads, ko->pausedWaits, timeoutPtr != 0);168} else {169return WAIT_CB_BAD_WAIT_ID;170}171}172173// Meant to be called in a registered end callback function for a wait type.174//175// The goal of this function is to resume the wait, or to complete it if a wait is no longer needed.176//177// This version allows you to specify the pausedWaits and waitingThreads vectors, primarily for178// MsgPipes which have two waiting thread lists. Unlike the matching WaitBeginCallback() function,179// this still validates the wait (since it needs other data from the object.)180//181// In most cases, use the other, simpler version of WaitEndCallback().182template <typename KO, WaitType waitType, typename WaitInfoType, typename PauseType, class TryUnlockFunc>183WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, TryUnlockFunc TryUnlock, WaitInfoType &waitData, std::vector<WaitInfoType> &waitingThreads, std::map<SceUID, PauseType> &pausedWaits) {184SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;185186// Note: Cancel does not affect suspended semaphore waits, probably same for others.187188u32 error;189SceUID uid = __KernelGetWaitID(threadID, waitType, error);190u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);191KO *ko = uid == 0 ? NULL : kernelObjects.Get<KO>(uid, error);192if (!ko || pausedWaits.find(pauseKey) == pausedWaits.end()) {193// TODO: Since it was deleted, we don't know how long was actually left.194// For now, we just say the full time was taken.195if (timeoutPtr != 0 && waitTimer != -1) {196Memory::Write_U32(0, timeoutPtr);197}198199__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE);200return WAIT_CB_SUCCESS;201}202203u64 waitDeadline = WaitPauseHelperGet(pauseKey, threadID, pausedWaits, waitData);204205// TODO: Don't wake up if __KernelCurHasReadyCallbacks()?206207bool wokeThreads;208// Attempt to unlock.209if (TryUnlock(ko, waitData, error, 0, wokeThreads)) {210return WAIT_CB_SUCCESS;211}212213// We only check if it timed out if it couldn't unlock.214s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks();215if (cyclesLeft < 0 && waitDeadline != 0) {216if (timeoutPtr != 0 && waitTimer != -1) {217Memory::Write_U32(0, timeoutPtr);218}219220__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);221return WAIT_CB_TIMED_OUT;222} else {223if (timeoutPtr != 0 && waitTimer != -1) {224CoreTiming::ScheduleEvent(cyclesLeft, waitTimer, __KernelGetCurThread());225}226return WAIT_CB_RESUMED_WAIT;227}228}229230// Meant to be called in a registered end callback function for a wait type.231//232// The goal of this function is to resume the wait, or to complete it if a wait is no longer needed.233//234// The TryUnlockFunc signature should be (choosen due to similarity to existing funcitons):235// bool TryUnlock(KO *ko, WaitInfoType waitingThreadInfo, u32 &error, int result, bool &wokeThreads)236template <typename KO, WaitType waitType, typename WaitInfoType, class TryUnlockFunc>237WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, TryUnlockFunc TryUnlock) {238u32 error;239SceUID uid = __KernelGetWaitID(threadID, waitType, error);240u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);241KO *ko = uid == 0 ? NULL : kernelObjects.Get<KO>(uid, error);242// We need the ko for the vectors, but to avoid a null check we validate it here too.243if (!ko) {244// TODO: Since it was deleted, we don't know how long was actually left.245// For now, we just say the full time was taken.246if (timeoutPtr != 0 && waitTimer != -1) {247Memory::Write_U32(0, timeoutPtr);248}249250__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE);251return WAIT_CB_SUCCESS;252}253254WaitInfoType waitData;255auto result = WaitEndCallback<KO, waitType>(threadID, prevCallbackId, waitTimer, TryUnlock, waitData, ko->waitingThreads, ko->pausedWaits);256if (result == WAIT_CB_RESUMED_WAIT) {257// TODO: Should this not go at the end?258ko->waitingThreads.push_back(waitData);259}260return result;261}262263// Verify that a thread has not been released from waiting, e.g. by sceKernelReleaseWaitThread().264// For a waiting thread info struct.265template <typename T>266inline bool VerifyWait(const T &waitInfo, WaitType waitType, SceUID uid) {267u32 error;268SceUID waitID = __KernelGetWaitID(waitInfo.threadID, waitType, error);269return waitID == uid && error == 0;270}271272// Verify that a thread has not been released from waiting, e.g. by sceKernelReleaseWaitThread().273template <>274inline bool VerifyWait(const SceUID &threadID, WaitType waitType, SceUID uid) {275u32 error;276SceUID waitID = __KernelGetWaitID(threadID, waitType, error);277return waitID == uid && error == 0;278}279280// Resume a thread from waiting for a particular object.281template <typename T>282inline bool ResumeFromWait(SceUID threadID, WaitType waitType, SceUID uid, T result) {283if (VerifyWait(threadID, waitType, uid)) {284__KernelResumeThreadFromWait(threadID, result);285return true;286}287return false;288}289290// Removes threads that are not waiting anymore from a waitingThreads list.291template <typename T>292inline void CleanupWaitingThreads(WaitType waitType, SceUID uid, std::vector<T> &waitingThreads) {293size_t size = waitingThreads.size();294for (size_t i = 0; i < size; ++i) {295if (!VerifyWait(waitingThreads[i], waitType, uid)) {296// Decrement size and swap what was there with i.297if (--size != i) {298std::swap(waitingThreads[i], waitingThreads[size]);299}300// Now we haven't checked the new i, so go back and do i again.301--i;302}303}304waitingThreads.resize(size);305}306307template <typename T>308inline void RemoveWaitingThread(std::vector<T> &waitingThreads, const SceUID threadID) {309waitingThreads.erase(std::remove(waitingThreads.begin(), waitingThreads.end(), threadID), waitingThreads.end());310}311312};313314315