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

View file

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

View file

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

View file

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

View file

@ -12,6 +12,7 @@
#include "mozilla/Atomics.h" #include "mozilla/Atomics.h"
#include "mozilla/EnumeratedArray.h" #include "mozilla/EnumeratedArray.h"
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
#include "mozilla/Move.h"
#include "mozilla/TypeTraits.h" #include "mozilla/TypeTraits.h"
#include "js/GCAPI.h" #include "js/GCAPI.h"
@ -936,10 +937,19 @@ class GCHelperState
}; };
// A generic task used to dispatch work to the helper thread system. // A generic task used to dispatch work to the helper thread system.
// Users should derive from GCParallelTask add what data they need and // Users supply a function pointer to call.
// override |run|. //
// 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 class GCParallelTask
{ {
public:
using TaskFunc = void (*)(GCParallelTask*);
private:
TaskFunc func_;
// The state of the parallel computation. // The state of the parallel computation.
enum TaskState { enum TaskState {
NotStarted, NotStarted,
@ -956,19 +966,24 @@ class GCParallelTask
// A flag to signal a request for early completion of the off-thread task. // A flag to signal a request for early completion of the off-thread task.
mozilla::Atomic<bool> cancel_; mozilla::Atomic<bool> cancel_;
virtual void run() = 0;
public: public:
GCParallelTask() : state(NotStarted), duration_(0) {} explicit GCParallelTask(TaskFunc func)
: func_(func),
state(NotStarted),
duration_(0),
cancel_(false)
{}
GCParallelTask(GCParallelTask&& other) GCParallelTask(GCParallelTask&& other)
: state(other.state), : func_(other.func_),
state(other.state),
duration_(0), duration_(0),
cancel_(false) cancel_(false)
{} {}
// Derived classes must override this to ensure that join() gets called // Derived classes must override this to ensure that join() gets called
// before members get destructed. // before members get destructed.
virtual ~GCParallelTask(); ~GCParallelTask();
// Time spent in the most recent invocation of this task. // Time spent in the most recent invocation of this task.
int64_t duration() const { return duration_; } int64_t duration() const { return duration_; }
@ -997,12 +1012,34 @@ class GCParallelTask
bool isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const; bool isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const;
bool isRunning() const; bool isRunning() const;
void runTask() {
func_(this);
}
// This should be friended to HelperThread, but cannot be because it // This should be friended to HelperThread, but cannot be because it
// would introduce several circular dependencies. // would introduce several circular dependencies.
public: public:
void runFromHelperThread(AutoLockHelperThreadState& locked); 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 (*IterateChunkCallback)(JSRuntime* rt, void* data, gc::Chunk* chunk);
typedef void (*IterateZoneCallback)(JSRuntime* rt, void* data, JS::Zone* zone); typedef void (*IterateZoneCallback)(JSRuntime* rt, void* data, JS::Zone* zone);
typedef void (*IterateArenaCallback)(JSRuntime* rt, void* data, gc::Arena* arena, 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(state == NotStarted);
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt)); MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
uint64_t timeStart = PRMJ_Now(); uint64_t timeStart = PRMJ_Now();
run(); runTask();
duration_ = PRMJ_Now() - timeStart; duration_ = PRMJ_Now() - timeStart;
} }
@ -1155,7 +1155,7 @@ js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
AutoUnlockHelperThreadState parallelSection(locked); AutoUnlockHelperThreadState parallelSection(locked);
gc::AutoSetThreadIsPerformingGC performingGC; gc::AutoSetThreadIsPerformingGC performingGC;
uint64_t timeStart = PRMJ_Now(); uint64_t timeStart = PRMJ_Now();
run(); runTask();
duration_ = PRMJ_Now() - timeStart; duration_ = PRMJ_Now() - timeStart;
} }