1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include <pspthreadman.h>
24
25 #include "backends/platform/psp/thread.h"
26 #include "backends/platform/psp/trace.h"
27
28 // Class PspThreadable --------------------------------------------------
29 // Inherit this to create C++ threads easily
30
threadCreateAndStart(const char * threadName,int priority,int stackSize,bool useVfpu)31 bool PspThreadable::threadCreateAndStart(const char *threadName, int priority, int stackSize, bool useVfpu /*= false*/) {
32 DEBUG_ENTER_FUNC();
33
34 if (_threadId != -1) {
35 PSP_ERROR("thread already created!\n");
36 return false;
37 }
38
39 _threadId = sceKernelCreateThread(threadName, __threadCallback, priority, stackSize, THREAD_ATTR_USER, 0); // add VFPU support
40
41 if (_threadId < 0) {
42 PSP_ERROR("failed to create %s thread. Error code %d\n", threadName, _threadId);
43 return false;
44 }
45
46 // We want to pass the pointer to this, but we'll have to take address of this so use a little trick
47 PspThreadable *_this = this;
48
49 if (sceKernelStartThread(_threadId, sizeof(uint32 *), &_this) < 0) {
50 PSP_ERROR("failed to start %s thread id[%d]\n", threadName, _threadId);
51 return false;
52 }
53
54 PSP_DEBUG_PRINT("Started %s thread with id[%x]\n", threadName, _threadId);
55
56 return true;
57 }
58
59 // Callback function to be called by PSP kernel
__threadCallback(SceSize,void * __this)60 int PspThreadable::__threadCallback(SceSize, void *__this) {
61 DEBUG_ENTER_FUNC();
62
63 PspThreadable *_this = *(PspThreadable **)__this; // Dereference the copied value which was 'this'
64
65 _this->threadFunction(); // call the virtual function
66
67 return 0;
68 }
69
70 // PspThread class
71 // Utilities to access general thread functions
72
delayMillis(uint32 ms)73 void PspThread::delayMillis(uint32 ms) {
74 sceKernelDelayThread(ms * 1000);
75 }
76
delayMicros(uint32 us)77 void PspThread::delayMicros(uint32 us) {
78 sceKernelDelayThread(us);
79 }
80
81 // Class PspSemaphore ------------------------------------------------
82 //#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
83 //#define __PSP_DEBUG_PRINT__ /* For debug printouts */
84
85 #include "backends/platform/psp/trace.h"
86
PspSemaphore(int initialValue,int maxValue)87 PspSemaphore::PspSemaphore(int initialValue, int maxValue/*=255*/) {
88 DEBUG_ENTER_FUNC();
89 _handle = 0;
90 _handle = (uint32)sceKernelCreateSema("ScummVM Sema", 0 /* attr */,
91 initialValue, maxValue,
92 0 /*option*/);
93 if (!_handle)
94 PSP_ERROR("failed to create semaphore.\n");
95 }
96
~PspSemaphore()97 PspSemaphore::~PspSemaphore() {
98 DEBUG_ENTER_FUNC();
99 if (_handle)
100 if (sceKernelDeleteSema((SceUID)_handle) < 0)
101 PSP_ERROR("failed to delete semaphore.\n");
102 }
103
numOfWaitingThreads()104 int PspSemaphore::numOfWaitingThreads() {
105 DEBUG_ENTER_FUNC();
106 SceKernelSemaInfo info;
107 info.numWaitThreads = 0;
108
109 if (sceKernelReferSemaStatus((SceUID)_handle, &info) < 0)
110 PSP_ERROR("failed to retrieve semaphore info for handle %d\n", _handle);
111
112 return info.numWaitThreads;
113 }
114
getValue()115 int PspSemaphore::getValue() {
116 DEBUG_ENTER_FUNC();
117 SceKernelSemaInfo info;
118 info.currentCount = 0;
119
120 if (sceKernelReferSemaStatus((SceUID)_handle, &info) < 0)
121 PSP_ERROR("failed to retrieve semaphore info for handle %d\n", _handle);
122
123 return info.currentCount;
124 }
125
pollForValue(int value)126 bool PspSemaphore::pollForValue(int value) {
127 DEBUG_ENTER_FUNC();
128 if (sceKernelPollSema((SceUID)_handle, value) < 0)
129 return false;
130
131 return true;
132 }
133
134 // false: timeout or error
takeWithTimeOut(uint32 timeOut)135 bool PspSemaphore::takeWithTimeOut(uint32 timeOut) {
136 DEBUG_ENTER_FUNC();
137
138 uint32 *pTimeOut = 0;
139 if (timeOut)
140 pTimeOut = &timeOut;
141
142 if (sceKernelWaitSema(_handle, 1, pTimeOut) < 0) // we always wait for 1
143 return false;
144 return true;
145 }
146
give(int num)147 bool PspSemaphore::give(int num /*=1*/) {
148 DEBUG_ENTER_FUNC();
149
150 if (sceKernelSignalSema((SceUID)_handle, num) < 0)
151 return false;
152 return true;
153 }
154
155 // Class PspMutex ------------------------------------------------------------
156
lock()157 bool PspMutex::lock() {
158 DEBUG_ENTER_FUNC();
159 int threadId = sceKernelGetThreadId();
160 bool ret = true;
161
162 if (_ownerId == threadId) {
163 _recursiveCount++;
164 } else {
165 ret = _semaphore.take();
166 _ownerId = threadId;
167 _recursiveCount = 0;
168 }
169 return ret;
170 }
171
unlock()172 bool PspMutex::unlock() {
173 DEBUG_ENTER_FUNC();
174 int threadId = sceKernelGetThreadId();
175 bool ret = true;
176
177 if (_ownerId != threadId) {
178 PSP_ERROR("attempt to unlock mutex by thread[%x] as opposed to owner[%x]\n",
179 threadId, _ownerId);
180 return false;
181 }
182
183 if (_recursiveCount) {
184 _recursiveCount--;
185 } else {
186 _ownerId = 0;
187 ret = _semaphore.give(1);
188 }
189 return ret;
190 }
191
192 // Class PspCondition -------------------------------------------------
193
194 // Release all threads waiting on the condition
releaseAll()195 void PspCondition::releaseAll() {
196 _mutex.lock();
197 if (_waitingThreads > _signaledThreads) { // we have signals to issue
198 int numWaiting = _waitingThreads - _signaledThreads; // threads we haven't signaled
199 _signaledThreads = _waitingThreads;
200
201 _waitSem.give(numWaiting);
202 _mutex.unlock();
203 for (int i=0; i<numWaiting; i++) // wait for threads to tell us they're awake
204 _doneSem.take();
205 } else {
206 _mutex.unlock();
207 }
208 }
209
210 // Mutex must be taken before entering wait
wait(PspMutex & externalMutex)211 void PspCondition::wait(PspMutex &externalMutex) {
212 _mutex.lock();
213 _waitingThreads++;
214 _mutex.unlock();
215
216 externalMutex.unlock(); // must unlock external mutex
217
218 _waitSem.take(); // sleep on the wait semaphore
219
220 // let the signaling thread know we're done
221 _mutex.lock();
222 if (_signaledThreads > 0 ) {
223 _doneSem.give(); // let the thread know
224 _signaledThreads--;
225 }
226 _waitingThreads--;
227 _mutex.unlock();
228
229 externalMutex.lock(); // must lock external mutex here for continuation
230 }
231