1 /*************************************************************************/
2 /*  thread_jandroid.cpp                                                  */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 
31 #include "thread_jandroid.h"
32 
33 #include "core/os/memory.h"
34 #include "core/safe_refcount.h"
35 #include "core/script_language.h"
36 
_thread_id_key_destr_callback(void * p_value)37 static void _thread_id_key_destr_callback(void *p_value) {
38 	memdelete(static_cast<Thread::ID *>(p_value));
39 }
40 
_create_thread_id_key()41 static pthread_key_t _create_thread_id_key() {
42 	pthread_key_t key;
43 	pthread_key_create(&key, &_thread_id_key_destr_callback);
44 	return key;
45 }
46 
47 pthread_key_t ThreadAndroid::thread_id_key = _create_thread_id_key();
48 Thread::ID ThreadAndroid::next_thread_id = 0;
49 
get_id() const50 Thread::ID ThreadAndroid::get_id() const {
51 
52 	return id;
53 }
54 
create_thread_jandroid()55 Thread *ThreadAndroid::create_thread_jandroid() {
56 
57 	return memnew(ThreadAndroid);
58 }
59 
thread_callback(void * userdata)60 void *ThreadAndroid::thread_callback(void *userdata) {
61 
62 	ThreadAndroid *t = reinterpret_cast<ThreadAndroid *>(userdata);
63 	setup_thread();
64 	ScriptServer::thread_enter(); //scripts may need to attach a stack
65 	t->id = atomic_increment(&next_thread_id);
66 	pthread_setspecific(thread_id_key, (void *)memnew(ID(t->id)));
67 	t->callback(t->user);
68 	ScriptServer::thread_exit();
69 	return NULL;
70 }
71 
create_func_jandroid(ThreadCreateCallback p_callback,void * p_user,const Settings &)72 Thread *ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback, void *p_user, const Settings &) {
73 
74 	ThreadAndroid *tr = memnew(ThreadAndroid);
75 	tr->callback = p_callback;
76 	tr->user = p_user;
77 	pthread_attr_init(&tr->pthread_attr);
78 	pthread_attr_setdetachstate(&tr->pthread_attr, PTHREAD_CREATE_JOINABLE);
79 
80 	pthread_create(&tr->pthread, &tr->pthread_attr, thread_callback, tr);
81 
82 	return tr;
83 }
84 
get_thread_id_func_jandroid()85 Thread::ID ThreadAndroid::get_thread_id_func_jandroid() {
86 
87 	void *value = pthread_getspecific(thread_id_key);
88 
89 	if (value)
90 		return *static_cast<ID *>(value);
91 
92 	ID new_id = atomic_increment(&next_thread_id);
93 	pthread_setspecific(thread_id_key, (void *)memnew(ID(new_id)));
94 	return new_id;
95 }
96 
wait_to_finish_func_jandroid(Thread * p_thread)97 void ThreadAndroid::wait_to_finish_func_jandroid(Thread *p_thread) {
98 
99 	ThreadAndroid *tp = static_cast<ThreadAndroid *>(p_thread);
100 	ERR_FAIL_COND(!tp);
101 	ERR_FAIL_COND(tp->pthread == 0);
102 
103 	pthread_join(tp->pthread, NULL);
104 	tp->pthread = 0;
105 }
106 
_thread_destroyed(void * value)107 void ThreadAndroid::_thread_destroyed(void *value) {
108 
109 	/* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
110 	JNIEnv *env = (JNIEnv *)value;
111 	if (env != NULL) {
112 		java_vm->DetachCurrentThread();
113 		pthread_setspecific(jvm_key, NULL);
114 	}
115 }
116 
117 pthread_key_t ThreadAndroid::jvm_key;
118 JavaVM *ThreadAndroid::java_vm = NULL;
119 
setup_thread()120 void ThreadAndroid::setup_thread() {
121 
122 	if (pthread_getspecific(jvm_key))
123 		return; //already setup
124 	JNIEnv *env;
125 	java_vm->AttachCurrentThread(&env, NULL);
126 	pthread_setspecific(jvm_key, (void *)env);
127 }
128 
make_default(JavaVM * p_java_vm)129 void ThreadAndroid::make_default(JavaVM *p_java_vm) {
130 
131 	java_vm = p_java_vm;
132 	create_func = create_func_jandroid;
133 	get_thread_id_func = get_thread_id_func_jandroid;
134 	wait_to_finish_func = wait_to_finish_func_jandroid;
135 	pthread_key_create(&jvm_key, _thread_destroyed);
136 	setup_thread();
137 }
138 
get_env()139 JNIEnv *ThreadAndroid::get_env() {
140 
141 	if (!pthread_getspecific(jvm_key)) {
142 		setup_thread();
143 	}
144 
145 	JNIEnv *env = NULL;
146 	java_vm->AttachCurrentThread(&env, NULL);
147 	return env;
148 }
149 
ThreadAndroid()150 ThreadAndroid::ThreadAndroid() {
151 
152 	pthread = 0;
153 }
154 
~ThreadAndroid()155 ThreadAndroid::~ThreadAndroid() {
156 }
157