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