| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- /**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file threadPosixImpl.cxx
- * @author drose
- * @date 2006-02-09
- */
- #include "threadPosixImpl.h"
- #include "selectThreadImpl.h"
- #ifdef THREAD_POSIX_IMPL
- #include "thread.h"
- #include "pointerTo.h"
- #include "config_pipeline.h"
- #include <sched.h>
- #ifdef ANDROID
- #include "config_express.h"
- #include <jni.h>
- static JavaVM *java_vm = nullptr;
- #endif
- pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0;
- bool ThreadPosixImpl::_got_pt_ptr_index = false;
- /**
- *
- */
- ThreadPosixImpl::
- ~ThreadPosixImpl() {
- if (thread_cat->is_debug()) {
- thread_cat.debug()
- << "Deleting thread " << _parent_obj->get_name() << "\n";
- }
- _mutex.lock();
- if (!_detached) {
- pthread_detach(_thread);
- _detached = true;
- }
- _mutex.unlock();
- }
- /**
- * Called for the main thread only, which has been already started, to fill in
- * the values appropriate to that thread.
- */
- void ThreadPosixImpl::
- setup_main_thread() {
- _status = S_running;
- _thread = pthread_self();
- }
- /**
- *
- */
- bool ThreadPosixImpl::
- start(ThreadPriority priority, bool joinable) {
- _mutex.lock();
- if (thread_cat->is_debug()) {
- thread_cat.debug() << "Starting " << *_parent_obj << "\n";
- }
- nassertd(_status == S_new) {
- _mutex.unlock();
- return false;
- }
- _joinable = joinable;
- _status = S_start_called;
- _detached = false;
- if (!_got_pt_ptr_index) {
- init_pt_ptr_index();
- }
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- if (!_joinable) {
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- _detached = true;
- }
- int result = pthread_attr_setstacksize(&attr, thread_stack_size);
- if (result != 0) {
- thread_cat->warning()
- << "Unable to set stack size.\n";
- }
- // Ensure the thread has "system" scope, which should ensure it can run in
- // parallel with other threads.
- result = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
- if (result != 0) {
- thread_cat->warning()
- << "Unable to set system scope.\n";
- }
- struct sched_param param;
- int current_policy = SCHED_OTHER;
- result = pthread_attr_setschedpolicy(&attr, current_policy);
- if (result != 0) {
- thread_cat->warning()
- << "Unable to set scheduling policy.\n";
- }
- result = 0;
- switch (priority) {
- case TP_low:
- param.sched_priority = sched_get_priority_min(current_policy);
- result = pthread_attr_setschedparam(&attr, ¶m);
- break;
- case TP_high:
- case TP_urgent:
- param.sched_priority = sched_get_priority_max(current_policy);
- result = pthread_attr_setschedparam(&attr, ¶m);
- break;
- case TP_normal:
- default:
- break;
- }
- if (result != 0) {
- thread_cat->warning()
- << "Unable to specify thread priority.\n";
- }
- // Increment the parent object's reference count first. The thread will
- // eventually decrement it when it terminates.
- _parent_obj->ref();
- result = pthread_create(&_thread, &attr, &root_func, (void *)this);
- pthread_attr_destroy(&attr);
- if (result != 0) {
- // Oops, we couldn't start the thread. Be sure to decrement the reference
- // count we incremented above, and return false to indicate failure.
- unref_delete(_parent_obj);
- _mutex.unlock();
- return false;
- }
- // Thread was successfully started.
- _mutex.unlock();
- return true;
- }
- /**
- * Blocks the calling process until the thread terminates. If the thread has
- * already terminated, this returns immediately.
- */
- void ThreadPosixImpl::
- join() {
- _mutex.lock();
- if (!_detached) {
- _mutex.unlock();
- void *return_val;
- pthread_join(_thread, &return_val);
- _detached = true;
- return;
- }
- _mutex.unlock();
- }
- /**
- *
- */
- std::string ThreadPosixImpl::
- get_unique_id() const {
- std::ostringstream strm;
- strm << getpid() << "." << (uintptr_t)_thread;
- return strm.str();
- }
- #ifdef ANDROID
- /**
- * Attaches the thread to the Java virtual machine. If this returns true, a
- * JNIEnv pointer can be acquired using get_jni_env().
- */
- bool ThreadPosixImpl::
- attach_java_vm() {
- JNIEnv *env;
- std::string thread_name = _parent_obj->get_name();
- JavaVMAttachArgs args;
- args.version = JNI_VERSION_1_2;
- args.name = thread_name.c_str();
- args.group = nullptr;
- if (java_vm->AttachCurrentThread(&env, &args) != 0) {
- thread_cat.error()
- << "Failed to attach Java VM to thread "
- << _parent_obj->get_name() << "!\n";
- _jni_env = nullptr;
- return false;
- }
- _jni_env = env;
- return true;
- }
- /**
- * Binds the Panda thread to the current thread, assuming that the current
- * thread is already a valid attached Java thread. Called by JNI_OnLoad.
- */
- void ThreadPosixImpl::
- bind_java_thread() {
- Thread *thread = Thread::get_current_thread();
- nassertv(thread != nullptr);
- // Get the JNIEnv for this Java thread, and store it on the corresponding
- // Panda thread object.
- JNIEnv *env;
- if (java_vm->GetEnv((void **)&env, JNI_VERSION_1_4) == JNI_OK) {
- nassertv(thread->_impl._jni_env == nullptr || thread->_impl._jni_env == env);
- thread->_impl._jni_env = env;
- } else {
- thread_cat->error()
- << "Called bind_java_thread() on thread "
- << *thread << ", which is not attached to Java VM!\n";
- }
- }
- #endif // ANDROID
- /**
- * The entry point of each thread.
- */
- void *ThreadPosixImpl::
- root_func(void *data) {
- TAU_REGISTER_THREAD();
- {
- // TAU_PROFILE("void ThreadPosixImpl::root_func()", " ", TAU_USER);
- ThreadPosixImpl *self = (ThreadPosixImpl *)data;
- int result = pthread_setspecific(_pt_ptr_index, self->_parent_obj);
- nassertr(result == 0, nullptr);
- {
- self->_mutex.lock();
- nassertd(self->_status == S_start_called) {
- self->_mutex.unlock();
- return nullptr;
- }
- self->_status = S_running;
- self->_mutex.unlock();
- }
- #ifdef ANDROID
- // Attach the Java VM to allow calling Java functions in this thread.
- self->attach_java_vm();
- #endif
- self->_parent_obj->thread_main();
- if (thread_cat->is_debug()) {
- thread_cat.debug()
- << "Terminating thread " << self->_parent_obj->get_name()
- << ", count = " << self->_parent_obj->get_ref_count() << "\n";
- }
- {
- self->_mutex.lock();
- nassertd(self->_status == S_running) {
- self->_mutex.unlock();
- return nullptr;
- }
- self->_status = S_finished;
- self->_mutex.unlock();
- }
- #ifdef ANDROID
- // We cannot let the thread end without detaching it.
- if (self->_jni_env != nullptr) {
- java_vm->DetachCurrentThread();
- self->_jni_env = nullptr;
- }
- #endif
- // Now drop the parent object reference that we grabbed in start(). This
- // might delete the parent object, and in turn, delete the ThreadPosixImpl
- // object.
- unref_delete(self->_parent_obj);
- }
- return nullptr;
- }
- /**
- * Allocate a new index to store the Thread parent pointer as a piece of per-
- * thread private data.
- */
- void ThreadPosixImpl::
- init_pt_ptr_index() {
- nassertv(!_got_pt_ptr_index);
- int result = pthread_key_create(&_pt_ptr_index, nullptr);
- if (result != 0) {
- thread_cat->error()
- << "Unable to associate Thread pointers with threads.\n";
- return;
- }
- _got_pt_ptr_index = true;
- // Assume that we must be in the main thread, since this method must be
- // called before the first thread is spawned.
- Thread *main_thread_obj = Thread::get_main_thread();
- result = pthread_setspecific(_pt_ptr_index, main_thread_obj);
- nassertv(result == 0);
- }
- #ifdef ANDROID
- /**
- * Called by Java when loading this library from the Java virtual machine.
- */
- jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
- // Store the JVM pointer globally.
- java_vm = jvm;
- ThreadPosixImpl::bind_java_thread();
- return JNI_VERSION_1_4;
- }
- #endif // ANDROID
- #endif // THREAD_POSIX_IMPL
|