| @@ -816,31 +816,31 @@ extern "C" void* threadEntryProc (void* userData) | |||
| void Thread::launchThread() | |||
| { | |||
| threadHandle_ = 0; | |||
| threadHandle = 0; | |||
| pthread_t handle = 0; | |||
| if (pthread_create (&handle, 0, threadEntryProc, this) == 0) | |||
| { | |||
| pthread_detach (handle); | |||
| threadHandle_ = (void*) handle; | |||
| threadId_ = (ThreadID) threadHandle_; | |||
| threadHandle = (void*) handle; | |||
| threadId = (ThreadID) threadHandle; | |||
| } | |||
| } | |||
| void Thread::closeThreadHandle() | |||
| { | |||
| threadId_ = 0; | |||
| threadHandle_ = 0; | |||
| threadId = 0; | |||
| threadHandle = 0; | |||
| } | |||
| void Thread::killThread() | |||
| { | |||
| if (threadHandle_ != 0) | |||
| if (threadHandle != 0) | |||
| { | |||
| #if JUCE_ANDROID | |||
| jassertfalse; // pthread_cancel not available! | |||
| #else | |||
| pthread_cancel ((pthread_t) threadHandle_); | |||
| pthread_cancel ((pthread_t) threadHandle); | |||
| #endif | |||
| } | |||
| } | |||
| @@ -125,25 +125,25 @@ static unsigned int __stdcall threadEntryProc (void* userData) | |||
| void Thread::launchThread() | |||
| { | |||
| unsigned int newThreadId; | |||
| threadHandle_ = (void*) _beginthreadex (0, 0, &threadEntryProc, this, 0, &newThreadId); | |||
| threadId_ = (ThreadID) newThreadId; | |||
| threadHandle = (void*) _beginthreadex (0, 0, &threadEntryProc, this, 0, &newThreadId); | |||
| threadId = (ThreadID) newThreadId; | |||
| } | |||
| void Thread::closeThreadHandle() | |||
| { | |||
| CloseHandle ((HANDLE) threadHandle_); | |||
| threadId_ = 0; | |||
| threadHandle_ = 0; | |||
| CloseHandle ((HANDLE) threadHandle); | |||
| threadId = 0; | |||
| threadHandle = 0; | |||
| } | |||
| void Thread::killThread() | |||
| { | |||
| if (threadHandle_ != 0) | |||
| if (threadHandle != 0) | |||
| { | |||
| #if JUCE_DEBUG | |||
| OutputDebugString (_T("** Warning - Forced thread termination **\n")); | |||
| OutputDebugStringA ("** Warning - Forced thread termination **\n"); | |||
| #endif | |||
| TerminateThread (threadHandle_, 0); | |||
| TerminateThread (threadHandle, 0); | |||
| } | |||
| } | |||
| @@ -23,119 +23,34 @@ | |||
| ============================================================================== | |||
| */ | |||
| class RunningThreadsList | |||
| static ThreadLocalValue<Thread*>& getCurrentThreadHolder() | |||
| { | |||
| public: | |||
| RunningThreadsList() | |||
| { | |||
| } | |||
| ~RunningThreadsList() | |||
| { | |||
| // Some threads are still running! Make sure you stop all your | |||
| // threads cleanly before your app quits! | |||
| jassert (threads.size() == 0); | |||
| } | |||
| void add (Thread* const thread) | |||
| { | |||
| const SpinLock::ScopedLockType sl (lock); | |||
| jassert (! threads.contains (thread)); | |||
| threads.add (thread); | |||
| } | |||
| void remove (Thread* const thread) | |||
| { | |||
| const SpinLock::ScopedLockType sl (lock); | |||
| jassert (threads.contains (thread)); | |||
| threads.removeValue (thread); | |||
| } | |||
| int size() const noexcept | |||
| { | |||
| return threads.size(); | |||
| } | |||
| Thread* getThreadWithID (const Thread::ThreadID targetID) const noexcept | |||
| { | |||
| const SpinLock::ScopedLockType sl (lock); | |||
| for (int i = threads.size(); --i >= 0;) | |||
| { | |||
| Thread* const t = threads.getUnchecked(i); | |||
| if (t->getThreadId() == targetID) | |||
| return t; | |||
| } | |||
| return nullptr; | |||
| } | |||
| void stopAll (const int timeOutMilliseconds) | |||
| { | |||
| signalAllThreadsToStop(); | |||
| for (;;) | |||
| { | |||
| Thread* firstThread = getFirstThread(); | |||
| if (firstThread != nullptr) | |||
| firstThread->stopThread (timeOutMilliseconds); | |||
| else | |||
| break; | |||
| } | |||
| } | |||
| static RunningThreadsList& getInstance() | |||
| { | |||
| static RunningThreadsList runningThreads; | |||
| return runningThreads; | |||
| } | |||
| private: | |||
| Array<Thread*> threads; | |||
| SpinLock lock; | |||
| void signalAllThreadsToStop() | |||
| { | |||
| const SpinLock::ScopedLockType sl (lock); | |||
| for (int i = threads.size(); --i >= 0;) | |||
| threads.getUnchecked(i)->signalThreadShouldExit(); | |||
| } | |||
| Thread* getFirstThread() const | |||
| { | |||
| const SpinLock::ScopedLockType sl (lock); | |||
| return threads.getFirst(); | |||
| } | |||
| }; | |||
| static ThreadLocalValue<Thread*> tls; | |||
| return tls; | |||
| } | |||
| //============================================================================== | |||
| void Thread::threadEntryPoint() | |||
| { | |||
| RunningThreadsList::getInstance().add (this); | |||
| getCurrentThreadHolder() = this; | |||
| JUCE_TRY | |||
| { | |||
| if (threadName_.isNotEmpty()) | |||
| setCurrentThreadName (threadName_); | |||
| if (threadName.isNotEmpty()) | |||
| setCurrentThreadName (threadName); | |||
| if (startSuspensionEvent_.wait (10000)) | |||
| if (startSuspensionEvent.wait (10000)) | |||
| { | |||
| jassert (getCurrentThreadId() == threadId_); | |||
| jassert (getCurrentThreadId() == threadId); | |||
| if (affinityMask_ != 0) | |||
| setCurrentThreadAffinityMask (affinityMask_); | |||
| if (affinityMask != 0) | |||
| setCurrentThreadAffinityMask (affinityMask); | |||
| run(); | |||
| } | |||
| } | |||
| JUCE_CATCH_ALL_ASSERT | |||
| RunningThreadsList::getInstance().remove (this); | |||
| getCurrentThreadHolder().releaseCurrentThreadStorage(); | |||
| closeThreadHandle(); | |||
| } | |||
| @@ -145,15 +60,14 @@ void JUCE_API juce_threadEntryPoint (void* userData) | |||
| static_cast <Thread*> (userData)->threadEntryPoint(); | |||
| } | |||
| //============================================================================== | |||
| Thread::Thread (const String& threadName) | |||
| : threadName_ (threadName), | |||
| threadHandle_ (nullptr), | |||
| threadId_ (0), | |||
| threadPriority_ (5), | |||
| affinityMask_ (0), | |||
| threadShouldExit_ (false) | |||
| Thread::Thread (const String& threadName_) | |||
| : threadName (threadName_), | |||
| threadHandle (nullptr), | |||
| threadId (0), | |||
| threadPriority (5), | |||
| affinityMask (0), | |||
| shouldExit (false) | |||
| { | |||
| } | |||
| @@ -176,13 +90,13 @@ void Thread::startThread() | |||
| { | |||
| const ScopedLock sl (startStopLock); | |||
| threadShouldExit_ = false; | |||
| shouldExit = false; | |||
| if (threadHandle_ == nullptr) | |||
| if (threadHandle == nullptr) | |||
| { | |||
| launchThread(); | |||
| setThreadPriority (threadHandle_, threadPriority_); | |||
| startSuspensionEvent_.signal(); | |||
| setThreadPriority (threadHandle, threadPriority); | |||
| startSuspensionEvent.signal(); | |||
| } | |||
| } | |||
| @@ -190,9 +104,9 @@ void Thread::startThread (const int priority) | |||
| { | |||
| const ScopedLock sl (startStopLock); | |||
| if (threadHandle_ == nullptr) | |||
| if (threadHandle == nullptr) | |||
| { | |||
| threadPriority_ = priority; | |||
| threadPriority = priority; | |||
| startThread(); | |||
| } | |||
| else | |||
| @@ -203,13 +117,13 @@ void Thread::startThread (const int priority) | |||
| bool Thread::isThreadRunning() const | |||
| { | |||
| return threadHandle_ != nullptr; | |||
| return threadHandle != nullptr; | |||
| } | |||
| //============================================================================== | |||
| void Thread::signalThreadShouldExit() | |||
| { | |||
| threadShouldExit_ = true; | |||
| shouldExit = true; | |||
| } | |||
| bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const | |||
| @@ -256,62 +170,51 @@ void Thread::stopThread (const int timeOutMilliseconds) | |||
| killThread(); | |||
| RunningThreadsList::getInstance().remove (this); | |||
| threadHandle_ = nullptr; | |||
| threadId_ = 0; | |||
| threadHandle = nullptr; | |||
| threadId = 0; | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| bool Thread::setPriority (const int priority) | |||
| bool Thread::setPriority (const int newPriority) | |||
| { | |||
| const ScopedLock sl (startStopLock); | |||
| if (setThreadPriority (threadHandle_, priority)) | |||
| if (setThreadPriority (threadHandle, newPriority)) | |||
| { | |||
| threadPriority_ = priority; | |||
| threadPriority = newPriority; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| bool Thread::setCurrentThreadPriority (const int priority) | |||
| bool Thread::setCurrentThreadPriority (const int newPriority) | |||
| { | |||
| return setThreadPriority (0, priority); | |||
| return setThreadPriority (0, newPriority); | |||
| } | |||
| void Thread::setAffinityMask (const uint32 affinityMask) | |||
| void Thread::setAffinityMask (const uint32 newAffinityMask) | |||
| { | |||
| affinityMask_ = affinityMask; | |||
| affinityMask = newAffinityMask; | |||
| } | |||
| //============================================================================== | |||
| bool Thread::wait (const int timeOutMilliseconds) const | |||
| { | |||
| return defaultEvent_.wait (timeOutMilliseconds); | |||
| return defaultEvent.wait (timeOutMilliseconds); | |||
| } | |||
| void Thread::notify() const | |||
| { | |||
| defaultEvent_.signal(); | |||
| defaultEvent.signal(); | |||
| } | |||
| //============================================================================== | |||
| int Thread::getNumRunningThreads() | |||
| { | |||
| return RunningThreadsList::getInstance().size(); | |||
| } | |||
| Thread* Thread::getCurrentThread() | |||
| { | |||
| return RunningThreadsList::getInstance().getThreadWithID (getCurrentThreadId()); | |||
| } | |||
| void Thread::stopAllThreads (const int timeOutMilliseconds) | |||
| { | |||
| RunningThreadsList::getInstance().stopAll (timeOutMilliseconds); | |||
| return *getCurrentThreadHolder(); | |||
| } | |||
| //============================================================================== | |||
| @@ -142,7 +142,7 @@ public: | |||
| @see signalThreadShouldExit | |||
| */ | |||
| inline bool threadShouldExit() const { return threadShouldExit_; } | |||
| inline bool threadShouldExit() const { return shouldExit; } | |||
| /** Waits for the thread to stop. | |||
| @@ -248,44 +248,30 @@ public: | |||
| @see getCurrentThreadId | |||
| */ | |||
| ThreadID getThreadId() const noexcept { return threadId_; } | |||
| ThreadID getThreadId() const noexcept { return threadId; } | |||
| /** Returns the name of the thread. | |||
| This is the name that gets set in the constructor. | |||
| */ | |||
| const String& getThreadName() const { return threadName_; } | |||
| const String& getThreadName() const { return threadName; } | |||
| /** Changes the name of the caller thread. | |||
| Different OSes may place different length or content limits on this name. | |||
| */ | |||
| static void setCurrentThreadName (const String& newThreadName); | |||
| //============================================================================== | |||
| /** Returns the number of currently-running threads. | |||
| @returns the number of Thread objects known to be currently running. | |||
| @see stopAllThreads | |||
| */ | |||
| static int getNumRunningThreads(); | |||
| /** Tries to stop all currently-running threads. | |||
| This will attempt to stop all the threads known to be running at the moment. | |||
| */ | |||
| static void stopAllThreads (int timeoutInMillisecs); | |||
| private: | |||
| //============================================================================== | |||
| const String threadName_; | |||
| void* volatile threadHandle_; | |||
| ThreadID threadId_; | |||
| const String threadName; | |||
| void* volatile threadHandle; | |||
| ThreadID threadId; | |||
| CriticalSection startStopLock; | |||
| WaitableEvent startSuspensionEvent_, defaultEvent_; | |||
| int threadPriority_; | |||
| uint32 affinityMask_; | |||
| bool volatile threadShouldExit_; | |||
| WaitableEvent startSuspensionEvent, defaultEvent; | |||
| int threadPriority; | |||
| uint32 affinityMask; | |||
| bool volatile shouldExit; | |||
| #ifndef DOXYGEN | |||
| friend void JUCE_API juce_threadEntryPoint (void*); | |||
| @@ -295,7 +281,7 @@ private: | |||
| void closeThreadHandle(); | |||
| void killThread(); | |||
| void threadEntryPoint(); | |||
| static bool setThreadPriority (void*, int priority); | |||
| static bool setThreadPriority (void*, int); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Thread); | |||
| }; | |||
| @@ -26,6 +26,9 @@ | |||
| #ifndef __JUCE_THREADLOCALVALUE_JUCEHEADER__ | |||
| #define __JUCE_THREADLOCALVALUE_JUCEHEADER__ | |||
| #if ! (JUCE_MSVC || (JUCE_MAC && defined (__clang__))) | |||
| #define JUCE_NO_COMPILER_THREAD_LOCAL 1 | |||
| #endif | |||
| //============================================================================== | |||
| /** | |||
| @@ -35,14 +38,16 @@ | |||
| an instance for each thread that requests one. The first time a thread attempts | |||
| to access its value, an object is created and added to the list for that thread. | |||
| Typically, you'll probably want to create a static instance of a ThreadLocalValue | |||
| object, or hold one within a singleton. | |||
| The templated class for your value could be a primitive type, or any class that | |||
| has a default constructor. | |||
| has a default constructor and copy operator. | |||
| Once a thread has accessed its object, that object will not be deleted until the | |||
| ThreadLocalValue object itself is deleted, even if its thread exits before that. | |||
| But, because thread ID numbers are used to identify threads, and OSes often re-use | |||
| these ID numbers, value objects will often be implicitly re-used by new threads whose | |||
| ID number is the same as one that was used by an earlier thread. | |||
| When a thread no longer needs to use its value, it can call releaseCurrentThreadStorage() | |||
| to allow the storage to be re-used by another thread. If a thread exits without calling | |||
| this method, the object storage will be left allocated until the ThreadLocalValue object | |||
| is deleted. | |||
| */ | |||
| template <typename Type> | |||
| class ThreadLocalValue | |||
| @@ -58,12 +63,14 @@ public: | |||
| */ | |||
| ~ThreadLocalValue() | |||
| { | |||
| #if JUCE_NO_COMPILER_THREAD_LOCAL | |||
| for (ObjectHolder* o = first.value; o != nullptr;) | |||
| { | |||
| ObjectHolder* const next = o->next; | |||
| delete o; | |||
| o = next; | |||
| } | |||
| #endif | |||
| } | |||
| /** Returns a reference to this thread's instance of the value. | |||
| @@ -71,21 +78,24 @@ public: | |||
| value object will be created - so if your value's class has a non-trivial | |||
| constructor, be aware that this method could invoke it. | |||
| */ | |||
| Type& operator*() const noexcept { return get(); } | |||
| Type& operator*() const noexcept { return get(); } | |||
| /** Returns a pointer to this thread's instance of the value. | |||
| Note that the first time a thread tries to access the value, an instance of the | |||
| value object will be created - so if your value's class has a non-trivial | |||
| constructor, be aware that this method could invoke it. | |||
| */ | |||
| operator Type*() const noexcept { return &get(); } | |||
| operator Type*() const noexcept { return &get(); } | |||
| /** Accesses a method or field of the value object. | |||
| Note that the first time a thread tries to access the value, an instance of the | |||
| value object will be created - so if your value's class has a non-trivial | |||
| constructor, be aware that this method could invoke it. | |||
| */ | |||
| Type* operator->() const noexcept { return &get(); } | |||
| Type* operator->() const noexcept { return &get(); } | |||
| /** Assigns a new value to the thread-local object. */ | |||
| ThreadLocalValue& operator= (const Type& newValue) { get() = newValue; return *this; } | |||
| /** Returns a reference to this thread's instance of the value. | |||
| Note that the first time a thread tries to access the value, an instance of the | |||
| @@ -94,12 +104,31 @@ public: | |||
| */ | |||
| Type& get() const noexcept | |||
| { | |||
| #if JUCE_NO_COMPILER_THREAD_LOCAL | |||
| const Thread::ThreadID threadId = Thread::getCurrentThreadId(); | |||
| for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) | |||
| if (o->threadId == threadId) | |||
| return o->object; | |||
| for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) | |||
| { | |||
| if (o->threadId == nullptr) | |||
| { | |||
| { | |||
| SpinLock::ScopedLockType sl (lock); | |||
| if (o->threadId != nullptr) | |||
| continue; | |||
| o->threadId = threadId; | |||
| } | |||
| o->object = Type(); | |||
| return o->object; | |||
| } | |||
| } | |||
| ObjectHolder* const newObject = new ObjectHolder (threadId); | |||
| do | |||
| @@ -109,17 +138,44 @@ public: | |||
| while (! first.compareAndSetBool (newObject, newObject->next)); | |||
| return newObject->object; | |||
| #elif JUCE_MAC | |||
| static __thread Type object; | |||
| return object; | |||
| #elif JUCE_MSVC | |||
| static __declspec(thread) Type object; | |||
| return object; | |||
| #endif | |||
| } | |||
| /** Called by a thread before it terminates, to allow this class to release | |||
| any storage associated with the thread. | |||
| */ | |||
| void releaseCurrentThreadStorage() | |||
| { | |||
| #if JUCE_NO_COMPILER_THREAD_LOCAL | |||
| const Thread::ThreadID threadId = Thread::getCurrentThreadId(); | |||
| for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) | |||
| { | |||
| if (o->threadId == threadId) | |||
| { | |||
| SpinLock::ScopedLockType sl (lock); | |||
| o->threadId = nullptr; | |||
| } | |||
| } | |||
| #endif | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| #if JUCE_NO_COMPILER_THREAD_LOCAL | |||
| struct ObjectHolder | |||
| { | |||
| ObjectHolder (const Thread::ThreadID& threadId_) | |||
| : threadId (threadId_), object() | |||
| {} | |||
| const Thread::ThreadID threadId; | |||
| Thread::ThreadID threadId; | |||
| ObjectHolder* next; | |||
| Type object; | |||
| @@ -127,6 +183,8 @@ private: | |||
| }; | |||
| mutable Atomic<ObjectHolder*> first; | |||
| SpinLock lock; | |||
| #endif | |||
| JUCE_DECLARE_NON_COPYABLE (ThreadLocalValue); | |||
| }; | |||