threadPosixImpl.cxx 7.8 KB

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