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