1 // This is copyrighted software. More information is at the end of this file.
2 #include "retro_emu_thread.h"
3 
4 #include <pthread.h>
5 
6 static pthread_t main_thread;
7 static pthread_t emu_thread;
8 static pthread_mutex_t emu_mutex;
9 static pthread_mutex_t main_mutex;
10 static pthread_cond_t emu_cv;
11 static pthread_cond_t main_cv;
12 static bool emu_keep_waiting = true;
13 static bool main_keep_waiting = true;
14 static bool emu_has_exited = false;
15 static bool emu_thread_canceled = false;
16 static bool emu_thread_initialized = false;
17 
retro_run_emulator(void * args)18 static void* retro_run_emulator(void *args)
19 {
20    char *args_str = (char *)args;
21    bool dynarec   = (*args_str++ == 1) ? true : false;
22    u32 cycles     = strtol(args_str, NULL, 10);
23 
24    emu_has_exited      = false;
25    emu_thread_canceled = false;
26 
27 #if defined(HAVE_DYNAREC)
28    if (dynarec)
29       execute_arm_translate(cycles);
30 #endif
31    execute_arm(cycles);
32 
33    emu_has_exited = true;
34    return NULL;
35 }
36 
retro_switch_to_emu_thread()37 static void retro_switch_to_emu_thread()
38 {
39    pthread_mutex_lock(&emu_mutex);
40    emu_keep_waiting = false;
41    pthread_mutex_unlock(&emu_mutex);
42    pthread_mutex_lock(&main_mutex);
43    pthread_cond_signal(&emu_cv);
44 
45    main_keep_waiting = true;
46    while (main_keep_waiting)
47    {
48       pthread_cond_wait(&main_cv, &main_mutex);
49    }
50    pthread_mutex_unlock(&main_mutex);
51 }
52 
retro_switch_to_main_thread()53 static void retro_switch_to_main_thread()
54 {
55    pthread_mutex_lock(&main_mutex);
56    main_keep_waiting = false;
57    pthread_mutex_unlock(&main_mutex);
58    pthread_mutex_lock(&emu_mutex);
59    pthread_cond_signal(&main_cv);
60 
61    emu_keep_waiting = true;
62    while (emu_keep_waiting)
63    {
64       pthread_cond_wait(&emu_cv, &emu_mutex);
65    }
66    pthread_mutex_unlock(&emu_mutex);
67 }
68 
retro_switch_thread()69 void retro_switch_thread()
70 {
71    if (pthread_self() == main_thread)
72       retro_switch_to_emu_thread();
73    else
74       retro_switch_to_main_thread();
75 }
76 
retro_init_emu_thread(bool dynarec,u32 cycles)77 bool retro_init_emu_thread(bool dynarec, u32 cycles)
78 {
79    char args[256];
80    args[0] = '\0';
81 
82    if (emu_thread_initialized)
83       return true;
84 
85    /* Keep this very simple:
86     * - First character: dynarec, 0/1
87     * - Remaining characters: cycles */
88    snprintf(args, sizeof(args), " %u", cycles);
89    args[0] = dynarec ? 1 : 0;
90 
91    main_thread = pthread_self();
92    if (pthread_mutex_init(&main_mutex, NULL))
93       goto main_mutex_error;
94    if (pthread_mutex_init(&emu_mutex, NULL))
95       goto emu_mutex_error;
96    if (pthread_cond_init(&main_cv, NULL))
97       goto main_cv_error;
98    if (pthread_cond_init(&emu_cv, NULL))
99       goto emu_cv_error;
100    if (pthread_create(&emu_thread, NULL, retro_run_emulator, args))
101       goto emu_thread_error;
102 
103    emu_thread_initialized = true;
104    return true;
105 
106 emu_thread_error:
107    pthread_cond_destroy(&emu_cv);
108 emu_cv_error:
109    pthread_cond_destroy(&main_cv);
110 main_cv_error:
111    pthread_mutex_destroy(&emu_mutex);
112 emu_mutex_error:
113    pthread_mutex_destroy(&main_mutex);
114 main_mutex_error:
115    return false;
116 }
117 
retro_deinit_emu_thread()118 void retro_deinit_emu_thread()
119 {
120    if (!emu_thread_initialized)
121       return;
122 
123    pthread_mutex_destroy(&main_mutex);
124    pthread_mutex_destroy(&emu_mutex);
125    pthread_cond_destroy(&main_cv);
126    pthread_cond_destroy(&emu_cv);
127    emu_thread_initialized = false;
128 }
129 
retro_is_emu_thread_initialized()130 bool retro_is_emu_thread_initialized()
131 {
132    return emu_thread_initialized;
133 }
134 
retro_join_emu_thread()135 void retro_join_emu_thread()
136 {
137    static bool is_joined = false;
138    if (is_joined)
139       return;
140 
141    pthread_join(emu_thread, NULL);
142    is_joined = true;
143 }
144 
retro_cancel_emu_thread()145 void retro_cancel_emu_thread()
146 {
147    if (emu_thread_canceled)
148       return;
149 
150    pthread_cancel(emu_thread);
151    emu_thread_canceled = true;
152 }
153 
retro_emu_thread_exited()154 bool retro_emu_thread_exited()
155 {
156    return emu_has_exited;
157 }
158 
159 /*
160 
161 Copyright (C) 2020 Nikos Chantziaras <realnc@gmail.com>
162 
163 This program is free software: you can redistribute it and/or modify it under
164 the terms of the GNU General Public License as published by the Free Software
165 Foundation, either version 2 of the License, or (at your option) any later
166 version.
167 
168 This program is distributed in the hope that it will be useful, but WITHOUT
169 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
170 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
171 
172 You should have received a copy of the GNU General Public License along with
173 this program. If not, see <https://www.gnu.org/licenses/>.
174 
175 */
176