mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
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:
parent
e882261638
commit
08a19e6d9c
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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++;
|
||||
}
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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) {
|
||||
/*
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user