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