21# pragma warning( disable: 4275 )
22# pragma warning(disable: 4251)
45#define ENABLE_VERBOSE_THREAD_TIMELINE 0
48#define AGX_MAX_NUM_THREADS 1024
150 inline bool joinable();
155 inline unsigned int getThreadState();
189 std::mutex m_handleMutex;
260 static Thread *getThread(
size_t id);
265 static Thread *getMainThread();
296 static bool isMainThread();
321 const char* description,
322 const char* extraDataTitle =
nullptr,
328 Real getOverheadTime()
const;
369 static int log(
const char *format, ...);
393 class MainThreadSingleton;
394 static Thread *initMainThread();
396 static void performNumThreadsChange();
400 void freeDefaultStorages();
401 void spawn(
Job *job);
402 void sortInsertJob(
Job *job);
405 void completeFrame(
Task *task);
407 bool isActive()
const;
410 template <
bool THREAD_TIMELINE_STATISTICS>
414 void blockingDoWork();
417 void wakeupThreads();
418 void pushTargetJob(
Thread *target,
Job *job,
bool activateTarget =
true);
419 static void taskCompleted(
Task *task);
424 Job *getExecutionJob();
426 Index getRandomOtherThreadId(
Index excludeIndex = InvalidIndex);
434 Notify::ThreadData *getNotifyData();
449 static const UInt32 mod = ((1ULL << 32) - 5);
450 static const UInt32 mul = 69070U;
458 FastRandom m_fastRandom;
462 inline Frame(
Task *t =
nullptr) : task(t), done(false) {}
463 inline Frame& operator=(
const Frame& other) {task = other.task; done = other.done.load();
return *
this;}
465 std::atomic<bool> done;
468 #define AGX_THREAD_FRAME_STACK_MAX_DEPTH 64
471 size_t m_activationDepth;
472 std::atomic<Int32> m_activationCount;
479 return lhs->getCostEstimate() < rhs->getCostEstimate();
486 Thread *m_listNodeNext;
488 using JobQueue = std::priority_queue<Job *, agx::VectorPOD<Job *>, JobCompare>;
490 JobQueue m_sharedJobs;
494 std::atomic<Int32> m_pushCounter;
495 std::atomic<bool> m_running;
506 Timer m_overheadTimer;
510 UInt64 m_savedRegisterState;
515 std::random_device m_randomDevice;
516 RandomGenerator m_mersienneTwister;
521 void registerContainerAllocation(
Container *container);
522 void unregisterContainerAllocation(
Container *container);
523 void *allocateScratchPadBuffer(
size_t numBytes);
524 void deallocateScratchPadBuffer(
void *buffer,
size_t numBytes);
526 struct ScratchPadArea
528 ScratchPadArea() : buffer(nullptr),
end(nullptr), head(nullptr), m_allocator(
"ScratchPad")
531 ~ScratchPadArea() { m_allocator.deallocateBytes(buffer); }
536 ByteAllocator m_allocator;
537 VectorPOD<Container *> m_activeAllocations;
540 ScratchPadArea m_scratchPad;
546 static Thread *s_mainThread;
547 static Callback1<Task *> s_taskCompletionCallback;
549 static bool s_enableJobTimeline;
551 static std::exception_ptr s_unhandledException;
554 struct LocalTimelineEntry
564 , extraDataTitle(nullptr)
568 enum JobType { PRE, DISPATCH, POST,
UNKNOWN };
579 const char* extraDataTitle;
583 Vector<LocalTimelineEntry> m_localTimelineEntries;
587 void exportTimelineEntries();
588 void reportTimelineJob(Job *job);
589 void pushTimelineEntry(
const LocalTimelineEntry& entry);
592 class LogChunk :
public Referenced
595 LogChunk(
size_t numBytes);
600 size_t numAvailable();
609 LogEntry(
agx::UInt64 timestamp_,
const char* message_);
614 int logImplementation(
const char* format, va_list ap);
615 static int cmpLogEntry(
const LogEntry& entry1,
const LogEntry& entry2);
617 LogChunkRef m_logChunk;
618 VectorPOD<LogEntry> m_logEntries;
620 typedef HashTable<agxData::EntityModel*, ref_ptr<Referenced>> DefaultStorageTable;
621 DefaultStorageTable m_defaultStorageTable;
657 return s_threads[id];
667 AGX_FORCE_INLINE bool Thread::isActive()
const {
return m_activationCount.load() > 0; }
671 return m_mersienneTwister;
680 va_start(arguments, format);
690 return m_byteAllocator.allocate(numBytes);
695 m_byteAllocator.deallocateBytes(ptr);
698 template <
typename T>
701 return this->getPool<T>()->allocate();
704 template <
typename T>
707 this->getPool<T>()->deallocate(ptr);
711 template <
typename T>
714 return this->getPool<T>()->create();
717 template <
typename T>
720 this->getPool<T>()->destroy(ptr);
723 template <
typename T>
726 uint32_t
id = agxData::getType<T>()->getId();
727 if (
id >= m_pools.size())
728 m_pools.resize(
id+1, 0);
731 m_pools[id] =
new MemoryPool<T>;
733 return static_cast<MemoryPool<T> *
>(m_pools[id]);
736 void setCurrentConstructionObject(
void *ptr);
737 void *getCurrentConstructionObject();
746 numBytes = (numBytes + 31) & ~
size_t(31);
749 if (m_scratchPad.head + numBytes > m_scratchPad.end)
751 size_t currentSize = m_scratchPad.end - m_scratchPad.buffer;
752 const Real growFactor = 1.5;
753 size_t newSize = (size_t)(
Real(currentSize + numBytes) * growFactor );
756 char *newBuffer = (
char *)m_scratchPad.m_allocator.allocateBytes(newSize, 64);
757 agxAssertN(newBuffer,
"Thread %d could not allocate %u bytes for job scratch pad!", this->
getId(), (
unsigned)newSize);
762 size_t numUsed = m_scratchPad.head - m_scratchPad.buffer;
765 memcpy(newBuffer, m_scratchPad.buffer, numUsed);
767 m_scratchPad.m_allocator.deallocateBytes(m_scratchPad.buffer);
770 for (
size_t i = 0; i < m_scratchPad.m_activeAllocations.size(); ++i)
772 Container *container = m_scratchPad.m_activeAllocations[i];
774 if (container->m_buffer)
776 ptrdiff_t offset = (
char *)container->m_buffer - (
char *)m_scratchPad.buffer;
777 container->m_buffer = newBuffer + offset;
781 m_scratchPad.buffer = newBuffer;
782 m_scratchPad.end = m_scratchPad.buffer + newSize;
783 m_scratchPad.head = m_scratchPad.buffer + numUsed;
786 void *mem = m_scratchPad.head;
787 m_scratchPad.head += numBytes;
792 AGX_FORCE_INLINE void Thread::deallocateScratchPadBuffer(
void *buffer,
size_t numBytes)
794 agxAssert(!buffer || (buffer >= m_scratchPad.buffer && buffer < m_scratchPad.end));
796 if ((
char *)buffer + numBytes == m_scratchPad.head)
797 m_scratchPad.head = (
char *)buffer;
800 AGX_FORCE_INLINE void Thread::registerContainerAllocation(Container *container)
802 m_scratchPad.m_activeAllocations.push_back(container);
806 AGX_FORCE_INLINE void Thread::unregisterContainerAllocation(Container *container)
811 agxAssert1(!m_scratchPad.m_activeAllocations.empty() && container == m_scratchPad.m_activeAllocations.back(),
"LIFO order required!");
812 m_scratchPad.m_activeAllocations.pop_back();
815 if (m_scratchPad.m_activeAllocations.empty())
816 m_scratchPad.head = m_scratchPad.buffer;
822#if ENABLE_VERBOSE_THREAD_TIMELINE
823 #define AGX_BEGIN_TIMELINE_REPORT(variable) \
824 auto variable ## _begin_time = agx::Timer::getCurrentTick()
826 #define AGX_END_TIMELINE_REPORT(variable, title) \
827 auto variable ## _end_time = agx::Timer::getCurrentTick(); \
828 agx::Thread::getCurrentThread()->reportSystemJob( variable ## _begin_time , variable ## _end_time, title);
830 #define AGX_END_TIMELINE_REPORT_DATA(variable, title, title2, data) \
831 auto variable ## _end_time = agx::Timer::getCurrentTick(); \
832 agx::Thread::getCurrentThread()->reportSystemJob( variable ## _begin_time , variable ## _end_time, title, title2, data)
834 #define AGX_BEGIN_TIMELINE_REPORT(variable)
835 #define AGX_END_TIMELINE_REPORT(variable, title)
836 #define AGX_END_TIMELINE_REPORT_DATA(variable, title, title2, data)
#define AGX_DECLARE_POINTER_TYPES(type)
#define AGX_THREAD_FRAME_STACK_MAX_DEPTH
#define AGX_MAX_NUM_THREADS
void agxFlushThreadLogs()
#define AGXPHYSICS_EXPORT
An abstract description of a data entity stored using SOA (structure of arrays) pattern in a EntitySt...
Data storage for a collection of entity instances of a specified EntityModel.
Basic wrapper class aroud std::thread.
BasicThread & operator=(const BasicThread &rhs)=delete
BasicThread()
Default constructor.
bool setThreadAffinity(agx::UInt64 cpumask)
Thread Affinity can be used to influence on which logical cores threads are scheduled and allowed to ...
BasicThread(const BasicThread &other)=delete
void detach()
Detaches the thread to the background.
void cancel()
Threads should normally not need to be killed.
virtual ~BasicThread()=default
Destructor.
std::atomic< unsigned int > m_state
bool join()
Joins the thread.
virtual void run()
This method is invoked by start.
bool joinable()
True if thread is joinable.
ThreadState
States for the thread.
unsigned int getThreadState()
Returns the current thread state.
bool start()
Launches the thread.
static std::thread::native_handle_type getCurrentThreadHandle()
Return a native_handle for the current executing thread.
Block synchronization primitive.
The Container is the base class for several of the container classes proided by AGX,...
The object defining a frame of reference and providing transformations operations.
An abstract job/workblock representation, which allows work threads to execute arbitrary tasks.
Inheritance with partial specialization due to bug with ref_ptr containers.
Class for handling logging of messages.
A representation of a generic task.
agx::Thread is a representation of an OS specific implementation of a computational thread.
static void writePerThreadStorage(ThreadStorageKey key, ThreadStorageData data)
Write to the thread-local location owned by the currently executing thread.
HashTable< Thread *, Index > ThreadIdTable
friend void AGXPHYSICS_EXPORT shutdown()
Shutdown of the AGX Dynamics API will be done when the number of shutdown matches the number of calls...
static void resetStartTick()
agx::Uuid generateUuid()
Generates a unique universal identifier.
void stop()
Stop the thread.
static void makeCurrentThreadMainThread()
Register current thread as main thread.
static int log(const char *format,...)
static Thread * getCurrentThread()
static void freePerThreadStorage(ThreadStorageKey key)
Deallocate the storage location.
static void addTask(Task *task)
agxData::EntityStorage * getDefaultStorage(agxData::EntityModel *entity)
static bool isMainThread()
void resetOverheadTime()
Reset the thread overhead time.
bool start()
Start the thread.
Thread(const Thread &)=delete
static void initThreadSystem()
static void shutdown()
Shutdown the threading system.
void reportSystemJob(UInt64 startTick, UInt64 endTick, const char *description, const char *extraDataTitle=nullptr, agx::Real64 extraData=0.0)
Add an entry to the job duration log.
static std::string getCurrentThreadDescription()
static void setEnableJobTimeline(bool flag)
Enable or disable job timeline statistics.
static Thread * registerAsAgxThread()
Register the current thread as an AGX thread.
friend void AGXCORE_EXPORT setNumThreads(size_t numThreads)
Set the number of threads to use (including the main thread).
pthread_key_t ThreadStorageKey
std::mt19937 RandomGenerator
Index getIndex() const
Get an index suitable for use when storing per-thread data in e.g.
static ThreadStorageKey allocatePerThreadStorage()
Allocate a storage location that is unique for each thread.
agxData::EntityStorageRef popTimelineEntryStorage()
RandomGenerator & getRandomGenerator()
Return a reference to the mersienne twister used for generating random numbers.
static bool immediateLogging
static void flushTimelineLogs()
static bool isShuttingDown()
static Thread * getThread(size_t id)
static Thread * getMainThread()
static bool getEnableJobTimeline()
static ThreadStorageData readPerThreadStorage(ThreadStorageKey key)
Read the thread-local value for the currently executing thread associated with the given key.
static void unregisterAsAgxThread()
Remove agx attributes from current thread.
Index getId() const
Returns the thread's AGX thread ID, a value between 0 and N-1, for AGX's internal threads,...
Real getOverheadTime() const
static Thread::ThreadIdTable * getPromotedThreads()
static void exportAllTimelines()
Generator of UUID values based on V4 http://en.wikipedia.org/wiki/Universally_unique_identifier.
A UUID, or Universally unique identifier, is intended to uniquely identify information in a distribut...
Vector containing 'raw' data.
#define agxAssert1(expr, msg)
#define agxAssertN(expr, format,...)
#define AGX_STATIC_ASSERT(X)
Contains classes for low level data storage for AGX.
The agx namespace contains the dynamics/math part of the AGX Dynamics API.
agx::VectorPOD< Thread * > ThreadPtrVector
LinearProbingHashSetImplementation< KeyT, HashT >::iterator end(LinearProbingHashSetImplementation< KeyT, HashT > &set)
VectorPOD< class Job * > JobPtrVector
void AGXPHYSICS_EXPORT init()
Initialize AGX Dynamics API including thread resources and must be executed before using the AGX API.