27 threadStackSize (stackSize)
33 if (deleteOnThreadEnd)
53 CurrentThreadHolder() noexcept {}
55 using Ptr = ReferenceCountedObjectPtr<CurrentThreadHolder>;
56 ThreadLocalValue<Thread*> value;
58 JUCE_DECLARE_NON_COPYABLE (CurrentThreadHolder)
61static char currentThreadHolderLock [
sizeof (
SpinLock)];
63static SpinLock* castToSpinLockWithoutAliasingWarning (
void* s)
68static CurrentThreadHolder::Ptr getCurrentThreadHolder()
70 static CurrentThreadHolder::Ptr currentThreadHolder;
73 if (currentThreadHolder ==
nullptr)
74 currentThreadHolder =
new CurrentThreadHolder();
76 return currentThreadHolder;
79void Thread::threadEntryPoint()
81 const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
82 currentThreadHolder->value =
this;
84 if (threadName.isNotEmpty())
89 if (startSuspensionEvent.wait (10000))
93 if (affinityMask != 0)
106 currentThreadHolder->value.releaseCurrentThreadStorage();
110 auto shouldDeleteThis = deleteOnThreadEnd;
113 if (shouldDeleteThis)
118void JUCE_API juce_threadEntryPoint (
void* userData)
120 static_cast<Thread*
> (userData)->threadEntryPoint();
124bool Thread::startThreadInternal (Priority threadPriority)
132 #if JUCE_ANDROID || JUCE_LINUX || JUCE_BSD
133 priority = threadPriority;
136 if (createNativeThread (threadPriority))
138 startSuspensionEvent.signal();
152 const ScopedLock sl (startStopLock);
154 if (threadHandle ==
nullptr)
156 realtimeOptions.reset();
157 return startThreadInternal (threadPriority);
165 const ScopedLock sl (startStopLock);
167 if (threadHandle ==
nullptr)
169 realtimeOptions = std::make_optional (options);
174 realtimeOptions.reset();
182 return threadHandle !=
nullptr;
187 return getCurrentThreadHolder()->value.get();
210 return currentThread->threadShouldExit();
239 const ScopedLock sl (startStopLock);
246 if (timeOutMilliseconds != 0)
258 threadHandle =
nullptr;
269 listeners.add (listener);
274 listeners.remove (listener);
279 return realtimeOptions.has_value();
284 affinityMask = newAffinityMask;
290 return defaultEvent.wait (timeOutMilliseconds);
295 defaultEvent.signal();
299struct LambdaThread final :
public Thread
301 LambdaThread (std::function<
void()>&& f) :
Thread (
"anonymous"), fn (std::move (f)) {}
309 std::function<void()> fn;
311 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaThread)
321 auto anon = std::make_unique<LambdaThread> (std::move (functionToRun));
322 anon->deleteOnThreadEnd =
true;
324 if (anon->startThread (priority))
338 for (
int i = 20; --i >= 0;)
350 return juce_isRunningUnderDebugger();
357class AtomicTests final :
public UnitTest
361 :
UnitTest (
"Atomics", UnitTestCategories::threads)
364 void runTest()
override
369 expect (numElementsInArray (a1) == 7);
371 expect (numElementsInArray (a2) == 3);
373 expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211);
374 expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211);
375 expect (ByteOrder::swap ((uint64) 0x1122334455667788ULL) == (uint64) 0x8877665544332211LL);
377 beginTest (
"Atomic int");
378 AtomicTester <int>::testInteger (*this);
379 beginTest (
"Atomic unsigned int");
380 AtomicTester <unsigned int>::testInteger (*this);
381 beginTest (
"Atomic int32");
382 AtomicTester <int32>::testInteger (*this);
383 beginTest (
"Atomic uint32");
384 AtomicTester <uint32>::testInteger (*this);
385 beginTest (
"Atomic long");
386 AtomicTester <long>::testInteger (*this);
387 beginTest (
"Atomic int*");
388 AtomicTester <int*>::testInteger (*this);
389 beginTest (
"Atomic float");
390 AtomicTester <float>::testFloat (*this);
391 #if ! JUCE_64BIT_ATOMICS_UNAVAILABLE
392 beginTest (
"Atomic int64");
393 AtomicTester <int64>::testInteger (*this);
394 beginTest (
"Atomic uint64");
395 AtomicTester <uint64>::testInteger (*this);
396 beginTest (
"Atomic double");
397 AtomicTester <double>::testFloat (*this);
399 beginTest (
"Atomic pointer increment/decrement");
400 Atomic<int*> a (a2);
int* b (a2);
404 beginTest (
"Atomic void*");
405 Atomic<void*> atomic;
408 atomic.set ((
void*) 10);
411 expect (atomic.value == c);
412 expect (atomic.get() == c);
416 template <
typename Type>
420 AtomicTester() =
default;
422 static void testInteger (UnitTest& test)
430 test.expect (a.value == c);
431 test.expect (a.get() == c);
435 test.expect (a.get() == c);
440 test.expect (a.get() == c);
442 test.expect (++a == ++c);
445 test.expect (--a == --c);
446 test.expect (a.get() == c);
454 static void testFloat (UnitTest& test)
463 test.expect (exactlyEqual (a.get(), (Type) 101));
464 test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200));
465 test.expect (exactlyEqual (a.get(), (Type) 101));
466 test.expect (a.compareAndSetBool ((Type) 200, a.get()));
467 test.expect (exactlyEqual (a.get(), (Type) 200));
469 test.expect (exactlyEqual (a.exchange ((Type) 300), (Type) 200));
470 test.expect (exactlyEqual (a.get(), (Type) 300));
473 test.expect (exactlyEqual (b.get(), a.get()));
478static AtomicTests atomicUnitTests;
481class ThreadLocalValueUnitTest final :
public UnitTest,
485 ThreadLocalValueUnitTest()
486 : UnitTest (
"ThreadLocalValue", UnitTestCategories::threads),
487 Thread (
"ThreadLocalValue Thread")
490 void runTest()
override
492 beginTest (
"values are thread local");
495 ThreadLocalValue<int> threadLocal;
497 sharedThreadLocal = &threadLocal;
499 sharedThreadLocal.get()->get() = 1;
502 signalThreadShouldExit();
503 waitForThreadToExit (-1);
505 mainThreadResult = sharedThreadLocal.get()->get();
507 expectEquals (mainThreadResult.get(), 1);
508 expectEquals (auxThreadResult.get(), 2);
511 beginTest (
"values are per-instance");
514 ThreadLocalValue<int> a, b;
519 expectEquals (a.get(), 1);
520 expectEquals (b.get(), 2);
525 Atomic<int> mainThreadResult, auxThreadResult;
526 Atomic<ThreadLocalValue<int>*> sharedThreadLocal;
530 sharedThreadLocal.get()->get() = 2;
531 auxThreadResult = sharedThreadLocal.get()->get();
535ThreadLocalValueUnitTest threadLocalValueUnitTest;
static void JUCE_CALLTYPE writeToLog(const String &message)
static bool JUCE_CALLTYPE isRunningUnderDebugger() noexcept
void enter() const noexcept
bool tryEnter() const noexcept
GenericScopedLock< SpinLock > ScopedLockType
virtual void exitSignalSent()=0
void setAffinityMask(uint32 affinityMask)
static void JUCE_CALLTYPE setCurrentThreadAffinityMask(uint32 affinityMask)
static void JUCE_CALLTYPE sleep(int milliseconds)
static Thread *JUCE_CALLTYPE getCurrentThread()
bool wait(double timeOutMilliseconds) const
static bool launch(std::function< void()> functionToRun)
ThreadID getThreadId() const noexcept
bool waitForThreadToExit(int timeOutMilliseconds) const
static void JUCE_CALLTYPE setCurrentThreadName(const String &newThreadName)
Thread(const String &threadName, size_t threadStackSize=osDefaultStackSize)
static bool currentThreadShouldExit()
bool threadShouldExit() const
bool startRealtimeThread(const RealtimeOptions &options)
static void JUCE_CALLTYPE yield()
static ThreadID JUCE_CALLTYPE getCurrentThreadId()
bool stopThread(int timeOutMilliseconds)
void addListener(Listener *)
void signalThreadShouldExit()
bool isThreadRunning() const
void removeListener(Listener *)
static uint32 getMillisecondCounter() noexcept