Bug 807480 - Add Rooted<T> roots to PerThread state. r=luke

Currently there are a number of global fields in JSRuntime* which are basically
tracking per-thread state.  This makes sense on the current trunk since there
is only ever a single thread associated with a runtime, but as Parallel JS (nee
Rivertrail) starts to land this assumption no longer holds.

This patch makes a struct, currently called |JS::PerThreadData|, that stores
per-thread data from the runtime.  There is one instance of this struct
embedded in "Runtime" itself (the field |mainThread|).  For now I have only
migrated the debug GC fields into |PerThread|, those are the ones causing me
immediate pain.  Eventually more fields will want to move into there.

The eventual goal is to distinguish thread-safe code, which will take as
argument a |JS::PerThread*|, from non-thread-safe code, which will take a
|JSRuntime*| or |JSContext*|.
This commit is contained in:
Nicholas D. Matsakis 2012-11-06 22:32:01 -05:00
parent 6240239870
commit 0f88896f1a
11 changed files with 190 additions and 76 deletions

View File

@ -519,27 +519,23 @@ class Rooted : public RootedBase<T>
{
#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));
PerThreadDataFriendFields *pt = PerThreadDataFriendFields::getMainThread(rtArg);
commonInit(pt->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 = PerThreadDataFriendFields::get(ptArg);
commonInit(pt->thingGCRooters);
#endif
}
@ -576,6 +572,22 @@ class Rooted : public RootedBase<T>
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)
@ -585,6 +597,15 @@ class Rooted : public RootedBase<T>
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)
@ -625,6 +646,17 @@ class Rooted : public RootedBase<T>
}
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

View File

@ -676,33 +676,36 @@ static JSBool js_NewRuntimeWasCalled = JS_FALSE;
/*
* Thread Local Storage slot for storing the runtime for a thread.
*/
namespace js {
mozilla::ThreadLocal<PerThreadData *> TlsPerThreadData;
}
namespace JS {
mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
#ifdef DEBUG
JS_FRIEND_API(void)
EnterAssertNoGCScope()
{
++TlsRuntime.get()->gcAssertNoGCDepth;
++TlsPerThreadData.get()->gcAssertNoGCDepth;
}
JS_FRIEND_API(void)
LeaveAssertNoGCScope()
{
--TlsRuntime.get()->gcAssertNoGCDepth;
JS_ASSERT(TlsRuntime.get()->gcAssertNoGCDepth >= 0);
--TlsPerThreadData.get()->gcAssertNoGCDepth;
JS_ASSERT(TlsPerThreadData.get()->gcAssertNoGCDepth >= 0);
}
JS_FRIEND_API(bool)
InNoGCScope()
{
return TlsRuntime.get()->gcAssertNoGCDepth > 0;
return TlsPerThreadData.get()->gcAssertNoGCDepth > 0;
}
JS_FRIEND_API(bool)
NeedRelaxedRootChecks()
{
return TlsRuntime.get()->gcRelaxRootChecks;
return TlsPerThreadData.get()->gcRelaxRootChecks;
}
#else
JS_FRIEND_API(void) EnterAssertNoGCScope() {}
@ -715,8 +718,17 @@ JS_FRIEND_API(bool) NeedRelaxedRootChecks() { return false; }
static const JSSecurityCallbacks NullSecurityCallbacks = { };
js::PerThreadData::PerThreadData(JSRuntime *runtime)
: runtime_(runtime)
#ifdef DEBUG
, gcRelaxRootChecks(false)
, gcAssertNoGCDepth(0)
#endif
{}
JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
: atomsCompartment(NULL),
: mainThread(this),
atomsCompartment(NULL),
#ifdef JS_THREADSAFE
ownerThread_(NULL),
#endif
@ -788,10 +800,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
gcSliceBudget(SliceBudget::Unlimited),
gcIncrementalEnabled(true),
gcExactScanningEnabled(true),
#ifdef DEBUG
gcRelaxRootChecks(false),
gcAssertNoGCDepth(0),
#endif
gcPoke(false),
heapState(Idle),
#ifdef JS_GC_ZEAL
@ -885,16 +893,12 @@ JSRuntime::init(uint32_t maxbytes)
ownerThread_ = PR_GetCurrentThread();
#endif
JS::TlsRuntime.set(this);
js::TlsPerThreadData.set(&mainThread);
#ifdef JS_METHODJIT_SPEW
JMCheckLogging();
#endif
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
PodArrayZero(thingGCRooters);
#endif
if (!js_InitGC(this, maxbytes))
return false;
@ -1018,9 +1022,9 @@ JSRuntime::setOwnerThread()
JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */
JS_ASSERT(requestDepth == 0);
JS_ASSERT(js_NewRuntimeWasCalled);
JS_ASSERT(JS::TlsRuntime.get() == NULL);
JS_ASSERT(js::TlsPerThreadData.get() == NULL);
ownerThread_ = PR_GetCurrentThread();
JS::TlsRuntime.set(this);
js::TlsPerThreadData.set(&mainThread);
nativeStackBase = GetNativeStackBase();
if (nativeStackQuota)
JS_SetNativeStackQuota(this, nativeStackQuota);
@ -1033,7 +1037,7 @@ JSRuntime::clearOwnerThread()
JS_ASSERT(requestDepth == 0);
JS_ASSERT(js_NewRuntimeWasCalled);
ownerThread_ = (void *)0xc1ea12; /* "clear" */
JS::TlsRuntime.set(NULL);
js::TlsPerThreadData.set(NULL);
nativeStackBase = 0;
#if JS_STACK_GROWTH_DIRECTION > 0
nativeStackLimit = UINTPTR_MAX;
@ -1047,7 +1051,7 @@ JSRuntime::abortIfWrongThread() const
{
if (ownerThread_ != PR_GetCurrentThread())
MOZ_CRASH();
if (this != JS::TlsRuntime.get())
if (!js::TlsPerThreadData.get()->associatedWith(this))
MOZ_CRASH();
}
@ -1055,7 +1059,7 @@ JS_FRIEND_API(void)
JSRuntime::assertValidThread() const
{
JS_ASSERT(ownerThread_ == PR_GetCurrentThread());
JS_ASSERT(this == JS::TlsRuntime.get());
JS_ASSERT(js::TlsPerThreadData.get()->associatedWith(this));
}
#endif /* JS_THREADSAFE */
@ -1092,7 +1096,7 @@ JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads)
InitMemorySubsystem();
if (!JS::TlsRuntime.init())
if (!js::TlsPerThreadData.init())
return NULL;
js_NewRuntimeWasCalled = JS_TRUE;

View File

@ -2889,8 +2889,6 @@ JS_IsInRequest(JSRuntime *rt);
namespace JS {
extern mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
inline bool
IsPoisonedId(jsid iden)
{

View File

@ -389,8 +389,70 @@ struct JSAtomState
#define NAME_OFFSET(name) offsetof(JSAtomState, name)
#define OFFSET_TO_NAME(rt,off) (*(js::FixedHeapPtr<js::PropertyName>*)((char*)&(rt)->atomState + (off)))
namespace js {
/*
* Encapsulates portions of the runtime/context that are tied to a
* single active thread. Normally, as most JS is single-threaded,
* there is only one instance of this struct, embedded in the
* JSRuntime as the field |mainThread|. During Parallel JS sections,
* however, there will be one instance per worker thread.
*
* The eventual plan is to designate thread-safe portions of the
* interpreter and runtime by having them take |PerThreadData*|
* arguments instead of |JSContext*| or |JSRuntime*|.
*/
class PerThreadData : public js::PerThreadDataFriendFields
{
/*
* Backpointer to the full shared JSRuntime* with which this
* thread is associaed. This is private because accessing the
* fields of this runtime can provoke race conditions, so the
* intention is that access will be mediated through safe
* functions like |associatedWith()| below.
*/
JSRuntime *runtime_;
public:
/*
* 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.
*/
#ifdef DEBUG
struct SavedGCRoot {
void *thing;
JSGCTraceKind kind;
SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
};
js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
bool gcRelaxRootChecks;
int gcAssertNoGCDepth;
#endif
PerThreadData(JSRuntime *runtime);
bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
};
} // namespace js
struct JSRuntime : js::RuntimeFriendFields
{
/* Per-thread data for the main thread that is associated with
* this JSRuntime, as opposed to any worker threads used in
* parallel sections. See definition of |PerThreadData| struct
* above for more details.
*
* NB: This field is statically asserted to be at offset
* sizeof(RuntimeFriendFields). See
* PerThreadDataFriendFields::getMainThread.
*/
js::PerThreadData mainThread;
/* Default compartment. */
JSCompartment *atomsCompartment;
@ -654,25 +716,6 @@ struct JSRuntime : js::RuntimeFriendFields
*/
bool gcExactScanningEnabled;
/*
* 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.
*/
#ifdef DEBUG
struct SavedGCRoot {
void *thing;
JSGCTraceKind kind;
SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
};
js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
bool gcRelaxRootChecks;
int gcAssertNoGCDepth;
#endif
bool gcPoke;
enum HeapState {

View File

@ -22,6 +22,16 @@
using namespace js;
using namespace JS;
// Required by PerThreadDataFriendFields::getMainThread()
JS_STATIC_ASSERT(offsetof(JSRuntime, mainThread) == sizeof(RuntimeFriendFields));
PerThreadDataFriendFields::PerThreadDataFriendFields()
{
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
PodArrayZero(thingGCRooters);
#endif
}
JS_FRIEND_API(void)
JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook)
{

View File

@ -183,6 +183,8 @@ JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook);
namespace js {
extern mozilla::ThreadLocal<PerThreadData *> TlsPerThreadData;
inline JSRuntime *
GetRuntime(const JSContext *cx)
{

View File

@ -992,7 +992,7 @@ MarkExactStackRoots(JSTracer *trc)
for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
MarkExactStackRooters(trc, cx->thingGCRooters[i], ThingRootKind(i));
}
MarkExactStackRooters(trc, rt->thingGCRooters[i], ThingRootKind(i));
MarkExactStackRooters(trc, rt->mainThread.thingGCRooters[i], ThingRootKind(i));
}
}
#endif /* JSGC_USE_EXACT_ROOTING */
@ -1125,7 +1125,8 @@ MarkIfGCThingWord(JSTracer *trc, uintptr_t w)
#ifdef DEBUG
if (trc->runtime->gcIncrementalState == MARK_ROOTS)
trc->runtime->gcSavedRoots.append(JSRuntime::SavedGCRoot(thing, traceKind));
trc->runtime->mainThread.gcSavedRoots.append(
PerThreadData::SavedGCRoot(thing, traceKind));
#endif
return CGCT_VALID;
@ -1186,8 +1187,8 @@ MarkConservativeStackRoots(JSTracer *trc, bool useSavedRoots)
#ifdef DEBUG
if (useSavedRoots) {
for (JSRuntime::SavedGCRoot *root = rt->gcSavedRoots.begin();
root != rt->gcSavedRoots.end();
for (PerThreadData::SavedGCRoot *root = rt->mainThread.gcSavedRoots.begin();
root != rt->mainThread.gcSavedRoots.end();
root++)
{
JS_SET_TRACING_NAME(trc, "cstack");
@ -1197,7 +1198,7 @@ MarkConservativeStackRoots(JSTracer *trc, bool useSavedRoots)
}
if (rt->gcIncrementalState == MARK_ROOTS)
rt->gcSavedRoots.clearAndFree();
rt->mainThread.gcSavedRoots.clearAndFree();
#endif
ConservativeGCData *cgcd = &rt->conservativeGC;
@ -4912,7 +4913,8 @@ CheckStackRoot(JSTracer *trc, uintptr_t *w)
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;

View File

@ -307,14 +307,6 @@ 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.
*/
Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
#endif
RuntimeFriendFields()
: interrupt(0),
nativeStackLimit(0) { }
@ -324,6 +316,32 @@ struct RuntimeFriendFields {
}
};
class PerThreadData;
struct PerThreadDataFriendFields
{
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 PerThreadDataFriendFields *get(js::PerThreadData *pt) {
return reinterpret_cast<PerThreadDataFriendFields *>(pt);
}
static PerThreadDataFriendFields *getMainThread(JSRuntime *rt) {
// mainThread must always appear directly after |RuntimeFriendFields|.
// Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp|
return reinterpret_cast<PerThreadDataFriendFields *>(
reinterpret_cast<char*>(rt) + sizeof(RuntimeFriendFields));
}
};
} /* namespace js */
#endif /* __cplusplus */

View File

@ -3417,7 +3417,7 @@ RelaxRootChecks(JSContext *cx, unsigned argc, jsval *vp)
}
#ifdef DEBUG
cx->runtime->gcRelaxRootChecks = true;
cx->runtime->mainThread.gcRelaxRootChecks = true;
#endif
return true;

View File

@ -1402,7 +1402,8 @@ StackIter::settleOnNewState()
}
StackIter::StackIter(JSContext *cx, SavedOption savedOption)
: maybecx_(cx),
: perThread_(&cx->runtime->mainThread),
maybecx_(cx),
savedOption_(savedOption),
script_(cx, NULL)
#ifdef JS_ION
@ -1426,7 +1427,9 @@ StackIter::StackIter(JSContext *cx, SavedOption savedOption)
}
StackIter::StackIter(JSRuntime *rt, StackSegment &seg)
: maybecx_(NULL), savedOption_(STOP_AT_SAVED),
: perThread_(&rt->mainThread),
maybecx_(NULL),
savedOption_(STOP_AT_SAVED),
script_(rt, NULL)
#ifdef JS_ION
, ionActivations_(rt),
@ -1444,14 +1447,15 @@ StackIter::StackIter(JSRuntime *rt, StackSegment &seg)
}
StackIter::StackIter(const StackIter &other)
: maybecx_(other.maybecx_),
: perThread_(other.perThread_),
maybecx_(other.maybecx_),
savedOption_(other.savedOption_),
state_(other.state_),
fp_(other.fp_),
calls_(other.calls_),
seg_(other.seg_),
pc_(other.pc_),
script_(other.maybecx_ ? other.maybecx_->runtime : TlsRuntime.get(), other.script_),
script_(perThread_, other.script_),
args_(other.args_)
#ifdef JS_ION
, ionActivations_(other.ionActivations_),

View File

@ -1712,6 +1712,7 @@ class GeneratorFrameGuard : public FrameGuard
class StackIter
{
friend class ContextStack;
PerThreadData *perThread_;
JSContext *maybecx_;
public:
enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };