bug 538463 - caching only single-threaded objects. r=jorendorff

This commit is contained in:
Igor Bukanov 2010-02-03 12:46:10 +03:00
Родитель 8784daeda5
Коммит fb2a22b4e8
12 изменённых файлов: 74 добавлений и 96 удалений

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

@ -2984,10 +2984,10 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
js_ShareWaitingTitles(cx);
/*
* Make sure that the GC from another thread respects
* GC_KEEP_ATOMS.
* Make sure that the last-ditch GC call on this thread keeps
* atoms even if another thread runs a full GC.
*/
if (gckind & GC_KEEP_ATOMS)
if (gckind == GC_LAST_DITCH)
JS_KEEP_ATOMS(rt);
/*
@ -3000,7 +3000,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
} while (rt->gcLevel > 0);
cx->thread->gcWaiting = false;
if (gckind & GC_KEEP_ATOMS)
if (gckind == GC_LAST_DITCH)
JS_UNKEEP_ATOMS(rt);
rt->requestCount += requestDebit;
}
@ -3149,20 +3149,9 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
}
#endif
if (gckind & GC_KEEP_ATOMS) {
/*
* The set slot request and last ditch GC kinds preserve all atoms and
* weak roots.
*/
keepAtoms = true;
} else {
/*
* Query rt->gcKeepAtoms only when we know that all other threads are
* suspended, see bug 541790.
*/
keepAtoms = (rt->gcKeepAtoms != 0);
/* The last-ditch GC preserves weak roots. */
if (gckind != GC_LAST_DITCH)
JS_CLEAR_WEAK_ROOTS(&cx->weakRoots);
}
restart:
rt->gcNumber++;
@ -3181,6 +3170,12 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
JS_ASSERT(!a->info.hasMarkedDoubles);
#endif
/*
* Do not collect atoms if explicitly requested or during the last-ditch
* GC. We query rt->gcKeepAtoms only when we know that all other threads
* are suspended, see bug 541790.
*/
keepAtoms = (gckind == GC_LAST_DITCH) || (rt->gcKeepAtoms != 0);
js_TraceRuntime(&trc, keepAtoms);
js_MarkScriptFilenames(rt, keepAtoms);
@ -3395,7 +3390,7 @@ out:
JSWeakRoots savedWeakRoots;
JSTempValueRooter tvr;
if (gckind & GC_KEEP_ATOMS) {
if (gckind == GC_LAST_DITCH) {
/*
* We allow JSGC_END implementation to force a full GC or allocate
* new GC things. Thus we must protect the weak roots from garbage
@ -3409,7 +3404,7 @@ out:
(void) callback(cx, JSGC_END);
if (gckind & GC_KEEP_ATOMS) {
if (gckind == GC_LAST_DITCH) {
JS_LOCK_GC(rt);
JS_UNKEEP_ATOMS(rt);
JS_POP_TEMP_ROOT(cx, &tvr);

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

@ -208,11 +208,8 @@ typedef enum JSGCInvocationKind {
/*
* Flag bit telling js_GC that the caller has already acquired rt->gcLock.
* Currently, this flag is set for the invocation kinds that also preserve
* atoms and weak roots, so we don't need another bit for GC_KEEP_ATOMS.
*/
GC_LOCK_HELD = 0x10,
GC_KEEP_ATOMS = GC_LOCK_HELD,
/*
* Called from js_SetProtoOrParent with a request to set an object's proto

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

@ -75,6 +75,7 @@
#include "jsvector.h"
#include "jsatominlines.h"
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
@ -460,7 +461,7 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
--vcap;
}
if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(vcap))) {
if (js_MatchPropertyCacheShape(cx, pobj, PCVCAP_SHAPE(vcap))) {
#ifdef DEBUG
jsid id = ATOM_TO_JSID(atom);

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

@ -368,6 +368,9 @@ typedef struct JSPropertyCache {
#define PCVAL_TO_SPROP(v) ((JSScopeProperty *) PCVAL_CLRTAG(v))
#define SPROP_TO_PCVAL(sprop) PCVAL_SETTAG(sprop, PCVAL_SPROP)
inline bool
js_MatchPropertyCacheShape(JSContext *cx, JSObject *obj, uint32 shape);
/*
* Fill property cache entry for key cx->fp->pc, optimized value word computed
* from obj and sprop, and entry capability forged from 24-bit OBJ_SHAPE(obj),
@ -414,7 +417,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
pobj = tmp_; \
} \
\
if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(entry->vcap))) { \
if (js_MatchPropertyCacheShape(cx,pobj,PCVCAP_SHAPE(entry->vcap))){\
PCMETER(cache_->pchits++); \
PCMETER(!PCVCAP_TAG(entry->vcap) || cache_->protopchits++); \
atom = NULL; \

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

@ -1337,17 +1337,6 @@ js_UnlockObj(JSContext *cx, JSObject *obj)
js_UnlockTitle(cx, &OBJ_SCOPE(obj)->title);
}
bool
js_LockObjIfShape(JSContext *cx, JSObject *obj, uint32 shape)
{
JS_ASSERT(OBJ_SCOPE(obj)->title.ownercx != cx);
js_LockObj(cx, obj);
if (OBJ_SHAPE(obj) == shape)
return true;
js_UnlockObj(cx, obj);
return false;
}
void
js_InitTitle(JSContext *cx, JSTitle *title)
{

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

@ -163,16 +163,6 @@ struct JSTitle {
#define JS_UNLOCK_OBJ(cx,obj) (CX_OWNS_SCOPE_TITLE(cx, OBJ_SCOPE(obj)) \
? (void)0 : js_UnlockObj(cx, obj))
/*
* Lock object only if its scope has the given shape.
*/
#define JS_LOCK_OBJ_IF_SHAPE(cx,obj,shape) \
(OBJ_SHAPE(obj) == (shape) \
? (OBJ_SCOPE(obj)->title.ownercx == (cx) \
? true \
: js_LockObjIfShape(cx, obj, shape)) \
: false)
#define JS_LOCK_TITLE(cx,title) \
((title)->ownercx == (cx) ? (void)0 \
: (js_LockTitle(cx, (title)), \
@ -197,7 +187,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 bool js_LockObjIfShape(JSContext *cx, JSObject *obj, uint32 shape);
extern void js_InitTitle(JSContext *cx, JSTitle *title);
extern void js_FinishTitle(JSContext *cx, JSTitle *title);
extern void js_LockTitle(JSContext *cx, JSTitle *title);
@ -267,9 +256,7 @@ extern void js_SetScopeInfo(JSScope *scope, const char *file, int line);
#define CX_OWNS_SCOPE_TITLE(cx,obj) true
#define JS_LOCK_OBJ(cx,obj) ((void)0)
#define JS_UNLOCK_OBJ(cx,obj) ((void)0)
#define JS_LOCK_OBJ_IF_SHAPE(cx,obj,shape) (OBJ_SHAPE(obj) == (shape))
#define JS_LOCK_OBJ_VOID(cx,obj,e) (e)
#define JS_LOCK_SCOPE(cx,scope) ((void)0)
#define JS_UNLOCK_SCOPE(cx,scope) ((void)0)
#define JS_DROP_ALL_EMPTY_SCOPE_LOCKS(cx,scope) ((void)0)
@ -295,6 +282,8 @@ extern void js_SetScopeInfo(JSScope *scope, const char *file, int line);
JS_NO_TIMEOUT)
#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone)
#define CX_OWNS_OBJECT_TITLE(cx,obj) CX_OWNS_SCOPE_TITLE(cx, OBJ_SCOPE(obj))
#ifndef JS_SET_OBJ_INFO
#define JS_SET_OBJ_INFO(obj,f,l) ((void)0)
#endif

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

@ -2868,7 +2868,7 @@ AllocSlots(JSContext *cx, JSObject *obj, size_t nslots);
static inline bool
InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* ops)
{
JS_ASSERT(OPS_IS_NATIVE(ops));
JS_ASSERT(ops->isNative());
JS_ASSERT(proto == OBJ_GET_PROTO(cx, obj));
/* Share proto's emptyScope only if obj is similar to proto. */
@ -2959,7 +2959,7 @@ js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
(!parent && proto) ? proto->getParent() : parent,
JSObject::defaultPrivate(clasp));
if (OPS_IS_NATIVE(ops)) {
if (ops->isNative()) {
if (!InitScopeForObject(cx, obj, proto, ops)) {
obj = NULL;
goto out;

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

@ -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 *****
@ -163,8 +163,23 @@ struct JSObjectOps {
JSHasInstanceOp hasInstance;
JSTraceOp trace;
JSFinalizeOp clear;
bool inline isNative() const;
};
extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps;
extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps;
/*
* Test whether the ops is native. FIXME bug 492938: consider how it would
* affect the performance to do just the !objectMap check.
*/
inline bool
JSObjectOps::isNative() const
{
return JS_LIKELY(this == &js_ObjectOps) || !objectMap;
}
struct JSObjectMap {
const JSObjectOps * const ops; /* high level object operation vtable */
uint32 shape; /* shape identifier */
@ -230,6 +245,8 @@ struct JSObject {
jsval fslots[JS_INITIAL_NSLOTS]; /* small number of fixed slots */
jsval *dslots; /* dynamically allocated slots */
bool isNative() const { return map->ops->isNative(); }
JSClass *getClass() const {
return (JSClass *) (classword & ~JSSLOT_CLASS_MASK_BITS);
}
@ -415,6 +432,8 @@ struct JSObject {
};
/* Compatibility macros. */
#define OBJ_IS_NATIVE(obj) ((obj)->isNative())
#define STOBJ_GET_PROTO(obj) ((obj)->getProto())
#define STOBJ_SET_PROTO(obj,proto) ((obj)->setProto(proto))
#define STOBJ_CLEAR_PROTO(obj) ((obj)->clearProto())
@ -482,7 +501,7 @@ STOBJ_GET_CLASS(const JSObject* obj)
}
#define OBJ_CHECK_SLOT(obj,slot) \
(JS_ASSERT(OBJ_IS_NATIVE(obj)), JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot))
(JS_ASSERT(obj->isNative()), JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot))
#define LOCKED_OBJ_GET_SLOT(obj,slot) \
(OBJ_CHECK_SLOT(obj, slot), STOBJ_GET_SLOT(obj, slot))
@ -538,15 +557,6 @@ STOBJ_GET_CLASS(const JSObject* obj)
*/
#define OBJ_GET_CLASS(cx,obj) STOBJ_GET_CLASS(obj)
/*
* Test whether the object is native. FIXME bug 492938: consider how it would
* affect the performance to do just the !ops->objectMap check.
*/
#define OPS_IS_NATIVE(ops) \
JS_LIKELY((ops) == &js_ObjectOps || !(ops)->objectMap)
#define OBJ_IS_NATIVE(obj) OPS_IS_NATIVE((obj)->map->ops)
#ifdef __cplusplus
inline void
OBJ_TO_INNER_OBJECT(JSContext *cx, JSObject *&obj)
@ -575,8 +585,6 @@ OBJ_TO_OUTER_OBJECT(JSContext *cx, JSObject *&obj)
}
#endif
extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps;
extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps;
extern JSClass js_ObjectClass;
extern JSClass js_WithClass;
extern JSClass js_BlockClass;

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

@ -720,7 +720,6 @@ BEGIN_CASE(JSOP_BINDNAME)
PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
if (!atom) {
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
JS_UNLOCK_OBJ(cx, obj2);
break;
}
} else {
@ -1268,13 +1267,11 @@ BEGIN_CASE(JSOP_NAMEDEC)
if (!(js_CodeSpec[op].format & JOF_POST))
rtmp = rval;
LOCKED_OBJ_SET_SLOT(obj, slot, rval);
JS_UNLOCK_OBJ(cx, obj);
PUSH_OPND(rtmp);
len = JSOP_INCNAME_LENGTH;
DO_NEXT_OP(len);
}
}
JS_UNLOCK_OBJ(cx, obj2);
LOAD_ATOM(0);
}
} else {
@ -1531,7 +1528,6 @@ BEGIN_CASE(JSOP_GETXPROP)
fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
&rval);
}
JS_UNLOCK_OBJ(cx, obj2);
break;
}
} else {
@ -1626,7 +1622,6 @@ BEGIN_CASE(JSOP_CALLPROP)
sprop = PCVAL_TO_SPROP(entry->vword);
NATIVE_GET(cx, obj, obj2, sprop, JSGET_NO_METHOD_BARRIER, &rval);
}
JS_UNLOCK_OBJ(cx, obj2);
STORE_OPND(-1, rval);
PUSH_OPND(lval);
goto end_callprop;
@ -1737,7 +1732,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
PCMETER(cache->settests++);
if (entry->kpc == regs.pc && entry->kshape == kshape) {
JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1);
if (JS_LOCK_OBJ_IF_SHAPE(cx, obj, kshape)) {
if (js_MatchPropertyCacheShape(cx, obj, kshape)) {
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
sprop = PCVAL_TO_SPROP(entry->vword);
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
@ -1770,18 +1765,22 @@ BEGIN_CASE(JSOP_SETMETHOD)
PCMETER(cache->pchits++);
PCMETER(cache->setpchits++);
NATIVE_SET(cx, obj, sprop, entry, &rval);
JS_UNLOCK_SCOPE(cx, scope);
break;
}
checkForAdd =
!(sprop->attrs & JSPROP_SHARED) &&
sprop->parent == scope->lastProperty();
} else {
/*
* We check that cx own obj here and will continue to
* own it after js_GetMutableScope returns so we can
* continue to skip JS_UNLOCK_OBJ calls.
*/
JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx, obj));
scope = js_GetMutableScope(cx, obj);
if (!scope) {
JS_UNLOCK_OBJ(cx, obj);
JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx, obj));
if (!scope)
goto error;
}
checkForAdd = !sprop->parent;
}
@ -1813,10 +1812,8 @@ BEGIN_CASE(JSOP_SETMETHOD)
!OBJ_GET_CLASS(cx, obj)->reserveSlots) {
++scope->freeslot;
} else {
if (!js_AllocSlot(cx, obj, &slot)) {
JS_UNLOCK_SCOPE(cx, scope);
if (!js_AllocSlot(cx, obj, &slot))
goto error;
}
}
/*
@ -1837,7 +1834,6 @@ BEGIN_CASE(JSOP_SETMETHOD)
sprop->flags, sprop->shortid);
if (!sprop2) {
js_FreeSlot(cx, obj, slot);
JS_UNLOCK_SCOPE(cx, scope);
goto error;
}
sprop = sprop2;
@ -1853,7 +1849,6 @@ BEGIN_CASE(JSOP_SETMETHOD)
*/
TRACE_2(SetPropHit, entry, sprop);
LOCKED_OBJ_SET_SLOT(obj, slot, rval);
JS_UNLOCK_SCOPE(cx, scope);
/*
* Purge the property cache of the id we may have just
@ -1863,7 +1858,6 @@ BEGIN_CASE(JSOP_SETMETHOD)
js_PurgeScopeChain(cx, obj, sprop->id);
break;
}
JS_UNLOCK_SCOPE(cx, scope);
PCMETER(cache->setpcmisses++);
}
}
@ -1883,7 +1877,6 @@ BEGIN_CASE(JSOP_SETMETHOD)
JS_ASSERT(!OBJ_SCOPE(obj2)->sealed());
NATIVE_SET(cx, obj, sprop, entry, &rval);
}
JS_UNLOCK_OBJ(cx, obj2);
if (sprop)
break;
}
@ -2332,7 +2325,6 @@ BEGIN_CASE(JSOP_CALLNAME)
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
if (PCVAL_IS_OBJECT(entry->vword)) {
rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
JS_UNLOCK_OBJ(cx, obj2);
goto do_push_rval;
}
@ -2340,7 +2332,6 @@ BEGIN_CASE(JSOP_CALLNAME)
slot = PCVAL_TO_SLOT(entry->vword);
JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
JS_UNLOCK_OBJ(cx, obj2);
goto do_push_rval;
}
@ -3367,6 +3358,13 @@ BEGIN_CASE(JSOP_NEWINIT)
JS_UNLOCK_OBJ(cx, obj);
goto error;
}
/*
* We cannot assume that js_GetMutableScope above creates a scope
* owned by cx and skip JS_UNLOCK_SCOPE. A new object debugger
* hook may add properties to the newly created object, suspend
* the current request and share the object with other threads.
*/
JS_UNLOCK_SCOPE(cx, scope);
}
}
@ -3395,14 +3393,19 @@ BEGIN_CASE(JSOP_INITMETHOD)
JS_ASSERT(OBJ_IS_NATIVE(obj));
JS_ASSERT(!OBJ_GET_CLASS(cx, obj)->reserveSlots);
JS_ASSERT(!(obj->getClass()->flags & JSCLASS_SHARE_ALL_PROPERTIES));
do {
JSScope *scope;
uint32 kshape;
JSPropertyCache *cache;
JSPropCacheEntry *entry;
JS_LOCK_OBJ(cx, obj);
/*
* We can not assume that the object created by JSOP_NEWINIT is still
* single-threaded as the debugger can access it from other threads.
*/
if (!CX_OWNS_OBJECT_TITLE(cx, obj))
goto do_initprop_miss;
scope = OBJ_SCOPE(obj);
JS_ASSERT(scope->object == obj);
JS_ASSERT(!scope->sealed());
@ -3455,10 +3458,8 @@ BEGIN_CASE(JSOP_INITMETHOD)
if (slot < STOBJ_NSLOTS(obj)) {
++scope->freeslot;
} else {
if (!js_AllocSlot(cx, obj, &slot)) {
JS_UNLOCK_SCOPE(cx, scope);
if (!js_AllocSlot(cx, obj, &slot))
goto error;
}
JS_ASSERT(slot == sprop->slot);
}
@ -3470,7 +3471,6 @@ BEGIN_CASE(JSOP_INITMETHOD)
sprop->attrs, sprop->flags, sprop->shortid);
if (!sprop2) {
js_FreeSlot(cx, obj, slot);
JS_UNLOCK_SCOPE(cx, scope);
goto error;
}
JS_ASSERT(sprop2 == sprop);
@ -3486,13 +3486,11 @@ BEGIN_CASE(JSOP_INITMETHOD)
*/
TRACE_2(SetPropHit, entry, sprop);
LOCKED_OBJ_SET_SLOT(obj, slot, rval);
JS_UNLOCK_SCOPE(cx, scope);
break;
}
do_initprop_miss:
PCMETER(cache->inipcmisses++);
JS_UNLOCK_SCOPE(cx, scope);
/* Get the immediate property name into id. */
LOAD_ATOM(0);

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

@ -208,7 +208,7 @@ JSScope *
JSScope::create(JSContext *cx, const JSObjectOps *ops, JSClass *clasp,
JSObject *obj, uint32 shape)
{
JS_ASSERT(OPS_IS_NATIVE(ops));
JS_ASSERT(ops->isNative());
JS_ASSERT(obj);
JSScope *scope = cx->create<JSScope>(ops, obj);

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

@ -521,7 +521,7 @@ JS_IS_SCOPE_LOCKED(JSContext *cx, JSScope *scope)
inline JSScope *
OBJ_SCOPE(JSObject *obj)
{
JS_ASSERT(OBJ_IS_NATIVE(obj));
JS_ASSERT(obj->isNative());
return (JSScope *) obj->map;
}

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

@ -78,6 +78,7 @@
#include "jstypedarray.h"
#include "jsatominlines.h"
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
@ -9415,10 +9416,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
JSAtom* atom;
JSPropCacheEntry* entry;
PROPERTY_CACHE_TEST(cx, pc, aobj, obj2, entry, atom);
if (!atom) {
// Null atom means that obj2 is locked and must now be unlocked.
JS_UNLOCK_OBJ(cx, obj2);
} else {
if (atom) {
// Miss: pre-fill the cache for the interpreter, as well as for our needs.
jsid id = ATOM_TO_JSID(atom);
JSProperty* prop;