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

This commit is contained in:
Igor Bukanov 2010-02-03 13:42:07 +03:00
Родитель d7f5b53e87
Коммит 9d7785e018
11 изменённых файлов: 116 добавлений и 74 удалений

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

@ -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; \

56
js/src/jsinterpinlines.h Normal file
Просмотреть файл

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99:
*
* ***** 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
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Igor Bukanov <igor@mir2.org>
*
* 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 ***** */
#ifndef jsinterpinlines_h___
#define jsinterpinlines_h___
#include "jsinterp.h"
#include "jslock.h"
#include "jsscope.h"
inline bool
js_MatchPropertyCacheShape(JSContext *cx, JSObject *obj, uint32 shape)
{
return CX_OWNS_OBJECT_TITLE(cx, obj) && OBJ_SHAPE(obj) == shape;
}
#endif /* jsinterpinlines_h___ */

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

@ -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;