blob: 86406f38843714cb255c34eae3f9181daba58e39 [file] [log] [blame]
/*
* Copyright (C) 2010 Apple Inc. All rights reserved.
* Copyright (C) 2017 Sony Interactive Entertainment Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <wtf/WorkQueue.h>
#include <mutex>
#include <wtf/Condition.h>
#include <wtf/Deque.h>
#include <wtf/Function.h>
#include <wtf/Lock.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/NumberOfCores.h>
#include <wtf/Ref.h>
#include <wtf/Threading.h>
#include <wtf/threads/BinarySemaphore.h>
namespace WTF {
WorkQueue& WorkQueue::main()
{
static NeverDestroyed<RefPtr<WorkQueue>> mainWorkQueue;
static std::once_flag onceKey;
std::call_once(onceKey, [&] {
mainWorkQueue.get() = constructMainWorkQueue();
});
return *mainWorkQueue.get();
}
WorkQueueBase::WorkQueueBase(const char* name, Type type, QOS qos)
{
platformInitialize(name, type, qos);
}
WorkQueueBase::~WorkQueueBase()
{
platformInvalidate();
}
Ref<WorkQueue> WorkQueue::create(const char* name, QOS qos)
{
return adoptRef(*new WorkQueue(name, qos));
}
Ref<ConcurrentWorkQueue> ConcurrentWorkQueue::create(const char* name, QOS qos)
{
return adoptRef(*new ConcurrentWorkQueue(name, qos));
}
#if !PLATFORM(COCOA)
void WorkQueueBase::dispatchSync(Function<void()>&& function)
{
BinarySemaphore semaphore;
dispatch([&semaphore, function = WTFMove(function)]() mutable {
function();
semaphore.signal();
});
semaphore.wait();
}
void WorkQueueBase::dispatchWithQOS(Function<void()>&& function, QOS)
{
dispatch(WTFMove(function));
}
void ConcurrentWorkQueue::apply(size_t iterations, WTF::Function<void(size_t index)>&& function)
{
if (!iterations)
return;
if (iterations == 1) {
function(0);
return;
}
class ThreadPool {
public:
ThreadPool()
{
// We don't need a thread for the current core.
unsigned threadCount = numberOfProcessorCores() - 1;
m_workers.reserveInitialCapacity(threadCount);
for (unsigned i = 0; i < threadCount; ++i) {
m_workers.uncheckedAppend(Thread::create("ThreadPool Worker", [this] {
threadBody();
}));
}
}
size_t workerCount() const { return m_workers.size(); }
void dispatch(const WTF::Function<void ()>* function)
{
Locker locker { m_lock };
m_queue.append(function);
m_condition.notifyOne();
}
private:
NO_RETURN void threadBody()
{
while (true) {
const WTF::Function<void ()>* function;
{
Locker locker { m_lock };
m_condition.wait(m_lock, [this] {
assertIsHeld(m_lock);
return !m_queue.isEmpty();
});
function = m_queue.takeFirst();
}
(*function)();
}
}
Lock m_lock;
Condition m_condition;
Deque<const Function<void()>*> m_queue WTF_GUARDED_BY_LOCK(m_lock);
Vector<Ref<Thread>> m_workers;
};
static LazyNeverDestroyed<ThreadPool> threadPool;
static std::once_flag onceFlag;
std::call_once(onceFlag, [] {
threadPool.construct();
});
// Cap the worker count to the number of iterations (excluding this thread)
const size_t workerCount = std::min(iterations - 1, threadPool->workerCount());
std::atomic<size_t> currentIndex(0);
std::atomic<size_t> activeThreads(workerCount + 1);
Condition condition;
Lock lock;
Function<void ()> applier = [&, function = WTFMove(function)] {
size_t index;
// Call the function for as long as there are iterations left.
while ((index = currentIndex++) < iterations)
function(index);
// If there are no active threads left, signal the caller.
if (!--activeThreads) {
Locker locker { lock };
condition.notifyOne();
}
};
for (size_t i = 0; i < workerCount; ++i)
threadPool->dispatch(&applier);
applier();
Locker locker { lock };
condition.wait(lock, [&] { return !activeThreads; });
}
#endif
}