Bullet Collision Detection & Physics Library
btThreads.cpp
Go to the documentation of this file.
1/*
2Copyright (c) 2003-2014 Erwin Coumans http://bullet.googlecode.com
3
4This software is provided 'as-is', without any express or implied warranty.
5In no event will the authors be held liable for any damages arising from the use of this software.
6Permission is granted to anyone to use this software for any purpose,
7including commercial applications, and to alter it and redistribute it freely,
8subject to the following restrictions:
9
101. 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.
112. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
123. 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
92{
93 // note: this lock does not sleep the thread.
94 while (!tryLock())
95 {
96 // spin
97 }
98}
99
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
122{
123 // note: this lock does not sleep the thread
124 while (!tryLock())
125 {
126 // spin
127 }
128}
129
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
150{
151 // note: this lock does not sleep the thread
152 while (!tryLock())
153 {
154 // spin
155 }
156}
157
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
173{
174 // note: this lock does not sleep the thread
175 while (!tryLock())
176 {
177 // spin
178 }
179}
180
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
245static 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
278typedef DWORD ThreadId_t;
279const static ThreadId_t kInvalidThreadId = 0;
280ThreadId_t gDebugThreadIds[BT_MAX_THREAD_COUNT];
281
282static 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 }
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
412void 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
439btScalar 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{
472public:
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
493class btTaskSchedulerOpenMP : public btITaskScheduler
494{
495 int m_numThreads;
496
497public:
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
558class btTaskSchedulerTBB : public btITaskScheduler
559{
560 int m_numThreads;
561 tbb::task_scheduler_init* m_tbbSchedulerInit;
562
563public:
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
653class btTaskSchedulerPPL : public btITaskScheduler
654{
655 int m_numThreads;
656 concurrency::combinable<btScalar> m_sum; // for parallelSum
657public:
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
716 concurrency::parallel_for(iBegin,
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
744 concurrency::parallel_for(iBegin,
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}
static int split(btDbvtNode **leaves, int count, const btVector3 &org, const btVector3 &axis)
Definition: btDbvt.cpp:258
unsigned int U
Definition: btGjkEpa3.h:78
#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:153
static T sum(const btAlignedObjectArray< T > &items)
btITaskScheduler * btGetTBBTaskScheduler()
Definition: btThreads.cpp:773
void btPopThreadsAreRunning()
Definition: btThreads.cpp:374
btITaskScheduler * btGetPPLTaskScheduler()
Definition: btThreads.cpp:784
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
bool btThreadsAreRunning()
Definition: btThreads.cpp:381
void btPushThreadsAreRunning()
Definition: btThreads.cpp:367
static int gThreadsRunningCounter
Definition: btThreads.cpp:245
btITaskScheduler * btGetOpenMPTaskScheduler()
Definition: btThreads.cpp:762
unsigned int btGetCurrentThreadIndex()
Definition: btThreads.cpp:290
static ThreadsafeCounter gThreadCounter
Definition: btThreads.cpp:247
void btSetTaskScheduler(btITaskScheduler *ts)
Definition: btThreads.cpp:386
btITaskScheduler * btGetTaskScheduler()
Definition: btThreads.cpp:407
btITaskScheduler * btGetSequentialTaskScheduler()
Definition: btThreads.cpp:755
#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
#define BT_OVERRIDE
Definition: btThreads.h:26
const unsigned int BT_MAX_THREAD_COUNT
Definition: btThreads.h:31
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
btSpinMutex – lightweight spin-mutex implemented with atomic ops, never puts a thread to sleep becaus...
Definition: btThreads.h:46
void lock()
Definition: btThreads.cpp:196
bool tryLock()
Definition: btThreads.cpp:206
void unlock()
Definition: btThreads.cpp:201
btTaskSchedulerSequential – non-threaded implementation of task scheduler (really just useful for tes...
Definition: btThreads.cpp:471
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
unsigned int getNext()
Definition: btThreads.cpp:227
btSpinMutex mMutex
Definition: btThreads.cpp:219
unsigned int mCounter
Definition: btThreads.cpp:218