1 /* Copyright (C) 2010-2016 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (rthreads.c).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 #ifdef __unix__
24 #define _POSIX_C_SOURCE 199309
25 #endif
26
27 #include <stdlib.h>
28
29 #include <boolean.h>
30 #include <rthreads/rthreads.h>
31
32 /* with RETRO_WIN32_USE_PTHREADS, pthreads can be used even on win32. Maybe only supported in MSVC>=2005 */
33
34 #if defined(_WIN32) && !defined(RETRO_WIN32_USE_PTHREADS)
35 #define USE_WIN32_THREADS
36 #ifdef _XBOX
37 #include <xtl.h>
38 #else
39 #define WIN32_LEAN_AND_MEAN
40 #include <windows.h>
41 #endif
42 #elif defined(GEKKO)
43 #include "gx_pthread.h"
44 #elif defined(PSP)
45 #include "psp_pthread.h"
46 #else
47 #include <pthread.h>
48 #include <time.h>
49 #endif
50
51 #if defined(VITA)
52 #include <sys/time.h>
53 #endif
54
55 #ifdef __MACH__
56 #include <mach/clock.h>
57 #include <mach/mach.h>
58 #endif
59
60 #include <sys/time.h>
61
62 struct thread_data
63 {
64 void (*func)(void*);
65 void *userdata;
66 };
67
68 struct sthread
69 {
70 #ifdef USE_WIN32_THREADS
71 HANDLE thread;
72 #else
73 pthread_t id;
74 #endif
75 };
76
77 struct slock
78 {
79 #ifdef USE_WIN32_THREADS
80 HANDLE lock;
81 #else
82 pthread_mutex_t lock;
83 #endif
84 };
85
86 struct scond
87 {
88 #ifdef USE_WIN32_THREADS
89 HANDLE event;
90 #else
91 pthread_cond_t cond;
92 #endif
93 };
94
95 #ifdef USE_WIN32_THREADS
thread_wrap(void * data_)96 static DWORD CALLBACK thread_wrap(void *data_)
97 #else
98 static void *thread_wrap(void *data_)
99 #endif
100 {
101 struct thread_data *data = (struct thread_data*)data_;
102 if (!data)
103 return 0;
104 data->func(data->userdata);
105 free(data);
106 return 0;
107 }
108
109 /**
110 * sthread_create:
111 * @start_routine : thread entry callback function
112 * @userdata : pointer to userdata that will be made
113 * available in thread entry callback function
114 *
115 * Create a new thread.
116 *
117 * Returns: pointer to new thread if successful, otherwise NULL.
118 */
sthread_create(void (* thread_func)(void *),void * userdata)119 sthread_t *sthread_create(void (*thread_func)(void*), void *userdata)
120 {
121 bool thread_created = false;
122 struct thread_data *data = NULL;
123 sthread_t *thread = (sthread_t*)calloc(1, sizeof(*thread));
124
125 if (!thread)
126 return NULL;
127
128 data = (struct thread_data*)calloc(1, sizeof(*data));
129 if (!data)
130 goto error;
131
132 data->func = thread_func;
133 data->userdata = userdata;
134
135 #ifdef USE_WIN32_THREADS
136 thread->thread = CreateThread(NULL, 0, thread_wrap, data, 0, NULL);
137 thread_created = !!thread->thread;
138 #else
139 #if defined(VITA)
140 pthread_attr_t thread_attr;
141 pthread_attr_init(&thread_attr);
142 pthread_attr_setstacksize(&thread_attr , 0x10000 );
143 thread_created = pthread_create(&thread->id, &thread_attr, thread_wrap, data) == 0;
144 #else
145 thread_created = pthread_create(&thread->id, NULL, thread_wrap, data) == 0;
146 #endif
147 #endif
148
149 if (!thread_created)
150 goto error;
151
152 return thread;
153
154 error:
155 if (data)
156 free(data);
157 free(thread);
158 return NULL;
159 }
160
161 /**
162 * sthread_detach:
163 * @thread : pointer to thread object
164 *
165 * Detach a thread. When a detached thread terminates, its
166 * resource sare automatically released back to the system
167 * without the need for another thread to join with the
168 * terminated thread.
169 *
170 * Returns: 0 on success, otherwise it returns a non-zero error number.
171 */
sthread_detach(sthread_t * thread)172 int sthread_detach(sthread_t *thread)
173 {
174 #ifdef USE_WIN32_THREADS
175 CloseHandle(thread->thread);
176 free(thread);
177 return 0;
178 #else
179 return pthread_detach(thread->id);
180 #endif
181 }
182
183 /**
184 * sthread_join:
185 * @thread : pointer to thread object
186 *
187 * Join with a terminated thread. Waits for the thread specified by
188 * @thread to terminate. If that thread has already terminated, then
189 * it will return immediately. The thread specified by @thread must
190 * be joinable.
191 *
192 * Returns: 0 on success, otherwise it returns a non-zero error number.
193 */
sthread_join(sthread_t * thread)194 void sthread_join(sthread_t *thread)
195 {
196 #ifdef USE_WIN32_THREADS
197 WaitForSingleObject(thread->thread, INFINITE);
198 CloseHandle(thread->thread);
199 #else
200 pthread_join(thread->id, NULL);
201 #endif
202 free(thread);
203 }
204
205 /**
206 * sthread_isself:
207 * @thread : pointer to thread object
208 *
209 * Returns: true (1) if calling thread is the specified thread
210 */
sthread_isself(sthread_t * thread)211 bool sthread_isself(sthread_t *thread)
212 {
213 #ifdef USE_WIN32_THREADS
214 return GetCurrentThread() == thread->thread;
215 #else
216 return pthread_equal(pthread_self(),thread->id);
217 #endif
218 }
219
220 /**
221 * slock_new:
222 *
223 * Create and initialize a new mutex. Must be manually
224 * freed.
225 *
226 * Returns: pointer to a new mutex if successful, otherwise NULL.
227 **/
slock_new(void)228 slock_t *slock_new(void)
229 {
230 slock_t *lock = (slock_t*)calloc(1, sizeof(*lock));
231 if (!lock)
232 return NULL;
233
234 #ifdef USE_WIN32_THREADS
235 lock->lock = CreateMutex(NULL, FALSE, NULL);
236 if (!lock->lock)
237 goto error;
238 #else
239 if ((pthread_mutex_init(&lock->lock, NULL) < 0))
240 goto error;
241 #endif
242
243 return lock;
244
245 error:
246 slock_free(lock);
247 return NULL;
248 }
249
250 /**
251 * slock_free:
252 * @lock : pointer to mutex object
253 *
254 * Frees a mutex.
255 **/
slock_free(slock_t * lock)256 void slock_free(slock_t *lock)
257 {
258 if (!lock)
259 return;
260
261 #ifdef USE_WIN32_THREADS
262 CloseHandle(lock->lock);
263 #else
264 pthread_mutex_destroy(&lock->lock);
265 #endif
266 free(lock);
267 }
268
269 /**
270 * slock_lock:
271 * @lock : pointer to mutex object
272 *
273 * Locks a mutex. If a mutex is already locked by
274 * another thread, the calling thread shall block until
275 * the mutex becomes available.
276 **/
slock_lock(slock_t * lock)277 void slock_lock(slock_t *lock)
278 {
279 #ifdef USE_WIN32_THREADS
280 WaitForSingleObject(lock->lock, INFINITE);
281 #else
282 pthread_mutex_lock(&lock->lock);
283 #endif
284 }
285
286 /**
287 * slock_unlock:
288 * @lock : pointer to mutex object
289 *
290 * Unlocks a mutex.
291 **/
slock_unlock(slock_t * lock)292 void slock_unlock(slock_t *lock)
293 {
294 #ifdef USE_WIN32_THREADS
295 ReleaseMutex(lock->lock);
296 #else
297 pthread_mutex_unlock(&lock->lock);
298 #endif
299 }
300
301 /**
302 * scond_new:
303 *
304 * Creates and initializes a condition variable. Must
305 * be manually freed.
306 *
307 * Returns: pointer to new condition variable on success,
308 * otherwise NULL.
309 **/
scond_new(void)310 scond_t *scond_new(void)
311 {
312 bool event_created = false;
313 scond_t *cond = (scond_t*)calloc(1, sizeof(*cond));
314
315 if (!cond)
316 return NULL;
317
318 #ifdef USE_WIN32_THREADS
319 cond->event = CreateEvent(NULL, FALSE, FALSE, NULL);
320 event_created = !!cond->event;
321 #else
322 event_created = (pthread_cond_init(&cond->cond, NULL) == 0);
323 #endif
324
325 if (!event_created)
326 goto error;
327
328 return cond;
329
330 error:
331 free(cond);
332 return NULL;
333 }
334
335 /**
336 * scond_free:
337 * @cond : pointer to condition variable object
338 *
339 * Frees a condition variable.
340 **/
scond_free(scond_t * cond)341 void scond_free(scond_t *cond)
342 {
343 if (!cond)
344 return;
345
346 #ifdef USE_WIN32_THREADS
347 CloseHandle(cond->event);
348 #else
349 pthread_cond_destroy(&cond->cond);
350 #endif
351 free(cond);
352 }
353
354 /**
355 * scond_wait:
356 * @cond : pointer to condition variable object
357 * @lock : pointer to mutex object
358 *
359 * Block on a condition variable (i.e. wait on a condition).
360 **/
scond_wait(scond_t * cond,slock_t * lock)361 void scond_wait(scond_t *cond, slock_t *lock)
362 {
363 #ifdef USE_WIN32_THREADS
364 WaitForSingleObject(cond->event, 0);
365
366 SignalObjectAndWait(lock->lock, cond->event, INFINITE, FALSE);
367 slock_lock(lock);
368 #else
369 pthread_cond_wait(&cond->cond, &lock->lock);
370 #endif
371 }
372
373 /**
374 * scond_broadcast:
375 * @cond : pointer to condition variable object
376 *
377 * Broadcast a condition. Unblocks all threads currently blocked
378 * on the specified condition variable @cond.
379 **/
scond_broadcast(scond_t * cond)380 int scond_broadcast(scond_t *cond)
381 {
382 #ifdef USE_WIN32_THREADS
383 /* FIXME _- check how this function should differ
384 * from scond_signal implementation. */
385 SetEvent(cond->event);
386 return 0;
387 #else
388 return pthread_cond_broadcast(&cond->cond);
389 #endif
390 }
391
392 /**
393 * scond_signal:
394 * @cond : pointer to condition variable object
395 *
396 * Signal a condition. Unblocks at least one of the threads currently blocked
397 * on the specified condition variable @cond.
398 **/
scond_signal(scond_t * cond)399 void scond_signal(scond_t *cond)
400 {
401 #ifdef USE_WIN32_THREADS
402 SetEvent(cond->event);
403 #else
404 pthread_cond_signal(&cond->cond);
405 #endif
406 }
407
408 /**
409 * scond_wait_timeout:
410 * @cond : pointer to condition variable object
411 * @lock : pointer to mutex object
412 * @timeout_us : timeout (in microseconds)
413 *
414 * Try to block on a condition variable (i.e. wait on a condition) until
415 * @timeout_us elapses.
416 *
417 * Returns: false (0) if timeout elapses before condition variable is
418 * signaled or broadcast, otherwise true (1).
419 **/
scond_wait_timeout(scond_t * cond,slock_t * lock,int64_t timeout_us)420 bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us)
421 {
422 #ifdef USE_WIN32_THREADS
423 DWORD ret;
424
425 WaitForSingleObject(cond->event, 0);
426 ret = SignalObjectAndWait(lock->lock, cond->event,
427 (DWORD)(timeout_us) / 1000, FALSE);
428
429 slock_lock(lock);
430 return ret == WAIT_OBJECT_0;
431 #else
432 int ret;
433 int64_t seconds, remainder;
434 struct timespec now = {0};
435
436 #ifdef __MACH__
437 /* OSX doesn't have clock_gettime. */
438 clock_serv_t cclock;
439 mach_timespec_t mts;
440
441 host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
442 clock_get_time(cclock, &mts);
443 mach_port_deallocate(mach_task_self(), cclock);
444 now.tv_sec = mts.tv_sec;
445 now.tv_nsec = mts.tv_nsec;
446 #elif defined(__mips__) || defined(VITA)
447 struct timeval tm;
448
449 gettimeofday(&tm, NULL);
450 now.tv_sec = tm.tv_sec;
451 now.tv_nsec = tm.tv_usec * 1000;
452 #elif defined(RETRO_WIN32_USE_PTHREADS)
453 _ftime64_s(&now);
454 #elif !defined(GEKKO)
455 /* timeout on libogc is duration, not end time. */
456 clock_gettime(CLOCK_REALTIME, &now);
457 #endif
458
459 seconds = timeout_us / INT64_C(1000000);
460 remainder = timeout_us % INT64_C(1000000);
461
462 now.tv_sec += seconds;
463 now.tv_nsec += remainder * INT64_C(1000);
464
465 ret = pthread_cond_timedwait(&cond->cond, &lock->lock, &now);
466 return (ret == 0);
467 #endif
468 }
469
470 #ifdef HAVE_THREAD_STORAGE
sthread_tls_create(sthread_tls_t * tls)471 bool sthread_tls_create(sthread_tls_t *tls)
472 {
473 #ifdef USE_WIN32_THREADS
474 return (*tls = TlsAlloc()) != TLS_OUT_OF_INDEXES;
475 #else
476 return pthread_key_create(tls, NULL) == 0;
477 #endif
478 }
479
sthread_tls_delete(sthread_tls_t * tls)480 bool sthread_tls_delete(sthread_tls_t *tls)
481 {
482 #ifdef USE_WIN32_THREADS
483 return TlsFree(*tls) != 0;
484 #else
485 return pthread_key_delete(*tls) == 0;
486 #endif
487 }
488
sthread_tls_get(sthread_tls_t * tls)489 void *sthread_tls_get(sthread_tls_t *tls)
490 {
491 #ifdef USE_WIN32_THREADS
492 return TlsGetValue(*tls);
493 #else
494 return pthread_getspecific(*tls);
495 #endif
496 }
497
sthread_tls_set(sthread_tls_t * tls,const void * data)498 bool sthread_tls_set(sthread_tls_t *tls, const void *data)
499 {
500 #ifdef USE_WIN32_THREADS
501 return TlsSetValue(*tls, (void*)data) != 0;
502 #else
503 return pthread_setspecific(*tls, data) == 0;
504 #endif
505 }
506 #endif
507