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
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.