#include "config.h"
#include "WasmWorklist.h"
#if ENABLE(WEBASSEMBLY)
#include "WasmPlan.h"
#include <wtf/NumberOfCores.h>
namespace JSC { namespace Wasm {
static const bool verbose = false;
const char* Worklist::priorityString(Priority priority)
{
switch (priority) {
case Priority::Preparation: return "Preparation";
case Priority::Shutdown: return "Shutdown";
case Priority::Compilation: return "Compilation";
case Priority::Synchronous: return "Synchronous";
}
RELEASE_ASSERT_NOT_REACHED();
}
class Worklist::Thread final : public AutomaticThread {
public:
using Base = AutomaticThread;
Thread(const AbstractLocker& locker, Worklist& work)
: Base(locker, work.m_lock, work.m_planEnqueued)
, worklist(work)
{
}
protected:
PollResult poll(const AbstractLocker&) override
{
auto& queue = worklist.m_queue;
synchronize.notifyAll();
while (!queue.isEmpty()) {
Priority priority = queue.peek().priority;
if (priority == Worklist::Priority::Shutdown)
return PollResult::Stop;
element = queue.peek();
if (!queue.peek().plan->multiThreaded())
queue.dequeue();
if (element.plan->hasWork())
return PollResult::Work;
queue.dequeue();
element = QueueElement();
}
return PollResult::Wait;
}
WorkResult work() override
{
auto complete = [&] (const AbstractLocker&) {
element = QueueElement();
return WorkResult::Continue;
};
Plan* plan = element.plan.get();
ASSERT(plan);
bool wasMultiThreaded = plan->multiThreaded();
plan->work(Plan::Partial);
ASSERT(!plan->hasWork() || plan->multiThreaded());
if (plan->hasWork() && !wasMultiThreaded && plan->multiThreaded()) {
LockHolder locker(*worklist.m_lock);
element.setToNextPriority();
worklist.m_queue.enqueue(WTFMove(element));
worklist.m_planEnqueued->notifyAll(locker);
return complete(locker);
}
return complete(holdLock(*worklist.m_lock));
}
public:
Condition synchronize;
Worklist& worklist;
QueueElement element;
};
void Worklist::QueueElement::setToNextPriority()
{
switch (priority) {
case Priority::Preparation:
priority = Priority::Compilation;
return;
case Priority::Synchronous:
return;
default:
break;
}
RELEASE_ASSERT_NOT_REACHED();
}
void Worklist::enqueue(Ref<Plan> plan)
{
LockHolder locker(*m_lock);
if (!ASSERT_DISABLED) {
for (const auto& element : m_queue)
ASSERT_UNUSED(element, element.plan.get() != &plan.get());
}
dataLogLnIf(verbose, "Enqueuing plan");
m_queue.enqueue({ Priority::Preparation, nextTicket(), WTFMove(plan) });
m_planEnqueued->notifyOne(locker);
}
void Worklist::completePlanSynchronously(Plan& plan)
{
{
LockHolder locker(*m_lock);
m_queue.decreaseKey([&] (QueueElement& element) {
if (element.plan == &plan) {
element.priority = Priority::Synchronous;
return true;
}
return false;
});
for (auto& thread : m_threads) {
if (thread->element.plan == &plan)
thread->element.priority = Priority::Synchronous;
}
}
plan.waitForCompletion();
}
void Worklist::stopAllPlansForVM(VM& vm)
{
LockHolder locker(*m_lock);
Vector<QueueElement> elements;
while (!m_queue.isEmpty()) {
QueueElement element = m_queue.dequeue();
bool didCancel = element.plan->tryRemoveVMAndCancelIfLast(vm);
if (!didCancel)
elements.append(WTFMove(element));
}
for (auto& element : elements)
m_queue.enqueue(WTFMove(element));
for (auto& thread : m_threads) {
if (thread->element.plan) {
bool didCancel = thread->element.plan->tryRemoveVMAndCancelIfLast(vm);
if (didCancel) {
thread->synchronize.wait(*m_lock);
}
}
}
}
Worklist::Worklist()
: m_lock(Box<Lock>::create())
, m_planEnqueued(AutomaticThreadCondition::create())
{
unsigned numberOfCompilationThreads = Options::useConcurrentJIT() ? WTF::numberOfProcessorCores() : 1;
m_threads.reserveCapacity(numberOfCompilationThreads);
LockHolder locker(*m_lock);
for (unsigned i = 0; i < numberOfCompilationThreads; i++)
m_threads.uncheckedAppend(std::make_unique<Worklist::Thread>(locker, *this));
}
Worklist::~Worklist()
{
{
LockHolder locker(*m_lock);
m_queue.enqueue({ Priority::Shutdown, nextTicket(), nullptr });
m_planEnqueued->notifyAll(locker);
}
for (unsigned i = 0; i < m_threads.size(); ++i)
m_threads[i]->join();
}
static Worklist* globalWorklist;
Worklist* existingWorklistOrNull() { return globalWorklist; }
Worklist& ensureWorklist()
{
static std::once_flag initializeWorklist;
std::call_once(initializeWorklist, [] {
globalWorklist = new Worklist();
});
return *globalWorklist;
}
} }
#endif // ENABLE(WEBASSEMBLY)