mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 650411 - assert in debug/release builds that JSRuntime is only used in a single-threaded manner (r=jorendorff,sr=dmandelin)
--HG-- extra : rebase_source : b20ddca63da88a8efe2f5dc06364785aebe8b9ea
This commit is contained in:
parent
1350be5c5c
commit
4263a80f79
@ -5347,6 +5347,8 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
|
||||
// Assert that we're on the thread we were created from.
|
||||
JS_ASSERT(cinfo->cxThread == JS_GetContextThread(cx));
|
||||
|
||||
JS_AbortIfWrongThread(JS_GetRuntime(cx));
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
JSAutoEnterCompartment ac;
|
||||
|
@ -80,8 +80,6 @@ CPPSRCS = \
|
||||
testScriptObject.cpp \
|
||||
testSetProperty.cpp \
|
||||
testStringBuffer.cpp \
|
||||
testThreadGC.cpp \
|
||||
testThreads.cpp \
|
||||
testTrap.cpp \
|
||||
testUTF8.cpp \
|
||||
testVersion.cpp \
|
||||
|
@ -1,195 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=99:
|
||||
*/
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
||||
#include "tests.h"
|
||||
#include "prthread.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
/*
|
||||
* We test that if a GC callback cancels the GC on a child thread the GC can
|
||||
* still proceed on the main thread even if the child thread continue to
|
||||
* run uninterrupted.
|
||||
*/
|
||||
|
||||
struct SharedData {
|
||||
enum ChildState {
|
||||
CHILD_STARTING,
|
||||
CHILD_RUNNING,
|
||||
CHILD_DONE,
|
||||
CHILD_ERROR
|
||||
};
|
||||
|
||||
JSRuntime *const runtime;
|
||||
PRThread *const mainThread;
|
||||
PRLock *const lock;
|
||||
PRCondVar *const signal;
|
||||
ChildState childState;
|
||||
bool childShouldStop;
|
||||
JSContext *childContext;
|
||||
|
||||
SharedData(JSRuntime *rt, bool *ok)
|
||||
: runtime(rt),
|
||||
mainThread(PR_GetCurrentThread()),
|
||||
lock(PR_NewLock()),
|
||||
signal(lock ? PR_NewCondVar(lock) : NULL),
|
||||
childState(CHILD_STARTING),
|
||||
childShouldStop(false),
|
||||
childContext(NULL)
|
||||
{
|
||||
JS_ASSERT(!*ok);
|
||||
*ok = !!signal;
|
||||
}
|
||||
|
||||
~SharedData() {
|
||||
if (signal)
|
||||
PR_DestroyCondVar(signal);
|
||||
if (lock)
|
||||
PR_DestroyLock(lock);
|
||||
}
|
||||
};
|
||||
|
||||
static SharedData *shared;
|
||||
|
||||
static JSBool
|
||||
CancelNonMainThreadGCCallback(JSContext *cx, JSGCStatus status)
|
||||
{
|
||||
return status != JSGC_BEGIN || PR_GetCurrentThread() == shared->mainThread;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
StopChildOperationCallback(JSContext *cx)
|
||||
{
|
||||
bool shouldStop;
|
||||
PR_Lock(shared->lock);
|
||||
shouldStop = shared->childShouldStop;
|
||||
PR_Unlock(shared->lock);
|
||||
return !shouldStop;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
NotifyMainThreadAboutBusyLoop(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
PR_Lock(shared->lock);
|
||||
JS_ASSERT(shared->childState == SharedData::CHILD_STARTING);
|
||||
shared->childState = SharedData::CHILD_RUNNING;
|
||||
shared->childContext = cx;
|
||||
PR_NotifyCondVar(shared->signal);
|
||||
PR_Unlock(shared->lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ChildThreadMain(void *arg)
|
||||
{
|
||||
JS_ASSERT(!arg);
|
||||
bool error = true;
|
||||
JSContext *cx = JS_NewContext(shared->runtime, 8192);
|
||||
if (cx) {
|
||||
JS_SetOperationCallback(cx, StopChildOperationCallback);
|
||||
JSAutoRequest ar(cx);
|
||||
JSObject *global = JS_NewCompartmentAndGlobalObject(cx, JSAPITest::basicGlobalClass(),
|
||||
NULL);
|
||||
if (global) {
|
||||
JS_SetGlobalObject(cx, global);
|
||||
if (JS_InitStandardClasses(cx, global) &&
|
||||
JS_DefineFunction(cx, global, "notify", NotifyMainThreadAboutBusyLoop, 0, 0)) {
|
||||
|
||||
jsval rval;
|
||||
static const char code[] = "var i = 0; notify(); for (var i = 0; ; ++i);";
|
||||
JSBool ok = JS_EvaluateScript(cx, global, code, strlen(code),
|
||||
__FILE__, __LINE__, &rval);
|
||||
if (!ok && !JS_IsExceptionPending(cx)) {
|
||||
/* Evaluate should only return via the callback cancellation. */
|
||||
error = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PR_Lock(shared->lock);
|
||||
shared->childState = error ? SharedData::CHILD_DONE : SharedData::CHILD_ERROR;
|
||||
shared->childContext = NULL;
|
||||
PR_NotifyCondVar(shared->signal);
|
||||
PR_Unlock(shared->lock);
|
||||
|
||||
if (cx)
|
||||
JS_DestroyContextNoGC(cx);
|
||||
}
|
||||
|
||||
BEGIN_TEST(testThreadGC_bug590533)
|
||||
{
|
||||
/*
|
||||
* Test the child thread busy running while the current thread calls
|
||||
* the GC both with JSRuntime->gcIsNeeded set and unset.
|
||||
*/
|
||||
bool ok = TestChildThread(true);
|
||||
CHECK(ok);
|
||||
ok = TestChildThread(false);
|
||||
CHECK(ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool TestChildThread(bool setGCIsNeeded)
|
||||
{
|
||||
bool ok = false;
|
||||
shared = new SharedData(rt, &ok);
|
||||
CHECK(ok);
|
||||
|
||||
JSGCCallback oldGCCallback = JS_SetGCCallback(cx, CancelNonMainThreadGCCallback);
|
||||
|
||||
PRThread *thread =
|
||||
PR_CreateThread(PR_USER_THREAD, ChildThreadMain, NULL,
|
||||
PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
|
||||
if (!thread)
|
||||
return false;
|
||||
|
||||
PR_Lock(shared->lock);
|
||||
while (shared->childState == SharedData::CHILD_STARTING)
|
||||
PR_WaitCondVar(shared->signal, PR_INTERVAL_NO_TIMEOUT);
|
||||
JS_ASSERT(shared->childState != SharedData::CHILD_DONE);
|
||||
ok = (shared->childState == SharedData::CHILD_RUNNING);
|
||||
PR_Unlock(shared->lock);
|
||||
|
||||
CHECK(ok);
|
||||
|
||||
if (setGCIsNeeded) {
|
||||
/*
|
||||
* Use JS internal API to set the GC trigger flag after we know
|
||||
* that the child is in a request and is about to run an infinite
|
||||
* loop. Then run the GC with JSRuntime->gcIsNeeded flag set.
|
||||
*/
|
||||
js::AutoLockGC lock(rt);
|
||||
js::TriggerGC(rt);
|
||||
}
|
||||
|
||||
JS_GC(cx);
|
||||
|
||||
PR_Lock(shared->lock);
|
||||
shared->childShouldStop = true;
|
||||
while (shared->childState == SharedData::CHILD_RUNNING) {
|
||||
JS_TriggerOperationCallback(shared->childContext);
|
||||
PR_WaitCondVar(shared->signal, PR_INTERVAL_NO_TIMEOUT);
|
||||
}
|
||||
JS_ASSERT(shared->childState != SharedData::CHILD_STARTING);
|
||||
ok = (shared->childState == SharedData::CHILD_DONE);
|
||||
PR_Unlock(shared->lock);
|
||||
|
||||
JS_SetGCCallback(cx, oldGCCallback);
|
||||
|
||||
PR_JoinThread(thread);
|
||||
|
||||
delete shared;
|
||||
shared = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
END_TEST(testThreadGC_bug590533)
|
||||
|
||||
#endif
|
@ -1,174 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=99:
|
||||
*/
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
||||
#include "tests.h"
|
||||
#include "prthread.h"
|
||||
|
||||
struct ThreadData {
|
||||
JSRuntime *rt;
|
||||
JSObject *obj;
|
||||
const char *code;
|
||||
bool ok;
|
||||
};
|
||||
|
||||
BEGIN_TEST(testThreads_bug561444)
|
||||
{
|
||||
const char *code = "<a><b/></a>.b.@c = '';";
|
||||
EXEC(code);
|
||||
|
||||
jsrefcount rc = JS_SuspendRequest(cx);
|
||||
{
|
||||
ThreadData data = {rt, global, code, false};
|
||||
PRThread *thread =
|
||||
PR_CreateThread(PR_USER_THREAD, threadMain, &data,
|
||||
PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
|
||||
CHECK(thread);
|
||||
PR_JoinThread(thread);
|
||||
CHECK(data.ok);
|
||||
}
|
||||
JS_ResumeRequest(cx, rc);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void threadMain(void *arg) {
|
||||
ThreadData *d = (ThreadData *) arg;
|
||||
|
||||
JSContext *cx = JS_NewContext(d->rt, 8192);
|
||||
if (!cx)
|
||||
return;
|
||||
JS_BeginRequest(cx);
|
||||
{
|
||||
JSAutoEnterCompartment ac;
|
||||
jsval v;
|
||||
d->ok = ac.enter(cx, d->obj) &&
|
||||
JS_EvaluateScript(cx, d->obj, d->code, strlen(d->code), __FILE__, __LINE__,
|
||||
&v);
|
||||
}
|
||||
JS_DestroyContext(cx);
|
||||
}
|
||||
END_TEST(testThreads_bug561444)
|
||||
|
||||
const PRUint32 NATIVE_STACK_SIZE = 64 * 1024;
|
||||
const PRUint32 NATIVE_STACK_HEADROOM = 8 * 1024;
|
||||
|
||||
template <class T>
|
||||
class Repeat {
|
||||
size_t n;
|
||||
const T &t;
|
||||
|
||||
public:
|
||||
Repeat(size_t n, const T &t) : n(n), t(t) {}
|
||||
|
||||
bool operator()() const {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (!t())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> Repeat<T> repeat(size_t n, const T &t) { return Repeat<T>(n, t); }
|
||||
|
||||
/* Class of callable that does something in n parallel threads. */
|
||||
template <class T>
|
||||
class Parallel {
|
||||
size_t n;
|
||||
const T &t;
|
||||
|
||||
struct pair { const Parallel *self; bool ok; };
|
||||
|
||||
static void threadMain(void *arg) {
|
||||
pair *p = (pair *) arg;
|
||||
if (!p->self->t())
|
||||
p->ok = false;
|
||||
}
|
||||
|
||||
public:
|
||||
Parallel(size_t n, const T &t) : n(n), t(t) {}
|
||||
|
||||
bool operator()() const {
|
||||
pair p = {this, true};
|
||||
|
||||
PRThread **thread = new PRThread *[n];
|
||||
if (!thread)
|
||||
return false;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < n; i++) {
|
||||
thread[i] = PR_CreateThread(PR_USER_THREAD, threadMain, &p, PR_PRIORITY_NORMAL,
|
||||
PR_LOCAL_THREAD, PR_JOINABLE_THREAD, NATIVE_STACK_SIZE);
|
||||
if (thread[i] == NULL) {
|
||||
p.ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (i--)
|
||||
PR_JoinThread(thread[i]);
|
||||
|
||||
delete[] thread;
|
||||
return p.ok;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> Parallel<T> parallel(size_t n, const T &t) { return Parallel<T>(n, t); }
|
||||
|
||||
/* Class of callable that creates a compartment and runs some code in it. */
|
||||
class eval {
|
||||
JSRuntime *rt;
|
||||
const char *code;
|
||||
|
||||
public:
|
||||
eval(JSRuntime *rt, const char *code) : rt(rt), code(code) {}
|
||||
|
||||
bool operator()() const {
|
||||
JSContext *cx = JS_NewContext(rt, 8192);
|
||||
if (!cx)
|
||||
return false;
|
||||
|
||||
JS_SetNativeStackQuota(cx, NATIVE_STACK_SIZE - NATIVE_STACK_HEADROOM);
|
||||
bool ok = false;
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
JSObject *global =
|
||||
JS_NewCompartmentAndGlobalObject(cx, JSAPITest::basicGlobalClass(), NULL);
|
||||
if (global) {
|
||||
JS_SetGlobalObject(cx, global);
|
||||
jsval rval;
|
||||
ok = JS_InitStandardClasses(cx, global) &&
|
||||
JS_EvaluateScript(cx, global, code, strlen(code), "", 0, &rval);
|
||||
}
|
||||
}
|
||||
JS_DestroyContextMaybeGC(cx);
|
||||
return ok;
|
||||
}
|
||||
};
|
||||
|
||||
BEGIN_TEST(testThreads_bug604782)
|
||||
{
|
||||
jsrefcount rc = JS_SuspendRequest(cx);
|
||||
bool ok = repeat(20, parallel(3, eval(rt, "for(i=0;i<1000;i++);")))();
|
||||
JS_ResumeRequest(cx, rc);
|
||||
CHECK(ok);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testThreads_bug604782)
|
||||
|
||||
BEGIN_TEST(testThreads_bug609103)
|
||||
{
|
||||
const char *code =
|
||||
"var x = {};\n"
|
||||
"for (var i = 0; i < 10000; i++)\n"
|
||||
" x = {next: x};\n";
|
||||
|
||||
jsrefcount rc = JS_SuspendRequest(cx);
|
||||
bool ok = parallel(2, eval(rt, code))();
|
||||
JS_ResumeRequest(cx, rc);
|
||||
CHECK(ok);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testThreads_bug609103)
|
||||
|
||||
#endif
|
@ -596,6 +596,7 @@ JS_GetTypeName(JSContext *cx, JSType type)
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, v1, v2);
|
||||
return StrictlyEqual(cx, Valueify(v1), Valueify(v2), equal);
|
||||
}
|
||||
@ -603,6 +604,7 @@ JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal)
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_LooselyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, v1, v2);
|
||||
return LooselyEqual(cx, Valueify(v1), Valueify(v2), equal);
|
||||
}
|
||||
@ -610,6 +612,7 @@ JS_LooselyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal)
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, v1, v2);
|
||||
return SameValue(cx, Valueify(v1), Valueify(v2), same);
|
||||
}
|
||||
@ -647,6 +650,10 @@ JSRuntime::JSRuntime()
|
||||
bool
|
||||
JSRuntime::init(uint32 maxbytes)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
ownerThread_ = js_CurrentThreadId();
|
||||
#endif
|
||||
|
||||
#ifdef JS_METHODJIT_SPEW
|
||||
JMCheckLogging();
|
||||
#endif
|
||||
@ -742,6 +749,28 @@ JSRuntime::~JSRuntime()
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
void
|
||||
JSRuntime::setOwnerThread()
|
||||
{
|
||||
JS_ASSERT(ownerThread_ == (void *)-1);
|
||||
ownerThread_ = js_CurrentThreadId();
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::clearOwnerThread()
|
||||
{
|
||||
JS_ASSERT(onOwnerThread());
|
||||
ownerThread_ = (void *)-1;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
JSRuntime::onOwnerThread() const
|
||||
{
|
||||
return ownerThread_ == js_CurrentThreadId();
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_PUBLIC_API(JSRuntime *)
|
||||
JS_NewRuntime(uint32 maxbytes)
|
||||
{
|
||||
@ -1998,12 +2027,14 @@ JS_ComputeThis(JSContext *cx, jsval *vp)
|
||||
JS_PUBLIC_API(void *)
|
||||
JS_malloc(JSContext *cx, size_t nbytes)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
return cx->malloc_(nbytes);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void *)
|
||||
JS_realloc(JSContext *cx, void *p, size_t nbytes)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
return cx->realloc_(p, nbytes);
|
||||
}
|
||||
|
||||
@ -5332,12 +5363,16 @@ JS_GetFlatStringChars(JSFlatString *str)
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
return CompareStrings(cx, str1, str2, result);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
JSLinearString *linearStr = str->ensureLinear(cx);
|
||||
if (!linearStr)
|
||||
return false;
|
||||
@ -5523,6 +5558,8 @@ JS_ReadStructuredClone(JSContext *cx, const uint64 *buf, size_t nbytes,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks,
|
||||
void *closure)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
if (version > JS_STRUCTURED_CLONE_VERSION) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION);
|
||||
return false;
|
||||
@ -5539,6 +5576,8 @@ JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **bufp, size_t *nbytesp,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks,
|
||||
void *closure)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
const JSStructuredCloneCallbacks *callbacks =
|
||||
optionalCallbacks ?
|
||||
optionalCallbacks :
|
||||
@ -6113,6 +6152,8 @@ JS_GetContextThread(JSContext *cx)
|
||||
JS_PUBLIC_API(jsword)
|
||||
JS_SetContextThread(JSContext *cx)
|
||||
{
|
||||
JS_AbortIfWrongThread(cx->runtime);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(!cx->outstandingRequests);
|
||||
if (cx->thread()) {
|
||||
@ -6130,9 +6171,36 @@ JS_SetContextThread(JSContext *cx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_ClearRuntimeThread(JSRuntime *rt)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
rt->clearOwnerThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetRuntimeThread(JSRuntime *rt)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
rt->setOwnerThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_AbortIfWrongThread(JSRuntime *rt)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
if (!rt->onOwnerThread())
|
||||
JS_Assert("rt->onOwnerThread()", __FILE__, __LINE__);
|
||||
#endif
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(jsword)
|
||||
JS_ClearContextThread(JSContext *cx)
|
||||
{
|
||||
JS_AbortIfWrongThread(cx->runtime);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
/*
|
||||
* cx must have exited all requests it entered and, if cx is associated
|
||||
|
@ -3729,6 +3729,51 @@ JS_SetContextThread(JSContext *cx);
|
||||
extern JS_PUBLIC_API(jsword)
|
||||
JS_ClearContextThread(JSContext *cx);
|
||||
|
||||
/*
|
||||
* A JS runtime always has an "owner thread". The owner thread is set when the
|
||||
* runtime is created (to the current thread) and practically all entry points
|
||||
* into the JS engine check that a runtime (or anything contained in the
|
||||
* runtime: context, compartment, object, etc) is only touched by its owner
|
||||
* thread. Embeddings may check this invariant outside the JS engine by calling
|
||||
* JS_AbortIfWrongThread (which will abort if not on the owner thread, even for
|
||||
* non-debug builds).
|
||||
*
|
||||
* It is possible to "move" a runtime between threads. This is accomplished by
|
||||
* calling JS_ClearRuntimeThread on a runtime's owner thread and then calling
|
||||
* JS_SetRuntimeThread on the new owner thread. The runtime must not be
|
||||
* accessed between JS_ClearRuntimeThread and JS_SetRuntimeThread. Also, the
|
||||
* caller is responsible for synchronizing the calls to Set/Clear.
|
||||
*/
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_AbortIfWrongThread(JSRuntime *rt);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_ClearRuntimeThread(JSRuntime *rt);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetRuntimeThread(JSRuntime *rt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
JS_END_EXTERN_C
|
||||
|
||||
class JSAutoSetRuntimeThread
|
||||
{
|
||||
JSRuntime *runtime;
|
||||
|
||||
public:
|
||||
JSAutoSetRuntimeThread(JSRuntime *runtime) : runtime(runtime) {
|
||||
JS_SetRuntimeThread(runtime);
|
||||
}
|
||||
|
||||
~JSAutoSetRuntimeThread() {
|
||||
JS_ClearRuntimeThread(runtime);
|
||||
}
|
||||
};
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
/*
|
||||
|
@ -316,6 +316,8 @@ static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
|
||||
JSContext *
|
||||
js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
{
|
||||
JS_AbortIfWrongThread(rt);
|
||||
|
||||
JSContext *cx;
|
||||
JSBool first;
|
||||
JSContextCallback cxCallback;
|
||||
@ -427,13 +429,14 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
void
|
||||
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JS_AbortIfWrongThread(rt);
|
||||
|
||||
JSContextCallback cxCallback;
|
||||
JSBool last;
|
||||
|
||||
JS_ASSERT(!cx->enumerators);
|
||||
|
||||
rt = cx->runtime;
|
||||
#ifdef JS_THREADSAFE
|
||||
/*
|
||||
* For API compatibility we allow to destroy contexts without a thread in
|
||||
|
@ -327,7 +327,8 @@ typedef js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> CompartmentVector;
|
||||
|
||||
}
|
||||
|
||||
struct JSRuntime {
|
||||
struct JSRuntime
|
||||
{
|
||||
/* Default compartment. */
|
||||
JSCompartment *atomsCompartment;
|
||||
#ifdef JS_THREADSAFE
|
||||
@ -340,6 +341,20 @@ struct JSRuntime {
|
||||
/* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */
|
||||
JSRuntimeState state;
|
||||
|
||||
/* See comment for JS_AbortIfWrongThread in jsapi.h. */
|
||||
#ifdef JS_THREADSAFE
|
||||
public:
|
||||
void clearOwnerThread();
|
||||
void setOwnerThread();
|
||||
JS_FRIEND_API(bool) onOwnerThread() const;
|
||||
private:
|
||||
void *ownerThread_;
|
||||
public:
|
||||
#else
|
||||
public:
|
||||
bool onOwnerThread() const { return true; }
|
||||
#endif
|
||||
|
||||
/* Context create/destroy callback. */
|
||||
JSContextCallback cxCallback;
|
||||
|
||||
@ -1334,11 +1349,11 @@ class AutoCheckRequestDepth {
|
||||
# define CHECK_REQUEST(cx) \
|
||||
JS_ASSERT((cx)->thread()); \
|
||||
JS_ASSERT((cx)->thread()->data.requestDepth || (cx)->thread() == (cx)->runtime->gcThread); \
|
||||
JS_ASSERT(cx->runtime->onOwnerThread()); \
|
||||
AutoCheckRequestDepth _autoCheckRequestDepth(cx);
|
||||
|
||||
#else
|
||||
# define CHECK_REQUEST(cx) ((void) 0)
|
||||
# define CHECK_REQUEST_THREAD(cx) ((void) 0)
|
||||
#endif
|
||||
|
||||
static inline JSAtom **
|
||||
|
@ -1905,6 +1905,7 @@ void
|
||||
MaybeGC(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JS_ASSERT(rt->onOwnerThread());
|
||||
|
||||
if (rt->gcZeal()) {
|
||||
GCREASON(MAYBEGC);
|
||||
@ -2671,6 +2672,7 @@ void
|
||||
js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JS_AbortIfWrongThread(rt);
|
||||
|
||||
/*
|
||||
* Don't collect garbage if the runtime isn't up, and cx is not the last
|
||||
@ -2856,6 +2858,8 @@ JSCompartment *
|
||||
NewCompartment(JSContext *cx, JSPrincipals *principals)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JS_AbortIfWrongThread(rt);
|
||||
|
||||
JSCompartment *compartment = cx->new_<JSCompartment>(rt);
|
||||
if (compartment && compartment->init()) {
|
||||
// Any compartment with the trusted principals -- and there can be
|
||||
|
@ -1111,7 +1111,7 @@ Quit(JSContext *cx, uintN argc, jsval *vp)
|
||||
gQuitting = JS_TRUE;
|
||||
#ifdef JS_THREADSAFE
|
||||
if (gWorkerThreadPool)
|
||||
js::workers::terminateAll(JS_GetRuntime(cx), gWorkerThreadPool);
|
||||
js::workers::terminateAll(gWorkerThreadPool);
|
||||
#endif
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -3233,245 +3233,6 @@ Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
|
||||
return !gCanceled;
|
||||
}
|
||||
|
||||
typedef struct ScatterThreadData ScatterThreadData;
|
||||
typedef struct ScatterData ScatterData;
|
||||
|
||||
typedef enum ScatterStatus {
|
||||
SCATTER_WAIT,
|
||||
SCATTER_GO,
|
||||
SCATTER_CANCEL
|
||||
} ScatterStatus;
|
||||
|
||||
struct ScatterData {
|
||||
ScatterThreadData *threads;
|
||||
jsval *results;
|
||||
PRLock *lock;
|
||||
PRCondVar *cvar;
|
||||
ScatterStatus status;
|
||||
};
|
||||
|
||||
struct ScatterThreadData {
|
||||
jsint index;
|
||||
ScatterData *shared;
|
||||
PRThread *thr;
|
||||
JSContext *cx;
|
||||
jsval fn;
|
||||
};
|
||||
|
||||
static void
|
||||
DoScatteredWork(JSContext *cx, ScatterThreadData *td)
|
||||
{
|
||||
jsval *rval = &td->shared->results[td->index];
|
||||
|
||||
if (!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
|
||||
*rval = JSVAL_VOID;
|
||||
JS_GetPendingException(cx, rval);
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
RunScatterThread(void *arg)
|
||||
{
|
||||
int stackDummy;
|
||||
ScatterThreadData *td;
|
||||
ScatterStatus st;
|
||||
JSContext *cx;
|
||||
|
||||
if (PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy))
|
||||
return;
|
||||
|
||||
td = (ScatterThreadData *)arg;
|
||||
cx = td->cx;
|
||||
|
||||
/* Wait for our signal. */
|
||||
PR_Lock(td->shared->lock);
|
||||
while ((st = td->shared->status) == SCATTER_WAIT)
|
||||
PR_WaitCondVar(td->shared->cvar, PR_INTERVAL_NO_TIMEOUT);
|
||||
PR_Unlock(td->shared->lock);
|
||||
|
||||
if (st == SCATTER_CANCEL)
|
||||
return;
|
||||
|
||||
/* We are good to go. */
|
||||
JS_SetContextThread(cx);
|
||||
JS_SetNativeStackQuota(cx, gMaxStackSize);
|
||||
JS_BeginRequest(cx);
|
||||
DoScatteredWork(cx, td);
|
||||
JS_EndRequest(cx);
|
||||
JS_ClearContextThread(cx);
|
||||
}
|
||||
|
||||
/*
|
||||
* scatter(fnArray) - Call each function in `fnArray` without arguments, each
|
||||
* in a different thread. When all threads have finished, return an array: the
|
||||
* return values. Errors are not propagated; if any of the function calls
|
||||
* fails, the corresponding element in the results array gets the exception
|
||||
* object, if any, else (undefined).
|
||||
*/
|
||||
static JSBool
|
||||
Scatter(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
jsuint i;
|
||||
jsuint n; /* number of threads */
|
||||
JSObject *inArr;
|
||||
JSObject *arr;
|
||||
JSObject *global;
|
||||
ScatterData sd;
|
||||
JSBool ok;
|
||||
|
||||
sd.lock = NULL;
|
||||
sd.cvar = NULL;
|
||||
sd.results = NULL;
|
||||
sd.threads = NULL;
|
||||
sd.status = SCATTER_WAIT;
|
||||
|
||||
if (argc == 0 || JSVAL_IS_PRIMITIVE(JS_ARGV(cx, vp)[0])) {
|
||||
JS_ReportError(cx, "the first argument must be an object");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
inArr = JSVAL_TO_OBJECT(JS_ARGV(cx, vp)[0]);
|
||||
ok = JS_GetArrayLength(cx, inArr, &n);
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (n == 0)
|
||||
goto success;
|
||||
|
||||
sd.lock = PR_NewLock();
|
||||
if (!sd.lock)
|
||||
goto fail;
|
||||
|
||||
sd.cvar = PR_NewCondVar(sd.lock);
|
||||
if (!sd.cvar)
|
||||
goto fail;
|
||||
|
||||
sd.results = (jsval *) malloc(n * sizeof(jsval));
|
||||
if (!sd.results)
|
||||
goto fail;
|
||||
for (i = 0; i < n; i++) {
|
||||
sd.results[i] = JSVAL_VOID;
|
||||
ok = JS_AddValueRoot(cx, &sd.results[i]);
|
||||
if (!ok) {
|
||||
while (i-- > 0)
|
||||
JS_RemoveValueRoot(cx, &sd.results[i]);
|
||||
free(sd.results);
|
||||
sd.results = NULL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
sd.threads = (ScatterThreadData *) malloc(n * sizeof(ScatterThreadData));
|
||||
if (!sd.threads)
|
||||
goto fail;
|
||||
for (i = 0; i < n; i++) {
|
||||
sd.threads[i].index = i;
|
||||
sd.threads[i].shared = &sd;
|
||||
sd.threads[i].thr = NULL;
|
||||
sd.threads[i].cx = NULL;
|
||||
sd.threads[i].fn = JSVAL_NULL;
|
||||
|
||||
ok = JS_AddValueRoot(cx, &sd.threads[i].fn);
|
||||
if (ok && !JS_GetElement(cx, inArr, i, &sd.threads[i].fn)) {
|
||||
JS_RemoveValueRoot(cx, &sd.threads[i].fn);
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
if (!ok) {
|
||||
while (i-- > 0)
|
||||
JS_RemoveValueRoot(cx, &sd.threads[i].fn);
|
||||
free(sd.threads);
|
||||
sd.threads = NULL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
global = JS_GetGlobalObject(cx);
|
||||
for (i = 1; i < n; i++) {
|
||||
JSContext *newcx = NewContext(JS_GetRuntime(cx));
|
||||
if (!newcx)
|
||||
goto fail;
|
||||
|
||||
{
|
||||
JSAutoRequest req(newcx);
|
||||
JS_SetGlobalObject(newcx, global);
|
||||
}
|
||||
JS_ClearContextThread(newcx);
|
||||
sd.threads[i].cx = newcx;
|
||||
}
|
||||
|
||||
for (i = 1; i < n; i++) {
|
||||
PRThread *t = PR_CreateThread(PR_USER_THREAD,
|
||||
RunScatterThread,
|
||||
&sd.threads[i],
|
||||
PR_PRIORITY_NORMAL,
|
||||
PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD,
|
||||
0);
|
||||
if (!t) {
|
||||
/* Failed to start thread. */
|
||||
PR_Lock(sd.lock);
|
||||
sd.status = SCATTER_CANCEL;
|
||||
PR_NotifyAllCondVar(sd.cvar);
|
||||
PR_Unlock(sd.lock);
|
||||
while (i-- > 1)
|
||||
PR_JoinThread(sd.threads[i].thr);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sd.threads[i].thr = t;
|
||||
}
|
||||
PR_Lock(sd.lock);
|
||||
sd.status = SCATTER_GO;
|
||||
PR_NotifyAllCondVar(sd.cvar);
|
||||
PR_Unlock(sd.lock);
|
||||
|
||||
DoScatteredWork(cx, &sd.threads[0]);
|
||||
|
||||
{
|
||||
JSAutoSuspendRequest suspended(cx);
|
||||
for (i = 1; i < n; i++) {
|
||||
PR_JoinThread(sd.threads[i].thr);
|
||||
}
|
||||
}
|
||||
|
||||
success:
|
||||
arr = JS_NewArrayObject(cx, n, sd.results);
|
||||
if (!arr)
|
||||
goto fail;
|
||||
*vp = OBJECT_TO_JSVAL(arr);
|
||||
ok = JS_TRUE;
|
||||
|
||||
out:
|
||||
if (sd.threads) {
|
||||
JSContext *acx;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
JS_RemoveValueRoot(cx, &sd.threads[i].fn);
|
||||
acx = sd.threads[i].cx;
|
||||
if (acx) {
|
||||
JS_SetContextThread(acx);
|
||||
DestroyContext(acx, true);
|
||||
}
|
||||
}
|
||||
free(sd.threads);
|
||||
}
|
||||
if (sd.results) {
|
||||
for (i = 0; i < n; i++)
|
||||
JS_RemoveValueRoot(cx, &sd.results[i]);
|
||||
free(sd.results);
|
||||
}
|
||||
if (sd.cvar)
|
||||
PR_DestroyCondVar(sd.cvar);
|
||||
if (sd.lock)
|
||||
PR_DestroyLock(sd.lock);
|
||||
|
||||
return ok;
|
||||
|
||||
fail:
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static bool
|
||||
InitWatchdog(JSRuntime *rt)
|
||||
{
|
||||
@ -3656,7 +3417,7 @@ CancelExecution(JSRuntime *rt)
|
||||
gExitCode = EXITCODE_TIMEOUT;
|
||||
#ifdef JS_THREADSAFE
|
||||
if (gWorkerThreadPool)
|
||||
js::workers::terminateAll(rt, gWorkerThreadPool);
|
||||
js::workers::terminateAll(gWorkerThreadPool);
|
||||
#endif
|
||||
JS_TriggerAllOperationCallbacks(rt);
|
||||
|
||||
@ -4197,7 +3958,6 @@ static JSFunctionSpec shell_functions[] = {
|
||||
#endif
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_FN("sleep", Sleep_fn, 1,0),
|
||||
JS_FN("scatter", Scatter, 1,0),
|
||||
#endif
|
||||
JS_FN("snarf", Snarf, 0,0),
|
||||
JS_FN("read", Snarf, 0,0),
|
||||
@ -4337,7 +4097,6 @@ static const char *const shell_help_messages[] = {
|
||||
#endif
|
||||
#ifdef JS_THREADSAFE
|
||||
"sleep(dt) Sleep for dt seconds",
|
||||
"scatter(fns) Call functions concurrently (ignoring errors)",
|
||||
#endif
|
||||
"snarf(filename) Read filename into returned string",
|
||||
"read(filename) Synonym for snarf",
|
||||
|
@ -170,6 +170,7 @@ class WorkerParent {
|
||||
}
|
||||
|
||||
void disposeChildren();
|
||||
void notifyTerminating();
|
||||
};
|
||||
|
||||
template <class T>
|
||||
@ -528,11 +529,12 @@ class ThreadPool
|
||||
return ok;
|
||||
}
|
||||
|
||||
void terminateAll(JSRuntime *rt) {
|
||||
void terminateAll() {
|
||||
// See comment about JS_ATOMIC_SET in the implementation of
|
||||
// JS_TriggerOperationCallback.
|
||||
JS_ATOMIC_SET(&terminating, 1);
|
||||
JS_TriggerAllOperationCallbacks(rt);
|
||||
if (mq)
|
||||
mq->notifyTerminating();
|
||||
}
|
||||
|
||||
/* This context is used only to free memory. */
|
||||
@ -593,6 +595,7 @@ class Worker : public WorkerParent
|
||||
ThreadPool *threadPool;
|
||||
WorkerParent *parent;
|
||||
JSObject *object; // Worker object exposed to parent
|
||||
JSRuntime *runtime;
|
||||
JSContext *context;
|
||||
JSLock *lock;
|
||||
Queue<Event *, SystemAllocPolicy> events; // owning pointers to pending events
|
||||
@ -603,7 +606,7 @@ class Worker : public WorkerParent
|
||||
static JSClass jsWorkerClass;
|
||||
|
||||
Worker()
|
||||
: threadPool(NULL), parent(NULL), object(NULL),
|
||||
: threadPool(NULL), parent(NULL), object(NULL), runtime(NULL),
|
||||
context(NULL), lock(NULL), current(NULL), terminated(false), terminateFlag(0) {}
|
||||
|
||||
bool init(JSContext *parentcx, WorkerParent *parent, JSObject *obj) {
|
||||
@ -616,13 +619,24 @@ class Worker : public WorkerParent
|
||||
this->object = obj;
|
||||
lock = JS_NEW_LOCK();
|
||||
return lock &&
|
||||
createRuntime(parentcx) &&
|
||||
createContext(parentcx, parent) &&
|
||||
JS_SetPrivate(parentcx, obj, this);
|
||||
}
|
||||
|
||||
bool createRuntime(JSContext *parentcx) {
|
||||
runtime = JS_NewRuntime(1L * 1024L * 1024L);
|
||||
if (!runtime) {
|
||||
JS_ReportOutOfMemory(parentcx);
|
||||
return false;
|
||||
}
|
||||
JS_ClearRuntimeThread(runtime);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool createContext(JSContext *parentcx, WorkerParent *parent) {
|
||||
JSRuntime *rt = JS_GetRuntime(parentcx);
|
||||
context = JS_NewContext(rt, 8192);
|
||||
JSAutoSetRuntimeThread guard(runtime);
|
||||
context = JS_NewContext(runtime, 8192);
|
||||
if (!context)
|
||||
return false;
|
||||
|
||||
@ -754,11 +768,17 @@ class Worker : public WorkerParent
|
||||
JS_DESTROY_LOCK(lock);
|
||||
lock = NULL;
|
||||
}
|
||||
if (runtime)
|
||||
JS_SetRuntimeThread(runtime);
|
||||
if (context) {
|
||||
JS_SetContextThread(context);
|
||||
JS_DestroyContextNoGC(context);
|
||||
context = NULL;
|
||||
}
|
||||
if (runtime) {
|
||||
JS_DestroyRuntime(runtime);
|
||||
runtime = NULL;
|
||||
}
|
||||
object = NULL;
|
||||
|
||||
// Do not call parent->removeChild(). This is called either from
|
||||
@ -797,6 +817,11 @@ class Worker : public WorkerParent
|
||||
JS_TriggerOperationCallback(context);
|
||||
}
|
||||
|
||||
void notifyTerminating() {
|
||||
setTerminateFlag();
|
||||
WorkerParent::notifyTerminating();
|
||||
}
|
||||
|
||||
void processOneEvent();
|
||||
|
||||
/* Trace method to be called from C++. */
|
||||
@ -977,6 +1002,14 @@ WorkerParent::disposeChildren()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WorkerParent::notifyTerminating()
|
||||
{
|
||||
AutoLock hold(getLock());
|
||||
for (ChildSet::Range r = children.all(); !r.empty(); r.popFront())
|
||||
r.front()->notifyTerminating();
|
||||
}
|
||||
|
||||
bool
|
||||
MainQueue::shouldStop()
|
||||
{
|
||||
@ -1110,7 +1143,8 @@ Worker::processOneEvent()
|
||||
|
||||
Event::Result result;
|
||||
{
|
||||
JSAutoRequest req(context);
|
||||
JSAutoSetRuntimeThread asrt(JS_GetRuntime(context));
|
||||
JSAutoRequest ar(context);
|
||||
result = event->process(context);
|
||||
}
|
||||
|
||||
@ -1126,7 +1160,8 @@ Worker::processOneEvent()
|
||||
}
|
||||
}
|
||||
if (result == Event::fail && !checkTermination()) {
|
||||
JSAutoRequest req(context);
|
||||
JSAutoSetRuntimeThread asrt(JS_GetRuntime(context));
|
||||
JSAutoRequest ar(context);
|
||||
Event *err = ErrorEvent::create(context, this);
|
||||
if (err && !parent->post(err)) {
|
||||
JS_ReportOutOfMemory(context);
|
||||
@ -1260,9 +1295,9 @@ js::workers::init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject
|
||||
}
|
||||
|
||||
void
|
||||
js::workers::terminateAll(JSRuntime *rt, ThreadPool *tp)
|
||||
js::workers::terminateAll(ThreadPool *tp)
|
||||
{
|
||||
tp->terminateAll(rt);
|
||||
tp->terminateAll();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -61,13 +61,13 @@ namespace js {
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize workers. This defines the Worker constructor on global.
|
||||
* Requires request. rootp must point to a GC root.
|
||||
*
|
||||
* On success, *rootp receives a pointer to an object, and init returns
|
||||
* Initialize workers. This defines the Worker constructor on global.
|
||||
* Requires request. rootp must point to a GC root.
|
||||
*
|
||||
* On success, *rootp receives a pointer to an object, and init returns
|
||||
* a non-null value. The caller must keep the object rooted and must
|
||||
* pass it to js::workers::finish later.
|
||||
*/
|
||||
*/
|
||||
ThreadPool *init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp);
|
||||
|
||||
/* Asynchronously signal for all workers to terminate.
|
||||
@ -75,7 +75,7 @@ namespace js {
|
||||
* Call this before calling finish() to shut down without waiting for
|
||||
* all messages to be proceesed.
|
||||
*/
|
||||
void terminateAll(JSRuntime *rt, ThreadPool *tp);
|
||||
void terminateAll(ThreadPool *tp);
|
||||
|
||||
/*
|
||||
* Finish running any workers, shut down the thread pool, and free all
|
||||
|
@ -9,10 +9,10 @@ if (typeof Worker != 'undefined') {
|
||||
JSTest.waitForExplicitFinish();
|
||||
var w = Worker(workerDir + "worker-fib-child.js");
|
||||
w.onmessage = function (event) {
|
||||
reportCompare("55", event.data, "worker-fib");
|
||||
reportCompare("21", event.data, "worker-fib");
|
||||
JSTest.testFinished();
|
||||
};
|
||||
w.postMessage("10\t" + workerDir); // 0 1 1 2 3 5 8 13 21 34 55
|
||||
w.postMessage("8\t" + workerDir); // 0 1 1 2 3 5 8 13 21
|
||||
} else {
|
||||
reportCompare(0, 0, "Test skipped. Shell workers required.");
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ def set_limits():
|
||||
try:
|
||||
import resource
|
||||
GB = 2**30
|
||||
resource.setrlimit(resource.RLIMIT_AS, (1*GB, 1*GB))
|
||||
resource.setrlimit(resource.RLIMIT_AS, (2*GB, 2*GB))
|
||||
except:
|
||||
return
|
||||
|
||||
|
@ -762,6 +762,8 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
|
||||
JSPrincipals* jsPrincipals = nsnull;
|
||||
JSCLContextHelper cx(this);
|
||||
|
||||
JS_AbortIfWrongThread(JS_GetRuntime(cx));
|
||||
|
||||
// preserve caller's compartment
|
||||
js::PreserveCompartment pc(cx);
|
||||
|
||||
|
@ -532,6 +532,34 @@ nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsXPConnect::NotifyLeaveMainThread()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
|
||||
JS_ClearRuntimeThread(mRuntime->GetJSRuntime());
|
||||
}
|
||||
|
||||
void
|
||||
nsXPConnect::NotifyEnterCycleCollectionThread()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!NS_IsMainThread(), "On main thread");
|
||||
JS_SetRuntimeThread(mRuntime->GetJSRuntime());
|
||||
}
|
||||
|
||||
void
|
||||
nsXPConnect::NotifyLeaveCycleCollectionThread()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!NS_IsMainThread(), "On main thread");
|
||||
JS_ClearRuntimeThread(mRuntime->GetJSRuntime());
|
||||
}
|
||||
|
||||
void
|
||||
nsXPConnect::NotifyEnterMainThread()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
|
||||
JS_SetRuntimeThread(mRuntime->GetJSRuntime());
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXPConnect::FinishTraverse()
|
||||
{
|
||||
@ -2444,7 +2472,9 @@ nsXPConnect::GetRuntime(JSRuntime **runtime)
|
||||
if(!runtime)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
*runtime = GetRuntime()->GetJSRuntime();
|
||||
JSRuntime *rt = GetRuntime()->GetJSRuntime();
|
||||
JS_AbortIfWrongThread(rt);
|
||||
*runtime = rt;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -502,6 +502,7 @@ XPCCallContext::GetCalleeClassInfo(nsIClassInfo * *aCalleeClassInfo)
|
||||
NS_IMETHODIMP
|
||||
XPCCallContext::GetJSContext(JSContext * *aJSContext)
|
||||
{
|
||||
JS_AbortIfWrongThread(JS_GetRuntime(mJSContext));
|
||||
*aJSContext = mJSContext;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -3544,6 +3544,8 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
|
||||
const char *filename, PRInt32 lineNo,
|
||||
JSVersion jsVersion, PRBool returnStringOnly, jsval *rval)
|
||||
{
|
||||
JS_AbortIfWrongThread(JS_GetRuntime(cx));
|
||||
|
||||
#ifdef DEBUG
|
||||
// NB: The "unsafe" unwrap here is OK because we must be called from chrome.
|
||||
{
|
||||
|
@ -546,6 +546,10 @@ public:
|
||||
nsCycleCollectionTraversalCallback &cb);
|
||||
|
||||
// nsCycleCollectionLanguageRuntime
|
||||
virtual void NotifyLeaveMainThread();
|
||||
virtual void NotifyEnterCycleCollectionThread();
|
||||
virtual void NotifyLeaveCycleCollectionThread();
|
||||
virtual void NotifyEnterMainThread();
|
||||
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
|
||||
bool explainExpectedLiveGarbage);
|
||||
virtual nsresult FinishTraverse();
|
||||
|
@ -3448,6 +3448,12 @@ class nsCycleCollectorRunner : public nsRunnable
|
||||
PRBool mShutdown;
|
||||
PRBool mCollected;
|
||||
|
||||
nsCycleCollectionJSRuntime *GetJSRuntime()
|
||||
{
|
||||
return static_cast<nsCycleCollectionJSRuntime*>
|
||||
(mCollector->mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]);
|
||||
}
|
||||
|
||||
public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
@ -3478,7 +3484,9 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GetJSRuntime()->NotifyEnterCycleCollectionThread();
|
||||
mCollected = mCollector->BeginCollection(mListener);
|
||||
GetJSRuntime()->NotifyLeaveCycleCollectionThread();
|
||||
|
||||
mReply.Notify();
|
||||
}
|
||||
@ -3517,8 +3525,10 @@ public:
|
||||
NS_ASSERTION(!mListener, "Should have cleared this already!");
|
||||
mListener = aListener;
|
||||
|
||||
GetJSRuntime()->NotifyLeaveMainThread();
|
||||
mRequest.Notify();
|
||||
mReply.Wait();
|
||||
GetJSRuntime()->NotifyEnterMainThread();
|
||||
|
||||
mListener = nsnull;
|
||||
|
||||
|
@ -76,6 +76,14 @@ void nsCycleCollector_shutdown();
|
||||
// nsCycleCollector_doCollect directly.
|
||||
struct nsCycleCollectionJSRuntime : public nsCycleCollectionLanguageRuntime
|
||||
{
|
||||
/**
|
||||
* Called before/after transitioning to/from the main thread.
|
||||
*/
|
||||
virtual void NotifyLeaveMainThread() = 0;
|
||||
virtual void NotifyEnterCycleCollectionThread() = 0;
|
||||
virtual void NotifyLeaveCycleCollectionThread() = 0;
|
||||
virtual void NotifyEnterMainThread() = 0;
|
||||
|
||||
/**
|
||||
* Should we force a JavaScript GC before a CC?
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user