|  | /*
  ==============================================================================
   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-11 by Raw Material Software Ltd.
  ------------------------------------------------------------------------------
   JUCE can be redistributed and/or modified under the terms of the GNU General
   Public License (Version 2), as published by the Free Software Foundation.
   A copy of the license is included in the JUCE distribution, or can be found
   online at www.gnu.org/licenses.
   JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  ------------------------------------------------------------------------------
   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.rawmaterialsoftware.com/juce for more information.
  ==============================================================================
*/
BEGIN_JUCE_NAMESPACE
//==============================================================================
class RunningThreadsList
{
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();
    }
};
//==============================================================================
void Thread::threadEntryPoint()
{
    RunningThreadsList::getInstance().add (this);
    JUCE_TRY
    {
        if (threadName_.isNotEmpty())
            setCurrentThreadName (threadName_);
        if (startSuspensionEvent_.wait (10000))
        {
            jassert (getCurrentThreadId() == threadId_);
            if (affinityMask_ != 0)
                setCurrentThreadAffinityMask (affinityMask_);
            run();
        }
    }
    JUCE_CATCH_ALL_ASSERT
    RunningThreadsList::getInstance().remove (this);
    closeThreadHandle();
}
// used to wrap the incoming call from the platform-specific code
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()
{
    /* If your thread class's destructor has been called without first stopping the thread, that
       means that this partially destructed object is still performing some work - and that's
       probably a Bad Thing!
       To avoid this type of nastiness, always make sure you call stopThread() before or during
       your subclass's destructor.
    */
    jassert (! isThreadRunning());
    stopThread (100);
}
//==============================================================================
void Thread::startThread()
{
    const ScopedLock sl (startStopLock);
    threadShouldExit_ = false;
    if (threadHandle_ == nullptr)
    {
        launchThread();
        setThreadPriority (threadHandle_, threadPriority_);
        startSuspensionEvent_.signal();
    }
}
void Thread::startThread (const int priority)
{
    const ScopedLock sl (startStopLock);
    if (threadHandle_ == nullptr)
    {
        threadPriority_ = priority;
        startThread();
    }
    else
    {
        setPriority (priority);
    }
}
bool Thread::isThreadRunning() const
{
    return threadHandle_ != nullptr;
}
//==============================================================================
void Thread::signalThreadShouldExit()
{
    threadShouldExit_ = true;
}
bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const
{
    // Doh! So how exactly do you expect this thread to wait for itself to stop??
    jassert (getThreadId() != getCurrentThreadId() || getCurrentThreadId() == 0);
    const int sleepMsPerIteration = 5;
    int count = timeOutMilliseconds / sleepMsPerIteration;
    while (isThreadRunning())
    {
        if (timeOutMilliseconds > 0 && --count < 0)
            return false;
        sleep (sleepMsPerIteration);
    }
    return true;
}
void Thread::stopThread (const int timeOutMilliseconds)
{
    // agh! You can't stop the thread that's calling this method! How on earth
    // would that work??
    jassert (getCurrentThreadId() != getThreadId());
    const ScopedLock sl (startStopLock);
    if (isThreadRunning())
    {
        signalThreadShouldExit();
        notify();
        if (timeOutMilliseconds != 0)
            waitForThreadToExit (timeOutMilliseconds);
        if (isThreadRunning())
        {
            // very bad karma if this point is reached, as there are bound to be
            // locks and events left in silly states when a thread is killed by force..
            jassertfalse;
            Logger::writeToLog ("!! killing thread by force !!");
            killThread();
            RunningThreadsList::getInstance().remove (this);
            threadHandle_ = nullptr;
            threadId_ = 0;
        }
    }
}
//==============================================================================
bool Thread::setPriority (const int priority)
{
    const ScopedLock sl (startStopLock);
    if (setThreadPriority (threadHandle_, priority))
    {
        threadPriority_ = priority;
        return true;
    }
    return false;
}
bool Thread::setCurrentThreadPriority (const int priority)
{
    return setThreadPriority (0, priority);
}
void Thread::setAffinityMask (const uint32 affinityMask)
{
    affinityMask_ = affinityMask;
}
//==============================================================================
bool Thread::wait (const int timeOutMilliseconds) const
{
    return defaultEvent_.wait (timeOutMilliseconds);
}
void Thread::notify() const
{
    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);
}
//==============================================================================
void SpinLock::enter() const noexcept
{
    if (! tryEnter())
    {
        for (int i = 20; --i >= 0;)
            if (tryEnter())
                return;
        while (! tryEnter())
            Thread::yield();
    }
}
//==============================================================================
#if JUCE_UNIT_TESTS
class AtomicTests  : public UnitTest
{
public:
    AtomicTests() : UnitTest ("Atomics") {}
    void runTest()
    {
        beginTest ("Misc");
        char a1[7];
        expect (numElementsInArray(a1) == 7);
        int a2[3];
        expect (numElementsInArray(a2) == 3);
        expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211);
        expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211);
        expect (ByteOrder::swap ((uint64) literal64bit (0x1122334455667788)) == literal64bit (0x8877665544332211));
        beginTest ("Atomic int");
        AtomicTester <int>::testInteger (*this);
        beginTest ("Atomic unsigned int");
        AtomicTester <unsigned int>::testInteger (*this);
        beginTest ("Atomic int32");
        AtomicTester <int32>::testInteger (*this);
        beginTest ("Atomic uint32");
        AtomicTester <uint32>::testInteger (*this);
        beginTest ("Atomic long");
        AtomicTester <long>::testInteger (*this);
        beginTest ("Atomic void*");
        AtomicTester <void*>::testInteger (*this);
        beginTest ("Atomic int*");
        AtomicTester <int*>::testInteger (*this);
        beginTest ("Atomic float");
        AtomicTester <float>::testFloat (*this);
      #if ! JUCE_64BIT_ATOMICS_UNAVAILABLE  // 64-bit intrinsics aren't available on some old platforms
        beginTest ("Atomic int64");
        AtomicTester <int64>::testInteger (*this);
        beginTest ("Atomic uint64");
        AtomicTester <uint64>::testInteger (*this);
        beginTest ("Atomic double");
        AtomicTester <double>::testFloat (*this);
      #endif
    }
    template <typename Type>
    class AtomicTester
    {
    public:
        AtomicTester() {}
        static void testInteger (UnitTest& test)
        {
            Atomic<Type> a, b;
            a.set ((Type) 10);
            test.expect (a.value == (Type) 10);
            test.expect (a.get() == (Type) 10);
            a += (Type) 15;
            test.expect (a.get() == (Type) 25);
            a.memoryBarrier();
            a -= (Type) 5;
            test.expect (a.get() == (Type) 20);
            test.expect (++a == (Type) 21);
            ++a;
            test.expect (--a == (Type) 21);
            test.expect (a.get() == (Type) 21);
            a.memoryBarrier();
            testFloat (test);
        }
        static void testFloat (UnitTest& test)
        {
            Atomic<Type> a, b;
            a = (Type) 21;
            a.memoryBarrier();
            /*  These are some simple test cases to check the atomics - let me know
                if any of these assertions fail on your system!
            */
            test.expect (a.get() == (Type) 21);
            test.expect (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21);
            test.expect (a.get() == (Type) 21);
            test.expect (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21);
            test.expect (a.get() == (Type) 101);
            test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200));
            test.expect (a.get() == (Type) 101);
            test.expect (a.compareAndSetBool ((Type) 200, a.get()));
            test.expect (a.get() == (Type) 200);
            test.expect (a.exchange ((Type) 300) == (Type) 200);
            test.expect (a.get() == (Type) 300);
            b = a;
            test.expect (b.get() == a.get());
        }
    };
};
static AtomicTests atomicUnitTests;
#endif
END_JUCE_NAMESPACE
 |