bug 551680 - replacing JS_(Suspend|Resume)Request with JSAutoSuspendRequest. r=mrbkap

This commit is contained in:
Igor Bukanov 2010-03-17 10:29:37 +03:00
Родитель 0d27280dc0
Коммит 3007509e8e
13 изменённых файлов: 178 добавлений и 268 удалений

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -514,71 +514,49 @@ DOMWorkerOperationCallback(JSContext* aCx)
nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
NS_ASSERTION(worker, "This must never be null!");
PRBool wasSuspended = PR_FALSE;
PRBool extraThreadAllowed = PR_FALSE;
jsrefcount suspendDepth = 0;
PRBool canceled = worker->IsCanceled();
if (!canceled && worker->IsSuspended()) {
JSAutoSuspendRequest suspended(aCx);
for (;;) {
// Kill execution if we're canceled.
if (worker->IsCanceled()) {
LOG(("Forcefully killing JS for worker [0x%p]",
static_cast<void*>(worker)));
// Since we're going to block this thread we should open up a new thread
// in the thread pool for other workers. Must check the return value to
// make sure we don't decrement when we failed.
PRBool extraThreadAllowed =
NS_SUCCEEDED(gDOMThreadService->ChangeThreadPoolMaxThreads(1));
if (wasSuspended) {
if (extraThreadAllowed) {
gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
}
JS_ResumeRequest(aCx, suspendDepth);
// Flush JIT caches now before suspending to avoid holding memory that we
// are not going to use.
JS_FlushCaches(aCx);
for (;;) {
nsAutoMonitor mon(worker->Pool()->Monitor());
// There's a small chance that the worker was canceled after our check
// above in which case we shouldn't wait here. We're guaranteed not to
// race here because the pool reenters its monitor after canceling each
// worker in order to notify its condition variable.
canceled = worker->IsCanceled();
if (!canceled && worker->IsSuspended()) {
mon.Wait();
}
// Kill execution of the currently running JS.
JS_ClearPendingException(aCx);
return JS_FALSE;
}
// Break out if we're not suspended.
if (!worker->IsSuspended()) {
if (wasSuspended) {
if (extraThreadAllowed) {
gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
}
JS_ResumeRequest(aCx, suspendDepth);
else {
break;
}
return JS_TRUE;
}
if (!wasSuspended) {
// Make sure to suspend our request while we block like this, otherwise we
// prevent GC for everyone.
suspendDepth = JS_SuspendRequest(aCx);
// Since we're going to block this thread we should open up a new thread
// in the thread pool for other workers. Must check the return value to
// make sure we don't decrement when we failed.
extraThreadAllowed =
NS_SUCCEEDED(gDOMThreadService->ChangeThreadPoolMaxThreads(1));
// Flush JIT caches now before suspending to avoid holding memory that we
// are not going to use.
JS_FlushCaches(aCx);
// Only do all this setup once.
wasSuspended = PR_TRUE;
}
nsAutoMonitor mon(worker->Pool()->Monitor());
// There's a small chance that the worker was canceled after our check
// above in which case we shouldn't wait here. We're guaranteed not to race
// here because the pool reenters its monitor after canceling each worker
// in order to notify its condition variable.
if (worker->IsSuspended() && !worker->IsCanceled()) {
mon.Wait();
if (extraThreadAllowed) {
gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
}
}
NS_NOTREACHED("Should never get here!");
return JS_FALSE;
if (canceled) {
LOG(("Forcefully killing JS for worker [0x%p]",
static_cast<void*>(worker)));
// Kill execution of the currently running JS.
JS_ClearPendingException(aCx);
return JS_FALSE;
}
return JS_TRUE;
}
void

Просмотреть файл

@ -236,11 +236,10 @@ Function::Execute(JSContext* cx, PRUint32 argc, jsval* vp)
// suspend the request before we call into the function, since the call
// may block or otherwise take a long time to return.
jsrefcount rc = JS_SuspendRequest(cx);
ffi_call(&mCIF, FFI_FN(mFunc), resultValue.mData, reinterpret_cast<void**>(values.Elements()));
JS_ResumeRequest(cx, rc);
{
JSAutoSuspendRequest suspended(cx);
ffi_call(&mCIF, FFI_FN(mFunc), resultValue.mData, reinterpret_cast<void**>(values.Elements()));
}
// prepare a JS object from the result
jsval rval;

Просмотреть файл

@ -878,6 +878,26 @@ JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth)
#endif
}
JS_PUBLIC_API(void)
JS_TransferRequest(JSContext *cx, JSContext *another)
{
JS_ASSERT(cx != another);
JS_ASSERT(cx->runtime == another->runtime);
#ifdef JS_THREADSAFE
JS_ASSERT(cx->thread);
JS_ASSERT(another->thread);
JS_ASSERT(cx->thread == another->thread);
JS_ASSERT(cx->requestDepth != 0);
JS_ASSERT(another->requestDepth == 0);
/* Serialize access to JSContext::requestDepth from other threads. */
JS_LOCK_GC(cx->runtime);
another->requestDepth = cx->requestDepth;
cx->requestDepth = 0;
JS_UNLOCK_GC(cx->runtime);
#endif
}
JS_PUBLIC_API(void)
JS_Lock(JSRuntime *rt)
{

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
* ***** BEGIN LICENSE BLOCK *****
@ -561,6 +561,9 @@ JS_SuspendRequest(JSContext *cx);
extern JS_PUBLIC_API(void)
JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth);
extern JS_PUBLIC_API(void)
JS_TransferRequest(JSContext *cx, JSContext *another);
#ifdef __cplusplus
JS_END_EXTERN_C
@ -626,6 +629,27 @@ class JSAutoSuspendRequest {
#endif
};
class JSAutoTransferRequest
{
public:
JSAutoTransferRequest(JSContext* cx1, JSContext* cx2)
: cx1(cx1), cx2(cx2) {
if(cx1 != cx2)
JS_TransferRequest(cx1, cx2);
}
~JSAutoTransferRequest() {
if(cx1 != cx2)
JS_TransferRequest(cx2, cx1);
}
private:
JSContext* const cx1;
JSContext* const cx2;
/* Not copyable. */
JSAutoTransferRequest(JSAutoTransferRequest &);
void operator =(JSAutoTransferRequest&);
};
JS_BEGIN_EXTERN_C
#endif

Просмотреть файл

@ -469,12 +469,15 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
size_t len = 0; /* initialize to avoid warnings */
do {
ScheduleWatchdog(cx->runtime, -1);
jsrefcount rc = JS_SuspendRequest(cx);
gCanceled = false;
errno = 0;
char *line = GetLine(file, startline == lineno ? "js> " : "");
char *line;
{
JSAutoSuspendRequest suspended(cx);
line = GetLine(file, startline == lineno ? "js> " : "");
}
if (!line) {
JS_ResumeRequest(cx, rc);
if (errno) {
JS_ReportError(cx, strerror(errno));
free(buffer);
@ -498,7 +501,6 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
if (!newBuf) {
free(buffer);
free(line);
JS_ResumeRequest(cx, rc);
JS_ReportOutOfMemory(cx);
return;
}
@ -512,7 +514,6 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY)
free(line);
}
lineno++;
JS_ResumeRequest(cx, rc);
if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
hitEOF = JS_TRUE;
break;
@ -1920,8 +1921,15 @@ DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
/* burn the leading lines */
line2 = JS_PCToLineNumber(cx, script, pc);
for (line1 = 0; line1 < line2 - 1; line1++)
(void) fgets(linebuf, LINE_BUF_LEN, file); /* Intentionally unused result. */
for (line1 = 0; line1 < line2 - 1; line1++) {
char *tmp = fgets(linebuf, LINE_BUF_LEN, file);
if (!tmp) {
JS_ReportError(cx, "failed to read %s fully",
script->filename);
ok = JS_FALSE;
goto bail;
}
}
bupline = 0;
while (pc < end) {
@ -2947,7 +2955,6 @@ EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
const jschar *src;
size_t srclen;
JSBool lazy, split, ok;
jsval v;
JSStackFrame *fp;
sobj = NULL;
@ -2963,7 +2970,7 @@ EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
}
JS_SetOptions(scx, JS_GetOptions(cx));
JS_BeginRequest(scx);
JS_TransferRequest(cx, scx);
src = JS_GetStringChars(str);
srclen = JS_GetStringLength(str);
split = lazy = JS_FALSE;
@ -2987,12 +2994,12 @@ EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
ok = JS_FALSE;
goto out;
}
v = BOOLEAN_TO_JSVAL(lazy);
ok = JS_SetProperty(cx, sobj, "lazy", &v);
AutoValueRooter root(scx, BOOLEAN_TO_JSVAL(lazy));
ok = JS_SetProperty(scx, sobj, "lazy", root.addr());
if (!ok)
goto out;
if (split)
sobj = split_outerObject(cx, sobj);
sobj = split_outerObject(scx, sobj);
}
if (srclen == 0) {
@ -3002,7 +3009,7 @@ EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
fp = JS_GetScriptedCaller(cx, NULL);
JS_SetGlobalObject(scx, sobj);
JS_ToggleOptions(scx, JSOPTION_DONT_REPORT_UNCAUGHT);
OBJ_TO_INNER_OBJECT(cx, sobj);
OBJ_TO_INNER_OBJECT(scx, sobj);
if (!sobj) {
ok = JS_FALSE;
goto out;
@ -3012,16 +3019,18 @@ EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
JS_PCToLineNumber(cx, fp->script,
fp->regs->pc),
rval);
if (!ok) {
if (JS_GetPendingException(scx, &v))
JS_SetPendingException(cx, v);
else
JS_ReportOutOfMemory(cx);
}
}
out:
JS_EndRequest(scx);
jsval exceptionValue = JSVAL_NULL;
JSBool exception = !ok && JS_GetPendingException(scx, &exceptionValue);
JS_TransferRequest(scx, cx);
if (exception)
JS_SetPendingException(cx, exceptionValue);
else if (!ok)
JS_ClearPendingException(cx);
WITH_LOCKED_CONTEXT_LIST(
JS_DestroyContextNoGC(scx)
);
@ -3135,7 +3144,7 @@ Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
if (t_ticks == 0) {
JS_YieldRequest(cx);
} else {
jsrefcount rc = JS_SuspendRequest(cx);
JSAutoSuspendRequest suspended(cx);
PR_Lock(gWatchdogLock);
PRIntervalTime to_wakeup = PR_IntervalNow() + t_ticks;
for (;;) {
@ -3148,7 +3157,6 @@ Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
t_ticks = to_wakeup - now;
}
PR_Unlock(gWatchdogLock);
JS_ResumeRequest(cx, rc);
}
return !gCanceled;
}
@ -3236,9 +3244,9 @@ Scatter(JSContext *cx, uintN argc, jsval *vp)
jsuint n; /* number of threads */
JSObject *inArr;
JSObject *arr;
JSObject *global;
ScatterData sd;
JSBool ok;
jsrefcount rc;
sd.lock = NULL;
sd.cvar = NULL;
@ -3305,6 +3313,7 @@ Scatter(JSContext *cx, uintN argc, jsval *vp)
}
}
global = JS_GetGlobalObject(cx);
for (i = 1; i < n; i++) {
JSContext *newcx;
WITH_LOCKED_CONTEXT_LIST(
@ -3312,9 +3321,11 @@ Scatter(JSContext *cx, uintN argc, jsval *vp)
);
if (!newcx)
goto fail;
JS_BeginRequest(newcx);
JS_SetGlobalObject(newcx, JS_GetGlobalObject(cx));
JS_EndRequest(newcx);
{
JSAutoTransferRequest transfer(cx, newcx);
JS_SetGlobalObject(newcx, global);
}
JS_ClearContextThread(newcx);
sd.threads[i].cx = newcx;
}
@ -3347,11 +3358,12 @@ Scatter(JSContext *cx, uintN argc, jsval *vp)
DoScatteredWork(cx, &sd.threads[0]);
rc = JS_SuspendRequest(cx);
for (i = 1; i < n; i++) {
PR_JoinThread(sd.threads[i].thr);
{
JSAutoSuspendRequest suspended(cx);
for (i = 1; i < n; i++) {
PR_JoinThread(sd.threads[i].thr);
}
}
JS_ResumeRequest(cx, rc);
success:
arr = JS_NewArrayObject(cx, n, sd.results);

Просмотреть файл

@ -195,7 +195,7 @@ JSBool XPCDispObject::Dispatch(XPCCallContext& ccx, IDispatch * disp,
// Scope the lock
{
// avoid deadlock in case the native method blocks somehow
AutoJSSuspendRequest req(ccx); // scoped suspend of request
JSAutoSuspendRequest req(ccx); // scoped suspend of request
// call IDispatch's invoke
invokeResult= disp->Invoke(
dispID, // IDispatch ID

Просмотреть файл

@ -2179,7 +2179,7 @@ nsXPConnect::UpdateXOWs(JSContext* aJSContext,
if(!list)
return NS_OK; // No wrappers to update.
AutoJSRequestWithNoCallContext req(aJSContext);
JSAutoRequest req(aJSContext);
Link* cur = list;
if(cur->obj && !PerformOp(aJSContext, aWay, cur->obj))

Просмотреть файл

@ -3221,7 +3221,7 @@ xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop)
if (!tempcx)
return NS_ERROR_OUT_OF_MEMORY;
AutoJSRequestWithNoCallContext req(tempcx);
JSAutoRequest req(tempcx);
JSObject *sandbox = JS_NewObject(tempcx, &SandboxClass, nsnull, nsnull);
if (!sandbox)
return NS_ERROR_XPC_UNEXPECTED;
@ -3592,7 +3592,7 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
nsresult rv = NS_OK;
{
AutoJSRequestWithNoCallContext req(sandcx->GetJSContext());
JSAutoRequest req(sandcx->GetJSContext());
JSString *str = nsnull;
if (!JS_EvaluateUCScriptForPrincipals(sandcx->GetJSContext(), sandbox,
jsPrincipals,
@ -3608,9 +3608,7 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
// Stash the exception in |cx| so we can execute code on
// sandcx without a pending exception.
{
AutoJSSuspendRequestWithNoCallContext sus(sandcx->GetJSContext());
AutoJSRequestWithNoCallContext cxreq(cx);
JSAutoTransferRequest transfer(sandcx->GetJSContext(), cx);
JS_SetPendingException(cx, exn);
}
@ -3620,8 +3618,7 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
// exception into a string.
str = JS_ValueToString(sandcx->GetJSContext(), exn);
AutoJSSuspendRequestWithNoCallContext sus(sandcx->GetJSContext());
AutoJSRequestWithNoCallContext cxreq(cx);
JSAutoTransferRequest transfer(sandcx->GetJSContext(), cx);
if (str) {
// We converted the exception to a string. Use that
// as the value exception.

Просмотреть файл

@ -157,7 +157,7 @@ LogSlimWrapperNotCreated(JSContext *cx, nsISupports *obj, const char *reason)
className ? " for " : "", className ? className : "", reason, obj);
if(className)
PR_Free(className);
AutoJSRequestWithNoCallContext autoRequest(cx);
JSAutoRequest autoRequest(cx);
xpc_DumpJSStack(cx, JS_FALSE, JS_FALSE, JS_FALSE);
}
#endif

Просмотреть файл

@ -3771,136 +3771,6 @@ private:
nsCString mCategory;
};
/***************************************************************************/
// XXX allowing for future notifications to XPCCallContext
class AutoJSRequest
{
public:
AutoJSRequest(XPCCallContext& aCCX)
: mCCX(aCCX), mCX(aCCX.GetJSContext()) {BeginRequest();}
~AutoJSRequest() {EndRequest();}
void EndRequest() {
if(mCX) {
JS_EndRequest(mCX);
mCX = nsnull;
}
}
private:
void BeginRequest() {
if(JS_GetContextThread(mCX))
JS_BeginRequest(mCX);
else
mCX = nsnull;
}
private:
XPCCallContext& mCCX;
JSContext* mCX;
};
class AutoJSSuspendRequest
{
public:
AutoJSSuspendRequest(XPCCallContext& aCCX)
: mCX(aCCX.GetJSContext()) {SuspendRequest();}
~AutoJSSuspendRequest() {ResumeRequest();}
void ResumeRequest() {
if(mCX) {
JS_ResumeRequest(mCX, mDepth);
mCX = nsnull;
}
}
private:
void SuspendRequest() {
if(JS_GetContextThread(mCX))
mDepth = JS_SuspendRequest(mCX);
else
mCX = nsnull;
}
private:
JSContext* mCX;
jsrefcount mDepth;
};
class AutoJSSuspendRequestWithNoCallContext
{
public:
AutoJSSuspendRequestWithNoCallContext(JSContext *aCX)
: mCX(aCX) {SuspendRequest();}
~AutoJSSuspendRequestWithNoCallContext() {ResumeRequest();}
void ResumeRequest() {
if(mCX) {
JS_ResumeRequest(mCX, mDepth);
mCX = nsnull;
}
}
private:
void SuspendRequest() {
if(JS_GetContextThread(mCX))
mDepth = JS_SuspendRequest(mCX);
else
mCX = nsnull;
}
private:
JSContext* mCX;
jsrefcount mDepth;
};
class AutoJSSuspendNonMainThreadRequest
{
public:
AutoJSSuspendNonMainThreadRequest(JSContext *aCX)
: mCX(aCX) {SuspendRequest();}
~AutoJSSuspendNonMainThreadRequest() {ResumeRequest();}
void ResumeRequest() {
if (mCX) {
JS_ResumeRequest(mCX, mDepth);
mCX = nsnull;
}
}
private:
void SuspendRequest() {
if (mCX && !XPCPerThreadData::IsMainThread(mCX))
mDepth = JS_SuspendRequest(mCX);
else
mCX = nsnull;
}
JSContext *mCX;
jsrefcount mDepth;
};
/*****************************************/
class AutoJSRequestWithNoCallContext
{
public:
AutoJSRequestWithNoCallContext(JSContext* aCX) : mCX(aCX) {BeginRequest();}
~AutoJSRequestWithNoCallContext() {EndRequest();}
void EndRequest() {
if(mCX) {
JS_EndRequest(mCX);
mCX = nsnull;
}
}
private:
void BeginRequest() {
if(JS_GetContextThread(mCX))
JS_BeginRequest(mCX);
else
mCX = nsnull;
}
private:
JSContext* mCX;
};
/***************************************************************************/
class AutoJSErrorAndExceptionEater
{

Просмотреть файл

@ -248,12 +248,12 @@ XPCJSContextStack::GetSafeJSContext(JSContext * *aSafeJSContext)
if(xpc && (xpcrt = xpc->GetRuntime()) && (rt = xpcrt->GetJSRuntime()))
{
JSObject *glob;
mSafeJSContext = JS_NewContext(rt, 8192);
if(mSafeJSContext)
{
// scoped JS Request
AutoJSRequestWithNoCallContext req(mSafeJSContext);
JSObject *glob;
JSAutoRequest req(mSafeJSContext);
glob = JS_NewObject(mSafeJSContext, &global_class, NULL, NULL);
#ifndef XPCONNECT_STANDALONE
@ -276,23 +276,26 @@ XPCJSContextStack::GetSafeJSContext(JSContext * *aSafeJSContext)
// nsCOMPtr or dealt with, or we'll release in the finalize
// hook.
#endif
if(!glob || NS_FAILED(xpc->InitClasses(mSafeJSContext, glob)))
if(glob && NS_FAILED(xpc->InitClasses(mSafeJSContext, glob)))
{
// Explicitly end the request since we are about to kill
// the JSContext that 'req' will try to use when it
// goes out of scope.
req.EndRequest();
JS_DestroyContext(mSafeJSContext);
mSafeJSContext = nsnull;
glob = nsnull;
}
// Save it off so we can destroy it later, even if
// mSafeJSContext has been set to another context
// via SetSafeJSContext. If we don't get here,
// then mSafeJSContext must have been set via
// SetSafeJSContext, and we're not responsible for
// destroying the passed-in context.
mOwnSafeJSContext = mSafeJSContext;
}
if(!glob && mSafeJSContext)
{
// Destroy the context outside the scope of JSAutoRequest that
// uses the context in its destructor.
JS_DestroyContext(mSafeJSContext);
mSafeJSContext = nsnull;
}
// Save it off so we can destroy it later, even if
// mSafeJSContext has been set to another context
// via SetSafeJSContext. If we don't get here,
// then mSafeJSContext must have been set via
// SetSafeJSContext, and we're not responsible for
// destroying the passed-in context.
mOwnSafeJSContext = mSafeJSContext;
}
}

Просмотреть файл

@ -2310,8 +2310,15 @@ XPCWrappedNative::CallMethod(XPCCallContext& ccx,
nsISupports* qiresult = nsnull;
{
AutoJSSuspendNonMainThreadRequest req(ccx.GetJSContext());
invokeResult = callee->QueryInterface(*iid, (void**) &qiresult);
if(XPCPerThreadData::IsMainThread(ccx))
{
invokeResult = callee->QueryInterface(*iid, (void**) &qiresult);
}
else
{
JSAutoSuspendRequest suspended(ccx);
invokeResult = callee->QueryInterface(*iid, (void**) &qiresult);
}
}
xpcc->SetLastResult(invokeResult);
@ -2721,10 +2728,18 @@ XPCWrappedNative::CallMethod(XPCCallContext& ccx,
// do the invoke
{
AutoJSSuspendNonMainThreadRequest req(ccx.GetJSContext());
invokeResult = NS_InvokeByIndex(callee, vtblIndex,
paramCount + wantsOptArgc,
dispatchParams);
uint8 allParamCount = paramCount + wantsOptArgc;
if(XPCPerThreadData::IsMainThread(ccx))
{
invokeResult = NS_InvokeByIndex(callee, vtblIndex,
allParamCount, dispatchParams);
}
else
{
JSAutoSuspendRequest suspended(ccx);
invokeResult = NS_InvokeByIndex(callee, vtblIndex,
allParamCount, dispatchParams);
}
}
xpcc->SetLastResult(invokeResult);

Просмотреть файл

@ -203,19 +203,11 @@ XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface)
const char *memberName = iface->GetMemberName(ccx, this);
jsrefcount suspendDepth = 0;
if(cx != ccx) {
// Switching contexts, suspend the old and enter the new request.
suspendDepth = JS_SuspendRequest(ccx);
JS_BeginRequest(cx);
}
JSFunction *fun = JS_NewFunction(cx, callback, argc, flags, nsnull,
memberName);
if(suspendDepth) {
JS_EndRequest(cx);
JS_ResumeRequest(ccx, suspendDepth);
JSFunction *fun;
// Switching contexts, suspend the old and enter the new request.
{
JSAutoTransferRequest transfer(ccx, cx);
fun = JS_NewFunction(cx, callback, argc, flags, nsnull, memberName);
}
if(!fun)