CompositingRunLoop.cpp [plain text]
#include "config.h"
#include "CompositingRunLoop.h"
#if USE(COORDINATED_GRAPHICS_THREADED)
#include <wtf/HashMap.h>
#include <wtf/MainThread.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/WorkQueue.h>
#if USE(GLIB_EVENT_LOOP)
#include <wtf/glib/RunLoopSourcePriority.h>
#endif
namespace WebKit {
class WorkQueuePool {
WTF_MAKE_NONCOPYABLE(WorkQueuePool);
friend class NeverDestroyed<WorkQueuePool>;
public:
static WorkQueuePool& singleton()
{
ASSERT(RunLoop::isMain());
static NeverDestroyed<WorkQueuePool> workQueuePool;
return workQueuePool;
}
void dispatch(void* context, Function<void ()>&& function)
{
ASSERT(RunLoop::isMain());
getOrCreateWorkQueueForContext(context).dispatch(WTFMove(function));
}
RunLoop& runLoop(void* context)
{
return getOrCreateWorkQueueForContext(context).runLoop();
}
void invalidate(void* context)
{
auto workQueue = m_workQueueMap.take(context);
ASSERT(workQueue);
if (m_workQueueMap.isEmpty()) {
m_sharedWorkQueue = nullptr;
m_threadCount = 0;
} else if (workQueue->hasOneRef())
m_threadCount--;
}
private:
WorkQueuePool()
{
m_threadCountLimit = 1;
}
WorkQueue& getOrCreateWorkQueueForContext(void* context)
{
auto addResult = m_workQueueMap.add(context, nullptr);
if (addResult.isNewEntry) {
if (m_threadCount >= m_threadCountLimit) {
ASSERT(m_sharedWorkQueue);
addResult.iterator->value = m_sharedWorkQueue;
} else {
addResult.iterator->value = WorkQueue::create("org.webkit.ThreadedCompositorWorkQueue");
if (!m_threadCount)
m_sharedWorkQueue = addResult.iterator->value;
m_threadCount++;
}
}
return *addResult.iterator->value;
}
HashMap<void*, RefPtr<WorkQueue>> m_workQueueMap;
RefPtr<WorkQueue> m_sharedWorkQueue;
unsigned m_threadCount { 0 };
unsigned m_threadCountLimit;
};
CompositingRunLoop::CompositingRunLoop(Function<void ()>&& updateFunction)
: m_updateTimer(WorkQueuePool::singleton().runLoop(this), this, &CompositingRunLoop::updateTimerFired)
, m_updateFunction(WTFMove(updateFunction))
{
#if USE(GLIB_EVENT_LOOP)
m_updateTimer.setPriority(RunLoopSourcePriority::CompositingThreadUpdateTimer);
m_updateTimer.setName("[WebKit] CompositingRunLoop");
#endif
}
CompositingRunLoop::~CompositingRunLoop()
{
ASSERT(RunLoop::isMain());
RunLoop::main().dispatch([context = this] { WorkQueuePool::singleton().invalidate(context); });
}
void CompositingRunLoop::performTask(Function<void ()>&& function)
{
ASSERT(RunLoop::isMain());
WorkQueuePool::singleton().dispatch(this, WTFMove(function));
}
void CompositingRunLoop::performTaskSync(Function<void ()>&& function)
{
ASSERT(RunLoop::isMain());
LockHolder locker(m_dispatchSyncConditionMutex);
WorkQueuePool::singleton().dispatch(this, [this, function = WTFMove(function)] {
function();
LockHolder locker(m_dispatchSyncConditionMutex);
m_dispatchSyncCondition.notifyOne();
});
m_dispatchSyncCondition.wait(m_dispatchSyncConditionMutex);
}
void CompositingRunLoop::scheduleUpdate()
{
LockHolder stateLocker(m_state.lock);
scheduleUpdate(stateLocker);
}
void CompositingRunLoop::scheduleUpdate(LockHolder& stateLocker)
{
UNUSED_PARAM(stateLocker);
switch (m_state.update) {
case UpdateState::Idle:
m_state.update = UpdateState::Scheduled;
m_updateTimer.startOneShot(0);
return;
case UpdateState::Scheduled:
return;
case UpdateState::InProgress:
case UpdateState::PendingCompletion:
m_state.pendingUpdate = true;
return;
}
}
void CompositingRunLoop::stopUpdates()
{
LockHolder locker(m_state.lock);
m_updateTimer.stop();
m_state.composition = CompositionState::Idle;
m_state.update = UpdateState::Idle;
m_state.pendingUpdate = false;
}
void CompositingRunLoop::compositionCompleted(LockHolder& stateLocker)
{
UNUSED_PARAM(stateLocker);
m_state.composition = CompositionState::Idle;
switch (m_state.update) {
case UpdateState::Idle:
case UpdateState::Scheduled:
case UpdateState::InProgress:
return;
case UpdateState::PendingCompletion:
if (m_state.pendingUpdate) {
m_state.pendingUpdate = false;
m_state.update = UpdateState::Scheduled;
m_updateTimer.startOneShot(0);
return;
}
m_state.update = UpdateState::Idle;
return;
}
}
void CompositingRunLoop::updateCompleted(LockHolder& stateLocker)
{
UNUSED_PARAM(stateLocker);
switch (m_state.update) {
case UpdateState::Idle:
case UpdateState::Scheduled:
return;
case UpdateState::InProgress:
if (m_state.composition == CompositionState::InProgress) {
m_state.update = UpdateState::PendingCompletion;
return;
}
if (m_state.pendingUpdate) {
m_state.pendingUpdate = false;
m_state.update = UpdateState::Scheduled;
m_updateTimer.startOneShot(0);
return;
}
m_state.update = UpdateState::Idle;
return;
case UpdateState::PendingCompletion:
return;
}
}
void CompositingRunLoop::updateTimerFired()
{
{
LockHolder locker(m_state.lock);
m_state.composition = CompositionState::InProgress;
m_state.update = UpdateState::InProgress;
}
m_updateFunction();
}
}
#endif // USE(COORDINATED_GRAPHICS_THREADED)