@@ -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) | ||||