Panda3D
threadPosixImpl.cxx
1 // Filename: threadPosixImpl.cxx
2 // Created by: drose (09Feb06)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "threadPosixImpl.h"
16 #include "selectThreadImpl.h"
17 
18 #ifdef THREAD_POSIX_IMPL
19 
20 #include "thread.h"
21 #include "pointerTo.h"
22 #include "config_pipeline.h"
23 #include <sched.h>
24 
25 #ifdef ANDROID
26 #include "config_express.h"
27 #include <jni.h>
28 #endif
29 
30 pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0;
31 bool ThreadPosixImpl::_got_pt_ptr_index = false;
32 
33 ////////////////////////////////////////////////////////////////////
34 // Function: ThreadPosixImpl::Destructor
35 // Access: Public
36 // Description:
37 ////////////////////////////////////////////////////////////////////
38 ThreadPosixImpl::
39 ~ThreadPosixImpl() {
40  if (thread_cat->is_debug()) {
41  thread_cat.debug()
42  << "Deleting thread " << _parent_obj->get_name() << "\n";
43  }
44 
45  _mutex.acquire();
46 
47  if (!_detached) {
48  pthread_detach(_thread);
49  _detached = true;
50  }
51 
52  _mutex.release();
53 }
54 
55 ////////////////////////////////////////////////////////////////////
56 // Function: ThreadPosixImpl::setup_main_thread
57 // Access: Public
58 // Description: Called for the main thread only, which has been
59 // already started, to fill in the values appropriate to
60 // that thread.
61 ////////////////////////////////////////////////////////////////////
62 void ThreadPosixImpl::
63 setup_main_thread() {
64  _status = S_running;
65 }
66 
67 ////////////////////////////////////////////////////////////////////
68 // Function: ThreadPosixImpl::start
69 // Access: Public
70 // Description:
71 ////////////////////////////////////////////////////////////////////
72 bool ThreadPosixImpl::
73 start(ThreadPriority priority, bool joinable) {
74  _mutex.acquire();
75  if (thread_cat->is_debug()) {
76  thread_cat.debug() << "Starting " << *_parent_obj << "\n";
77  }
78 
79  nassertd(_status == S_new) {
80  _mutex.release();
81  return false;
82  }
83 
84  _joinable = joinable;
85  _status = S_start_called;
86  _detached = false;
87 
88  if (!_got_pt_ptr_index) {
89  init_pt_ptr_index();
90  }
91 
92  pthread_attr_t attr;
93  pthread_attr_init(&attr);
94 
95  if (!_joinable) {
96  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
97  _detached = true;
98  }
99 
100  int result = pthread_attr_setstacksize(&attr, thread_stack_size);
101  if (result != 0) {
102  thread_cat->warning()
103  << "Unable to set stack size.\n";
104  }
105 
106  // Ensure the thread has "system" scope, which should ensure it can
107  // run in parallel with other threads.
108  result = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
109  if (result != 0) {
110  thread_cat->warning()
111  << "Unable to set system scope.\n";
112  }
113 
114  struct sched_param param;
115  int current_policy = SCHED_OTHER;
116  result = pthread_attr_setschedpolicy(&attr, current_policy);
117  if (result != 0) {
118  thread_cat->warning()
119  << "Unable to set scheduling policy.\n";
120 
121  }
122 
123  result = 0;
124  switch (priority) {
125  case TP_low:
126  param.sched_priority = sched_get_priority_min(current_policy);
127  result = pthread_attr_setschedparam(&attr, &param);
128  break;
129 
130  case TP_high:
131  case TP_urgent:
132  param.sched_priority = sched_get_priority_max(current_policy);
133  result = pthread_attr_setschedparam(&attr, &param);
134  break;
135 
136  case TP_normal:
137  default:
138  break;
139  }
140 
141  if (result != 0) {
142  thread_cat->warning()
143  << "Unable to specify thread priority.\n";
144  }
145 
146  // Increment the parent object's reference count first. The thread
147  // will eventually decrement it when it terminates.
148  _parent_obj->ref();
149  result = pthread_create(&_thread, &attr, &root_func, (void *)this);
150 
151  pthread_attr_destroy(&attr);
152 
153  if (result != 0) {
154  // Oops, we couldn't start the thread. Be sure to decrement the
155  // reference count we incremented above, and return false to
156  // indicate failure.
157  unref_delete(_parent_obj);
158  _mutex.release();
159  return false;
160  }
161 
162  // Thread was successfully started.
163  _mutex.release();
164  return true;
165 }
166 
167 ////////////////////////////////////////////////////////////////////
168 // Function: ThreadPosixImpl::join
169 // Access: Public
170 // Description: Blocks the calling process until the thread
171 // terminates. If the thread has already terminated,
172 // this returns immediately.
173 ////////////////////////////////////////////////////////////////////
174 void ThreadPosixImpl::
175 join() {
176  _mutex.acquire();
177  if (!_detached) {
178  _mutex.release();
179  void *return_val;
180  pthread_join(_thread, &return_val);
181  _detached = true;
182  return;
183  }
184  _mutex.release();
185 }
186 
187 ////////////////////////////////////////////////////////////////////
188 // Function: ThreadPosixImpl::get_unique_id
189 // Access: Public
190 // Description:
191 ////////////////////////////////////////////////////////////////////
192 string ThreadPosixImpl::
193 get_unique_id() const {
194  ostringstream strm;
195  strm << getpid() << "." << _thread;
196 
197  return strm.str();
198 }
199 
200 ////////////////////////////////////////////////////////////////////
201 // Function: ThreadPosixImpl::root_func
202 // Access: Private, Static
203 // Description: The entry point of each thread.
204 ////////////////////////////////////////////////////////////////////
205 void *ThreadPosixImpl::
206 root_func(void *data) {
207  TAU_REGISTER_THREAD();
208  {
209  //TAU_PROFILE("void ThreadPosixImpl::root_func()", " ", TAU_USER);
210 
211  ThreadPosixImpl *self = (ThreadPosixImpl *)data;
212  int result = pthread_setspecific(_pt_ptr_index, self->_parent_obj);
213  nassertr(result == 0, NULL);
214 
215  {
216  self->_mutex.acquire();
217  nassertd(self->_status == S_start_called) {
218  self->_mutex.release();
219  return NULL;
220  }
221 
222  self->_status = S_running;
223  self->_mutex.release();
224  }
225 
226 #ifdef ANDROID
227  // Attach the Java VM to allow calling Java functions in this thread.
228  JavaVM *jvm = get_java_vm();
229  JNIEnv *env;
230  if (jvm == NULL || jvm->AttachCurrentThread(&env, NULL) != 0) {
231  thread_cat.error()
232  << "Failed to attach Java VM to thread "
233  << self->_parent_obj->get_name() << "!\n";
234  env = NULL;
235  }
236 #endif
237 
238  self->_parent_obj->thread_main();
239 
240  if (thread_cat->is_debug()) {
241  thread_cat.debug()
242  << "Terminating thread " << self->_parent_obj->get_name()
243  << ", count = " << self->_parent_obj->get_ref_count() << "\n";
244  }
245 
246  {
247  self->_mutex.acquire();
248  nassertd(self->_status == S_running) {
249  self->_mutex.release();
250  return NULL;
251  }
252  self->_status = S_finished;
253  self->_mutex.release();
254  }
255 
256 #ifdef ANDROID
257  if (env != NULL) {
258  jvm->DetachCurrentThread();
259  }
260 #endif
261 
262  // Now drop the parent object reference that we grabbed in start().
263  // This might delete the parent object, and in turn, delete the
264  // ThreadPosixImpl object.
265  unref_delete(self->_parent_obj);
266  }
267 
268  return NULL;
269 }
270 
271 ////////////////////////////////////////////////////////////////////
272 // Function: ThreadPosixImpl::init_pt_ptr_index
273 // Access: Private, Static
274 // Description: Allocate a new index to store the Thread parent
275 // pointer as a piece of per-thread private data.
276 ////////////////////////////////////////////////////////////////////
277 void ThreadPosixImpl::
278 init_pt_ptr_index() {
279  nassertv(!_got_pt_ptr_index);
280 
281  int result = pthread_key_create(&_pt_ptr_index, NULL);
282  if (result != 0) {
283  thread_cat->error()
284  << "Unable to associate Thread pointers with threads.\n";
285  return;
286  }
287 
288  _got_pt_ptr_index = true;
289 
290  // Assume that we must be in the main thread, since this method must
291  // be called before the first thread is spawned.
292  Thread *main_thread_obj = Thread::get_main_thread();
293  result = pthread_setspecific(_pt_ptr_index, main_thread_obj);
294  nassertv(result == 0);
295 }
296 
297 #endif // THREAD_POSIX_IMPL
static Thread * get_main_thread()
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process...
Definition: thread.I:107
A thread; that is, a lightweight process.
Definition: thread.h:51