
#include "LinearMath/btMinMax.h"
#include "LinearMath/btAlignedObjectArray.h"
#include "LinearMath/btThreads.h"
#include "LinearMath/btQuickprof.h"
#include <stdio.h>
#include <algorithm>

#if BT_THREADSAFE

#include "btThreadSupportInterface.h"

#if defined(_WIN32)

#define WIN32_LEAN_AND_MEAN

#include <windows.h>

#endif

typedef unsigned long long btU64;
static const int kCacheLineSize = 64;

void btSpinPause()
{
#if defined(_WIN32)
	YieldProcessor();
#endif
}

struct WorkerThreadStatus
{
	enum Type
	{
		kInvalid,
		kWaitingForWork,
		kWorking,
		kSleeping,
	};
};

ATTRIBUTE_ALIGNED64(class)
WorkerThreadDirectives
{
	static const int kMaxThreadCount = BT_MAX_THREAD_COUNT;
	// directives for all worker threads packed into a single cacheline
	char m_threadDirs[kMaxThreadCount];

public:
	enum Type
	{
		kInvalid,
		kGoToSleep,         // go to sleep
		kStayAwakeButIdle,  // wait for not checking job queue
		kScanForJobs,       // actively scan job queue for jobs
	};
	WorkerThreadDirectives()
	{
		for (int i = 0; i < kMaxThreadCount; ++i)
		{
			m_threadDirs[i] = 0;
		}
	}

	Type getDirective(int threadId)
	{
		btAssert(threadId < kMaxThreadCount);
		return static_cast<Type>(m_threadDirs[threadId]);
	}

	void setDirectiveByRange(int threadBegin, int threadEnd, Type dir)
	{
		btAssert(threadBegin < threadEnd);
		btAssert(threadEnd <= kMaxThreadCount);
		char dirChar = static_cast<char>(dir);
		for (int i = threadBegin; i < threadEnd; ++i)
		{
			m_threadDirs[i] = dirChar;
		}
	}
};

class JobQueue;

ATTRIBUTE_ALIGNED64(struct)
ThreadLocalStorage
{
	int m_threadId;
	WorkerThreadStatus::Type m_status;
	int m_numJobsFinished;
	btSpinMutex m_mutex;
	btScalar m_sumResult;
	WorkerThreadDirectives* m_directive;
	JobQueue* m_queue;
	btClock* m_clock;
	unsigned int m_cooldownTime;
};

struct IJob
{
	virtual void executeJob(int threadId) = 0;
};

class ParallelForJob : public IJob
{
	const btIParallelForBody* m_body;
	int m_begin;
	int m_end;

public:
	ParallelForJob(int iBegin, int iEnd, const btIParallelForBody& body)
	{
		m_body = &body;
		m_begin = iBegin;
		m_end = iEnd;
	}
	virtual void executeJob(int threadId) BT_OVERRIDE
	{
		BT_PROFILE("executeJob");

		// call the functor body to do the work
		m_body->forLoop(m_begin, m_end);
	}
};

class ParallelSumJob : public IJob
{
	const btIParallelSumBody* m_body;
	ThreadLocalStorage* m_threadLocalStoreArray;
	int m_begin;
	int m_end;

public:
	ParallelSumJob(int iBegin, int iEnd, const btIParallelSumBody& body, ThreadLocalStorage* tls)
	{
		m_body = &body;
		m_threadLocalStoreArray = tls;
		m_begin = iBegin;
		m_end = iEnd;
	}
	virtual void executeJob(int threadId) BT_OVERRIDE
	{
		BT_PROFILE("executeJob");

		// call the functor body to do the work
		btScalar val = m_body->sumLoop(m_begin, m_end);
#if BT_PARALLEL_SUM_DETERMINISTISM
		// by truncating bits of the result, we can make the parallelSum deterministic (at the expense of precision)
		const float TRUNC_SCALE = float(1 << 19);
		val = floor(val * TRUNC_SCALE + 0.5f) / TRUNC_SCALE;  // truncate some bits
#endif
		m_threadLocalStoreArray[threadId].m_sumResult += val;
	}
};

ATTRIBUTE_ALIGNED64(class)
JobQueue
{
	btThreadSupportInterface* m_threadSupport;
	btCriticalSection* m_queueLock;
	btSpinMutex m_mutex;

	btAlignedObjectArray<IJob*> m_jobQueue;
	char* m_jobMem;
	int m_jobMemSize;
	bool m_queueIsEmpty;
	int m_tailIndex;
	int m_headIndex;
	int m_allocSize;
	bool m_useSpinMutex;
	btAlignedObjectArray<JobQueue*> m_neighborContexts;
	char m_cachePadding[kCacheLineSize];  // prevent false sharing

	void freeJobMem()
	{
		if (m_jobMem)
		{
			// free old
			btAlignedFree(m_jobMem);
			m_jobMem = NULL;
		}
	}
	void resizeJobMem(int newSize)
	{
		if (newSize > m_jobMemSize)
		{
			freeJobMem();
			m_jobMem = static_cast<char*>(btAlignedAlloc(newSize, kCacheLineSize));
			m_jobMemSize = newSize;
		}
	}

public:
	JobQueue()
	{
		m_jobMem = NULL;
		m_jobMemSize = 0;
		m_threadSupport = NULL;
		m_queueLock = NULL;
		m_headIndex = 0;
		m_tailIndex = 0;
		m_useSpinMutex = false;
	}
	~JobQueue()
	{
		exit();
	}
	void exit()
	{
		freeJobMem();
		if (m_queueLock && m_threadSupport)
		{
			m_threadSupport->deleteCriticalSection(m_queueLock);
			m_queueLock = NULL;
			m_threadSupport = 0;
		}
	}

	void init(btThreadSupportInterface * threadSup, btAlignedObjectArray<JobQueue> * contextArray)
	{
		m_threadSupport = threadSup;
		if (threadSup)
		{
			m_queueLock = m_threadSupport->createCriticalSection();
		}
		setupJobStealing(contextArray, contextArray->size());
	}
	void setupJobStealing(btAlignedObjectArray<JobQueue> * contextArray, int numActiveContexts)
	{
		btAlignedObjectArray<JobQueue>& contexts = *contextArray;
		int selfIndex = 0;
		for (int i = 0; i < contexts.size(); ++i)
		{
			if (this == &contexts[i])
			{
				selfIndex = i;
				break;
			}
		}
		int numNeighbors = btMin(2, contexts.size() - 1);
		int neighborOffsets[] = {-1, 1, -2, 2, -3, 3};
		int numOffsets = sizeof(neighborOffsets) / sizeof(neighborOffsets[0]);
		m_neighborContexts.reserve(numNeighbors);
		m_neighborContexts.resizeNoInitialize(0);
		for (int i = 0; i < numOffsets && m_neighborContexts.size() < numNeighbors; i++)
		{
			int neighborIndex = selfIndex + neighborOffsets[i];
			if (neighborIndex >= 0 && neighborIndex < numActiveContexts)
			{
				m_neighborContexts.push_back(&contexts[neighborIndex]);
			}
		}
	}

	bool isQueueEmpty() const { return m_queueIsEmpty; }
	void lockQueue()
	{
		if (m_useSpinMutex)
		{
			m_mutex.lock();
		}
		else
		{
			m_queueLock->lock();
		}
	}
	void unlockQueue()
	{
		if (m_useSpinMutex)
		{
			m_mutex.unlock();
		}
		else
		{
			m_queueLock->unlock();
		}
	}
	void clearQueue(int jobCount, int jobSize)
	{
		lockQueue();
		m_headIndex = 0;
		m_tailIndex = 0;
		m_allocSize = 0;
		m_queueIsEmpty = true;
		int jobBufSize = jobSize * jobCount;
		// make sure we have enough memory allocated to store jobs
		if (jobBufSize > m_jobMemSize)
		{
			resizeJobMem(jobBufSize);
		}
		// make sure job queue is big enough
		if (jobCount > m_jobQueue.capacity())
		{
			m_jobQueue.reserve(jobCount);
		}
		unlockQueue();
		m_jobQueue.resizeNoInitialize(0);
	}
	void* allocJobMem(int jobSize)
	{
		btAssert(m_jobMemSize >= (m_allocSize + jobSize));
		void* jobMem = &m_jobMem[m_allocSize];
		m_allocSize += jobSize;
		return jobMem;
	}
	void submitJob(IJob * job)
	{
		btAssert(reinterpret_cast<char*>(job) >= &m_jobMem[0] && reinterpret_cast<char*>(job) < &m_jobMem[0] + m_allocSize);
		m_jobQueue.push_back(job);
		lockQueue();
		m_tailIndex++;
		m_queueIsEmpty = false;
		unlockQueue();
	}
	IJob* consumeJobFromOwnQueue()
	{
		if (m_queueIsEmpty)
		{
			// lock free path. even if this is taken erroneously it isn't harmful
			return NULL;
		}
		IJob* job = NULL;
		lockQueue();
		if (!m_queueIsEmpty)
		{
			job = m_jobQueue[m_headIndex++];
			btAssert(reinterpret_cast<char*>(job) >= &m_jobMem[0] && reinterpret_cast<char*>(job) < &m_jobMem[0] + m_allocSize);
			if (m_headIndex == m_tailIndex)
			{
				m_queueIsEmpty = true;
			}
		}
		unlockQueue();
		return job;
	}
	IJob* consumeJob()
	{
		if (IJob* job = consumeJobFromOwnQueue())
		{
			return job;
		}
		// own queue is empty, try to steal from neighbor
		for (int i = 0; i < m_neighborContexts.size(); ++i)
		{
			JobQueue* otherContext = m_neighborContexts[i];
			if (IJob* job = otherContext->consumeJobFromOwnQueue())
			{
				return job;
			}
		}
		return NULL;
	}
};

static void WorkerThreadFunc(void* userPtr)
{
	BT_PROFILE("WorkerThreadFunc");
	ThreadLocalStorage* localStorage = (ThreadLocalStorage*)userPtr;
	JobQueue* jobQueue = localStorage->m_queue;

	bool shouldSleep = false;
	int threadId = localStorage->m_threadId;
	while (!shouldSleep)
	{
		// do work
		localStorage->m_mutex.lock();
		while (IJob* job = jobQueue->consumeJob())
		{
			localStorage->m_status = WorkerThreadStatus::kWorking;
			job->executeJob(threadId);
			localStorage->m_numJobsFinished++;
		}
		localStorage->m_status = WorkerThreadStatus::kWaitingForWork;
		localStorage->m_mutex.unlock();
		btU64 clockStart = localStorage->m_clock->getTimeMicroseconds();
		// while queue is empty,
		while (jobQueue->isQueueEmpty())
		{
			// todo: spin wait a bit to avoid hammering the empty queue
			btSpinPause();
			if (localStorage->m_directive->getDirective(threadId) == WorkerThreadDirectives::kGoToSleep)
			{
				shouldSleep = true;
				break;
			}
			// if jobs are incoming,
			if (localStorage->m_directive->getDirective(threadId) == WorkerThreadDirectives::kScanForJobs)
			{
				clockStart = localStorage->m_clock->getTimeMicroseconds();  // reset clock
			}
			else
			{
				for (int i = 0; i < 50; ++i)
				{
					btSpinPause();
					btSpinPause();
					btSpinPause();
					btSpinPause();
					if (localStorage->m_directive->getDirective(threadId) == WorkerThreadDirectives::kScanForJobs || !jobQueue->isQueueEmpty())
					{
						break;
					}
				}
				// if no jobs incoming and queue has been empty for the cooldown time, sleep
				btU64 timeElapsed = localStorage->m_clock->getTimeMicroseconds() - clockStart;
				if (timeElapsed > localStorage->m_cooldownTime)
				{
					shouldSleep = true;
					break;
				}
			}
		}
	}
	{
		BT_PROFILE("sleep");
		// go sleep
		localStorage->m_mutex.lock();
		localStorage->m_status = WorkerThreadStatus::kSleeping;
		localStorage->m_mutex.unlock();
	}
}

class btTaskSchedulerDefault : public btITaskScheduler
{
	btThreadSupportInterface* m_threadSupport;
	WorkerThreadDirectives* m_workerDirective;
	btAlignedObjectArray<JobQueue> m_jobQueues;
	btAlignedObjectArray<JobQueue*> m_perThreadJobQueues;
	btAlignedObjectArray<ThreadLocalStorage> m_threadLocalStorage;
	btSpinMutex m_antiNestingLock;  // prevent nested parallel-for
	btClock m_clock;
	int m_numThreads;
	int m_numWorkerThreads;
	int m_numActiveJobQueues;
	int m_maxNumThreads;
	int m_numJobs;
	static const int kFirstWorkerThreadId = 1;

public:
	btTaskSchedulerDefault() : btITaskScheduler("ThreadSupport")
	{
		m_threadSupport = NULL;
		m_workerDirective = NULL;
	}

	virtual ~btTaskSchedulerDefault()
	{
		waitForWorkersToSleep();

		for (int i = 0; i < m_jobQueues.size(); ++i)
		{
			m_jobQueues[i].exit();
		}

		if (m_threadSupport)
		{
			delete m_threadSupport;
			m_threadSupport = NULL;
		}
		if (m_workerDirective)
		{
			btAlignedFree(m_workerDirective);
			m_workerDirective = NULL;
		}
	}

	void init()
	{
		btThreadSupportInterface::ConstructionInfo constructionInfo("TaskScheduler", WorkerThreadFunc);
		m_threadSupport = btThreadSupportInterface::create(constructionInfo);
		m_workerDirective = static_cast<WorkerThreadDirectives*>(btAlignedAlloc(sizeof(*m_workerDirective), 64));

		m_numWorkerThreads = m_threadSupport->getNumWorkerThreads();
		m_maxNumThreads = m_threadSupport->getNumWorkerThreads() + 1;
		m_numThreads = m_maxNumThreads;
		// ideal to have one job queue for each physical processor (except for the main thread which needs no queue)
		int numThreadsPerQueue = m_threadSupport->getLogicalToPhysicalCoreRatio();
		int numJobQueues = (numThreadsPerQueue == 1) ? (m_maxNumThreads - 1) : (m_maxNumThreads / numThreadsPerQueue);
		m_jobQueues.resize(numJobQueues);
		m_numActiveJobQueues = numJobQueues;
		for (int i = 0; i < m_jobQueues.size(); ++i)
		{
			m_jobQueues[i].init(m_threadSupport, &m_jobQueues);
		}
		m_perThreadJobQueues.resize(m_numThreads);
		for (int i = 0; i < m_numThreads; i++)
		{
			JobQueue* jq = NULL;
			// only worker threads get a job queue
			if (i > 0)
			{
				if (numThreadsPerQueue == 1)
				{
					// one queue per worker thread
					jq = &m_jobQueues[i - kFirstWorkerThreadId];
				}
				else
				{
					// 2 threads share each queue
					jq = &m_jobQueues[i / numThreadsPerQueue];
				}
			}
			m_perThreadJobQueues[i] = jq;
		}
		m_threadLocalStorage.resize(m_numThreads);
		for (int i = 0; i < m_numThreads; i++)
		{
			ThreadLocalStorage& storage = m_threadLocalStorage[i];
			storage.m_threadId = i;
			storage.m_directive = m_workerDirective;
			storage.m_status = WorkerThreadStatus::kSleeping;
			storage.m_cooldownTime = 100;  // 100 microseconds, threads go to sleep after this long if they have nothing to do
			storage.m_clock = &m_clock;
			storage.m_queue = m_perThreadJobQueues[i];
		}
		setWorkerDirectives(WorkerThreadDirectives::kGoToSleep);  // no work for them yet
		setNumThreads(m_threadSupport->getCacheFriendlyNumThreads());
	}

	void setWorkerDirectives(WorkerThreadDirectives::Type dir)
	{
		m_workerDirective->setDirectiveByRange(kFirstWorkerThreadId, m_numThreads, dir);
	}

	virtual int getMaxNumThreads() const BT_OVERRIDE
	{
		return m_maxNumThreads;
	}

	virtual int getNumThreads() const BT_OVERRIDE
	{
		return m_numThreads;
	}

	virtual void setNumThreads(int numThreads) BT_OVERRIDE
	{
		m_numThreads = btMax(btMin(numThreads, int(m_maxNumThreads)), 1);
		m_numWorkerThreads = m_numThreads - 1;
		m_numActiveJobQueues = 0;
		// if there is at least 1 worker,
		if (m_numWorkerThreads > 0)
		{
			// re-setup job stealing between queues to avoid attempting to steal from an inactive job queue
			JobQueue* lastActiveContext = m_perThreadJobQueues[m_numThreads - 1];
			int iLastActiveContext = lastActiveContext - &m_jobQueues[0];
			m_numActiveJobQueues = iLastActiveContext + 1;
			for (int i = 0; i < m_jobQueues.size(); ++i)
			{
				m_jobQueues[i].setupJobStealing(&m_jobQueues, m_numActiveJobQueues);
			}
		}
		m_workerDirective->setDirectiveByRange(m_numThreads, BT_MAX_THREAD_COUNT, WorkerThreadDirectives::kGoToSleep);
	}

	void waitJobs()
	{
		BT_PROFILE("waitJobs");
		// have the main thread work until the job queues are empty
		int numMainThreadJobsFinished = 0;
		for (int i = 0; i < m_numActiveJobQueues; ++i)
		{
			while (IJob* job = m_jobQueues[i].consumeJob())
			{
				job->executeJob(0);
				numMainThreadJobsFinished++;
			}
		}

		// done with jobs for now, tell workers to rest (but not sleep)
		setWorkerDirectives(WorkerThreadDirectives::kStayAwakeButIdle);

		btU64 clockStart = m_clock.getTimeMicroseconds();
		// wait for workers to finish any jobs in progress
		while (true)
		{
			int numWorkerJobsFinished = 0;
			for (int iThread = kFirstWorkerThreadId; iThread < m_numThreads; ++iThread)
			{
				ThreadLocalStorage* storage = &m_threadLocalStorage[iThread];
				storage->m_mutex.lock();
				numWorkerJobsFinished += storage->m_numJobsFinished;
				storage->m_mutex.unlock();
			}
			if (numWorkerJobsFinished + numMainThreadJobsFinished == m_numJobs)
			{
				break;
			}
			btU64 timeElapsed = m_clock.getTimeMicroseconds() - clockStart;
			btAssert(timeElapsed < 1000);
			if (timeElapsed > 100000)
			{
				break;
			}
			btSpinPause();
		}
	}

	void wakeWorkers(int numWorkersToWake)
	{
		BT_PROFILE("wakeWorkers");
		btAssert(m_workerDirective->getDirective(1) == WorkerThreadDirectives::kScanForJobs);
		int numDesiredWorkers = btMin(numWorkersToWake, m_numWorkerThreads);
		int numActiveWorkers = 0;
		for (int iWorker = 0; iWorker < m_numWorkerThreads; ++iWorker)
		{
			// note this count of active workers is not necessarily totally reliable, because a worker thread could be
			// just about to put itself to sleep. So we may on occasion fail to wake up all the workers. It should be rare.
			ThreadLocalStorage& storage = m_threadLocalStorage[kFirstWorkerThreadId + iWorker];
			if (storage.m_status != WorkerThreadStatus::kSleeping)
			{
				numActiveWorkers++;
			}
		}
		for (int iWorker = 0; iWorker < m_numWorkerThreads && numActiveWorkers < numDesiredWorkers; ++iWorker)
		{
			ThreadLocalStorage& storage = m_threadLocalStorage[kFirstWorkerThreadId + iWorker];
			if (storage.m_status == WorkerThreadStatus::kSleeping)
			{
				m_threadSupport->runTask(iWorker, &storage);
				numActiveWorkers++;
			}
		}
	}

	void waitForWorkersToSleep()
	{
		BT_PROFILE("waitForWorkersToSleep");
		setWorkerDirectives(WorkerThreadDirectives::kGoToSleep);
		m_threadSupport->waitForAllTasks();
		for (int i = kFirstWorkerThreadId; i < m_numThreads; i++)
		{
			ThreadLocalStorage& storage = m_threadLocalStorage[i];
			btAssert(storage.m_status == WorkerThreadStatus::kSleeping);
		}
	}

	virtual void sleepWorkerThreadsHint() BT_OVERRIDE
	{
		BT_PROFILE("sleepWorkerThreadsHint");
		// hint the task scheduler that we may not be using these threads for a little while
		setWorkerDirectives(WorkerThreadDirectives::kGoToSleep);
	}

	void prepareWorkerThreads()
	{
		for (int i = kFirstWorkerThreadId; i < m_numThreads; ++i)
		{
			ThreadLocalStorage& storage = m_threadLocalStorage[i];
			storage.m_mutex.lock();
			storage.m_numJobsFinished = 0;
			storage.m_mutex.unlock();
		}
		setWorkerDirectives(WorkerThreadDirectives::kScanForJobs);
	}

	virtual void parallelFor(int iBegin, int iEnd, int grainSize, const btIParallelForBody& body) BT_OVERRIDE
	{
		BT_PROFILE("parallelFor_ThreadSupport");
		btAssert(iEnd >= iBegin);
		btAssert(grainSize >= 1);
		int iterationCount = iEnd - iBegin;
		if (iterationCount > grainSize && m_numWorkerThreads > 0 && m_antiNestingLock.tryLock())
		{
			typedef ParallelForJob JobType;
			int jobCount = (iterationCount + grainSize - 1) / grainSize;
			m_numJobs = jobCount;
			btAssert(jobCount >= 2);  // need more than one job for multithreading
			int jobSize = sizeof(JobType);

			for (int i = 0; i < m_numActiveJobQueues; ++i)
			{
				m_jobQueues[i].clearQueue(jobCount, jobSize);
			}
			// prepare worker threads for incoming work
			prepareWorkerThreads();
			// submit all of the jobs
			int iJob = 0;
			int iThread = kFirstWorkerThreadId;  // first worker thread
			for (int i = iBegin; i < iEnd; i += grainSize)
			{
				btAssert(iJob < jobCount);
				int iE = btMin(i + grainSize, iEnd);
				JobQueue* jq = m_perThreadJobQueues[iThread];
				btAssert(jq);
				btAssert((jq - &m_jobQueues[0]) < m_numActiveJobQueues);
				void* jobMem = jq->allocJobMem(jobSize);
				JobType* job = new (jobMem) ParallelForJob(i, iE, body);  // placement new
				jq->submitJob(job);
				iJob++;
				iThread++;
				if (iThread >= m_numThreads)
				{
					iThread = kFirstWorkerThreadId;  // first worker thread
				}
			}
			wakeWorkers(jobCount - 1);

			// put the main thread to work on emptying the job queue and then wait for all workers to finish
			waitJobs();
			m_antiNestingLock.unlock();
		}
		else
		{
			BT_PROFILE("parallelFor_mainThread");
			// just run on main thread
			body.forLoop(iBegin, iEnd);
		}
	}
	virtual btScalar parallelSum(int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body) BT_OVERRIDE
	{
		BT_PROFILE("parallelSum_ThreadSupport");
		btAssert(iEnd >= iBegin);
		btAssert(grainSize >= 1);
		int iterationCount = iEnd - iBegin;
		if (iterationCount > grainSize && m_numWorkerThreads > 0 && m_antiNestingLock.tryLock())
		{
			typedef ParallelSumJob JobType;
			int jobCount = (iterationCount + grainSize - 1) / grainSize;
			m_numJobs = jobCount;
			btAssert(jobCount >= 2);  // need more than one job for multithreading
			int jobSize = sizeof(JobType);
			for (int i = 0; i < m_numActiveJobQueues; ++i)
			{
				m_jobQueues[i].clearQueue(jobCount, jobSize);
			}

			// initialize summation
			for (int iThread = 0; iThread < m_numThreads; ++iThread)
			{
				m_threadLocalStorage[iThread].m_sumResult = btScalar(0);
			}

			// prepare worker threads for incoming work
			prepareWorkerThreads();
			// submit all of the jobs
			int iJob = 0;
			int iThread = kFirstWorkerThreadId;  // first worker thread
			for (int i = iBegin; i < iEnd; i += grainSize)
			{
				btAssert(iJob < jobCount);
				int iE = btMin(i + grainSize, iEnd);
				JobQueue* jq = m_perThreadJobQueues[iThread];
				btAssert(jq);
				btAssert((jq - &m_jobQueues[0]) < m_numActiveJobQueues);
				void* jobMem = jq->allocJobMem(jobSize);
				JobType* job = new (jobMem) ParallelSumJob(i, iE, body, &m_threadLocalStorage[0]);  // placement new
				jq->submitJob(job);
				iJob++;
				iThread++;
				if (iThread >= m_numThreads)
				{
					iThread = kFirstWorkerThreadId;  // first worker thread
				}
			}
			wakeWorkers(jobCount - 1);

			// put the main thread to work on emptying the job queue and then wait for all workers to finish
			waitJobs();

			// add up all the thread sums
			btScalar sum = btScalar(0);
			for (int iThread = 0; iThread < m_numThreads; ++iThread)
			{
				sum += m_threadLocalStorage[iThread].m_sumResult;
			}
			m_antiNestingLock.unlock();
			return sum;
		}
		else
		{
			BT_PROFILE("parallelSum_mainThread");
			// just run on main thread
			return body.sumLoop(iBegin, iEnd);
		}
	}
};

btITaskScheduler* btCreateDefaultTaskScheduler()
{
	btTaskSchedulerDefault* ts = new btTaskSchedulerDefault();
	ts->init();
	return ts;
}

#else  // #if BT_THREADSAFE

btITaskScheduler* btCreateDefaultTaskScheduler()
{
	return NULL;
}

#endif  // #else // #if BT_THREADSAFE
