Bug 786136 - Allow for automatic storage of stack roots in the runtime; r=luke r=billm

This stores the active JSRuntime* in TLS so that it is available for use by the
exact rooting when no JSContext* is available.
This commit is contained in:
Terrence Cole 2012-08-29 10:35:56 -07:00
parent e882261638
commit 08a19e6d9c
9 changed files with 126 additions and 59 deletions

View File

@ -12,8 +12,6 @@
#include "mozilla/TypeTraits.h"
#include "jsapi.h"
#include "js/TemplateLib.h"
#include "js/Utility.h"
@ -238,6 +236,8 @@ typedef MutableHandle<Value> MutableHandleValue;
typedef JSObject * RawObject;
typedef JSString * RawString;
extern mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
/*
* By default, pointers should use the inheritance hierarchy to find their
* ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that
@ -268,10 +268,10 @@ class RootedBase {};
template <typename T>
class Rooted : public RootedBase<T>
{
void init(JSContext *cx_)
void init(JSContext *cxArg)
{
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
ContextFriendFields *cx = ContextFriendFields::get(cx_);
ContextFriendFields *cx = ContextFriendFields::get(cxArg);
ThingRootKind kind = RootMethods<T>::kind();
this->stack = reinterpret_cast<Rooted<T>**>(&cx->thingGCRooters[kind]);
@ -282,7 +282,27 @@ class Rooted : public RootedBase<T>
#endif
}
void init(JSRuntime *rtArg)
{
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
RuntimeFriendFields *rt = const_cast<RuntimeFriendFields *>(RuntimeFriendFields::get(rtArg));
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));
#endif
}
public:
Rooted() : ptr(RootMethods<T>::initial()) { init(JS::TlsRuntime); }
Rooted(const T &initial) : ptr(initial) { init(JS::TlsRuntime); }
Rooted(JSRuntime *rt) : ptr(RootMethods<T>::initial()) { init(rt); }
Rooted(JSRuntime *rt, T initial) : ptr(initial) { init(rt); }
Rooted(JSContext *cx) : ptr(RootMethods<T>::initial()) { init(cx); }
Rooted(JSContext *cx, T initial) : ptr(initial) { init(cx); }
@ -319,13 +339,11 @@ class Rooted : public RootedBase<T>
}
private:
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
Rooted<T> **stack, *prev;
#endif
T ptr;
Rooted() MOZ_DELETE;
Rooted(const Rooted &) MOZ_DELETE;
};

View File

@ -10,6 +10,7 @@
*/
#include "mozilla/FloatingPoint.h"
#include "mozilla/ThreadLocal.h"
#include <ctype.h>
#include <stdarg.h>
@ -719,6 +720,13 @@ JS_IsBuiltinFunctionConstructor(JSFunction *fun)
*/
static JSBool js_NewRuntimeWasCalled = JS_FALSE;
/*
* Thread Local Storage slot for storing the runtime for a thread.
*/
namespace JS {
mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
}
static const JSSecurityCallbacks NullSecurityCallbacks = { };
JSRuntime::JSRuntime()
@ -877,6 +885,8 @@ JSRuntime::init(uint32_t maxbytes)
ownerThread_ = PR_GetCurrentThread();
#endif
JS::TlsRuntime.set(this);
#ifdef JS_METHODJIT_SPEW
JMCheckLogging();
#endif
@ -938,7 +948,9 @@ JSRuntime::init(uint32_t maxbytes)
JSRuntime::~JSRuntime()
{
JS_ASSERT(onOwnerThread());
#ifdef JS_THREADSAFE
clearOwnerThread();
#endif
delete_(debugScopes);
@ -994,7 +1006,10 @@ JSRuntime::setOwnerThread()
{
JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */
JS_ASSERT(requestDepth == 0);
JS_ASSERT(js_NewRuntimeWasCalled);
JS_ASSERT(JS::TlsRuntime.get() == NULL);
ownerThread_ = PR_GetCurrentThread();
JS::TlsRuntime.set(this);
nativeStackBase = GetNativeStackBase();
if (nativeStackQuota)
JS_SetNativeStackQuota(this, nativeStackQuota);
@ -1003,9 +1018,11 @@ JSRuntime::setOwnerThread()
void
JSRuntime::clearOwnerThread()
{
JS_ASSERT(onOwnerThread());
assertValidThread();
JS_ASSERT(requestDepth == 0);
JS_ASSERT(js_NewRuntimeWasCalled);
ownerThread_ = (void *)0xc1ea12; /* "clear" */
JS::TlsRuntime.set(NULL);
nativeStackBase = 0;
#if JS_STACK_GROWTH_DIRECTION > 0
nativeStackLimit = UINTPTR_MAX;
@ -1014,10 +1031,20 @@ JSRuntime::clearOwnerThread()
#endif
}
JS_FRIEND_API(bool)
JSRuntime::onOwnerThread() const
JS_FRIEND_API(void)
JSRuntime::abortIfWrongThread() const
{
return ownerThread_ == PR_GetCurrentThread();
if (ownerThread_ != PR_GetCurrentThread())
MOZ_CRASH();
if (this != JS::TlsRuntime.get())
MOZ_CRASH();
}
JS_FRIEND_API(void)
JSRuntime::assertValidThread() const
{
JS_ASSERT(ownerThread_ == PR_GetCurrentThread());
JS_ASSERT(this == JS::TlsRuntime.get());
}
#endif /* JS_THREADSAFE */
@ -1054,6 +1081,9 @@ JS_NewRuntime(uint32_t maxbytes)
InitMemorySubsystem();
if (!JS::TlsRuntime.init())
return false;
js_NewRuntimeWasCalled = JS_TRUE;
}
@ -1101,7 +1131,7 @@ static void
StartRequest(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (rt->requestDepth) {
rt->requestDepth++;
@ -1118,7 +1148,7 @@ static void
StopRequest(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
JS_ASSERT(rt->requestDepth != 0);
if (rt->requestDepth != 1) {
rt->requestDepth--;
@ -1166,7 +1196,7 @@ JS_SuspendRequest(JSContext *cx)
{
#ifdef JS_THREADSAFE
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
unsigned saveDepth = rt->requestDepth;
if (!saveDepth)
@ -1186,7 +1216,7 @@ JS_ResumeRequest(JSContext *cx, unsigned saveDepth)
{
#ifdef JS_THREADSAFE
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (saveDepth == 0)
return;
JS_ASSERT(saveDepth >= 1);
@ -1202,7 +1232,7 @@ JS_PUBLIC_API(JSBool)
JS_IsInRequest(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
return rt->requestDepth != 0;
#else
return false;
@ -1213,7 +1243,7 @@ JS_PUBLIC_API(JSBool)
JS_IsInSuspendedRequest(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
return rt->suspendCount != 0;
#else
return false;
@ -7025,10 +7055,7 @@ JS_SetRuntimeThread(JSRuntime *rt)
extern JS_NEVER_INLINE JS_PUBLIC_API(void)
JS_AbortIfWrongThread(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
if (!rt->onOwnerThread())
MOZ_CRASH();
#endif
rt->abortIfWrongThread();
}
#ifdef JS_GC_ZEAL

View File

@ -14,6 +14,9 @@
#include "mozilla/Attributes.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/StandardInteger.h"
#ifdef __cplusplus
# include "mozilla/ThreadLocal.h"
#endif
#include <stddef.h>
#include <stdio.h>
@ -3018,6 +3021,8 @@ JS_END_EXTERN_C
namespace JS {
extern mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
inline bool
IsPoisonedId(jsid iden)
{

View File

@ -1438,7 +1438,7 @@ AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx)
: cx(cx)
{
JS_ASSERT(cx->runtime->requestDepth || cx->runtime->isHeapBusy());
JS_ASSERT(cx->runtime->onOwnerThread());
cx->runtime->assertValidThread();
cx->runtime->checkRequestDepth++;
}

View File

@ -374,13 +374,15 @@ struct JSRuntime : js::RuntimeFriendFields
void *ownerThread() const { return ownerThread_; }
void clearOwnerThread();
void setOwnerThread();
JS_FRIEND_API(bool) onOwnerThread() const;
JS_FRIEND_API(void) abortIfWrongThread() const;
JS_FRIEND_API(void) assertValidThread() const;
private:
void *ownerThread_;
public:
#else
public:
bool onOwnerThread() const { return true; }
void abortIfWrongThread() const {}
void assertValidThread() const {}
#endif
/* Keeper of the contiguous stack used by all contexts in this thread. */

View File

@ -183,25 +183,6 @@ JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook);
namespace js {
struct RuntimeFriendFields {
/*
* If non-zero, we were been asked to call the operation callback as soon
* as possible.
*/
volatile int32_t interrupt;
/* Limit pointer for checking native stack consumption. */
uintptr_t nativeStackLimit;
RuntimeFriendFields()
: interrupt(0),
nativeStackLimit(0) { }
static const RuntimeFriendFields *get(const JSRuntime *rt) {
return reinterpret_cast<const RuntimeFriendFields *>(rt);
}
};
inline JSRuntime *
GetRuntime(const JSContext *cx)
{

View File

@ -2599,7 +2599,7 @@ TriggerOperationCallback(JSRuntime *rt, gcreason::Reason reason)
void
TriggerGC(JSRuntime *rt, gcreason::Reason reason)
{
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (rt->isHeapBusy())
return;
@ -2612,7 +2612,7 @@ void
TriggerCompartmentGC(JSCompartment *comp, gcreason::Reason reason)
{
JSRuntime *rt = comp->rt;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (rt->isHeapBusy())
return;
@ -2636,7 +2636,7 @@ void
MaybeGC(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (rt->gcZeal() == ZealAllocValue || rt->gcZeal() == ZealPokeValue) {
PrepareForFullGC(rt);
@ -4824,6 +4824,16 @@ SetValidateGC(JSContext *cx, bool enabled)
#if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
JS_ALWAYS_INLINE void
CheckStackRootThings(uintptr_t *w, Rooted<void*> *rooter, bool *matched)
{
while (rooter) {
if (rooter->address() == static_cast<void*>(w))
*matched = true;
rooter = rooter->previous();
}
}
static void
CheckStackRoot(JSTracer *trc, uintptr_t *w)
{
@ -4837,21 +4847,17 @@ CheckStackRoot(JSTracer *trc, uintptr_t *w)
if (test == CGCT_VALID) {
bool matched = false;
JSRuntime *rt = trc->runtime;
for (ContextIter cx(rt); !cx.done(); cx.next()) {
for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
Rooted<void*> *rooter = cx->thingGCRooters[i];
while (rooter) {
if (rooter->address() == static_cast<void*>(w))
for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
CheckStackRootThings(w, rt->thingGCRooters[i], &matched);
for (ContextIter cx(rt); !cx.done(); cx.next()) {
CheckStackRootThings(w, cx->thingGCRooters[i], &matched);
SkipRoot *skip = cx->skipGCRooters;
while (skip) {
if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w)))
matched = true;
rooter = rooter->previous();
skip = skip->previous();
}
}
SkipRoot *skip = cx->skipGCRooters;
while (skip) {
if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w)))
matched = true;
skip = skip->previous();
}
}
if (!matched) {
/*

View File

@ -290,6 +290,33 @@ struct ContextFriendFields {
#endif
};
struct RuntimeFriendFields {
/*
* If non-zero, we were been asked to call the operation callback as soon
* as possible.
*/
volatile int32_t interrupt;
/* 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) { }
static const RuntimeFriendFields *get(const JSRuntime *rt) {
return reinterpret_cast<const RuntimeFriendFields *>(rt);
}
};
} /* namespace JS */
#endif /* __cplusplus */

View File

@ -1456,7 +1456,8 @@ mjit::Compiler::finishThisUp()
}
JSScript *script =
(from.inlineIndex == UINT32_MAX) ? outerScript : inlineFrames[from.inlineIndex]->script;
(from.inlineIndex == UINT32_MAX) ? outerScript.get()
: inlineFrames[from.inlineIndex]->script;
uint32_t codeOffset = from.ool
? masm.size() + from.returnOffset
: from.returnOffset;