[code]#include “…/…/juce_core/basics/juce_StandardHeader.h”
#include
#include
BEGIN_JUCE_NAMESPACE
#include “juce_AsyncUpdater.h”
#include “…/…/juce_core/threads/juce_Thread.h”
#include “…/…/juce_core/threads/juce_WaitableEvent.h”
#include “…/…/juce_core/threads/juce_ScopedLock.h”
#include “juce_MessageManager.h”
#include “…/application/juce_DeletedAtShutdown.h”
template class LockFreeFIFO
{
public:
LockFreeFIFO(size_t size)
: readIndex(0), writeIndex(0), buffer(size)
{
}
void resize(size_t size)
{
buffer.resize(size);
}
size_t size() const { return buffer.size(); }
void pop()
{
if ((readIndex+1) >= buffer.size())
{
readIndex = 0;
}
else
{
readIndex++;
}
}
const Type &front() const
{
return buffer[readIndex];
}
bool push(const Type &object)
{
const size_t newHead = (writeIndex+1)%buffer.size();
if (newHead != readIndex)
{
buffer[writeIndex] = object;
writeIndex = newHead;
return true;
}
return false;
}
bool empty() const
{
if (readIndex == writeIndex)
{
return true;
}
return false;
}
private:
volatile size_t readIndex, writeIndex;
std::vector buffer;
};
class InternalAsyncUpdaterThread :
private Thread,
private MessageListener,
private DeletedAtShutdown
{
private:
friend class AsyncUpdater;
static InternalAsyncUpdaterThread* instance;
CriticalSection lock;
WaitableEvent waitableEvent;
LockFreeFIFO<AsyncUpdater*> fifo;
std::set<AsyncUpdater*> asyncUpdaters;
InternalAsyncUpdaterThread (const InternalAsyncUpdaterThread&);
const InternalAsyncUpdaterThread& operator= (const InternalAsyncUpdaterThread&);
public:
void addAsyncUpdater (AsyncUpdater* const a)
{
jassert(MessageManager::getInstance()->isThisTheMessageThread());
asyncUpdaters.insert(a);
if (fifo.size() < asyncUpdaters.size())
{
ScopedLock l(lock);
fifo.resize(asyncUpdaters.size());
}
}
void removeAsyncUpdater (AsyncUpdater* const a)
{
jassert(MessageManager::getInstance()->isThisTheMessageThread());
asyncUpdaters.erase(a);
}
InternalAsyncUpdaterThread()
: Thread (“Juce AsyncUpdater”), fifo(1024)
{
startThread ();
}
~InternalAsyncUpdaterThread()
{
signalThreadShouldExit();
waitableEvent.signal();
waitForThreadToExit(-1);
jassert (instance == this || instance == 0);
if (instance == this)
instance = 0;
}
void post(AsyncUpdater* const a)
{
ScopedLock l(lock);
bool res = fifo.push(a); // there can’t be another instance of the AsyncUpdater in the fifo as asyncMessagePending would be true so post won’t be called
jassert(res);
waitableEvent.signal();
}
virtual void handleMessage(const Message &)
{
jassert(MessageManager::getInstance()->isThisTheMessageThread());
while(!fifo.empty())
{
AsyncUpdater* a = fifo.front();
fifo.pop();
if (asyncUpdaters.find(a) != asyncUpdaters.end())
{
a->handleUpdateNowIfNeeded();
}
}
}
void run()
{
while (!threadShouldExit())
{
waitableEvent.wait();
if (!threadShouldExit())
{
postMessage(new Message());
}
}
}
static bool hasInstance()
{
return (instance != 0);
}
static InternalAsyncUpdaterThread* getInstance()
{
if (instance == 0)
instance = new InternalAsyncUpdaterThread();
return instance;
}
};
InternalAsyncUpdaterThread* InternalAsyncUpdaterThread::instance = 0;
//==============================================================================
AsyncUpdater::AsyncUpdater() throw()
: asyncMessagePending (false)
{
InternalAsyncUpdaterThread::getInstance()->addAsyncUpdater(this);
}
AsyncUpdater::~AsyncUpdater()
{
if (InternalAsyncUpdaterThread::hasInstance()) // might happen if another DeletedAtShutdown inherit from AsyncUpdater(like juce::Desktop)
{
InternalAsyncUpdaterThread::getInstance()->removeAsyncUpdater(this);
}
}
void AsyncUpdater::triggerAsyncUpdate() throw()
{
if (! asyncMessagePending)
{
asyncMessagePending = true;
InternalAsyncUpdaterThread::getInstance()->post(this);
}
}
void AsyncUpdater::cancelPendingUpdate() throw()
{
asyncMessagePending = false;
}
void AsyncUpdater::handleUpdateNowIfNeeded()
{
if (asyncMessagePending)
{
asyncMessagePending = false;
handleAsyncUpdate();
}
}
END_JUCE_NAMESPACE
[/code]
I was not really happy with the timer solution, so I’ve come up with this.
I wasn’t able to do a real lock free fifo as I was lacking something like juce_atomic_set
Comments are welcomed.
Thanks,