| @@ -44,6 +44,7 @@ | |||||
| #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 | #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 | ||||
| #define JUCE_STDLIB_HAS_STD_FUNCTION_SUPPORT 1 | #define JUCE_STDLIB_HAS_STD_FUNCTION_SUPPORT 1 | ||||
| #define JUCE_COMPILER_SUPPORTS_THREAD_LOCAL 1 | |||||
| #endif | #endif | ||||
| #ifndef JUCE_EXCEPTIONS_DISABLED | #ifndef JUCE_EXCEPTIONS_DISABLED | ||||
| @@ -67,6 +68,7 @@ | |||||
| #if (defined (_LIBCPP_VERSION) || ! (JUCE_MAC || JUCE_IOS)) | #if (defined (_LIBCPP_VERSION) || ! (JUCE_MAC || JUCE_IOS)) | ||||
| #define JUCE_STDLIB_HAS_STD_FUNCTION_SUPPORT 1 | #define JUCE_STDLIB_HAS_STD_FUNCTION_SUPPORT 1 | ||||
| #define JUCE_COMPILER_SUPPORTS_THREAD_LOCAL 1 | |||||
| #endif | #endif | ||||
| #if __has_feature (cxx_generalized_initializers) && (defined (_LIBCPP_VERSION) || ! (JUCE_MAC || JUCE_IOS)) | #if __has_feature (cxx_generalized_initializers) && (defined (_LIBCPP_VERSION) || ! (JUCE_MAC || JUCE_IOS)) | ||||
| @@ -106,6 +108,7 @@ | |||||
| #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 | #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 | ||||
| #define JUCE_DELETED_FUNCTION = delete | #define JUCE_DELETED_FUNCTION = delete | ||||
| #define JUCE_STDLIB_HAS_STD_FUNCTION_SUPPORT 1 | #define JUCE_STDLIB_HAS_STD_FUNCTION_SUPPORT 1 | ||||
| #define JUCE_COMPILER_SUPPORTS_THREAD_LOCAL 1 | |||||
| #endif | #endif | ||||
| #if _MSC_VER >= 1900 | #if _MSC_VER >= 1900 | ||||
| @@ -277,9 +277,9 @@ bool JUCE_CALLTYPE Process::isRunningUnderDebugger() noexcept | |||||
| return juce_isRunningUnderDebugger(); | return juce_isRunningUnderDebugger(); | ||||
| } | } | ||||
| //============================================================================== | |||||
| #if JUCE_UNIT_TESTS | #if JUCE_UNIT_TESTS | ||||
| //============================================================================== | |||||
| class AtomicTests : public UnitTest | class AtomicTests : public UnitTest | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -401,4 +401,60 @@ public: | |||||
| static AtomicTests atomicUnitTests; | static AtomicTests atomicUnitTests; | ||||
| //============================================================================== | |||||
| class ThreadLocalValueUnitTest : public UnitTest, private Thread | |||||
| { | |||||
| public: | |||||
| ThreadLocalValueUnitTest() | |||||
| : UnitTest ("ThreadLocalValue"), | |||||
| Thread ("ThreadLocalValue Thread") | |||||
| {} | |||||
| void runTest() override | |||||
| { | |||||
| beginTest ("values are thread local"); | |||||
| { | |||||
| ThreadLocalValue<int> threadLocal; | |||||
| sharedThreadLocal = &threadLocal; | |||||
| sharedThreadLocal.get()->get() = 1; | |||||
| startThread(); | |||||
| signalThreadShouldExit(); | |||||
| waitForThreadToExit (-1); | |||||
| mainThreadResult = sharedThreadLocal.get()->get(); | |||||
| expectEquals (mainThreadResult.get(), 1); | |||||
| expectEquals (auxThreadResult.get(), 2); | |||||
| } | |||||
| beginTest ("values are per-instance"); | |||||
| { | |||||
| ThreadLocalValue<int> a, b; | |||||
| a.get() = 1; | |||||
| b.get() = 2; | |||||
| expectEquals (a.get(), 1); | |||||
| expectEquals (b.get(), 2); | |||||
| } | |||||
| } | |||||
| private: | |||||
| Atomic<int> mainThreadResult, auxThreadResult; | |||||
| Atomic<ThreadLocalValue<int>*> sharedThreadLocal; | |||||
| void run() override | |||||
| { | |||||
| sharedThreadLocal.get()->get() = 2; | |||||
| auxThreadResult = sharedThreadLocal.get()->get(); | |||||
| } | |||||
| }; | |||||
| ThreadLocalValueUnitTest threadLocalValueUnitTest; | |||||
| #endif | #endif | ||||
| @@ -22,13 +22,6 @@ | |||||
| #pragma once | #pragma once | ||||
| // (NB: on win32, native thread-locals aren't possible in a dynamically loaded DLL in XP). | |||||
| #if ! ((JUCE_MSVC && (JUCE_64BIT || ! defined (JucePlugin_PluginCode))) \ | |||||
| || (JUCE_MAC && JUCE_CLANG && defined (MAC_OS_X_VERSION_10_7) \ | |||||
| && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7)) | |||||
| #define JUCE_NO_COMPILER_THREAD_LOCAL 1 | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| Provides cross-platform support for thread-local objects. | Provides cross-platform support for thread-local objects. | ||||
| @@ -61,7 +54,7 @@ public: | |||||
| */ | */ | ||||
| ~ThreadLocalValue() | ~ThreadLocalValue() | ||||
| { | { | ||||
| #if JUCE_NO_COMPILER_THREAD_LOCAL | |||||
| #if ! JUCE_COMPILER_SUPPORTS_THREAD_LOCAL | |||||
| for (ObjectHolder* o = first.value; o != nullptr;) | for (ObjectHolder* o = first.value; o != nullptr;) | ||||
| { | { | ||||
| ObjectHolder* const next = o->next; | ObjectHolder* const next = o->next; | ||||
| @@ -102,7 +95,10 @@ public: | |||||
| */ | */ | ||||
| Type& get() const noexcept | Type& get() const noexcept | ||||
| { | { | ||||
| #if JUCE_NO_COMPILER_THREAD_LOCAL | |||||
| #if JUCE_COMPILER_SUPPORTS_THREAD_LOCAL | |||||
| static thread_local HashMap<const void*, Type> holder; | |||||
| return holder.getReference (this); | |||||
| #else | |||||
| const Thread::ThreadID threadId = Thread::getCurrentThreadId(); | const Thread::ThreadID threadId = Thread::getCurrentThreadId(); | ||||
| for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) | for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) | ||||
| @@ -136,12 +132,6 @@ public: | |||||
| while (! first.compareAndSetBool (newObject, newObject->next)); | while (! first.compareAndSetBool (newObject, newObject->next)); | ||||
| return newObject->object; | return newObject->object; | ||||
| #elif JUCE_MAC | |||||
| static __thread Type object; | |||||
| return object; | |||||
| #elif JUCE_MSVC | |||||
| static __declspec(thread) Type object; | |||||
| return object; | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -150,7 +140,7 @@ public: | |||||
| */ | */ | ||||
| void releaseCurrentThreadStorage() | void releaseCurrentThreadStorage() | ||||
| { | { | ||||
| #if JUCE_NO_COMPILER_THREAD_LOCAL | |||||
| #if ! JUCE_COMPILER_SUPPORTS_THREAD_LOCAL | |||||
| const Thread::ThreadID threadId = Thread::getCurrentThreadId(); | const Thread::ThreadID threadId = Thread::getCurrentThreadId(); | ||||
| for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) | for (ObjectHolder* o = first.get(); o != nullptr; o = o->next) | ||||
| @@ -166,7 +156,7 @@ public: | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_NO_COMPILER_THREAD_LOCAL | |||||
| #if ! JUCE_COMPILER_SUPPORTS_THREAD_LOCAL | |||||
| struct ObjectHolder | struct ObjectHolder | ||||
| { | { | ||||
| ObjectHolder (const Thread::ThreadID& tid) | ObjectHolder (const Thread::ThreadID& tid) | ||||