--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -117,20 +117,20 @@ struct CompartmentStats
struct IterateData
{
IterateData(JSMallocSizeOfFun mallocSizeOf, GetNameCallback getNameCb,
DestroyNameCallback destroyNameCb)
: runtimeObject(0)
, runtimeAtomsTable(0)
, runtimeContexts(0)
- , runtimeThreadsNormal(0)
- , runtimeThreadsTemporary(0)
- , runtimeThreadsRegexpCode(0)
- , runtimeThreadsStackCommitted(0)
+ , runtimeNormal(0)
+ , runtimeTemporary(0)
+ , runtimeRegexpCode(0)
+ , runtimeStackCommitted(0)
, gcHeapChunkTotal(0)
, gcHeapChunkCleanUnused(0)
, gcHeapChunkDirtyUnused(0)
, gcHeapChunkCleanDecommitted(0)
, gcHeapChunkDirtyDecommitted(0)
, gcHeapArenaUnused(0)
, gcHeapChunkAdmin(0)
, gcHeapUnusedPercentage(0)
@@ -148,20 +148,20 @@ struct IterateData
, mallocSizeOf(mallocSizeOf)
, getNameCb(getNameCb)
, destroyNameCb(destroyNameCb)
{}
int64_t runtimeObject;
int64_t runtimeAtomsTable;
int64_t runtimeContexts;
- int64_t runtimeThreadsNormal;
- int64_t runtimeThreadsTemporary;
- int64_t runtimeThreadsRegexpCode;
- int64_t runtimeThreadsStackCommitted;
+ int64_t runtimeNormal;
+ int64_t runtimeTemporary;
+ int64_t runtimeRegexpCode;
+ int64_t runtimeStackCommitted;
int64_t gcHeapChunkTotal;
int64_t gcHeapChunkCleanUnused;
int64_t gcHeapChunkDirtyUnused;
int64_t gcHeapChunkCleanDecommitted;
int64_t gcHeapChunkDirtyDecommitted;
int64_t gcHeapArenaUnused;
int64_t gcHeapChunkAdmin;
int64_t gcHeapUnusedPercentage;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -125,17 +125,16 @@ CPPSRCS = \
jsgcmark.cpp \
jsgcchunk.cpp \
jsgcstats.cpp \
jscrashreport.cpp \
jshash.cpp \
jsinfer.cpp \
jsinterp.cpp \
jsiter.cpp \
- jslock.cpp \
jslog2.cpp \
jsmath.cpp \
jsnativestack.cpp \
jsnum.cpp \
jsobj.cpp \
json.cpp \
jsonparser.cpp \
jsopcode.cpp \
@@ -614,17 +613,17 @@ check-malloc-function-usage: $(filter-ou
"in Makefile.in" "cx->calloc_ or rt->calloc_" $^
$(srcdir)/config/check_source_count.py "\bjs_realloc\b" 0 \
"in Makefile.in" "cx->realloc_ or rt->realloc_" $^
$(srcdir)/config/check_source_count.py "\bjs_free\b" 0 \
"in Makefile.in" "cx->free_" $^
# We desire these numbers to go down, not up. See "User guide to memory
# management within SpiderMonkey" in jsutil.h.
- $(srcdir)/config/check_source_count.py OffTheBooks:: 59 \
+ $(srcdir)/config/check_source_count.py OffTheBooks:: 58 \
"in Makefile.in" "{cx,rt}->{new_,array_new,malloc_,calloc_,realloc_}" $^
# This should go to zero, if possible.
$(srcdir)/config/check_source_count.py UnwantedForeground:: 31 \
"in Makefile.in" "{cx,rt}->{free_,delete_,array_delete}" $^
ifneq ($(OS_ARCH),WINNT) # FIXME: this should be made work on Windows too.
#check:: check-malloc-function-usage FIXME: disable on JM until closer to merge time.
endif
@@ -695,20 +694,16 @@ ifdef JS_THREADSAFE
DEFINES += -DJS_THREADSAFE
endif
ifdef JS_HAS_CTYPES
DEFINES += -DJS_HAS_CTYPES
DEFINES += -DDLL_PREFIX=\"$(DLL_PREFIX)\" -DDLL_SUFFIX=\"$(DLL_SUFFIX)\"
endif
-ifdef JS_NO_THIN_LOCKS
-DEFINES += -DJS_USE_ONLY_NSPR_LOCKS
-endif
-
ifdef JS_VERSION
DEFINES += -DJS_VERSION=$(JS_VERSION)
endif
# We do not want to have obsolete NSPR functionality in threadsafe builds.
DEFINES += -DNO_NSPR_10_SUPPORT
ifneq ($(findstring -L,$(NSPR_LIBS)),)
@@ -776,30 +771,16 @@ endif # GNU_CC
endif
ifeq ($(OS_RELEASE),4.1)
EXTRA_LIBS += -ldl -lnsl
else
EXTRA_LIBS += -lposix4 -ldl -lnsl -lsocket
endif
endif
-ifdef SOLARIS_SUNPRO_CXX
-ifeq ($(TARGET_CPU),sparc)
-# Sun Studio SPARC doesn't work well with gcc inline asm, use lock_SunOS_sparc*.il
-jslock.o: jslock.cpp Makefile.in lock_sparcv8plus.il lock_sparcv9.il
- $(REPORT_BUILD)
- @$(MAKE_DEPS_AUTO_CXX)
-ifeq (sparcv9,$(findstring sparcv9,$(OS_TEST)))
- $(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv9.il $<
-else
- $(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv8plus.il $<
-endif # sparcv9
-endif # sparc
-endif # SOLARIS_SUNPRO_CXX
-
# An AIX Optimization bug causes PR_dtoa() & JS_dtoa to produce wrong result.
# This suppresses optimization for this single compilation unit.
ifeq ($(OS_ARCH),AIX)
jsatom.o: jsatom.cpp Makefile.in
$(REPORT_BUILD)
@$(MAKE_DEPS_AUTO_CXX)
$(CXX) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $<
jsdtoa.o: jsdtoa.cpp Makefile.in
--- a/js/src/MemoryMetrics.cpp
+++ b/js/src/MemoryMetrics.cpp
@@ -214,47 +214,36 @@ CollectCompartmentStatsForRuntime(JSRunt
gc::ChunkSize;
IterateCompartmentsArenasCells(cx, data, CompartmentMemoryCallback,
ArenaCallback, CellCallback);
IterateChunks(cx, data, ChunkCallback);
data->runtimeObject = data->mallocSizeOf(rt, sizeof(JSRuntime));
+ size_t normal, temporary, regexpCode, stackCommitted;
+ rt->sizeOfExcludingThis(data->mallocSizeOf,
+ &normal,
+ &temporary,
+ ®expCode,
+ &stackCommitted);
+
+ data->runtimeNormal = normal;
+ data->runtimeTemporary = temporary;
+ data->runtimeRegexpCode = regexpCode;
+ data->runtimeStackCommitted = stackCommitted;
+
// Nb: we use sizeOfExcludingThis() because atomState.atoms is within
// JSRuntime, and so counted when JSRuntime is counted.
data->runtimeAtomsTable =
rt->atomState.atoms.sizeOfExcludingThis(data->mallocSizeOf);
- {
- // Need the GC lock to call JS_ContextIteratorUnlocked() and to
- // access rt->threads.
- AutoLockGC lock(rt);
-
- JSContext *acx, *iter = NULL;
- while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) {
- data->runtimeContexts +=
- acx->sizeOfIncludingThis(data->mallocSizeOf);
- }
-
- for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
- JSThread *thread = r.front().value;
- size_t normal, temporary, regexpCode, stackCommitted;
- thread->sizeOfIncludingThis(data->mallocSizeOf,
- &normal,
- &temporary,
- ®expCode,
- &stackCommitted);
-
- data->runtimeThreadsNormal += normal;
- data->runtimeThreadsTemporary += temporary;
- data->runtimeThreadsRegexpCode += regexpCode;
- data->runtimeThreadsStackCommitted += stackCommitted;
- }
- }
+ JSContext *acx, *iter = NULL;
+ while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL)
+ data->runtimeContexts += acx->sizeOfIncludingThis(data->mallocSizeOf);
}
JS_DestroyContextNoGC(cx);
// This is initialized to all bytes stored in used chunks, and then we
// subtract used space from it each time around the loop.
data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal -
data->gcHeapChunkCleanUnused -
@@ -341,36 +330,27 @@ GetExplicitNonHeapForRuntime(JSRuntime *
{
JSAutoRequest ar(cx);
// explicit/<compartment>/mjit-code
size_t n = 0;
IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback);
*amount += n;
- {
- // Need the GC lock to call JS_ContextIteratorUnlocked() and to
- // access rt->threads.
- AutoLockGC lock(rt);
+ // explicit/runtime/regexp-code
+ // explicit/runtime/stack-committed
+ size_t regexpCode, stackCommitted;
+ rt->sizeOfExcludingThis(mallocSizeOf,
+ NULL,
+ NULL,
+ ®expCode,
+ &stackCommitted);
- // explicit/runtime/threads/regexp-code
- // explicit/runtime/threads/stack-committed
- for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
- JSThread *thread = r.front().value;
- size_t regexpCode, stackCommitted;
- thread->sizeOfIncludingThis(mallocSizeOf,
- NULL,
- NULL,
- ®expCode,
- &stackCommitted);
-
- *amount += regexpCode;
- *amount += stackCommitted;
- }
- }
+ *amount += regexpCode;
+ *amount += stackCommitted;
}
JS_DestroyContextNoGC(cx);
return true;
}
} // namespace JS
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -57,29 +57,16 @@
#endif
using namespace std;
namespace js {
namespace ctypes {
/*******************************************************************************
-** Helper classes
-*******************************************************************************/
-
-class ScopedContextThread
-{
-public:
- ScopedContextThread(JSContext* cx) : mCx(cx) { JS_SetContextThread(cx); }
- ~ScopedContextThread() { JS_ClearContextThread(mCx); }
-private:
- JSContext* mCx;
-};
-
-/*******************************************************************************
** JSAPI function prototypes
*******************************************************************************/
static JSBool ConstructAbstract(JSContext* cx, uintN argc, jsval* vp);
namespace CType {
static JSBool ConstructData(JSContext* cx, uintN argc, jsval* vp);
static JSBool ConstructBasic(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
@@ -2786,17 +2773,16 @@ CType::FinalizeProtoClass(JSContext* cx,
// for use with FunctionType closures. And if we're here, in this finalizer,
// we're guaranteed to not need it anymore. Note that this slot will only
// be set for the object (of class CTypeProto) ctypes.FunctionType.prototype.
jsval slot;
if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSURECX, &slot) || JSVAL_IS_VOID(slot))
return;
JSContext* closureCx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
- JS_SetContextThread(closureCx);
JS_DestroyContextNoGC(closureCx);
}
void
CType::Trace(JSTracer* trc, JSObject* obj)
{
// Make sure our TypeCode slot is legit. If it's not, bail.
jsval slot = obj->getSlot(SLOT_TYPECODE);
@@ -5378,24 +5364,17 @@ CClosure::Create(JSContext* cx,
return NULL;
}
if (!JS_SetReservedSlot(cx, proto, SLOT_CLOSURECX,
PRIVATE_TO_JSVAL(cinfo->cx))) {
JS_DestroyContextNoGC(cinfo->cx);
return NULL;
}
-
- JS_ClearContextThread(cinfo->cx);
- }
-
-#ifdef DEBUG
- // We want *this* context's thread here so use cx instead of cinfo->cx.
- cinfo->cxThread = JS_GetContextThread(cx);
-#endif
+ }
// Prepare the error sentinel value. It's important to do this now, because
// we might be unable to convert the value to the proper type. If so, we want
// the caller to know about it _now_, rather than some uncertain time in the
// future when the error sentinel is actually needed.
if (!JSVAL_IS_VOID(errVal)) {
// Make sure the callback returns something.
@@ -5500,21 +5479,16 @@ CClosure::ClosureStub(ffi_cif* cif, void
// Retrieve the essentials from our closure object.
ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData);
JSContext* cx = cinfo->cx;
JSObject* typeObj = cinfo->typeObj;
JSObject* thisObj = cinfo->thisObj;
JSObject* jsfnObj = cinfo->jsfnObj;
- ScopedContextThread scopedThread(cx);
-
- // Assert that we're on the thread we were created from.
- JS_ASSERT(cinfo->cxThread == JS_GetContextThread(cx));
-
JS_AbortIfWrongThread(JS_GetRuntime(cx));
JSAutoRequest ar(cx);
JSAutoEnterCompartment ac;
if (!ac.enter(cx, jsfnObj))
return;
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -326,19 +326,16 @@ struct ClosureInfo
JSRuntime* rt; // Used in the destructor, where cx might have already
// been GCed.
JSObject* closureObj; // CClosure object
JSObject* typeObj; // FunctionType describing the C function
JSObject* thisObj; // 'this' object to use for the JS function call
JSObject* jsfnObj; // JS function
void* errResult; // Result that will be returned if the closure throws
ffi_closure* closure; // The C closure itself
-#ifdef DEBUG
- intptr_t cxThread; // The thread on which the context may be used
-#endif
// Anything conditionally freed in the destructor should be initialized to
// NULL here.
ClosureInfo(JSRuntime* runtime)
: rt(runtime)
, errResult(NULL)
, closure(NULL)
{}
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -55,24 +55,26 @@
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jsbool.h"
#include "jsclone.h"
#include "jscntxt.h"
#include "jsversion.h"
#include "jsdate.h"
+#include "jsdtoa.h"
#include "jsexn.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsgcmark.h"
#include "jsinterp.h"
#include "jsiter.h"
#include "jslock.h"
#include "jsmath.h"
+#include "jsnativestack.h"
#include "jsnum.h"
#include "json.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsprobes.h"
#include "jsproxy.h"
#include "jsscope.h"
#include "jsscript.h"
@@ -83,17 +85,18 @@
#include "jstypedarray.h"
#include "ds/LifoAlloc.h"
#include "builtin/MapObject.h"
#include "builtin/RegExp.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/BytecodeEmitter.h"
#include "js/MemoryMetrics.h"
-#include "mozilla/Util.h" // DebugOnly
+#include "mozilla/Util.h"
+#include "yarr/BumpPointerAllocator.h"
#include "jsatominlines.h"
#include "jsinferinlines.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "vm/RegExpObject-inl.h"
@@ -675,21 +678,37 @@ JS_IsBuiltinFunctionConstructor(JSFuncti
/*
* Has a new runtime ever been created? This flag is used to detect unsafe
* changes to js_CStringsAreUTF8 after a runtime has been created, and to
* control things that should happen only once across all runtimes.
*/
static JSBool js_NewRuntimeWasCalled = JS_FALSE;
JSRuntime::JSRuntime()
- : atomsCompartment(NULL),
+ : interrupt(0),
+ atomsCompartment(NULL),
+#ifdef JS_THREADSAFE
+ ownerThread_(NULL),
+#endif
+ tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+ execAlloc_(NULL),
+ bumpAlloc_(NULL),
+ repCache_(NULL),
+ interpreterFrames(NULL),
cxCallback(NULL),
compartmentCallback(NULL),
activityCallback(NULL),
activityCallbackArg(NULL),
+#ifdef JS_THREADSAFE
+ suspendCount(0),
+ requestDepth(0),
+# ifdef DEBUG
+ checkRequestDepth(0),
+# endif
+#endif
gcSystemAvailableChunkListHead(NULL),
gcUserAvailableChunkListHead(NULL),
gcKeepAtoms(0),
gcBytes(0),
gcTriggerBytes(0),
gcLastBytes(0),
gcMaxBytes(0),
gcMaxMallocBytes(0),
@@ -729,60 +748,54 @@ JSRuntime::JSRuntime()
positiveInfinityValue(UndefinedValue()),
emptyString(NULL),
debugMode(false),
profilingScripts(false),
hadOutOfMemory(false),
data(NULL),
#ifdef JS_THREADSAFE
gcLock(NULL),
- gcDone(NULL),
- requestDone(NULL),
requestCount(0),
- gcThread(NULL),
gcHelperThread(thisFromCtor()),
#endif
debuggerMutations(0),
securityCallbacks(NULL),
structuredCloneCallbacks(NULL),
telemetryCallback(NULL),
propertyRemovals(0),
- scriptFilenameTable(NULL),
-#ifdef JS_THREADSAFE
- scriptFilenameTableLock(NULL),
-#endif
thousandsSeparator(0),
decimalSeparator(0),
numGrouping(0),
anynameObject(NULL),
functionNamespaceObject(NULL),
-#ifdef JS_THREADSAFE
- interruptCounter(0),
-#else
- threadData(thisFromCtor()),
-#endif
+ waiveGCQuota(false),
+ dtoaState(NULL),
+ pendingProxyOperation(NULL),
trustedPrincipals_(NULL),
- wrapObjectCallback(NULL),
+ wrapObjectCallback(TransparentObjectWrapper),
preWrapObjectCallback(NULL),
preserveWrapperCallback(NULL),
+#ifdef DEBUG
+ noGCOrAllocationCheck(0),
+#endif
inOOMReport(0)
{
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
JS_INIT_CLIST(&contextList);
JS_INIT_CLIST(&debuggerList);
PodZero(&globalDebugHooks);
PodZero(&atomState);
}
bool
JSRuntime::init(uint32_t maxbytes)
{
#ifdef JS_THREADSAFE
- ownerThread_ = js_CurrentThreadId();
+ ownerThread_ = PR_GetCurrentThread();
#endif
#ifdef JS_METHODJIT_SPEW
JMCheckLogging();
#endif
if (!js_InitGC(this, maxbytes))
return false;
@@ -795,35 +808,38 @@ JSRuntime::init(uint32_t maxbytes)
}
atomsCompartment->isSystemCompartment = true;
atomsCompartment->setGCLastBytes(8192, GC_NORMAL);
if (!js_InitAtomState(this))
return false;
- wrapObjectCallback = js::TransparentObjectWrapper;
-
-#ifdef JS_THREADSAFE
- /* this is asymmetric with JS_ShutDown: */
- if (!js_SetupLocks(8, 16))
- return false;
-#endif
-
- debugMode = false;
- if (!js_InitThreads(this))
- return false;
if (!InitRuntimeNumberState(this))
return false;
+ dtoaState = js_NewDtoaState();
+ if (!dtoaState)
+ return false;
+
+ if (!stackSpace.init())
+ return false;
+
+ conservativeGC.nativeStackBase = GetNativeStackBase();
return true;
}
JSRuntime::~JSRuntime()
{
+ JS_ASSERT(onOwnerThread());
+
+ delete_<JSC::ExecutableAllocator>(execAlloc_);
+ delete_<WTF::BumpPointerAllocator>(bumpAlloc_);
+ JS_ASSERT(!repCache_);
+
#ifdef DEBUG
/* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
if (!JS_CLIST_IS_EMPTY(&contextList)) {
JSContext *cx, *iter = NULL;
uintN cxcount = 0;
while ((cx = js_ContextIterator(this, JS_TRUE, &iter)) != NULL) {
fprintf(stderr,
"JS API usage error: found live context at %p\n",
@@ -832,51 +848,53 @@ JSRuntime::~JSRuntime()
}
fprintf(stderr,
"JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n",
cxcount, (cxcount == 1) ? "" : "s");
}
#endif
FinishRuntimeNumberState(this);
- js_FinishThreads(this);
js_FinishAtomState(this);
+ if (dtoaState)
+ js_DestroyDtoaState(dtoaState);
+
js_FinishGC(this);
#ifdef JS_THREADSAFE
if (gcLock)
- JS_DESTROY_LOCK(gcLock);
- if (gcDone)
- JS_DESTROY_CONDVAR(gcDone);
- if (requestDone)
- JS_DESTROY_CONDVAR(requestDone);
+ PR_DestroyLock(gcLock);
#endif
}
#ifdef JS_THREADSAFE
void
JSRuntime::setOwnerThread()
{
- JS_ASSERT(ownerThread_ == (void *)-1);
- ownerThread_ = js_CurrentThreadId();
+ JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */
+ JS_ASSERT(requestDepth == 0);
+ ownerThread_ = PR_GetCurrentThread();
+ conservativeGC.nativeStackBase = GetNativeStackBase();
}
void
JSRuntime::clearOwnerThread()
{
JS_ASSERT(onOwnerThread());
- ownerThread_ = (void *)-1;
+ JS_ASSERT(requestDepth == 0);
+ ownerThread_ = (void *)0xc1ea12; /* "clear" */
+ conservativeGC.nativeStackBase = 0;
}
JS_FRIEND_API(bool)
JSRuntime::onOwnerThread() const
{
- return ownerThread_ == js_CurrentThreadId();
-}
-#endif
+ return ownerThread_ == PR_GetCurrentThread();
+}
+#endif /* JS_THREADSAFE */
JS_PUBLIC_API(JSRuntime *)
JS_NewRuntime(uint32_t maxbytes)
{
if (!js_NewRuntimeWasCalled) {
#ifdef DEBUG
/*
* This code asserts that the numbers associated with the error names
@@ -926,20 +944,16 @@ JS_DestroyRuntime(JSRuntime *rt)
Probes::destroyRuntime(rt);
Foreground::delete_(rt);
}
JS_PUBLIC_API(void)
JS_ShutDown(void)
{
Probes::shutdown();
-
-#ifdef JS_THREADSAFE
- js_CleanupLocks();
-#endif
PRMJ_NowShutdown();
}
JS_PUBLIC_API(void *)
JS_GetRuntimePrivate(JSRuntime *rt)
{
return rt->data;
}
@@ -973,76 +987,53 @@ JS::UserCompartmentCount(const JSRuntime
}
return n;
}
#ifdef JS_THREADSAFE
static void
StartRequest(JSContext *cx)
{
- JSThread *t = cx->thread();
- JS_ASSERT(CURRENT_THREAD_IS_ME(t));
-
- if (t->data.requestDepth) {
- t->data.requestDepth++;
+ JSRuntime *rt = cx->runtime;
+ JS_ASSERT(rt->onOwnerThread());
+
+ if (rt->requestDepth) {
+ rt->requestDepth++;
} else {
- JSRuntime *rt = cx->runtime;
AutoLockGC lock(rt);
- /* Wait until the GC is finished. */
- if (rt->gcThread != cx->thread()) {
- while (rt->gcThread)
- JS_AWAIT_GC_DONE(rt);
- }
-
/* Indicate that a request is running. */
rt->requestCount++;
- t->data.requestDepth = 1;
-
- /*
- * Adjust rt->interruptCounter to reflect any interrupts added while the
- * thread was suspended.
- */
- if (t->data.interruptFlags)
- JS_ATOMIC_INCREMENT(&rt->interruptCounter);
+ rt->requestDepth = 1;
if (rt->requestCount == 1 && rt->activityCallback)
rt->activityCallback(rt->activityCallbackArg, true);
}
}
static void
StopRequest(JSContext *cx)
{
- JSThread *t = cx->thread();
- JS_ASSERT(CURRENT_THREAD_IS_ME(t));
- JS_ASSERT(t->data.requestDepth != 0);
- if (t->data.requestDepth != 1) {
- t->data.requestDepth--;
+ JSRuntime *rt = cx->runtime;
+ JS_ASSERT(rt->onOwnerThread());
+ JS_ASSERT(rt->requestDepth != 0);
+ if (rt->requestDepth != 1) {
+ rt->requestDepth--;
} else {
- t->data.conservativeGC.updateForRequestEnd(t->suspendCount);
+ rt->conservativeGC.updateForRequestEnd(rt->suspendCount);
/* Lock before clearing to interlock with ClaimScope, in jslock.c. */
- JSRuntime *rt = cx->runtime;
AutoLockGC lock(rt);
- t->data.requestDepth = 0;
-
- /*
- * Adjust rt->interruptCounter to reflect any interrupts added while the
- * thread still had active requests.
- */
- if (t->data.interruptFlags)
- JS_ATOMIC_DECREMENT(&rt->interruptCounter);
+ rt->requestDepth = 0;
/* Give the GC a chance to run if this was the last request running. */
JS_ASSERT(rt->requestCount > 0);
rt->requestCount--;
if (rt->requestCount == 0) {
- JS_NOTIFY_REQUEST_DONE(rt);
if (rt->activityCallback)
rt->activityCallback(rt->activityCallbackArg, false);
}
}
}
#endif /* JS_THREADSAFE */
JS_PUBLIC_API(void)
@@ -1073,55 +1064,66 @@ JS_YieldRequest(JSContext *cx)
JS_ResumeRequest(cx, JS_SuspendRequest(cx));
#endif
}
JS_PUBLIC_API(jsrefcount)
JS_SuspendRequest(JSContext *cx)
{
#ifdef JS_THREADSAFE
- JSThread *t = cx->thread();
- JS_ASSERT(CURRENT_THREAD_IS_ME(t));
-
- jsrefcount saveDepth = t->data.requestDepth;
+ JSRuntime *rt = cx->runtime;
+ JS_ASSERT(rt->onOwnerThread());
+
+ jsrefcount saveDepth = rt->requestDepth;
if (!saveDepth)
return 0;
- t->suspendCount++;
- t->data.requestDepth = 1;
+ rt->suspendCount++;
+ rt->requestDepth = 1;
StopRequest(cx);
return saveDepth;
#else
return 0;
#endif
}
JS_PUBLIC_API(void)
JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth)
{
#ifdef JS_THREADSAFE
- JSThread *t = cx->thread();
- JS_ASSERT(CURRENT_THREAD_IS_ME(t));
+ JSRuntime *rt = cx->runtime;
+ JS_ASSERT(rt->onOwnerThread());
if (saveDepth == 0)
return;
JS_ASSERT(saveDepth >= 1);
- JS_ASSERT(!t->data.requestDepth);
- JS_ASSERT(t->suspendCount);
+ JS_ASSERT(!rt->requestDepth);
+ JS_ASSERT(rt->suspendCount);
StartRequest(cx);
- t->data.requestDepth = saveDepth;
- t->suspendCount--;
+ rt->requestDepth = saveDepth;
+ rt->suspendCount--;
#endif
}
JS_PUBLIC_API(JSBool)
-JS_IsInRequest(JSContext *cx)
+JS_IsInRequest(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
- JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
- return JS_THREAD_DATA(cx)->requestDepth != 0;
+ JS_ASSERT(rt->onOwnerThread());
+ return rt->requestDepth != 0;
+#else
+ return false;
+#endif
+}
+
+JS_PUBLIC_API(JSBool)
+JS_IsInSuspendedRequest(JSRuntime *rt)
+{
+#ifdef JS_THREADSAFE
+ JS_ASSERT(rt->onOwnerThread());
+ return rt->suspendCount != 0;
#else
return false;
#endif
}
JS_PUBLIC_API(JSContextCallback)
JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback)
{
@@ -3002,33 +3004,29 @@ JS_SetThreadStackLimit(JSContext *cx, ui
limitAddr = UINTPTR_MAX;
#endif
cx->stackLimit = limitAddr;
}
JS_PUBLIC_API(void)
JS_SetNativeStackQuota(JSContext *cx, size_t stackSize)
{
-#ifdef JS_THREADSAFE
- JS_ASSERT(cx->thread());
-#endif
-
#if JS_STACK_GROWTH_DIRECTION > 0
if (stackSize == 0) {
cx->stackLimit = UINTPTR_MAX;
} else {
- uintptr_t stackBase = reinterpret_cast<uintptr_t>(JS_THREAD_DATA(cx)->nativeStackBase);
+ uintptr_t stackBase = cx->runtime->nativeStackBase;
JS_ASSERT(stackBase <= size_t(-1) - stackSize);
cx->stackLimit = stackBase + stackSize - 1;
}
#else
if (stackSize == 0) {
cx->stackLimit = 0;
} else {
- uintptr_t stackBase = reinterpret_cast<uintptr_t>(JS_THREAD_DATA(cx)->nativeStackBase);
+ uintptr_t stackBase = uintptr_t(cx->runtime->conservativeGC.nativeStackBase);
JS_ASSERT(stackBase >= stackSize);
cx->stackLimit = stackBase - (stackSize - 1);
}
#endif
}
/************************************************************************/
@@ -5501,19 +5499,16 @@ JS_New(JSContext *cx, JSObject *ctor, ui
}
return &args.rval().toObject();
}
JS_PUBLIC_API(JSOperationCallback)
JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback)
{
-#ifdef JS_THREADSAFE
- JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
-#endif
JSOperationCallback old = cx->operationCallback;
cx->operationCallback = callback;
return old;
}
JS_PUBLIC_API(JSOperationCallback)
JS_GetOperationCallback(JSContext *cx)
{
@@ -5521,36 +5516,31 @@ JS_GetOperationCallback(JSContext *cx)
}
JS_PUBLIC_API(void)
JS_TriggerOperationCallback(JSContext *cx)
{
#ifdef JS_THREADSAFE
AutoLockGC lock(cx->runtime);
#endif
- TriggerOperationCallback(cx);
+ cx->runtime->triggerOperationCallback();
}
JS_PUBLIC_API(void)
-JS_TriggerAllOperationCallbacks(JSRuntime *rt)
+JS_TriggerRuntimeOperationCallback(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
AutoLockGC lock(rt);
#endif
- TriggerAllOperationCallbacks(rt);
+ rt->triggerOperationCallback();
}
JS_PUBLIC_API(JSBool)
JS_IsRunning(JSContext *cx)
{
-#ifdef JS_THREADSAFE
- if (!cx->thread())
- return false;
-#endif
-
StackFrame *fp = cx->maybefp();
while (fp && fp->isDummyFrame())
fp = fp->prev();
return fp != NULL;
}
JS_PUBLIC_API(JSBool)
JS_SaveFrameChain(JSContext *cx)
@@ -6573,60 +6563,23 @@ JS_ThrowStopIteration(JSContext *cx)
{
AssertNoGC(cx);
return js_ThrowStopIteration(cx);
}
JS_PUBLIC_API(intptr_t)
JS_GetCurrentThread()
{
- return reinterpret_cast<intptr_t>(js_CurrentThreadId());
-}
-
-/*
- * Get the owning thread id of a context. Returns 0 if the context is not
- * owned by any thread.
- */
-JS_PUBLIC_API(intptr_t)
-JS_GetContextThread(JSContext *cx)
-{
#ifdef JS_THREADSAFE
- return cx->thread() ? reinterpret_cast<intptr_t>(cx->thread()->id) : 0;
+ return reinterpret_cast<intptr_t>(PR_GetCurrentThread());
#else
return 0;
#endif
}
-/*
- * Set the current thread as the owning thread of a context. Returns the
- * old owning thread id, or -1 if the operation failed.
- */
-JS_PUBLIC_API(intptr_t)
-JS_SetContextThread(JSContext *cx)
-{
- /* This function can be called by a finalizer. */
- JS_AbortIfWrongThread(cx->runtime);
-
-#ifdef JS_THREADSAFE
- JS_ASSERT(!cx->outstandingRequests);
- if (cx->thread()) {
- JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
- return reinterpret_cast<intptr_t>(cx->thread()->id);
- }
-
- if (!js_InitContextThreadAndLockGC(cx)) {
- js_ReportOutOfMemory(cx);
- return -1;
- }
-
- JS_UNLOCK_GC(cx->runtime);
-#endif
- return 0;
-}
-
extern JS_PUBLIC_API(void)
JS_ClearRuntimeThread(JSRuntime *rt)
{
AssertNoGC(rt);
#ifdef JS_THREADSAFE
rt->clearOwnerThread();
#endif
}
@@ -6644,54 +6597,16 @@ extern JS_NEVER_INLINE JS_PUBLIC_API(voi
JS_AbortIfWrongThread(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
if (!rt->onOwnerThread())
JS_Assert("rt->onOwnerThread()", __FILE__, __LINE__);
#endif
}
-JS_PUBLIC_API(intptr_t)
-JS_ClearContextThread(JSContext *cx)
-{
- JS_AbortIfWrongThread(cx->runtime);
- AssertNoGC(cx);
-
-#ifdef JS_THREADSAFE
- /*
- * cx must have exited all requests it entered and, if cx is associated
- * with a thread, this must be called only from that thread. If not, this
- * is a harmless no-op.
- */
- JS_ASSERT(cx->outstandingRequests == 0);
- JSThread *t = cx->thread();
- if (!t)
- return 0;
- JS_ASSERT(CURRENT_THREAD_IS_ME(t));
-
- /*
- * We must not race with a GC that accesses cx->thread for all threads,
- * see bug 476934.
- */
- JSRuntime *rt = cx->runtime;
- AutoLockGC lock(rt);
- js_WaitForGC(rt);
- js_ClearContextThread(cx);
- JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth);
-
- /*
- * We can access t->id as long as the GC lock is held and we cannot race
- * with the GC that may delete t.
- */
- return reinterpret_cast<intptr_t>(t->id);
-#else
- return 0;
-#endif
-}
-
#ifdef JS_GC_ZEAL
JS_PUBLIC_API(void)
JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment)
{
bool schedule = zeal >= js::gc::ZealAllocThreshold && zeal < js::gc::ZealVerifierThreshold;
cx->runtime->gcZeal_ = zeal;
cx->runtime->gcZealFrequency = frequency;
cx->runtime->gcNextScheduled = schedule ? frequency : 0;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2318,16 +2318,19 @@ extern JS_PUBLIC_API(void)
JS_DestroyRuntime(JSRuntime *rt);
extern JS_PUBLIC_API(void)
JS_ShutDown(void);
JS_PUBLIC_API(void *)
JS_GetRuntimePrivate(JSRuntime *rt);
+extern JS_PUBLIC_API(JSRuntime *)
+JS_GetRuntime(JSContext *cx);
+
JS_PUBLIC_API(void)
JS_SetRuntimePrivate(JSRuntime *rt, void *data);
extern JS_PUBLIC_API(void)
JS_BeginRequest(JSContext *cx);
extern JS_PUBLIC_API(void)
JS_EndRequest(JSContext *cx);
@@ -2338,17 +2341,20 @@ JS_YieldRequest(JSContext *cx);
extern JS_PUBLIC_API(jsrefcount)
JS_SuspendRequest(JSContext *cx);
extern JS_PUBLIC_API(void)
JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth);
extern JS_PUBLIC_API(JSBool)
-JS_IsInRequest(JSContext *cx);
+JS_IsInRequest(JSRuntime *rt);
+
+extern JS_PUBLIC_API(JSBool)
+JS_IsInSuspendedRequest(JSRuntime *rt);
#ifdef __cplusplus
JS_END_EXTERN_C
inline bool
IsPoisonedId(jsid iden)
{
if (JSID_IS_STRING(iden))
@@ -2420,24 +2426,24 @@ class JSAutoSuspendRequest {
#endif
};
class JSAutoCheckRequest {
public:
JSAutoCheckRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) {
#if defined JS_THREADSAFE && defined DEBUG
mContext = cx;
- JS_ASSERT(JS_IsInRequest(cx));
+ JS_ASSERT(JS_IsInRequest(JS_GetRuntime(cx)));
#endif
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
~JSAutoCheckRequest() {
#if defined JS_THREADSAFE && defined DEBUG
- JS_ASSERT(JS_IsInRequest(mContext));
+ JS_ASSERT(JS_IsInRequest(JS_GetRuntime(mContext)));
#endif
}
private:
#if defined JS_THREADSAFE && defined DEBUG
JSContext *mContext;
#endif
@@ -4484,17 +4490,17 @@ JS_SetOperationCallback(JSContext *cx, J
extern JS_PUBLIC_API(JSOperationCallback)
JS_GetOperationCallback(JSContext *cx);
extern JS_PUBLIC_API(void)
JS_TriggerOperationCallback(JSContext *cx);
extern JS_PUBLIC_API(void)
-JS_TriggerAllOperationCallbacks(JSRuntime *rt);
+JS_TriggerRuntimeOperationCallback(JSRuntime *rt);
extern JS_PUBLIC_API(JSBool)
JS_IsRunning(JSContext *cx);
/*
* Saving and restoring frame chains.
*
* These two functions are used to set aside cx's call stack while that stack
@@ -5257,34 +5263,16 @@ JS_ThrowReportedError(JSContext *cx, con
*/
extern JS_PUBLIC_API(JSBool)
JS_ThrowStopIteration(JSContext *cx);
extern JS_PUBLIC_API(intptr_t)
JS_GetCurrentThread();
/*
- * Associate the current thread with the given context. This is done
- * implicitly by JS_NewContext.
- *
- * Returns the old thread id for this context, which should be treated as
- * an opaque value. This value is provided for comparison to 0, which
- * indicates that ClearContextThread has been called on this context
- * since the last SetContextThread, or non-0, which indicates the opposite.
- */
-extern JS_PUBLIC_API(intptr_t)
-JS_GetContextThread(JSContext *cx);
-
-extern JS_PUBLIC_API(intptr_t)
-JS_SetContextThread(JSContext *cx);
-
-extern JS_PUBLIC_API(intptr_t)
-JS_ClearContextThread(JSContext *cx);
-
-/*
* A JS runtime always has an "owner thread". The owner thread is set when the
* runtime is created (to the current thread) and practically all entry points
* into the JS engine check that a runtime (or anything contained in the
* runtime: context, compartment, object, etc) is only touched by its owner
* thread. Embeddings may check this invariant outside the JS engine by calling
* JS_AbortIfWrongThread (which will abort if not on the owner thread, even for
* non-debug builds).
*
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -328,21 +328,18 @@ JSBool
js_InitAtomState(JSRuntime *rt)
{
JSAtomState *state = &rt->atomState;
JS_ASSERT(!state->atoms.initialized());
if (!state->atoms.init(JS_STRING_HASH_COUNT))
return false;
-#ifdef JS_THREADSAFE
- js_InitLock(&state->lock);
-#endif
JS_ASSERT(state->atoms.initialized());
- return JS_TRUE;
+ return true;
}
void
js_FinishAtomState(JSRuntime *rt)
{
JSAtomState *state = &rt->atomState;
if (!state->atoms.initialized()) {
@@ -350,20 +347,16 @@ js_FinishAtomState(JSRuntime *rt)
* We are called with uninitialized state when JS_NewRuntime fails and
* calls JS_DestroyRuntime on a partially initialized runtime.
*/
return;
}
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront())
r.front().asPtr()->finalize(rt);
-
-#ifdef JS_THREADSAFE
- js_FinishLock(&state->lock);
-#endif
}
bool
js_InitCommonAtoms(JSContext *cx)
{
JSAtomState *state = &cx->runtime->atomState;
JSAtom **atoms = state->commonAtomsStart();
for (size_t i = 0; i < ArrayLength(js_common_atom_names); i++, atoms++) {
@@ -434,17 +427,16 @@ js_SweepAtomState(JSContext *cx)
bool
AtomIsInterned(JSContext *cx, JSAtom *atom)
{
/* We treat static strings as interned because they're never collected. */
if (StaticStrings::isStatic(atom))
return true;
- AutoLockAtomsCompartment lock(cx);
AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(atom);
if (!p)
return false;
return p->isTagged();
}
enum OwnCharsBehavior
@@ -463,18 +455,16 @@ static JSAtom *
AtomizeInline(JSContext *cx, const jschar **pchars, size_t length,
InternBehavior ib, OwnCharsBehavior ocb = CopyChars)
{
const jschar *chars = *pchars;
if (JSAtom *s = cx->runtime->staticStrings.lookup(chars, length))
return s;
- AutoLockAtomsCompartment lock(cx);
-
AtomSet &atoms = cx->runtime->atomState.atoms;
AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length));
if (p) {
JSAtom *atom = p->asPtr();
p->setTagged(bool(ib));
return atom;
}
@@ -523,19 +513,16 @@ JSAtom *
js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
{
if (str->isAtom()) {
JSAtom &atom = str->asAtom();
/* N.B. static atoms are effectively always interned. */
if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
return &atom;
- /* Here we have to check whether the atom is already interned. */
- AutoLockAtomsCompartment lock(cx);
-
AtomSet &atoms = cx->runtime->atomState.atoms;
AtomSet::Ptr p = atoms.lookup(AtomHasher::Lookup(&atom));
JS_ASSERT(p); /* Non-static atom must exist in atom state set. */
JS_ASSERT(p->asPtr() == &atom);
JS_ASSERT(ib == InternAtom);
p->setTagged(bool(ib));
return &atom;
}
@@ -605,19 +592,19 @@ js_AtomizeChars(JSContext *cx, const jsc
return AtomizeInline(cx, &chars, length, ib);
}
JSAtom *
js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
{
if (JSAtom *atom = cx->runtime->staticStrings.lookup(chars, length))
return atom;
- AutoLockAtomsCompartment lock(cx);
- AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length));
- return p ? p->asPtr() : NULL;
+ if (AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length)))
+ return p->asPtr();
+ return NULL;
}
#ifdef DEBUG
JS_FRIEND_API(void)
js_DumpAtoms(JSContext *cx, FILE *fp)
{
JSAtomState *state = &cx->runtime->atomState;
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -235,20 +235,16 @@ enum FlationCoding
};
} /* namespace js */
struct JSAtomState
{
js::AtomSet atoms;
-#ifdef JS_THREADSAFE
- JSThinLock lock;
-#endif
-
/*
* From this point until the end of struct definition the struct must
* contain only js::PropertyName fields. We use this to access the storage
* occupied by the common atoms in js_FinishCommonAtoms.
*
* js_common_atom_names defined in jsatom.cpp contains C strings for atoms
* in the order of atom fields here. Therefore you must update that array
* if you change member order here.
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -59,25 +59,23 @@
#include "jstypes.h"
#include "jsutil.h"
#include "jsclist.h"
#include "jsprf.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsversion.h"
#include "jsdbgapi.h"
-#include "jsdtoa.h"
#include "jsexn.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsgcmark.h"
#include "jsiter.h"
#include "jslock.h"
#include "jsmath.h"
-#include "jsnativestack.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jspubtd.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
@@ -91,343 +89,100 @@
#include "jsatominlines.h"
#include "jscntxtinlines.h"
#include "jscompartment.h"
#include "jsobjinlines.h"
using namespace js;
using namespace js::gc;
-namespace js {
-
-ThreadData::ThreadData(JSRuntime *rt)
- : rt(rt),
- interruptFlags(0),
-#ifdef JS_THREADSAFE
- requestDepth(0),
-#endif
- tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
- execAlloc(NULL),
- bumpAlloc(NULL),
- repCache(NULL),
- dtoaState(NULL),
- nativeStackBase(GetNativeStackBase()),
- pendingProxyOperation(NULL),
- interpreterFrames(NULL)
-{
-#ifdef DEBUG
- noGCOrAllocationCheck = 0;
-#endif
-}
-
-ThreadData::~ThreadData()
+void
+JSRuntime::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
+ size_t *regexpCode, size_t *stackCommitted)
{
- JS_ASSERT(!repCache);
-
- rt->delete_<JSC::ExecutableAllocator>(execAlloc);
- rt->delete_<WTF::BumpPointerAllocator>(bumpAlloc);
-
- if (dtoaState)
- js_DestroyDtoaState(dtoaState);
-}
-
-bool
-ThreadData::init()
-{
- JS_ASSERT(!repCache);
- return stackSpace.init() && !!(dtoaState = js_NewDtoaState());
-}
-
-#ifdef JS_THREADSAFE
-void
-ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
- size_t *regexpCode, size_t *stackCommitted)
-{
- /*
- * There are other ThreadData members that could be measured; the ones
- * below have been seen by DMD to be worth measuring. More stuff may be
- * added later.
- */
-
/*
* The computedSize is 0 because sizeof(DtoaState) isn't available here and
* it's not worth making it available.
*/
if (normal)
*normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0);
if (temporary)
*temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
if (regexpCode) {
size_t method = 0, regexp = 0, unused = 0;
- if (execAlloc)
- execAlloc->sizeOfCode(&method, ®exp, &unused);
+ if (execAlloc_)
+ execAlloc_->sizeOfCode(&method, ®exp, &unused);
JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */
*regexpCode = regexp + unused;
}
if (stackCommitted)
*stackCommitted = stackSpace.sizeOfCommitted();
}
-#endif
-void
-ThreadData::triggerOperationCallback(JSRuntime *rt)
+JS_FRIEND_API(void)
+JSRuntime::triggerOperationCallback()
{
- JS_ASSERT(rt == this->rt);
-
/*
- * Use JS_ATOMIC_SET and JS_ATOMIC_INCREMENT in the hope that it ensures
- * the write will become immediately visible to other processors polling
- * the flag. Note that we only care about visibility here, not read/write
- * ordering: this field can only be written with the GC lock held.
+ * Use JS_ATOMIC_SET in the hope that it ensures the write will become
+ * immediately visible to other processors polling the flag.
*/
- if (interruptFlags)
- return;
- JS_ATOMIC_SET(&interruptFlags, 1);
-
-#ifdef JS_THREADSAFE
- /* rt->interruptCounter does not reflect suspended threads. */
- if (requestDepth != 0)
- JS_ATOMIC_INCREMENT(&rt->interruptCounter);
-#endif
+ JS_ATOMIC_SET(&interrupt, 1);
}
JSC::ExecutableAllocator *
-ThreadData::createExecutableAllocator(JSContext *cx)
+JSRuntime::createExecutableAllocator(JSContext *cx)
{
- JS_ASSERT(!execAlloc);
- JS_ASSERT(cx->runtime == rt);
+ JS_ASSERT(!execAlloc_);
+ JS_ASSERT(cx->runtime == this);
- execAlloc = rt->new_<JSC::ExecutableAllocator>();
- if (!execAlloc)
+ execAlloc_ = new_<JSC::ExecutableAllocator>();
+ if (!execAlloc_)
js_ReportOutOfMemory(cx);
- return execAlloc;
+ return execAlloc_;
}
WTF::BumpPointerAllocator *
-ThreadData::createBumpPointerAllocator(JSContext *cx)
+JSRuntime::createBumpPointerAllocator(JSContext *cx)
{
- JS_ASSERT(!bumpAlloc);
- JS_ASSERT(cx->runtime == rt);
+ JS_ASSERT(!bumpAlloc_);
+ JS_ASSERT(cx->runtime == this);
- bumpAlloc = rt->new_<WTF::BumpPointerAllocator>();
- if (!bumpAlloc)
+ bumpAlloc_ = new_<WTF::BumpPointerAllocator>();
+ if (!bumpAlloc_)
js_ReportOutOfMemory(cx);
- return bumpAlloc;
+ return bumpAlloc_;
}
RegExpPrivateCache *
-ThreadData::createRegExpPrivateCache(JSContext *cx)
+JSRuntime::createRegExpPrivateCache(JSContext *cx)
{
- JS_ASSERT(!repCache);
- JS_ASSERT(cx->runtime == rt);
+ JS_ASSERT(!repCache_);
+ JS_ASSERT(cx->runtime == this);
- RegExpPrivateCache *newCache = rt->new_<RegExpPrivateCache>(rt);
+ RegExpPrivateCache *newCache = new_<RegExpPrivateCache>(this);
if (!newCache || !newCache->init()) {
js_ReportOutOfMemory(cx);
- rt->delete_<RegExpPrivateCache>(newCache);
+ delete_<RegExpPrivateCache>(newCache);
return NULL;
}
- repCache = newCache;
- return repCache;
+ repCache_ = newCache;
+ return repCache_;
}
-void
-ThreadData::purgeRegExpPrivateCache()
-{
- rt->delete_<RegExpPrivateCache>(repCache);
- repCache = NULL;
-}
-
-} /* namespace js */
-
JSScript *
js_GetCurrentScript(JSContext *cx)
{
return cx->hasfp() ? cx->fp()->maybeScript() : NULL;
}
-
-#ifdef JS_THREADSAFE
-
-void
-JSThread::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
- size_t *regexpCode, size_t *stackCommitted)
-{
- data.sizeOfExcludingThis(mallocSizeOf, normal, temporary, regexpCode, stackCommitted);
- if (normal)
- *normal += mallocSizeOf(this, sizeof(JSThread));
-}
-
-JSThread *
-js_CurrentThreadAndLockGC(JSRuntime *rt)
-{
- void *id = js_CurrentThreadId();
- JS_LOCK_GC(rt);
-
- /*
- * We must not race with a GC that accesses cx->thread for JSContext
- * instances on all threads, see bug 476934.
- */
- js_WaitForGC(rt);
-
- JSThread *thread;
- JSThread::Map::AddPtr p = rt->threads.lookupForAdd(id);
- if (p) {
- thread = p->value;
-
- /*
- * If thread has no contexts, it might be left over from a previous
- * thread with the same id but a different stack address.
- */
- if (JS_CLIST_IS_EMPTY(&thread->contextList))
- thread->data.nativeStackBase = GetNativeStackBase();
- } else {
- JS_UNLOCK_GC(rt);
-
- thread = OffTheBooks::new_<JSThread>(rt, id);
- if (!thread || !thread->init()) {
- Foreground::delete_(thread);
- return NULL;
- }
- JS_LOCK_GC(rt);
- js_WaitForGC(rt);
- if (!rt->threads.relookupOrAdd(p, id, thread)) {
- JS_UNLOCK_GC(rt);
- Foreground::delete_(thread);
- return NULL;
- }
-
- /* Another thread cannot add an entry for the current thread id. */
- JS_ASSERT(p->value == thread);
- }
- JS_ASSERT(thread->id == id);
-
- /*
- * We skip the assert under glibc due to an apparent bug there, see
- * bug 608526.
- */
-#ifndef __GLIBC__
- JS_ASSERT(GetNativeStackBase() == thread->data.nativeStackBase);
-#endif
-
- return thread;
-}
-
-JSBool
-js_InitContextThreadAndLockGC(JSContext *cx)
-{
- JSThread *thread = js_CurrentThreadAndLockGC(cx->runtime);
- if (!thread)
- return false;
-
- JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
- cx->setThread(thread);
- return true;
-}
-
-void
-JSContext::setThread(JSThread *thread)
-{
- thread_ = thread;
- stack.threadReset();
-}
-
-void
-js_ClearContextThread(JSContext *cx)
-{
- JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
- JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
- cx->setThread(NULL);
-}
-
-#endif /* JS_THREADSAFE */
-
-ThreadData *
-js_CurrentThreadData(JSRuntime *rt)
-{
-#ifdef JS_THREADSAFE
- JSThread *thread = js_CurrentThreadAndLockGC(rt);
- if (!thread)
- return NULL;
-
- return &thread->data;
-#else
- return &rt->threadData;
-#endif
-}
-
-JSBool
-js_InitThreads(JSRuntime *rt)
-{
-#ifdef JS_THREADSAFE
- return rt->threads.init(4);
-#else
- return rt->threadData.init();
-#endif
-}
-
-void
-js_FinishThreads(JSRuntime *rt)
-{
-#ifdef JS_THREADSAFE
- if (!rt->threads.initialized())
- return;
- for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
- JSThread *thread = r.front().value;
- Foreground::delete_(thread);
- }
- rt->threads.clear();
-#endif
-}
-
-void
-js_PurgeThreads(JSContext *cx)
-{
-#ifdef JS_THREADSAFE
- for (JSThread::Map::Enum e(cx->runtime->threads);
- !e.empty();
- e.popFront()) {
- JSThread *thread = e.front().value;
-
- if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
- JS_ASSERT(cx->thread() != thread);
- Foreground::delete_(thread);
- e.removeFront();
- } else {
- thread->data.purge(cx);
- }
- }
-#else
- cx->runtime->threadData.purge(cx);
-#endif
-}
-
-void
-js_PurgeThreads_PostGlobalSweep(JSContext *cx)
-{
-#ifdef JS_THREADSAFE
- for (JSThread::Map::Enum e(cx->runtime->threads);
- !e.empty();
- e.popFront())
- {
- JSThread *thread = e.front().value;
-
- JS_ASSERT(!JS_CLIST_IS_EMPTY(&thread->contextList));
- thread->data.purgeRegExpPrivateCache();
- }
-#else
- cx->runtime->threadData.purgeRegExpPrivateCache();
-#endif
-}
-
JSContext *
js_NewContext(JSRuntime *rt, size_t stackChunkSize)
{
JS_AbortIfWrongThread(rt);
/*
* We need to initialize the new context fully before adding it to the
* runtime list. After that it can be accessed from another thread via
@@ -439,30 +194,22 @@ js_NewContext(JSRuntime *rt, size_t stac
JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT);
if (!cx->busyArrays.init()) {
Foreground::delete_(cx);
return NULL;
}
-#ifdef JS_THREADSAFE
- if (!js_InitContextThreadAndLockGC(cx)) {
- Foreground::delete_(cx);
- return NULL;
- }
-#endif
-
/*
* Here the GC lock is still held after js_InitContextThreadAndLockGC took it and
* the GC is not running on another thread.
*/
bool first = JS_CLIST_IS_EMPTY(&rt->contextList);
JS_APPEND_LINK(&cx->link, &rt->contextList);
- JS_UNLOCK_GC(rt);
js_InitRandom(cx);
/*
* If cx is the first context on this runtime, initialize well-known atoms,
* keywords, numbers, and strings. If one of these steps should fail, the
* runtime will be left in a partially initialized state, with zeroes and
* nulls stored in the default-initialized remainder of the struct. We'll
@@ -496,60 +243,40 @@ js_NewContext(JSRuntime *rt, size_t stac
}
void
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
{
JSRuntime *rt = cx->runtime;
JS_AbortIfWrongThread(rt);
- JSContextCallback cxCallback;
-
JS_ASSERT(!cx->enumerators);
#ifdef JS_THREADSAFE
/*
- * For API compatibility we allow to destroy contexts without a thread in
- * optimized builds. We assume that the embedding knows that an OOM error
- * cannot happen in JS_SetContextThread.
- */
- JS_ASSERT(cx->thread() && CURRENT_THREAD_IS_ME(cx->thread()));
- if (!cx->thread())
- JS_SetContextThread(cx);
-
- /*
* For API compatibility we support destroying contexts with non-zero
* cx->outstandingRequests but we assume that all JS_BeginRequest calls
* on this cx contributes to cx->thread->data.requestDepth and there is no
* JS_SuspendRequest calls that set aside the counter.
*/
- JS_ASSERT(cx->outstandingRequests <= cx->thread()->data.requestDepth);
+ JS_ASSERT(cx->outstandingRequests <= cx->runtime->requestDepth);
#endif
if (mode != JSDCM_NEW_FAILED) {
- cxCallback = rt->cxCallback;
- if (cxCallback) {
+ if (JSContextCallback cxCallback = rt->cxCallback) {
/*
* JSCONTEXT_DESTROY callback is not allowed to fail and must
* return true.
*/
DebugOnly<JSBool> callbackStatus = cxCallback(cx, JSCONTEXT_DESTROY);
JS_ASSERT(callbackStatus);
}
}
JS_LOCK_GC(rt);
-#ifdef JS_THREADSAFE
- /*
- * Typically we are called outside a request, so ensure that the GC is not
- * running before removing the context from rt->contextList, see bug 477021.
- */
- if (cx->thread()->data.requestDepth == 0)
- js_WaitForGC(rt);
-#endif
JS_REMOVE_LINK(&cx->link);
bool last = !rt->hasContexts();
if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
#ifdef JS_THREADSAFE
|| cx->outstandingRequests != 0
#endif
) {
JS_ASSERT(!rt->gcRunning);
@@ -561,17 +288,17 @@ js_DestroyContext(JSContext *cx, JSDestr
if (last) {
#ifdef JS_THREADSAFE
/*
* If this thread is not in a request already, begin one now so
* that we wait for any racing GC started on a not-last context to
* finish, before we plow ahead and unpin atoms.
*/
- if (cx->thread()->data.requestDepth == 0)
+ if (cx->runtime->requestDepth == 0)
JS_BeginRequest(cx);
#endif
/*
* Dump remaining type inference results first. This printing
* depends on atoms still existing.
*/
{
@@ -602,27 +329,19 @@ js_DestroyContext(JSContext *cx, JSDestr
JS_LOCK_GC(rt);
} else {
if (mode == JSDCM_FORCE_GC)
js_GC(cx, NULL, GC_NORMAL, gcstats::DESTROYCONTEXT);
else if (mode == JSDCM_MAYBE_GC)
JS_MaybeGC(cx);
JS_LOCK_GC(rt);
- js_WaitForGC(rt);
}
}
#ifdef JS_THREADSAFE
-#ifdef DEBUG
- JSThread *t = cx->thread();
-#endif
- js_ClearContextThread(cx);
- JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth);
-#endif
-#ifdef JS_THREADSAFE
rt->gcHelperThread.waitBackgroundSweepEnd();
#endif
JS_UNLOCK_GC(rt);
Foreground::delete_(cx);
}
JSContext *
js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
@@ -640,17 +359,17 @@ js_ContextIterator(JSRuntime *rt, JSBool
}
JS_FRIEND_API(JSContext *)
js_NextActiveContext(JSRuntime *rt, JSContext *cx)
{
JSContext *iter = cx;
#ifdef JS_THREADSAFE
while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
- if (cx->outstandingRequests && cx->thread()->data.requestDepth)
+ if (cx->outstandingRequests && cx->runtime->requestDepth)
break;
}
return cx;
#else
return js_ContextIterator(rt, JS_FALSE, &iter);
#endif
}
@@ -733,19 +452,16 @@ PopulateReportBlame(JSContext *cx, JSErr
* Instead we just invoke the errorReporter with an "Out Of Memory"
* type message, and then hope the process ends swiftly.
*/
void
js_ReportOutOfMemory(JSContext *cx)
{
cx->runtime->hadOutOfMemory = true;
- /* AtomizeInline can call this indirectly when it creates the string. */
- AutoUnlockAtomsCompartmentWhenLocked unlockAtomsCompartment(cx);
-
JSErrorReport report;
JSErrorReporter onError = cx->errorReporter;
/* Get the message for this error, but we won't expand any arguments. */
const JSErrorFormatString *efs =
js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
const char *msg = efs ? efs->format : "Out of memory";
@@ -1201,33 +917,27 @@ js_GetErrorMessage(void *userRef, const
if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
return &js_ErrorFormatString[errorNumber];
return NULL;
}
JSBool
js_InvokeOperationCallback(JSContext *cx)
{
- JSRuntime *rt = cx->runtime;
- ThreadData *td = JS_THREAD_DATA(cx);
+ JS_ASSERT_REQUEST_DEPTH(cx);
- JS_ASSERT_REQUEST_DEPTH(cx);
- JS_ASSERT(td->interruptFlags != 0);
+ JSRuntime *rt = cx->runtime;
+ JS_ASSERT(rt->interrupt != 0);
/*
* Reset the callback counter first, then run GC and yield. If another
* thread is racing us here we will accumulate another callback request
* which will be serviced at the next opportunity.
*/
- JS_LOCK_GC(rt);
- td->interruptFlags = 0;
-#ifdef JS_THREADSAFE
- JS_ATOMIC_DECREMENT(&rt->interruptCounter);
-#endif
- JS_UNLOCK_GC(rt);
+ JS_ATOMIC_SET(&rt->interrupt, 0);
if (rt->gcIsNeeded)
js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason);
#ifdef JS_THREADSAFE
/*
* We automatically yield the current context every time the operation
* callback is hit since we might be called as a result of an impending
@@ -1251,53 +961,21 @@ js_InvokeOperationCallback(JSContext *cx
return !cb || cb(cx);
}
JSBool
js_HandleExecutionInterrupt(JSContext *cx)
{
JSBool result = JS_TRUE;
- if (JS_THREAD_DATA(cx)->interruptFlags)
+ if (cx->runtime->interrupt)
result = js_InvokeOperationCallback(cx) && result;
return result;
}
-namespace js {
-
-void
-TriggerOperationCallback(JSContext *cx)
-{
- /*
- * We allow for cx to come from another thread. Thus we must deal with
- * possible JS_ClearContextThread calls when accessing cx->thread. But we
- * assume that the calling thread is in a request so JSThread cannot be
- * GC-ed.
- */
- ThreadData *td;
-#ifdef JS_THREADSAFE
- JSThread *thread = cx->thread();
- if (!thread)
- return;
- td = &thread->data;
-#else
- td = JS_THREAD_DATA(cx);
-#endif
- td->triggerOperationCallback(cx->runtime);
-}
-
-void
-TriggerAllOperationCallbacks(JSRuntime *rt)
-{
- for (ThreadDataIter i(rt); !i.empty(); i.popFront())
- i.threadData()->triggerOperationCallback(rt);
-}
-
-} /* namespace js */
-
StackFrame *
js_GetScriptedCaller(JSContext *cx, StackFrame *fp)
{
if (!fp)
fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
while (fp && fp->isDummyFrame())
fp = fp->prev();
JS_ASSERT_IF(fp, fp->isScriptFrame());
@@ -1349,30 +1027,26 @@ JSContext::JSContext(JSRuntime *rt)
generatingError(false),
#if JS_STACK_GROWTH_DIRECTION > 0
stackLimit(UINTPTR_MAX),
#else
stackLimit(0),
#endif
runtime(rt),
compartment(NULL),
-#ifdef JS_THREADSAFE
- thread_(NULL),
-#endif
stack(thisDuringConstruction()), /* depends on cx->thread_ */
parseMapPool_(NULL),
globalObject(NULL),
argumentFormatMap(NULL),
lastMessage(NULL),
errorReporter(NULL),
operationCallback(NULL),
data(NULL),
data2(NULL),
#ifdef JS_THREADSAFE
- atomsCompartmentIsLocked(false),
outstandingRequests(0),
#endif
autoGCRooters(NULL),
debugHooks(&rt->globalDebugHooks),
securityCallbacks(NULL),
resolveFlags(0),
rngSeed(0),
iterValue(MagicValue(JS_NO_ITER_VALUE)),
@@ -1402,20 +1076,16 @@ JSContext::JSContext(JSRuntime *rt)
#ifdef DEBUG
checkGCRooters = NULL;
#endif
#endif
}
JSContext::~JSContext()
{
-#ifdef JS_THREADSAFE
- JS_ASSERT(!thread_);
-#endif
-
/* Free the stuff hanging off of cx. */
if (parseMapPool_)
Foreground::delete_<ParseMapPool>(parseMapPool_);
if (lastMessage)
Foreground::free_(lastMessage);
/* Remove any argument formatters. */
@@ -1504,25 +1174,16 @@ bool
JSContext::runningWithTrustedPrincipals() const
{
return !compartment || compartment->principals == runtime->trustedPrincipals();
}
JS_FRIEND_API(void)
JSRuntime::onTooMuchMalloc()
{
-#ifdef JS_THREADSAFE
- AutoLockGC lock(this);
-
- /*
- * We can be called outside a request and can race against a GC that
- * mutates the JSThread set during the sweeping phase.
- */
- js_WaitForGC(this);
-#endif
TriggerGC(this, gcstats::TOOMUCHMALLOC);
}
JS_FRIEND_API(void *)
JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
{
/*
* Retry when we are done with the background sweeping and have stopped
@@ -1544,16 +1205,29 @@ JSRuntime::onOutOfMemory(void *p, size_t
if (p)
return p;
if (cx)
js_ReportOutOfMemory(cx);
return NULL;
}
void
+JSRuntime::purge(JSContext *cx)
+{
+ tempLifoAlloc.freeUnused();
+ gsnCache.purge();
+
+ /* FIXME: bug 506341 */
+ propertyCache.purge(cx);
+
+ delete_<RegExpPrivateCache>(repCache_);
+ repCache_ = NULL;
+}
+
+void
JSContext::purge()
{
if (!activeCompilations) {
Foreground::delete_<ParseMapPool>(parseMapPool_);
parseMapPool_ = NULL;
}
}
@@ -1652,23 +1326,22 @@ JSContext::sizeOfIncludingThis(JSMallocS
namespace JS {
#if defined JS_THREADSAFE && defined DEBUG
AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx)
: cx(cx)
{
- JS_ASSERT(cx->thread());
- JS_ASSERT(cx->thread()->data.requestDepth || cx->thread() == cx->runtime->gcThread);
+ JS_ASSERT(cx->runtime->requestDepth || cx->runtime->gcRunning);
JS_ASSERT(cx->runtime->onOwnerThread());
- cx->thread()->checkRequestDepth++;
+ cx->runtime->checkRequestDepth++;
}
AutoCheckRequestDepth::~AutoCheckRequestDepth()
{
- JS_ASSERT(cx->thread()->checkRequestDepth != 0);
- cx->thread()->checkRequestDepth--;
+ JS_ASSERT(cx->runtime->checkRequestDepth != 0);
+ cx->runtime->checkRequestDepth--;
}
#endif
} // namespace JS
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -118,260 +118,163 @@ struct GSNCache {
inline GSNCache *
GetGSNCache(JSContext *cx);
struct PendingProxyOperation {
PendingProxyOperation *next;
JSObject *object;
};
-struct ThreadData {
- JSRuntime *rt;
-
- /*
- * If non-zero, we were been asked to call the operation callback as soon
- * as possible. If the thread has an active request, this contributes
- * towards rt->interruptCounter.
- */
- volatile int32_t interruptFlags;
-
-#ifdef JS_THREADSAFE
- /* The request depth for this thread. */
- unsigned requestDepth;
-#endif
-
- /* Keeper of the contiguous stack used by all contexts in this thread. */
- StackSpace stackSpace;
-
- /* Temporary arena pool used while compiling and decompiling. */
- static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
- LifoAlloc tempLifoAlloc;
-
- private:
- /*
- * Both of these allocators are used for regular expression code which is shared at the
- * thread-data level.
- */
- JSC::ExecutableAllocator *execAlloc;
- WTF::BumpPointerAllocator *bumpAlloc;
- js::RegExpPrivateCache *repCache;
-
- JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
- WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
- js::RegExpPrivateCache *createRegExpPrivateCache(JSContext *cx);
+typedef Vector<ScriptOpcodeCountsPair, 0, SystemAllocPolicy> ScriptOpcodeCountsVector;
- public:
- JSC::ExecutableAllocator *getOrCreateExecutableAllocator(JSContext *cx) {
- if (execAlloc)
- return execAlloc;
-
- return createExecutableAllocator(cx);
- }
-
- WTF::BumpPointerAllocator *getOrCreateBumpPointerAllocator(JSContext *cx) {
- if (bumpAlloc)
- return bumpAlloc;
-
- return createBumpPointerAllocator(cx);
- }
-
- js::RegExpPrivateCache *getRegExpPrivateCache() {
- return repCache;
- }
- js::RegExpPrivateCache *getOrCreateRegExpPrivateCache(JSContext *cx) {
- if (repCache)
- return repCache;
-
- return createRegExpPrivateCache(cx);
- }
-
- /* Called at the end of the global GC sweep phase to deallocate repCache memory. */
- void purgeRegExpPrivateCache();
-
- /*
- * The GSN cache is per thread since even multi-cx-per-thread embeddings
- * do not interleave js_GetSrcNote calls.
- */
- GSNCache gsnCache;
-
- /* Property cache for faster call/get/set invocation. */
- PropertyCache propertyCache;
-
- /* State used by jsdtoa.cpp. */
- DtoaState *dtoaState;
-
+struct ConservativeGCData
+{
/* Base address of the native stack for the current thread. */
uintptr_t *nativeStackBase;
- /* List of currently pending operations on proxies. */
- PendingProxyOperation *pendingProxyOperation;
+ /*
+ * The GC scans conservatively between ThreadData::nativeStackBase and
+ * nativeStackTop unless the latter is NULL.
+ */
+ uintptr_t *nativeStackTop;
+
+ union {
+ jmp_buf jmpbuf;
+ uintptr_t words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))];
+ } registerSnapshot;
- ConservativeGCThreadData conservativeGC;
+ /*
+ * Cycle collector uses this to communicate that the native stack of the
+ * GC thread should be scanned only if the thread have more than the given
+ * threshold of requests.
+ */
+ unsigned requestThreshold;
-#ifdef DEBUG
- size_t noGCOrAllocationCheck;
+ ConservativeGCData()
+ : nativeStackBase(NULL), nativeStackTop(NULL), requestThreshold(0)
+ {}
+
+ ~ConservativeGCData() {
+#ifdef JS_THREADSAFE
+ /*
+ * The conservative GC scanner should be disabled when the thread leaves
+ * the last request.
+ */
+ JS_ASSERT(!hasStackToScan());
#endif
-
- ThreadData(JSRuntime *rt);
- ~ThreadData();
-
- bool init();
-
- void mark(JSTracer *trc) {
- stackSpace.mark(trc);
}
- void purge(JSContext *cx) {
- tempLifoAlloc.freeUnused();
- gsnCache.purge();
-
- /* FIXME: bug 506341. */
- propertyCache.purge(cx);
- }
+ JS_NEVER_INLINE void recordStackTop();
#ifdef JS_THREADSAFE
- void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
- size_t *regexpCode, size_t *stackCommitted);
+ void updateForRequestEnd(unsigned suspendCount) {
+ if (suspendCount)
+ recordStackTop();
+ else
+ nativeStackTop = NULL;
+ }
#endif
- /* This must be called with the GC lock held. */
- void triggerOperationCallback(JSRuntime *rt);
-
- /*
- * Frames currently running in js::Interpret. See InterpreterFrames for
- * details.
- */
- InterpreterFrames *interpreterFrames;
+ bool hasStackToScan() const {
+ return !!nativeStackTop;
+ }
};
} /* namespace js */
-#ifdef JS_THREADSAFE
-
-/*
- * Structure uniquely representing a thread. It holds thread-private data
- * that can be accessed without a global lock.
- */
-struct JSThread {
- typedef js::HashMap<void *,
- JSThread *,
- js::DefaultHasher<void *>,
- js::SystemAllocPolicy> Map;
-
- /* Linked list of all contexts in use on this thread. */
- JSCList contextList;
-
- /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */
- void *id;
-
- /* Number of JS_SuspendRequest calls without JS_ResumeRequest. */
- unsigned suspendCount;
-
-# ifdef DEBUG
- unsigned checkRequestDepth;
-# endif
-
- /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
- js::ThreadData data;
-
- JSThread(JSRuntime *rt, void *id)
- : id(id),
- suspendCount(0),
-# ifdef DEBUG
- checkRequestDepth(0),
-# endif
- data(rt)
- {
- JS_INIT_CLIST(&contextList);
- }
-
- ~JSThread() {
- /* The thread must have zero contexts. */
- JS_ASSERT(JS_CLIST_IS_EMPTY(&contextList));
- }
-
- bool init() {
- return data.init();
- }
-
- JS_FRIEND_API(void) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal,
- size_t *temporary, size_t *regexpCode,
- size_t *stackCommitted);
-};
-
-#define JS_THREAD_DATA(cx) (&(cx)->thread()->data)
-
-extern JSThread *
-js_CurrentThreadAndLockGC(JSRuntime *rt);
-
-/*
- * The function takes the GC lock and does not release in successful return.
- * On error (out of memory) the function releases the lock but delegates
- * the error reporting to the caller.
- */
-extern JSBool
-js_InitContextThreadAndLockGC(JSContext *cx);
-
-/*
- * On entrance the GC lock must be held and it will be held on exit.
- */
-extern void
-js_ClearContextThread(JSContext *cx);
-
-#endif /* JS_THREADSAFE */
-
-typedef enum JSDestroyContextMode {
- JSDCM_NO_GC,
- JSDCM_MAYBE_GC,
- JSDCM_FORCE_GC,
- JSDCM_NEW_FAILED
-} JSDestroyContextMode;
-
-typedef struct JSPropertyTreeEntry {
- JSDHashEntryHdr hdr;
- js::Shape *child;
-} JSPropertyTreeEntry;
-
-namespace js {
-
-typedef Vector<ScriptOpcodeCountsPair, 0, SystemAllocPolicy> ScriptOpcodeCountsVector;
-
-}
-
struct JSRuntime
{
+ /*
+ * If non-zero, we were been asked to call the operation callback as soon
+ * as possible.
+ */
+ volatile int32_t interrupt;
+
/* Default compartment. */
JSCompartment *atomsCompartment;
/* List of compartments (protected by the GC lock). */
js::CompartmentVector compartments;
/* See comment for JS_AbortIfWrongThread in jsapi.h. */
#ifdef JS_THREADSAFE
public:
+ void *ownerThread() const { return ownerThread_; }
void clearOwnerThread();
void setOwnerThread();
JS_FRIEND_API(bool) onOwnerThread() const;
private:
void *ownerThread_;
public:
#else
public:
bool onOwnerThread() const { return true; }
#endif
+ /* Keeper of the contiguous stack used by all contexts in this thread. */
+ js::StackSpace stackSpace;
+
+ /* Temporary arena pool used while compiling and decompiling. */
+ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
+ js::LifoAlloc tempLifoAlloc;
+
+ private:
+ /*
+ * Both of these allocators are used for regular expression code which is shared at the
+ * thread-data level.
+ */
+ JSC::ExecutableAllocator *execAlloc_;
+ WTF::BumpPointerAllocator *bumpAlloc_;
+ js::RegExpPrivateCache *repCache_;
+
+ JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
+ WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
+ js::RegExpPrivateCache *createRegExpPrivateCache(JSContext *cx);
+
+ public:
+ JSC::ExecutableAllocator *getExecutableAllocator(JSContext *cx) {
+ return execAlloc_ ? execAlloc_ : createExecutableAllocator(cx);
+ }
+ WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) {
+ return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx);
+ }
+ js::RegExpPrivateCache *maybeRegExpPrivateCache() {
+ return repCache_;
+ }
+ js::RegExpPrivateCache *getRegExpPrivateCache(JSContext *cx) {
+ return repCache_ ? repCache_ : createRegExpPrivateCache(cx);
+ }
+
+ /*
+ * Frames currently running in js::Interpret. See InterpreterFrames for
+ * details.
+ */
+ js::InterpreterFrames *interpreterFrames;
+
/* Context create/destroy callback. */
JSContextCallback cxCallback;
/* Compartment create/destroy callback. */
JSCompartmentCallback compartmentCallback;
js::ActivityCallback activityCallback;
void *activityCallbackArg;
+#ifdef JS_THREADSAFE
+ /* Number of JS_SuspendRequest calls withot JS_ResumeRequest. */
+ unsigned suspendCount;
+
+ /* The request depth for this thread. */
+ unsigned requestDepth;
+
+# ifdef DEBUG
+ unsigned checkRequestDepth;
+# endif
+#endif
+
/* Garbage collector state, used by jsgc.c. */
/*
* Set of all GC chunks with at least one allocated thing. The
* conservative GC uses it to quickly check if a possible GC thing points
* into an allocated chunk.
*/
js::GCChunkSet gcChunkSet;
@@ -514,17 +417,17 @@ struct JSRuntime
/* Well-known numbers held for use by this runtime's contexts. */
js::Value NaNValue;
js::Value negativeInfinityValue;
js::Value positiveInfinityValue;
JSAtom *emptyString;
- /* List of active contexts sharing this runtime; protected by gcLock. */
+ /* List of active contexts sharing this runtime. */
JSCList contextList;
bool hasContexts() const {
return !JS_CLIST_IS_EMPTY(&contextList);
}
/* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */
JSDebugHooks globalDebugHooks;
@@ -545,30 +448,19 @@ struct JSRuntime
JSCList debuggerList;
/* Client opaque pointers */
void *data;
#ifdef JS_THREADSAFE
/* These combine to interlock the GC and new requests. */
PRLock *gcLock;
- PRCondVar *gcDone;
- PRCondVar *requestDone;
uint32_t requestCount;
- JSThread *gcThread;
js::GCHelperThread gcHelperThread;
-
- /*
- * Mapping from NSPR thread identifiers to JSThreads.
- *
- * This map can be accessed by the GC thread; or by the thread that holds
- * gcLock, if GC is not running.
- */
- JSThread::Map threads;
#endif /* JS_THREADSAFE */
uint32_t debuggerMutations;
/*
* Security callbacks set on the runtime are used by each context unless
* an override is set on the context.
*/
@@ -582,45 +474,53 @@ struct JSRuntime
/*
* The propertyRemovals counter is incremented for every JSObject::clear,
* and for each JSObject::remove method call that frees a slot in the given
* object. See js_NativeGet and js_NativeSet in jsobj.cpp.
*/
int32_t propertyRemovals;
- /* Script filename table. */
- struct JSHashTable *scriptFilenameTable;
-#ifdef JS_THREADSAFE
- PRLock *scriptFilenameTableLock;
-#endif
-
/* Number localization, used by jsnum.c */
const char *thousandsSeparator;
const char *decimalSeparator;
const char *numGrouping;
/*
* Weak references to lazily-created, well-known XML singletons.
*
* NB: Singleton objects must be carefully disconnected from the rest of
* the object graph usually associated with a JSContext's global object,
* including the set of standard class objects. See jsxml.c for details.
*/
JSObject *anynameObject;
JSObject *functionNamespaceObject;
-#ifdef JS_THREADSAFE
- /* Number of threads with active requests and unhandled interrupts. */
- volatile int32_t interruptCounter;
-#else
- js::ThreadData threadData;
+ /*
+ * Flag indicating that we are waiving any soft limits on the GC heap
+ * because we want allocations to be infallible (except when we hit OOM).
+ */
+ bool waiveGCQuota;
-#define JS_THREAD_DATA(cx) (&(cx)->runtime->threadData)
-#endif
+ /*
+ * The GSN cache is per thread since even multi-cx-per-thread embeddings
+ * do not interleave js_GetSrcNote calls.
+ */
+ js::GSNCache gsnCache;
+
+ /* Property cache for faster call/get/set invocation. */
+ js::PropertyCache propertyCache;
+
+ /* State used by jsdtoa.cpp. */
+ DtoaState *dtoaState;
+
+ /* List of currently pending operations on proxies. */
+ js::PendingProxyOperation *pendingProxyOperation;
+
+ js::ConservativeGCData conservativeGC;
private:
JSPrincipals *trustedPrincipals_;
public:
void setTrustedPrincipals(JSPrincipals *p) { trustedPrincipals_ = p; }
JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; }
/* Literal table maintained by jsatom.c functions. */
@@ -628,16 +528,20 @@ struct JSRuntime
/* Tables of strings that are pre-allocated in the atomsCompartment. */
js::StaticStrings staticStrings;
JSWrapObjectCallback wrapObjectCallback;
JSPreWrapCallback preWrapObjectCallback;
js::PreserveWrapperCallback preserveWrapperCallback;
+#ifdef DEBUG
+ size_t noGCOrAllocationCheck;
+#endif
+
/*
* To ensure that cx->malloc does not cause a GC, we set this flag during
* OOM reporting (in js_ReportOutOfMemory). If a GC is requested while
* reporting the OOM, we ignore it.
*/
int32_t inOOMReport;
JSRuntime();
@@ -734,23 +638,30 @@ struct JSRuntime
* This should be called after system malloc/realloc returns NULL to try
* to recove some memory or to report an error. Failures in malloc and
* calloc are signaled by p == null and p == reinterpret_cast<void *>(1).
* Other values of p mean a realloc failure.
*
* The function must be called outside the GC lock.
*/
JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
+
+ JS_FRIEND_API(void) triggerOperationCallback();
+
+ void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
+ size_t *regexpCode, size_t *stackCommitted);
+
+ void purge(JSContext *cx);
};
-/* Common macros to access thread-local caches in JSThread or JSRuntime. */
-#define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache)
+/* Common macros to access thread-local caches in JSRuntime. */
+#define JS_PROPERTY_CACHE(cx) (cx->runtime->propertyCache)
-#define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms);
-#define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms);
+#define JS_KEEP_ATOMS(rt) (rt)->gcKeepAtoms++;
+#define JS_UNKEEP_ATOMS(rt) (rt)->gcKeepAtoms--;
#ifdef JS_ARGUMENT_FORMATTER_DEFINED
/*
* Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to
* formatter functions. Elements are sorted in non-increasing format string
* length order.
*/
struct JSArgumentFormatMap {
@@ -894,34 +805,24 @@ struct JSContext
* True if generating an error, to prevent runaway recursion.
* NB: generatingError packs with throwing below.
*/
JSPackedBool generatingError;
/* Limit pointer for checking native stack consumption during recursion. */
uintptr_t stackLimit;
- /* Data shared by threads in an address space. */
+ /* Data shared by contexts and compartments in an address space. */
JSRuntime *const runtime;
/* GC heap compartment. */
JSCompartment *compartment;
inline void setCompartment(JSCompartment *compartment);
-#ifdef JS_THREADSAFE
- private:
- JSThread *thread_;
- public:
- JSThread *thread() const { return thread_; }
-
- void setThread(JSThread *thread);
- static const size_t threadOffset() { return offsetof(JSContext, thread_); }
-#endif
-
/* Current execution stack. */
js::ContextStack stack;
/* ContextStack convenience functions */
inline bool hasfp() const { return stack.hasfp(); }
inline js::StackFrame* fp() const { return stack.fp(); }
inline js::StackFrame* maybefp() const { return stack.maybefp(); }
inline js::FrameRegs& regs() const { return stack.regs(); }
@@ -1039,27 +940,20 @@ struct JSContext
return !!(runOptions & ropt);
}
bool hasStrictOption() const { return hasRunOption(JSOPTION_STRICT); }
bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); }
bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); }
bool hasJITHardeningOption() const { return !hasRunOption(JSOPTION_SOFTEN); }
- js::LifoAlloc &tempLifoAlloc() { return JS_THREAD_DATA(this)->tempLifoAlloc; }
+ js::LifoAlloc &tempLifoAlloc() { return runtime->tempLifoAlloc; }
inline js::LifoAlloc &typeLifoAlloc();
#ifdef JS_THREADSAFE
- /*
- * AtomizeInline uses this flag to tell RunLastDitchGC and
- * js_ReportOutOfMemory that they should temporarily unlock the atoms
- * compartment.
- */
- bool atomsCompartmentIsLocked;
-
unsigned outstandingRequests;/* number of JS_BeginRequest calls
without the corresponding
JS_EndRequest. */
JSCList threadLinks; /* JSThread contextList linkage */
#endif
/* Stack of thread-stack-allocated GC roots. */
js::AutoGCRooter *autoGCRooters;
@@ -1160,18 +1054,16 @@ struct JSContext
#ifdef JS_THREADSAFE
/*
* When non-null JSContext::free_ delegates the job to the background
* thread.
*/
js::GCHelperThread *gcBackgroundFree;
#endif
- js::ThreadData *threadData() { return JS_THREAD_DATA(this); }
-
inline void* malloc_(size_t bytes) {
return runtime->malloc_(bytes, this);
}
inline void* mallocNoReport(size_t bytes) {
JS_ASSERT(bytes != 0);
return runtime->malloc_(bytes, NULL);
}
@@ -1246,23 +1138,16 @@ struct JSContext
JS_FRIEND_API(size_t) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const;
static inline JSContext *fromLinkField(JSCList *link) {
JS_ASSERT(link);
return reinterpret_cast<JSContext *>(uintptr_t(link) - offsetof(JSContext, link));
}
-#ifdef JS_THREADSAFE
- static inline JSContext *fromThreadLinks(JSCList *link) {
- JS_ASSERT(link);
- return reinterpret_cast<JSContext *>(uintptr_t(link) - offsetof(JSContext, threadLinks));
- }
-#endif
-
private:
/*
* The allocation code calls the function to indicate either OOM failure
* when p is null or that a memory pressure counter has reached some
* threshold when p is not null. The function takes the pointer and not
* a boolean flag to minimize the amount of code in its inlined callers.
*/
JS_FRIEND_API(void) checkMallocGCPressure(void *p);
@@ -1320,89 +1205,40 @@ class AutoXMLRooter : private AutoGCRoot
friend void AutoGCRooter::trace(JSTracer *trc);
private:
JSXML * const xml;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
#endif /* JS_HAS_XML_SUPPORT */
+#ifdef JS_THREADSAFE
+# define JS_LOCK_GC(rt) PR_Lock((rt)->gcLock)
+# define JS_UNLOCK_GC(rt) PR_Unlock((rt)->gcLock)
+#else
+# define JS_LOCK_GC(rt)
+# define JS_UNLOCK_GC(rt)
+#endif
+
class AutoUnlockGC {
private:
JSRuntime *rt;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
explicit AutoUnlockGC(JSRuntime *rt
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: rt(rt)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
JS_UNLOCK_GC(rt);
}
~AutoUnlockGC() { JS_LOCK_GC(rt); }
};
-class AutoLockAtomsCompartment {
- private:
- JSContext *cx;
- JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-
- public:
- AutoLockAtomsCompartment(JSContext *cx
- JS_GUARD_OBJECT_NOTIFIER_PARAM)
- : cx(cx)
- {
- JS_GUARD_OBJECT_NOTIFIER_INIT;
-#ifdef JS_THREADSAFE
- JS_ASSERT(!cx->atomsCompartmentIsLocked);
- JS_LOCK(cx, &cx->runtime->atomState.lock);
- cx->atomsCompartmentIsLocked = true;
-#endif
- }
-
- ~AutoLockAtomsCompartment() {
-#ifdef JS_THREADSAFE
- JS_ASSERT(cx->atomsCompartmentIsLocked);
- cx->atomsCompartmentIsLocked = false;
- JS_UNLOCK(cx, &cx->runtime->atomState.lock);
-#endif
- }
-};
-
-class AutoUnlockAtomsCompartmentWhenLocked {
- JSContext *cx;
- JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-
- public:
- AutoUnlockAtomsCompartmentWhenLocked(JSContext *cx
- JS_GUARD_OBJECT_NOTIFIER_PARAM)
- : cx(NULL)
- {
- JS_GUARD_OBJECT_NOTIFIER_INIT;
- #ifdef JS_THREADSAFE
- if (cx->atomsCompartmentIsLocked) {
- this->cx = cx;
- cx->atomsCompartmentIsLocked = false;
- JS_UNLOCK(cx, &cx->runtime->atomState.lock);
- }
-#endif
- }
-
- ~AutoUnlockAtomsCompartmentWhenLocked() {
-#ifdef JS_THREADSAFE
- if (cx) {
- JS_ASSERT(!cx->atomsCompartmentIsLocked);
- JS_LOCK(cx, &cx->runtime->atomState.lock);
- cx->atomsCompartmentIsLocked = true;
- }
-#endif
- }
-};
-
class AutoKeepAtoms {
JSRuntime *rt;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
explicit AutoKeepAtoms(JSRuntime *rt
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: rt(rt)
@@ -1473,111 +1309,56 @@ class JSAutoResolveFlags
~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; }
private:
JSContext *mContext;
uintN mSaved;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
-extern js::ThreadData *
-js_CurrentThreadData(JSRuntime *rt);
-
-extern JSBool
-js_InitThreads(JSRuntime *rt);
-
-extern void
-js_FinishThreads(JSRuntime *rt);
-
-extern void
-js_PurgeThreads(JSContext *cx);
-
-extern void
-js_PurgeThreads_PostGlobalSweep(JSContext *cx);
-
namespace js {
-#ifdef JS_THREADSAFE
-
-/* Iterator over ThreadData from all JSThread instances. */
-class ThreadDataIter : public JSThread::Map::Range
-{
- public:
- ThreadDataIter(JSRuntime *rt) : JSThread::Map::Range(rt->threads.all()) {}
-
- ThreadData *threadData() const {
- return &front().value->data;
- }
-};
-
-#else /* !JS_THREADSAFE */
-
-class ThreadDataIter
-{
- JSRuntime *runtime;
- bool done;
- public:
- ThreadDataIter(JSRuntime *rt) : runtime(rt), done(false) {}
-
- bool empty() const {
- return done;
- }
-
- void popFront() {
- JS_ASSERT(!done);
- done = true;
- }
-
- ThreadData *threadData() const {
- JS_ASSERT(!done);
- return &runtime->threadData;
- }
-};
-
-#endif /* !JS_THREADSAFE */
-
/*
* Enumerate all contexts in a runtime that are in the same thread as a given
* context.
*/
class ThreadContextRange {
JSCList *begin;
JSCList *end;
public:
explicit ThreadContextRange(JSContext *cx) {
-#ifdef JS_THREADSAFE
- end = &cx->thread()->contextList;
-#else
end = &cx->runtime->contextList;
-#endif
begin = end->next;
}
bool empty() const { return begin == end; }
void popFront() { JS_ASSERT(!empty()); begin = begin->next; }
JSContext *front() const {
-#ifdef JS_THREADSAFE
- return JSContext::fromThreadLinks(begin);
-#else
return JSContext::fromLinkField(begin);
-#endif
}
};
} /* namespace js */
/*
* Create and destroy functions for JSContext, which is manually allocated
* and exclusively owned.
*/
extern JSContext *
js_NewContext(JSRuntime *rt, size_t stackChunkSize);
+typedef enum JSDestroyContextMode {
+ JSDCM_NO_GC,
+ JSDCM_MAYBE_GC,
+ JSDCM_FORCE_GC,
+ JSDCM_NEW_FAILED
+} JSDestroyContextMode;
+
extern void
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode);
/*
* If unlocked, acquire and release rt->gcLock around *iterp update; otherwise
* the caller must be holding rt->gcLock.
*/
extern JSContext *
@@ -1653,53 +1434,40 @@ js_ReportValueErrorFlags(JSContext *cx,
#define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2) \
((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \
spindex, v, fallback, arg1, arg2))
extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
#ifdef JS_THREADSAFE
-# define JS_ASSERT_REQUEST_DEPTH(cx) (JS_ASSERT((cx)->thread()), \
- JS_ASSERT((cx)->thread()->data.requestDepth >= 1))
+# define JS_ASSERT_REQUEST_DEPTH(cx) JS_ASSERT((cx)->runtime->requestDepth >= 1)
#else
# define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0)
#endif
/*
* If the operation callback flag was set, call the operation callback.
* This macro can run the full GC. Return true if it is OK to continue and
* false otherwise.
*/
#define JS_CHECK_OPERATION_LIMIT(cx) \
(JS_ASSERT_REQUEST_DEPTH(cx), \
- (!JS_THREAD_DATA(cx)->interruptFlags || js_InvokeOperationCallback(cx)))
+ (!cx->runtime->interrupt || js_InvokeOperationCallback(cx)))
/*
* Invoke the operation callback and return false if the current execution
* is to be terminated.
*/
extern JSBool
js_InvokeOperationCallback(JSContext *cx);
extern JSBool
js_HandleExecutionInterrupt(JSContext *cx);
-namespace js {
-
-/* These must be called with GC lock taken. */
-
-void
-TriggerOperationCallback(JSContext *cx);
-
-void
-TriggerAllOperationCallbacks(JSRuntime *rt);
-
-} /* namespace js */
-
/*
* Get the topmost scripted frame in a context. Note: if the topmost frame is
* in the middle of an inline call, that call will be expanded. To avoid this,
* use cx->stack.currentScript or cx->stack.currentScriptedScopeChain.
*/
extern js::StackFrame *
js_GetScriptedCaller(JSContext *cx, js::StackFrame *fp);
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -82,17 +82,17 @@ GetGlobalForScopeChain(JSContext *cx)
if (!scope)
return NULL;
return &scope->asGlobal();
}
inline GSNCache *
GetGSNCache(JSContext *cx)
{
- return &JS_THREAD_DATA(cx)->gsnCache;
+ return &cx->runtime->gsnCache;
}
class AutoNamespaceArray : protected AutoGCRooter {
public:
AutoNamespaceArray(JSContext *cx) : AutoGCRooter(cx, NAMESPACES) {
array.init();
}
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -556,20 +556,20 @@ CallContextDebugHandler(JSContext *cx, J
if (!CanCallContextDebugHandler(cx))
return JSTRAP_RETURN;
return cx->debugHooks->debuggerHandler(cx, script, bc, rval,
cx->debugHooks->debuggerHandlerData);
}
#ifdef JS_THREADSAFE
-JSThread *
-GetContextThread(const JSContext *cx)
+void *
+GetOwnerThread(const JSContext *cx)
{
- return cx->thread();
+ return cx->runtime->ownerThread();
}
JS_FRIEND_API(unsigned)
GetContextOutstandingRequests(const JSContext *cx)
{
return cx->outstandingRequests;
}
@@ -580,28 +580,28 @@ GetRuntimeGCLock(const JSRuntime *rt)
}
AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: context(cx)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
- ThreadData &threadData = context->thread()->data;
- JS_ASSERT(threadData.requestDepth >= 1);
- JS_ASSERT(!threadData.conservativeGC.requestThreshold);
- if (threadData.requestDepth == 1)
- threadData.conservativeGC.requestThreshold = 1;
+ JSRuntime *rt = context->runtime;
+ JS_ASSERT(rt->requestDepth >= 1);
+ JS_ASSERT(!rt->conservativeGC.requestThreshold);
+ if (rt->requestDepth == 1)
+ rt->conservativeGC.requestThreshold = 1;
}
AutoSkipConservativeScan::~AutoSkipConservativeScan()
{
- ThreadData &threadData = context->thread()->data;
- if (threadData.requestDepth == 1)
- threadData.conservativeGC.requestThreshold = 0;
+ JSRuntime *rt = context->runtime;
+ if (rt->requestDepth == 1)
+ rt->conservativeGC.requestThreshold = 0;
}
#endif
JS_FRIEND_API(JSCompartment *)
GetContextCompartment(const JSContext *cx)
{
return cx->compartment;
}
@@ -621,22 +621,19 @@ SetActivityCallback(JSRuntime *rt, Activ
JS_FRIEND_API(bool)
IsContextRunningJS(JSContext *cx)
{
return !cx->stack.empty();
}
JS_FRIEND_API(void)
-TriggerOperationCallbacksForActiveContexts(JSRuntime *rt)
+TriggerOperationCallback(JSRuntime *rt)
{
- JSContext* cx = NULL;
- while ((cx = js_NextActiveContext(rt, cx))) {
- TriggerOperationCallback(cx);
- }
+ rt->triggerOperationCallback();
}
JS_FRIEND_API(const CompartmentVector&)
GetRuntimeCompartments(JSRuntime *rt)
{
return rt->compartments;
}
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -470,18 +470,18 @@ GetPCCountScriptCount(JSContext *cx);
JS_FRIEND_API(JSString *)
GetPCCountScriptSummary(JSContext *cx, size_t script);
JS_FRIEND_API(JSString *)
GetPCCountScriptContents(JSContext *cx, size_t script);
#ifdef JS_THREADSAFE
-JS_FRIEND_API(JSThread *)
-GetContextThread(const JSContext *cx);
+JS_FRIEND_API(void *)
+GetOwnerThread(const JSContext *cx);
JS_FRIEND_API(unsigned)
GetContextOutstandingRequests(const JSContext *cx);
JS_FRIEND_API(PRLock *)
GetRuntimeGCLock(const JSRuntime *rt);
class JS_FRIEND_API(AutoSkipConservativeScan)
@@ -557,17 +557,17 @@ CanCallContextDebugHandler(JSContext *cx
extern JS_FRIEND_API(JSTrapStatus)
CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval);
extern JS_FRIEND_API(bool)
IsContextRunningJS(JSContext *cx);
/* Must be called with GC lock taken. */
extern JS_FRIEND_API(void)
-TriggerOperationCallbacksForActiveContexts(JSRuntime *rt);
+TriggerOperationCallback(JSRuntime *rt);
class SystemAllocPolicy;
typedef Vector<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector;
extern JS_FRIEND_API(const CompartmentVector&)
GetRuntimeCompartments(JSRuntime *rt);
extern JS_FRIEND_API(size_t)
SizeOfJSContext();
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -101,16 +101,22 @@
#ifdef MOZ_VALGRIND
# define JS_VALGRIND
#endif
#ifdef JS_VALGRIND
# include <valgrind/memcheck.h>
#endif
+#ifdef XP_WIN
+# include "jswin.h"
+#else
+# include <unistd.h>
+#endif
+
using namespace mozilla;
using namespace js;
using namespace js::gc;
namespace js {
namespace gc {
#ifdef JS_GC_ZEAL
@@ -870,25 +876,19 @@ js_InitGC(JSRuntime *rt, uint32_t maxbyt
if (!rt->gcRootsHash.init(256))
return false;
if (!rt->gcLocksHash.init(256))
return false;
#ifdef JS_THREADSAFE
- rt->gcLock = JS_NEW_LOCK();
+ rt->gcLock = PR_NewLock();
if (!rt->gcLock)
return false;
- rt->gcDone = JS_NEW_CONDVAR(rt->gcLock);
- if (!rt->gcDone)
- return false;
- rt->requestDone = JS_NEW_CONDVAR(rt->gcLock);
- if (!rt->requestDone)
- return false;
if (!rt->gcHelperThread.init())
return false;
#endif
/*
* Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
* for default backward API compatibility.
*/
@@ -1074,33 +1074,41 @@ MarkWordConservatively(JSTracer *trc, ui
static void
MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t *end)
{
JS_ASSERT(begin <= end);
for (const uintptr_t *i = begin; i < end; ++i)
MarkWordConservatively(trc, *i);
}
-static void
-MarkThreadDataConservatively(JSTracer *trc, ThreadData *td)
+static JS_NEVER_INLINE void
+MarkConservativeStackRoots(JSTracer *trc, JSRuntime *rt)
{
- ConservativeGCThreadData *ctd = &td->conservativeGC;
- JS_ASSERT(ctd->hasStackToScan());
+ ConservativeGCData *cgcd = &rt->conservativeGC;
+ if (!cgcd->hasStackToScan()) {
+#ifdef JS_THREADSAFE
+ JS_ASSERT(!rt->suspendCount);
+ JS_ASSERT(rt->requestDepth <= cgcd->requestThreshold);
+#endif
+ return;
+ }
+
uintptr_t *stackMin, *stackEnd;
#if JS_STACK_GROWTH_DIRECTION > 0
- stackMin = td->nativeStackBase;
- stackEnd = ctd->nativeStackTop;
+ stackMin = rt->conservativeGC.nativeStackBase;
+ stackEnd = cgcd->nativeStackTop;
#else
- stackMin = ctd->nativeStackTop + 1;
- stackEnd = td->nativeStackBase;
+ stackMin = cgcd->nativeStackTop + 1;
+ stackEnd = rt->conservativeGC.nativeStackBase;
#endif
+
JS_ASSERT(stackMin <= stackEnd);
MarkRangeConservatively(trc, stackMin, stackEnd);
- MarkRangeConservatively(trc, ctd->registerSnapshot.words,
- ArrayEnd(ctd->registerSnapshot.words));
+ MarkRangeConservatively(trc, cgcd->registerSnapshot.words,
+ ArrayEnd(cgcd->registerSnapshot.words));
}
void
MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv)
{
/*
* Normally, the drainMarkStack phase of marking will never trace outside
* of the compartment currently being collected. However, conservative
@@ -1129,69 +1137,49 @@ MarkStackRangeConservatively(JSTracer *t
JS_ASSERT(begin <= end);
for (const uintptr_t *i = begin; i < end; i += sizeof(Value) / sizeof(uintptr_t))
MarkWordConservatively(trc, *i);
#else
MarkRangeConservatively(trc, begin, end);
#endif
}
-void
-MarkConservativeStackRoots(JSTracer *trc)
-{
-#ifdef JS_THREADSAFE
- for (JSThread::Map::Range r = trc->runtime->threads.all(); !r.empty(); r.popFront()) {
- JSThread *thread = r.front().value;
- ConservativeGCThreadData *ctd = &thread->data.conservativeGC;
- if (ctd->hasStackToScan()) {
- JS_ASSERT_IF(!thread->data.requestDepth, thread->suspendCount);
- MarkThreadDataConservatively(trc, &thread->data);
- } else {
- JS_ASSERT(!thread->suspendCount);
- JS_ASSERT(thread->data.requestDepth <= ctd->requestThreshold);
- }
- }
-#else
- MarkThreadDataConservatively(trc, &trc->runtime->threadData);
-#endif
-}
-
JS_NEVER_INLINE void
-ConservativeGCThreadData::recordStackTop()
+ConservativeGCData::recordStackTop()
{
/* Update the native stack pointer if it points to a bigger stack. */
uintptr_t dummy;
nativeStackTop = &dummy;
/*
- * To record and update the register snapshot for the conservative
- * scanning with the latest values we use setjmp.
+ * To record and update the register snapshot for the conservative scanning
+ * with the latest values we use setjmp.
*/
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4611)
#endif
(void) setjmp(registerSnapshot.jmpbuf);
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
}
-static inline void
+void
RecordNativeStackTopForGC(JSContext *cx)
{
- ConservativeGCThreadData *ctd = &JS_THREAD_DATA(cx)->conservativeGC;
+ ConservativeGCData *cgcd = &cx->runtime->conservativeGC;
#ifdef JS_THREADSAFE
/* Record the stack top here only if we are called from a request. */
- JS_ASSERT(cx->thread()->data.requestDepth >= ctd->requestThreshold);
- if (cx->thread()->data.requestDepth == ctd->requestThreshold)
+ JS_ASSERT(cx->runtime->requestDepth >= cgcd->requestThreshold);
+ if (cx->runtime->requestDepth == cgcd->requestThreshold)
return;
#endif
- ctd->recordStackTop();
+ cgcd->recordStackTop();
}
} /* namespace js */
bool
js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, gc::AllocKind *thingKind, void **thing)
{
return js::IsAddressableGCThing(rt, w, thingKind, NULL, thing) == CGCT_VALID;
@@ -1259,48 +1247,45 @@ js_AddRootRT(JSRuntime *rt, jsval *vp, c
/*
* Due to the long-standing, but now removed, use of rt->gcLock across the
* bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking
* properly with a racing GC, without calling JS_AddRoot from a request.
* We have to preserve API compatibility here, now that we avoid holding
* rt->gcLock across the mark phase (including the root hashtable mark).
*/
AutoLockGC lock(rt);
- js_WaitForGC(rt);
return !!rt->gcRootsHash.put((void *)vp,
RootInfo(name, JS_GC_ROOT_VALUE_PTR));
}
JS_FRIEND_API(JSBool)
js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name)
{
/*
* Due to the long-standing, but now removed, use of rt->gcLock across the
* bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking
* properly with a racing GC, without calling JS_AddRoot from a request.
* We have to preserve API compatibility here, now that we avoid holding
* rt->gcLock across the mark phase (including the root hashtable mark).
*/
AutoLockGC lock(rt);
- js_WaitForGC(rt);
return !!rt->gcRootsHash.put((void *)rp,
RootInfo(name, JS_GC_ROOT_GCTHING_PTR));
}
JS_FRIEND_API(JSBool)
js_RemoveRoot(JSRuntime *rt, void *rp)
{
/*
* Due to the JS_RemoveRootRT API, we may be called outside of a request.
* Same synchronization drill as above in js_AddRoot.
*/
AutoLockGC lock(rt);
- js_WaitForGC(rt);
rt->gcRootsHash.remove(rp);
rt->gcPoke = JS_TRUE;
return JS_TRUE;
}
typedef RootedValueMap::Range RootRange;
typedef RootedValueMap::Entry RootEntry;
typedef RootedValueMap::Enum RootEnum;
@@ -1655,19 +1640,16 @@ ArenaLists::finalizeScripts(JSContext *c
finalizeNow(cx, FINALIZE_SCRIPT);
}
static void
RunLastDitchGC(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
- /* The atoms are locked when we create a string in AtomizeInline. */
- AutoUnlockAtomsCompartmentWhenLocked unlockAtomsCompartment(cx);
-
/* The last ditch GC preserves all atoms. */
AutoKeepAtoms keep(rt);
js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcstats::LASTDITCH);
}
/* static */ void *
ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind)
{
@@ -2095,17 +2077,17 @@ MarkWeakReferences(GCMarker *gcmarker)
}
static void
MarkRuntime(JSTracer *trc)
{
JSRuntime *rt = trc->runtime;
if (rt->hasContexts())
- MarkConservativeStackRoots(trc);
+ MarkConservativeStackRoots(trc, rt);
for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
gc_root_traversal(trc, r.front());
for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront())
gc_lock_traversal(r.front(), trc);
if (rt->scriptPCCounters) {
@@ -2130,44 +2112,42 @@ MarkRuntime(JSTracer *trc)
for (CellIterUnderGC i(c, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->pcCounters)
MarkRoot(trc, script, "profilingScripts");
}
}
}
- for (ThreadDataIter i(rt); !i.empty(); i.popFront())
- i.threadData()->mark(trc);
+ rt->stackSpace.mark(trc);
/* The embedding can register additional roots here. */
if (JSTraceDataOp op = rt->gcBlackRootsTraceOp)
(*op)(trc, rt->gcBlackRootsData);
if (!IS_GC_MARKING_TRACER(trc)) {
/* We don't want to miss these when called from TraceRuntime. */
if (JSTraceDataOp op = rt->gcGrayRootsTraceOp)
(*op)(trc, rt->gcGrayRootsData);
}
}
void
TriggerGC(JSRuntime *rt, gcstats::Reason reason)
{
+ JS_ASSERT(rt->onOwnerThread());
+
if (rt->gcRunning || rt->gcIsNeeded)
return;
- /*
- * Trigger the GC when it is safe to call an operation callback on any
- * thread.
- */
+ /* Trigger the GC when it is safe to call an operation callback. */
rt->gcIsNeeded = true;
rt->gcTriggerCompartment = NULL;
rt->gcTriggerReason = reason;
- TriggerAllOperationCallbacks(rt);
+ rt->triggerOperationCallback();
}
void
TriggerCompartmentGC(JSCompartment *comp, gcstats::Reason reason)
{
JSRuntime *rt = comp->rt;
JS_ASSERT(!rt->gcRunning);
@@ -2197,17 +2177,17 @@ TriggerCompartmentGC(JSCompartment *comp
/*
* Trigger the GC when it is safe to call an operation callback on any
* thread.
*/
rt->gcIsNeeded = true;
rt->gcTriggerCompartment = comp;
rt->gcTriggerReason = reason;
- TriggerAllOperationCallbacks(comp->rt);
+ comp->rt->triggerOperationCallback();
}
void
MaybeGC(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
@@ -2370,30 +2350,47 @@ ExpireChunksAndArenas(JSRuntime *rt, boo
}
if (shouldShrink)
DecommitArenas(rt);
}
#ifdef JS_THREADSAFE
+static unsigned
+GetCPUCount()
+{
+ static unsigned ncpus = 0;
+ if (ncpus == 0) {
+# ifdef XP_WIN
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ ncpus = unsigned(sysinfo.dwNumberOfProcessors);
+# else
+ long n = sysconf(_SC_NPROCESSORS_ONLN);
+ ncpus = (n > 0) ? unsigned(n) : 1;
+# endif
+ }
+ return ncpus;
+}
+
bool
GCHelperThread::init()
{
if (!(wakeup = PR_NewCondVar(rt->gcLock)))
return false;
if (!(done = PR_NewCondVar(rt->gcLock)))
return false;
thread = PR_CreateThread(PR_USER_THREAD, threadMain, this, PR_PRIORITY_NORMAL,
PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
if (!thread)
return false;
- backgroundAllocation = (js_GetCPUCount() >= 2);
+ backgroundAllocation = (GetCPUCount() >= 2);
return true;
}
void
GCHelperThread::finish()
{
PRThread *join = NULL;
{
@@ -2669,17 +2666,17 @@ SweepCompartments(JSContext *cx, JSGCInv
static void
BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind)
{
JSRuntime *rt = cx->runtime;
for (GCCompartmentsIter c(rt); !c.done(); c.next())
c->purge(cx);
- js_PurgeThreads(cx);
+ rt->purge(cx);
{
JSContext *iter = NULL;
while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
acx->purge();
}
/*
@@ -2833,23 +2830,17 @@ SweepPhase(JSContext *cx, GCMarker *gcma
{
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_XPCONNECT);
if (rt->gcCallback)
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
}
}
-/*
- * Perform mark-and-sweep GC.
- *
- * In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each
- * other thread must be either outside all requests or blocked waiting for GC
- * to finish. The caller must hold rt->gcLock.
- */
+/* Perform mark-and-sweep GC. If comp is set, we perform a single-compartment GC. */
static void
MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind)
{
JSRuntime *rt = cx->runtime;
rt->gcNumber++;
/* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */
rt->gcIsNeeded = false;
@@ -2869,210 +2860,65 @@ MarkAndSweep(JSContext *cx, JSGCInvocati
rt->gcIncrementalTracer = &gcmarker;
BeginMarkPhase(cx, &gcmarker, gckind);
gcmarker.drainMarkStack();
EndMarkPhase(cx, &gcmarker, gckind);
SweepPhase(cx, &gcmarker, gckind);
}
-#ifdef JS_THREADSAFE
-
-/*
- * If the GC is running and we're called on another thread, wait for this GC
- * activation to finish. We can safely wait here without fear of deadlock (in
- * the case where we are called within a request on another thread's context)
- * because the GC doesn't set rt->gcRunning until after it has waited for all
- * active requests to end.
- *
- * We call here js_CurrentThreadId() after checking for rt->gcState to avoid
- * an expensive call when the GC is not running.
- */
-void
-js_WaitForGC(JSRuntime *rt)
-{
- if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
- do {
- JS_AWAIT_GC_DONE(rt);
- } while (rt->gcRunning);
- }
-}
-
-/*
- * GC is running on another thread. Temporarily suspend all requests running
- * on the current thread and wait until the GC is done.
- */
-static void
-LetOtherGCFinish(JSContext *cx)
-{
- JSRuntime *rt = cx->runtime;
- JS_ASSERT(rt->gcThread);
- JS_ASSERT(cx->thread() != rt->gcThread);
-
- size_t requestDebit = cx->thread()->data.requestDepth ? 1 : 0;
- JS_ASSERT(requestDebit <= rt->requestCount);
- if (requestDebit != 0) {
- rt->requestCount -= requestDebit;
- if (rt->requestCount == 0)
- JS_NOTIFY_REQUEST_DONE(rt);
-
- /*
- * Update the native stack before we wait so the GC thread see the
- * correct stack bounds.
- */
- RecordNativeStackTopForGC(cx);
- }
-
- /*
- * Check that we did not release the GC lock above and let the GC to
- * finish before we wait.
- */
- JS_ASSERT(rt->gcThread);
-
- /*
- * Wait for GC to finish on the other thread, even if requestDebit is 0
- * and even if GC has not started yet because the gcThread is waiting in
- * AutoGCSession. This ensures that js_GC never returns without a full GC
- * cycle happening.
- */
- do {
- JS_AWAIT_GC_DONE(rt);
- } while (rt->gcThread);
-
- rt->requestCount += requestDebit;
-}
-
-#endif
-
class AutoGCSession {
public:
explicit AutoGCSession(JSContext *cx);
~AutoGCSession();
private:
JSContext *context;
AutoGCSession(const AutoGCSession&) MOZ_DELETE;
void operator=(const AutoGCSession&) MOZ_DELETE;
};
-/*
- * Start a new GC session. Together with LetOtherGCFinish this function
- * contains the rendezvous algorithm by which we stop the world for GC.
- *
- * This thread becomes the GC thread. Wait for all other threads to quiesce.
- * Then set rt->gcRunning and return.
- */
+/* Start a new GC session. */
AutoGCSession::AutoGCSession(JSContext *cx)
: context(cx)
{
- JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
+ JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
JSRuntime *rt = cx->runtime;
-
-#ifdef JS_THREADSAFE
- if (rt->gcThread && rt->gcThread != cx->thread())
- LetOtherGCFinish(cx);
-#endif
-
JS_ASSERT(!rt->gcRunning);
-
-#ifdef JS_THREADSAFE
- /* No other thread is in GC, so indicate that we're now in GC. */
- JS_ASSERT(!rt->gcThread);
- rt->gcThread = cx->thread();
-
- /*
- * Notify operation callbacks on other threads, which will give them a
- * chance to yield their requests. Threads without requests perform their
- * callback at some later point, which then will be unnecessary, but
- * harmless.
- */
- for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
- JSThread *thread = r.front().value;
- if (thread != cx->thread())
- thread->data.triggerOperationCallback(rt);
- }
-
- /*
- * Discount the request on the current thread from contributing to
- * rt->requestCount before we wait for all other requests to finish.
- * JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on
- * rt->requestCount transitions to 0.
- */
- size_t requestDebit = cx->thread()->data.requestDepth ? 1 : 0;
- JS_ASSERT(requestDebit <= rt->requestCount);
- if (requestDebit != rt->requestCount) {
- rt->requestCount -= requestDebit;
-
- do {
- JS_AWAIT_REQUEST_DONE(rt);
- } while (rt->requestCount > 0);
- rt->requestCount += requestDebit;
- }
-
-#endif /* JS_THREADSAFE */
-
- /*
- * Set rt->gcRunning here within the GC lock, and after waiting for any
- * active requests to end. This way js_WaitForGC called outside a request
- * would not block on the GC that is waiting for other requests to finish
- * with rt->gcThread set while JS_BeginRequest would do such wait.
- */
rt->gcRunning = true;
}
-/* End the current GC session and allow other threads to proceed. */
AutoGCSession::~AutoGCSession()
{
JSRuntime *rt = context->runtime;
rt->gcRunning = false;
-#ifdef JS_THREADSAFE
- JS_ASSERT(rt->gcThread == context->thread());
- rt->gcThread = NULL;
- JS_NOTIFY_GC_DONE(rt);
-#endif
}
/*
* GC, repeatedly if necessary, until we think we have not created any new
- * garbage and no other threads are demanding more GC. We disable inlining
- * to ensure that the bottom of the stack with possible GC roots recorded in
- * js_GC excludes any pointers we use during the marking implementation.
+ * garbage. We disable inlining to ensure that the bottom of the stack with
+ * possible GC roots recorded in js_GC excludes any pointers we use during the
+ * marking implementation.
*/
static JS_NEVER_INLINE void
GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
{
JSRuntime *rt = cx->runtime;
JS_ASSERT_IF(comp, comp != rt->atomsCompartment);
JS_ASSERT_IF(comp, rt->gcMode == JSGC_MODE_COMPARTMENT);
- /*
- * Recursive GC is no-op and a call from another thread waits the started
- * GC cycle to finish.
- */
- if (rt->gcMarkAndSweep) {
-#ifdef JS_THREADSAFE
- JS_ASSERT(rt->gcThread);
- if (rt->gcThread != cx->thread()) {
- /* We do not return until another GC finishes. */
- LetOtherGCFinish(cx);
- }
-#endif
+ /* Recursive GC is no-op. */
+ if (rt->gcMarkAndSweep)
return;
- }
AutoGCSession gcsession(cx);
- /*
- * Don't GC if any thread is reporting an OOM. We check the flag after we
- * have set up the GC session and know that the thread that reported OOM
- * is either the current thread or waits for the GC to complete on this
- * thread.
- */
+ /* Don't GC if we are reporting an OOM. */
if (rt->inOOMReport)
return;
/*
* We should not be depending on cx->compartment in the GC, so set it to
* NULL to look for violations.
*/
SwitchToCompartment sc(cx, (JSCompartment *)NULL);
@@ -3092,19 +2938,16 @@ GCCycle(JSContext *cx, JSCompartment *co
JS_ASSERT(!cx->gcBackgroundFree);
rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
if (rt->hasContexts() && rt->gcHelperThread.prepareForBackgroundSweep())
cx->gcBackgroundFree = &rt->gcHelperThread;
#endif
MarkAndSweep(cx, gckind);
- if (!comp)
- js_PurgeThreads_PostGlobalSweep(cx);
-
#ifdef JS_THREADSAFE
if (cx->gcBackgroundFree) {
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
cx->gcBackgroundFree = NULL;
rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK);
}
#endif
@@ -3205,17 +3048,17 @@ void
TraceRuntime(JSTracer *trc)
{
JS_ASSERT(!IS_GC_MARKING_TRACER(trc));
#ifdef JS_THREADSAFE
{
JSContext *cx = trc->context;
JSRuntime *rt = cx->runtime;
- if (rt->gcThread != cx->thread()) {
+ if (!rt->gcRunning) {
AutoLockGC lock(rt);
AutoGCSession gcsession(cx);
rt->gcHelperThread.waitBackgroundSweepEnd();
AutoUnlockGC unlock(rt);
AutoCopyFreeListToArenas copy(rt);
RecordNativeStackTopForGC(trc->context);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1315,21 +1315,16 @@ struct WrapperHasher
static bool match(const Value &l, const Value &k) { return l == k; }
};
typedef HashMap<Value, Value, WrapperHasher, SystemAllocPolicy> WrapperMap;
} /* namespace js */
-#ifdef DEBUG
-extern bool
-CheckAllocation(JSContext *cx);
-#endif
-
extern JS_FRIEND_API(JSGCTraceKind)
js_GetGCThingTraceKind(void *thing);
extern JSBool
js_InitGC(JSRuntime *rt, uint32_t maxbytes);
extern void
js_FinishGC(JSRuntime *rt);
@@ -1411,32 +1406,16 @@ typedef enum JSGCInvocationKind {
/* Minimize GC triggers and release empty GC chunks right away. */
GC_SHRINK = 1
} JSGCInvocationKind;
/* Pass NULL for |comp| to get a full GC. */
extern void
js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcstats::Reason r);
-#ifdef JS_THREADSAFE
-/*
- * This is a helper for code at can potentially run outside JS request to
- * ensure that the GC is not running when the function returns.
- *
- * This function must be called with the GC lock held.
- */
-extern void
-js_WaitForGC(JSRuntime *rt);
-
-#else /* !JS_THREADSAFE */
-
-# define js_WaitForGC(rt) ((void) 0)
-
-#endif
-
namespace js {
#ifdef JS_THREADSAFE
class GCHelperThread {
enum State {
IDLE,
SWEEPING,
@@ -1580,67 +1559,16 @@ struct GCChunkHasher {
JS_ASSERT(!(uintptr_t(k) & gc::ChunkMask));
JS_ASSERT(!(uintptr_t(l) & gc::ChunkMask));
return k == l;
}
};
typedef HashSet<js::gc::Chunk *, GCChunkHasher, SystemAllocPolicy> GCChunkSet;
-struct ConservativeGCThreadData {
-
- /*
- * The GC scans conservatively between ThreadData::nativeStackBase and
- * nativeStackTop unless the latter is NULL.
- */
- uintptr_t *nativeStackTop;
-
- union {
- jmp_buf jmpbuf;
- uintptr_t words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))];
- } registerSnapshot;
-
- /*
- * Cycle collector uses this to communicate that the native stack of the
- * GC thread should be scanned only if the thread have more than the given
- * threshold of requests.
- */
- unsigned requestThreshold;
-
- ConservativeGCThreadData()
- : nativeStackTop(NULL), requestThreshold(0)
- {
- }
-
- ~ConservativeGCThreadData() {
-#ifdef JS_THREADSAFE
- /*
- * The conservative GC scanner should be disabled when the thread leaves
- * the last request.
- */
- JS_ASSERT(!hasStackToScan());
-#endif
- }
-
- JS_NEVER_INLINE void recordStackTop();
-
-#ifdef JS_THREADSAFE
- void updateForRequestEnd(unsigned suspendCount) {
- if (suspendCount)
- recordStackTop();
- else
- nativeStackTop = NULL;
- }
-#endif
-
- bool hasStackToScan() const {
- return !!nativeStackTop;
- }
-};
-
template<class T>
struct MarkStack {
T *stack;
T *tos;
T *limit;
bool push(T item) {
if (tos == limit)
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -341,17 +341,17 @@ class CellIter: public CellIterImpl
#endif
if (lists->isSynchronizedFreeList(kind)) {
lists = NULL;
} else {
JS_ASSERT(!comp->rt->gcRunning);
lists->copyFreeListToArena(kind);
}
#ifdef DEBUG
- counter = &JS_THREAD_DATA(cx)->noGCOrAllocationCheck;
+ counter = &cx->runtime->noGCOrAllocationCheck;
++*counter;
#endif
init(comp, kind);
}
~CellIter() {
#ifdef DEBUG
JS_ASSERT(*counter > 0);
@@ -379,17 +379,17 @@ inline T *
NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
{
JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
#ifdef JS_THREADSAFE
JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment),
kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
#endif
JS_ASSERT(!cx->runtime->gcRunning);
- JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
+ JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
#ifdef JS_GC_ZEAL
if (cx->runtime->needZealousGC())
js::gc::RunDebugGC(cx);
#endif
js::gc::MaybeCheckStackRoots(cx);
@@ -406,17 +406,17 @@ inline T *
TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
{
JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
#ifdef JS_THREADSAFE
JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment),
kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
#endif
JS_ASSERT(!cx->runtime->gcRunning);
- JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
+ JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
#ifdef JS_GC_ZEAL
if (cx->runtime->needZealousGC())
return NULL;
#endif
void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize);
return static_cast<T *>(t);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1245,23 +1245,23 @@ class GenericInterruptEnabler : public I
T *variable;
T value;
};
inline InterpreterFrames::InterpreterFrames(JSContext *cx, FrameRegs *regs,
const InterruptEnablerBase &enabler)
: context(cx), regs(regs), enabler(enabler)
{
- older = JS_THREAD_DATA(cx)->interpreterFrames;
- JS_THREAD_DATA(cx)->interpreterFrames = this;
+ older = cx->runtime->interpreterFrames;
+ cx->runtime->interpreterFrames = this;
}
inline InterpreterFrames::~InterpreterFrames()
{
- JS_THREAD_DATA(context)->interpreterFrames = older;
+ context->runtime->interpreterFrames = older;
}
#if defined(DEBUG) && !defined(JS_THREADSAFE)
void
js::AssertValidPropertyCacheHit(JSContext *cx,
JSObject *start, JSObject *found,
PropertyCacheEntry *entry)
{
@@ -1541,17 +1541,17 @@ js::Interpret(JSContext *cx, StackFrame
JS_END_MACRO
/*
* Prepare to call a user-supplied branch handler, and abort the script
* if it returns false.
*/
#define CHECK_BRANCH() \
JS_BEGIN_MACRO \
- if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
+ if (cx->runtime->interrupt && !js_HandleExecutionInterrupt(cx)) \
goto error; \
JS_END_MACRO
#define BRANCH(n) \
JS_BEGIN_MACRO \
regs.pc += (n); \
op = (JSOp) *regs.pc; \
if ((n) <= 0) \
deleted file mode 100644
--- a/js/src/jslock.cpp
+++ /dev/null
@@ -1,743 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifdef JS_THREADSAFE
-
-/*
- * JS locking stubs.
- */
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef XP_WIN
-# include "jswin.h"
-#else
-# include <unistd.h>
-#endif
-
-#include "jspubtd.h"
-#include "jstypes.h"
-#include "jsutil.h"
-#include "jsstdint.h"
-#include "jscntxt.h"
-#include "jsgc.h"
-#include "jslock.h"
-#include "jsscope.h"
-#include "jsstr.h"
-
-#include "jsscopeinlines.h"
-
-using namespace js;
-
-#define ReadWord(W) (W)
-
-#if !defined(__GNUC__)
-# define __asm__ asm
-# define __volatile__ volatile
-#endif
-
-/* Implement NativeCompareAndSwap. */
-
-#if defined(_MSC_VER) && defined(_M_IX86)
-// TODO: Bug 716204 - undo this pragma.
-#pragma warning( disable : 4035 )
-JS_BEGIN_EXTERN_C
-extern long __cdecl
-_InterlockedCompareExchange(long *volatile dest, long exchange, long comp);
-JS_END_EXTERN_C
-#pragma intrinsic(_InterlockedCompareExchange)
-JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(long));
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwapHelper(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- _InterlockedCompareExchange((long*) w, nv, ov);
- __asm {
- sete al
- }
-}
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- return (NativeCompareAndSwapHelper(w, ov, nv) & 1);
-}
-
-#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64))
-/*
- * Compared with the _InterlockedCompareExchange in the 32 bit case above MSVC
- * declares _InterlockedCompareExchange64 through <windows.h>.
- */
-#pragma intrinsic(_InterlockedCompareExchange64)
-JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(long long));
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- return _InterlockedCompareExchange64((long long *volatile)w, nv, ov) == ov;
-}
-
-#elif defined(XP_MACOSX) || defined(DARWIN)
-
-#include <libkern/OSAtomic.h>
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- /* Details on these functions available in the manpage for atomic */
- return OSAtomicCompareAndSwapPtrBarrier(reinterpret_cast<void *>(ov),
- reinterpret_cast<void *>(nv),
- reinterpret_cast<void * volatile *>(w));
-}
-
-#elif defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC))
-
-/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- unsigned int res;
-
- __asm__ __volatile__ (
- "lock\n"
- "cmpxchgl %2, (%1)\n"
- "sete %%al\n"
- "andl $1, %%eax\n"
- : "=a" (res)
-#ifdef __SUNPRO_CC
-/* Different code for Sun Studio because of a bug of SS12U1 */
- : "c" (w), "d" (nv), "a" (ov)
-#else
- : "r" (w), "r" (nv), "a" (ov)
-#endif
- : "cc", "memory");
- return (int)res;
-}
-#elif defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC))
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- unsigned int res;
-
- __asm__ __volatile__ (
- "lock\n"
- "cmpxchgq %2, (%1)\n"
- "sete %%al\n"
- "movzbl %%al, %%eax\n"
- : "=a" (res)
- : "r" (w), "r" (nv), "a" (ov)
- : "cc", "memory");
- return (int)res;
-}
-
-#elif defined(__sparc)
-#if defined(__GNUC__)
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- unsigned int res;
-
- __asm__ __volatile__ (
- "membar #StoreLoad | #LoadLoad\n"
-#if JS_BITS_PER_WORD == 32
- "cas [%1],%2,%3\n"
-#else
- "casx [%1],%2,%3\n"
-#endif
- "membar #StoreLoad | #LoadLoad\n"
- "cmp %2,%3\n"
- "be,a 1f\n"
- "mov 1,%0\n"
- "mov 0,%0\n"
- "1:"
- : "=r" (res)
- : "r" (w), "r" (ov), "r" (nv));
- return (int)res;
-}
-
-#elif defined(__SUNPRO_CC)
-
-/* Implementation in lock_sparc*.il */
-extern "C" int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv);
-
-#endif
-
-#elif defined(AIX)
-
-#include <sys/atomic_op.h>
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- int res;
- JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(long));
-
- res = compare_and_swaplp((atomic_l)w, &ov, nv);
- if (res)
- __asm__("isync");
- return res;
-}
-
-#elif defined(__arm__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
-
-JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(int));
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- return __sync_bool_compare_and_swap(w, ov, nv);
-}
-
-#elif defined(USE_ARM_KUSER)
-
-/* See https://bugzilla.mozilla.org/show_bug.cgi?id=429387 for a
- * description of this ABI; this is a function provided at a fixed
- * location by the kernel in the memory space of each process.
- */
-typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
-#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
-
-JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(int));
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- volatile int *vp = (volatile int *) w;
- PRInt32 failed = 1;
-
- /* Loop until a __kernel_cmpxchg succeeds. See bug 446169 */
- do {
- failed = __kernel_cmpxchg(ov, nv, vp);
- } while (failed && *vp == ov);
- return !failed;
-}
-
-#elif JS_HAS_NATIVE_COMPARE_AND_SWAP
-
-#error "JS_HAS_NATIVE_COMPARE_AND_SWAP should be 0 if your platform lacks a compare-and-swap instruction."
-
-#endif /* arch-tests */
-
-#if JS_HAS_NATIVE_COMPARE_AND_SWAP
-
-JSBool
-js_CompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- return !!NativeCompareAndSwap(w, ov, nv);
-}
-
-#elif defined(NSPR_LOCK)
-
-# ifdef __GNUC__
-# warning "js_CompareAndSwap is implemented using NSPR lock"
-# endif
-
-JSBool
-js_CompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
- int result;
- static PRLock *CompareAndSwapLock = JS_NEW_LOCK();
-
- JS_ACQUIRE_LOCK(CompareAndSwapLock);
- result = (*w == ov);
- if (result)
- *w = nv;
- JS_RELEASE_LOCK(CompareAndSwapLock);
- return result;
-}
-
-#else /* !defined(NSPR_LOCK) */
-
-#error "NSPR_LOCK should be on when the platform lacks native compare-and-swap."
-
-#endif
-
-void
-js_AtomicSetMask(volatile intptr_t *w, intptr_t mask)
-{
- intptr_t ov, nv;
-
- do {
- ov = *w;
- nv = ov | mask;
- } while (!js_CompareAndSwap(w, ov, nv));
-}
-
-void
-js_AtomicClearMask(volatile intptr_t *w, intptr_t mask)
-{
- intptr_t ov, nv;
-
- do {
- ov = *w;
- nv = ov & ~mask;
- } while (!js_CompareAndSwap(w, ov, nv));
-}
-
-unsigned
-js_GetCPUCount()
-{
- static unsigned ncpus = 0;
- if (ncpus == 0) {
-# ifdef XP_WIN
- SYSTEM_INFO sysinfo;
- GetSystemInfo(&sysinfo);
- ncpus = unsigned(sysinfo.dwNumberOfProcessors);
-# else
- long n = sysconf(_SC_NPROCESSORS_ONLN);
- ncpus = (n > 0) ? unsigned(n) : 1;
-# endif
- }
- return ncpus;
-}
-
-#ifndef NSPR_LOCK
-
-struct JSFatLock {
- int susp;
- PRLock *slock;
- PRCondVar *svar;
- JSFatLock *next;
- JSFatLock **prevp;
-};
-
-typedef struct JSFatLockTable {
- JSFatLock *free_;
- JSFatLock *taken;
-} JSFatLockTable;
-
-#define GLOBAL_LOCK_INDEX(id) (((uint32_t)(uintptr_t)(id)>>2) & global_locks_mask)
-
-static void
-js_Dequeue(JSThinLock *);
-
-static PRLock **global_locks;
-static uint32_t global_lock_count = 1;
-static uint32_t global_locks_log2 = 0;
-static uint32_t global_locks_mask = 0;
-
-static void
-js_LockGlobal(void *id)
-{
- uint32_t i = GLOBAL_LOCK_INDEX(id);
- PR_Lock(global_locks[i]);
-}
-
-static void
-js_UnlockGlobal(void *id)
-{
- uint32_t i = GLOBAL_LOCK_INDEX(id);
- PR_Unlock(global_locks[i]);
-}
-
-#endif /* !NSPR_LOCK */
-
-void
-js_InitLock(JSThinLock *tl)
-{
-#ifdef NSPR_LOCK
- tl->owner = 0;
- tl->fat = (JSFatLock*)JS_NEW_LOCK();
-#else
- PodZero(tl);
-#endif
-}
-
-void
-js_FinishLock(JSThinLock *tl)
-{
-#ifdef NSPR_LOCK
- tl->owner = 0xdeadbeef;
- if (tl->fat)
- JS_DESTROY_LOCK(((JSLock*)tl->fat));
-#else
- JS_ASSERT(tl->owner == 0);
- JS_ASSERT(tl->fat == NULL);
-#endif
-}
-
-#ifndef NSPR_LOCK
-
-static JSFatLock *
-NewFatlock()
-{
- JSFatLock *fl = (JSFatLock *) OffTheBooks::malloc_(sizeof(JSFatLock)); /* for now */
- if (!fl) return NULL;
- fl->susp = 0;
- fl->next = NULL;
- fl->prevp = NULL;
- fl->slock = PR_NewLock();
- fl->svar = PR_NewCondVar(fl->slock);
- return fl;
-}
-
-static void
-DestroyFatlock(JSFatLock *fl)
-{
- PR_DestroyLock(fl->slock);
- PR_DestroyCondVar(fl->svar);
- UnwantedForeground::free_(fl);
-}
-
-static JSFatLock *
-ListOfFatlocks(int listc)
-{
- JSFatLock *m;
- JSFatLock *m0;
- int i;
-
- JS_ASSERT(listc>0);
- m0 = m = NewFatlock();
- for (i=1; i<listc; i++) {
- m->next = NewFatlock();
- m = m->next;
- }
- return m0;
-}
-
-static void
-DeleteListOfFatlocks(JSFatLock *m)
-{
- JSFatLock *m0;
- for (; m; m=m0) {
- m0 = m->next;
- DestroyFatlock(m);
- }
-}
-
-static JSFatLockTable *fl_list_table = NULL;
-static uint32_t fl_list_table_len = 0;
-static uint32_t fl_list_chunk_len = 0;
-
-static JSFatLock *
-GetFatlock(void *id)
-{
- JSFatLock *m;
-
- uint32_t i = GLOBAL_LOCK_INDEX(id);
- if (fl_list_table[i].free_ == NULL) {
-#ifdef DEBUG
- if (fl_list_table[i].taken)
- printf("Ran out of fat locks!\n");
-#endif
- fl_list_table[i].free_ = ListOfFatlocks(fl_list_chunk_len);
- }
- m = fl_list_table[i].free_;
- fl_list_table[i].free_ = m->next;
- m->susp = 0;
- m->next = fl_list_table[i].taken;
- m->prevp = &fl_list_table[i].taken;
- if (fl_list_table[i].taken)
- fl_list_table[i].taken->prevp = &m->next;
- fl_list_table[i].taken = m;
- return m;
-}
-
-static void
-PutFatlock(JSFatLock *m, void *id)
-{
- uint32_t i;
- if (m == NULL)
- return;
-
- /* Unlink m from fl_list_table[i].taken. */
- *m->prevp = m->next;
- if (m->next)
- m->next->prevp = m->prevp;
-
- /* Insert m in fl_list_table[i].free. */
- i = GLOBAL_LOCK_INDEX(id);
- m->next = fl_list_table[i].free_;
- fl_list_table[i].free_ = m;
-}
-
-#endif /* !NSPR_LOCK */
-
-JSBool
-js_SetupLocks(int listc, int globc)
-{
-#ifndef NSPR_LOCK
- uint32_t i;
-
- if (global_locks)
- return JS_TRUE;
-#ifdef DEBUG
- if (listc > 10000 || listc < 0) /* listc == fat lock list chunk length */
- printf("Bad number %d in js_SetupLocks()!\n", listc);
- if (globc > 100 || globc < 0) /* globc == number of global locks */
- printf("Bad number %d in js_SetupLocks()!\n", listc);
-#endif
- global_locks_log2 = JS_CEILING_LOG2W(globc);
- global_locks_mask = JS_BITMASK(global_locks_log2);
- global_lock_count = JS_BIT(global_locks_log2);
- global_locks = (PRLock **) OffTheBooks::malloc_(global_lock_count * sizeof(PRLock*));
- if (!global_locks)
- return JS_FALSE;
- for (i = 0; i < global_lock_count; i++) {
- global_locks[i] = PR_NewLock();
- if (!global_locks[i]) {
- global_lock_count = i;
- js_CleanupLocks();
- return JS_FALSE;
- }
- }
- fl_list_table = (JSFatLockTable *) OffTheBooks::malloc_(i * sizeof(JSFatLockTable));
- if (!fl_list_table) {
- js_CleanupLocks();
- return JS_FALSE;
- }
- fl_list_table_len = global_lock_count;
- for (i = 0; i < global_lock_count; i++)
- fl_list_table[i].free_ = fl_list_table[i].taken = NULL;
- fl_list_chunk_len = listc;
-#endif /* !NSPR_LOCK */
- return JS_TRUE;
-}
-
-void
-js_CleanupLocks()
-{
-#ifndef NSPR_LOCK
- uint32_t i;
-
- if (global_locks) {
- for (i = 0; i < global_lock_count; i++)
- PR_DestroyLock(global_locks[i]);
- UnwantedForeground::free_(global_locks);
- global_locks = NULL;
- global_lock_count = 1;
- global_locks_log2 = 0;
- global_locks_mask = 0;
- }
- if (fl_list_table) {
- for (i = 0; i < fl_list_table_len; i++) {
- DeleteListOfFatlocks(fl_list_table[i].free_);
- fl_list_table[i].free_ = NULL;
- DeleteListOfFatlocks(fl_list_table[i].taken);
- fl_list_table[i].taken = NULL;
- }
- UnwantedForeground::free_(fl_list_table);
- fl_list_table = NULL;
- fl_list_table_len = 0;
- }
-#endif /* !NSPR_LOCK */
-}
-
-#ifdef NSPR_LOCK
-
-static JS_ALWAYS_INLINE void
-ThinLock(JSThinLock *tl, intptr_t me)
-{
- JS_ACQUIRE_LOCK((JSLock *) tl->fat);
- tl->owner = me;
-}
-
-static JS_ALWAYS_INLINE void
-ThinUnlock(JSThinLock *tl, intptr_t /*me*/)
-{
- tl->owner = 0;
- JS_RELEASE_LOCK((JSLock *) tl->fat);
-}
-
-#else
-
-/*
- * Fast locking and unlocking is implemented by delaying the allocation of a
- * system lock (fat lock) until contention. As long as a locking thread A
- * runs uncontended, the lock is represented solely by storing A's identity in
- * the object being locked.
- *
- * If another thread B tries to lock the object currently locked by A, B is
- * enqueued into a fat lock structure (which might have to be allocated and
- * pointed to by the object), and suspended using NSPR conditional variables
- * (wait). A wait bit (Bacon bit) is set in the lock word of the object,
- * signalling to A that when releasing the lock, B must be dequeued and
- * notified.
- *
- * The basic operation of the locking primitives (js_Lock, js_Unlock,
- * js_Enqueue, and js_Dequeue) is compare-and-swap. Hence, when locking into
- * the word pointed at by p, compare-and-swap(p, 0, A) success implies that p
- * is unlocked. Similarly, when unlocking p, if compare-and-swap(p, A, 0)
- * succeeds this implies that p is uncontended (no one is waiting because the
- * wait bit is not set).
- *
- * When dequeueing, the lock is released, and one of the threads suspended on
- * the lock is notified. If other threads still are waiting, the wait bit is
- * kept (in js_Enqueue), and if not, the fat lock is deallocated.
- *
- * The functions js_Enqueue, js_Dequeue, js_SuspendThread, and js_ResumeThread
- * are serialized using a global lock. For scalability, a hashtable of global
- * locks is used, which is indexed modulo the thin lock pointer.
- */
-
-/*
- * Invariants:
- * (i) global lock is held
- * (ii) fl->susp >= 0
- */
-static int
-js_SuspendThread(JSThinLock *tl)
-{
- JSFatLock *fl;
- if (tl->fat == NULL)
- fl = tl->fat = GetFatlock(tl);
- else
- fl = tl->fat;
- JS_ASSERT(fl->susp >= 0);
- fl->susp++;
- PR_Lock(fl->slock);
- js_UnlockGlobal(tl);
- DebugOnly<PRStatus> stat = PR_WaitCondVar(fl->svar, PR_INTERVAL_NO_TIMEOUT);
- JS_ASSERT(stat != PR_FAILURE);
- PR_Unlock(fl->slock);
- js_LockGlobal(tl);
- fl->susp--;
- if (fl->susp == 0) {
- PutFatlock(fl, tl);
- tl->fat = NULL;
- }
- return tl->fat == NULL;
-}
-
-/*
- * (i) global lock is held
- * (ii) fl->susp > 0
- */
-static void
-js_ResumeThread(JSThinLock *tl)
-{
- JSFatLock *fl = tl->fat;
- JS_ASSERT(fl != NULL);
- JS_ASSERT(fl->susp > 0);
- PR_Lock(fl->slock);
- js_UnlockGlobal(tl);
- DebugOnly<PRStatus> stat = PR_NotifyCondVar(fl->svar);
- JS_ASSERT(stat != PR_FAILURE);
- PR_Unlock(fl->slock);
-}
-
-static void
-js_Enqueue(JSThinLock *tl, intptr_t me)
-{
- intptr_t o, n;
-
- js_LockGlobal(tl);
- for (;;) {
- o = ReadWord(tl->owner);
- n = Thin_SetWait(o);
- if (o != 0 && NativeCompareAndSwap(&tl->owner, o, n)) {
- if (js_SuspendThread(tl))
- me = Thin_RemoveWait(me);
- else
- me = Thin_SetWait(me);
- }
- else if (NativeCompareAndSwap(&tl->owner, 0, me)) {
- js_UnlockGlobal(tl);
- return;
- }
- }
-}
-
-static void
-js_Dequeue(JSThinLock *tl)
-{
- intptr_t o;
-
- js_LockGlobal(tl);
- o = ReadWord(tl->owner);
- JS_ASSERT(Thin_GetWait(o) != 0);
- JS_ASSERT(tl->fat != NULL);
- if (!NativeCompareAndSwap(&tl->owner, o, 0)) /* release it */
- JS_ASSERT(0);
- js_ResumeThread(tl);
-}
-
-static JS_ALWAYS_INLINE void
-ThinLock(JSThinLock *tl, intptr_t me)
-{
- JS_ASSERT(CURRENT_THREAD_IS_ME(me));
- if (NativeCompareAndSwap(&tl->owner, 0, me))
- return;
- if (Thin_RemoveWait(ReadWord(tl->owner)) != me)
- js_Enqueue(tl, me);
-#ifdef DEBUG
- else
- JS_ASSERT(0);
-#endif
-}
-
-static JS_ALWAYS_INLINE void
-ThinUnlock(JSThinLock *tl, intptr_t me)
-{
- JS_ASSERT(CURRENT_THREAD_IS_ME(me));
-
- /*
- * Since we can race with the NativeCompareAndSwap in js_Enqueue, we need
- * to use a C_A_S here as well -- Arjan van de Ven 30/1/08
- */
- if (NativeCompareAndSwap(&tl->owner, me, 0))
- return;
-
- JS_ASSERT(Thin_GetWait(tl->owner));
- if (Thin_RemoveWait(ReadWord(tl->owner)) == me)
- js_Dequeue(tl);
-#ifdef DEBUG
- else
- JS_ASSERT(0); /* unbalanced unlock */
-#endif
-}
-
-#endif /* !NSPR_LOCK */
-
-void
-js_Lock(JSContext *cx, JSThinLock *tl)
-{
- ThinLock(tl, CX_THINLOCK_ID(cx));
-}
-
-void
-js_Unlock(JSContext *cx, JSThinLock *tl)
-{
- ThinUnlock(tl, CX_THINLOCK_ID(cx));
-}
-
-#endif /* JS_THREADSAFE */
--- a/js/src/jslock.h
+++ b/js/src/jslock.h
@@ -34,207 +34,54 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef jslock_h__
#define jslock_h__
-#include "jstypes.h"
#include "jsapi.h"
-#include "jsprvtd.h"
#ifdef JS_THREADSAFE
+
# include "pratom.h"
# include "prlock.h"
# include "prcvar.h"
# include "prthread.h"
# include "prinit.h"
-#endif
-#ifdef JS_THREADSAFE
-
-#if (defined(_WIN32) && defined(_M_IX86)) || \
- (defined(_WIN64) && (defined(_M_AMD64) || defined(_M_X64))) || \
- (defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \
- (defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \
- (defined(__sparc) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \
- (defined(__arm__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) || \
- defined(AIX) || \
- defined(USE_ARM_KUSER)
-# define JS_HAS_NATIVE_COMPARE_AND_SWAP 1
-#else
-# define JS_HAS_NATIVE_COMPARE_AND_SWAP 0
-#endif
-
-#if defined(JS_USE_ONLY_NSPR_LOCKS) || !JS_HAS_NATIVE_COMPARE_AND_SWAP
-# define NSPR_LOCK 1
-#else
-# undef NSPR_LOCK
-#endif
-
-#define Thin_GetWait(W) ((intptr_t)(W) & 0x1)
-#define Thin_SetWait(W) ((intptr_t)(W) | 0x1)
-#define Thin_RemoveWait(W) ((intptr_t)(W) & ~0x1)
-
-typedef struct JSFatLock JSFatLock;
-
-typedef struct JSThinLock {
- intptr_t owner;
- JSFatLock *fat;
-} JSThinLock;
-
-#define CX_THINLOCK_ID(cx) ((intptr_t)(cx)->thread())
-#define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId())
-
-typedef PRLock JSLock;
-
-/*
- * Atomic increment and decrement for a reference counter, given jsrefcount *p.
- * NB: jsrefcount is int32, aka PRInt32, so that pratom.h functions work.
- */
-#define JS_ATOMIC_INCREMENT(p) PR_ATOMIC_INCREMENT((PRInt32 *)(p))
-#define JS_ATOMIC_DECREMENT(p) PR_ATOMIC_DECREMENT((PRInt32 *)(p))
-#define JS_ATOMIC_ADD(p,v) PR_ATOMIC_ADD((PRInt32 *)(p), (PRInt32)(v))
-#define JS_ATOMIC_SET(p,v) PR_ATOMIC_SET((PRInt32 *)(p), (PRInt32)(v))
-
-#define js_CurrentThreadId() PR_GetCurrentThread()
-#define JS_NEW_LOCK() PR_NewLock()
-#define JS_DESTROY_LOCK(l) PR_DestroyLock(l)
-#define JS_ACQUIRE_LOCK(l) PR_Lock(l)
-#define JS_RELEASE_LOCK(l) PR_Unlock(l)
-
-#define JS_NEW_CONDVAR(l) PR_NewCondVar(l)
-#define JS_DESTROY_CONDVAR(cv) PR_DestroyCondVar(cv)
-#define JS_WAIT_CONDVAR(cv,to) PR_WaitCondVar(cv,to)
-#define JS_NO_TIMEOUT PR_INTERVAL_NO_TIMEOUT
-#define JS_NOTIFY_CONDVAR(cv) PR_NotifyCondVar(cv)
-#define JS_NOTIFY_ALL_CONDVAR(cv) PR_NotifyAllCondVar(cv)
-
-#define JS_LOCK(cx, tl) js_Lock(cx, tl)
-#define JS_UNLOCK(cx, tl) js_Unlock(cx, tl)
-
-extern void js_Lock(JSContext *cx, JSThinLock *tl);
-extern void js_Unlock(JSContext *cx, JSThinLock *tl);
-extern int js_SetupLocks(int,int);
-extern void js_CleanupLocks();
-extern void js_InitLock(JSThinLock *);
-extern void js_FinishLock(JSThinLock *);
-
-#else /* !JS_THREADSAFE */
+# define JS_ATOMIC_INCREMENT(p) PR_ATOMIC_INCREMENT((PRInt32 *)(p))
+# define JS_ATOMIC_DECREMENT(p) PR_ATOMIC_DECREMENT((PRInt32 *)(p))
+# define JS_ATOMIC_ADD(p,v) PR_ATOMIC_ADD((PRInt32 *)(p), (PRInt32)(v))
+# define JS_ATOMIC_SET(p,v) PR_ATOMIC_SET((PRInt32 *)(p), (PRInt32)(v))
-#define JS_ATOMIC_INCREMENT(p) (++*(p))
-#define JS_ATOMIC_DECREMENT(p) (--*(p))
-#define JS_ATOMIC_ADD(p,v) (*(p) += (v))
-#define JS_ATOMIC_SET(p,v) (*(p) = (v))
-
-#define js_CurrentThreadId() ((void*)NULL)
-#define JS_NEW_LOCK() NULL
-#define JS_DESTROY_LOCK(l) ((void)0)
-#define JS_ACQUIRE_LOCK(l) ((void)0)
-#define JS_RELEASE_LOCK(l) ((void)0)
-#define JS_LOCK(cx, tl) ((void)0)
-#define JS_UNLOCK(cx, tl) ((void)0)
-
-#define JS_NEW_CONDVAR(l) NULL
-#define JS_DESTROY_CONDVAR(cv) ((void)0)
-#define JS_WAIT_CONDVAR(cv,to) ((void)0)
-#define JS_NOTIFY_CONDVAR(cv) ((void)0)
-#define JS_NOTIFY_ALL_CONDVAR(cv) ((void)0)
-
-#endif /* !JS_THREADSAFE */
-
-#define JS_LOCK_GC(rt) JS_ACQUIRE_LOCK((rt)->gcLock)
-#define JS_UNLOCK_GC(rt) JS_RELEASE_LOCK((rt)->gcLock)
-#define JS_AWAIT_GC_DONE(rt) JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT)
-#define JS_NOTIFY_GC_DONE(rt) JS_NOTIFY_ALL_CONDVAR((rt)->gcDone)
-#define JS_AWAIT_REQUEST_DONE(rt) JS_WAIT_CONDVAR((rt)->requestDone, \
- JS_NO_TIMEOUT)
-#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone)
-
-#ifndef JS_SET_OBJ_INFO
-#define JS_SET_OBJ_INFO(obj,f,l) ((void)0)
-#endif
-#ifndef JS_SET_TITLE_INFO
-#define JS_SET_TITLE_INFO(title,f,l) ((void)0)
-#endif
-
-#ifdef JS_THREADSAFE
+#else /* JS_THREADSAFE */
-extern JSBool
-js_CompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv);
-
-/* Atomically bitwise-or the mask into the word *w using compare and swap. */
-extern void
-js_AtomicSetMask(volatile intptr_t *w, intptr_t mask);
-
-/*
- * Atomically bitwise-and the complement of the mask into the word *w using
- * compare and swap.
- */
-extern void
-js_AtomicClearMask(volatile intptr_t *w, intptr_t mask);
-
-#define JS_ATOMIC_SET_MASK(w, mask) js_AtomicSetMask(w, mask)
-#define JS_ATOMIC_CLEAR_MASK(w, mask) js_AtomicClearMask(w, mask)
-
-extern unsigned
-js_GetCPUCount();
+# define JS_ATOMIC_INCREMENT(p) (++*(p))
+# define JS_ATOMIC_DECREMENT(p) (--*(p))
+# define JS_ATOMIC_ADD(p,v) (*(p) += (v))
+# define JS_ATOMIC_SET(p,v) (*(p) = (v))
-#else
-
-static inline JSBool
-js_CompareAndSwap(intptr_t *w, intptr_t ov, intptr_t nv)
-{
- return (*w == ov) ? *w = nv, JS_TRUE : JS_FALSE;
-}
-
-#define JS_ATOMIC_SET_MASK(w, mask) (*(w) |= (mask))
-#define JS_ATOMIC_CLEAR_MASK(w, mask) (*(w) &= ~(mask))
-
-static inline unsigned
-js_GetCPUCount()
-{
- return 1;
-}
-
-#endif
-
-#ifdef __cplusplus
+#endif /* JS_THREADSAFE */
namespace js {
-#ifdef JS_THREADSAFE
-class AutoLock {
- private:
- JSLock *lock;
-
- public:
- AutoLock(JSLock *lock) : lock(lock) { JS_ACQUIRE_LOCK(lock); }
- ~AutoLock() { JS_RELEASE_LOCK(lock); }
-};
-# define JS_AUTO_LOCK_GUARD(name, l) AutoLock name((l));
-#else
-# define JS_AUTO_LOCK_GUARD(name, l)
-#endif
-
-class AutoAtomicIncrement {
+class AutoAtomicIncrement
+{
int32_t *p;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
AutoAtomicIncrement(int32_t *p JS_GUARD_OBJECT_NOTIFIER_PARAM)
: p(p) {
JS_GUARD_OBJECT_NOTIFIER_INIT;
JS_ATOMIC_INCREMENT(p);
}
~AutoAtomicIncrement() {
JS_ATOMIC_DECREMENT(p);
}
};
-} /* namespace js */
-
-#endif
+} /* namespace js */
#endif /* jslock_h___ */
deleted file mode 100644
--- a/js/src/jslocko.asm
+++ /dev/null
@@ -1,60 +0,0 @@
-; -*- Mode: asm; tab-width: 8; c-basic-offset: 4 -*-
-
-; ***** BEGIN LICENSE BLOCK *****
-; Version: MPL 1.1/GPL 2.0/LGPL 2.1
-;
-; The contents of this file are subject to the Mozilla Public License Version
-; 1.1 (the "License"); you may not use this file except in compliance with
-; the License. You may obtain a copy of the License at
-; http://www.mozilla.org/MPL/
-;
-; Software distributed under the License is distributed on an "AS IS" basis,
-; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-; for the specific language governing rights and limitations under the
-; License.
-;
-; The Original Code is an OS/2 implementation of js_CompareAndSwap in assembly.
-;
-; The Initial Developer of the Original Code is
-; IBM Corporation.
-; Portions created by the Initial Developer are Copyright (C) 2001
-; the Initial Developer. All Rights Reserved.
-;
-; Contributor(s):
-;
-; Alternatively, the contents of this file may be used under the terms of
-; either the GNU General Public License Version 2 or later (the "GPL"), or
-; the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-; in which case the provisions of the GPL or the LGPL are applicable instead
-; of those above. If you wish to allow use of your version of this file only
-; under the terms of either the GPL or the LGPL, and not to allow others to
-; use your version of this file under the terms of the MPL, indicate your
-; decision by deleting the provisions above and replace them with the notice
-; and other provisions required by the GPL or the LGPL. If you do not delete
-; the provisions above, a recipient may use your version of this file under
-; the terms of any one of the MPL, the GPL or the LGPL.
-;
-; ***** END LICENSE BLOCK *****
-
- .486P
- .MODEL FLAT, OPTLINK
- .STACK
-
- .CODE
-
-;;;---------------------------------------------------------------------
-;;; int _Optlink js_CompareAndSwap(intptr_t *w, intptr_t ov, intptr_t nv)
-;;;---------------------------------------------------------------------
-js_CompareAndSwap PROC OPTLINK EXPORT
- push ebx
- mov ebx, eax
- mov eax, edx
- mov edx, ebx
- lock cmpxchg [ebx], ecx
- sete al
- and eax, 1h
- pop ebx
- ret
-js_CompareAndSwap endp
-
- END
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -103,17 +103,17 @@ ComputeAccurateDecimalInteger(JSContext
char c = char(start[i]);
JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
cstr[i] = c;
}
cstr[length] = 0;
char *estr;
int err = 0;
- *dp = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err);
+ *dp = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err);
if (err == JS_DTOA_ENOMEM) {
JS_ReportOutOfMemory(cx);
cx->free_(cstr);
return false;
}
if (err == JS_DTOA_ERANGE && *dp == HUGE_VAL)
*dp = js_PositiveInfinity;
cx->free_(cstr);
@@ -811,17 +811,17 @@ num_to(JSContext *cx, Native native, JSD
ToCStringBuf cbuf;
numStr = IntToCString(&cbuf, jsint(precision));
JS_ASSERT(numStr);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
return JS_FALSE;
}
}
- numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf,
+ numStr = js_dtostr(cx->runtime->dtoaState, buf, sizeof buf,
oneArgMode, (jsint)precision + precisionOffset, d);
if (!numStr) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
JSString *str = js_NewStringCopyZ(cx, numStr);
if (!str)
return JS_FALSE;
@@ -1082,20 +1082,20 @@ FracNumberToCString(JSContext *cx, ToCSt
* Printing floating-point numbers quickly and accurately with integers.
* Florian Loitsch, PLDI 2010.
*
* It fails on a small number of cases, whereupon we fall back to
* js_dtostr() (which uses David Gay's dtoa).
*/
numStr = v8::internal::DoubleToCString(d, cbuf->sbuf, cbuf->sbufSize);
if (!numStr)
- numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, cbuf->sbuf, cbuf->sbufSize,
+ numStr = js_dtostr(cx->runtime->dtoaState, cbuf->sbuf, cbuf->sbufSize,
DTOSTR_STANDARD, 0, d);
} else {
- numStr = cbuf->dbuf = js_dtobasestr(JS_THREAD_DATA(cx)->dtoaState, base, d);
+ numStr = cbuf->dbuf = js_dtobasestr(cx->runtime->dtoaState, base, d);
}
return numStr;
}
char *
NumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base/* = 10*/)
{
int32_t i;
@@ -1416,17 +1416,17 @@ js_strtod(JSContext *cx, const jschar *s
istr = cstr;
if ((negative = (*istr == '-')) != 0 || *istr == '+')
istr++;
if (*istr == 'I' && !strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) {
d = negative ? js_NegativeInfinity : js_PositiveInfinity;
estr = istr + 8;
} else {
int err;
- d = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err);
+ d = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err);
if (d == HUGE_VAL)
d = js_PositiveInfinity;
else if (d == -HUGE_VAL)
d = js_NegativeInfinity;
}
i = estr - cstr;
if (cstr != cbuf)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1514,31 +1514,16 @@ JSObject::getPrivateDataOffset(size_t nf
struct JSObject_Slots2 : JSObject { js::Value fslots[2]; };
struct JSObject_Slots4 : JSObject { js::Value fslots[4]; };
struct JSObject_Slots8 : JSObject { js::Value fslots[8]; };
struct JSObject_Slots12 : JSObject { js::Value fslots[12]; };
struct JSObject_Slots16 : JSObject { js::Value fslots[16]; };
#define JSSLOT_FREE(clasp) JSCLASS_RESERVED_SLOTS(clasp)
-#ifdef JS_THREADSAFE
-
-/*
- * The GC runs only when all threads except the one on which the GC is active
- * are suspended at GC-safe points, so calling obj->getSlot() from the GC's
- * thread is safe when rt->gcRunning is set. See jsgc.cpp for details.
- */
-#define THREAD_IS_RUNNING_GC(rt, thread) \
- ((rt)->gcRunning && (rt)->gcThread == (thread))
-
-#define CX_THREAD_IS_RUNNING_GC(cx) \
- THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread)
-
-#endif /* JS_THREADSAFE */
-
class JSValueArray {
public:
jsval *array;
size_t length;
JSValueArray(jsval *v, size_t c) : array(v), length(c) {}
};
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -457,17 +457,17 @@ ToDisassemblySource(JSContext *cx, jsval
return false;
nbytes = JS_sprintf_append(NULL, "%s", nbytes);
if (!nbytes)
return false;
bytes->initBytes(nbytes);
return true;
}
- if (cx->runtime->gcRunning || JS_THREAD_DATA(cx)->noGCOrAllocationCheck) {
+ if (cx->runtime->gcRunning || cx->runtime->noGCOrAllocationCheck) {
char *source = JS_sprintf_append(NULL, "<value>");
if (!source)
return false;
bytes->initBytes(source);
return true;
}
if (!JSVAL_IS_PRIMITIVE(v)) {
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -78,17 +78,17 @@ GetFunctionProxyConstruct(JSObject *prox
JS_ASSERT(IsFunctionProxy(proxy));
JS_ASSERT(proxy->slotSpan() > JSSLOT_PROXY_CONSTRUCT);
return proxy->getSlotRef(JSSLOT_PROXY_CONSTRUCT);
}
static bool
OperationInProgress(JSContext *cx, JSObject *proxy)
{
- PendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
+ PendingProxyOperation *op = cx->runtime->pendingProxyOperation;
while (op) {
if (op->object == proxy)
return true;
op = op->next;
}
return false;
}
@@ -705,28 +705,28 @@ ScriptedProxyHandler::iterate(JSContext
return ProxyHandler::iterate(cx, proxy, flags, vp);
return Trap(cx, handler, tvr.value(), 0, NULL, vp) &&
ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(iterate), *vp);
}
ScriptedProxyHandler ScriptedProxyHandler::singleton;
class AutoPendingProxyOperation {
- ThreadData *data;
+ JSRuntime *rt;
PendingProxyOperation op;
public:
- AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : data(JS_THREAD_DATA(cx)) {
- op.next = data->pendingProxyOperation;
+ AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : rt(cx->runtime) {
+ op.next = rt->pendingProxyOperation;
op.object = proxy;
- data->pendingProxyOperation = &op;
+ rt->pendingProxyOperation = &op;
}
~AutoPendingProxyOperation() {
- JS_ASSERT(data->pendingProxyOperation == &op);
- data->pendingProxyOperation = op.next;
+ JS_ASSERT(rt->pendingProxyOperation == &op);
+ rt->pendingProxyOperation = op.next;
}
};
bool
Proxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
PropertyDescriptor *desc)
{
JS_CHECK_RECURSION(cx, return false);
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -81,17 +81,16 @@ typedef uintptr_t jsatomid;
/* Struct typedefs. */
typedef struct JSArgumentFormatMap JSArgumentFormatMap;
typedef struct JSGCThing JSGCThing;
typedef struct JSGenerator JSGenerator;
typedef struct JSNativeEnumerator JSNativeEnumerator;
typedef struct JSProperty JSProperty;
typedef struct JSSharpObjectMap JSSharpObjectMap;
-typedef struct JSThread JSThread;
typedef struct JSTryNote JSTryNote;
/* Friend "Advanced API" typedefs. */
typedef struct JSAtomState JSAtomState;
typedef struct JSCodeSpec JSCodeSpec;
typedef struct JSPrinter JSPrinter;
typedef struct JSStackHeader JSStackHeader;
typedef struct JSSubString JSSubString;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -776,17 +776,17 @@ JSScript::initCounts(JSContext *cx)
cursor += capacity * sizeof(double);
next = pc + GetBytecodeLength(pc);
}
JS_ASSERT(size_t(cursor - base) == bytes);
/* Enable interrupts in any interpreter frames running on this script. */
InterpreterFrames *frames;
- for (frames = JS_THREAD_DATA(cx)->interpreterFrames; frames; frames = frames->older)
+ for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
frames->enableInterruptsIfRunning(this);
return true;
}
void
JSScript::destroyCounts(JSContext *cx)
{
@@ -1730,17 +1730,17 @@ JSScript::ensureHasDebug(JSContext *cx)
return false;
/*
* Ensure that any Interpret() instances running on this script have
* interrupts enabled. The interrupts must stay enabled until the
* debug state is destroyed.
*/
InterpreterFrames *frames;
- for (frames = JS_THREAD_DATA(cx)->interpreterFrames; frames; frames = frames->older)
+ for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
frames->enableInterruptsIfRunning(this);
return true;
}
bool
JSScript::recompileForStepMode(JSContext *cx)
{
deleted file mode 100644
--- a/js/src/lock_sparcv8plus.il
+++ /dev/null
@@ -1,84 +0,0 @@
-!
-! ***** BEGIN LICENSE BLOCK *****
-! Version: MPL 1.1/GPL 2.0/LGPL 2.1
-!
-! The contents of this file are subject to the Mozilla Public License Version
-! 1.1 (the "License"); you may not use this file except in compliance with
-! the License. You may obtain a copy of the License at
-! http://www.mozilla.org/MPL/
-!
-! Software distributed under the License is distributed on an "AS IS" basis,
-! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-! for the specific language governing rights and limitations under the
-! License.
-!
-! The Original Code is Mozilla Communicator client code, released
-! March 31, 1998.
-!
-! The Initial Developer of the Original Code is
-! Netscape Communications Corporation.
-! Portions created by the Initial Developer are Copyright (C) 1998-1999
-! the Initial Developer. All Rights Reserved.
-!
-! Contributor(s):
-!
-! Alternatively, the contents of this file may be used under the terms of
-! either the GNU General Public License Version 2 or later (the "GPL"), or
-! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-! in which case the provisions of the GPL or the LGPL are applicable instead
-! of those above. If you wish to allow use of your version of this file only
-! under the terms of either the GPL or the LGPL, and not to allow others to
-! use your version of this file under the terms of the MPL, indicate your
-! decision by deleting the provisions above and replace them with the notice
-! and other provisions required by the GPL or the LGPL. If you do not delete
-! the provisions above, a recipient may use your version of this file under
-! the terms of any one of the MPL, the GPL or the LGPL.
-!
-! ***** END LICENSE BLOCK *****
-
-!
-! atomic compare-and-swap routines for V8+ (ultrasparc)
-!
-! ======================================================================
-!
-! Perform the sequence *a = b atomically with respect to previous value
-! of a (a0). If *a==a0 then assign *a to b, all in one atomic operation.
-! Returns 1 if assignment happened, and 0 otherwise.
-!
-! usage : old_val = compare_and_swap(address, oldval, newval)
-!
-! -----------------------
-! Note on REGISTER USAGE:
-! as this is a LEAF procedure, a new stack frame is not created;
-! we use the caller stack frame so what would normally be %i (input)
-! registers are actually %o (output registers). Also, we must not
-! overwrite the contents of %l (local) registers as they are not
-! assumed to be volatile during calls.
-!
-! So, the registers used are:
-! %o0 [input] - the address of the value to increment
-! %o1 [input] - the old value to compare with
-! %o2 [input] - the new value to set for [%o0]
-! %o3 [local] - work register
-! -----------------------
-! ======================================================================
-!
-! v8plus
-
- .inline NativeCompareAndSwap,3
-
- stbar
- cas [%o0],%o1,%o2 ! compare *w with old value and set to new if equal
- cmp %o1,%o2 ! did we succeed?
- be,a 1f ! yes
- mov 1,%o0 ! return true (annulled when no jump)
- mov 0,%o0 ! return false
-1:
-
- .end
-
-!
-! end
-!
-! ======================================================================
-!
deleted file mode 100644
--- a/js/src/lock_sparcv9.il
+++ /dev/null
@@ -1,84 +0,0 @@
-!
-! ***** BEGIN LICENSE BLOCK *****
-! Version: MPL 1.1/GPL 2.0/LGPL 2.1
-!
-! The contents of this file are subject to the Mozilla Public License Version
-! 1.1 (the "License"); you may not use this file except in compliance with
-! the License. You may obtain a copy of the License at
-! http://www.mozilla.org/MPL/
-!
-! Software distributed under the License is distributed on an "AS IS" basis,
-! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-! for the specific language governing rights and limitations under the
-! License.
-!
-! The Original Code is Mozilla Communicator client code, released
-! March 31, 1998.
-!
-! The Initial Developer of the Original Code is
-! Netscape Communications Corporation.
-! Portions created by the Initial Developer are Copyright (C) 1998-1999
-! the Initial Developer. All Rights Reserved.
-!
-! Contributor(s):
-!
-! Alternatively, the contents of this file may be used under the terms of
-! either the GNU General Public License Version 2 or later (the "GPL"), or
-! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-! in which case the provisions of the GPL or the LGPL are applicable instead
-! of those above. If you wish to allow use of your version of this file only
-! under the terms of either the GPL or the LGPL, and not to allow others to
-! use your version of this file under the terms of the MPL, indicate your
-! decision by deleting the provisions above and replace them with the notice
-! and other provisions required by the GPL or the LGPL. If you do not delete
-! the provisions above, a recipient may use your version of this file under
-! the terms of any one of the MPL, the GPL or the LGPL.
-!
-! ***** END LICENSE BLOCK *****
-
-!
-! atomic compare-and-swap routines for V9 (ultrasparc)
-!
-! ======================================================================
-!
-! Perform the sequence *a = b atomically with respect to previous value
-! of a (a0). If *a==a0 then assign *a to b, all in one atomic operation.
-! Returns 1 if assignment happened, and 0 otherwise.
-!
-! usage : old_val = compare_and_swap(address, oldval, newval)
-!
-! -----------------------
-! Note on REGISTER USAGE:
-! as this is a LEAF procedure, a new stack frame is not created;
-! we use the caller stack frame so what would normally be %i (input)
-! registers are actually %o (output registers). Also, we must not
-! overwrite the contents of %l (local) registers as they are not
-! assumed to be volatile during calls.
-!
-! So, the registers used are:
-! %o0 [input] - the address of the value to increment
-! %o1 [input] - the old value to compare with
-! %o2 [input] - the new value to set for [%o0]
-! %o3 [local] - work register
-! -----------------------
-! ======================================================================
-!
-! v9
-
- .inline NativeCompareAndSwap,3
-
- stbar
- casx [%o0],%o1,%o2 ! compare *w with old value and set to new if equal
- cmp %o1,%o2 ! did we succeed?
- be,a 1f ! yes
- mov 1,%o0 ! return true (annulled when no jump)
- mov 0,%o0 ! return false
-1:
-
- .end
-
-!
-! end
-!
-! ======================================================================
-!
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -3928,29 +3928,17 @@ mjit::Compiler::emitStubCall(void *ptr,
void
mjit::Compiler::interruptCheckHelper()
{
Jump jump;
if (cx->runtime->gcZeal() >= js::gc::ZealVerifierThreshold) {
/* For barrier verification, always take the interrupt so we can verify. */
jump = masm.jump();
} else {
- /*
- * Bake in and test the address of the interrupt counter for the runtime.
- * This is faster than doing two additional loads for the context's
- * thread data, but will cause this thread to run slower if there are
- * pending interrupts on some other thread. For non-JS_THREADSAFE builds
- * we can skip this, as there is only one flag to poll.
- */
-#ifdef JS_THREADSAFE
- void *interrupt = (void*) &cx->runtime->interruptCounter;
-#else
- void *interrupt = (void*) &JS_THREAD_DATA(cx)->interruptFlags;
-#endif
-
+ void *interrupt = (void*) &cx->runtime->interrupt;
#if defined(JS_CPU_X86) || defined(JS_CPU_ARM) || defined(JS_CPU_MIPS)
jump = masm.branch32(Assembler::NotEqual, AbsoluteAddress(interrupt), Imm32(0));
#else
/* Handle processors that can't load from absolute addresses. */
RegisterID reg = frame.allocReg();
masm.move(ImmPtr(interrupt), reg);
jump = masm.branchTest32(Assembler::NonZero, Address(reg, 0));
frame.freeReg(reg);
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1889,17 +1889,17 @@ stubs::InvariantFailure(VMFrame &f, void
return rval;
}
void JS_FASTCALL
stubs::Exception(VMFrame &f)
{
// Check the interrupt flag to allow interrupting deeply nested exception
// handling.
- if (JS_THREAD_DATA(f.cx)->interruptFlags && !js_HandleExecutionInterrupt(f.cx))
+ if (f.cx->runtime->interrupt && !js_HandleExecutionInterrupt(f.cx))
THROW();
f.regs.sp[0] = f.cx->getPendingException();
f.cx->clearPendingException();
}
void JS_FASTCALL
stubs::FunctionFramePrologue(VMFrame &f)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3450,17 +3450,17 @@ CancelExecution(JSRuntime *rt)
{
gCanceled = true;
if (gExitCode == 0)
gExitCode = EXITCODE_TIMEOUT;
#ifdef JS_THREADSAFE
if (gWorkerThreadPool)
js::workers::terminateAll(gWorkerThreadPool);
#endif
- JS_TriggerAllOperationCallbacks(rt);
+ JS_TriggerRuntimeOperationCallback(rt);
static const char msg[] = "Script runs for too long, terminating.\n";
#if defined(XP_UNIX) && !defined(JS_THREADSAFE)
/* It is not safe to call fputs from signals. */
/* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
ssize_t dummy = write(2, msg, sizeof(msg) - 1);
(void)dummy;
#else
--- a/js/src/shell/jsworkers.cpp
+++ b/js/src/shell/jsworkers.cpp
@@ -50,16 +50,26 @@
#include "jscntxt.h"
#include "jsdbgapi.h"
#include "jsstdint.h"
#include "jslock.h"
#include "jsworkers.h"
extern size_t gMaxStackSize;
+class AutoLock
+{
+ private:
+ PRLock *lock;
+
+ public:
+ AutoLock(PRLock *lock) : lock(lock) { PR_Lock(lock); }
+ ~AutoLock() { PR_Unlock(lock); }
+};
+
/*
* JavaScript shell workers.
*
* == Object lifetime rules ==
*
* - The ThreadPool lasts from init() to finish().
*
* - The ThreadPool owns the MainQueue and the WorkerQueue. Those live from
@@ -145,17 +155,17 @@ class Worker;
class WorkerParent {
protected:
typedef HashSet<Worker *, DefaultHasher<Worker *>, SystemAllocPolicy> ChildSet;
ChildSet children;
bool initWorkerParent() { return children.init(8); }
public:
- virtual JSLock *getLock() = 0;
+ virtual PRLock *getLock() = 0;
virtual ThreadPool *getThreadPool() = 0;
virtual bool post(Event *item) = 0; // false on OOM or queue closed
virtual void trace(JSTracer *trc) = 0;
bool addChild(Worker *w) {
AutoLock hold(getLock());
return children.put(w) != NULL;
}
@@ -172,65 +182,65 @@ class WorkerParent {
void notifyTerminating();
};
template <class T>
class ThreadSafeQueue
{
protected:
Queue<T, SystemAllocPolicy> queue;
- JSLock *lock;
+ PRLock *lock;
PRCondVar *condvar;
bool closed;
private:
Vector<T, 8, SystemAllocPolicy> busy;
protected:
ThreadSafeQueue() : lock(NULL), condvar(NULL), closed(false) {}
~ThreadSafeQueue() {
if (condvar)
- JS_DESTROY_CONDVAR(condvar);
+ PR_DestroyCondVar(condvar);
if (lock)
- JS_DESTROY_LOCK(lock);
+ PR_DestroyLock(lock);
}
// Called by take() with the lock held.
virtual bool shouldStop() { return closed; }
public:
bool initThreadSafeQueue() {
JS_ASSERT(!lock);
JS_ASSERT(!condvar);
- return (lock = JS_NEW_LOCK()) && (condvar = JS_NEW_CONDVAR(lock));
+ return (lock = PR_NewLock()) && (condvar = PR_NewCondVar(lock));
}
bool post(T t) {
AutoLock hold(lock);
if (closed)
return false;
if (queue.empty())
- JS_NOTIFY_ALL_CONDVAR(condvar);
+ PR_NotifyAllCondVar(condvar);
return queue.push(t);
}
void close() {
AutoLock hold(lock);
closed = true;
queue.clear();
- JS_NOTIFY_ALL_CONDVAR(condvar);
+ PR_NotifyAllCondVar(condvar);
}
// The caller must hold the lock.
bool take(T *t) {
while (queue.empty()) {
if (shouldStop())
return false;
- JS_WAIT_CONDVAR(condvar, JS_NO_TIMEOUT);
+ PR_WaitCondVar(condvar, PR_INTERVAL_NO_TIMEOUT);
}
*t = queue.pop();
busy.append(*t);
return true;
}
// The caller must hold the lock.
void drop(T item) {
@@ -248,17 +258,17 @@ class ThreadSafeQueue
bool isIdle() {
AutoLock hold(lock);
return lockedIsIdle();
}
void wake() {
AutoLock hold(lock);
- JS_NOTIFY_ALL_CONDVAR(condvar);
+ PR_NotifyAllCondVar(condvar);
}
void trace(JSTracer *trc) {
AutoLock hold(lock);
for (T *p = busy.begin(); p != busy.end(); p++)
(*p)->trace(trc);
queue.trace(trc);
}
@@ -366,17 +376,17 @@ class MainQueue MOZ_FINAL : public Event
bool init() { return initThreadSafeQueue() && initWorkerParent(); }
void destroy(JSContext *cx) {
while (!queue.empty())
queue.pop()->destroy(cx);
delete this;
}
- virtual JSLock *getLock() { return lock; }
+ virtual PRLock *getLock() { return lock; }
virtual ThreadPool *getThreadPool() { return threadPool; }
protected:
virtual bool shouldStop();
public:
virtual bool post(Event *event) { return EventQueue::post(event); }
@@ -385,17 +395,17 @@ class MainQueue MOZ_FINAL : public Event
void traceChildren(JSTracer *trc) { EventQueue::trace(trc); }
JSBool mainThreadWork(JSContext *cx, bool continueOnError) {
JSAutoSuspendRequest suspend(cx);
AutoLock hold(lock);
Event *event;
while (take(&event)) {
- JS_RELEASE_LOCK(lock);
+ PR_Unlock(lock);
Event::Result result;
{
JSAutoRequest req(cx);
result = event->process(cx);
if (result == Event::forwardToParent) {
// FIXME - pointlessly truncates the string to 8 bits
jsval data;
JSAutoByteString bytes;
@@ -409,17 +419,17 @@ class MainQueue MOZ_FINAL : public Event
result = Event::fail;
}
if (result == Event::fail && continueOnError) {
if (JS_IsExceptionPending(cx) && !JS_ReportPendingException(cx))
JS_ClearPendingException(cx);
result = Event::ok;
}
}
- JS_ACQUIRE_LOCK(lock);
+ PR_Lock(lock);
drop(event);
event->destroy(cx);
if (result != Event::ok)
return false;
}
return true;
}
};
@@ -591,17 +601,17 @@ class ThreadPool
class Worker MOZ_FINAL : public WorkerParent
{
private:
ThreadPool *threadPool;
WorkerParent *parent;
JSObject *object; // Worker object exposed to parent
JSRuntime *runtime;
JSContext *context;
- JSLock *lock;
+ PRLock *lock;
Queue<Event *, SystemAllocPolicy> events; // owning pointers to pending events
Event *current;
bool terminated;
int32_t terminateFlag;
static JSClass jsWorkerClass;
Worker()
@@ -611,17 +621,17 @@ class Worker MOZ_FINAL : public WorkerPa
bool init(JSContext *parentcx, WorkerParent *parent, JSObject *obj) {
JS_ASSERT(!threadPool && !this->parent && !object && !lock);
if (!initWorkerParent() || !parent->addChild(this))
return false;
threadPool = parent->getThreadPool();
this->parent = parent;
this->object = obj;
- lock = JS_NEW_LOCK();
+ lock = PR_NewLock();
return lock &&
createRuntime(parentcx) &&
createContext(parentcx, parent) &&
JS_SetPrivate(parentcx, obj, this);
}
bool createRuntime(JSContext *parentcx) {
runtime = JS_NewRuntime(1L * 1024L * 1024L);
@@ -676,17 +686,16 @@ class Worker MOZ_FINAL : public WorkerPa
ctor = JS_GetConstructor(context, proto);
if (!ctor)
goto bad;
js::SetFunctionNativeReserved(post, 0, PRIVATE_TO_JSVAL(this));
JS_EndRequest(context);
- JS_ClearContextThread(context);
return true;
bad:
JS_EndRequest(context);
JS_DestroyContext(context);
context = NULL;
return false;
}
@@ -764,23 +773,22 @@ class Worker MOZ_FINAL : public WorkerPa
dispose();
}
void dispose() {
JS_ASSERT(!current);
while (!events.empty())
events.pop()->destroy(context);
if (lock) {
- JS_DESTROY_LOCK(lock);
+ PR_DestroyLock(lock);
lock = NULL;
}
if (runtime)
JS_SetRuntimeThread(runtime);
if (context) {
- JS_SetContextThread(context);
JS_DestroyContextNoGC(context);
context = NULL;
}
if (runtime) {
JS_DestroyRuntime(runtime);
runtime = NULL;
}
object = NULL;
@@ -796,17 +804,17 @@ class Worker MOZ_FINAL : public WorkerPa
JSString *scriptName, JSObject *obj);
JSObject *asObject() { return object; }
JSObject *getGlobal() { return JS_GetGlobalObject(context); }
WorkerParent *getParent() { return parent; }
- virtual JSLock *getLock() { return lock; }
+ virtual PRLock *getLock() { return lock; }
virtual ThreadPool *getThreadPool() { return threadPool; }
bool post(Event *event) {
AutoLock hold(lock);
if (terminated)
return false;
if (!current && events.empty() && !threadPool->getWorkerQueue()->post(this))
@@ -1025,25 +1033,25 @@ MainQueue::trace(JSTracer *trc)
}
void
WorkerQueue::work() {
AutoLock hold(lock);
Worker *w;
while (take(&w)) { // can block outside the mutex
- JS_RELEASE_LOCK(lock);
+ PR_Unlock(lock);
w->processOneEvent(); // enters request on w->context
- JS_ACQUIRE_LOCK(lock);
+ PR_Lock(lock);
drop(w);
if (lockedIsIdle()) {
- JS_RELEASE_LOCK(lock);
+ PR_Unlock(lock);
main->wake();
- JS_ACQUIRE_LOCK(lock);
+ PR_Lock(lock);
}
}
}
const bool mswin =
#ifdef XP_WIN
true
#else
@@ -1131,18 +1139,16 @@ Worker::processOneEvent()
AutoLock hold1(lock);
if (lockedCheckTermination() || events.empty())
return;
event = current = events.pop();
}
JS_SetRuntimeThread(runtime);
- JS_SetContextThread(context);
- JS_SetNativeStackQuota(context, gMaxStackSize);
Event::Result result;
{
JSAutoRequest ar(context);
result = event->process(context);
}
// Note: we have to leave the above request before calling parent->post or
@@ -1166,17 +1172,16 @@ Worker::processOneEvent()
}
if (!err) {
// FIXME - out of memory, probably should panic
}
}
if (event)
event->destroy(context);
- JS_ClearContextThread(context);
JS_ClearRuntimeThread(runtime);
{
AutoLock hold2(lock);
current = NULL;
if (!lockedCheckTermination() && !events.empty()) {
// Re-enqueue this worker. OOM here effectively kills the worker.
if (!threadPool->getWorkerQueue()->post(this))
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -258,32 +258,22 @@ RegExpObject::setMultiline(bool enabled)
inline void
RegExpObject::setSticky(bool enabled)
{
setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
}
/* RegExpPrivate inlines. */
-inline RegExpPrivateCache *
-detail::RegExpPrivate::getOrCreateCache(JSContext *cx)
-{
- if (RegExpPrivateCache *cache = cx->threadData()->getOrCreateRegExpPrivateCache(cx))
- return cache;
-
- js_ReportOutOfMemory(cx);
- return NULL;
-}
-
inline bool
detail::RegExpPrivate::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
RegExpPrivateCacheKind targetKind,
AlreadyIncRefed<RegExpPrivate> *result)
{
- RegExpPrivateCache *cache = getOrCreateCache(cx);
+ RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx);
if (!cache)
return false;
if (RegExpPrivateCache::Ptr p = cache->lookup(atom)) {
RegExpPrivateCacheValue &cacheValue = p->value;
if (cacheValue.kind() == targetKind && cacheValue.rep()->getFlags() == flags) {
NeedsIncRef<RegExpPrivate> cached(cacheValue.rep());
cached->incref(cx);
@@ -302,17 +292,17 @@ detail::RegExpPrivate::cacheInsert(JSCon
{
JS_ASSERT(priv);
/*
* Note: allocation performed since lookup may cause a garbage collection,
* so we have to re-lookup the cache (and inside the cache) after the
* allocation is performed.
*/
- RegExpPrivateCache *cache = getOrCreateCache(cx);
+ RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx);
if (!cache)
return false;
if (RegExpPrivateCache::AddPtr addPtr = cache->lookupForAdd(atom)) {
/* We clobber existing entries with the same source (but different flags or kind). */
JS_ASSERT(addPtr->value.rep()->getFlags() != priv->getFlags() ||
addPtr->value.kind() != kind);
addPtr->value.reset(priv, kind);
@@ -396,30 +386,30 @@ detail::RegExpPrivateCode::compile(JSCon
/*
* The YARR JIT compiler attempts to compile the parsed pattern. If
* it cannot, it informs us via |codeBlock.isFallBack()|, in which
* case we have to bytecode compile it.
*/
#ifdef JS_METHODJIT
if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
- JSC::ExecutableAllocator *execAlloc = cx->threadData()->getOrCreateExecutableAllocator(cx);
+ JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecutableAllocator(cx);
if (!execAlloc) {
js_ReportOutOfMemory(cx);
return false;
}
JSGlobalData globalData(execAlloc);
jitCompile(yarrPattern, &globalData, codeBlock);
if (!codeBlock.isFallBack())
return true;
}
#endif
- WTF::BumpPointerAllocator *bumpAlloc = cx->threadData()->getOrCreateBumpPointerAllocator(cx);
+ WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx);
if (!bumpAlloc) {
js_ReportOutOfMemory(cx);
return false;
}
codeBlock.setFallBack(true);
byteCode = byteCompile(yarrPattern, bumpAlloc).get();
return true;
@@ -505,21 +495,23 @@ detail::RegExpPrivate::decref(JSContext
#ifdef JS_THREADSAFE
JS_OPT_ASSERT_IF(cx->runtime->gcHelperThread.getThread(),
PR_GetCurrentThread() != cx->runtime->gcHelperThread.getThread());
#endif
if (--refCount != 0)
return;
- RegExpPrivateCache *cache;
- if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) {
- RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom());
- if (ptr && ptr->value.rep() == this)
- cache->remove(ptr);
+ if (RegExpPrivateCache *cache = cx->runtime->maybeRegExpPrivateCache()) {
+ if (source->isAtom()) {
+ if (RegExpPrivateCache::Ptr p = cache->lookup(&source->asAtom())) {
+ if (p->value.rep() == this)
+ cache->remove(p);
+ }
+ }
}
#ifdef DEBUG
this->~RegExpPrivate();
memset(this, 0xcd, sizeof(*this));
cx->free_(this);
#else
cx->delete_(this);
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -383,17 +383,16 @@ class RegExpPrivate
bool compile(JSContext *cx, TokenStream *ts);
static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
static RegExpPrivate *
createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
TokenStream *tokenStream);
- static RegExpPrivateCache *getOrCreateCache(JSContext *cx);
static bool cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
RegExpPrivateCacheKind kind, AlreadyIncRefed<RegExpPrivate> *result);
static bool cacheInsert(JSContext *cx, JSAtom *atom,
RegExpPrivateCacheKind kind, RegExpPrivate *priv);
public:
static AlreadyIncRefed<RegExpPrivate>
create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts);
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -487,49 +487,25 @@ StackSpace::sizeOfCommitted()
return (trustedEnd_ - base_) * sizeof(Value);
#endif
}
/*****************************************************************************/
ContextStack::ContextStack(JSContext *cx)
: seg_(NULL),
- space_(&JS_THREAD_DATA(cx)->stackSpace),
+ space_(&cx->runtime->stackSpace),
cx_(cx)
-{
- threadReset();
-}
+{}
ContextStack::~ContextStack()
{
JS_ASSERT(!seg_);
}
-void
-ContextStack::threadReset()
-{
-#ifdef JS_THREADSAFE
- if (cx_->thread())
- space_ = &JS_THREAD_DATA(cx_)->stackSpace;
- else
- space_ = NULL;
-#else
- space_ = &JS_THREAD_DATA(cx_)->stackSpace;
-#endif
-}
-
-#ifdef DEBUG
-void
-ContextStack::assertSpaceInSync() const
-{
- JS_ASSERT(space_);
- JS_ASSERT(space_ == &JS_THREAD_DATA(cx_)->stackSpace);
-}
-#endif
-
bool
ContextStack::onTop() const
{
return seg_ && seg_ == space().seg_;
}
bool
ContextStack::containsSlow(const StackFrame *target) const
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -312,17 +312,16 @@ public:
~JSCLContextHelper();
void reportErrorAfterPop(char *buf);
operator JSContext*() const {return mContext;}
private:
JSContext* mContext;
- intN mContextThread;
nsIThreadJSContextStack* mContextStack;
char* mBuf;
// prevent copying and assignment
JSCLContextHelper(const JSCLContextHelper &) MOZ_DELETE;
const JSCLContextHelper& operator=(const JSCLContextHelper &) MOZ_DELETE;
};
@@ -1350,33 +1349,28 @@ mozJSComponentLoader::ModuleEntry::GetFa
return NULL;
return f.forget();
}
//----------------------------------------------------------------------
JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
- : mContext(loader->mContext), mContextThread(0),
+ : mContext(loader->mContext),
mContextStack(loader->mContextStack),
mBuf(nsnull)
{
mContextStack->Push(mContext);
- mContextThread = JS_GetContextThread(mContext);
- if (mContextThread) {
- JS_BeginRequest(mContext);
- }
+ JS_BeginRequest(mContext);
}
JSCLContextHelper::~JSCLContextHelper()
{
if (mContextStack) {
- if (mContextThread) {
- JS_EndRequest(mContext);
- }
+ JS_EndRequest(mContext);
mContextStack->Pop(nsnull);
JSContext* cx = nsnull;
mContextStack->Peek(&cx);
mContextStack = nsnull;
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -941,17 +941,17 @@ XPCJSRuntime::WatchdogMain(void *arg)
sleepInterval = PR_INTERVAL_NO_TIMEOUT;
self->mWatchdogHibernating = true;
}
#ifdef DEBUG
PRStatus status =
#endif
PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval);
JS_ASSERT(status == PR_SUCCESS);
- js::TriggerOperationCallbacksForActiveContexts(self->mJSRuntime);
+ js::TriggerOperationCallback(self->mJSRuntime);
}
/* Wake up the main thread waiting for the watchdog to terminate. */
PR_NotifyCondVar(self->mWatchdogWakeup);
}
//static
void
@@ -1038,17 +1038,16 @@ void XPCJSRuntime::SystemIsBeingShutDown
JSContext *
XPCJSRuntime::GetJSCycleCollectionContext()
{
if (!mJSCycleCollectionContext) {
mJSCycleCollectionContext = JS_NewContext(mJSRuntime, 0);
if (!mJSCycleCollectionContext)
return nsnull;
- JS_ClearContextThread(mJSCycleCollectionContext);
}
return mJSCycleCollectionContext;
}
XPCJSRuntime::~XPCJSRuntime()
{
if (mWatchdogWakeup) {
// If the watchdog thread is running, tell it to terminate waking it
@@ -1062,20 +1061,18 @@ XPCJSRuntime::~XPCJSRuntime()
PR_NotifyCondVar(mWatchdogWakeup);
PR_WaitCondVar(mWatchdogWakeup, PR_INTERVAL_NO_TIMEOUT);
}
}
PR_DestroyCondVar(mWatchdogWakeup);
mWatchdogWakeup = nsnull;
}
- if (mJSCycleCollectionContext) {
- JS_SetContextThread(mJSCycleCollectionContext);
+ if (mJSCycleCollectionContext)
JS_DestroyContextNoGC(mJSCycleCollectionContext);
- }
#ifdef XPC_DUMP_AT_SHUTDOWN
{
// count the total JSContexts in use
JSContext* iter = nsnull;
int count = 0;
while (JS_ContextIterator(mJSRuntime, &iter))
count ++;
@@ -1634,40 +1631,40 @@ ReportJSRuntimeStats(const JS::IterateDa
callback, closure);
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"),
nsIMemoryReporter::KIND_HEAP, data.runtimeContexts,
"Memory used by JSContext objects and certain structures "
"hanging off them." SLOP_BYTES_STRING,
callback, closure);
- ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/normal"),
- nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsNormal,
- "Memory used by JSThread objects and their data, "
+ ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/normal"),
+ nsIMemoryReporter::KIND_HEAP, data.runtimeNormal,
+ "Memory used by a JSRuntime, "
"excluding memory that is reported by "
"other reporters under 'explicit/js/runtime/'." SLOP_BYTES_STRING,
callback, closure);
- ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/temporary"),
- nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsTemporary,
- "Memory held transiently in JSThreads and used during "
+ ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"),
+ nsIMemoryReporter::KIND_HEAP, data.runtimeTemporary,
+ "Memory held transiently in JSRuntime and used during "
"compilation. It mostly holds parse nodes."
SLOP_BYTES_STRING,
callback, closure);
- ReportMemoryBytes0(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/regexp-code"),
- nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsRegexpCode,
+ ReportMemoryBytes0(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"),
+ nsIMemoryReporter::KIND_NONHEAP, data.runtimeRegexpCode,
"Memory used by the regexp JIT to hold generated code.",
callback, closure);
- ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/stack-committed"),
- nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsStackCommitted,
- "Memory used for the thread stacks. This is the committed portions "
- "of the stacks; any uncommitted portions are not measured because they "
- "hardly cost anything.",
+ ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"),
+ nsIMemoryReporter::KIND_NONHEAP, data.runtimeStackCommitted,
+ "Memory used for the JS call stack. This is the committed portion "
+ "of the stack; the uncommitted portion is not measured because it "
+ "hardly costs anything.",
callback, closure);
ReportGCHeapBytes(pathPrefix +
NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
&gcTotal, data.gcHeapChunkDirtyUnused,
"Memory on the garbage-collected JavaScript heap, within chunks with at "
"least one allocated GC thing, that could be holding useful data but "
"currently isn't. Memory here is mutually exclusive with memory reported"
@@ -1960,19 +1957,19 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect*
xpc::WrapperFactory::Rewrap,
xpc::WrapperFactory::PrepareForWrapping);
js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper);
#ifdef MOZ_CRASHREPORTER
JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
#endif
JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
- mWatchdogWakeup = JS_NEW_CONDVAR(js::GetRuntimeGCLock(mJSRuntime));
+ mWatchdogWakeup = PR_NewCondVar(js::GetRuntimeGCLock(mJSRuntime));
if (!mWatchdogWakeup)
- NS_RUNTIMEABORT("JS_NEW_CONDVAR failed.");
+ NS_RUNTIMEABORT("PR_NewCondVar failed.");
js::SetActivityCallback(mJSRuntime, ActivityCallback, this);
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount));
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount));
NS_RegisterMemoryMultiReporter(new XPConnectJSCompartmentsMultiReporter);
}
--- a/js/xpconnect/src/XPCThreadContext.cpp
+++ b/js/xpconnect/src/XPCThreadContext.cpp
@@ -50,17 +50,16 @@
using namespace mozilla;
/***************************************************************************/
XPCJSContextStack::~XPCJSContextStack()
{
if (mOwnSafeJSContext) {
- JS_SetContextThread(mOwnSafeJSContext);
JS_DestroyContext(mOwnSafeJSContext);
mOwnSafeJSContext = nsnull;
}
}
JSContext*
XPCJSContextStack::Pop()
{
@@ -104,17 +103,16 @@ GetPrincipalFromCx(JSContext *cx)
return globalData->GetPrincipal();
}
return nsnull;
}
bool
XPCJSContextStack::Push(JSContext *cx)
{
- MOZ_ASSERT_IF(cx, JS_GetContextThread(cx));
if (mStack.Length() == 0) {
mStack.AppendElement(cx);
return true;
}
XPCJSContextInfo &e = mStack[mStack.Length() - 1];
if (e.cx) {
if (e.cx == cx) {
@@ -418,17 +416,17 @@ XPCPerThreadData::GetDataImpl(JSContext
if (PR_FAILURE == PR_SetThreadPrivate(gTLSIndex, data)) {
NS_ERROR("PR_SetThreadPrivate failed!");
delete data;
return nsnull;
}
}
if (cx && !sMainJSThread && NS_IsMainThread()) {
- sMainJSThread = js::GetContextThread(cx);
+ sMainJSThread = js::GetOwnerThread(cx);
sMainThreadData = data;
sMainThreadData->mThread = PR_GetCurrentThread();
}
return data;
}
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -63,20 +63,18 @@ bool AutoScriptEvaluate::StartEvaluating
if (!mJSContext)
return true;
mEvaluated = true;
if (!JS_GetErrorReporter(mJSContext)) {
JS_SetErrorReporter(mJSContext, errorReporter);
mErrorReporterSet = true;
}
- mContextHasThread = JS_GetContextThread(mJSContext);
- if (mContextHasThread)
- JS_BeginRequest(mJSContext);
+ JS_BeginRequest(mJSContext);
if (!mEnterCompartment.enter(mJSContext, scope))
return false;
// Saving the exception state keeps us from interfering with another script
// that may also be running on this context. This occurred first with the
// js debugger, as described in
// http://bugzilla.mozilla.org/show_bug.cgi?id=88130 but presumably could
// show up in any situation where a script calls into a wrapped js component
@@ -97,18 +95,17 @@ AutoScriptEvaluate::~AutoScriptEvaluate(
{
if (!mJSContext || !mEvaluated)
return;
if (mState)
JS_RestoreExceptionState(mJSContext, mState);
else
JS_ClearPendingException(mJSContext);
- if (mContextHasThread)
- JS_EndRequest(mJSContext);
+ JS_EndRequest(mJSContext);
// If this is a JSContext that has a private context that provides a
// nsIXPCScriptNotify interface, then notify the object the script has
// been executed.
//
// Note: We rely on the rule that if any JSContext in our JSRuntime has
// private data that points to an nsISupports subclass, it has also set
// the JSOPTION_PRIVATE_IS_NSISUPPORTS option.
@@ -538,18 +535,18 @@ GetContextFromObject(JSObject *obj)
if (!ac.enter(ccx, obj))
return nsnull;
XPCWrappedNativeScope* scope =
XPCWrappedNativeScope::FindInJSObjectScope(ccx, obj);
XPCContext *xpcc = scope->GetContext();
if (xpcc) {
JSContext *cx = xpcc->GetJSContext();
- if (JS_GetContextThread(cx) == JS_GetCurrentThread())
- return cx;
+ JS_AbortIfWrongThread(JS_GetRuntime(cx));
+ return cx;
}
return nsnull;
}
class SameOriginCheckedComponent : public nsISecurityCheckedComponent
{
public:
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -549,24 +549,20 @@ nsXPConnect::BeginCycleCollection(nsCycl
// It is important not to call GetSafeJSContext while on the
// cycle-collector thread since this context will be destroyed
// asynchronously and race with the main thread. In particular, we must
// ensure that a context is passed to the XPCCallContext constructor.
JSContext *cx = mRuntime->GetJSCycleCollectionContext();
if (!cx)
return NS_ERROR_OUT_OF_MEMORY;
- // Clear after mCycleCollectionContext is destroyed
- JS_SetContextThread(cx);
-
NS_ASSERTION(!mCycleCollectionContext, "Didn't call FinishTraverse?");
mCycleCollectionContext = new XPCCallContext(NATIVE_CALLER, cx);
if (!mCycleCollectionContext->IsValid()) {
mCycleCollectionContext = nsnull;
- JS_ClearContextThread(cx);
return NS_ERROR_FAILURE;
}
static bool gcHasRun = false;
if (!gcHasRun) {
JSRuntime* rt = JS_GetRuntime(mCycleCollectionContext->GetJSContext());
if (!rt)
NS_RUNTIMEABORT("Failed to get JS runtime!");
@@ -605,21 +601,25 @@ nsXPConnect::BeginCycleCollection(nsCycl
NoteWeakMapsTracer trc(mCycleCollectionContext->GetJSContext(),
TraceWeakMapping, cb);
js::TraceWeakMaps(&trc);
return NS_OK;
}
-void
+bool
nsXPConnect::NotifyLeaveMainThread()
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
- JS_ClearRuntimeThread(mRuntime->GetJSRuntime());
+ JSRuntime *rt = mRuntime->GetJSRuntime();
+ if (JS_IsInRequest(rt) || JS_IsInSuspendedRequest(rt))
+ return false;
+ JS_ClearRuntimeThread(rt);
+ return true;
}
void
nsXPConnect::NotifyEnterCycleCollectionThread()
{
NS_ABORT_IF_FALSE(!NS_IsMainThread(), "On main thread");
JS_SetRuntimeThread(mRuntime->GetJSRuntime());
}
@@ -636,21 +636,18 @@ nsXPConnect::NotifyEnterMainThread()
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
JS_SetRuntimeThread(mRuntime->GetJSRuntime());
}
nsresult
nsXPConnect::FinishTraverse()
{
- if (mCycleCollectionContext) {
- JSContext *cx = mCycleCollectionContext->GetJSContext();
+ if (mCycleCollectionContext)
mCycleCollectionContext = nsnull;
- JS_ClearContextThread(cx);
- }
return NS_OK;
}
nsresult
nsXPConnect::FinishCycleCollection()
{
#ifdef DEBUG_CC
if (mJSRoots.ops) {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -534,17 +534,17 @@ public:
// nsCycleCollectionParticipant
NS_IMETHOD Root(void *p);
NS_IMETHOD Unlink(void *p);
NS_IMETHOD Unroot(void *p);
NS_IMETHOD Traverse(void *p,
nsCycleCollectionTraversalCallback &cb);
// nsCycleCollectionLanguageRuntime
- virtual void NotifyLeaveMainThread();
+ virtual bool NotifyLeaveMainThread();
virtual void NotifyEnterCycleCollectionThread();
virtual void NotifyLeaveCycleCollectionThread();
virtual void NotifyEnterMainThread();
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
bool explainExpectedLiveGarbage);
virtual nsresult FinishTraverse();
virtual nsresult FinishCycleCollection();
virtual nsCycleCollectionParticipant *ToParticipant(void *p);
@@ -3660,19 +3660,17 @@ public:
{
// Do a release-mode assert that we're not doing anything significant in
// XPConnect off the main thread. If you're an extension developer hitting
// this, you need to change your code. See bug 716167.
if (!NS_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread()))
JS_Assert("NS_IsMainThread()", __FILE__, __LINE__);
if (cx) {
- NS_ASSERTION(js::GetContextThread(cx), "Uh, JS context w/o a thread?");
-
- if (js::GetContextThread(cx) == sMainJSThread)
+ if (js::GetOwnerThread(cx) == sMainJSThread)
return sMainThreadData;
} else if (sMainThreadData && sMainThreadData->mThread == PR_GetCurrentThread()) {
return sMainThreadData;
}
return GetDataImpl(cx);
}
@@ -3765,17 +3763,17 @@ public:
void ClearWrappedNativeThreadsafetyReportDepth()
{mWrappedNativeThreadsafetyReportDepth = 0;}
#endif
static void ShutDown()
{sMainJSThread = nsnull; sMainThreadData = nsnull;}
static bool IsMainThread(JSContext *cx)
- { return js::GetContextThread(cx) == sMainJSThread; }
+ { return js::GetOwnerThread(cx) == sMainJSThread; }
private:
XPCPerThreadData();
static XPCPerThreadData* GetDataImpl(JSContext *cx);
private:
XPCJSContextStack* mJSContextStack;
XPCPerThreadData* mNextThread;
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -3743,20 +3743,23 @@ public:
if (!mCollector->PrepareForCollection(&whiteNodes))
return 0;
NS_ASSERTION(!mListener, "Should have cleared this already!");
if (aListener && NS_FAILED(aListener->Begin()))
aListener = nsnull;
mListener = aListener;
- GetJSRuntime()->NotifyLeaveMainThread();
- mRequest.Notify();
- mReply.Wait();
- GetJSRuntime()->NotifyEnterMainThread();
+ if (GetJSRuntime()->NotifyLeaveMainThread()) {
+ mRequest.Notify();
+ mReply.Wait();
+ GetJSRuntime()->NotifyEnterMainThread();
+ } else {
+ mCollected = mCollector->BeginCollection(mListener);
+ }
mListener = nsnull;
if (mCollected) {
mCollected = mCollector->FinishCollection(aListener);
mCollector->CleanupAfterCollection();
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -86,18 +86,21 @@ void nsCycleCollector_shutdown();
// If the JS runtime is registered nsCycleCollector_collect will call
// nsCycleCollectionJSRuntime::Collect which will call
// nsCycleCollector_doCollect, else nsCycleCollector_collect will call
// nsCycleCollector_doCollect directly.
struct nsCycleCollectionJSRuntime : public nsCycleCollectionLanguageRuntime
{
/**
* Called before/after transitioning to/from the main thread.
+ *
+ * NotifyLeaveMainThread may return 'false' to prevent the cycle collector
+ * from leaving the main thread.
*/
- virtual void NotifyLeaveMainThread() = 0;
+ virtual bool NotifyLeaveMainThread() = 0;
virtual void NotifyEnterCycleCollectionThread() = 0;
virtual void NotifyLeaveCycleCollectionThread() = 0;
virtual void NotifyEnterMainThread() = 0;
/**
* Should we force a JavaScript GC before a CC?
*/
virtual bool NeedCollect() = 0;