Panda3D
Loading...
Searching...
No Matches
threadPosixImpl.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file threadPosixImpl.cxx
10 * @author drose
11 * @date 2006-02-09
12 */
13
14#include "threadPosixImpl.h"
15#include "selectThreadImpl.h"
16
17#ifdef THREAD_POSIX_IMPL
18
19#include "thread.h"
20#include "pointerTo.h"
21#include "config_pipeline.h"
22#include <sched.h>
23
24#ifdef ANDROID
25#include "config_express.h"
26#include <jni.h>
27
28static JavaVM *java_vm = nullptr;
29#endif
30
31pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0;
32bool ThreadPosixImpl::_got_pt_ptr_index = false;
33
34/**
35 *
36 */
37ThreadPosixImpl::
38~ThreadPosixImpl() {
39 if (thread_cat->is_debug()) {
40 thread_cat.debug()
41 << "Deleting thread " << _parent_obj->get_name() << "\n";
42 }
43
44 _mutex.lock();
45
46 if (!_detached) {
47 pthread_detach(_thread);
48 _detached = true;
49 }
50
51 _mutex.unlock();
52}
53
54/**
55 * Called for the main thread only, which has been already started, to fill in
56 * the values appropriate to that thread.
57 */
58void ThreadPosixImpl::
59setup_main_thread() {
60 _status = S_running;
61 _thread = pthread_self();
62}
63
64/**
65 *
66 */
67bool ThreadPosixImpl::
68start(ThreadPriority priority, bool joinable) {
69 _mutex.lock();
70 if (thread_cat->is_debug()) {
71 thread_cat.debug() << "Starting " << *_parent_obj << "\n";
72 }
73
74 nassertd(_status == S_new) {
75 _mutex.unlock();
76 return false;
77 }
78
79 _joinable = joinable;
80 _status = S_start_called;
81 _detached = false;
82
83 if (!_got_pt_ptr_index) {
84 init_pt_ptr_index();
85 }
86
87 pthread_attr_t attr;
88 pthread_attr_init(&attr);
89
90 if (!_joinable) {
91 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
92 _detached = true;
93 }
94
95 int result = pthread_attr_setstacksize(&attr, thread_stack_size);
96 if (result != 0) {
97 thread_cat->warning()
98 << "Unable to set stack size.\n";
99 }
100
101 // Ensure the thread has "system" scope, which should ensure it can run in
102 // parallel with other threads.
103 result = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
104 if (result != 0) {
105 thread_cat->warning()
106 << "Unable to set system scope.\n";
107 }
108
109 struct sched_param param;
110 int current_policy = SCHED_OTHER;
111 result = pthread_attr_setschedpolicy(&attr, current_policy);
112 if (result != 0) {
113 thread_cat->warning()
114 << "Unable to set scheduling policy.\n";
115
116 }
117
118 result = 0;
119 switch (priority) {
120 case TP_low:
121 param.sched_priority = sched_get_priority_min(current_policy);
122 result = pthread_attr_setschedparam(&attr, &param);
123 break;
124
125 case TP_high:
126 case TP_urgent:
127 param.sched_priority = sched_get_priority_max(current_policy);
128 result = pthread_attr_setschedparam(&attr, &param);
129 break;
130
131 case TP_normal:
132 default:
133 break;
134 }
135
136 if (result != 0) {
137 thread_cat->warning()
138 << "Unable to specify thread priority.\n";
139 }
140
141 // Increment the parent object's reference count first. The thread will
142 // eventually decrement it when it terminates.
143 _parent_obj->ref();
144 result = pthread_create(&_thread, &attr, &root_func, (void *)this);
145
146 pthread_attr_destroy(&attr);
147
148 if (result != 0) {
149 // Oops, we couldn't start the thread. Be sure to decrement the reference
150 // count we incremented above, and return false to indicate failure.
151 unref_delete(_parent_obj);
152 _mutex.unlock();
153 return false;
154 }
155
156 // Thread was successfully started.
157 _mutex.unlock();
158 return true;
159}
160
161/**
162 * Blocks the calling process until the thread terminates. If the thread has
163 * already terminated, this returns immediately.
164 */
165void ThreadPosixImpl::
166join() {
167 _mutex.lock();
168 if (!_detached) {
169 _mutex.unlock();
170 void *return_val;
171 pthread_join(_thread, &return_val);
172 _detached = true;
173 return;
174 }
175 _mutex.unlock();
176}
177
178/**
179 *
180 */
181std::string ThreadPosixImpl::
182get_unique_id() const {
183 std::ostringstream strm;
184 strm << getpid() << "." << (uintptr_t)_thread;
185
186 return strm.str();
187}
188
189#ifdef ANDROID
190/**
191 * Attaches the thread to the Java virtual machine. If this returns true, a
192 * JNIEnv pointer can be acquired using get_jni_env().
193 */
194bool ThreadPosixImpl::
195attach_java_vm() {
196 JNIEnv *env;
197 std::string thread_name = _parent_obj->get_name();
198 JavaVMAttachArgs args;
199 args.version = JNI_VERSION_1_2;
200 args.name = thread_name.c_str();
201 args.group = nullptr;
202 if (java_vm->AttachCurrentThread(&env, &args) != 0) {
203 thread_cat.error()
204 << "Failed to attach Java VM to thread "
205 << _parent_obj->get_name() << "!\n";
206 _jni_env = nullptr;
207 return false;
208 }
209 _jni_env = env;
210 return true;
211}
212
213/**
214 * Binds the Panda thread to the current thread, assuming that the current
215 * thread is already a valid attached Java thread. Called by JNI_OnLoad.
216 */
217void ThreadPosixImpl::
218bind_java_thread() {
220 nassertv(thread != nullptr);
221
222 // Get the JNIEnv for this Java thread, and store it on the corresponding
223 // Panda thread object.
224 JNIEnv *env;
225 if (java_vm->GetEnv((void **)&env, JNI_VERSION_1_4) == JNI_OK) {
226 nassertv(thread->_impl._jni_env == nullptr || thread->_impl._jni_env == env);
227 thread->_impl._jni_env = env;
228 } else {
229 thread_cat->error()
230 << "Called bind_java_thread() on thread "
231 << *thread << ", which is not attached to Java VM!\n";
232 }
233}
234#endif // ANDROID
235
236/**
237 * The entry point of each thread.
238 */
239void *ThreadPosixImpl::
240root_func(void *data) {
241 TAU_REGISTER_THREAD();
242 {
243 // TAU_PROFILE("void ThreadPosixImpl::root_func()", " ", TAU_USER);
244
245 ThreadPosixImpl *self = (ThreadPosixImpl *)data;
246 int result = pthread_setspecific(_pt_ptr_index, self->_parent_obj);
247 nassertr(result == 0, nullptr);
248
249 {
250 self->_mutex.lock();
251 nassertd(self->_status == S_start_called) {
252 self->_mutex.unlock();
253 return nullptr;
254 }
255
256 self->_status = S_running;
257 self->_mutex.unlock();
258 }
259
260#ifdef ANDROID
261 // Attach the Java VM to allow calling Java functions in this thread.
262 self->attach_java_vm();
263#endif
264
265 self->_parent_obj->thread_main();
266
267 if (thread_cat->is_debug()) {
268 thread_cat.debug()
269 << "Terminating thread " << self->_parent_obj->get_name()
270 << ", count = " << self->_parent_obj->get_ref_count() << "\n";
271 }
272
273 {
274 self->_mutex.lock();
275 nassertd(self->_status == S_running) {
276 self->_mutex.unlock();
277 return nullptr;
278 }
279 self->_status = S_finished;
280 self->_mutex.unlock();
281 }
282
283#ifdef ANDROID
284 // We cannot let the thread end without detaching it.
285 if (self->_jni_env != nullptr) {
286 java_vm->DetachCurrentThread();
287 self->_jni_env = nullptr;
288 }
289#endif
290
291 // Now drop the parent object reference that we grabbed in start(). This
292 // might delete the parent object, and in turn, delete the ThreadPosixImpl
293 // object.
294 unref_delete(self->_parent_obj);
295 }
296
297 return nullptr;
298}
299
300/**
301 * Allocate a new index to store the Thread parent pointer as a piece of per-
302 * thread private data.
303 */
304void ThreadPosixImpl::
305init_pt_ptr_index() {
306 nassertv(!_got_pt_ptr_index);
307
308 int result = pthread_key_create(&_pt_ptr_index, nullptr);
309 if (result != 0) {
310 thread_cat->error()
311 << "Unable to associate Thread pointers with threads.\n";
312 return;
313 }
314
315 _got_pt_ptr_index = true;
316
317 // Assume that we must be in the main thread, since this method must be
318 // called before the first thread is spawned.
319 Thread *main_thread_obj = Thread::get_main_thread();
320 result = pthread_setspecific(_pt_ptr_index, main_thread_obj);
321 nassertv(result == 0);
322}
323
324#ifdef ANDROID
325/**
326 * Called by Java when loading this library from the Java virtual machine.
327 */
328jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
329 // Store the JVM pointer globally.
330 java_vm = jvm;
331
332 ThreadPosixImpl::bind_java_thread();
333 return JNI_VERSION_1_4;
334}
335#endif // ANDROID
336
337#endif // THREAD_POSIX_IMPL
A thread; that is, a lightweight process.
Definition thread.h:46
get_main_thread
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process.
Definition thread.h:107
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition thread.h:109
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void unref_delete(RefCountType *ptr)
This global helper function will unref the given ReferenceCount object, and if the reference count re...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.