зеркало из https://github.com/mozilla/pjs.git
Bug 675078 - rm JSThreadData and JSThread (JSRuntime is now officially single-threaded) (r=igor,rs=mccr8)
--HG-- extra : rebase_source : f6d2c2c6083f4589691ede571159bfd7e7ef8701
This commit is contained in:
Родитель
23500b9155
Коммит
a4a1b33c3f
|
@ -122,10 +122,10 @@ struct IterateData
|
||||||
: runtimeObject(0)
|
: runtimeObject(0)
|
||||||
, runtimeAtomsTable(0)
|
, runtimeAtomsTable(0)
|
||||||
, runtimeContexts(0)
|
, runtimeContexts(0)
|
||||||
, runtimeThreadsNormal(0)
|
, runtimeNormal(0)
|
||||||
, runtimeThreadsTemporary(0)
|
, runtimeTemporary(0)
|
||||||
, runtimeThreadsRegexpCode(0)
|
, runtimeRegexpCode(0)
|
||||||
, runtimeThreadsStackCommitted(0)
|
, runtimeStackCommitted(0)
|
||||||
, gcHeapChunkTotal(0)
|
, gcHeapChunkTotal(0)
|
||||||
, gcHeapChunkCleanUnused(0)
|
, gcHeapChunkCleanUnused(0)
|
||||||
, gcHeapChunkDirtyUnused(0)
|
, gcHeapChunkDirtyUnused(0)
|
||||||
|
@ -153,10 +153,10 @@ struct IterateData
|
||||||
int64_t runtimeObject;
|
int64_t runtimeObject;
|
||||||
int64_t runtimeAtomsTable;
|
int64_t runtimeAtomsTable;
|
||||||
int64_t runtimeContexts;
|
int64_t runtimeContexts;
|
||||||
int64_t runtimeThreadsNormal;
|
int64_t runtimeNormal;
|
||||||
int64_t runtimeThreadsTemporary;
|
int64_t runtimeTemporary;
|
||||||
int64_t runtimeThreadsRegexpCode;
|
int64_t runtimeRegexpCode;
|
||||||
int64_t runtimeThreadsStackCommitted;
|
int64_t runtimeStackCommitted;
|
||||||
int64_t gcHeapChunkTotal;
|
int64_t gcHeapChunkTotal;
|
||||||
int64_t gcHeapChunkCleanUnused;
|
int64_t gcHeapChunkCleanUnused;
|
||||||
int64_t gcHeapChunkDirtyUnused;
|
int64_t gcHeapChunkDirtyUnused;
|
||||||
|
|
|
@ -130,7 +130,6 @@ CPPSRCS = \
|
||||||
jsinfer.cpp \
|
jsinfer.cpp \
|
||||||
jsinterp.cpp \
|
jsinterp.cpp \
|
||||||
jsiter.cpp \
|
jsiter.cpp \
|
||||||
jslock.cpp \
|
|
||||||
jslog2.cpp \
|
jslog2.cpp \
|
||||||
jsmath.cpp \
|
jsmath.cpp \
|
||||||
jsnativestack.cpp \
|
jsnativestack.cpp \
|
||||||
|
@ -619,7 +618,7 @@ check-malloc-function-usage: $(filter-out %jsalloc.h %jscntxt.h %jsutil.h, $(ALL
|
||||||
|
|
||||||
# We desire these numbers to go down, not up. See "User guide to memory
|
# We desire these numbers to go down, not up. See "User guide to memory
|
||||||
# management within SpiderMonkey" in jsutil.h.
|
# 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_}" $^
|
"in Makefile.in" "{cx,rt}->{new_,array_new,malloc_,calloc_,realloc_}" $^
|
||||||
# This should go to zero, if possible.
|
# This should go to zero, if possible.
|
||||||
$(srcdir)/config/check_source_count.py UnwantedForeground:: 31 \
|
$(srcdir)/config/check_source_count.py UnwantedForeground:: 31 \
|
||||||
|
@ -700,10 +699,6 @@ DEFINES += -DJS_HAS_CTYPES
|
||||||
DEFINES += -DDLL_PREFIX=\"$(DLL_PREFIX)\" -DDLL_SUFFIX=\"$(DLL_SUFFIX)\"
|
DEFINES += -DDLL_PREFIX=\"$(DLL_PREFIX)\" -DDLL_SUFFIX=\"$(DLL_SUFFIX)\"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef JS_NO_THIN_LOCKS
|
|
||||||
DEFINES += -DJS_USE_ONLY_NSPR_LOCKS
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef JS_VERSION
|
ifdef JS_VERSION
|
||||||
DEFINES += -DJS_VERSION=$(JS_VERSION)
|
DEFINES += -DJS_VERSION=$(JS_VERSION)
|
||||||
endif
|
endif
|
||||||
|
@ -781,20 +776,6 @@ EXTRA_LIBS += -lposix4 -ldl -lnsl -lsocket
|
||||||
endif
|
endif
|
||||||
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.
|
# An AIX Optimization bug causes PR_dtoa() & JS_dtoa to produce wrong result.
|
||||||
# This suppresses optimization for this single compilation unit.
|
# This suppresses optimization for this single compilation unit.
|
||||||
ifeq ($(OS_ARCH),AIX)
|
ifeq ($(OS_ARCH),AIX)
|
||||||
|
|
|
@ -219,37 +219,26 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
|
||||||
|
|
||||||
data->runtimeObject = data->mallocSizeOf(rt, sizeof(JSRuntime));
|
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
|
// Nb: we use sizeOfExcludingThis() because atomState.atoms is within
|
||||||
// JSRuntime, and so counted when JSRuntime is counted.
|
// JSRuntime, and so counted when JSRuntime is counted.
|
||||||
data->runtimeAtomsTable =
|
data->runtimeAtomsTable =
|
||||||
rt->atomState.atoms.sizeOfExcludingThis(data->mallocSizeOf);
|
rt->atomState.atoms.sizeOfExcludingThis(data->mallocSizeOf);
|
||||||
|
|
||||||
{
|
JSContext *acx, *iter = NULL;
|
||||||
// Need the GC lock to call JS_ContextIteratorUnlocked() and to
|
while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL)
|
||||||
// access rt->threads.
|
data->runtimeContexts += acx->sizeOfIncludingThis(data->mallocSizeOf);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_DestroyContextNoGC(cx);
|
JS_DestroyContextNoGC(cx);
|
||||||
|
@ -346,26 +335,17 @@ GetExplicitNonHeapForRuntime(JSRuntime *rt, int64_t *amount,
|
||||||
IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback);
|
IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback);
|
||||||
*amount += n;
|
*amount += n;
|
||||||
|
|
||||||
{
|
// explicit/runtime/regexp-code
|
||||||
// Need the GC lock to call JS_ContextIteratorUnlocked() and to
|
// explicit/runtime/stack-committed
|
||||||
// access rt->threads.
|
size_t regexpCode, stackCommitted;
|
||||||
AutoLockGC lock(rt);
|
rt->sizeOfExcludingThis(mallocSizeOf,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
®expCode,
|
||||||
|
&stackCommitted);
|
||||||
|
|
||||||
// explicit/runtime/threads/regexp-code
|
*amount += regexpCode;
|
||||||
// explicit/runtime/threads/stack-committed
|
*amount += stackCommitted;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_DestroyContextNoGC(cx);
|
JS_DestroyContextNoGC(cx);
|
||||||
|
|
|
@ -61,19 +61,6 @@ using namespace std;
|
||||||
namespace js {
|
namespace js {
|
||||||
namespace ctypes {
|
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
|
** JSAPI function prototypes
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -2791,7 +2778,6 @@ CType::FinalizeProtoClass(JSContext* cx, JSObject* obj)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JSContext* closureCx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
|
JSContext* closureCx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
|
||||||
JS_SetContextThread(closureCx);
|
|
||||||
JS_DestroyContextNoGC(closureCx);
|
JS_DestroyContextNoGC(closureCx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5383,15 +5369,8 @@ CClosure::Create(JSContext* cx,
|
||||||
JS_DestroyContextNoGC(cinfo->cx);
|
JS_DestroyContextNoGC(cinfo->cx);
|
||||||
return NULL;
|
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
|
// 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
|
// 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
|
// the caller to know about it _now_, rather than some uncertain time in the
|
||||||
|
@ -5505,11 +5484,6 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
|
||||||
JSObject* thisObj = cinfo->thisObj;
|
JSObject* thisObj = cinfo->thisObj;
|
||||||
JSObject* jsfnObj = cinfo->jsfnObj;
|
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));
|
JS_AbortIfWrongThread(JS_GetRuntime(cx));
|
||||||
|
|
||||||
JSAutoRequest ar(cx);
|
JSAutoRequest ar(cx);
|
||||||
|
|
|
@ -331,9 +331,6 @@ struct ClosureInfo
|
||||||
JSObject* jsfnObj; // JS function
|
JSObject* jsfnObj; // JS function
|
||||||
void* errResult; // Result that will be returned if the closure throws
|
void* errResult; // Result that will be returned if the closure throws
|
||||||
ffi_closure* closure; // The C closure itself
|
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
|
// Anything conditionally freed in the destructor should be initialized to
|
||||||
// NULL here.
|
// NULL here.
|
||||||
|
|
283
js/src/jsapi.cpp
283
js/src/jsapi.cpp
|
@ -60,6 +60,7 @@
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
#include "jsversion.h"
|
#include "jsversion.h"
|
||||||
#include "jsdate.h"
|
#include "jsdate.h"
|
||||||
|
#include "jsdtoa.h"
|
||||||
#include "jsexn.h"
|
#include "jsexn.h"
|
||||||
#include "jsfun.h"
|
#include "jsfun.h"
|
||||||
#include "jsgc.h"
|
#include "jsgc.h"
|
||||||
|
@ -68,6 +69,7 @@
|
||||||
#include "jsiter.h"
|
#include "jsiter.h"
|
||||||
#include "jslock.h"
|
#include "jslock.h"
|
||||||
#include "jsmath.h"
|
#include "jsmath.h"
|
||||||
|
#include "jsnativestack.h"
|
||||||
#include "jsnum.h"
|
#include "jsnum.h"
|
||||||
#include "json.h"
|
#include "json.h"
|
||||||
#include "jsobj.h"
|
#include "jsobj.h"
|
||||||
|
@ -88,7 +90,8 @@
|
||||||
#include "frontend/BytecodeCompiler.h"
|
#include "frontend/BytecodeCompiler.h"
|
||||||
#include "frontend/BytecodeEmitter.h"
|
#include "frontend/BytecodeEmitter.h"
|
||||||
#include "js/MemoryMetrics.h"
|
#include "js/MemoryMetrics.h"
|
||||||
#include "mozilla/Util.h" // DebugOnly
|
#include "mozilla/Util.h"
|
||||||
|
#include "yarr/BumpPointerAllocator.h"
|
||||||
|
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
#include "jsinferinlines.h"
|
#include "jsinferinlines.h"
|
||||||
|
@ -692,11 +695,27 @@ JS_IsBuiltinFunctionConstructor(JSFunction *fun)
|
||||||
static JSBool js_NewRuntimeWasCalled = JS_FALSE;
|
static JSBool js_NewRuntimeWasCalled = JS_FALSE;
|
||||||
|
|
||||||
JSRuntime::JSRuntime()
|
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),
|
cxCallback(NULL),
|
||||||
compartmentCallback(NULL),
|
compartmentCallback(NULL),
|
||||||
activityCallback(NULL),
|
activityCallback(NULL),
|
||||||
activityCallbackArg(NULL),
|
activityCallbackArg(NULL),
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
suspendCount(0),
|
||||||
|
requestDepth(0),
|
||||||
|
# ifdef DEBUG
|
||||||
|
checkRequestDepth(0),
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
gcSystemAvailableChunkListHead(NULL),
|
gcSystemAvailableChunkListHead(NULL),
|
||||||
gcUserAvailableChunkListHead(NULL),
|
gcUserAvailableChunkListHead(NULL),
|
||||||
gcKeepAtoms(0),
|
gcKeepAtoms(0),
|
||||||
|
@ -746,10 +765,7 @@ JSRuntime::JSRuntime()
|
||||||
data(NULL),
|
data(NULL),
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
gcLock(NULL),
|
gcLock(NULL),
|
||||||
gcDone(NULL),
|
|
||||||
requestDone(NULL),
|
|
||||||
requestCount(0),
|
requestCount(0),
|
||||||
gcThread(NULL),
|
|
||||||
gcHelperThread(thisFromCtor()),
|
gcHelperThread(thisFromCtor()),
|
||||||
#endif
|
#endif
|
||||||
debuggerMutations(0),
|
debuggerMutations(0),
|
||||||
|
@ -757,24 +773,21 @@ JSRuntime::JSRuntime()
|
||||||
structuredCloneCallbacks(NULL),
|
structuredCloneCallbacks(NULL),
|
||||||
telemetryCallback(NULL),
|
telemetryCallback(NULL),
|
||||||
propertyRemovals(0),
|
propertyRemovals(0),
|
||||||
scriptFilenameTable(NULL),
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
scriptFilenameTableLock(NULL),
|
|
||||||
#endif
|
|
||||||
thousandsSeparator(0),
|
thousandsSeparator(0),
|
||||||
decimalSeparator(0),
|
decimalSeparator(0),
|
||||||
numGrouping(0),
|
numGrouping(0),
|
||||||
anynameObject(NULL),
|
anynameObject(NULL),
|
||||||
functionNamespaceObject(NULL),
|
functionNamespaceObject(NULL),
|
||||||
#ifdef JS_THREADSAFE
|
waiveGCQuota(false),
|
||||||
interruptCounter(0),
|
dtoaState(NULL),
|
||||||
#else
|
pendingProxyOperation(NULL),
|
||||||
threadData(thisFromCtor()),
|
|
||||||
#endif
|
|
||||||
trustedPrincipals_(NULL),
|
trustedPrincipals_(NULL),
|
||||||
wrapObjectCallback(NULL),
|
wrapObjectCallback(TransparentObjectWrapper),
|
||||||
preWrapObjectCallback(NULL),
|
preWrapObjectCallback(NULL),
|
||||||
preserveWrapperCallback(NULL),
|
preserveWrapperCallback(NULL),
|
||||||
|
#ifdef DEBUG
|
||||||
|
noGCOrAllocationCheck(0),
|
||||||
|
#endif
|
||||||
inOOMReport(0)
|
inOOMReport(0)
|
||||||
{
|
{
|
||||||
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
|
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
|
||||||
|
@ -789,7 +802,7 @@ bool
|
||||||
JSRuntime::init(uint32_t maxbytes)
|
JSRuntime::init(uint32_t maxbytes)
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
ownerThread_ = js_CurrentThreadId();
|
ownerThread_ = PR_GetCurrentThread();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef JS_METHODJIT_SPEW
|
#ifdef JS_METHODJIT_SPEW
|
||||||
|
@ -812,25 +825,28 @@ JSRuntime::init(uint32_t maxbytes)
|
||||||
if (!js_InitAtomState(this))
|
if (!js_InitAtomState(this))
|
||||||
return false;
|
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))
|
if (!InitRuntimeNumberState(this))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
dtoaState = js_NewDtoaState();
|
||||||
|
if (!dtoaState)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stackSpace.init())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
conservativeGC.nativeStackBase = GetNativeStackBase();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSRuntime::~JSRuntime()
|
JSRuntime::~JSRuntime()
|
||||||
{
|
{
|
||||||
|
JS_ASSERT(onOwnerThread());
|
||||||
|
|
||||||
|
delete_<JSC::ExecutableAllocator>(execAlloc_);
|
||||||
|
delete_<WTF::BumpPointerAllocator>(bumpAlloc_);
|
||||||
|
JS_ASSERT(!repCache_);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
/* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
|
/* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
|
||||||
if (!JS_CLIST_IS_EMPTY(&contextList)) {
|
if (!JS_CLIST_IS_EMPTY(&contextList)) {
|
||||||
|
@ -849,17 +865,15 @@ JSRuntime::~JSRuntime()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FinishRuntimeNumberState(this);
|
FinishRuntimeNumberState(this);
|
||||||
js_FinishThreads(this);
|
|
||||||
js_FinishAtomState(this);
|
js_FinishAtomState(this);
|
||||||
|
|
||||||
|
if (dtoaState)
|
||||||
|
js_DestroyDtoaState(dtoaState);
|
||||||
|
|
||||||
js_FinishGC(this);
|
js_FinishGC(this);
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
if (gcLock)
|
if (gcLock)
|
||||||
JS_DESTROY_LOCK(gcLock);
|
PR_DestroyLock(gcLock);
|
||||||
if (gcDone)
|
|
||||||
JS_DESTROY_CONDVAR(gcDone);
|
|
||||||
if (requestDone)
|
|
||||||
JS_DESTROY_CONDVAR(requestDone);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,23 +881,27 @@ JSRuntime::~JSRuntime()
|
||||||
void
|
void
|
||||||
JSRuntime::setOwnerThread()
|
JSRuntime::setOwnerThread()
|
||||||
{
|
{
|
||||||
JS_ASSERT(ownerThread_ == (void *)-1);
|
JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */
|
||||||
ownerThread_ = js_CurrentThreadId();
|
JS_ASSERT(requestDepth == 0);
|
||||||
|
ownerThread_ = PR_GetCurrentThread();
|
||||||
|
conservativeGC.nativeStackBase = GetNativeStackBase();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSRuntime::clearOwnerThread()
|
JSRuntime::clearOwnerThread()
|
||||||
{
|
{
|
||||||
JS_ASSERT(onOwnerThread());
|
JS_ASSERT(onOwnerThread());
|
||||||
ownerThread_ = (void *)-1;
|
JS_ASSERT(requestDepth == 0);
|
||||||
|
ownerThread_ = (void *)0xc1ea12; /* "clear" */
|
||||||
|
conservativeGC.nativeStackBase = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_FRIEND_API(bool)
|
JS_FRIEND_API(bool)
|
||||||
JSRuntime::onOwnerThread() const
|
JSRuntime::onOwnerThread() const
|
||||||
{
|
{
|
||||||
return ownerThread_ == js_CurrentThreadId();
|
return ownerThread_ == PR_GetCurrentThread();
|
||||||
}
|
}
|
||||||
#endif
|
#endif /* JS_THREADSAFE */
|
||||||
|
|
||||||
JS_PUBLIC_API(JSRuntime *)
|
JS_PUBLIC_API(JSRuntime *)
|
||||||
JS_NewRuntime(uint32_t maxbytes)
|
JS_NewRuntime(uint32_t maxbytes)
|
||||||
|
@ -943,10 +961,6 @@ JS_PUBLIC_API(void)
|
||||||
JS_ShutDown(void)
|
JS_ShutDown(void)
|
||||||
{
|
{
|
||||||
Probes::shutdown();
|
Probes::shutdown();
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
js_CleanupLocks();
|
|
||||||
#endif
|
|
||||||
PRMJ_NowShutdown();
|
PRMJ_NowShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -990,31 +1004,17 @@ JS::UserCompartmentCount(const JSRuntime *rt)
|
||||||
static void
|
static void
|
||||||
StartRequest(JSContext *cx)
|
StartRequest(JSContext *cx)
|
||||||
{
|
{
|
||||||
JSThread *t = cx->thread();
|
JSRuntime *rt = cx->runtime;
|
||||||
JS_ASSERT(CURRENT_THREAD_IS_ME(t));
|
JS_ASSERT(rt->onOwnerThread());
|
||||||
|
|
||||||
if (t->data.requestDepth) {
|
if (rt->requestDepth) {
|
||||||
t->data.requestDepth++;
|
rt->requestDepth++;
|
||||||
} else {
|
} else {
|
||||||
JSRuntime *rt = cx->runtime;
|
|
||||||
AutoLockGC lock(rt);
|
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. */
|
/* Indicate that a request is running. */
|
||||||
rt->requestCount++;
|
rt->requestCount++;
|
||||||
t->data.requestDepth = 1;
|
rt->requestDepth = 1;
|
||||||
|
|
||||||
/*
|
|
||||||
* Adjust rt->interruptCounter to reflect any interrupts added while the
|
|
||||||
* thread was suspended.
|
|
||||||
*/
|
|
||||||
if (t->data.interruptFlags)
|
|
||||||
JS_ATOMIC_INCREMENT(&rt->interruptCounter);
|
|
||||||
|
|
||||||
if (rt->requestCount == 1 && rt->activityCallback)
|
if (rt->requestCount == 1 && rt->activityCallback)
|
||||||
rt->activityCallback(rt->activityCallbackArg, true);
|
rt->activityCallback(rt->activityCallbackArg, true);
|
||||||
|
@ -1024,32 +1024,23 @@ StartRequest(JSContext *cx)
|
||||||
static void
|
static void
|
||||||
StopRequest(JSContext *cx)
|
StopRequest(JSContext *cx)
|
||||||
{
|
{
|
||||||
JSThread *t = cx->thread();
|
JSRuntime *rt = cx->runtime;
|
||||||
JS_ASSERT(CURRENT_THREAD_IS_ME(t));
|
JS_ASSERT(rt->onOwnerThread());
|
||||||
JS_ASSERT(t->data.requestDepth != 0);
|
JS_ASSERT(rt->requestDepth != 0);
|
||||||
if (t->data.requestDepth != 1) {
|
if (rt->requestDepth != 1) {
|
||||||
t->data.requestDepth--;
|
rt->requestDepth--;
|
||||||
} else {
|
} else {
|
||||||
t->data.conservativeGC.updateForRequestEnd(t->suspendCount);
|
rt->conservativeGC.updateForRequestEnd(rt->suspendCount);
|
||||||
|
|
||||||
/* Lock before clearing to interlock with ClaimScope, in jslock.c. */
|
/* Lock before clearing to interlock with ClaimScope, in jslock.c. */
|
||||||
JSRuntime *rt = cx->runtime;
|
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
|
|
||||||
t->data.requestDepth = 0;
|
rt->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);
|
|
||||||
|
|
||||||
/* Give the GC a chance to run if this was the last request running. */
|
/* Give the GC a chance to run if this was the last request running. */
|
||||||
JS_ASSERT(rt->requestCount > 0);
|
JS_ASSERT(rt->requestCount > 0);
|
||||||
rt->requestCount--;
|
rt->requestCount--;
|
||||||
if (rt->requestCount == 0) {
|
if (rt->requestCount == 0) {
|
||||||
JS_NOTIFY_REQUEST_DONE(rt);
|
|
||||||
if (rt->activityCallback)
|
if (rt->activityCallback)
|
||||||
rt->activityCallback(rt->activityCallbackArg, false);
|
rt->activityCallback(rt->activityCallbackArg, false);
|
||||||
}
|
}
|
||||||
|
@ -1090,15 +1081,15 @@ JS_PUBLIC_API(jsrefcount)
|
||||||
JS_SuspendRequest(JSContext *cx)
|
JS_SuspendRequest(JSContext *cx)
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
JSThread *t = cx->thread();
|
JSRuntime *rt = cx->runtime;
|
||||||
JS_ASSERT(CURRENT_THREAD_IS_ME(t));
|
JS_ASSERT(rt->onOwnerThread());
|
||||||
|
|
||||||
jsrefcount saveDepth = t->data.requestDepth;
|
jsrefcount saveDepth = rt->requestDepth;
|
||||||
if (!saveDepth)
|
if (!saveDepth)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
t->suspendCount++;
|
rt->suspendCount++;
|
||||||
t->data.requestDepth = 1;
|
rt->requestDepth = 1;
|
||||||
StopRequest(cx);
|
StopRequest(cx);
|
||||||
return saveDepth;
|
return saveDepth;
|
||||||
#else
|
#else
|
||||||
|
@ -1110,25 +1101,36 @@ JS_PUBLIC_API(void)
|
||||||
JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth)
|
JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth)
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
JSThread *t = cx->thread();
|
JSRuntime *rt = cx->runtime;
|
||||||
JS_ASSERT(CURRENT_THREAD_IS_ME(t));
|
JS_ASSERT(rt->onOwnerThread());
|
||||||
if (saveDepth == 0)
|
if (saveDepth == 0)
|
||||||
return;
|
return;
|
||||||
JS_ASSERT(saveDepth >= 1);
|
JS_ASSERT(saveDepth >= 1);
|
||||||
JS_ASSERT(!t->data.requestDepth);
|
JS_ASSERT(!rt->requestDepth);
|
||||||
JS_ASSERT(t->suspendCount);
|
JS_ASSERT(rt->suspendCount);
|
||||||
StartRequest(cx);
|
StartRequest(cx);
|
||||||
t->data.requestDepth = saveDepth;
|
rt->requestDepth = saveDepth;
|
||||||
t->suspendCount--;
|
rt->suspendCount--;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(JSBool)
|
JS_PUBLIC_API(JSBool)
|
||||||
JS_IsInRequest(JSContext *cx)
|
JS_IsInRequest(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
|
JS_ASSERT(rt->onOwnerThread());
|
||||||
return JS_THREAD_DATA(cx)->requestDepth != 0;
|
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
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
@ -3019,15 +3021,11 @@ JS_SetThreadStackLimit(JSContext *cx, uintptr_t limitAddr)
|
||||||
JS_PUBLIC_API(void)
|
JS_PUBLIC_API(void)
|
||||||
JS_SetNativeStackQuota(JSContext *cx, size_t stackSize)
|
JS_SetNativeStackQuota(JSContext *cx, size_t stackSize)
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
JS_ASSERT(cx->thread());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JS_STACK_GROWTH_DIRECTION > 0
|
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||||
if (stackSize == 0) {
|
if (stackSize == 0) {
|
||||||
cx->stackLimit = UINTPTR_MAX;
|
cx->stackLimit = UINTPTR_MAX;
|
||||||
} else {
|
} 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);
|
JS_ASSERT(stackBase <= size_t(-1) - stackSize);
|
||||||
cx->stackLimit = stackBase + stackSize - 1;
|
cx->stackLimit = stackBase + stackSize - 1;
|
||||||
}
|
}
|
||||||
|
@ -3035,7 +3033,7 @@ JS_SetNativeStackQuota(JSContext *cx, size_t stackSize)
|
||||||
if (stackSize == 0) {
|
if (stackSize == 0) {
|
||||||
cx->stackLimit = 0;
|
cx->stackLimit = 0;
|
||||||
} else {
|
} 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);
|
JS_ASSERT(stackBase >= stackSize);
|
||||||
cx->stackLimit = stackBase - (stackSize - 1);
|
cx->stackLimit = stackBase - (stackSize - 1);
|
||||||
}
|
}
|
||||||
|
@ -5518,9 +5516,6 @@ JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv)
|
||||||
JS_PUBLIC_API(JSOperationCallback)
|
JS_PUBLIC_API(JSOperationCallback)
|
||||||
JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback)
|
JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback)
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
|
|
||||||
#endif
|
|
||||||
JSOperationCallback old = cx->operationCallback;
|
JSOperationCallback old = cx->operationCallback;
|
||||||
cx->operationCallback = callback;
|
cx->operationCallback = callback;
|
||||||
return old;
|
return old;
|
||||||
|
@ -5538,26 +5533,21 @@ JS_TriggerOperationCallback(JSContext *cx)
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
AutoLockGC lock(cx->runtime);
|
AutoLockGC lock(cx->runtime);
|
||||||
#endif
|
#endif
|
||||||
TriggerOperationCallback(cx);
|
cx->runtime->triggerOperationCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(void)
|
JS_PUBLIC_API(void)
|
||||||
JS_TriggerAllOperationCallbacks(JSRuntime *rt)
|
JS_TriggerRuntimeOperationCallback(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
#endif
|
#endif
|
||||||
TriggerAllOperationCallbacks(rt);
|
rt->triggerOperationCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(JSBool)
|
JS_PUBLIC_API(JSBool)
|
||||||
JS_IsRunning(JSContext *cx)
|
JS_IsRunning(JSContext *cx)
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
if (!cx->thread())
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
StackFrame *fp = cx->maybefp();
|
StackFrame *fp = cx->maybefp();
|
||||||
while (fp && fp->isDummyFrame())
|
while (fp && fp->isDummyFrame())
|
||||||
fp = fp->prev();
|
fp = fp->prev();
|
||||||
|
@ -6589,51 +6579,14 @@ JS_ThrowStopIteration(JSContext *cx)
|
||||||
|
|
||||||
JS_PUBLIC_API(intptr_t)
|
JS_PUBLIC_API(intptr_t)
|
||||||
JS_GetCurrentThread()
|
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
|
#ifdef JS_THREADSAFE
|
||||||
return cx->thread() ? reinterpret_cast<intptr_t>(cx->thread()->id) : 0;
|
return reinterpret_cast<intptr_t>(PR_GetCurrentThread());
|
||||||
#else
|
#else
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#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)
|
extern JS_PUBLIC_API(void)
|
||||||
JS_ClearRuntimeThread(JSRuntime *rt)
|
JS_ClearRuntimeThread(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
|
@ -6661,44 +6614,6 @@ JS_AbortIfWrongThread(JSRuntime *rt)
|
||||||
#endif
|
#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
|
#ifdef JS_GC_ZEAL
|
||||||
JS_PUBLIC_API(void)
|
JS_PUBLIC_API(void)
|
||||||
JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment)
|
JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment)
|
||||||
|
|
|
@ -2323,6 +2323,9 @@ JS_ShutDown(void);
|
||||||
JS_PUBLIC_API(void *)
|
JS_PUBLIC_API(void *)
|
||||||
JS_GetRuntimePrivate(JSRuntime *rt);
|
JS_GetRuntimePrivate(JSRuntime *rt);
|
||||||
|
|
||||||
|
extern JS_PUBLIC_API(JSRuntime *)
|
||||||
|
JS_GetRuntime(JSContext *cx);
|
||||||
|
|
||||||
JS_PUBLIC_API(void)
|
JS_PUBLIC_API(void)
|
||||||
JS_SetRuntimePrivate(JSRuntime *rt, void *data);
|
JS_SetRuntimePrivate(JSRuntime *rt, void *data);
|
||||||
|
|
||||||
|
@ -2343,7 +2346,10 @@ extern JS_PUBLIC_API(void)
|
||||||
JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth);
|
JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth);
|
||||||
|
|
||||||
extern JS_PUBLIC_API(JSBool)
|
extern JS_PUBLIC_API(JSBool)
|
||||||
JS_IsInRequest(JSContext *cx);
|
JS_IsInRequest(JSRuntime *rt);
|
||||||
|
|
||||||
|
extern JS_PUBLIC_API(JSBool)
|
||||||
|
JS_IsInSuspendedRequest(JSRuntime *rt);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
JS_END_EXTERN_C
|
JS_END_EXTERN_C
|
||||||
|
@ -2425,14 +2431,14 @@ class JSAutoCheckRequest {
|
||||||
JSAutoCheckRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) {
|
JSAutoCheckRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||||
#if defined JS_THREADSAFE && defined DEBUG
|
#if defined JS_THREADSAFE && defined DEBUG
|
||||||
mContext = cx;
|
mContext = cx;
|
||||||
JS_ASSERT(JS_IsInRequest(cx));
|
JS_ASSERT(JS_IsInRequest(JS_GetRuntime(cx)));
|
||||||
#endif
|
#endif
|
||||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
~JSAutoCheckRequest() {
|
~JSAutoCheckRequest() {
|
||||||
#if defined JS_THREADSAFE && defined DEBUG
|
#if defined JS_THREADSAFE && defined DEBUG
|
||||||
JS_ASSERT(JS_IsInRequest(mContext));
|
JS_ASSERT(JS_IsInRequest(JS_GetRuntime(mContext)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4489,7 +4495,7 @@ extern JS_PUBLIC_API(void)
|
||||||
JS_TriggerOperationCallback(JSContext *cx);
|
JS_TriggerOperationCallback(JSContext *cx);
|
||||||
|
|
||||||
extern JS_PUBLIC_API(void)
|
extern JS_PUBLIC_API(void)
|
||||||
JS_TriggerAllOperationCallbacks(JSRuntime *rt);
|
JS_TriggerRuntimeOperationCallback(JSRuntime *rt);
|
||||||
|
|
||||||
extern JS_PUBLIC_API(JSBool)
|
extern JS_PUBLIC_API(JSBool)
|
||||||
JS_IsRunning(JSContext *cx);
|
JS_IsRunning(JSContext *cx);
|
||||||
|
@ -5261,24 +5267,6 @@ JS_ThrowStopIteration(JSContext *cx);
|
||||||
extern JS_PUBLIC_API(intptr_t)
|
extern JS_PUBLIC_API(intptr_t)
|
||||||
JS_GetCurrentThread();
|
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
|
* 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
|
* runtime is created (to the current thread) and practically all entry points
|
||||||
|
|
|
@ -333,11 +333,8 @@ js_InitAtomState(JSRuntime *rt)
|
||||||
if (!state->atoms.init(JS_STRING_HASH_COUNT))
|
if (!state->atoms.init(JS_STRING_HASH_COUNT))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
js_InitLock(&state->lock);
|
|
||||||
#endif
|
|
||||||
JS_ASSERT(state->atoms.initialized());
|
JS_ASSERT(state->atoms.initialized());
|
||||||
return JS_TRUE;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -355,10 +352,6 @@ js_FinishAtomState(JSRuntime *rt)
|
||||||
|
|
||||||
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront())
|
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront())
|
||||||
r.front().asPtr()->finalize(rt);
|
r.front().asPtr()->finalize(rt);
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
js_FinishLock(&state->lock);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -433,7 +426,6 @@ AtomIsInterned(JSContext *cx, JSAtom *atom)
|
||||||
if (StaticStrings::isStatic(atom))
|
if (StaticStrings::isStatic(atom))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
AutoLockAtomsCompartment lock(cx);
|
|
||||||
AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(atom);
|
AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(atom);
|
||||||
if (!p)
|
if (!p)
|
||||||
return false;
|
return false;
|
||||||
|
@ -462,8 +454,6 @@ AtomizeInline(JSContext *cx, const jschar **pchars, size_t length,
|
||||||
if (JSAtom *s = cx->runtime->staticStrings.lookup(chars, length))
|
if (JSAtom *s = cx->runtime->staticStrings.lookup(chars, length))
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
AutoLockAtomsCompartment lock(cx);
|
|
||||||
|
|
||||||
AtomSet &atoms = cx->runtime->atomState.atoms;
|
AtomSet &atoms = cx->runtime->atomState.atoms;
|
||||||
AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length));
|
AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length));
|
||||||
|
|
||||||
|
@ -522,9 +512,6 @@ js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
|
||||||
if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
|
if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
|
||||||
return &atom;
|
return &atom;
|
||||||
|
|
||||||
/* Here we have to check whether the atom is already interned. */
|
|
||||||
AutoLockAtomsCompartment lock(cx);
|
|
||||||
|
|
||||||
AtomSet &atoms = cx->runtime->atomState.atoms;
|
AtomSet &atoms = cx->runtime->atomState.atoms;
|
||||||
AtomSet::Ptr p = atoms.lookup(AtomHasher::Lookup(&atom));
|
AtomSet::Ptr p = atoms.lookup(AtomHasher::Lookup(&atom));
|
||||||
JS_ASSERT(p); /* Non-static atom must exist in atom state set. */
|
JS_ASSERT(p); /* Non-static atom must exist in atom state set. */
|
||||||
|
@ -604,9 +591,9 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
|
||||||
{
|
{
|
||||||
if (JSAtom *atom = cx->runtime->staticStrings.lookup(chars, length))
|
if (JSAtom *atom = cx->runtime->staticStrings.lookup(chars, length))
|
||||||
return atom;
|
return atom;
|
||||||
AutoLockAtomsCompartment lock(cx);
|
if (AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length)))
|
||||||
AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length));
|
return p->asPtr();
|
||||||
return p ? p->asPtr() : NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
|
@ -268,10 +268,6 @@ struct JSAtomState
|
||||||
{
|
{
|
||||||
js::AtomSet atoms;
|
js::AtomSet atoms;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
JSThinLock lock;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From this point until the end of struct definition the struct must
|
* From this point until the end of struct definition the struct must
|
||||||
* contain only js::PropertyName fields. We use this to access the storage
|
* contain only js::PropertyName fields. We use this to access the storage
|
||||||
|
|
|
@ -64,7 +64,6 @@
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
#include "jsversion.h"
|
#include "jsversion.h"
|
||||||
#include "jsdbgapi.h"
|
#include "jsdbgapi.h"
|
||||||
#include "jsdtoa.h"
|
|
||||||
#include "jsexn.h"
|
#include "jsexn.h"
|
||||||
#include "jsfun.h"
|
#include "jsfun.h"
|
||||||
#include "jsgc.h"
|
#include "jsgc.h"
|
||||||
|
@ -72,7 +71,6 @@
|
||||||
#include "jsiter.h"
|
#include "jsiter.h"
|
||||||
#include "jslock.h"
|
#include "jslock.h"
|
||||||
#include "jsmath.h"
|
#include "jsmath.h"
|
||||||
#include "jsnativestack.h"
|
|
||||||
#include "jsnum.h"
|
#include "jsnum.h"
|
||||||
#include "jsobj.h"
|
#include "jsobj.h"
|
||||||
#include "jsopcode.h"
|
#include "jsopcode.h"
|
||||||
|
@ -96,57 +94,10 @@
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::gc;
|
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()
|
|
||||||
{
|
|
||||||
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
|
void
|
||||||
ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
|
JSRuntime::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
|
||||||
size_t *regexpCode, size_t *stackCommitted)
|
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
|
* The computedSize is 0 because sizeof(DtoaState) isn't available here and
|
||||||
* it's not worth making it available.
|
* it's not worth making it available.
|
||||||
|
@ -159,8 +110,8 @@ ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal,
|
||||||
|
|
||||||
if (regexpCode) {
|
if (regexpCode) {
|
||||||
size_t method = 0, regexp = 0, unused = 0;
|
size_t method = 0, regexp = 0, unused = 0;
|
||||||
if (execAlloc)
|
if (execAlloc_)
|
||||||
execAlloc->sizeOfCode(&method, ®exp, &unused);
|
execAlloc_->sizeOfCode(&method, ®exp, &unused);
|
||||||
JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */
|
JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */
|
||||||
*regexpCode = regexp + unused;
|
*regexpCode = regexp + unused;
|
||||||
}
|
}
|
||||||
|
@ -168,261 +119,65 @@ ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal,
|
||||||
if (stackCommitted)
|
if (stackCommitted)
|
||||||
*stackCommitted = stackSpace.sizeOfCommitted();
|
*stackCommitted = stackSpace.sizeOfCommitted();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
JS_FRIEND_API(void)
|
||||||
ThreadData::triggerOperationCallback(JSRuntime *rt)
|
JSRuntime::triggerOperationCallback()
|
||||||
{
|
{
|
||||||
JS_ASSERT(rt == this->rt);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use JS_ATOMIC_SET and JS_ATOMIC_INCREMENT in the hope that it ensures
|
* Use JS_ATOMIC_SET in the hope that it ensures the write will become
|
||||||
* the write will become immediately visible to other processors polling
|
* immediately visible to other processors polling the flag.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
if (interruptFlags)
|
JS_ATOMIC_SET(&interrupt, 1);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSC::ExecutableAllocator *
|
JSC::ExecutableAllocator *
|
||||||
ThreadData::createExecutableAllocator(JSContext *cx)
|
JSRuntime::createExecutableAllocator(JSContext *cx)
|
||||||
{
|
{
|
||||||
JS_ASSERT(!execAlloc);
|
JS_ASSERT(!execAlloc_);
|
||||||
JS_ASSERT(cx->runtime == rt);
|
JS_ASSERT(cx->runtime == this);
|
||||||
|
|
||||||
execAlloc = rt->new_<JSC::ExecutableAllocator>();
|
execAlloc_ = new_<JSC::ExecutableAllocator>();
|
||||||
if (!execAlloc)
|
if (!execAlloc_)
|
||||||
js_ReportOutOfMemory(cx);
|
js_ReportOutOfMemory(cx);
|
||||||
return execAlloc;
|
return execAlloc_;
|
||||||
}
|
}
|
||||||
|
|
||||||
WTF::BumpPointerAllocator *
|
WTF::BumpPointerAllocator *
|
||||||
ThreadData::createBumpPointerAllocator(JSContext *cx)
|
JSRuntime::createBumpPointerAllocator(JSContext *cx)
|
||||||
{
|
{
|
||||||
JS_ASSERT(!bumpAlloc);
|
JS_ASSERT(!bumpAlloc_);
|
||||||
JS_ASSERT(cx->runtime == rt);
|
JS_ASSERT(cx->runtime == this);
|
||||||
|
|
||||||
bumpAlloc = rt->new_<WTF::BumpPointerAllocator>();
|
bumpAlloc_ = new_<WTF::BumpPointerAllocator>();
|
||||||
if (!bumpAlloc)
|
if (!bumpAlloc_)
|
||||||
js_ReportOutOfMemory(cx);
|
js_ReportOutOfMemory(cx);
|
||||||
return bumpAlloc;
|
return bumpAlloc_;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegExpPrivateCache *
|
RegExpPrivateCache *
|
||||||
ThreadData::createRegExpPrivateCache(JSContext *cx)
|
JSRuntime::createRegExpPrivateCache(JSContext *cx)
|
||||||
{
|
{
|
||||||
JS_ASSERT(!repCache);
|
JS_ASSERT(!repCache_);
|
||||||
JS_ASSERT(cx->runtime == rt);
|
JS_ASSERT(cx->runtime == this);
|
||||||
|
|
||||||
RegExpPrivateCache *newCache = rt->new_<RegExpPrivateCache>(rt);
|
RegExpPrivateCache *newCache = new_<RegExpPrivateCache>(this);
|
||||||
|
|
||||||
if (!newCache || !newCache->init()) {
|
if (!newCache || !newCache->init()) {
|
||||||
js_ReportOutOfMemory(cx);
|
js_ReportOutOfMemory(cx);
|
||||||
rt->delete_<RegExpPrivateCache>(newCache);
|
delete_<RegExpPrivateCache>(newCache);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
repCache = newCache;
|
repCache_ = newCache;
|
||||||
return repCache;
|
return repCache_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ThreadData::purgeRegExpPrivateCache()
|
|
||||||
{
|
|
||||||
rt->delete_<RegExpPrivateCache>(repCache);
|
|
||||||
repCache = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* namespace js */
|
|
||||||
|
|
||||||
JSScript *
|
JSScript *
|
||||||
js_GetCurrentScript(JSContext *cx)
|
js_GetCurrentScript(JSContext *cx)
|
||||||
{
|
{
|
||||||
return cx->hasfp() ? cx->fp()->maybeScript() : NULL;
|
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 *
|
JSContext *
|
||||||
js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||||
{
|
{
|
||||||
|
@ -444,20 +199,12 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||||
return NULL;
|
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
|
* Here the GC lock is still held after js_InitContextThreadAndLockGC took it and
|
||||||
* the GC is not running on another thread.
|
* the GC is not running on another thread.
|
||||||
*/
|
*/
|
||||||
bool first = JS_CLIST_IS_EMPTY(&rt->contextList);
|
bool first = JS_CLIST_IS_EMPTY(&rt->contextList);
|
||||||
JS_APPEND_LINK(&cx->link, &rt->contextList);
|
JS_APPEND_LINK(&cx->link, &rt->contextList);
|
||||||
JS_UNLOCK_GC(rt);
|
|
||||||
|
|
||||||
js_InitRandom(cx);
|
js_InitRandom(cx);
|
||||||
|
|
||||||
|
@ -501,32 +248,20 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||||
JSRuntime *rt = cx->runtime;
|
JSRuntime *rt = cx->runtime;
|
||||||
JS_AbortIfWrongThread(rt);
|
JS_AbortIfWrongThread(rt);
|
||||||
|
|
||||||
JSContextCallback cxCallback;
|
|
||||||
|
|
||||||
JS_ASSERT(!cx->enumerators);
|
JS_ASSERT(!cx->enumerators);
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#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
|
* For API compatibility we support destroying contexts with non-zero
|
||||||
* cx->outstandingRequests but we assume that all JS_BeginRequest calls
|
* cx->outstandingRequests but we assume that all JS_BeginRequest calls
|
||||||
* on this cx contributes to cx->thread->data.requestDepth and there is no
|
* on this cx contributes to cx->thread->data.requestDepth and there is no
|
||||||
* JS_SuspendRequest calls that set aside the counter.
|
* JS_SuspendRequest calls that set aside the counter.
|
||||||
*/
|
*/
|
||||||
JS_ASSERT(cx->outstandingRequests <= cx->thread()->data.requestDepth);
|
JS_ASSERT(cx->outstandingRequests <= cx->runtime->requestDepth);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (mode != JSDCM_NEW_FAILED) {
|
if (mode != JSDCM_NEW_FAILED) {
|
||||||
cxCallback = rt->cxCallback;
|
if (JSContextCallback cxCallback = rt->cxCallback) {
|
||||||
if (cxCallback) {
|
|
||||||
/*
|
/*
|
||||||
* JSCONTEXT_DESTROY callback is not allowed to fail and must
|
* JSCONTEXT_DESTROY callback is not allowed to fail and must
|
||||||
* return true.
|
* return true.
|
||||||
|
@ -537,14 +272,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_LOCK_GC(rt);
|
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);
|
JS_REMOVE_LINK(&cx->link);
|
||||||
bool last = !rt->hasContexts();
|
bool last = !rt->hasContexts();
|
||||||
if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
|
if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
|
||||||
|
@ -566,7 +293,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||||
* that we wait for any racing GC started on a not-last context to
|
* that we wait for any racing GC started on a not-last context to
|
||||||
* finish, before we plow ahead and unpin atoms.
|
* finish, before we plow ahead and unpin atoms.
|
||||||
*/
|
*/
|
||||||
if (cx->thread()->data.requestDepth == 0)
|
if (cx->runtime->requestDepth == 0)
|
||||||
JS_BeginRequest(cx);
|
JS_BeginRequest(cx);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -607,16 +334,8 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||||
JS_MaybeGC(cx);
|
JS_MaybeGC(cx);
|
||||||
|
|
||||||
JS_LOCK_GC(rt);
|
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
|
#ifdef JS_THREADSAFE
|
||||||
rt->gcHelperThread.waitBackgroundSweepEnd();
|
rt->gcHelperThread.waitBackgroundSweepEnd();
|
||||||
#endif
|
#endif
|
||||||
|
@ -645,7 +364,7 @@ js_NextActiveContext(JSRuntime *rt, JSContext *cx)
|
||||||
JSContext *iter = cx;
|
JSContext *iter = cx;
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
|
while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
|
||||||
if (cx->outstandingRequests && cx->thread()->data.requestDepth)
|
if (cx->outstandingRequests && cx->runtime->requestDepth)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return cx;
|
return cx;
|
||||||
|
@ -738,9 +457,6 @@ js_ReportOutOfMemory(JSContext *cx)
|
||||||
{
|
{
|
||||||
cx->runtime->hadOutOfMemory = true;
|
cx->runtime->hadOutOfMemory = true;
|
||||||
|
|
||||||
/* AtomizeInline can call this indirectly when it creates the string. */
|
|
||||||
AutoUnlockAtomsCompartmentWhenLocked unlockAtomsCompartment(cx);
|
|
||||||
|
|
||||||
JSErrorReport report;
|
JSErrorReport report;
|
||||||
JSErrorReporter onError = cx->errorReporter;
|
JSErrorReporter onError = cx->errorReporter;
|
||||||
|
|
||||||
|
@ -1206,23 +922,17 @@ js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
|
||||||
JSBool
|
JSBool
|
||||||
js_InvokeOperationCallback(JSContext *cx)
|
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
|
* Reset the callback counter first, then run GC and yield. If another
|
||||||
* thread is racing us here we will accumulate another callback request
|
* thread is racing us here we will accumulate another callback request
|
||||||
* which will be serviced at the next opportunity.
|
* which will be serviced at the next opportunity.
|
||||||
*/
|
*/
|
||||||
JS_LOCK_GC(rt);
|
JS_ATOMIC_SET(&rt->interrupt, 0);
|
||||||
td->interruptFlags = 0;
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
JS_ATOMIC_DECREMENT(&rt->interruptCounter);
|
|
||||||
#endif
|
|
||||||
JS_UNLOCK_GC(rt);
|
|
||||||
|
|
||||||
if (rt->gcIsNeeded)
|
if (rt->gcIsNeeded)
|
||||||
js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason);
|
js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason);
|
||||||
|
@ -1256,43 +966,11 @@ JSBool
|
||||||
js_HandleExecutionInterrupt(JSContext *cx)
|
js_HandleExecutionInterrupt(JSContext *cx)
|
||||||
{
|
{
|
||||||
JSBool result = JS_TRUE;
|
JSBool result = JS_TRUE;
|
||||||
if (JS_THREAD_DATA(cx)->interruptFlags)
|
if (cx->runtime->interrupt)
|
||||||
result = js_InvokeOperationCallback(cx) && result;
|
result = js_InvokeOperationCallback(cx) && result;
|
||||||
return 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 *
|
StackFrame *
|
||||||
js_GetScriptedCaller(JSContext *cx, StackFrame *fp)
|
js_GetScriptedCaller(JSContext *cx, StackFrame *fp)
|
||||||
{
|
{
|
||||||
|
@ -1354,9 +1032,6 @@ JSContext::JSContext(JSRuntime *rt)
|
||||||
#endif
|
#endif
|
||||||
runtime(rt),
|
runtime(rt),
|
||||||
compartment(NULL),
|
compartment(NULL),
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
thread_(NULL),
|
|
||||||
#endif
|
|
||||||
stack(thisDuringConstruction()), /* depends on cx->thread_ */
|
stack(thisDuringConstruction()), /* depends on cx->thread_ */
|
||||||
parseMapPool_(NULL),
|
parseMapPool_(NULL),
|
||||||
globalObject(NULL),
|
globalObject(NULL),
|
||||||
|
@ -1367,7 +1042,6 @@ JSContext::JSContext(JSRuntime *rt)
|
||||||
data(NULL),
|
data(NULL),
|
||||||
data2(NULL),
|
data2(NULL),
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
atomsCompartmentIsLocked(false),
|
|
||||||
outstandingRequests(0),
|
outstandingRequests(0),
|
||||||
#endif
|
#endif
|
||||||
autoGCRooters(NULL),
|
autoGCRooters(NULL),
|
||||||
|
@ -1407,10 +1081,6 @@ JSContext::JSContext(JSRuntime *rt)
|
||||||
|
|
||||||
JSContext::~JSContext()
|
JSContext::~JSContext()
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
JS_ASSERT(!thread_);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Free the stuff hanging off of cx. */
|
/* Free the stuff hanging off of cx. */
|
||||||
if (parseMapPool_)
|
if (parseMapPool_)
|
||||||
Foreground::delete_<ParseMapPool>(parseMapPool_);
|
Foreground::delete_<ParseMapPool>(parseMapPool_);
|
||||||
|
@ -1509,15 +1179,6 @@ JSContext::runningWithTrustedPrincipals() const
|
||||||
JS_FRIEND_API(void)
|
JS_FRIEND_API(void)
|
||||||
JSRuntime::onTooMuchMalloc()
|
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);
|
TriggerGC(this, gcstats::TOOMUCHMALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1548,6 +1209,19 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSRuntime::purge(JSContext *cx)
|
||||||
|
{
|
||||||
|
tempLifoAlloc.freeUnused();
|
||||||
|
gsnCache.purge();
|
||||||
|
|
||||||
|
/* FIXME: bug 506341 */
|
||||||
|
propertyCache.purge(cx);
|
||||||
|
|
||||||
|
delete_<RegExpPrivateCache>(repCache_);
|
||||||
|
repCache_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSContext::purge()
|
JSContext::purge()
|
||||||
{
|
{
|
||||||
|
@ -1657,16 +1331,15 @@ namespace JS {
|
||||||
AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx)
|
AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx)
|
||||||
: cx(cx)
|
: cx(cx)
|
||||||
{
|
{
|
||||||
JS_ASSERT(cx->thread());
|
JS_ASSERT(cx->runtime->requestDepth || cx->runtime->gcRunning);
|
||||||
JS_ASSERT(cx->thread()->data.requestDepth || cx->thread() == cx->runtime->gcThread);
|
|
||||||
JS_ASSERT(cx->runtime->onOwnerThread());
|
JS_ASSERT(cx->runtime->onOwnerThread());
|
||||||
cx->thread()->checkRequestDepth++;
|
cx->runtime->checkRequestDepth++;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoCheckRequestDepth::~AutoCheckRequestDepth()
|
AutoCheckRequestDepth::~AutoCheckRequestDepth()
|
||||||
{
|
{
|
||||||
JS_ASSERT(cx->thread()->checkRequestDepth != 0);
|
JS_ASSERT(cx->runtime->checkRequestDepth != 0);
|
||||||
cx->thread()->checkRequestDepth--;
|
cx->runtime->checkRequestDepth--;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
552
js/src/jscntxt.h
552
js/src/jscntxt.h
|
@ -123,221 +123,71 @@ struct PendingProxyOperation {
|
||||||
JSObject *object;
|
JSObject *object;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ThreadData {
|
typedef Vector<ScriptOpcodeCountsPair, 0, SystemAllocPolicy> ScriptOpcodeCountsVector;
|
||||||
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);
|
|
||||||
|
|
||||||
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. */
|
/* Base address of the native stack for the current thread. */
|
||||||
uintptr_t *nativeStackBase;
|
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;
|
||||||
|
|
||||||
ConservativeGCThreadData conservativeGC;
|
union {
|
||||||
|
jmp_buf jmpbuf;
|
||||||
#ifdef DEBUG
|
uintptr_t words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))];
|
||||||
size_t noGCOrAllocationCheck;
|
} registerSnapshot;
|
||||||
#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
|
|
||||||
size_t *regexpCode, size_t *stackCommitted);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This must be called with the GC lock held. */
|
|
||||||
void triggerOperationCallback(JSRuntime *rt);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Frames currently running in js::Interpret. See InterpreterFrames for
|
* Cycle collector uses this to communicate that the native stack of the
|
||||||
* details.
|
* GC thread should be scanned only if the thread have more than the given
|
||||||
|
* threshold of requests.
|
||||||
*/
|
*/
|
||||||
InterpreterFrames *interpreterFrames;
|
unsigned requestThreshold;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_NEVER_INLINE void recordStackTop();
|
||||||
|
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
void updateForRequestEnd(unsigned suspendCount) {
|
||||||
|
if (suspendCount)
|
||||||
|
recordStackTop();
|
||||||
|
else
|
||||||
|
nativeStackTop = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool hasStackToScan() const {
|
||||||
|
return !!nativeStackTop;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace js */
|
} /* 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
|
struct JSRuntime
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* If non-zero, we were been asked to call the operation callback as soon
|
||||||
|
* as possible.
|
||||||
|
*/
|
||||||
|
volatile int32_t interrupt;
|
||||||
|
|
||||||
/* Default compartment. */
|
/* Default compartment. */
|
||||||
JSCompartment *atomsCompartment;
|
JSCompartment *atomsCompartment;
|
||||||
|
|
||||||
|
@ -347,6 +197,7 @@ struct JSRuntime
|
||||||
/* See comment for JS_AbortIfWrongThread in jsapi.h. */
|
/* See comment for JS_AbortIfWrongThread in jsapi.h. */
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
public:
|
public:
|
||||||
|
void *ownerThread() const { return ownerThread_; }
|
||||||
void clearOwnerThread();
|
void clearOwnerThread();
|
||||||
void setOwnerThread();
|
void setOwnerThread();
|
||||||
JS_FRIEND_API(bool) onOwnerThread() const;
|
JS_FRIEND_API(bool) onOwnerThread() const;
|
||||||
|
@ -358,6 +209,46 @@ struct JSRuntime
|
||||||
bool onOwnerThread() const { return true; }
|
bool onOwnerThread() const { return true; }
|
||||||
#endif
|
#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. */
|
/* Context create/destroy callback. */
|
||||||
JSContextCallback cxCallback;
|
JSContextCallback cxCallback;
|
||||||
|
|
||||||
|
@ -367,6 +258,18 @@ struct JSRuntime
|
||||||
js::ActivityCallback activityCallback;
|
js::ActivityCallback activityCallback;
|
||||||
void *activityCallbackArg;
|
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. */
|
/* Garbage collector state, used by jsgc.c. */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -519,7 +422,7 @@ struct JSRuntime
|
||||||
|
|
||||||
JSAtom *emptyString;
|
JSAtom *emptyString;
|
||||||
|
|
||||||
/* List of active contexts sharing this runtime; protected by gcLock. */
|
/* List of active contexts sharing this runtime. */
|
||||||
JSCList contextList;
|
JSCList contextList;
|
||||||
|
|
||||||
bool hasContexts() const {
|
bool hasContexts() const {
|
||||||
|
@ -550,20 +453,9 @@ struct JSRuntime
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
/* These combine to interlock the GC and new requests. */
|
/* These combine to interlock the GC and new requests. */
|
||||||
PRLock *gcLock;
|
PRLock *gcLock;
|
||||||
PRCondVar *gcDone;
|
|
||||||
PRCondVar *requestDone;
|
|
||||||
uint32_t requestCount;
|
uint32_t requestCount;
|
||||||
JSThread *gcThread;
|
|
||||||
|
|
||||||
js::GCHelperThread gcHelperThread;
|
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 */
|
#endif /* JS_THREADSAFE */
|
||||||
|
|
||||||
uint32_t debuggerMutations;
|
uint32_t debuggerMutations;
|
||||||
|
@ -587,12 +479,6 @@ struct JSRuntime
|
||||||
*/
|
*/
|
||||||
int32_t propertyRemovals;
|
int32_t propertyRemovals;
|
||||||
|
|
||||||
/* Script filename table. */
|
|
||||||
struct JSHashTable *scriptFilenameTable;
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
PRLock *scriptFilenameTableLock;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Number localization, used by jsnum.c */
|
/* Number localization, used by jsnum.c */
|
||||||
const char *thousandsSeparator;
|
const char *thousandsSeparator;
|
||||||
const char *decimalSeparator;
|
const char *decimalSeparator;
|
||||||
|
@ -608,14 +494,28 @@ struct JSRuntime
|
||||||
JSObject *anynameObject;
|
JSObject *anynameObject;
|
||||||
JSObject *functionNamespaceObject;
|
JSObject *functionNamespaceObject;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
/*
|
||||||
/* Number of threads with active requests and unhandled interrupts. */
|
* Flag indicating that we are waiving any soft limits on the GC heap
|
||||||
volatile int32_t interruptCounter;
|
* because we want allocations to be infallible (except when we hit OOM).
|
||||||
#else
|
*/
|
||||||
js::ThreadData threadData;
|
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:
|
private:
|
||||||
JSPrincipals *trustedPrincipals_;
|
JSPrincipals *trustedPrincipals_;
|
||||||
|
@ -633,6 +533,10 @@ struct JSRuntime
|
||||||
JSPreWrapCallback preWrapObjectCallback;
|
JSPreWrapCallback preWrapObjectCallback;
|
||||||
js::PreserveWrapperCallback preserveWrapperCallback;
|
js::PreserveWrapperCallback preserveWrapperCallback;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
size_t noGCOrAllocationCheck;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To ensure that cx->malloc does not cause a GC, we set this flag during
|
* 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
|
* OOM reporting (in js_ReportOutOfMemory). If a GC is requested while
|
||||||
|
@ -739,13 +643,20 @@ struct JSRuntime
|
||||||
* The function must be called outside the GC lock.
|
* 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 *) 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. */
|
/* Common macros to access thread-local caches in JSRuntime. */
|
||||||
#define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache)
|
#define JS_PROPERTY_CACHE(cx) (cx->runtime->propertyCache)
|
||||||
|
|
||||||
#define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms);
|
#define JS_KEEP_ATOMS(rt) (rt)->gcKeepAtoms++;
|
||||||
#define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms);
|
#define JS_UNKEEP_ATOMS(rt) (rt)->gcKeepAtoms--;
|
||||||
|
|
||||||
#ifdef JS_ARGUMENT_FORMATTER_DEFINED
|
#ifdef JS_ARGUMENT_FORMATTER_DEFINED
|
||||||
/*
|
/*
|
||||||
|
@ -899,7 +810,7 @@ struct JSContext
|
||||||
/* Limit pointer for checking native stack consumption during recursion. */
|
/* Limit pointer for checking native stack consumption during recursion. */
|
||||||
uintptr_t stackLimit;
|
uintptr_t stackLimit;
|
||||||
|
|
||||||
/* Data shared by threads in an address space. */
|
/* Data shared by contexts and compartments in an address space. */
|
||||||
JSRuntime *const runtime;
|
JSRuntime *const runtime;
|
||||||
|
|
||||||
/* GC heap compartment. */
|
/* GC heap compartment. */
|
||||||
|
@ -907,16 +818,6 @@ struct JSContext
|
||||||
|
|
||||||
inline void setCompartment(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. */
|
/* Current execution stack. */
|
||||||
js::ContextStack stack;
|
js::ContextStack stack;
|
||||||
|
|
||||||
|
@ -1044,17 +945,10 @@ struct JSContext
|
||||||
bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); }
|
bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); }
|
||||||
bool hasJITHardeningOption() const { return !hasRunOption(JSOPTION_SOFTEN); }
|
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();
|
inline js::LifoAlloc &typeLifoAlloc();
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#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
|
unsigned outstandingRequests;/* number of JS_BeginRequest calls
|
||||||
without the corresponding
|
without the corresponding
|
||||||
JS_EndRequest. */
|
JS_EndRequest. */
|
||||||
|
@ -1165,8 +1059,6 @@ struct JSContext
|
||||||
js::GCHelperThread *gcBackgroundFree;
|
js::GCHelperThread *gcBackgroundFree;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
js::ThreadData *threadData() { return JS_THREAD_DATA(this); }
|
|
||||||
|
|
||||||
inline void* malloc_(size_t bytes) {
|
inline void* malloc_(size_t bytes) {
|
||||||
return runtime->malloc_(bytes, this);
|
return runtime->malloc_(bytes, this);
|
||||||
}
|
}
|
||||||
|
@ -1251,13 +1143,6 @@ struct JSContext
|
||||||
return reinterpret_cast<JSContext *>(uintptr_t(link) - offsetof(JSContext, 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:
|
private:
|
||||||
/*
|
/*
|
||||||
* The allocation code calls the function to indicate either OOM failure
|
* The allocation code calls the function to indicate either OOM failure
|
||||||
|
@ -1325,6 +1210,14 @@ class AutoXMLRooter : private AutoGCRooter {
|
||||||
};
|
};
|
||||||
#endif /* JS_HAS_XML_SUPPORT */
|
#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 {
|
class AutoUnlockGC {
|
||||||
private:
|
private:
|
||||||
JSRuntime *rt;
|
JSRuntime *rt;
|
||||||
|
@ -1341,63 +1234,6 @@ class AutoUnlockGC {
|
||||||
~AutoUnlockGC() { JS_LOCK_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 {
|
class AutoKeepAtoms {
|
||||||
JSRuntime *rt;
|
JSRuntime *rt;
|
||||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
@ -1478,62 +1314,8 @@ class JSAutoResolveFlags
|
||||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
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 {
|
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
|
* Enumerate all contexts in a runtime that are in the same thread as a given
|
||||||
* context.
|
* context.
|
||||||
|
@ -1544,11 +1326,7 @@ class ThreadContextRange {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ThreadContextRange(JSContext *cx) {
|
explicit ThreadContextRange(JSContext *cx) {
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
end = &cx->thread()->contextList;
|
|
||||||
#else
|
|
||||||
end = &cx->runtime->contextList;
|
end = &cx->runtime->contextList;
|
||||||
#endif
|
|
||||||
begin = end->next;
|
begin = end->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1556,11 +1334,7 @@ public:
|
||||||
void popFront() { JS_ASSERT(!empty()); begin = begin->next; }
|
void popFront() { JS_ASSERT(!empty()); begin = begin->next; }
|
||||||
|
|
||||||
JSContext *front() const {
|
JSContext *front() const {
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
return JSContext::fromThreadLinks(begin);
|
|
||||||
#else
|
|
||||||
return JSContext::fromLinkField(begin);
|
return JSContext::fromLinkField(begin);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1573,6 +1347,13 @@ public:
|
||||||
extern JSContext *
|
extern JSContext *
|
||||||
js_NewContext(JSRuntime *rt, size_t stackChunkSize);
|
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
|
extern void
|
||||||
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode);
|
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode);
|
||||||
|
|
||||||
|
@ -1658,8 +1439,7 @@ js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
|
||||||
extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
|
extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
# define JS_ASSERT_REQUEST_DEPTH(cx) (JS_ASSERT((cx)->thread()), \
|
# define JS_ASSERT_REQUEST_DEPTH(cx) JS_ASSERT((cx)->runtime->requestDepth >= 1)
|
||||||
JS_ASSERT((cx)->thread()->data.requestDepth >= 1))
|
|
||||||
#else
|
#else
|
||||||
# define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0)
|
# define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0)
|
||||||
#endif
|
#endif
|
||||||
|
@ -1671,7 +1451,7 @@ extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
|
||||||
*/
|
*/
|
||||||
#define JS_CHECK_OPERATION_LIMIT(cx) \
|
#define JS_CHECK_OPERATION_LIMIT(cx) \
|
||||||
(JS_ASSERT_REQUEST_DEPTH(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
|
* Invoke the operation callback and return false if the current execution
|
||||||
|
@ -1683,18 +1463,6 @@ js_InvokeOperationCallback(JSContext *cx);
|
||||||
extern JSBool
|
extern JSBool
|
||||||
js_HandleExecutionInterrupt(JSContext *cx);
|
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
|
* 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,
|
* in the middle of an inline call, that call will be expanded. To avoid this,
|
||||||
|
|
|
@ -87,7 +87,7 @@ GetGlobalForScopeChain(JSContext *cx)
|
||||||
inline GSNCache *
|
inline GSNCache *
|
||||||
GetGSNCache(JSContext *cx)
|
GetGSNCache(JSContext *cx)
|
||||||
{
|
{
|
||||||
return &JS_THREAD_DATA(cx)->gsnCache;
|
return &cx->runtime->gsnCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AutoNamespaceArray : protected AutoGCRooter {
|
class AutoNamespaceArray : protected AutoGCRooter {
|
||||||
|
|
|
@ -561,10 +561,10 @@ CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
JSThread *
|
void *
|
||||||
GetContextThread(const JSContext *cx)
|
GetOwnerThread(const JSContext *cx)
|
||||||
{
|
{
|
||||||
return cx->thread();
|
return cx->runtime->ownerThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_FRIEND_API(unsigned)
|
JS_FRIEND_API(unsigned)
|
||||||
|
@ -585,18 +585,18 @@ AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx
|
||||||
{
|
{
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
|
||||||
ThreadData &threadData = context->thread()->data;
|
JSRuntime *rt = context->runtime;
|
||||||
JS_ASSERT(threadData.requestDepth >= 1);
|
JS_ASSERT(rt->requestDepth >= 1);
|
||||||
JS_ASSERT(!threadData.conservativeGC.requestThreshold);
|
JS_ASSERT(!rt->conservativeGC.requestThreshold);
|
||||||
if (threadData.requestDepth == 1)
|
if (rt->requestDepth == 1)
|
||||||
threadData.conservativeGC.requestThreshold = 1;
|
rt->conservativeGC.requestThreshold = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoSkipConservativeScan::~AutoSkipConservativeScan()
|
AutoSkipConservativeScan::~AutoSkipConservativeScan()
|
||||||
{
|
{
|
||||||
ThreadData &threadData = context->thread()->data;
|
JSRuntime *rt = context->runtime;
|
||||||
if (threadData.requestDepth == 1)
|
if (rt->requestDepth == 1)
|
||||||
threadData.conservativeGC.requestThreshold = 0;
|
rt->conservativeGC.requestThreshold = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -626,12 +626,9 @@ IsContextRunningJS(JSContext *cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_FRIEND_API(void)
|
JS_FRIEND_API(void)
|
||||||
TriggerOperationCallbacksForActiveContexts(JSRuntime *rt)
|
TriggerOperationCallback(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
JSContext* cx = NULL;
|
rt->triggerOperationCallback();
|
||||||
while ((cx = js_NextActiveContext(rt, cx))) {
|
|
||||||
TriggerOperationCallback(cx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_FRIEND_API(const CompartmentVector&)
|
JS_FRIEND_API(const CompartmentVector&)
|
||||||
|
|
|
@ -475,8 +475,8 @@ JS_FRIEND_API(JSString *)
|
||||||
GetPCCountScriptContents(JSContext *cx, size_t script);
|
GetPCCountScriptContents(JSContext *cx, size_t script);
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
JS_FRIEND_API(JSThread *)
|
JS_FRIEND_API(void *)
|
||||||
GetContextThread(const JSContext *cx);
|
GetOwnerThread(const JSContext *cx);
|
||||||
|
|
||||||
JS_FRIEND_API(unsigned)
|
JS_FRIEND_API(unsigned)
|
||||||
GetContextOutstandingRequests(const JSContext *cx);
|
GetContextOutstandingRequests(const JSContext *cx);
|
||||||
|
@ -562,7 +562,7 @@ IsContextRunningJS(JSContext *cx);
|
||||||
|
|
||||||
/* Must be called with GC lock taken. */
|
/* Must be called with GC lock taken. */
|
||||||
extern JS_FRIEND_API(void)
|
extern JS_FRIEND_API(void)
|
||||||
TriggerOperationCallbacksForActiveContexts(JSRuntime *rt);
|
TriggerOperationCallback(JSRuntime *rt);
|
||||||
|
|
||||||
class SystemAllocPolicy;
|
class SystemAllocPolicy;
|
||||||
typedef Vector<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector;
|
typedef Vector<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector;
|
||||||
|
|
295
js/src/jsgc.cpp
295
js/src/jsgc.cpp
|
@ -106,6 +106,12 @@
|
||||||
# include <valgrind/memcheck.h>
|
# include <valgrind/memcheck.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef XP_WIN
|
||||||
|
# include "jswin.h"
|
||||||
|
#else
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::gc;
|
using namespace js::gc;
|
||||||
|
@ -875,15 +881,9 @@ js_InitGC(JSRuntime *rt, uint32_t maxbytes)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
rt->gcLock = JS_NEW_LOCK();
|
rt->gcLock = PR_NewLock();
|
||||||
if (!rt->gcLock)
|
if (!rt->gcLock)
|
||||||
return false;
|
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())
|
if (!rt->gcHelperThread.init())
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1079,23 +1079,31 @@ MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t *
|
||||||
MarkWordConservatively(trc, *i);
|
MarkWordConservatively(trc, *i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static JS_NEVER_INLINE void
|
||||||
MarkThreadDataConservatively(JSTracer *trc, ThreadData *td)
|
MarkConservativeStackRoots(JSTracer *trc, JSRuntime *rt)
|
||||||
{
|
{
|
||||||
ConservativeGCThreadData *ctd = &td->conservativeGC;
|
ConservativeGCData *cgcd = &rt->conservativeGC;
|
||||||
JS_ASSERT(ctd->hasStackToScan());
|
if (!cgcd->hasStackToScan()) {
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
JS_ASSERT(!rt->suspendCount);
|
||||||
|
JS_ASSERT(rt->requestDepth <= cgcd->requestThreshold);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uintptr_t *stackMin, *stackEnd;
|
uintptr_t *stackMin, *stackEnd;
|
||||||
#if JS_STACK_GROWTH_DIRECTION > 0
|
#if JS_STACK_GROWTH_DIRECTION > 0
|
||||||
stackMin = td->nativeStackBase;
|
stackMin = rt->conservativeGC.nativeStackBase;
|
||||||
stackEnd = ctd->nativeStackTop;
|
stackEnd = cgcd->nativeStackTop;
|
||||||
#else
|
#else
|
||||||
stackMin = ctd->nativeStackTop + 1;
|
stackMin = cgcd->nativeStackTop + 1;
|
||||||
stackEnd = td->nativeStackBase;
|
stackEnd = rt->conservativeGC.nativeStackBase;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
JS_ASSERT(stackMin <= stackEnd);
|
JS_ASSERT(stackMin <= stackEnd);
|
||||||
MarkRangeConservatively(trc, stackMin, stackEnd);
|
MarkRangeConservatively(trc, stackMin, stackEnd);
|
||||||
MarkRangeConservatively(trc, ctd->registerSnapshot.words,
|
MarkRangeConservatively(trc, cgcd->registerSnapshot.words,
|
||||||
ArrayEnd(ctd->registerSnapshot.words));
|
ArrayEnd(cgcd->registerSnapshot.words));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1134,36 +1142,16 @@ MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv)
|
||||||
#endif
|
#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
|
JS_NEVER_INLINE void
|
||||||
ConservativeGCThreadData::recordStackTop()
|
ConservativeGCData::recordStackTop()
|
||||||
{
|
{
|
||||||
/* Update the native stack pointer if it points to a bigger stack. */
|
/* Update the native stack pointer if it points to a bigger stack. */
|
||||||
uintptr_t dummy;
|
uintptr_t dummy;
|
||||||
nativeStackTop = &dummy;
|
nativeStackTop = &dummy;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To record and update the register snapshot for the conservative
|
* To record and update the register snapshot for the conservative scanning
|
||||||
* scanning with the latest values we use setjmp.
|
* with the latest values we use setjmp.
|
||||||
*/
|
*/
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
# pragma warning(push)
|
# pragma warning(push)
|
||||||
|
@ -1175,18 +1163,18 @@ ConservativeGCThreadData::recordStackTop()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
void
|
||||||
RecordNativeStackTopForGC(JSContext *cx)
|
RecordNativeStackTopForGC(JSContext *cx)
|
||||||
{
|
{
|
||||||
ConservativeGCThreadData *ctd = &JS_THREAD_DATA(cx)->conservativeGC;
|
ConservativeGCData *cgcd = &cx->runtime->conservativeGC;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
/* Record the stack top here only if we are called from a request. */
|
/* Record the stack top here only if we are called from a request. */
|
||||||
JS_ASSERT(cx->thread()->data.requestDepth >= ctd->requestThreshold);
|
JS_ASSERT(cx->runtime->requestDepth >= cgcd->requestThreshold);
|
||||||
if (cx->thread()->data.requestDepth == ctd->requestThreshold)
|
if (cx->runtime->requestDepth == cgcd->requestThreshold)
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
ctd->recordStackTop();
|
cgcd->recordStackTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
@ -1264,7 +1252,6 @@ js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name)
|
||||||
* rt->gcLock across the mark phase (including the root hashtable mark).
|
* rt->gcLock across the mark phase (including the root hashtable mark).
|
||||||
*/
|
*/
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
js_WaitForGC(rt);
|
|
||||||
|
|
||||||
return !!rt->gcRootsHash.put((void *)vp,
|
return !!rt->gcRootsHash.put((void *)vp,
|
||||||
RootInfo(name, JS_GC_ROOT_VALUE_PTR));
|
RootInfo(name, JS_GC_ROOT_VALUE_PTR));
|
||||||
|
@ -1281,7 +1268,6 @@ js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name)
|
||||||
* rt->gcLock across the mark phase (including the root hashtable mark).
|
* rt->gcLock across the mark phase (including the root hashtable mark).
|
||||||
*/
|
*/
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
js_WaitForGC(rt);
|
|
||||||
|
|
||||||
return !!rt->gcRootsHash.put((void *)rp,
|
return !!rt->gcRootsHash.put((void *)rp,
|
||||||
RootInfo(name, JS_GC_ROOT_GCTHING_PTR));
|
RootInfo(name, JS_GC_ROOT_GCTHING_PTR));
|
||||||
|
@ -1295,7 +1281,6 @@ js_RemoveRoot(JSRuntime *rt, void *rp)
|
||||||
* Same synchronization drill as above in js_AddRoot.
|
* Same synchronization drill as above in js_AddRoot.
|
||||||
*/
|
*/
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
js_WaitForGC(rt);
|
|
||||||
rt->gcRootsHash.remove(rp);
|
rt->gcRootsHash.remove(rp);
|
||||||
rt->gcPoke = JS_TRUE;
|
rt->gcPoke = JS_TRUE;
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
|
@ -1660,9 +1645,6 @@ RunLastDitchGC(JSContext *cx)
|
||||||
{
|
{
|
||||||
JSRuntime *rt = cx->runtime;
|
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. */
|
/* The last ditch GC preserves all atoms. */
|
||||||
AutoKeepAtoms keep(rt);
|
AutoKeepAtoms keep(rt);
|
||||||
js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcstats::LASTDITCH);
|
js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcstats::LASTDITCH);
|
||||||
|
@ -2100,7 +2082,7 @@ MarkRuntime(JSTracer *trc)
|
||||||
JSRuntime *rt = trc->runtime;
|
JSRuntime *rt = trc->runtime;
|
||||||
|
|
||||||
if (rt->hasContexts())
|
if (rt->hasContexts())
|
||||||
MarkConservativeStackRoots(trc);
|
MarkConservativeStackRoots(trc, rt);
|
||||||
|
|
||||||
for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
|
for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
|
||||||
gc_root_traversal(trc, r.front());
|
gc_root_traversal(trc, r.front());
|
||||||
|
@ -2141,8 +2123,7 @@ MarkRuntime(JSTracer *trc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ThreadDataIter i(rt); !i.empty(); i.popFront())
|
rt->stackSpace.mark(trc);
|
||||||
i.threadData()->mark(trc);
|
|
||||||
|
|
||||||
/* The embedding can register additional roots here. */
|
/* The embedding can register additional roots here. */
|
||||||
if (JSTraceDataOp op = rt->gcBlackRootsTraceOp)
|
if (JSTraceDataOp op = rt->gcBlackRootsTraceOp)
|
||||||
|
@ -2158,17 +2139,16 @@ MarkRuntime(JSTracer *trc)
|
||||||
void
|
void
|
||||||
TriggerGC(JSRuntime *rt, gcstats::Reason reason)
|
TriggerGC(JSRuntime *rt, gcstats::Reason reason)
|
||||||
{
|
{
|
||||||
|
JS_ASSERT(rt->onOwnerThread());
|
||||||
|
|
||||||
if (rt->gcRunning || rt->gcIsNeeded)
|
if (rt->gcRunning || rt->gcIsNeeded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/* Trigger the GC when it is safe to call an operation callback. */
|
||||||
* Trigger the GC when it is safe to call an operation callback on any
|
|
||||||
* thread.
|
|
||||||
*/
|
|
||||||
rt->gcIsNeeded = true;
|
rt->gcIsNeeded = true;
|
||||||
rt->gcTriggerCompartment = NULL;
|
rt->gcTriggerCompartment = NULL;
|
||||||
rt->gcTriggerReason = reason;
|
rt->gcTriggerReason = reason;
|
||||||
TriggerAllOperationCallbacks(rt);
|
rt->triggerOperationCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -2208,7 +2188,7 @@ TriggerCompartmentGC(JSCompartment *comp, gcstats::Reason reason)
|
||||||
rt->gcIsNeeded = true;
|
rt->gcIsNeeded = true;
|
||||||
rt->gcTriggerCompartment = comp;
|
rt->gcTriggerCompartment = comp;
|
||||||
rt->gcTriggerReason = reason;
|
rt->gcTriggerReason = reason;
|
||||||
TriggerAllOperationCallbacks(comp->rt);
|
comp->rt->triggerOperationCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -2381,6 +2361,23 @@ ExpireChunksAndArenas(JSRuntime *rt, bool shouldShrink)
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#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
|
bool
|
||||||
GCHelperThread::init()
|
GCHelperThread::init()
|
||||||
{
|
{
|
||||||
|
@ -2394,7 +2391,7 @@ GCHelperThread::init()
|
||||||
if (!thread)
|
if (!thread)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
backgroundAllocation = (js_GetCPUCount() >= 2);
|
backgroundAllocation = (GetCPUCount() >= 2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2680,7 +2677,7 @@ BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind)
|
||||||
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
for (GCCompartmentsIter c(rt); !c.done(); c.next())
|
||||||
c->purge(cx);
|
c->purge(cx);
|
||||||
|
|
||||||
js_PurgeThreads(cx);
|
rt->purge(cx);
|
||||||
|
|
||||||
{
|
{
|
||||||
JSContext *iter = NULL;
|
JSContext *iter = NULL;
|
||||||
|
@ -2844,13 +2841,7 @@ SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Perform mark-and-sweep GC. If comp is set, we perform a single-compartment GC. */
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
static void
|
static void
|
||||||
MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind)
|
MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind)
|
||||||
{
|
{
|
||||||
|
@ -2880,74 +2871,6 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind)
|
||||||
SweepPhase(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 {
|
class AutoGCSession {
|
||||||
public:
|
public:
|
||||||
explicit AutoGCSession(JSContext *cx);
|
explicit AutoGCSession(JSContext *cx);
|
||||||
|
@ -2960,88 +2883,27 @@ class AutoGCSession {
|
||||||
void operator=(const AutoGCSession&) MOZ_DELETE;
|
void operator=(const AutoGCSession&) MOZ_DELETE;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/* Start a new GC session. */
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
AutoGCSession::AutoGCSession(JSContext *cx)
|
AutoGCSession::AutoGCSession(JSContext *cx)
|
||||||
: context(cx)
|
: context(cx)
|
||||||
{
|
{
|
||||||
JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
|
JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
|
||||||
JSRuntime *rt = cx->runtime;
|
JSRuntime *rt = cx->runtime;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
if (rt->gcThread && rt->gcThread != cx->thread())
|
|
||||||
LetOtherGCFinish(cx);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JS_ASSERT(!rt->gcRunning);
|
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;
|
rt->gcRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End the current GC session and allow other threads to proceed. */
|
|
||||||
AutoGCSession::~AutoGCSession()
|
AutoGCSession::~AutoGCSession()
|
||||||
{
|
{
|
||||||
JSRuntime *rt = context->runtime;
|
JSRuntime *rt = context->runtime;
|
||||||
rt->gcRunning = false;
|
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
|
* 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
|
* garbage. We disable inlining to ensure that the bottom of the stack with
|
||||||
* to ensure that the bottom of the stack with possible GC roots recorded in
|
* possible GC roots recorded in js_GC excludes any pointers we use during the
|
||||||
* js_GC excludes any pointers we use during the marking implementation.
|
* marking implementation.
|
||||||
*/
|
*/
|
||||||
static JS_NEVER_INLINE void
|
static JS_NEVER_INLINE void
|
||||||
GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
||||||
|
@ -3051,29 +2913,13 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
||||||
JS_ASSERT_IF(comp, comp != rt->atomsCompartment);
|
JS_ASSERT_IF(comp, comp != rt->atomsCompartment);
|
||||||
JS_ASSERT_IF(comp, rt->gcMode == JSGC_MODE_COMPARTMENT);
|
JS_ASSERT_IF(comp, rt->gcMode == JSGC_MODE_COMPARTMENT);
|
||||||
|
|
||||||
/*
|
/* Recursive GC is no-op. */
|
||||||
* Recursive GC is no-op and a call from another thread waits the started
|
if (rt->gcMarkAndSweep)
|
||||||
* 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
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
AutoGCSession gcsession(cx);
|
AutoGCSession gcsession(cx);
|
||||||
|
|
||||||
/*
|
/* Don't GC if we are reporting an OOM. */
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
if (rt->inOOMReport)
|
if (rt->inOOMReport)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -3103,9 +2949,6 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
||||||
|
|
||||||
MarkAndSweep(cx, gckind);
|
MarkAndSweep(cx, gckind);
|
||||||
|
|
||||||
if (!comp)
|
|
||||||
js_PurgeThreads_PostGlobalSweep(cx);
|
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
if (cx->gcBackgroundFree) {
|
if (cx->gcBackgroundFree) {
|
||||||
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
|
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
|
||||||
|
@ -3216,7 +3059,7 @@ TraceRuntime(JSTracer *trc)
|
||||||
{
|
{
|
||||||
JSContext *cx = trc->context;
|
JSContext *cx = trc->context;
|
||||||
JSRuntime *rt = cx->runtime;
|
JSRuntime *rt = cx->runtime;
|
||||||
if (rt->gcThread != cx->thread()) {
|
if (!rt->gcRunning) {
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
AutoGCSession gcsession(cx);
|
AutoGCSession gcsession(cx);
|
||||||
|
|
||||||
|
|
|
@ -1320,11 +1320,6 @@ typedef HashMap<Value, Value, WrapperHasher, SystemAllocPolicy> WrapperMap;
|
||||||
|
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
extern bool
|
|
||||||
CheckAllocation(JSContext *cx);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern JS_FRIEND_API(JSGCTraceKind)
|
extern JS_FRIEND_API(JSGCTraceKind)
|
||||||
js_GetGCThingTraceKind(void *thing);
|
js_GetGCThingTraceKind(void *thing);
|
||||||
|
|
||||||
|
@ -1416,22 +1411,6 @@ typedef enum JSGCInvocationKind {
|
||||||
extern void
|
extern void
|
||||||
js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcstats::Reason r);
|
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 {
|
namespace js {
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
@ -1585,57 +1564,6 @@ struct GCChunkHasher {
|
||||||
|
|
||||||
typedef HashSet<js::gc::Chunk *, GCChunkHasher, SystemAllocPolicy> GCChunkSet;
|
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>
|
template<class T>
|
||||||
struct MarkStack {
|
struct MarkStack {
|
||||||
T *stack;
|
T *stack;
|
||||||
|
|
|
@ -346,7 +346,7 @@ class CellIter: public CellIterImpl
|
||||||
lists->copyFreeListToArena(kind);
|
lists->copyFreeListToArena(kind);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
counter = &JS_THREAD_DATA(cx)->noGCOrAllocationCheck;
|
counter = &cx->runtime->noGCOrAllocationCheck;
|
||||||
++*counter;
|
++*counter;
|
||||||
#endif
|
#endif
|
||||||
init(comp, kind);
|
init(comp, kind);
|
||||||
|
@ -384,7 +384,7 @@ NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
|
||||||
kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
|
kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
|
||||||
#endif
|
#endif
|
||||||
JS_ASSERT(!cx->runtime->gcRunning);
|
JS_ASSERT(!cx->runtime->gcRunning);
|
||||||
JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
|
JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
#ifdef JS_GC_ZEAL
|
||||||
if (cx->runtime->needZealousGC())
|
if (cx->runtime->needZealousGC())
|
||||||
|
@ -411,7 +411,7 @@ TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
|
||||||
kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
|
kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
|
||||||
#endif
|
#endif
|
||||||
JS_ASSERT(!cx->runtime->gcRunning);
|
JS_ASSERT(!cx->runtime->gcRunning);
|
||||||
JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
|
JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
#ifdef JS_GC_ZEAL
|
||||||
if (cx->runtime->needZealousGC())
|
if (cx->runtime->needZealousGC())
|
||||||
|
|
|
@ -1258,13 +1258,13 @@ inline InterpreterFrames::InterpreterFrames(JSContext *cx, FrameRegs *regs,
|
||||||
const InterruptEnablerBase &enabler)
|
const InterruptEnablerBase &enabler)
|
||||||
: context(cx), regs(regs), enabler(enabler)
|
: context(cx), regs(regs), enabler(enabler)
|
||||||
{
|
{
|
||||||
older = JS_THREAD_DATA(cx)->interpreterFrames;
|
older = cx->runtime->interpreterFrames;
|
||||||
JS_THREAD_DATA(cx)->interpreterFrames = this;
|
cx->runtime->interpreterFrames = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline InterpreterFrames::~InterpreterFrames()
|
inline InterpreterFrames::~InterpreterFrames()
|
||||||
{
|
{
|
||||||
JS_THREAD_DATA(context)->interpreterFrames = older;
|
context->runtime->interpreterFrames = older;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(DEBUG) && !defined(JS_THREADSAFE)
|
#if defined(DEBUG) && !defined(JS_THREADSAFE)
|
||||||
|
@ -1554,7 +1554,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
|
||||||
*/
|
*/
|
||||||
#define CHECK_BRANCH() \
|
#define CHECK_BRANCH() \
|
||||||
JS_BEGIN_MACRO \
|
JS_BEGIN_MACRO \
|
||||||
if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
|
if (cx->runtime->interrupt && !js_HandleExecutionInterrupt(cx)) \
|
||||||
goto error; \
|
goto error; \
|
||||||
JS_END_MACRO
|
JS_END_MACRO
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
|
181
js/src/jslock.h
181
js/src/jslock.h
|
@ -39,185 +39,34 @@
|
||||||
#ifndef jslock_h__
|
#ifndef jslock_h__
|
||||||
#define jslock_h__
|
#define jslock_h__
|
||||||
|
|
||||||
#include "jstypes.h"
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jsprvtd.h"
|
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
|
||||||
# include "pratom.h"
|
# include "pratom.h"
|
||||||
# include "prlock.h"
|
# include "prlock.h"
|
||||||
# include "prcvar.h"
|
# include "prcvar.h"
|
||||||
# include "prthread.h"
|
# include "prthread.h"
|
||||||
# include "prinit.h"
|
# include "prinit.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef 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))
|
||||||
|
|
||||||
#if (defined(_WIN32) && defined(_M_IX86)) || \
|
#else /* JS_THREADSAFE */
|
||||||
(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 JS_ATOMIC_INCREMENT(p) (++*(p))
|
||||||
# define NSPR_LOCK 1
|
# define JS_ATOMIC_DECREMENT(p) (--*(p))
|
||||||
#else
|
# define JS_ATOMIC_ADD(p,v) (*(p) += (v))
|
||||||
# undef NSPR_LOCK
|
# define JS_ATOMIC_SET(p,v) (*(p) = (v))
|
||||||
#endif
|
|
||||||
|
|
||||||
#define Thin_GetWait(W) ((intptr_t)(W) & 0x1)
|
#endif /* JS_THREADSAFE */
|
||||||
#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) (++*(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
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
class AutoAtomicIncrement
|
||||||
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 {
|
|
||||||
int32_t *p;
|
int32_t *p;
|
||||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
|
||||||
|
@ -233,8 +82,6 @@ class AutoAtomicIncrement {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace js */
|
} /* namespace js */
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* jslock_h___ */
|
#endif /* jslock_h___ */
|
||||||
|
|
|
@ -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
|
|
|
@ -108,7 +108,7 @@ ComputeAccurateDecimalInteger(JSContext *cx, const jschar *start, const jschar *
|
||||||
|
|
||||||
char *estr;
|
char *estr;
|
||||||
int err = 0;
|
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) {
|
if (err == JS_DTOA_ENOMEM) {
|
||||||
JS_ReportOutOfMemory(cx);
|
JS_ReportOutOfMemory(cx);
|
||||||
cx->free_(cstr);
|
cx->free_(cstr);
|
||||||
|
@ -816,7 +816,7 @@ num_to(JSContext *cx, Native native, JSDToStrMode zeroArgMode, JSDToStrMode oneA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
oneArgMode, (jsint)precision + precisionOffset, d);
|
||||||
if (!numStr) {
|
if (!numStr) {
|
||||||
JS_ReportOutOfMemory(cx);
|
JS_ReportOutOfMemory(cx);
|
||||||
|
@ -1087,10 +1087,10 @@ FracNumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base =
|
||||||
*/
|
*/
|
||||||
numStr = v8::internal::DoubleToCString(d, cbuf->sbuf, cbuf->sbufSize);
|
numStr = v8::internal::DoubleToCString(d, cbuf->sbuf, cbuf->sbufSize);
|
||||||
if (!numStr)
|
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);
|
DTOSTR_STANDARD, 0, d);
|
||||||
} else {
|
} 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;
|
return numStr;
|
||||||
}
|
}
|
||||||
|
@ -1421,7 +1421,7 @@ js_strtod(JSContext *cx, const jschar *s, const jschar *send,
|
||||||
estr = istr + 8;
|
estr = istr + 8;
|
||||||
} else {
|
} else {
|
||||||
int err;
|
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)
|
if (d == HUGE_VAL)
|
||||||
d = js_PositiveInfinity;
|
d = js_PositiveInfinity;
|
||||||
else if (d == -HUGE_VAL)
|
else if (d == -HUGE_VAL)
|
||||||
|
|
|
@ -1519,21 +1519,6 @@ struct JSObject_Slots16 : JSObject { js::Value fslots[16]; };
|
||||||
|
|
||||||
#define JSSLOT_FREE(clasp) JSCLASS_RESERVED_SLOTS(clasp)
|
#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 {
|
class JSValueArray {
|
||||||
public:
|
public:
|
||||||
jsval *array;
|
jsval *array;
|
||||||
|
|
|
@ -462,7 +462,7 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes)
|
||||||
return true;
|
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>");
|
char *source = JS_sprintf_append(NULL, "<value>");
|
||||||
if (!source)
|
if (!source)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -83,7 +83,7 @@ GetFunctionProxyConstruct(JSObject *proxy)
|
||||||
static bool
|
static bool
|
||||||
OperationInProgress(JSContext *cx, JSObject *proxy)
|
OperationInProgress(JSContext *cx, JSObject *proxy)
|
||||||
{
|
{
|
||||||
PendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
|
PendingProxyOperation *op = cx->runtime->pendingProxyOperation;
|
||||||
while (op) {
|
while (op) {
|
||||||
if (op->object == proxy)
|
if (op->object == proxy)
|
||||||
return true;
|
return true;
|
||||||
|
@ -710,18 +710,18 @@ ScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value
|
||||||
ScriptedProxyHandler ScriptedProxyHandler::singleton;
|
ScriptedProxyHandler ScriptedProxyHandler::singleton;
|
||||||
|
|
||||||
class AutoPendingProxyOperation {
|
class AutoPendingProxyOperation {
|
||||||
ThreadData *data;
|
JSRuntime *rt;
|
||||||
PendingProxyOperation op;
|
PendingProxyOperation op;
|
||||||
public:
|
public:
|
||||||
AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : data(JS_THREAD_DATA(cx)) {
|
AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : rt(cx->runtime) {
|
||||||
op.next = data->pendingProxyOperation;
|
op.next = rt->pendingProxyOperation;
|
||||||
op.object = proxy;
|
op.object = proxy;
|
||||||
data->pendingProxyOperation = &op;
|
rt->pendingProxyOperation = &op;
|
||||||
}
|
}
|
||||||
|
|
||||||
~AutoPendingProxyOperation() {
|
~AutoPendingProxyOperation() {
|
||||||
JS_ASSERT(data->pendingProxyOperation == &op);
|
JS_ASSERT(rt->pendingProxyOperation == &op);
|
||||||
data->pendingProxyOperation = op.next;
|
rt->pendingProxyOperation = op.next;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,6 @@ typedef struct JSGenerator JSGenerator;
|
||||||
typedef struct JSNativeEnumerator JSNativeEnumerator;
|
typedef struct JSNativeEnumerator JSNativeEnumerator;
|
||||||
typedef struct JSProperty JSProperty;
|
typedef struct JSProperty JSProperty;
|
||||||
typedef struct JSSharpObjectMap JSSharpObjectMap;
|
typedef struct JSSharpObjectMap JSSharpObjectMap;
|
||||||
typedef struct JSThread JSThread;
|
|
||||||
typedef struct JSTryNote JSTryNote;
|
typedef struct JSTryNote JSTryNote;
|
||||||
|
|
||||||
/* Friend "Advanced API" typedefs. */
|
/* Friend "Advanced API" typedefs. */
|
||||||
|
|
|
@ -781,7 +781,7 @@ JSScript::initCounts(JSContext *cx)
|
||||||
|
|
||||||
/* Enable interrupts in any interpreter frames running on this script. */
|
/* Enable interrupts in any interpreter frames running on this script. */
|
||||||
InterpreterFrames *frames;
|
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);
|
frames->enableInterruptsIfRunning(this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1735,7 +1735,7 @@ JSScript::ensureHasDebug(JSContext *cx)
|
||||||
* debug state is destroyed.
|
* debug state is destroyed.
|
||||||
*/
|
*/
|
||||||
InterpreterFrames *frames;
|
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);
|
frames->enableInterruptsIfRunning(this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -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
|
|
||||||
!
|
|
||||||
! ======================================================================
|
|
||||||
!
|
|
|
@ -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
|
|
||||||
!
|
|
||||||
! ======================================================================
|
|
||||||
!
|
|
|
@ -3933,19 +3933,7 @@ mjit::Compiler::interruptCheckHelper()
|
||||||
/* For barrier verification, always take the interrupt so we can verify. */
|
/* For barrier verification, always take the interrupt so we can verify. */
|
||||||
jump = masm.jump();
|
jump = masm.jump();
|
||||||
} else {
|
} else {
|
||||||
/*
|
void *interrupt = (void*) &cx->runtime->interrupt;
|
||||||
* 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
|
|
||||||
|
|
||||||
#if defined(JS_CPU_X86) || defined(JS_CPU_ARM) || defined(JS_CPU_MIPS)
|
#if defined(JS_CPU_X86) || defined(JS_CPU_ARM) || defined(JS_CPU_MIPS)
|
||||||
jump = masm.branch32(Assembler::NotEqual, AbsoluteAddress(interrupt), Imm32(0));
|
jump = masm.branch32(Assembler::NotEqual, AbsoluteAddress(interrupt), Imm32(0));
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -1897,7 +1897,7 @@ stubs::Exception(VMFrame &f)
|
||||||
{
|
{
|
||||||
// Check the interrupt flag to allow interrupting deeply nested exception
|
// Check the interrupt flag to allow interrupting deeply nested exception
|
||||||
// handling.
|
// handling.
|
||||||
if (JS_THREAD_DATA(f.cx)->interruptFlags && !js_HandleExecutionInterrupt(f.cx))
|
if (f.cx->runtime->interrupt && !js_HandleExecutionInterrupt(f.cx))
|
||||||
THROW();
|
THROW();
|
||||||
|
|
||||||
f.regs.sp[0] = f.cx->getPendingException();
|
f.regs.sp[0] = f.cx->getPendingException();
|
||||||
|
|
|
@ -3455,7 +3455,7 @@ CancelExecution(JSRuntime *rt)
|
||||||
if (gWorkerThreadPool)
|
if (gWorkerThreadPool)
|
||||||
js::workers::terminateAll(gWorkerThreadPool);
|
js::workers::terminateAll(gWorkerThreadPool);
|
||||||
#endif
|
#endif
|
||||||
JS_TriggerAllOperationCallbacks(rt);
|
JS_TriggerRuntimeOperationCallback(rt);
|
||||||
|
|
||||||
static const char msg[] = "Script runs for too long, terminating.\n";
|
static const char msg[] = "Script runs for too long, terminating.\n";
|
||||||
#if defined(XP_UNIX) && !defined(JS_THREADSAFE)
|
#if defined(XP_UNIX) && !defined(JS_THREADSAFE)
|
||||||
|
|
|
@ -55,6 +55,16 @@
|
||||||
|
|
||||||
extern size_t gMaxStackSize;
|
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.
|
* JavaScript shell workers.
|
||||||
*
|
*
|
||||||
|
@ -150,7 +160,7 @@ class WorkerParent {
|
||||||
bool initWorkerParent() { return children.init(8); }
|
bool initWorkerParent() { return children.init(8); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual JSLock *getLock() = 0;
|
virtual PRLock *getLock() = 0;
|
||||||
virtual ThreadPool *getThreadPool() = 0;
|
virtual ThreadPool *getThreadPool() = 0;
|
||||||
virtual bool post(Event *item) = 0; // false on OOM or queue closed
|
virtual bool post(Event *item) = 0; // false on OOM or queue closed
|
||||||
virtual void trace(JSTracer *trc) = 0;
|
virtual void trace(JSTracer *trc) = 0;
|
||||||
|
@ -177,7 +187,7 @@ class ThreadSafeQueue
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
Queue<T, SystemAllocPolicy> queue;
|
Queue<T, SystemAllocPolicy> queue;
|
||||||
JSLock *lock;
|
PRLock *lock;
|
||||||
PRCondVar *condvar;
|
PRCondVar *condvar;
|
||||||
bool closed;
|
bool closed;
|
||||||
|
|
||||||
|
@ -189,9 +199,9 @@ class ThreadSafeQueue
|
||||||
|
|
||||||
~ThreadSafeQueue() {
|
~ThreadSafeQueue() {
|
||||||
if (condvar)
|
if (condvar)
|
||||||
JS_DESTROY_CONDVAR(condvar);
|
PR_DestroyCondVar(condvar);
|
||||||
if (lock)
|
if (lock)
|
||||||
JS_DESTROY_LOCK(lock);
|
PR_DestroyLock(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by take() with the lock held.
|
// Called by take() with the lock held.
|
||||||
|
@ -201,7 +211,7 @@ class ThreadSafeQueue
|
||||||
bool initThreadSafeQueue() {
|
bool initThreadSafeQueue() {
|
||||||
JS_ASSERT(!lock);
|
JS_ASSERT(!lock);
|
||||||
JS_ASSERT(!condvar);
|
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) {
|
bool post(T t) {
|
||||||
|
@ -209,7 +219,7 @@ class ThreadSafeQueue
|
||||||
if (closed)
|
if (closed)
|
||||||
return false;
|
return false;
|
||||||
if (queue.empty())
|
if (queue.empty())
|
||||||
JS_NOTIFY_ALL_CONDVAR(condvar);
|
PR_NotifyAllCondVar(condvar);
|
||||||
return queue.push(t);
|
return queue.push(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +227,7 @@ class ThreadSafeQueue
|
||||||
AutoLock hold(lock);
|
AutoLock hold(lock);
|
||||||
closed = true;
|
closed = true;
|
||||||
queue.clear();
|
queue.clear();
|
||||||
JS_NOTIFY_ALL_CONDVAR(condvar);
|
PR_NotifyAllCondVar(condvar);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The caller must hold the lock.
|
// The caller must hold the lock.
|
||||||
|
@ -225,7 +235,7 @@ class ThreadSafeQueue
|
||||||
while (queue.empty()) {
|
while (queue.empty()) {
|
||||||
if (shouldStop())
|
if (shouldStop())
|
||||||
return false;
|
return false;
|
||||||
JS_WAIT_CONDVAR(condvar, JS_NO_TIMEOUT);
|
PR_WaitCondVar(condvar, PR_INTERVAL_NO_TIMEOUT);
|
||||||
}
|
}
|
||||||
*t = queue.pop();
|
*t = queue.pop();
|
||||||
busy.append(*t);
|
busy.append(*t);
|
||||||
|
@ -253,7 +263,7 @@ class ThreadSafeQueue
|
||||||
|
|
||||||
void wake() {
|
void wake() {
|
||||||
AutoLock hold(lock);
|
AutoLock hold(lock);
|
||||||
JS_NOTIFY_ALL_CONDVAR(condvar);
|
PR_NotifyAllCondVar(condvar);
|
||||||
}
|
}
|
||||||
|
|
||||||
void trace(JSTracer *trc) {
|
void trace(JSTracer *trc) {
|
||||||
|
@ -371,7 +381,7 @@ class MainQueue MOZ_FINAL : public EventQueue, public WorkerParent
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual JSLock *getLock() { return lock; }
|
virtual PRLock *getLock() { return lock; }
|
||||||
virtual ThreadPool *getThreadPool() { return threadPool; }
|
virtual ThreadPool *getThreadPool() { return threadPool; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -390,7 +400,7 @@ class MainQueue MOZ_FINAL : public EventQueue, public WorkerParent
|
||||||
|
|
||||||
Event *event;
|
Event *event;
|
||||||
while (take(&event)) {
|
while (take(&event)) {
|
||||||
JS_RELEASE_LOCK(lock);
|
PR_Unlock(lock);
|
||||||
Event::Result result;
|
Event::Result result;
|
||||||
{
|
{
|
||||||
JSAutoRequest req(cx);
|
JSAutoRequest req(cx);
|
||||||
|
@ -414,7 +424,7 @@ class MainQueue MOZ_FINAL : public EventQueue, public WorkerParent
|
||||||
result = Event::ok;
|
result = Event::ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JS_ACQUIRE_LOCK(lock);
|
PR_Lock(lock);
|
||||||
drop(event);
|
drop(event);
|
||||||
event->destroy(cx);
|
event->destroy(cx);
|
||||||
if (result != Event::ok)
|
if (result != Event::ok)
|
||||||
|
@ -596,7 +606,7 @@ class Worker MOZ_FINAL : public WorkerParent
|
||||||
JSObject *object; // Worker object exposed to parent
|
JSObject *object; // Worker object exposed to parent
|
||||||
JSRuntime *runtime;
|
JSRuntime *runtime;
|
||||||
JSContext *context;
|
JSContext *context;
|
||||||
JSLock *lock;
|
PRLock *lock;
|
||||||
Queue<Event *, SystemAllocPolicy> events; // owning pointers to pending events
|
Queue<Event *, SystemAllocPolicy> events; // owning pointers to pending events
|
||||||
Event *current;
|
Event *current;
|
||||||
bool terminated;
|
bool terminated;
|
||||||
|
@ -616,7 +626,7 @@ class Worker MOZ_FINAL : public WorkerParent
|
||||||
threadPool = parent->getThreadPool();
|
threadPool = parent->getThreadPool();
|
||||||
this->parent = parent;
|
this->parent = parent;
|
||||||
this->object = obj;
|
this->object = obj;
|
||||||
lock = JS_NEW_LOCK();
|
lock = PR_NewLock();
|
||||||
return lock &&
|
return lock &&
|
||||||
createRuntime(parentcx) &&
|
createRuntime(parentcx) &&
|
||||||
createContext(parentcx, parent) &&
|
createContext(parentcx, parent) &&
|
||||||
|
@ -681,7 +691,6 @@ class Worker MOZ_FINAL : public WorkerParent
|
||||||
js::SetFunctionNativeReserved(post, 0, PRIVATE_TO_JSVAL(this));
|
js::SetFunctionNativeReserved(post, 0, PRIVATE_TO_JSVAL(this));
|
||||||
|
|
||||||
JS_EndRequest(context);
|
JS_EndRequest(context);
|
||||||
JS_ClearContextThread(context);
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
bad:
|
bad:
|
||||||
|
@ -769,13 +778,12 @@ class Worker MOZ_FINAL : public WorkerParent
|
||||||
while (!events.empty())
|
while (!events.empty())
|
||||||
events.pop()->destroy(context);
|
events.pop()->destroy(context);
|
||||||
if (lock) {
|
if (lock) {
|
||||||
JS_DESTROY_LOCK(lock);
|
PR_DestroyLock(lock);
|
||||||
lock = NULL;
|
lock = NULL;
|
||||||
}
|
}
|
||||||
if (runtime)
|
if (runtime)
|
||||||
JS_SetRuntimeThread(runtime);
|
JS_SetRuntimeThread(runtime);
|
||||||
if (context) {
|
if (context) {
|
||||||
JS_SetContextThread(context);
|
|
||||||
JS_DestroyContextNoGC(context);
|
JS_DestroyContextNoGC(context);
|
||||||
context = NULL;
|
context = NULL;
|
||||||
}
|
}
|
||||||
|
@ -801,7 +809,7 @@ class Worker MOZ_FINAL : public WorkerParent
|
||||||
|
|
||||||
WorkerParent *getParent() { return parent; }
|
WorkerParent *getParent() { return parent; }
|
||||||
|
|
||||||
virtual JSLock *getLock() { return lock; }
|
virtual PRLock *getLock() { return lock; }
|
||||||
|
|
||||||
virtual ThreadPool *getThreadPool() { return threadPool; }
|
virtual ThreadPool *getThreadPool() { return threadPool; }
|
||||||
|
|
||||||
|
@ -1030,15 +1038,15 @@ WorkerQueue::work() {
|
||||||
|
|
||||||
Worker *w;
|
Worker *w;
|
||||||
while (take(&w)) { // can block outside the mutex
|
while (take(&w)) { // can block outside the mutex
|
||||||
JS_RELEASE_LOCK(lock);
|
PR_Unlock(lock);
|
||||||
w->processOneEvent(); // enters request on w->context
|
w->processOneEvent(); // enters request on w->context
|
||||||
JS_ACQUIRE_LOCK(lock);
|
PR_Lock(lock);
|
||||||
drop(w);
|
drop(w);
|
||||||
|
|
||||||
if (lockedIsIdle()) {
|
if (lockedIsIdle()) {
|
||||||
JS_RELEASE_LOCK(lock);
|
PR_Unlock(lock);
|
||||||
main->wake();
|
main->wake();
|
||||||
JS_ACQUIRE_LOCK(lock);
|
PR_Lock(lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1136,8 +1144,6 @@ Worker::processOneEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_SetRuntimeThread(runtime);
|
JS_SetRuntimeThread(runtime);
|
||||||
JS_SetContextThread(context);
|
|
||||||
JS_SetNativeStackQuota(context, gMaxStackSize);
|
|
||||||
|
|
||||||
Event::Result result;
|
Event::Result result;
|
||||||
{
|
{
|
||||||
|
@ -1171,7 +1177,6 @@ Worker::processOneEvent()
|
||||||
|
|
||||||
if (event)
|
if (event)
|
||||||
event->destroy(context);
|
event->destroy(context);
|
||||||
JS_ClearContextThread(context);
|
|
||||||
JS_ClearRuntimeThread(runtime);
|
JS_ClearRuntimeThread(runtime);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -263,22 +263,12 @@ RegExpObject::setSticky(bool enabled)
|
||||||
|
|
||||||
/* RegExpPrivate inlines. */
|
/* 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
|
inline bool
|
||||||
detail::RegExpPrivate::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
|
detail::RegExpPrivate::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
|
||||||
RegExpPrivateCacheKind targetKind,
|
RegExpPrivateCacheKind targetKind,
|
||||||
AlreadyIncRefed<RegExpPrivate> *result)
|
AlreadyIncRefed<RegExpPrivate> *result)
|
||||||
{
|
{
|
||||||
RegExpPrivateCache *cache = getOrCreateCache(cx);
|
RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx);
|
||||||
if (!cache)
|
if (!cache)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -307,7 +297,7 @@ detail::RegExpPrivate::cacheInsert(JSContext *cx, JSAtom *atom, RegExpPrivateCac
|
||||||
* so we have to re-lookup the cache (and inside the cache) after the
|
* so we have to re-lookup the cache (and inside the cache) after the
|
||||||
* allocation is performed.
|
* allocation is performed.
|
||||||
*/
|
*/
|
||||||
RegExpPrivateCache *cache = getOrCreateCache(cx);
|
RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx);
|
||||||
if (!cache)
|
if (!cache)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -401,7 +391,7 @@ detail::RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, Token
|
||||||
|
|
||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
|
if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
|
||||||
JSC::ExecutableAllocator *execAlloc = cx->threadData()->getOrCreateExecutableAllocator(cx);
|
JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecutableAllocator(cx);
|
||||||
if (!execAlloc) {
|
if (!execAlloc) {
|
||||||
js_ReportOutOfMemory(cx);
|
js_ReportOutOfMemory(cx);
|
||||||
return false;
|
return false;
|
||||||
|
@ -414,7 +404,7 @@ detail::RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, Token
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WTF::BumpPointerAllocator *bumpAlloc = cx->threadData()->getOrCreateBumpPointerAllocator(cx);
|
WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx);
|
||||||
if (!bumpAlloc) {
|
if (!bumpAlloc) {
|
||||||
js_ReportOutOfMemory(cx);
|
js_ReportOutOfMemory(cx);
|
||||||
return false;
|
return false;
|
||||||
|
@ -510,11 +500,13 @@ detail::RegExpPrivate::decref(JSContext *cx)
|
||||||
if (--refCount != 0)
|
if (--refCount != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RegExpPrivateCache *cache;
|
if (RegExpPrivateCache *cache = cx->runtime->maybeRegExpPrivateCache()) {
|
||||||
if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) {
|
if (source->isAtom()) {
|
||||||
RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom());
|
if (RegExpPrivateCache::Ptr p = cache->lookup(&source->asAtom())) {
|
||||||
if (ptr && ptr->value.rep() == this)
|
if (p->value.rep() == this)
|
||||||
cache->remove(ptr);
|
cache->remove(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
|
@ -388,7 +388,6 @@ class RegExpPrivate
|
||||||
createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
|
createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
|
||||||
TokenStream *tokenStream);
|
TokenStream *tokenStream);
|
||||||
|
|
||||||
static RegExpPrivateCache *getOrCreateCache(JSContext *cx);
|
|
||||||
static bool cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
|
static bool cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
|
||||||
RegExpPrivateCacheKind kind, AlreadyIncRefed<RegExpPrivate> *result);
|
RegExpPrivateCacheKind kind, AlreadyIncRefed<RegExpPrivate> *result);
|
||||||
static bool cacheInsert(JSContext *cx, JSAtom *atom,
|
static bool cacheInsert(JSContext *cx, JSAtom *atom,
|
||||||
|
|
|
@ -492,39 +492,15 @@ StackSpace::sizeOfCommitted()
|
||||||
|
|
||||||
ContextStack::ContextStack(JSContext *cx)
|
ContextStack::ContextStack(JSContext *cx)
|
||||||
: seg_(NULL),
|
: seg_(NULL),
|
||||||
space_(&JS_THREAD_DATA(cx)->stackSpace),
|
space_(&cx->runtime->stackSpace),
|
||||||
cx_(cx)
|
cx_(cx)
|
||||||
{
|
{}
|
||||||
threadReset();
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextStack::~ContextStack()
|
ContextStack::~ContextStack()
|
||||||
{
|
{
|
||||||
JS_ASSERT(!seg_);
|
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
|
bool
|
||||||
ContextStack::onTop() const
|
ContextStack::onTop() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -317,7 +317,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JSContext* mContext;
|
JSContext* mContext;
|
||||||
intN mContextThread;
|
|
||||||
nsIThreadJSContextStack* mContextStack;
|
nsIThreadJSContextStack* mContextStack;
|
||||||
char* mBuf;
|
char* mBuf;
|
||||||
|
|
||||||
|
@ -1355,23 +1354,18 @@ mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
|
JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
|
||||||
: mContext(loader->mContext), mContextThread(0),
|
: mContext(loader->mContext),
|
||||||
mContextStack(loader->mContextStack),
|
mContextStack(loader->mContextStack),
|
||||||
mBuf(nsnull)
|
mBuf(nsnull)
|
||||||
{
|
{
|
||||||
mContextStack->Push(mContext);
|
mContextStack->Push(mContext);
|
||||||
mContextThread = JS_GetContextThread(mContext);
|
JS_BeginRequest(mContext);
|
||||||
if (mContextThread) {
|
|
||||||
JS_BeginRequest(mContext);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSCLContextHelper::~JSCLContextHelper()
|
JSCLContextHelper::~JSCLContextHelper()
|
||||||
{
|
{
|
||||||
if (mContextStack) {
|
if (mContextStack) {
|
||||||
if (mContextThread) {
|
JS_EndRequest(mContext);
|
||||||
JS_EndRequest(mContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
mContextStack->Pop(nsnull);
|
mContextStack->Pop(nsnull);
|
||||||
|
|
||||||
|
|
|
@ -946,7 +946,7 @@ XPCJSRuntime::WatchdogMain(void *arg)
|
||||||
#endif
|
#endif
|
||||||
PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval);
|
PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval);
|
||||||
JS_ASSERT(status == PR_SUCCESS);
|
JS_ASSERT(status == PR_SUCCESS);
|
||||||
js::TriggerOperationCallbacksForActiveContexts(self->mJSRuntime);
|
js::TriggerOperationCallback(self->mJSRuntime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wake up the main thread waiting for the watchdog to terminate. */
|
/* Wake up the main thread waiting for the watchdog to terminate. */
|
||||||
|
@ -1043,7 +1043,6 @@ XPCJSRuntime::GetJSCycleCollectionContext()
|
||||||
mJSCycleCollectionContext = JS_NewContext(mJSRuntime, 0);
|
mJSCycleCollectionContext = JS_NewContext(mJSRuntime, 0);
|
||||||
if (!mJSCycleCollectionContext)
|
if (!mJSCycleCollectionContext)
|
||||||
return nsnull;
|
return nsnull;
|
||||||
JS_ClearContextThread(mJSCycleCollectionContext);
|
|
||||||
}
|
}
|
||||||
return mJSCycleCollectionContext;
|
return mJSCycleCollectionContext;
|
||||||
}
|
}
|
||||||
|
@ -1067,10 +1066,8 @@ XPCJSRuntime::~XPCJSRuntime()
|
||||||
mWatchdogWakeup = nsnull;
|
mWatchdogWakeup = nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mJSCycleCollectionContext) {
|
if (mJSCycleCollectionContext)
|
||||||
JS_SetContextThread(mJSCycleCollectionContext);
|
|
||||||
JS_DestroyContextNoGC(mJSCycleCollectionContext);
|
JS_DestroyContextNoGC(mJSCycleCollectionContext);
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef XPC_DUMP_AT_SHUTDOWN
|
#ifdef XPC_DUMP_AT_SHUTDOWN
|
||||||
{
|
{
|
||||||
|
@ -1639,30 +1636,30 @@ ReportJSRuntimeStats(const JS::IterateData &data, const nsACString &pathPrefix,
|
||||||
"hanging off them." SLOP_BYTES_STRING,
|
"hanging off them." SLOP_BYTES_STRING,
|
||||||
callback, closure);
|
callback, closure);
|
||||||
|
|
||||||
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/normal"),
|
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/normal"),
|
||||||
nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsNormal,
|
nsIMemoryReporter::KIND_HEAP, data.runtimeNormal,
|
||||||
"Memory used by JSThread objects and their data, "
|
"Memory used by a JSRuntime, "
|
||||||
"excluding memory that is reported by "
|
"excluding memory that is reported by "
|
||||||
"other reporters under 'explicit/js/runtime/'." SLOP_BYTES_STRING,
|
"other reporters under 'explicit/js/runtime/'." SLOP_BYTES_STRING,
|
||||||
callback, closure);
|
callback, closure);
|
||||||
|
|
||||||
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/temporary"),
|
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"),
|
||||||
nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsTemporary,
|
nsIMemoryReporter::KIND_HEAP, data.runtimeTemporary,
|
||||||
"Memory held transiently in JSThreads and used during "
|
"Memory held transiently in JSRuntime and used during "
|
||||||
"compilation. It mostly holds parse nodes."
|
"compilation. It mostly holds parse nodes."
|
||||||
SLOP_BYTES_STRING,
|
SLOP_BYTES_STRING,
|
||||||
callback, closure);
|
callback, closure);
|
||||||
|
|
||||||
ReportMemoryBytes0(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/regexp-code"),
|
ReportMemoryBytes0(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"),
|
||||||
nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsRegexpCode,
|
nsIMemoryReporter::KIND_NONHEAP, data.runtimeRegexpCode,
|
||||||
"Memory used by the regexp JIT to hold generated code.",
|
"Memory used by the regexp JIT to hold generated code.",
|
||||||
callback, closure);
|
callback, closure);
|
||||||
|
|
||||||
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/stack-committed"),
|
ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"),
|
||||||
nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsStackCommitted,
|
nsIMemoryReporter::KIND_NONHEAP, data.runtimeStackCommitted,
|
||||||
"Memory used for the thread stacks. This is the committed portions "
|
"Memory used for the JS call stack. This is the committed portion "
|
||||||
"of the stacks; any uncommitted portions are not measured because they "
|
"of the stack; the uncommitted portion is not measured because it "
|
||||||
"hardly cost anything.",
|
"hardly costs anything.",
|
||||||
callback, closure);
|
callback, closure);
|
||||||
|
|
||||||
ReportGCHeapBytes(pathPrefix +
|
ReportGCHeapBytes(pathPrefix +
|
||||||
|
@ -1965,9 +1962,9 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
||||||
JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
|
JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
|
||||||
#endif
|
#endif
|
||||||
JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
|
JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
|
||||||
mWatchdogWakeup = JS_NEW_CONDVAR(js::GetRuntimeGCLock(mJSRuntime));
|
mWatchdogWakeup = PR_NewCondVar(js::GetRuntimeGCLock(mJSRuntime));
|
||||||
if (!mWatchdogWakeup)
|
if (!mWatchdogWakeup)
|
||||||
NS_RUNTIMEABORT("JS_NEW_CONDVAR failed.");
|
NS_RUNTIMEABORT("PR_NewCondVar failed.");
|
||||||
|
|
||||||
js::SetActivityCallback(mJSRuntime, ActivityCallback, this);
|
js::SetActivityCallback(mJSRuntime, ActivityCallback, this);
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,6 @@ using namespace mozilla;
|
||||||
XPCJSContextStack::~XPCJSContextStack()
|
XPCJSContextStack::~XPCJSContextStack()
|
||||||
{
|
{
|
||||||
if (mOwnSafeJSContext) {
|
if (mOwnSafeJSContext) {
|
||||||
JS_SetContextThread(mOwnSafeJSContext);
|
|
||||||
JS_DestroyContext(mOwnSafeJSContext);
|
JS_DestroyContext(mOwnSafeJSContext);
|
||||||
mOwnSafeJSContext = nsnull;
|
mOwnSafeJSContext = nsnull;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +108,6 @@ GetPrincipalFromCx(JSContext *cx)
|
||||||
bool
|
bool
|
||||||
XPCJSContextStack::Push(JSContext *cx)
|
XPCJSContextStack::Push(JSContext *cx)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(cx, JS_GetContextThread(cx));
|
|
||||||
if (mStack.Length() == 0) {
|
if (mStack.Length() == 0) {
|
||||||
mStack.AppendElement(cx);
|
mStack.AppendElement(cx);
|
||||||
return true;
|
return true;
|
||||||
|
@ -423,7 +421,7 @@ XPCPerThreadData::GetDataImpl(JSContext *cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cx && !sMainJSThread && NS_IsMainThread()) {
|
if (cx && !sMainJSThread && NS_IsMainThread()) {
|
||||||
sMainJSThread = js::GetContextThread(cx);
|
sMainJSThread = js::GetOwnerThread(cx);
|
||||||
|
|
||||||
sMainThreadData = data;
|
sMainThreadData = data;
|
||||||
|
|
||||||
|
|
|
@ -68,10 +68,8 @@ bool AutoScriptEvaluate::StartEvaluating(JSObject *scope, JSErrorReporter errorR
|
||||||
JS_SetErrorReporter(mJSContext, errorReporter);
|
JS_SetErrorReporter(mJSContext, errorReporter);
|
||||||
mErrorReporterSet = true;
|
mErrorReporterSet = true;
|
||||||
}
|
}
|
||||||
mContextHasThread = JS_GetContextThread(mJSContext);
|
|
||||||
if (mContextHasThread)
|
|
||||||
JS_BeginRequest(mJSContext);
|
|
||||||
|
|
||||||
|
JS_BeginRequest(mJSContext);
|
||||||
if (!mEnterCompartment.enter(mJSContext, scope))
|
if (!mEnterCompartment.enter(mJSContext, scope))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -102,8 +100,7 @@ AutoScriptEvaluate::~AutoScriptEvaluate()
|
||||||
else
|
else
|
||||||
JS_ClearPendingException(mJSContext);
|
JS_ClearPendingException(mJSContext);
|
||||||
|
|
||||||
if (mContextHasThread)
|
JS_EndRequest(mJSContext);
|
||||||
JS_EndRequest(mJSContext);
|
|
||||||
|
|
||||||
// If this is a JSContext that has a private context that provides a
|
// If this is a JSContext that has a private context that provides a
|
||||||
// nsIXPCScriptNotify interface, then notify the object the script has
|
// nsIXPCScriptNotify interface, then notify the object the script has
|
||||||
|
@ -543,8 +540,8 @@ GetContextFromObject(JSObject *obj)
|
||||||
|
|
||||||
if (xpcc) {
|
if (xpcc) {
|
||||||
JSContext *cx = xpcc->GetJSContext();
|
JSContext *cx = xpcc->GetJSContext();
|
||||||
if (JS_GetContextThread(cx) == JS_GetCurrentThread())
|
JS_AbortIfWrongThread(JS_GetRuntime(cx));
|
||||||
return cx;
|
return cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nsnull;
|
return nsnull;
|
||||||
|
|
|
@ -554,14 +554,10 @@ nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
|
||||||
if (!cx)
|
if (!cx)
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
// Clear after mCycleCollectionContext is destroyed
|
|
||||||
JS_SetContextThread(cx);
|
|
||||||
|
|
||||||
NS_ASSERTION(!mCycleCollectionContext, "Didn't call FinishTraverse?");
|
NS_ASSERTION(!mCycleCollectionContext, "Didn't call FinishTraverse?");
|
||||||
mCycleCollectionContext = new XPCCallContext(NATIVE_CALLER, cx);
|
mCycleCollectionContext = new XPCCallContext(NATIVE_CALLER, cx);
|
||||||
if (!mCycleCollectionContext->IsValid()) {
|
if (!mCycleCollectionContext->IsValid()) {
|
||||||
mCycleCollectionContext = nsnull;
|
mCycleCollectionContext = nsnull;
|
||||||
JS_ClearContextThread(cx);
|
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,11 +606,15 @@ nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
nsXPConnect::NotifyLeaveMainThread()
|
nsXPConnect::NotifyLeaveMainThread()
|
||||||
{
|
{
|
||||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
|
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
|
void
|
||||||
|
@ -641,11 +641,8 @@ nsXPConnect::NotifyEnterMainThread()
|
||||||
nsresult
|
nsresult
|
||||||
nsXPConnect::FinishTraverse()
|
nsXPConnect::FinishTraverse()
|
||||||
{
|
{
|
||||||
if (mCycleCollectionContext) {
|
if (mCycleCollectionContext)
|
||||||
JSContext *cx = mCycleCollectionContext->GetJSContext();
|
|
||||||
mCycleCollectionContext = nsnull;
|
mCycleCollectionContext = nsnull;
|
||||||
JS_ClearContextThread(cx);
|
|
||||||
}
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -539,7 +539,7 @@ public:
|
||||||
nsCycleCollectionTraversalCallback &cb);
|
nsCycleCollectionTraversalCallback &cb);
|
||||||
|
|
||||||
// nsCycleCollectionLanguageRuntime
|
// nsCycleCollectionLanguageRuntime
|
||||||
virtual void NotifyLeaveMainThread();
|
virtual bool NotifyLeaveMainThread();
|
||||||
virtual void NotifyEnterCycleCollectionThread();
|
virtual void NotifyEnterCycleCollectionThread();
|
||||||
virtual void NotifyLeaveCycleCollectionThread();
|
virtual void NotifyLeaveCycleCollectionThread();
|
||||||
virtual void NotifyEnterMainThread();
|
virtual void NotifyEnterMainThread();
|
||||||
|
@ -3665,9 +3665,7 @@ public:
|
||||||
JS_Assert("NS_IsMainThread()", __FILE__, __LINE__);
|
JS_Assert("NS_IsMainThread()", __FILE__, __LINE__);
|
||||||
|
|
||||||
if (cx) {
|
if (cx) {
|
||||||
NS_ASSERTION(js::GetContextThread(cx), "Uh, JS context w/o a thread?");
|
if (js::GetOwnerThread(cx) == sMainJSThread)
|
||||||
|
|
||||||
if (js::GetContextThread(cx) == sMainJSThread)
|
|
||||||
return sMainThreadData;
|
return sMainThreadData;
|
||||||
} else if (sMainThreadData && sMainThreadData->mThread == PR_GetCurrentThread()) {
|
} else if (sMainThreadData && sMainThreadData->mThread == PR_GetCurrentThread()) {
|
||||||
return sMainThreadData;
|
return sMainThreadData;
|
||||||
|
@ -3770,7 +3768,7 @@ public:
|
||||||
{sMainJSThread = nsnull; sMainThreadData = nsnull;}
|
{sMainJSThread = nsnull; sMainThreadData = nsnull;}
|
||||||
|
|
||||||
static bool IsMainThread(JSContext *cx)
|
static bool IsMainThread(JSContext *cx)
|
||||||
{ return js::GetContextThread(cx) == sMainJSThread; }
|
{ return js::GetOwnerThread(cx) == sMainJSThread; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
XPCPerThreadData();
|
XPCPerThreadData();
|
||||||
|
|
|
@ -75,8 +75,6 @@ _CHROME_FILES = \
|
||||||
test_getweakmapkeys.xul \
|
test_getweakmapkeys.xul \
|
||||||
test_weakmaps.xul \
|
test_weakmaps.xul \
|
||||||
test_bug706301.xul \
|
test_bug706301.xul \
|
||||||
test_ccbeginfail.xul \
|
|
||||||
test_ccdump.xul \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# Disabled until this test gets updated to test the new proxy based
|
# Disabled until this test gets updated to test the new proxy based
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
|
||||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
|
||||||
<!--
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=710761
|
|
||||||
-->
|
|
||||||
<window title="Mozilla Bug 710761"
|
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
||||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
|
||||||
|
|
||||||
<!-- test results are displayed in the html:body -->
|
|
||||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=710761"
|
|
||||||
target="_blank">Mozilla Bug 710761</a>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<!-- test code goes here -->
|
|
||||||
<script type="application/javascript">
|
|
||||||
<![CDATA[
|
|
||||||
/** Test for Bug 710761 **/
|
|
||||||
|
|
||||||
let noCallbacks = true;
|
|
||||||
|
|
||||||
var beginFailListener = {
|
|
||||||
QueryInterface: function QueryInterface(aIID) {
|
|
||||||
if (aIID.equals(Components.interfaces.nsICycleCollectorListener) ||
|
|
||||||
aIID.equals(Components.interfaces.nsISupports))
|
|
||||||
return this;
|
|
||||||
throw Components.results.NS_NOINTERFACE;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* nsICycleCollectorListener */
|
|
||||||
begin: function () {
|
|
||||||
throw Components.results.NS_ERROR_FAILURE;
|
|
||||||
},
|
|
||||||
noteRefCountedObject: function (addr, rc, descr) {
|
|
||||||
noCallbacks = false;
|
|
||||||
},
|
|
||||||
noteGCedObject: function (addr, marked, descr) {
|
|
||||||
noCallbacks = false;
|
|
||||||
},
|
|
||||||
noteEdge: function (addr, descr) {
|
|
||||||
noCallbacks = false;
|
|
||||||
},
|
|
||||||
beginResults: function () {
|
|
||||||
noCallbacks = false;
|
|
||||||
},
|
|
||||||
describeRoot: function (addr, known) {
|
|
||||||
noCallbacks = false;
|
|
||||||
},
|
|
||||||
describeGarbage: function (addr) {
|
|
||||||
noCallbacks = false;
|
|
||||||
},
|
|
||||||
end: function () {
|
|
||||||
noCallbacks = false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
|
||||||
.cycleCollect(beginFailListener);
|
|
||||||
|
|
||||||
ok(noCallbacks, "If cycle collector listener begin fails, no further callbacks should be called.");
|
|
||||||
|
|
||||||
]]>
|
|
||||||
</script>
|
|
||||||
</window>
|
|
|
@ -1,49 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
|
||||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
|
||||||
<!--
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=709162
|
|
||||||
-->
|
|
||||||
<window title="Mozilla Bug 709162"
|
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
||||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
|
||||||
|
|
||||||
<!-- test results are displayed in the html:body -->
|
|
||||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=709162"
|
|
||||||
target="_blank">Mozilla Bug 709162</a>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<!-- test code goes here -->
|
|
||||||
<script type="application/javascript">
|
|
||||||
<![CDATA[
|
|
||||||
/** Test for Bug 709162 **/
|
|
||||||
|
|
||||||
var emptyListener = {
|
|
||||||
QueryInterface: function QueryInterface(aIID) {
|
|
||||||
if (aIID.equals(Components.interfaces.nsICycleCollectorListener) ||
|
|
||||||
aIID.equals(Components.interfaces.nsISupports))
|
|
||||||
return this;
|
|
||||||
throw Components.results.NS_NOINTERFACE;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* nsICycleCollectorListener */
|
|
||||||
begin: function () {},
|
|
||||||
noteRefCountedObject: function (addr, rc, descr) {},
|
|
||||||
noteGCedObject: function (addr, marked, descr) {},
|
|
||||||
noteEdge: function (addr, descr) {},
|
|
||||||
beginResults: function () {},
|
|
||||||
describeRoot: function (addr, known) {},
|
|
||||||
describeGarbage: function (addr) {},
|
|
||||||
end: function () {},
|
|
||||||
};
|
|
||||||
|
|
||||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
|
||||||
.cycleCollect(emptyListener);
|
|
||||||
|
|
||||||
ok(true, "Dump cycle collector graph without crashing.");
|
|
||||||
|
|
||||||
]]>
|
|
||||||
</script>
|
|
||||||
</window>
|
|
|
@ -3748,10 +3748,13 @@ public:
|
||||||
aListener = nsnull;
|
aListener = nsnull;
|
||||||
mListener = aListener;
|
mListener = aListener;
|
||||||
|
|
||||||
GetJSRuntime()->NotifyLeaveMainThread();
|
if (GetJSRuntime()->NotifyLeaveMainThread()) {
|
||||||
mRequest.Notify();
|
mRequest.Notify();
|
||||||
mReply.Wait();
|
mReply.Wait();
|
||||||
GetJSRuntime()->NotifyEnterMainThread();
|
GetJSRuntime()->NotifyEnterMainThread();
|
||||||
|
} else {
|
||||||
|
mCollected = mCollector->BeginCollection(mListener);
|
||||||
|
}
|
||||||
|
|
||||||
mListener = nsnull;
|
mListener = nsnull;
|
||||||
|
|
||||||
|
|
|
@ -91,8 +91,11 @@ struct nsCycleCollectionJSRuntime : public nsCycleCollectionLanguageRuntime
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Called before/after transitioning to/from the main thread.
|
* 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 NotifyEnterCycleCollectionThread() = 0;
|
||||||
virtual void NotifyLeaveCycleCollectionThread() = 0;
|
virtual void NotifyLeaveCycleCollectionThread() = 0;
|
||||||
virtual void NotifyEnterMainThread() = 0;
|
virtual void NotifyEnterMainThread() = 0;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче