1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #pragma once
19 
20 #include <vector>
21 #include <map>
22 #include <algorithm>
23 
24 #include "Common/CommonTypes.h"
25 #include "Core/HLE/sceKernelThread.h"
26 
27 namespace HLEKernel
28 {
29 
30 // Should be called from the CoreTiming handler for the wait func.
31 template <typename KO, WaitType waitType>
WaitExecTimeout(SceUID threadID)32 inline void WaitExecTimeout(SceUID threadID) {
33 	u32 error;
34 	SceUID uid = __KernelGetWaitID(threadID, waitType, error);
35 	u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
36 	KO *ko = uid == 0 ? NULL : kernelObjects.Get<KO>(uid, error);
37 	if (ko)
38 	{
39 		if (timeoutPtr != 0)
40 			Memory::Write_U32(0, timeoutPtr);
41 
42 		// This thread isn't waiting anymore, but we'll remove it from waitingThreads later.
43 		// The reason is, if it times out, but what it was waiting on is DELETED prior to it
44 		// actually running, it will get a DELETE result instead of a TIMEOUT.
45 		// So, we need to remember it or we won't be able to mark it DELETE instead later.
46 		__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
47 		__KernelReSchedule("wait timed out");
48 	}
49 }
50 
51 // Move a thread from the waiting thread list to the paused thread list.
52 // This version is for vectors which contain structs, which must have SceUID threadID and u64 pausedTimeout.
53 // Should not be called directly.
54 template <typename WaitInfoType, typename PauseType>
WaitPauseHelperUpdate(SceUID pauseKey,SceUID threadID,std::vector<WaitInfoType> & waitingThreads,std::map<SceUID,PauseType> & pausedWaits,u64 pauseTimeout)55 inline bool WaitPauseHelperUpdate(SceUID pauseKey, SceUID threadID, std::vector<WaitInfoType> &waitingThreads, std::map<SceUID, PauseType> &pausedWaits, u64 pauseTimeout) {
56 	WaitInfoType waitData = {0};
57 	for (size_t i = 0; i < waitingThreads.size(); i++) {
58 		WaitInfoType *t = &waitingThreads[i];
59 		if (t->threadID == threadID)
60 		{
61 			waitData = *t;
62 			// TODO: Hmm, what about priority/fifo order?  Does it lose its place in line?
63 			waitingThreads.erase(waitingThreads.begin() + i);
64 			break;
65 		}
66 	}
67 
68 	if (waitData.threadID != threadID)
69 		return false;
70 
71 	waitData.pausedTimeout = pauseTimeout;
72 	pausedWaits[pauseKey] = waitData;
73 	return true;
74 }
75 
76 // Move a thread from the waiting thread list to the paused thread list.
77 // This version is for a simpler list of SceUIDs.  The paused list is a std::map<SceUID, u64>.
78 // Should not be called directly.
79 template <>
80 inline bool WaitPauseHelperUpdate<SceUID, u64>(SceUID pauseKey, SceUID threadID, std::vector<SceUID> &waitingThreads, std::map<SceUID, u64> &pausedWaits, u64 pauseTimeout) {
81 	// TODO: Hmm, what about priority/fifo order?  Does it lose its place in line?
82 	waitingThreads.erase(std::remove(waitingThreads.begin(), waitingThreads.end(), threadID), waitingThreads.end());
83 	pausedWaits[pauseKey] = pauseTimeout;
84 	return true;
85 }
86 
87 // Retrieve the paused wait info from the list, and pop it.
88 // Returns the pausedTimeout value.
89 // Should not be called directly.
90 template <typename WaitInfoType, typename PauseType>
WaitPauseHelperGet(SceUID pauseKey,SceUID threadID,std::map<SceUID,PauseType> & pausedWaits,WaitInfoType & waitData)91 inline u64 WaitPauseHelperGet(SceUID pauseKey, SceUID threadID, std::map<SceUID, PauseType> &pausedWaits, WaitInfoType &waitData) {
92 	waitData = pausedWaits[pauseKey];
93 	u64 waitDeadline = waitData.pausedTimeout;
94 	pausedWaits.erase(pauseKey);
95 	return waitDeadline;
96 }
97 
98 // Retrieve the paused wait info from the list, and pop it.
99 // This version is for a simple std::map paused list.
100 // Should not be called directly.
101 template <>
102 inline u64 WaitPauseHelperGet<SceUID, u64>(SceUID pauseKey, SceUID threadID, std::map<SceUID, u64> &pausedWaits, SceUID &waitData) {
103 	waitData = threadID;
104 	u64 waitDeadline = pausedWaits[pauseKey];
105 	pausedWaits.erase(pauseKey);
106 	return waitDeadline;
107 }
108 
109 enum WaitBeginEndCallbackResult {
110 	// Returned when the thread cannot be found in the waiting threads list.
111 	// Only returned for struct types, which have other data than the threadID.
112 	WAIT_CB_BAD_WAIT_DATA = -2,
113 	// Returned when the wait ID of the thread no longer matches the kernel object.
114 	WAIT_CB_BAD_WAIT_ID = -1,
115 	// Success, whether that means the wait was paused, deleted, etc.
116 	WAIT_CB_SUCCESS = 0,
117 	// Success, and resumed waiting.  Useful for logging.
118 	WAIT_CB_RESUMED_WAIT = 1,
119 	// Success, but the wait timed out.  Useful for logging.
120 	WAIT_CB_TIMED_OUT = 2,
121 };
122 
123 // Meant to be called in a registered begin callback function for a wait type.
124 //
125 // The goal of this function is to pause the wait.  While inside a callback, waits are released.
126 // Once the callback returns, the wait should be resumed (see WaitEndCallback.)
127 //
128 // This assumes the object has been validated already.  The primary purpose is if you need
129 // to use a specific pausedWaits list (for example, sceMsgPipe has two types of waiting per object.)
130 //
131 // In most cases, use the other, simpler version of WaitBeginCallback().
132 template <typename WaitInfoType, typename PauseType>
133 WaitBeginEndCallbackResult WaitBeginCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, std::vector<WaitInfoType> &waitingThreads, std::map<SceUID, PauseType> &pausedWaits, bool doTimeout = true) {
134 	SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;
135 
136 	// This means two callbacks in a row.  PSP crashes if the same callback waits inside itself (may need more testing.)
137 	// TODO: Handle this better?
138 	if (pausedWaits.find(pauseKey) != pausedWaits.end()) {
139 		return WAIT_CB_SUCCESS;
140 	}
141 
142 	u64 pausedTimeout = 0;
143 	if (doTimeout && waitTimer != -1) {
144 		s64 cyclesLeft = CoreTiming::UnscheduleEvent(waitTimer, threadID);
145 		pausedTimeout = CoreTiming::GetTicks() + cyclesLeft;
146 	}
147 
148 	if (!WaitPauseHelperUpdate(pauseKey, threadID, waitingThreads, pausedWaits, pausedTimeout)) {
149 		return WAIT_CB_BAD_WAIT_DATA;
150 	}
151 
152 	return WAIT_CB_SUCCESS;
153 }
154 
155 // Meant to be called in a registered begin callback function for a wait type.
156 //
157 // The goal of this function is to pause the wait.  While inside a callback, waits are released.
158 // Once the callback returns, the wait should be resumed (see WaitEndCallback.)
159 //
160 // In the majority of cases, calling this function is sufficient for the BeginCallback handler.
161 template <typename KO, WaitType waitType, typename WaitInfoType>
WaitBeginCallback(SceUID threadID,SceUID prevCallbackId,int waitTimer)162 WaitBeginEndCallbackResult WaitBeginCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer) {
163 	u32 error;
164 	SceUID uid = __KernelGetWaitID(threadID, waitType, error);
165 	u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
166 	KO *ko = uid == 0 ? NULL : kernelObjects.Get<KO>(uid, error);
167 	if (ko) {
168 		return WaitBeginCallback(threadID, prevCallbackId, waitTimer, ko->waitingThreads, ko->pausedWaits, timeoutPtr != 0);
169 	} else {
170 		return WAIT_CB_BAD_WAIT_ID;
171 	}
172 }
173 
174 // Meant to be called in a registered end callback function for a wait type.
175 //
176 // The goal of this function is to resume the wait, or to complete it if a wait is no longer needed.
177 //
178 // This version allows you to specify the pausedWaits and waitingThreads vectors, primarily for
179 // MsgPipes which have two waiting thread lists.  Unlike the matching WaitBeginCallback() function,
180 // this still validates the wait (since it needs other data from the object.)
181 //
182 // In most cases, use the other, simpler version of WaitEndCallback().
183 template <typename KO, WaitType waitType, typename WaitInfoType, typename PauseType, class TryUnlockFunc>
WaitEndCallback(SceUID threadID,SceUID prevCallbackId,int waitTimer,TryUnlockFunc TryUnlock,WaitInfoType & waitData,std::vector<WaitInfoType> & waitingThreads,std::map<SceUID,PauseType> & pausedWaits)184 WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, TryUnlockFunc TryUnlock, WaitInfoType &waitData, std::vector<WaitInfoType> &waitingThreads, std::map<SceUID, PauseType> &pausedWaits) {
185 	SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;
186 
187 	// Note: Cancel does not affect suspended semaphore waits, probably same for others.
188 
189 	u32 error;
190 	SceUID uid = __KernelGetWaitID(threadID, waitType, error);
191 	u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
192 	KO *ko = uid == 0 ? NULL : kernelObjects.Get<KO>(uid, error);
193 	if (!ko || pausedWaits.find(pauseKey) == pausedWaits.end()) {
194 		// TODO: Since it was deleted, we don't know how long was actually left.
195 		// For now, we just say the full time was taken.
196 		if (timeoutPtr != 0 && waitTimer != -1) {
197 			Memory::Write_U32(0, timeoutPtr);
198 		}
199 
200 		__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE);
201 		return WAIT_CB_SUCCESS;
202 	}
203 
204 	u64 waitDeadline = WaitPauseHelperGet(pauseKey, threadID, pausedWaits, waitData);
205 
206 	// TODO: Don't wake up if __KernelCurHasReadyCallbacks()?
207 
208 	bool wokeThreads;
209 	// Attempt to unlock.
210 	if (TryUnlock(ko, waitData, error, 0, wokeThreads)) {
211 		return WAIT_CB_SUCCESS;
212 	}
213 
214 	// We only check if it timed out if it couldn't unlock.
215 	s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks();
216 	if (cyclesLeft < 0 && waitDeadline != 0) {
217 		if (timeoutPtr != 0 && waitTimer != -1) {
218 			Memory::Write_U32(0, timeoutPtr);
219 		}
220 
221 		__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
222 		return WAIT_CB_TIMED_OUT;
223 	} else {
224 		if (timeoutPtr != 0 && waitTimer != -1) {
225 			CoreTiming::ScheduleEvent(cyclesLeft, waitTimer, __KernelGetCurThread());
226 		}
227 		return WAIT_CB_RESUMED_WAIT;
228 	}
229 }
230 
231 // Meant to be called in a registered end callback function for a wait type.
232 //
233 // The goal of this function is to resume the wait, or to complete it if a wait is no longer needed.
234 //
235 // The TryUnlockFunc signature should be (choosen due to similarity to existing funcitons):
236 // bool TryUnlock(KO *ko, WaitInfoType waitingThreadInfo, u32 &error, int result, bool &wokeThreads)
237 template <typename KO, WaitType waitType, typename WaitInfoType, class TryUnlockFunc>
WaitEndCallback(SceUID threadID,SceUID prevCallbackId,int waitTimer,TryUnlockFunc TryUnlock)238 WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, TryUnlockFunc TryUnlock) {
239 	u32 error;
240 	SceUID uid = __KernelGetWaitID(threadID, waitType, error);
241 	u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
242 	KO *ko = uid == 0 ? NULL : kernelObjects.Get<KO>(uid, error);
243 	// We need the ko for the vectors, but to avoid a null check we validate it here too.
244 	if (!ko) {
245 		// TODO: Since it was deleted, we don't know how long was actually left.
246 		// For now, we just say the full time was taken.
247 		if (timeoutPtr != 0 && waitTimer != -1) {
248 			Memory::Write_U32(0, timeoutPtr);
249 		}
250 
251 		__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE);
252 		return WAIT_CB_SUCCESS;
253 	}
254 
255 	WaitInfoType waitData;
256 	auto result = WaitEndCallback<KO, waitType>(threadID, prevCallbackId, waitTimer, TryUnlock, waitData, ko->waitingThreads, ko->pausedWaits);
257 	if (result == WAIT_CB_RESUMED_WAIT) {
258 		// TODO: Should this not go at the end?
259 		ko->waitingThreads.push_back(waitData);
260 	}
261 	return result;
262 }
263 
264 // Verify that a thread has not been released from waiting, e.g. by sceKernelReleaseWaitThread().
265 // For a waiting thread info struct.
266 template <typename T>
VerifyWait(const T & waitInfo,WaitType waitType,SceUID uid)267 inline bool VerifyWait(const T &waitInfo, WaitType waitType, SceUID uid) {
268 	u32 error;
269 	SceUID waitID = __KernelGetWaitID(waitInfo.threadID, waitType, error);
270 	return waitID == uid && error == 0;
271 }
272 
273 // Verify that a thread has not been released from waiting, e.g. by sceKernelReleaseWaitThread().
274 template <>
VerifyWait(const SceUID & threadID,WaitType waitType,SceUID uid)275 inline bool VerifyWait(const SceUID &threadID, WaitType waitType, SceUID uid) {
276 	u32 error;
277 	SceUID waitID = __KernelGetWaitID(threadID, waitType, error);
278 	return waitID == uid && error == 0;
279 }
280 
281 // Resume a thread from waiting for a particular object.
282 template <typename T>
ResumeFromWait(SceUID threadID,WaitType waitType,SceUID uid,T result)283 inline bool ResumeFromWait(SceUID threadID, WaitType waitType, SceUID uid, T result) {
284 	if (VerifyWait(threadID, waitType, uid)) {
285 		__KernelResumeThreadFromWait(threadID, result);
286 		return true;
287 	}
288 	return false;
289 }
290 
291 // Removes threads that are not waiting anymore from a waitingThreads list.
292 template <typename T>
CleanupWaitingThreads(WaitType waitType,SceUID uid,std::vector<T> & waitingThreads)293 inline void CleanupWaitingThreads(WaitType waitType, SceUID uid, std::vector<T> &waitingThreads) {
294 	size_t size = waitingThreads.size();
295 	for (size_t i = 0; i < size; ++i) {
296 		if (!VerifyWait(waitingThreads[i], waitType, uid)) {
297 			// Decrement size and swap what was there with i.
298 			if (--size != i) {
299 				std::swap(waitingThreads[i], waitingThreads[size]);
300 			}
301 			// Now we haven't checked the new i, so go back and do i again.
302 			--i;
303 		}
304 	}
305 	waitingThreads.resize(size);
306 }
307 
308 template <typename T>
RemoveWaitingThread(std::vector<T> & waitingThreads,const SceUID threadID)309 inline void RemoveWaitingThread(std::vector<T> &waitingThreads, const SceUID threadID) {
310 	waitingThreads.erase(std::remove(waitingThreads.begin(), waitingThreads.end(), threadID), waitingThreads.end());
311 }
312 
313 };
314