mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Don't fatten a flyweight lock unnecessarily in JS_SetPrototype; misc. cleanups (63097, r=mccabe, sr=jband).
This commit is contained in:
parent
1c49ac5fbf
commit
7b1d57a4dc
@ -1161,7 +1161,7 @@ DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
return JS_FALSE;
|
||||
bytes = JS_GetStringBytes(str);
|
||||
if (strcmp(bytes, "arena") == 0) {
|
||||
#ifdef ARENAMETER
|
||||
#ifdef JS_ARENAMETER
|
||||
JS_DumpArenaStats(stdout);
|
||||
#endif
|
||||
} else if (strcmp(bytes, "atom") == 0) {
|
||||
|
@ -799,9 +799,7 @@ JS_EndRequest(JSContext *cx)
|
||||
scope->u.count = 0; /* don't assume NULL puns as 0 */
|
||||
scope->ownercx = NULL; /* NB: set last, after lock init */
|
||||
nshares++;
|
||||
#ifdef DEBUG
|
||||
JS_ATOMIC_INCREMENT(&rt->sharedScopes);
|
||||
#endif
|
||||
JS_RUNTIME_METER(rt, sharedScopes);
|
||||
}
|
||||
}
|
||||
if (nshares)
|
||||
@ -1968,6 +1966,7 @@ DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,
|
||||
if (attrs & JSPROP_INDEX) {
|
||||
id = INT_TO_JSVAL((jsint)name);
|
||||
atom = NULL;
|
||||
attrs &= ~JSPROP_INDEX;
|
||||
} else {
|
||||
atom = js_Atomize(cx, name, strlen(name), 0);
|
||||
if (!atom)
|
||||
@ -2054,7 +2053,12 @@ JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps)
|
||||
if (prop) {
|
||||
if (OBJ_IS_NATIVE(obj)) {
|
||||
sprop = (JSScopeProperty *)prop;
|
||||
#ifdef JS_DOUBLE_HASHING
|
||||
sprop->attrs |= JSPROP_INDEX;
|
||||
sprop->tinyid = ps->tinyid;
|
||||
#else
|
||||
sprop->id = INT_TO_JSVAL(ps->tinyid);
|
||||
#endif
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, obj, prop);
|
||||
}
|
||||
@ -2085,7 +2089,12 @@ JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name,
|
||||
if (ok && prop) {
|
||||
if (OBJ_IS_NATIVE(obj)) {
|
||||
sprop = (JSScopeProperty *)prop;
|
||||
#ifdef JS_DOUBLE_HASHING
|
||||
sprop->attrs |= JSPROP_INDEX;
|
||||
sprop->tinyid = tinyid;
|
||||
#else
|
||||
sprop->id = INT_TO_JSVAL(tinyid);
|
||||
#endif
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, obj, prop);
|
||||
}
|
||||
@ -2358,7 +2367,12 @@ JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj,
|
||||
if (ok && prop) {
|
||||
if (OBJ_IS_NATIVE(obj)) {
|
||||
sprop = (JSScopeProperty *)prop;
|
||||
#ifdef JS_DOUBLE_HASHING
|
||||
sprop->attrs |= JSPROP_INDEX;
|
||||
sprop->tinyid = tinyid;
|
||||
#else
|
||||
sprop->id = INT_TO_JSVAL(tinyid);
|
||||
#endif
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, obj, prop);
|
||||
}
|
||||
|
@ -255,9 +255,7 @@ js_LiveContext(JSRuntime *rt, JSContext *cx)
|
||||
if (cl == &cx->links)
|
||||
return JS_TRUE;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
JS_ATOMIC_INCREMENT(&rt->deadContexts);
|
||||
#endif
|
||||
JS_RUNTIME_METER(rt, deadContexts);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -157,6 +157,7 @@ struct JSRuntime {
|
||||
|
||||
/* Used to serialize cycle checks when setting __proto__ or __parent__. */
|
||||
PRLock *setSlotLock;
|
||||
JSScope *setSlotScope; /* deadlock avoidance, see jslock.c */
|
||||
|
||||
/*
|
||||
* State for sharing single-threaded scopes, once a second thread tries to
|
||||
@ -196,9 +197,23 @@ struct JSRuntime {
|
||||
jsrefcount liveScopes;
|
||||
jsrefcount sharedScopes;
|
||||
jsrefcount totalScopes;
|
||||
|
||||
/* String instrumentation. */
|
||||
jsrefcount liveStrings;
|
||||
jsrefcount totalStrings;
|
||||
double lengthSum;
|
||||
double lengthSquaredSum;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
# define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which)
|
||||
# define JS_RUNTIME_UNMETER(rt, which) JS_ATOMIC_DECREMENT(&(rt)->which)
|
||||
#else
|
||||
# define JS_RUNTIME_METER(rt, which) /* nothing */
|
||||
# define JS_RUNTIME_UNMETER(rt, which) /* nothing */
|
||||
#endif
|
||||
|
||||
#define JS_ENABLE_GC(rt) JS_ATOMIC_DECREMENT(&(rt)->gcDisabled);
|
||||
#define JS_DISABLE_GC(rt) JS_ATOMIC_INCREMENT(&(rt)->gcDisabled);
|
||||
|
||||
|
@ -565,12 +565,6 @@ ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
# define METER_INVOCATION(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which)
|
||||
#else
|
||||
# define METER_INVOCATION(rt, which) /* nothing */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Find a function reference and its 'this' object implicit first parameter
|
||||
* under argc arguments on cx's stack, and call the function. Push missing
|
||||
@ -788,7 +782,7 @@ have_fun:
|
||||
frame.varobj = fp->varobj;
|
||||
frame.scopeChain = fp->scopeChain;
|
||||
ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
|
||||
METER_INVOCATION(cx->runtime, nativeCalls);
|
||||
JS_RUNTIME_METER(cx->runtime, nativeCalls);
|
||||
} else if (script) {
|
||||
/* Use parent scope so js_GetCallObject can find the right "Call". */
|
||||
frame.scopeChain = parent;
|
||||
@ -2298,7 +2292,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
goto out;
|
||||
}
|
||||
obj = JSVAL_TO_OBJECT(rval);
|
||||
METER_INVOCATION(rt, constructs);
|
||||
JS_RUNTIME_METER(rt, constructs);
|
||||
break;
|
||||
|
||||
case JSOP_DELNAME:
|
||||
@ -2601,7 +2595,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
pc = script->code;
|
||||
endpc = pc + script->length;
|
||||
inlineCallCount++;
|
||||
METER_INVOCATION(rt, inlineCalls);
|
||||
JS_RUNTIME_METER(rt, inlineCalls);
|
||||
continue;
|
||||
|
||||
bad_inline_call:
|
||||
@ -2614,7 +2608,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
RESTORE_SP(fp);
|
||||
if (!ok)
|
||||
goto out;
|
||||
METER_INVOCATION(rt, nonInlineCalls);
|
||||
JS_RUNTIME_METER(rt, nonInlineCalls);
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
if (cx->rval2set) {
|
||||
/*
|
||||
|
@ -246,12 +246,6 @@ js_unlog_scope(JSScope *scope)
|
||||
|
||||
#endif /* DEBUG_SCOPE_COUNT */
|
||||
|
||||
#ifdef DEBUG
|
||||
# define METER_LOCK_EVENT(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which)
|
||||
#else
|
||||
# define METER_LOCK_EVENT(rt, which) /* nothing */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return true if scope's ownercx, or the ownercx of a single-threaded scope
|
||||
* for which ownercx is waiting to become multi-threaded and shared, is cx.
|
||||
@ -268,7 +262,7 @@ WillDeadlock(JSScope *scope, JSContext *cx)
|
||||
do {
|
||||
ownercx = scope->ownercx;
|
||||
if (ownercx == cx) {
|
||||
METER_LOCK_EVENT(cx->runtime, deadlocksAvoided);
|
||||
JS_RUNTIME_METER(cx->runtime, deadlocksAvoided);
|
||||
return JS_TRUE;
|
||||
}
|
||||
} while (ownercx && (scope = ownercx->scopeToShare) != NULL);
|
||||
@ -276,8 +270,11 @@ WillDeadlock(JSScope *scope, JSContext *cx)
|
||||
}
|
||||
|
||||
/*
|
||||
* Make scope multi-threaded, i.e. shared its ownership among contexts in rt
|
||||
* using a "thin" or (if necessary due to contention) "fat" lock.
|
||||
* Make scope multi-threaded, i.e. share its ownership among contexts in rt
|
||||
* using a "thin" or (if necessary due to contention) "fat" lock. Called only
|
||||
* from ClaimScope, immediately below, when we detect deadlock were we to wait
|
||||
* for scope's lock, because its ownercx is waiting on a scope owned by the
|
||||
* calling cx.
|
||||
*
|
||||
* (i) rt->gcLock held
|
||||
*/
|
||||
@ -295,9 +292,32 @@ ShareScope(JSRuntime *rt, JSScope *scope)
|
||||
JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone);
|
||||
}
|
||||
js_InitLock(&scope->lock);
|
||||
scope->u.count = 0;
|
||||
if (scope == rt->setSlotScope) {
|
||||
/*
|
||||
* Nesting locks on another thread that's using scope->ownercx: give
|
||||
* the held lock a reentrancy count of 1 and set its lock.owner field
|
||||
* directly (no compare-and-swap needed while scope->ownercx is still
|
||||
* non-null). See below in ClaimScope, before the ShareScope call,
|
||||
* for more on why this is necessary.
|
||||
*
|
||||
* If NSPR_LOCK is defined, we cannot deadlock holding rt->gcLock and
|
||||
* acquiring scope->lock.fat here, against another thread holding that
|
||||
* fat lock and trying to grab rt->gcLock. This is because no other
|
||||
* thread can attempt to acquire scope->lock.fat until scope->ownercx
|
||||
* is null *and* our thread has released rt->gcLock, which interlocks
|
||||
* scope->ownercx's transition to null against tests of that member
|
||||
* in ClaimScope.
|
||||
*/
|
||||
scope->lock.owner = scope->ownercx->thread;
|
||||
#ifdef NSPR_LOCK
|
||||
JS_ACQUIRE_LOCK((JSLock*)scope->lock.fat);
|
||||
#endif
|
||||
scope->u.count = 1;
|
||||
} else {
|
||||
scope->u.count = 0;
|
||||
}
|
||||
scope->ownercx = NULL; /* NB: set last, after lock init */
|
||||
METER_LOCK_EVENT(rt, sharedScopes);
|
||||
JS_RUNTIME_METER(rt, sharedScopes);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -317,7 +337,7 @@ ClaimScope(JSScope *scope, JSContext *cx)
|
||||
PRStatus stat;
|
||||
|
||||
rt = cx->runtime;
|
||||
METER_LOCK_EVENT(rt, claimAttempts);
|
||||
JS_RUNTIME_METER(rt, claimAttempts);
|
||||
JS_LOCK_GC(rt);
|
||||
|
||||
/* Reload in case ownercx went away while we blocked on the lock. */
|
||||
@ -343,7 +363,7 @@ ClaimScope(JSScope *scope, JSContext *cx)
|
||||
JS_ASSERT(scope->u.count == 0);
|
||||
scope->ownercx = cx;
|
||||
JS_UNLOCK_GC(rt);
|
||||
METER_LOCK_EVENT(rt, claimedScopes);
|
||||
JS_RUNTIME_METER(rt, claimedScopes);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -357,10 +377,18 @@ ClaimScope(JSScope *scope, JSContext *cx)
|
||||
* could hold locks on scope, we would need to keep reentrancy counts
|
||||
* for all such "flyweight" (ownercx != NULL) locks, so that control
|
||||
* would unwind properly once these locks became "thin" or "fat".
|
||||
* Apart from the js_SetProtoOrParent exception, the engine promotes
|
||||
* a scope from exclusive to shared access only when locking, never
|
||||
* when holding or unlocking.
|
||||
*
|
||||
* In the case of js_SetProtoOrParent, we ensure that the "outer"
|
||||
* scope's lock is not flyweight before that function tries to hold
|
||||
* the inner scope's lock.
|
||||
* If ownercx's thread is calling js_SetProtoOrParent, trying to lock
|
||||
* the inner scope (the scope of the object being set as the prototype
|
||||
* of the outer object), ShareScope will find the outer object's scope
|
||||
* at rt->setSlotScope. If it's the same as scope, we give it a lock
|
||||
* held by ownercx's thread with reentrancy count of 1, then we return
|
||||
* here and break. After that we unwind to js_[GS]etSlotThreadSafe or
|
||||
* js_LockScope (our caller), where we wait on the newly-fattened lock
|
||||
* until ownercx's thread unwinds from js_SetProtoOrParent.
|
||||
*/
|
||||
if (ownercx->scopeToShare &&
|
||||
WillDeadlock(ownercx->scopeToShare, cx)) {
|
||||
@ -891,19 +919,6 @@ js_UnlockRuntime(JSRuntime *rt)
|
||||
PR_Unlock(rt->rtLock);
|
||||
}
|
||||
|
||||
void
|
||||
js_PromoteScopeLock(JSContext *cx, JSScope *scope)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
|
||||
JS_ASSERT(cx == scope->ownercx);
|
||||
rt = cx->runtime;
|
||||
JS_LOCK_GC(rt);
|
||||
ShareScope(rt, scope);
|
||||
JS_UNLOCK_GC(rt);
|
||||
js_LockScope(cx, scope);
|
||||
}
|
||||
|
||||
void
|
||||
js_LockScope(JSContext *cx, JSScope *scope)
|
||||
{
|
||||
|
@ -152,7 +152,6 @@ extern void js_LockRuntime(JSRuntime *rt);
|
||||
extern void js_UnlockRuntime(JSRuntime *rt);
|
||||
extern void js_LockObj(JSContext *cx, JSObject *obj);
|
||||
extern void js_UnlockObj(JSContext *cx, JSObject *obj);
|
||||
extern void js_PromoteScopeLock(JSContext *cx, JSScope *scope);
|
||||
extern void js_LockScope(JSContext *cx, JSScope *scope);
|
||||
extern void js_UnlockScope(JSContext *cx, JSScope *scope);
|
||||
extern int js_SetupLocks(int,int);
|
||||
|
@ -230,7 +230,6 @@ js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
|
||||
* rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
|
||||
* etc...
|
||||
* (2) rt->setSlotLock < obj's scope lock < pobj's scope lock.
|
||||
* rt->setSlotLock < obj's scope lock < rt->gcLock
|
||||
*
|
||||
* We avoid AB-BA deadlock by restricting obj from being on pobj's parent
|
||||
* or proto chain (pobj may already be on obj's parent or proto chain; it
|
||||
@ -275,14 +274,13 @@ js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
|
||||
} else if (OBJ_IS_NATIVE(pobj) && OBJ_SCOPE(pobj) != scope) {
|
||||
#ifdef JS_THREADSAFE
|
||||
/*
|
||||
* Avoid deadlock by never nesting a scope lock (for pobj, in
|
||||
* this case) within a "flyweight" scope lock (for obj). Give
|
||||
* scope a non-flyweight lock, allowing it to be shared among
|
||||
* multiple threads. See ClaimScope in jslock.c.
|
||||
* We are about to nest scope locks. Help jslock.c:ShareScope
|
||||
* keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
|
||||
* avoiding deadlock, by recording scope in rt->setSlotScope.
|
||||
*/
|
||||
if (scope->ownercx) {
|
||||
JS_ASSERT(scope->ownercx == cx);
|
||||
js_PromoteScopeLock(cx, scope);
|
||||
rt->setSlotScope = scope;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -293,6 +291,9 @@ js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
|
||||
js_DropObjectMap(cx, &scope->map, obj);
|
||||
JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
|
||||
scope = newscope;
|
||||
#ifdef JS_THREADSAFE
|
||||
rt->setSlotScope = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
|
||||
|
@ -2298,9 +2298,41 @@ js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag)
|
||||
return NULL;
|
||||
str->length = length;
|
||||
str->chars = chars;
|
||||
#ifdef DEBUG
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JS_RUNTIME_METER(rt, liveStrings);
|
||||
JS_RUNTIME_METER(rt, totalStrings);
|
||||
JS_LOCK_RUNTIME_VOID(rt,
|
||||
(rt->lengthSum += (double)length,
|
||||
rt->lengthSquaredSum += (double)length * (double)length));
|
||||
}
|
||||
#endif
|
||||
return str;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <math.h>
|
||||
|
||||
void printJSStringStats(JSRuntime *rt) {
|
||||
double mean = 0., var = 0., sigma = 0.;
|
||||
jsrefcount count = rt->totalStrings;
|
||||
if (count > 0 && rt->lengthSum >= 0) {
|
||||
mean = rt->lengthSum / count;
|
||||
var = count * rt->lengthSquaredSum - rt->lengthSum * rt->lengthSum;
|
||||
if (var < 0.0 || count <= 1)
|
||||
var = 0.0;
|
||||
else
|
||||
var /= count * (count - 1);
|
||||
|
||||
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
|
||||
sigma = (var != 0.) ? sqrt(var) : 0.;
|
||||
}
|
||||
fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n",
|
||||
(unsigned long)count, mean, sigma);
|
||||
}
|
||||
#endif
|
||||
|
||||
JSString *
|
||||
js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag)
|
||||
{
|
||||
@ -2349,6 +2381,7 @@ js_FinalizeString(JSContext *cx, JSString *str)
|
||||
JSHashNumber hash;
|
||||
JSHashEntry *he, **hep;
|
||||
|
||||
JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
|
||||
if (str->chars) {
|
||||
JS_free(cx, str->chars);
|
||||
str->chars = NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user