MarkingConstraintSolver.cpp [plain text]
#include "config.h"
#include "MarkingConstraintSolver.h"
#include "JSCInlines.h"
#include "MarkingConstraintSet.h"
namespace JSC {
MarkingConstraintSolver::MarkingConstraintSolver(MarkingConstraintSet& set)
: m_heap(set.m_heap)
, m_mainVisitor(m_heap.collectorSlotVisitor())
, m_set(set)
{
m_heap.forEachSlotVisitor(
[&] (SlotVisitor& visitor) {
m_visitCounters.append(VisitCounter(visitor));
});
}
MarkingConstraintSolver::~MarkingConstraintSolver()
{
}
bool MarkingConstraintSolver::didVisitSomething() const
{
for (const VisitCounter& visitCounter : m_visitCounters) {
if (visitCounter.visitCount())
return true;
}
return false;
}
void MarkingConstraintSolver::execute(SchedulerPreference preference, ScopedLambda<Optional<unsigned>()> pickNext)
{
m_pickNextIsStillActive = true;
RELEASE_ASSERT(!m_numThreadsThatMayProduceWork);
if (Options::useParallelMarkingConstraintSolver()) {
dataLogIf(Options::logGC(), preference == ParallelWorkFirst ? "P" : "N", "<");
m_heap.runFunctionInParallel(
[&] (SlotVisitor& visitor) { runExecutionThread(visitor, preference, pickNext); });
dataLogIf(Options::logGC(), ">");
} else
runExecutionThread(m_mainVisitor, preference, pickNext);
RELEASE_ASSERT(!m_pickNextIsStillActive);
RELEASE_ASSERT(!m_numThreadsThatMayProduceWork);
if (!m_toExecuteSequentially.isEmpty()) {
for (unsigned indexToRun : m_toExecuteSequentially)
execute(*m_set.m_set[indexToRun]);
m_toExecuteSequentially.clear();
}
RELEASE_ASSERT(m_toExecuteInParallel.isEmpty());
}
void MarkingConstraintSolver::drain(BitVector& unexecuted)
{
auto iter = unexecuted.begin();
auto end = unexecuted.end();
if (iter == end)
return;
auto pickNext = scopedLambda<Optional<unsigned>()>(
[&] () -> Optional<unsigned> {
if (iter == end)
return WTF::nullopt;
return *iter++;
});
execute(NextConstraintFirst, pickNext);
unexecuted.clearAll();
}
void MarkingConstraintSolver::converge(const Vector<MarkingConstraint*>& order)
{
if (didVisitSomething())
return;
if (order.isEmpty())
return;
size_t index = 0;
if (order[index]->quickWorkEstimate(m_mainVisitor) > 0.) {
execute(*order[index++]);
if (m_toExecuteInParallel.isEmpty()
&& (order.isEmpty() || didVisitSomething()))
return;
}
auto pickNext = scopedLambda<Optional<unsigned>()>(
[&] () -> Optional<unsigned> {
if (didVisitSomething())
return WTF::nullopt;
if (index >= order.size())
return WTF::nullopt;
MarkingConstraint& constraint = *order[index++];
return constraint.index();
});
execute(ParallelWorkFirst, pickNext);
}
void MarkingConstraintSolver::execute(MarkingConstraint& constraint)
{
if (m_executed.get(constraint.index()))
return;
constraint.prepareToExecute(NoLockingNecessary, m_mainVisitor);
constraint.execute(m_mainVisitor);
m_executed.set(constraint.index());
}
void MarkingConstraintSolver::addParallelTask(RefPtr<SharedTask<void(SlotVisitor&)>> task, MarkingConstraint& constraint)
{
auto locker = holdLock(m_lock);
m_toExecuteInParallel.append(TaskWithConstraint(WTFMove(task), &constraint));
}
void MarkingConstraintSolver::runExecutionThread(SlotVisitor& visitor, SchedulerPreference preference, ScopedLambda<Optional<unsigned>()> pickNext)
{
for (;;) {
bool doParallelWorkMode;
MarkingConstraint* constraint = nullptr;
unsigned indexToRun = UINT_MAX;
TaskWithConstraint task;
{
auto locker = holdLock(m_lock);
for (;;) {
auto tryParallelWork = [&] () -> bool {
if (m_toExecuteInParallel.isEmpty())
return false;
task = m_toExecuteInParallel.first();
constraint = task.constraint;
doParallelWorkMode = true;
return true;
};
auto tryNextConstraint = [&] () -> bool {
if (!m_pickNextIsStillActive)
return false;
for (;;) {
Optional<unsigned> pickResult = pickNext();
if (!pickResult) {
m_pickNextIsStillActive = false;
return false;
}
if (m_executed.get(*pickResult))
continue;
MarkingConstraint& candidateConstraint = *m_set.m_set[*pickResult];
if (candidateConstraint.concurrency() == ConstraintConcurrency::Sequential) {
m_toExecuteSequentially.append(*pickResult);
continue;
}
if (candidateConstraint.parallelism() == ConstraintParallelism::Parallel)
m_numThreadsThatMayProduceWork++;
indexToRun = *pickResult;
constraint = &candidateConstraint;
doParallelWorkMode = false;
constraint->prepareToExecute(locker, visitor);
return true;
}
};
if (preference == ParallelWorkFirst) {
if (tryParallelWork() || tryNextConstraint())
break;
} else {
if (tryNextConstraint() || tryParallelWork())
break;
}
if (!m_numThreadsThatMayProduceWork)
return;
m_condition.wait(m_lock);
}
}
if (doParallelWorkMode)
constraint->doParallelWork(visitor, *task.task);
else {
if (constraint->parallelism() == ConstraintParallelism::Parallel) {
visitor.m_currentConstraint = constraint;
visitor.m_currentSolver = this;
}
constraint->execute(visitor);
visitor.m_currentConstraint = nullptr;
visitor.m_currentSolver = nullptr;
}
{
auto locker = holdLock(m_lock);
if (doParallelWorkMode) {
if (!m_toExecuteInParallel.isEmpty()
&& task == m_toExecuteInParallel.first())
m_toExecuteInParallel.takeFirst();
else
ASSERT(!m_toExecuteInParallel.contains(task));
} else {
if (constraint->parallelism() == ConstraintParallelism::Parallel)
m_numThreadsThatMayProduceWork--;
m_executed.set(indexToRun);
}
m_condition.notifyAll();
}
}
}
}