Bullet Collision Detection & Physics Library
btThreadSupportPosix.cpp
Go to the documentation of this file.
1 /*
2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2018 Erwin Coumans http://bulletphysics.com
4 
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
10 
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
14 */
15 
16 
17 #if BT_THREADSAFE && !defined( _WIN32 )
18 
19 
20 #include "LinearMath/btScalar.h"
22 #include "LinearMath/btThreads.h"
23 #include "LinearMath/btMinMax.h"
25 
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 
30 
31 #ifndef _XOPEN_SOURCE
32 #define _XOPEN_SOURCE 600 //for definition of pthread_barrier_t, see http://pages.cs.wisc.edu/~travitch/pthreads_primer.html
33 #endif //_XOPEN_SOURCE
34 #include <pthread.h>
35 #include <semaphore.h>
36 #include <unistd.h> //for sysconf
37 
38 
45 #if __cplusplus >= 201103L
46 
47 #include <thread>
48 
49 int btGetNumHardwareThreads()
50 {
51  return btMin<int>(BT_MAX_THREAD_COUNT, std::thread::hardware_concurrency());
52 }
53 
54 #else
55 
56 int btGetNumHardwareThreads()
57 {
58  return btMin<int>(BT_MAX_THREAD_COUNT, sysconf( _SC_NPROCESSORS_ONLN ));
59 }
60 
61 #endif
62 
63 
64 // btThreadSupportPosix helps to initialize/shutdown libspe2, start/stop SPU tasks and communication
65 class btThreadSupportPosix : public btThreadSupportInterface
66 {
67 public:
68  struct btThreadStatus
69  {
70  int m_taskId;
71  int m_commandId;
72  int m_status;
73 
74  ThreadFunc m_userThreadFunc;
75  void* m_userPtr; //for taskDesc etc
76 
77  pthread_t thread;
78  //each tread will wait until this signal to start its work
79  sem_t* startSemaphore;
80 
81  // this is a copy of m_mainSemaphore,
82  //each tread will signal once it is finished with its work
83  sem_t* m_mainSemaphore;
84  unsigned long threadUsed;
85  };
86 private:
87  typedef unsigned long long UINT64;
88 
89  btAlignedObjectArray<btThreadStatus> m_activeThreadStatus;
90  // m_mainSemaphoresemaphore will signal, if and how many threads are finished with their work
91  sem_t* m_mainSemaphore;
92  int m_numThreads;
93  UINT64 m_startedThreadsMask;
94  void startThreads( const ConstructionInfo& threadInfo );
95  void stopThreads();
96  int waitForResponse();
97 
98 public:
99  btThreadSupportPosix( const ConstructionInfo& threadConstructionInfo );
100  virtual ~btThreadSupportPosix();
101 
102  virtual int getNumWorkerThreads() const BT_OVERRIDE { return m_numThreads; }
103  // TODO: return the number of logical processors sharing the first L3 cache
104  virtual int getCacheFriendlyNumThreads() const BT_OVERRIDE { return m_numThreads + 1; }
105  // TODO: detect if CPU has hyperthreading enabled
106  virtual int getLogicalToPhysicalCoreRatio() const BT_OVERRIDE { return 1; }
107 
108  virtual void runTask( int threadIndex, void* userData ) BT_OVERRIDE;
109  virtual void waitForAllTasks() BT_OVERRIDE;
110 
111  virtual btCriticalSection* createCriticalSection() BT_OVERRIDE;
112  virtual void deleteCriticalSection( btCriticalSection* criticalSection ) BT_OVERRIDE;
113 };
114 
115 
116 #define checkPThreadFunction(returnValue) \
117  if(0 != returnValue) { \
118  printf("PThread problem at line %i in file %s: %i %d\n", __LINE__, __FILE__, returnValue, errno); \
119  }
120 
121 // The number of threads should be equal to the number of available cores
122 // Todo: each worker should be linked to a single core, using SetThreadIdealProcessor.
123 
124 
125 btThreadSupportPosix::btThreadSupportPosix( const ConstructionInfo& threadConstructionInfo )
126 {
127  startThreads( threadConstructionInfo );
128 }
129 
130 // cleanup/shutdown Libspe2
131 btThreadSupportPosix::~btThreadSupportPosix()
132 {
133  stopThreads();
134 }
135 
136 #if (defined (__APPLE__))
137 #define NAMED_SEMAPHORES
138 #endif
139 
140 
141 static sem_t* createSem( const char* baseName )
142 {
143  static int semCount = 0;
144 #ifdef NAMED_SEMAPHORES
145  char name[ 32 ];
147  snprintf( name, 32, "/%8.s-%4.d-%4.4d", baseName, getpid(), semCount++ );
148  sem_t* tempSem = sem_open( name, O_CREAT, 0600, 0 );
149 
150  if ( tempSem != reinterpret_cast<sem_t *>( SEM_FAILED ) )
151  {
152  // printf("Created \"%s\" Semaphore %p\n", name, tempSem);
153  }
154  else
155  {
156  //printf("Error creating Semaphore %d\n", errno);
157  exit( -1 );
158  }
160 #else
161  sem_t* tempSem = new sem_t;
162  checkPThreadFunction( sem_init( tempSem, 0, 0 ) );
163 #endif
164  return tempSem;
165 }
166 
167 static void destroySem( sem_t* semaphore )
168 {
169 #ifdef NAMED_SEMAPHORES
170  checkPThreadFunction( sem_close( semaphore ) );
171 #else
172  checkPThreadFunction( sem_destroy( semaphore ) );
173  delete semaphore;
174 #endif
175 }
176 
177 static void *threadFunction( void *argument )
178 {
179  btThreadSupportPosix::btThreadStatus* status = ( btThreadSupportPosix::btThreadStatus* )argument;
180 
181  while ( 1 )
182  {
183  checkPThreadFunction( sem_wait( status->startSemaphore ) );
184  void* userPtr = status->m_userPtr;
185 
186  if ( userPtr )
187  {
188  btAssert( status->m_status );
189  status->m_userThreadFunc( userPtr );
190  status->m_status = 2;
191  checkPThreadFunction( sem_post( status->m_mainSemaphore ) );
192  status->threadUsed++;
193  }
194  else
195  {
196  //exit Thread
197  status->m_status = 3;
198  checkPThreadFunction( sem_post( status->m_mainSemaphore ) );
199  printf( "Thread with taskId %i exiting\n", status->m_taskId );
200  break;
201  }
202  }
203 
204  printf( "Thread TERMINATED\n" );
205  return 0;
206 }
207 
209 void btThreadSupportPosix::runTask( int threadIndex, void* userData )
210 {
212  btThreadStatus& threadStatus = m_activeThreadStatus[ threadIndex ];
213  btAssert( threadIndex >= 0 );
214  btAssert( threadIndex < m_activeThreadStatus.size() );
215 
216  threadStatus.m_commandId = 1;
217  threadStatus.m_status = 1;
218  threadStatus.m_userPtr = userData;
219  m_startedThreadsMask |= UINT64( 1 ) << threadIndex;
220 
221  // fire event to start new task
222  checkPThreadFunction( sem_post( threadStatus.startSemaphore ) );
223 }
224 
225 
227 int btThreadSupportPosix::waitForResponse()
228 {
231 
232  btAssert( m_activeThreadStatus.size() );
233 
234  // wait for any of the threads to finish
235  checkPThreadFunction( sem_wait( m_mainSemaphore ) );
236  // get at least one thread which has finished
237  size_t last = -1;
238 
239  for ( size_t t = 0; t < size_t( m_activeThreadStatus.size() ); ++t )
240  {
241  if ( 2 == m_activeThreadStatus[ t ].m_status )
242  {
243  last = t;
244  break;
245  }
246  }
247 
248  btThreadStatus& threadStatus = m_activeThreadStatus[ last ];
249 
250  btAssert( threadStatus.m_status > 1 );
251  threadStatus.m_status = 0;
252 
253  // need to find an active spu
254  btAssert( last >= 0 );
255  m_startedThreadsMask &= ~( UINT64( 1 ) << last );
256 
257  return last;
258 }
259 
260 
261 void btThreadSupportPosix::waitForAllTasks()
262 {
263  while ( m_startedThreadsMask )
264  {
265  waitForResponse();
266  }
267 }
268 
269 
270 void btThreadSupportPosix::startThreads( const ConstructionInfo& threadConstructionInfo )
271 {
272  m_numThreads = btGetNumHardwareThreads() - 1; // main thread exists already
273  printf( "%s creating %i threads.\n", __FUNCTION__, m_numThreads );
274  m_activeThreadStatus.resize( m_numThreads );
275  m_startedThreadsMask = 0;
276 
277  m_mainSemaphore = createSem( "main" );
278  //checkPThreadFunction(sem_wait(mainSemaphore));
279 
280  for ( int i = 0; i < m_numThreads; i++ )
281  {
282  printf( "starting thread %d\n", i );
283  btThreadStatus& threadStatus = m_activeThreadStatus[ i ];
284  threadStatus.startSemaphore = createSem( "threadLocal" );
285  checkPThreadFunction( pthread_create( &threadStatus.thread, NULL, &threadFunction, (void*) &threadStatus ) );
286 
287  threadStatus.m_userPtr = 0;
288  threadStatus.m_taskId = i;
289  threadStatus.m_commandId = 0;
290  threadStatus.m_status = 0;
291  threadStatus.m_mainSemaphore = m_mainSemaphore;
292  threadStatus.m_userThreadFunc = threadConstructionInfo.m_userThreadFunc;
293  threadStatus.threadUsed = 0;
294 
295  printf( "started thread %d \n", i );
296  }
297 }
298 
300 void btThreadSupportPosix::stopThreads()
301 {
302  for ( size_t t = 0; t < size_t( m_activeThreadStatus.size() ); ++t )
303  {
304  btThreadStatus& threadStatus = m_activeThreadStatus[ t ];
305  printf( "%s: Thread %i used: %ld\n", __FUNCTION__, int( t ), threadStatus.threadUsed );
306 
307  threadStatus.m_userPtr = 0;
308  checkPThreadFunction( sem_post( threadStatus.startSemaphore ) );
309  checkPThreadFunction( sem_wait( m_mainSemaphore ) );
310 
311  printf( "destroy semaphore\n" );
312  destroySem( threadStatus.startSemaphore );
313  printf( "semaphore destroyed\n" );
314  checkPThreadFunction( pthread_join( threadStatus.thread, 0 ) );
315 
316  }
317  printf( "destroy main semaphore\n" );
318  destroySem( m_mainSemaphore );
319  printf( "main semaphore destroyed\n" );
320  m_activeThreadStatus.clear();
321 }
322 
323 class btCriticalSectionPosix : public btCriticalSection
324 {
325  pthread_mutex_t m_mutex;
326 
327 public:
328  btCriticalSectionPosix()
329  {
330  pthread_mutex_init( &m_mutex, NULL );
331  }
332  virtual ~btCriticalSectionPosix()
333  {
334  pthread_mutex_destroy( &m_mutex );
335  }
336 
337  virtual void lock()
338  {
339  pthread_mutex_lock( &m_mutex );
340  }
341  virtual void unlock()
342  {
343  pthread_mutex_unlock( &m_mutex );
344  }
345 };
346 
347 
348 btCriticalSection* btThreadSupportPosix::createCriticalSection()
349 {
350  return new btCriticalSectionPosix();
351 }
352 
353 void btThreadSupportPosix::deleteCriticalSection( btCriticalSection* cs )
354 {
355  delete cs;
356 }
357 
358 
359 btThreadSupportInterface* btThreadSupportInterface::create( const ConstructionInfo& info )
360 {
361  return new btThreadSupportPosix( info );
362 }
363 
364 #endif // BT_THREADSAFE && !defined( _WIN32 )
365 
The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods It...
static btThreadSupportInterface * create(const ConstructionInfo &info)
#define btAssert(x)
Definition: btScalar.h:131
const unsigned int BT_MAX_THREAD_COUNT
Definition: btThreads.h:33
void clear()
clear the array, deallocated memory. Generally it is better to use array.resize(0), to reduce performance overhead of run-time memory (de)allocations.
int size() const
return the number of elements in the array
#define BT_OVERRIDE
Definition: btThreads.h:28
void resize(int newsize, const T &fillData=T())