Bug 1465108 - Use function pointers rather than virtual run method for GC parallel tasks r=sfink a=abillings a=RyanVM

This commit is contained in:
Jon Coppeard 2018-05-10 10:09:31 +01:00 committed by Roy Tam
commit 89e332cfec
6 changed files with 72 additions and 37 deletions

View file

@ -73,7 +73,7 @@ class ChunkPool
// Performs extra allocation off the main thread so that when memory is
// required on the main thread it will already be available and waiting.
class BackgroundAllocTask : public GCParallelTask
class BackgroundAllocTask : public GCParallelTaskHelper<BackgroundAllocTask>
{
// Guarded by the GC lock.
JSRuntime* runtime;
@ -85,12 +85,11 @@ class BackgroundAllocTask : public GCParallelTask
BackgroundAllocTask(JSRuntime* rt, ChunkPool& pool);
bool enabled() const { return enabled_; }
protected:
void run() override;
void run();
};
// Search the provided Chunks for free arenas and decommit them.
class BackgroundDecommitTask : public GCParallelTask
// Search the provided Chunks for free arenas and recommit them.
class BackgroundDecommitTask : public GCParallelTaskHelper<BackgroundDecommitTask>
{
public:
using ChunkVector = mozilla::Vector<Chunk*>;
@ -98,8 +97,7 @@ class BackgroundDecommitTask : public GCParallelTask
explicit BackgroundDecommitTask(JSRuntime *rt) : runtime(rt) {}
void setChunksToScan(ChunkVector &chunks);
protected:
void run() override;
void run();
private:
JSRuntime* runtime;
@ -1171,8 +1169,10 @@ class GCRuntime
/*
* Concurrent sweep infrastructure.
*/
void startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked);
void joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked);
void startTask(GCParallelTask& task, gcstats::Phase phase,
AutoLockHelperThreadState& locked);
void joinTask(GCParallelTask& task, gcstats::Phase phase,
AutoLockHelperThreadState& locked);
/*
* List head of arenas allocated during the sweep phase.

View file

@ -43,19 +43,19 @@ using mozilla::PodZero;
static const uintptr_t CanaryMagicValue = 0xDEADB15D;
struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTask
struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTaskHelper<FreeMallocedBuffersTask>
{
explicit FreeMallocedBuffersTask(FreeOp* fop) : fop_(fop) {}
bool init() { return buffers_.init(); }
void transferBuffersToFree(MallocedBuffersSet& buffersToFree,
const AutoLockHelperThreadState& lock);
~FreeMallocedBuffersTask() override { join(); }
~FreeMallocedBuffersTask() { join(); }
void run();
private:
FreeOp* fop_;
MallocedBuffersSet buffers_;
virtual void run() override;
};
struct js::Nursery::SweepAction

View file

@ -22,9 +22,6 @@
using mozilla::Maybe;
namespace js {
class GCParallelTask;
namespace gcstats {
enum Phase : uint8_t {

View file

@ -2156,7 +2156,7 @@ ArenasToUpdate::getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned maxL
return { begin, last->next };
}
struct UpdatePointersTask : public GCParallelTask
struct UpdatePointersTask : public GCParallelTaskHelper<UpdatePointersTask>
{
// Maximum number of arenas to update in one block.
#ifdef DEBUG
@ -2172,14 +2172,13 @@ struct UpdatePointersTask : public GCParallelTask
arenas_.end = nullptr;
}
~UpdatePointersTask() override { join(); }
void run();
private:
JSRuntime* rt_;
ArenasToUpdate* source_;
ArenaListSegment arenas_;
virtual void run() override;
bool getArenasToUpdate();
void updateArenas();
};
@ -2985,7 +2984,6 @@ js::gc::BackgroundDecommitTask::run()
AutoLockGC lock(runtime);
for (Chunk* chunk : toDecommit) {
// The arena list is not doubly-linked, so we have to work in the free
// list order and not in the natural order.
while (chunk->info.numArenasFreeCommitted) {
@ -4359,7 +4357,8 @@ GCRuntime::endMarkingZoneGroup()
marker.setMarkColorBlack();
}
class GCSweepTask : public GCParallelTask
template <typename Derived>
class GCSweepTask : public GCParallelTaskHelper<Derived>
{
GCSweepTask(const GCSweepTask&) = delete;
@ -4369,13 +4368,13 @@ class GCSweepTask : public GCParallelTask
public:
explicit GCSweepTask(JSRuntime* rt) : runtime(rt) {}
GCSweepTask(GCSweepTask&& other)
: GCParallelTask(mozilla::Move(other)),
: GCParallelTaskHelper<Derived>(mozilla::Move(other)),
runtime(other.runtime)
{}
};
// Causes the given WeakCache to be swept when run.
class SweepWeakCacheTask : public GCSweepTask
class SweepWeakCacheTask : public GCSweepTask<SweepWeakCacheTask>
{
JS::WeakCache<void*>& cache;
@ -4387,15 +4386,15 @@ class SweepWeakCacheTask : public GCSweepTask
: GCSweepTask(mozilla::Move(other)), cache(other.cache)
{}
void run() override {
void run() {
cache.sweep();
}
};
#define MAKE_GC_SWEEP_TASK(name) \
class name : public GCSweepTask { \
void run() override; \
class name : public GCSweepTask<name> { \
public: \
void run(); \
explicit name (JSRuntime* rt) : GCSweepTask(rt) {} \
}
MAKE_GC_SWEEP_TASK(SweepAtomsTask);
@ -4447,7 +4446,8 @@ SweepMiscTask::run()
}
void
GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked)
GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase,
AutoLockHelperThreadState& locked)
{
if (!task.startWithLockHeld(locked)) {
AutoUnlockHelperThreadState unlock(locked);
@ -4457,7 +4457,8 @@ GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperT
}
void
GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked)
GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase,
AutoLockHelperThreadState& locked)
{
gcstats::AutoPhase ap(stats, task, phase);
task.joinWithLockHeld(locked);

View file

@ -12,6 +12,7 @@
#include "mozilla/Atomics.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Move.h"
#include "mozilla/TypeTraits.h"
#include "js/GCAPI.h"
@ -936,10 +937,19 @@ class GCHelperState
};
// A generic task used to dispatch work to the helper thread system.
// Users should derive from GCParallelTask add what data they need and
// override |run|.
// Users supply a function pointer to call.
//
// Note that we don't use virtual functions here because destructors can write
// the vtable pointer on entry, which can causes races if synchronization
// happens there.
class GCParallelTask
{
public:
using TaskFunc = void (*)(GCParallelTask*);
private:
TaskFunc func_;
// The state of the parallel computation.
enum TaskState {
NotStarted,
@ -956,19 +966,24 @@ class GCParallelTask
// A flag to signal a request for early completion of the off-thread task.
mozilla::Atomic<bool> cancel_;
virtual void run() = 0;
public:
GCParallelTask() : state(NotStarted), duration_(0) {}
explicit GCParallelTask(TaskFunc func)
: func_(func),
state(NotStarted),
duration_(0),
cancel_(false)
{}
GCParallelTask(GCParallelTask&& other)
: state(other.state),
: func_(other.func_),
state(other.state),
duration_(0),
cancel_(false)
{}
// Derived classes must override this to ensure that join() gets called
// before members get destructed.
virtual ~GCParallelTask();
~GCParallelTask();
// Time spent in the most recent invocation of this task.
int64_t duration() const { return duration_; }
@ -997,12 +1012,34 @@ class GCParallelTask
bool isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const;
bool isRunning() const;
void runTask() {
func_(this);
}
// This should be friended to HelperThread, but cannot be because it
// would introduce several circular dependencies.
public:
void runFromHelperThread(AutoLockHelperThreadState& locked);
};
// CRTP template to handle cast to derived type when calling run().
template <typename Derived>
class GCParallelTaskHelper : public GCParallelTask
{
public:
GCParallelTaskHelper()
: GCParallelTask(&runTaskTyped)
{}
GCParallelTaskHelper(GCParallelTaskHelper&& other)
: GCParallelTask(mozilla::Move(other))
{}
private:
static void runTaskTyped(GCParallelTask* task) {
static_cast<Derived*>(task)->run();
}
};
typedef void (*IterateChunkCallback)(JSRuntime* rt, void* data, gc::Chunk* chunk);
typedef void (*IterateZoneCallback)(JSRuntime* rt, void* data, JS::Zone* zone);
typedef void (*IterateArenaCallback)(JSRuntime* rt, void* data, gc::Arena* arena,

View file

@ -1144,7 +1144,7 @@ js::GCParallelTask::runFromMainThread(JSRuntime* rt)
MOZ_ASSERT(state == NotStarted);
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
uint64_t timeStart = PRMJ_Now();
run();
runTask();
duration_ = PRMJ_Now() - timeStart;
}
@ -1155,7 +1155,7 @@ js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
AutoUnlockHelperThreadState parallelSection(locked);
gc::AutoSetThreadIsPerformingGC performingGC;
uint64_t timeStart = PRMJ_Now();
run();
runTask();
duration_ = PRMJ_Now() - timeStart;
}