Bug 1122640 - Free nursery huge slots off main thread r=terrence

This commit is contained in:
Jon Coppeard 2015-02-04 16:12:06 +00:00
parent 103cb3319e
commit e859cbf0d5
5 changed files with 76 additions and 5 deletions

View File

@ -8,6 +8,7 @@
#include "gc/Nursery-inl.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Move.h"
#include "jscompartment.h"
#include "jsgc.h"
@ -36,6 +37,19 @@ using mozilla::ArrayLength;
using mozilla::PodCopy;
using mozilla::PodZero;
struct js::Nursery::FreeHugeSlotsTask : public GCParallelTask
{
FreeHugeSlotsTask(FreeOp *fop) : fop_(fop) {}
bool init() { return slots_.init(); }
void transferSlotsToFree(HugeSlotsSet &slotsToFree);
private:
FreeOp *fop_;
HugeSlotsSet slots_;
virtual void run() MOZ_OVERRIDE;
};
bool
js::Nursery::init(uint32_t maxNurseryBytes)
{
@ -53,6 +67,10 @@ js::Nursery::init(uint32_t maxNurseryBytes)
if (!heap)
return false;
freeHugeSlotsTask = js_new<FreeHugeSlotsTask>(runtime()->defaultFreeOp());
if (!freeHugeSlotsTask || !freeHugeSlotsTask->init())
return false;
heapStart_ = uintptr_t(heap);
heapEnd_ = heapStart_ + nurserySize();
currentStart_ = start();
@ -80,6 +98,8 @@ js::Nursery::~Nursery()
{
if (start())
UnmapPages((void *)start(), nurserySize());
js_delete(freeHugeSlotsTask);
}
void
@ -963,13 +983,48 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, ObjectGroupList
#undef TIME_END
#undef TIME_TOTAL
void
js::Nursery::FreeHugeSlotsTask::transferSlotsToFree(HugeSlotsSet &slotsToFree)
{
// Transfer the contents of the source set to the task's slots_ member by
// swapping the sets, which also clears the source.
MOZ_ASSERT(!isRunning());
MOZ_ASSERT(slots_.empty());
mozilla::Swap(slots_, slotsToFree);
}
void
js::Nursery::FreeHugeSlotsTask::run()
{
for (HugeSlotsSet::Range r = slots_.all(); !r.empty(); r.popFront())
fop_->free_(r.front());
slots_.clear();
}
void
js::Nursery::freeHugeSlots()
{
FreeOp *fop = runtime()->defaultFreeOp();
for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront())
fop->free_(r.front());
hugeSlots.clear();
if (hugeSlots.empty())
return;
bool started;
{
AutoLockHelperThreadState lock;
freeHugeSlotsTask->joinWithLockHeld();
freeHugeSlotsTask->transferSlotsToFree(hugeSlots);
started = freeHugeSlotsTask->startWithLockHeld();
}
if (!started)
freeHugeSlotsTask->runFromMainThread(runtime());
MOZ_ASSERT(hugeSlots.empty());
}
void
js::Nursery::waitBackgroundFreeEnd()
{
freeHugeSlotsTask->join();
}
void

View File

@ -68,7 +68,8 @@ class Nursery
numNurseryChunks_(0),
finalizers_(nullptr),
profileThreshold_(0),
enableProfiling_(false)
enableProfiling_(false),
freeHugeSlotsTask(nullptr)
{}
~Nursery();
@ -141,6 +142,8 @@ class Nursery
setForwardingPointer(oldData, newData, direct);
}
void waitBackgroundFreeEnd();
size_t sizeOfHeapCommitted() const {
return numActiveChunks_ * gc::ChunkSize;
}
@ -224,6 +227,10 @@ class Nursery
typedef HashSet<HeapSlot *, PointerHasher<HeapSlot *, 3>, SystemAllocPolicy> HugeSlotsSet;
HugeSlotsSet hugeSlots;
/* A task structure used to free the huge slots on a background thread. */
struct FreeHugeSlotsTask;
FreeHugeSlotsTask *freeHugeSlotsTask;
/*
* During a collection most hoisted slot and element buffers indicate their
* new location with a forwarding pointer at the base. This does not work

View File

@ -6347,6 +6347,9 @@ GCRuntime::onOutOfMallocMemory()
// Stop allocating new chunks.
allocTask.cancel(GCParallelTask::CancelAndWait);
// Wait for background free of nursery huge slots to finish.
nursery.waitBackgroundFreeEnd();
AutoLockGC lock(rt);
onOutOfMallocMemory(lock);
}

View File

@ -998,6 +998,7 @@ class GCParallelTask
public:
GCParallelTask() : state(NotStarted), duration_(0) {}
virtual ~GCParallelTask();
// Time spent in the most recent invocation of this task.
int64_t duration() const { return duration_; }

View File

@ -738,6 +738,11 @@ GlobalHelperThreadState::canStartGCParallelTask()
return !gcParallelWorklist().empty();
}
js::GCParallelTask::~GCParallelTask()
{
join();
}
bool
js::GCParallelTask::startWithLockHeld()
{