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