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  _thread = pthread_self();
62 }
63 
64 /**
65  *
66  */
67 bool ThreadPosixImpl::
68 start(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  */
165 void ThreadPosixImpl::
166 join() {
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  */
181 std::string ThreadPosixImpl::
182 get_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  */
194 bool ThreadPosixImpl::
195 attach_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  */
217 void ThreadPosixImpl::
218 bind_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  */
239 void *ThreadPosixImpl::
240 root_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  */
304 void ThreadPosixImpl::
305 init_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  */
328 jint 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
Thread::get_main_thread
get_main_thread
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process.
Definition: thread.h:107
unref_delete
void unref_delete(RefCountType *ptr)
This global helper function will unref the given ReferenceCount object, and if the reference count re...
Definition: referenceCount.I:344
Thread::get_current_thread
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
threadPosixImpl.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
selectThreadImpl.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_pipeline.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_express.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
pointerTo.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
thread.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.