Factor out the GC rooters into per-thread state.
Factor out the GC rooters into per-thread state.
--- a/js/src/builtin/ParallelArray.cpp
+++ b/js/src/builtin/ParallelArray.cpp
@@ -646,23 +646,25 @@ ParallelArrayObject::ParallelArrayTaskSe
return opDefn_.pre(numThreads);
}
template<typename OpDefn, uint32_t MaxArgc>
bool
ParallelArrayObject::ParallelArrayTaskSet<OpDefn, MaxArgc>::parallel(
ThreadContext &threadCx)
{
+ JS::PerThreadData *pt = threadCx.perThreadData;
+
// compute number of arguments
const uint32_t argc = opDefn_.argc();
JS_ASSERT(argc <= MaxArgc); // other compilation should have failed
Value actualArgv[MaxArgc + 1], *argv = actualArgv + 1;
// Set 'callee' and 'this'.
- RootedFunction callee(cx_, elementalFun_->toFunction());
+ RootedFunction callee(pt, elementalFun_->toFunction());
argv[-1] = ObjectValue(*callee);
argv[0] = UndefinedValue();
// find jitcode ptr
IonScript *ion = callee->script()->ionScript(COMPILE_MODE_PAR);
IonCode *code = ion->method();
void *jitcode = code->raw();
EnterIonCode enter = cx_->compartment->ionCompartment()->enterJITInfallible();
@@ -1534,16 +1536,17 @@ ParallelArrayObject::MapOp::MapOp(MapOpD
bool
ParallelArrayObject::MapOp::init() {
return true;
}
bool
ParallelArrayObject::MapOp::initializeArgv(Value *argv, unsigned i) {
+ // XXX should not use cx
if (!getParallelArrayElement(opDefn_.cx, source, i, &elem))
return false;
// Arguments are in eic(h) order.
argv[1] = elem;
argv[2] = Int32Value(i);
argv[3] = ObjectValue(*source);
return true;
@@ -1653,22 +1656,23 @@ ParallelArrayObject::ReduceOp::execute(E
// ReduceOpDefn::pre() ensures that there is at least one element
// for each thread to process:
JS_ASSERT(end - start > 1);
// NB---I am not 100% comfortable with using RootedValue here, but
// there doesn't seem to be an easy alternative.
+ PerThreadData *pt = args.threadCx.perThreadData;
HandleParallelArrayObject source = opDefn_.source;
- RootedValue acc(opDefn_.cx);
+ RootedValue acc(pt);
if (!getParallelArrayElement(opDefn_.cx, source, start, &acc))
return false;
- RootedValue elem(opDefn_.cx);
+ RootedValue elem(pt);
for (unsigned i = start + 1; i < end; i++) {
if (!args.threadCx.check())
return false;
if (!getParallelArrayElement(opDefn_.cx, source, i, &elem))
return false;
args.argv[1] = acc;
@@ -1803,17 +1807,17 @@ ParallelArrayObject::FilterCopyTaskSet::
count += counts_[i];
unsigned length, start, end;
length = source_->outermostDimension();
ComputeTileBounds(threadCx, length, &start, &end);
for (unsigned i = start; i < end; i++) {
Value felem = filter_->getDenseArrayElement(i + filterBase_);
if (ToBoolean(felem)) {
- RootedValue elem(cx_); // FIXME
+ RootedValue elem(threadCx.perThreadData);
if (!getParallelArrayElement(cx_, source_, i, &elem))
return false;
resultBuffer_->setDenseArrayElement(count++, elem);
}
}
return true;
}
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -511,37 +511,35 @@ class RootedBase {};
*/
template <typename T>
class Rooted : public RootedBase<T>
{
void init(JSContext *cxArg)
{
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
ContextFriendFields *cx = ContextFriendFields::get(cxArg);
-
- ThingRootKind kind = RootMethods<T>::kind();
- this->stack = reinterpret_cast<Rooted<T>**>(&cx->thingGCRooters[kind]);
- this->prev = *stack;
- *stack = this;
-
- JS_ASSERT(!RootMethods<T>::poisoned(ptr));
+ commonInit(cx->thingGCRooters);
#endif
}
void init(JSRuntime *rtArg)
{
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
- RuntimeFriendFields *rt = const_cast<RuntimeFriendFields *>(RuntimeFriendFields::get(rtArg));
+ RuntimeFriendFields *rt =
+ const_cast<RuntimeFriendFields *>(RuntimeFriendFields::get(rtArg));
+ commonInit(rt->thingGCRooters);
+#endif
+ }
- ThingRootKind kind = RootMethods<T>::kind();
- this->stack = reinterpret_cast<Rooted<T>**>(&rt->thingGCRooters[kind]);
- this->prev = *stack;
- *stack = this;
-
- JS_ASSERT(!RootMethods<T>::poisoned(ptr));
+ void init(JS::PerThreadData *ptArg)
+ {
+#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
+ PerThreadDataFriendFields *pt =
+ const_cast<PerThreadDataFriendFields *>(PerThreadDataFriendFields::get(ptArg));
+ commonInit(pt->thingGCRooters);
#endif
}
public:
Rooted(JSRuntime *rt
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: ptr(RootMethods<T>::initial())
{
@@ -568,25 +566,50 @@ class Rooted : public RootedBase<T>
Rooted(JSContext *cx, T initial
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: ptr(initial)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
init(cx);
}
+ Rooted(JS::PerThreadData *pt
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : ptr(RootMethods<T>::initial())
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ init(pt);
+ }
+
+ Rooted(JS::PerThreadData *pt, T initial
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : ptr(initial)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ init(pt);
+ }
+
template <typename S>
Rooted(JSContext *cx, const Return<S> &initial
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: ptr(initial.ptr_)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
init(cx);
}
+ template <typename S>
+ Rooted(JS::PerThreadData *pt, const Return<S> &initial
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : ptr(initial.ptr_)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ init(pt);
+ }
+
~Rooted()
{
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
JS_ASSERT(*stack == this);
*stack = prev;
#endif
}
@@ -617,16 +640,27 @@ class Rooted : public RootedBase<T>
template <typename S>
T & operator =(const Return<S> &value)
{
ptr = value.ptr_;
return ptr;
}
private:
+ void commonInit(Rooted<void*> **thingGCRooters) {
+#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
+ ThingRootKind kind = RootMethods<T>::kind();
+ this->stack = reinterpret_cast<Rooted<T>**>(&thingGCRooters[kind]);
+ this->prev = *stack;
+ *stack = this;
+
+ JS_ASSERT(!RootMethods<T>::poisoned(ptr));
+#endif
+ }
+
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
Rooted<T> **stack, *prev;
#endif
T ptr;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
Rooted(const Rooted &) MOZ_DELETE;
};
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -387,17 +387,17 @@ struct JSAtomState
JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD)
#undef PROPERTYNAME_FIELD
};
#define NAME_OFFSET(name) offsetof(JSAtomState, name)
#define OFFSET_TO_NAME(rt,off) (*(js::FixedHeapPtr<js::PropertyName>*)((char*)&(rt)->atomState + (off)))
namespace JS {
-struct PerThreadData
+struct PerThreadData : public js::PerThreadDataFriendFields
{
JSRuntime *runtime; // backpointer to the full shraed runtime
/*
* We save all conservative scanned roots in this vector so that
* conservative scanning can be "replayed" deterministically. In DEBUG mode,
* this allows us to run a non-incremental GC after every incremental GC to
* ensure that no objects were missed.
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -996,16 +996,17 @@ MarkExactStackRooters(JSTracer *trc, Roo
static void
MarkExactStackRoots(JSTracer *trc)
{
for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
MarkExactStackRooters(trc, cx->thingGCRooters[i], ThingRootKind(i));
}
+ MarkExactStackRooters(trc, rt->mainThread.thingGCRooters[i], ThingRootKind(i));
MarkExactStackRooters(trc, rt->thingGCRooters[i], ThingRootKind(i));
}
}
#endif /* JSGC_USE_EXACT_ROOTING */
enum ConservativeGCTest
{
CGCT_VALID,
@@ -4932,16 +4933,18 @@ CheckStackRoot(JSTracer *trc, uintptr_t
ConservativeGCTest test = MarkIfGCThingWord(trc, *w);
if (test == CGCT_VALID) {
bool matched = false;
JSRuntime *rt = trc->runtime;
for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
CheckStackRootThings(w, rt->thingGCRooters[i], ThingRootKind(i), &matched);
+ CheckStackRootThings(w, rt->mainThread.thingGCRooters[i],
+ ThingRootKind(i), &matched);
for (ContextIter cx(rt); !cx.done(); cx.next()) {
CheckStackRootThings(w, cx->thingGCRooters[i], ThingRootKind(i), &matched);
SkipRoot *skip = cx->skipGCRooters;
while (skip) {
if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w)))
matched = true;
skip = skip->previous();
}
--- a/js/src/jsmonitor.h
+++ b/js/src/jsmonitor.h
@@ -11,22 +11,16 @@
#include <stdlib.h>
#include "mozilla/Util.h"
#include "js/Utility.h"
#include "prlock.h"
#include "prcvar.h"
namespace js {
-#ifndef JS_THREADSAFE
-
-class Monitor {};
-
-#else
-
/*
* A base class used for types intended to be used in a parallel
* fashion, such as the workers in the |ThreadPool| class. Combines a
* lock and a condition variable. You can acquire the lock or signal
* the condition variable using the |AutoLockMonitor| type.
*/
class Monitor
{
@@ -78,13 +72,11 @@ class AutoUnlockMonitor
private:
Monitor &monitor;
public:
AutoUnlockMonitor(Monitor &monitor) : monitor(monitor) { PR_Unlock(monitor.lock_); }
~AutoUnlockMonitor() { PR_Lock(monitor.lock_); }
};
-#endif /* def JS_THREADSAFE */
-
}
#endif /* ndef jsmonitor_h___ */
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -14,17 +14,17 @@
#include "jsprototypes.h"
#include "jstypes.h"
/*
* Allow headers to reference JS::Value without #including the whole jsapi.h.
* Unfortunately, typedefs (hence jsval) cannot be declared.
*/
#ifdef __cplusplus
-namespace JS { class Value; }
+namespace JS { class Value; struct PerThreadData; }
#endif
/*
* In release builds, jsid is defined to be an integral type. This
* prevents many bugs from being caught at compile time. E.g.:
*
* jsid id = ...
* if (id == JS_TRUE) // error
@@ -309,26 +309,45 @@ struct RuntimeFriendFields {
/* Limit pointer for checking native stack consumption. */
uintptr_t nativeStackLimit;
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
/*
* Stack allocated GC roots for stack GC heap pointers, which may be
* overwritten if moved during a GC.
+ *
+ * The actual set of roots is split between the |JSContext*|, the
+ * |JSRuntime*|, and any active |JS::PerThread| data. This is purely
+ * for efficiency. A given |Rooted<T>| pointer is associated with
+ * precisely one of those.
*/
Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
#endif
RuntimeFriendFields()
: interrupt(0),
nativeStackLimit(0) { }
static const RuntimeFriendFields *get(const JSRuntime *rt) {
return reinterpret_cast<const RuntimeFriendFields *>(rt);
}
};
+struct PerThreadDataFriendFields {
+#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
+ /*
+ * Stack allocated GC roots for stack GC heap pointers, which may be
+ * overwritten if moved during a GC.
+ */
+ Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
+#endif
+
+ static const PerThreadDataFriendFields *get(const JS::PerThreadData *pt) {
+ return reinterpret_cast<const PerThreadDataFriendFields *>(pt);
+ }
+};
+
} /* namespace js */
#endif /* __cplusplus */
#endif /* jspubtd_h___ */
--- a/js/src/jstaskset.cpp
+++ b/js/src/jstaskset.cpp
@@ -4,16 +4,17 @@
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jstaskset.h"
#include "jsmonitor.h"
#include "jscntxt.h"
#include "jscompartment.h"
+#include "prthread.h"
namespace js {
class TaskSetSharedContext
: public TaskExecutor,
public Monitor
{
////////////////////////////////////////////////////////////////////////
@@ -60,17 +61,17 @@ class TaskSetSharedContext
// be *read* without the lock.
volatile bool rendezvous_;
// Invoked only from the main thread:
void executeFromMainThread(uintptr_t stackLimit);
// Executes slice #threadId of the work, either from a worker or
// the main thread.
- void executePortion(size_t threadId, uintptr_t stackLimit);
+ void executePortion(JS::PerThreadData *perThread, size_t threadId, uintptr_t stackLimit);
// Rendezvous protocol:
//
// Use AutoRendezvous rather than invoking initiateRendezvous()
// and endRendezvous() directly.
friend class AutoRendezvous;
@@ -285,40 +286,43 @@ TaskSetSharedContext::transferArenasToCo
void
TaskSetSharedContext::executeFromWorker(size_t workerId, uintptr_t stackLimit)
{
JS_ASSERT(workerId < numThreads_ - 1);
JS::PerThreadData thisThread(cx_->runtime);
JS::TlsPerThreadData.set(&thisThread);
- executePortion(workerId, stackLimit);
+ executePortion(&thisThread, workerId, stackLimit);
JS::TlsPerThreadData.set(NULL);
AutoLockMonitor lock(*this);
uncompleted_ -= 1;
if (blocked_ == uncompleted_) {
// Signal the main thread that we have terminated. It will be
// either working, arranging a rendezvous, or waiting for
// workers to complete.
lock.notify();
}
}
void
TaskSetSharedContext::executeFromMainThread(uintptr_t stackLimit)
{
- executePortion(numThreads_ - 1, stackLimit);
+ executePortion(&cx_->runtime->mainThread, numThreads_ - 1, stackLimit);
}
void
-TaskSetSharedContext::executePortion(size_t threadId, uintptr_t stackLimit)
+TaskSetSharedContext::executePortion(JS::PerThreadData *perThread,
+ size_t threadId,
+ uintptr_t stackLimit)
{
gc::ArenaLists *arenaLists = arenaListss_[threadId];
- ThreadContext threadCx(threadId, numThreads_, stackLimit, arenaLists, this);
+ ThreadContext threadCx(perThread, threadId, numThreads_,
+ stackLimit, arenaLists, this);
AutoSetThreadContext autoContext(&threadCx);
if (!taskSet_.parallel(threadCx))
abort_ = true;
}
bool
TaskSetSharedContext::setFatal()
@@ -440,20 +444,22 @@ TaskSetSharedContext::endRendezvous(Thre
// signal other threads that rendezvous is over
PR_NotifyAllCondVar(rendezvousEnd_);
}
/****************************************************************************
* ThreadContext
*/
-ThreadContext::ThreadContext(size_t threadId, size_t numThreads,
+ThreadContext::ThreadContext(JS::PerThreadData *perThreadData,
+ size_t threadId, size_t numThreads,
uintptr_t stackLimit, js::gc::ArenaLists *arenaLists,
TaskSetSharedContext *shared)
- : threadId(threadId),
+ : perThreadData(perThreadData),
+ threadId(threadId),
numThreads(numThreads),
ionStackLimit(stackLimit),
arenaLists(arenaLists),
shared(shared)
{}
bool
ThreadContext::isMainThread()
--- a/js/src/jstaskset.h
+++ b/js/src/jstaskset.h
@@ -21,44 +21,34 @@ enum ParallelResult { TP_SUCCESS, TP_RET
struct TaskSet;
// Executes the given |TaskSet| in parallel using the runtime's
// |ThreadPool|, returning upon completion. In general, if there are
// |N| workers in the threadpool, the problem will be divided into
// |N+1| slices, as the main thread will also execute one slice.
ParallelResult ExecuteTaskSet(JSContext *cx, TaskSet &taskSet);
-#ifndef JS_THREADSAFE_ION
-
-struct TaskSet {};
-struct ThreadContext {};
-
-static inline bool InParallelSection() {
- return ThreadContext::current() != NULL;
-}
-
-#else
-
class TaskSetSharedContext;
class AutoRendezvous;
class AutoSetThreadContext;
namespace gc { struct ArenaLists; }
struct ThreadContext
{
public:
+ JS::PerThreadData *perThreadData;
const size_t threadId;
const size_t numThreads;
uintptr_t ionStackLimit;
// Arenas to use when allocating on this thread.
// See |js::ion::ParFunctions::ParNewGCThing()|.
gc::ArenaLists *const arenaLists;
- ThreadContext(size_t threadId, size_t numThreads,
+ ThreadContext(JS::PerThreadData *perThreadData, size_t threadId, size_t numThreads,
uintptr_t stackLimit, js::gc::ArenaLists *arenaLists,
TaskSetSharedContext *shared);
// True if this is the main thread, false if it is one of the parallel workers
bool isMainThread();
// Generally speaking, if a thread returns false, that is
// interpreted as a "bailout"---meaning, a recoverable error. If
@@ -117,16 +107,18 @@ public:
//
// Returns true on success, false to halt parallel execution.
virtual bool post(size_t numThreads) = 0;
};
/* True if this thread is currently executing a ParallelArray
operation across multiple threads. */
static inline bool InParallelSection() {
+# ifdef JS_THREADSAFE_ION
return ThreadContext::current() != NULL;
+# else
+ return false;
+# endif
}
#endif
}
-
-#endif
--- a/js/src/jsthreadpool.cpp
+++ b/js/src/jsthreadpool.cpp
@@ -2,28 +2,23 @@
/* vim: set ts=4 sw=4 et tw=99: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jscntxt.h"
#include "jslock.h"
#include "jsthreadpool.h"
+#include "prthread.h"
#if JS_ION
# include "ion/IonBuilder.h"
#endif
namespace js {
-#ifndef JS_THREADSAFE_ION
-
-struct ThreadPool {};
-
-#else
-
/****************************************************************************
* WorkerThread
*
* Each |WorkerThread| just hangs around waiting for items to be added
* to its |worklist_|. Whenever something is added, it gets executed.
* Once the worker's state is set to |TERMINATING|, the worker will
* exit as soon as its queue is empty.
*/
@@ -199,16 +194,17 @@ ThreadPool::~ThreadPool() {
WorkerThread *worker = workers_.popCopy();
delete worker;
}
}
bool
ThreadPool::init()
{
+#ifdef JS_THREADSAFE_ION
// Compute desired number of workers based on env var or # of CPUs.
size_t numWorkers = 0;
char *pathreads = getenv("PATHREADS");
if (pathreads != NULL) {
numWorkers = strtol(pathreads, NULL, 10);
} else {
numWorkers = GetCPUCount() - 1;
}
@@ -228,16 +224,18 @@ ThreadPool::init()
if (!workers_.append(worker)) {
delete worker;
return false;
}
if (!worker->start()) {
return false;
}
}
+#endif
+
return true;
}
void
ThreadPool::terminateWorkers()
{
for (size_t i = 0; i < workers_.length(); i++) {
workers_[i]->terminate();
@@ -265,11 +263,9 @@ ThreadPool::submitAll(TaskExecutor *exec
}
bool
ThreadPool::terminate() {
terminateWorkers();
return true;
}
-#endif
-
}
--- a/js/src/jsthreadpool.h
+++ b/js/src/jsthreadpool.h
@@ -7,42 +7,31 @@
#ifndef jsthreadpool_h___
#define jsthreadpool_h___
#if defined(JS_THREADSAFE) && defined(JS_ION)
# define JS_THREADSAFE_ION
#endif
-#ifdef JS_THREADSAFE_ION
#include <stddef.h>
#include "mozilla/StandardInteger.h"
#include "prtypes.h"
+#include "js/Vector.h"
+#include "jsalloc.h"
#include "prlock.h"
#include "prcvar.h"
-#include "js/Vector.h"
-#include "jsalloc.h"
-#endif
struct JSContext;
struct JSRuntime;
struct JSCompartment;
struct JSScript;
namespace js {
-#ifndef JS_THREADSAFE_ION
-
-struct ThreadPool {
- bool init() { return true; }
- size_t numWorkers() { return 0; }
-};
-
-#else
-
class WorkerThread;
typedef void (*TaskFun)(void *userdata, size_t workerId, uintptr_t stackLimit);
class TaskExecutor
{
public:
virtual void executeFromWorker(size_t workerId, uintptr_t stackLimit) = 0;
@@ -80,15 +69,13 @@ public:
bool submitOne(TaskExecutor *executor);
// Submits a job that will be executed by all workers.
bool submitAll(TaskExecutor *executor);
bool terminate();
};
-#endif
-
}
#endif
--- a/js/src/jsthreadpoolinlines.h
+++ b/js/src/jsthreadpoolinlines.h
@@ -1,12 +1,12 @@
-#ifdef JS_THREADSAFE_ION
-
namespace js {
ThreadContext *
ThreadContext::current() {
+#ifdef JS_THREADSAFE_ION
return (ThreadContext*) PR_GetThreadPrivate(ThreadPrivateIndex);
+#else
+ return NULL;
+#endif
}
}
-
-#endif