Blender  V3.3
btThreads.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2003-2014 Erwin Coumans http://bullet.googlecode.com
3 
4 This software is provided 'as-is', without any express or implied warranty.
5 In no event will the authors be held liable for any damages arising from the use of this software.
6 Permission is granted to anyone to use this software for any purpose,
7 including commercial applications, and to alter it and redistribute it freely,
8 subject to the following restrictions:
9 
10 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.
11 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
12 3. This notice may not be removed or altered from any source distribution.
13 */
14 
15 #include "btThreads.h"
16 #include "btQuickprof.h"
17 #include <algorithm> // for min and max
18 
19 #if BT_USE_OPENMP && BT_THREADSAFE
20 
21 #include <omp.h>
22 
23 #endif // #if BT_USE_OPENMP && BT_THREADSAFE
24 
25 #if BT_USE_PPL && BT_THREADSAFE
26 
27 // use Microsoft Parallel Patterns Library (installed with Visual Studio 2010 and later)
28 #include <ppl.h> // if you get a compile error here, check whether your version of Visual Studio includes PPL
29 // Visual Studio 2010 and later should come with it
30 #include <concrtrm.h> // for GetProcessorCount()
31 
32 #endif // #if BT_USE_PPL && BT_THREADSAFE
33 
34 #if BT_USE_TBB && BT_THREADSAFE
35 
36 // use Intel Threading Building Blocks for thread management
37 #define __TBB_NO_IMPLICIT_LINKAGE 1
38 #include <tbb/tbb.h>
39 #include <tbb/task_scheduler_init.h>
40 #include <tbb/parallel_for.h>
41 #include <tbb/blocked_range.h>
42 
43 #endif // #if BT_USE_TBB && BT_THREADSAFE
44 
45 #if BT_THREADSAFE
46 //
47 // Lightweight spin-mutex based on atomics
48 // Using ordinary system-provided mutexes like Windows critical sections was noticeably slower
49 // presumably because when it fails to lock at first it would sleep the thread and trigger costly
50 // context switching.
51 //
52 
53 #if __cplusplus >= 201103L
54 
55 // for anything claiming full C++11 compliance, use C++11 atomics
56 // on GCC or Clang you need to compile with -std=c++11
57 #define USE_CPP11_ATOMICS 1
58 
59 #elif defined(_MSC_VER)
60 
61 // on MSVC, use intrinsics instead
62 #define USE_MSVC_INTRINSICS 1
63 
64 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
65 
66 // available since GCC 4.7 and some versions of clang
67 // todo: check for clang
68 #define USE_GCC_BUILTIN_ATOMICS 1
69 
70 #elif defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
71 
72 // available since GCC 4.1
73 #define USE_GCC_BUILTIN_ATOMICS_OLD 1
74 
75 #endif
76 
77 #if USE_CPP11_ATOMICS
78 
79 #include <atomic>
80 #include <thread>
81 
82 #define THREAD_LOCAL_STATIC thread_local static
83 
85 {
86  std::atomic<int>* aDest = reinterpret_cast<std::atomic<int>*>(&mLock);
87  int expected = 0;
88  return std::atomic_compare_exchange_weak_explicit(aDest, &expected, int(1), std::memory_order_acq_rel, std::memory_order_acquire);
89 }
90 
91 void btSpinMutex::lock()
92 {
93  // note: this lock does not sleep the thread.
94  while (!tryLock())
95  {
96  // spin
97  }
98 }
99 
100 void btSpinMutex::unlock()
101 {
102  std::atomic<int>* aDest = reinterpret_cast<std::atomic<int>*>(&mLock);
103  std::atomic_store_explicit(aDest, int(0), std::memory_order_release);
104 }
105 
106 #elif USE_MSVC_INTRINSICS
107 
108 #define WIN32_LEAN_AND_MEAN
109 
110 #include <windows.h>
111 #include <intrin.h>
112 
113 #define THREAD_LOCAL_STATIC __declspec(thread) static
114 
116 {
117  volatile long* aDest = reinterpret_cast<long*>(&mLock);
118  return (0 == _InterlockedCompareExchange(aDest, 1, 0));
119 }
120 
121 void btSpinMutex::lock()
122 {
123  // note: this lock does not sleep the thread
124  while (!tryLock())
125  {
126  // spin
127  }
128 }
129 
130 void btSpinMutex::unlock()
131 {
132  volatile long* aDest = reinterpret_cast<long*>(&mLock);
133  _InterlockedExchange(aDest, 0);
134 }
135 
136 #elif USE_GCC_BUILTIN_ATOMICS
137 
138 #define THREAD_LOCAL_STATIC static __thread
139 
141 {
142  int expected = 0;
143  bool weak = false;
144  const int memOrderSuccess = __ATOMIC_ACQ_REL;
145  const int memOrderFail = __ATOMIC_ACQUIRE;
146  return __atomic_compare_exchange_n(&mLock, &expected, int(1), weak, memOrderSuccess, memOrderFail);
147 }
148 
149 void btSpinMutex::lock()
150 {
151  // note: this lock does not sleep the thread
152  while (!tryLock())
153  {
154  // spin
155  }
156 }
157 
158 void btSpinMutex::unlock()
159 {
160  __atomic_store_n(&mLock, int(0), __ATOMIC_RELEASE);
161 }
162 
163 #elif USE_GCC_BUILTIN_ATOMICS_OLD
164 
165 #define THREAD_LOCAL_STATIC static __thread
166 
168 {
169  return __sync_bool_compare_and_swap(&mLock, int(0), int(1));
170 }
171 
172 void btSpinMutex::lock()
173 {
174  // note: this lock does not sleep the thread
175  while (!tryLock())
176  {
177  // spin
178  }
179 }
180 
181 void btSpinMutex::unlock()
182 {
183  // write 0
184  __sync_fetch_and_and(&mLock, int(0));
185 }
186 
187 #else //#elif USE_MSVC_INTRINSICS
188 
189 #error "no threading primitives defined -- unknown platform"
190 
191 #endif //#else //#elif USE_MSVC_INTRINSICS
192 
193 #else //#if BT_THREADSAFE
194 
195 // These should not be called ever
197 {
198  btAssert(!"unimplemented btSpinMutex::lock() called");
199 }
200 
202 {
203  btAssert(!"unimplemented btSpinMutex::unlock() called");
204 }
205 
207 {
208  btAssert(!"unimplemented btSpinMutex::tryLock() called");
209  return true;
210 }
211 
212 #define THREAD_LOCAL_STATIC static
213 
214 #endif // #else //#if BT_THREADSAFE
215 
217 {
218  unsigned int mCounter;
220 
222  {
223  mCounter = 0;
224  --mCounter; // first count should come back 0
225  }
226 
227  unsigned int getNext()
228  {
229  // no need to optimize this with atomics, it is only called ONCE per thread!
230  mMutex.lock();
231  mCounter++;
233  {
234  btAssert(!"thread counter exceeded");
235  // wrap back to the first worker index
236  mCounter = 1;
237  }
238  unsigned int val = mCounter;
239  mMutex.unlock();
240  return val;
241  }
242 };
243 
245 static int gThreadsRunningCounter = 0; // useful for detecting if we are trying to do nested parallel-for calls
248 
249 //
250 // BT_DETECT_BAD_THREAD_INDEX tries to detect when there are multiple threads assigned the same thread index.
251 //
252 // BT_DETECT_BAD_THREAD_INDEX is a developer option to test if
253 // certain assumptions about how the task scheduler manages its threads
254 // holds true.
255 // The main assumption is:
256 // - when the threadpool is resized, the task scheduler either
257 // 1. destroys all worker threads and creates all new ones in the correct number, OR
258 // 2. never destroys a worker thread
259 //
260 // We make that assumption because we can't easily enumerate the worker threads of a task scheduler
261 // to assign nice sequential thread-indexes. We also do not get notified if a worker thread is destroyed,
262 // so we can't tell when a thread-index is no longer being used.
263 // We allocate thread-indexes as needed with a sequential global thread counter.
264 //
265 // Our simple thread-counting scheme falls apart if the task scheduler destroys some threads but
266 // continues to re-use other threads and the application repeatedly resizes the thread pool of the
267 // task scheduler.
268 // In order to prevent the thread-counter from exceeding the global max (BT_MAX_THREAD_COUNT), we
269 // wrap the thread counter back to 1. This should only happen if the worker threads have all been
270 // destroyed and re-created.
271 //
272 // BT_DETECT_BAD_THREAD_INDEX only works for Win32 right now,
273 // but could be adapted to work with pthreads
274 #define BT_DETECT_BAD_THREAD_INDEX 0
275 
276 #if BT_DETECT_BAD_THREAD_INDEX
277 
278 typedef DWORD ThreadId_t;
279 const static ThreadId_t kInvalidThreadId = 0;
280 ThreadId_t gDebugThreadIds[BT_MAX_THREAD_COUNT];
281 
282 static ThreadId_t getDebugThreadId()
283 {
284  return GetCurrentThreadId();
285 }
286 
287 #endif // #if BT_DETECT_BAD_THREAD_INDEX
288 
289 // return a unique index per thread, main thread is 0, worker threads are in [1, BT_MAX_THREAD_COUNT)
291 {
292  const unsigned int kNullIndex = ~0U;
293  THREAD_LOCAL_STATIC unsigned int sThreadIndex = kNullIndex;
294  if (sThreadIndex == kNullIndex)
295  {
296  sThreadIndex = gThreadCounter.getNext();
297  btAssert(sThreadIndex < BT_MAX_THREAD_COUNT);
298  }
299 #if BT_DETECT_BAD_THREAD_INDEX
300  if (gBtTaskScheduler && sThreadIndex > 0)
301  {
302  ThreadId_t tid = getDebugThreadId();
303  // if not set
304  if (gDebugThreadIds[sThreadIndex] == kInvalidThreadId)
305  {
306  // set it
307  gDebugThreadIds[sThreadIndex] = tid;
308  }
309  else
310  {
311  if (gDebugThreadIds[sThreadIndex] != tid)
312  {
313  // this could indicate the task scheduler is breaking our assumptions about
314  // how threads are managed when threadpool is resized
315  btAssert(!"there are 2 or more threads with the same thread-index!");
316  __debugbreak();
317  }
318  }
319  }
320 #endif // #if BT_DETECT_BAD_THREAD_INDEX
321  return sThreadIndex;
322 }
323 
325 {
326  return btGetCurrentThreadIndex() == 0;
327 }
328 
330 {
331  // for when all current worker threads are destroyed
334 }
335 
337 {
338  m_name = name;
340  m_isActive = false;
341 }
342 
344 {
345  // gThreadCounter is used to assign a thread-index to each worker thread in a task scheduler.
346  // The main thread is always thread-index 0, and worker threads are numbered from 1 to 63 (BT_MAX_THREAD_COUNT-1)
347  // The thread-indexes need to be unique amongst the threads that can be running simultaneously.
348  // Since only one task scheduler can be used at a time, it is OK for a pair of threads that belong to different
349  // task schedulers to share the same thread index because they can't be running at the same time.
350  // So each task scheduler needs to keep its own thread counter value
351  if (!m_isActive)
352  {
353  gThreadCounter.mCounter = m_savedThreadCounter; // restore saved thread counter
354  m_isActive = true;
355  }
356 }
357 
359 {
360  if (m_isActive)
361  {
362  m_savedThreadCounter = gThreadCounter.mCounter; // save thread counter
363  m_isActive = false;
364  }
365 }
366 
368 {
372 }
373 
375 {
379 }
380 
382 {
383  return gThreadsRunningCounter != 0;
384 }
385 
387 {
388  int threadId = btGetCurrentThreadIndex(); // make sure we call this on main thread at least once before any workers run
389  if (threadId != 0)
390  {
391  btAssert(!"btSetTaskScheduler must be called from the main thread!");
392  return;
393  }
394  if (gBtTaskScheduler)
395  {
396  // deactivate old task scheduler
398  }
399  gBtTaskScheduler = ts;
400  if (ts)
401  {
402  // activate new task scheduler
403  ts->activate();
404  }
405 }
406 
408 {
409  return gBtTaskScheduler;
410 }
411 
412 void btParallelFor(int iBegin, int iEnd, int grainSize, const btIParallelForBody& body)
413 {
414 #if BT_THREADSAFE
415 
416 #if BT_DETECT_BAD_THREAD_INDEX
417  if (!btThreadsAreRunning())
418  {
419  // clear out thread ids
420  for (int i = 0; i < BT_MAX_THREAD_COUNT; ++i)
421  {
422  gDebugThreadIds[i] = kInvalidThreadId;
423  }
424  }
425 #endif // #if BT_DETECT_BAD_THREAD_INDEX
426 
427  btAssert(gBtTaskScheduler != NULL); // call btSetTaskScheduler() with a valid task scheduler first!
428  gBtTaskScheduler->parallelFor(iBegin, iEnd, grainSize, body);
429 
430 #else // #if BT_THREADSAFE
431 
432  // non-parallel version of btParallelFor
433  btAssert(!"called btParallelFor in non-threadsafe build. enable BT_THREADSAFE");
434  body.forLoop(iBegin, iEnd);
435 
436 #endif // #if BT_THREADSAFE
437 }
438 
439 btScalar btParallelSum(int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body)
440 {
441 #if BT_THREADSAFE
442 
443 #if BT_DETECT_BAD_THREAD_INDEX
444  if (!btThreadsAreRunning())
445  {
446  // clear out thread ids
447  for (int i = 0; i < BT_MAX_THREAD_COUNT; ++i)
448  {
449  gDebugThreadIds[i] = kInvalidThreadId;
450  }
451  }
452 #endif // #if BT_DETECT_BAD_THREAD_INDEX
453 
454  btAssert(gBtTaskScheduler != NULL); // call btSetTaskScheduler() with a valid task scheduler first!
455  return gBtTaskScheduler->parallelSum(iBegin, iEnd, grainSize, body);
456 
457 #else // #if BT_THREADSAFE
458 
459  // non-parallel version of btParallelSum
460  btAssert(!"called btParallelFor in non-threadsafe build. enable BT_THREADSAFE");
461  return body.sumLoop(iBegin, iEnd);
462 
463 #endif //#else // #if BT_THREADSAFE
464 }
465 
471 {
472 public:
474  virtual int getMaxNumThreads() const BT_OVERRIDE { return 1; }
475  virtual int getNumThreads() const BT_OVERRIDE { return 1; }
476  virtual void setNumThreads(int numThreads) BT_OVERRIDE {}
477  virtual void parallelFor(int iBegin, int iEnd, int grainSize, const btIParallelForBody& body) BT_OVERRIDE
478  {
479  BT_PROFILE("parallelFor_sequential");
480  body.forLoop(iBegin, iEnd);
481  }
482  virtual btScalar parallelSum(int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body) BT_OVERRIDE
483  {
484  BT_PROFILE("parallelSum_sequential");
485  return body.sumLoop(iBegin, iEnd);
486  }
487 };
488 
489 #if BT_USE_OPENMP && BT_THREADSAFE
493 class btTaskSchedulerOpenMP : public btITaskScheduler
494 {
495  int m_numThreads;
496 
497 public:
498  btTaskSchedulerOpenMP() : btITaskScheduler("OpenMP")
499  {
500  m_numThreads = 0;
501  }
502  virtual int getMaxNumThreads() const BT_OVERRIDE
503  {
504  return omp_get_max_threads();
505  }
506  virtual int getNumThreads() const BT_OVERRIDE
507  {
508  return m_numThreads;
509  }
510  virtual void setNumThreads(int numThreads) BT_OVERRIDE
511  {
512  // With OpenMP, because it is a standard with various implementations, we can't
513  // know for sure if every implementation has the same behavior of destroying all
514  // previous threads when resizing the threadpool
515  m_numThreads = (std::max)(1, (std::min)(int(BT_MAX_THREAD_COUNT), numThreads));
516  omp_set_num_threads(1); // hopefully, all previous threads get destroyed here
517  omp_set_num_threads(m_numThreads);
518  m_savedThreadCounter = 0;
519  if (m_isActive)
520  {
522  }
523  }
524  virtual void parallelFor(int iBegin, int iEnd, int grainSize, const btIParallelForBody& body) BT_OVERRIDE
525  {
526  BT_PROFILE("parallelFor_OpenMP");
528 #pragma omp parallel for schedule(static, 1)
529  for (int i = iBegin; i < iEnd; i += grainSize)
530  {
531  BT_PROFILE("OpenMP_forJob");
532  body.forLoop(i, (std::min)(i + grainSize, iEnd));
533  }
535  }
536  virtual btScalar parallelSum(int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body) BT_OVERRIDE
537  {
538  BT_PROFILE("parallelFor_OpenMP");
540  btScalar sum = btScalar(0);
541 #pragma omp parallel for schedule(static, 1) reduction(+ \
542  : sum)
543  for (int i = iBegin; i < iEnd; i += grainSize)
544  {
545  BT_PROFILE("OpenMP_sumJob");
546  sum += body.sumLoop(i, (std::min)(i + grainSize, iEnd));
547  }
549  return sum;
550  }
551 };
552 #endif // #if BT_USE_OPENMP && BT_THREADSAFE
553 
554 #if BT_USE_TBB && BT_THREADSAFE
558 class btTaskSchedulerTBB : public btITaskScheduler
559 {
560  int m_numThreads;
561  tbb::task_scheduler_init* m_tbbSchedulerInit;
562 
563 public:
564  btTaskSchedulerTBB() : btITaskScheduler("IntelTBB")
565  {
566  m_numThreads = 0;
567  m_tbbSchedulerInit = NULL;
568  }
569  ~btTaskSchedulerTBB()
570  {
571  if (m_tbbSchedulerInit)
572  {
573  delete m_tbbSchedulerInit;
574  m_tbbSchedulerInit = NULL;
575  }
576  }
577 
578  virtual int getMaxNumThreads() const BT_OVERRIDE
579  {
580  return tbb::task_scheduler_init::default_num_threads();
581  }
582  virtual int getNumThreads() const BT_OVERRIDE
583  {
584  return m_numThreads;
585  }
586  virtual void setNumThreads(int numThreads) BT_OVERRIDE
587  {
588  m_numThreads = (std::max)(1, (std::min)(int(BT_MAX_THREAD_COUNT), numThreads));
589  if (m_tbbSchedulerInit)
590  {
591  // destroys all previous threads
592  delete m_tbbSchedulerInit;
593  m_tbbSchedulerInit = NULL;
594  }
595  m_tbbSchedulerInit = new tbb::task_scheduler_init(m_numThreads);
596  m_savedThreadCounter = 0;
597  if (m_isActive)
598  {
600  }
601  }
602  struct ForBodyAdapter
603  {
604  const btIParallelForBody* mBody;
605 
606  ForBodyAdapter(const btIParallelForBody* body) : mBody(body) {}
607  void operator()(const tbb::blocked_range<int>& range) const
608  {
609  BT_PROFILE("TBB_forJob");
610  mBody->forLoop(range.begin(), range.end());
611  }
612  };
613  virtual void parallelFor(int iBegin, int iEnd, int grainSize, const btIParallelForBody& body) BT_OVERRIDE
614  {
615  BT_PROFILE("parallelFor_TBB");
616  ForBodyAdapter tbbBody(&body);
618  tbb::parallel_for(tbb::blocked_range<int>(iBegin, iEnd, grainSize),
619  tbbBody,
620  tbb::simple_partitioner());
622  }
623  struct SumBodyAdapter
624  {
625  const btIParallelSumBody* mBody;
626  btScalar mSum;
627 
628  SumBodyAdapter(const btIParallelSumBody* body) : mBody(body), mSum(btScalar(0)) {}
629  SumBodyAdapter(const SumBodyAdapter& src, tbb::split) : mBody(src.mBody), mSum(btScalar(0)) {}
630  void join(const SumBodyAdapter& src) { mSum += src.mSum; }
631  void operator()(const tbb::blocked_range<int>& range)
632  {
633  BT_PROFILE("TBB_sumJob");
634  mSum += mBody->sumLoop(range.begin(), range.end());
635  }
636  };
637  virtual btScalar parallelSum(int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body) BT_OVERRIDE
638  {
639  BT_PROFILE("parallelSum_TBB");
640  SumBodyAdapter tbbBody(&body);
642  tbb::parallel_deterministic_reduce(tbb::blocked_range<int>(iBegin, iEnd, grainSize), tbbBody);
644  return tbbBody.mSum;
645  }
646 };
647 #endif // #if BT_USE_TBB && BT_THREADSAFE
648 
649 #if BT_USE_PPL && BT_THREADSAFE
653 class btTaskSchedulerPPL : public btITaskScheduler
654 {
655  int m_numThreads;
656  concurrency::combinable<btScalar> m_sum; // for parallelSum
657 public:
658  btTaskSchedulerPPL() : btITaskScheduler("PPL")
659  {
660  m_numThreads = 0;
661  }
662  virtual int getMaxNumThreads() const BT_OVERRIDE
663  {
664  return concurrency::GetProcessorCount();
665  }
666  virtual int getNumThreads() const BT_OVERRIDE
667  {
668  return m_numThreads;
669  }
670  virtual void setNumThreads(int numThreads) BT_OVERRIDE
671  {
672  // capping the thread count for PPL due to a thread-index issue
673  const int maxThreadCount = (std::min)(int(BT_MAX_THREAD_COUNT), 31);
674  m_numThreads = (std::max)(1, (std::min)(maxThreadCount, numThreads));
675  using namespace concurrency;
676  if (CurrentScheduler::Id() != -1)
677  {
678  CurrentScheduler::Detach();
679  }
680  SchedulerPolicy policy;
681  {
682  // PPL seems to destroy threads when threadpool is shrunk, but keeps reusing old threads
683  // force it to destroy old threads
684  policy.SetConcurrencyLimits(1, 1);
685  CurrentScheduler::Create(policy);
686  CurrentScheduler::Detach();
687  }
688  policy.SetConcurrencyLimits(m_numThreads, m_numThreads);
689  CurrentScheduler::Create(policy);
690  m_savedThreadCounter = 0;
691  if (m_isActive)
692  {
694  }
695  }
696  struct ForBodyAdapter
697  {
698  const btIParallelForBody* mBody;
699  int mGrainSize;
700  int mIndexEnd;
701 
702  ForBodyAdapter(const btIParallelForBody* body, int grainSize, int end) : mBody(body), mGrainSize(grainSize), mIndexEnd(end) {}
703  void operator()(int i) const
704  {
705  BT_PROFILE("PPL_forJob");
706  mBody->forLoop(i, (std::min)(i + mGrainSize, mIndexEnd));
707  }
708  };
709  virtual void parallelFor(int iBegin, int iEnd, int grainSize, const btIParallelForBody& body) BT_OVERRIDE
710  {
711  BT_PROFILE("parallelFor_PPL");
712  // PPL dispatch
713  ForBodyAdapter pplBody(&body, grainSize, iEnd);
715  // note: MSVC 2010 doesn't support partitioner args, so avoid them
717  iEnd,
718  grainSize,
719  pplBody);
721  }
722  struct SumBodyAdapter
723  {
724  const btIParallelSumBody* mBody;
725  concurrency::combinable<btScalar>* mSum;
726  int mGrainSize;
727  int mIndexEnd;
728 
729  SumBodyAdapter(const btIParallelSumBody* body, concurrency::combinable<btScalar>* sum, int grainSize, int end) : mBody(body), mSum(sum), mGrainSize(grainSize), mIndexEnd(end) {}
730  void operator()(int i) const
731  {
732  BT_PROFILE("PPL_sumJob");
733  mSum->local() += mBody->sumLoop(i, (std::min)(i + mGrainSize, mIndexEnd));
734  }
735  };
736  static btScalar sumFunc(btScalar a, btScalar b) { return a + b; }
737  virtual btScalar parallelSum(int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body) BT_OVERRIDE
738  {
739  BT_PROFILE("parallelSum_PPL");
740  m_sum.clear();
741  SumBodyAdapter pplBody(&body, &m_sum, grainSize, iEnd);
743  // note: MSVC 2010 doesn't support partitioner args, so avoid them
745  iEnd,
746  grainSize,
747  pplBody);
749  return m_sum.combine(sumFunc);
750  }
751 };
752 #endif // #if BT_USE_PPL && BT_THREADSAFE
753 
754 // create a non-threaded task scheduler (always available)
756 {
757  static btTaskSchedulerSequential sTaskScheduler;
758  return &sTaskScheduler;
759 }
760 
761 // create an OpenMP task scheduler (if available, otherwise returns null)
763 {
764 #if BT_USE_OPENMP && BT_THREADSAFE
765  static btTaskSchedulerOpenMP sTaskScheduler;
766  return &sTaskScheduler;
767 #else
768  return NULL;
769 #endif
770 }
771 
772 // create an Intel TBB task scheduler (if available, otherwise returns null)
774 {
775 #if BT_USE_TBB && BT_THREADSAFE
776  static btTaskSchedulerTBB sTaskScheduler;
777  return &sTaskScheduler;
778 #else
779  return NULL;
780 #endif
781 }
782 
783 // create a PPL task scheduler (if available, otherwise returns null)
785 {
786 #if BT_USE_PPL && BT_THREADSAFE
787  static btTaskSchedulerPPL sTaskScheduler;
788  return &sTaskScheduler;
789 #else
790  return NULL;
791 #endif
792 }
#define U
#define BT_PROFILE(name)
Definition: btQuickprof.h:198
float btScalar
The btScalar type abstracts floating point numbers, to easily switch between double and single floati...
Definition: btScalar.h:314
#define btAssert(x)
Definition: btScalar.h:295
btSequentialImpulseConstraintSolverMt int btPersistentManifold int btTypedConstraint int const btContactSolverInfo btIDebugDraw *debugDrawer BT_OVERRIDE
static T sum(const btAlignedObjectArray< T > &items)
void btPopThreadsAreRunning()
Definition: btThreads.cpp:374
btITaskScheduler * btGetSequentialTaskScheduler()
Definition: btThreads.cpp:755
void btResetThreadIndexCounter()
Definition: btThreads.cpp:329
static btITaskScheduler * gBtTaskScheduler
Definition: btThreads.cpp:244
btScalar btParallelSum(int iBegin, int iEnd, int grainSize, const btIParallelSumBody &body)
Definition: btThreads.cpp:439
static btSpinMutex gThreadsRunningCounterMutex
Definition: btThreads.cpp:246
btITaskScheduler * btGetTaskScheduler()
Definition: btThreads.cpp:407
bool btThreadsAreRunning()
Definition: btThreads.cpp:381
void btPushThreadsAreRunning()
Definition: btThreads.cpp:367
static int gThreadsRunningCounter
Definition: btThreads.cpp:245
btITaskScheduler * btGetTBBTaskScheduler()
Definition: btThreads.cpp:773
unsigned int btGetCurrentThreadIndex()
Definition: btThreads.cpp:290
btITaskScheduler * btGetOpenMPTaskScheduler()
Definition: btThreads.cpp:762
static ThreadsafeCounter gThreadCounter
Definition: btThreads.cpp:247
void btSetTaskScheduler(btITaskScheduler *ts)
Definition: btThreads.cpp:386
btITaskScheduler * btGetPPLTaskScheduler()
Definition: btThreads.cpp:784
#define THREAD_LOCAL_STATIC
Definition: btThreads.cpp:212
bool btIsMainThread()
Definition: btThreads.cpp:324
void btParallelFor(int iBegin, int iEnd, int grainSize, const btIParallelForBody &body)
Definition: btThreads.cpp:412
const unsigned int BT_MAX_THREAD_COUNT
Definition: btThreads.h:31
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition: btTransform.h:90
virtual void forLoop(int iBegin, int iEnd) const =0
virtual btScalar sumLoop(int iBegin, int iEnd) const =0
btITaskScheduler(const char *name)
Definition: btThreads.cpp:336
virtual int getNumThreads() const =0
unsigned int m_savedThreadCounter
Definition: btThreads.h:143
virtual int getMaxNumThreads() const =0
virtual void parallelFor(int iBegin, int iEnd, int grainSize, const btIParallelForBody &body)=0
virtual void deactivate()
Definition: btThreads.cpp:358
virtual void setNumThreads(int numThreads)=0
const char * m_name
Definition: btThreads.h:142
virtual btScalar parallelSum(int iBegin, int iEnd, int grainSize, const btIParallelSumBody &body)=0
virtual void activate()
Definition: btThreads.cpp:343
void lock()
Definition: btThreads.cpp:196
bool tryLock()
Definition: btThreads.cpp:206
void unlock()
Definition: btThreads.cpp:201
virtual void setNumThreads(int numThreads) BT_OVERRIDE
Definition: btThreads.cpp:476
virtual int getMaxNumThreads() const BT_OVERRIDE
Definition: btThreads.cpp:474
virtual btScalar parallelSum(int iBegin, int iEnd, int grainSize, const btIParallelSumBody &body) BT_OVERRIDE
Definition: btThreads.cpp:482
virtual int getNumThreads() const BT_OVERRIDE
Definition: btThreads.cpp:475
virtual void parallelFor(int iBegin, int iEnd, int grainSize, const btIParallelForBody &body) BT_OVERRIDE
Definition: btThreads.cpp:477
SyclQueue void void * src
static unsigned a[3]
Definition: RandGen.cpp:78
void split(const std::string &s, const char delim, std::vector< std::string > &tokens)
Definition: abc_util.cc:92
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
#define min(a, b)
Definition: sort.c:35
unsigned int getNext()
Definition: btThreads.cpp:227
btSpinMutex mMutex
Definition: btThreads.cpp:219
unsigned int mCounter
Definition: btThreads.cpp:218
float max