Do not use possibly stale values for cache size, etc.
Those value can become invalid if cache gets cleared by GC. Review URL: http://codereview.chromium.org/6348002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6353 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
d2df943bde
commit
4b6981f74d
@ -670,16 +670,17 @@ void JSFunctionResultCache::JSFunctionResultCacheVerify() {
|
|||||||
|
|
||||||
int finger = Smi::cast(get(kFingerIndex))->value();
|
int finger = Smi::cast(get(kFingerIndex))->value();
|
||||||
ASSERT(kEntriesIndex <= finger);
|
ASSERT(kEntriesIndex <= finger);
|
||||||
ASSERT(finger < size || finger == kEntriesIndex);
|
ASSERT((finger < size) || (finger == kEntriesIndex && finger == size));
|
||||||
ASSERT_EQ(0, finger % kEntrySize);
|
ASSERT_EQ(0, finger % kEntrySize);
|
||||||
|
|
||||||
if (FLAG_enable_slow_asserts) {
|
if (FLAG_enable_slow_asserts) {
|
||||||
STATIC_ASSERT(2 == kEntrySize);
|
for (int i = kEntriesIndex; i < size; i++) {
|
||||||
for (int i = kEntriesIndex; i < length(); i += kEntrySize) {
|
ASSERT(!get(i)->IsTheHole());
|
||||||
|
get(i)->Verify();
|
||||||
|
}
|
||||||
|
for (int i = size; i < length(); i++) {
|
||||||
|
ASSERT(get(i)->IsTheHole());
|
||||||
get(i)->Verify();
|
get(i)->Verify();
|
||||||
get(i + 1)->Verify();
|
|
||||||
// Key and value must be either both the holes, or not.
|
|
||||||
ASSERT(get(i)->IsTheHole() == get(i + 1)->IsTheHole());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1978,13 +1978,13 @@ void ExternalTwoByteString::set_resource(
|
|||||||
|
|
||||||
|
|
||||||
void JSFunctionResultCache::MakeZeroSize() {
|
void JSFunctionResultCache::MakeZeroSize() {
|
||||||
set(kFingerIndex, Smi::FromInt(kEntriesIndex));
|
set_finger_index(kEntriesIndex);
|
||||||
set(kCacheSizeIndex, Smi::FromInt(kEntriesIndex));
|
set_size(kEntriesIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void JSFunctionResultCache::Clear() {
|
void JSFunctionResultCache::Clear() {
|
||||||
int cache_size = Smi::cast(get(kCacheSizeIndex))->value();
|
int cache_size = size();
|
||||||
Object** entries_start = RawField(this, OffsetOfElementAt(kEntriesIndex));
|
Object** entries_start = RawField(this, OffsetOfElementAt(kEntriesIndex));
|
||||||
MemsetPointer(entries_start,
|
MemsetPointer(entries_start,
|
||||||
Heap::the_hole_value(),
|
Heap::the_hole_value(),
|
||||||
@ -1993,6 +1993,26 @@ void JSFunctionResultCache::Clear() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JSFunctionResultCache::size() {
|
||||||
|
return Smi::cast(get(kCacheSizeIndex))->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSFunctionResultCache::set_size(int size) {
|
||||||
|
set(kCacheSizeIndex, Smi::FromInt(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JSFunctionResultCache::finger_index() {
|
||||||
|
return Smi::cast(get(kFingerIndex))->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSFunctionResultCache::set_finger_index(int finger_index) {
|
||||||
|
set(kFingerIndex, Smi::FromInt(finger_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
byte ByteArray::get(int index) {
|
byte ByteArray::get(int index) {
|
||||||
ASSERT(index >= 0 && index < this->length());
|
ASSERT(index >= 0 && index < this->length());
|
||||||
return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
|
return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
|
||||||
|
@ -2613,6 +2613,11 @@ class JSFunctionResultCache: public FixedArray {
|
|||||||
inline void MakeZeroSize();
|
inline void MakeZeroSize();
|
||||||
inline void Clear();
|
inline void Clear();
|
||||||
|
|
||||||
|
inline int size();
|
||||||
|
inline void set_size(int size);
|
||||||
|
inline int finger_index();
|
||||||
|
inline void set_finger_index(int finger_index);
|
||||||
|
|
||||||
// Casting
|
// Casting
|
||||||
static inline JSFunctionResultCache* cast(Object* obj);
|
static inline JSFunctionResultCache* cast(Object* obj);
|
||||||
|
|
||||||
|
112
src/runtime.cc
112
src/runtime.cc
@ -10654,51 +10654,12 @@ static MaybeObject* Runtime_Abort(Arguments args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MUST_USE_RESULT static MaybeObject* CacheMiss(FixedArray* cache_obj,
|
|
||||||
int index,
|
|
||||||
Object* key_obj) {
|
|
||||||
ASSERT(index % 2 == 0); // index of the key
|
|
||||||
ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
|
|
||||||
ASSERT(index < cache_obj->length());
|
|
||||||
|
|
||||||
HandleScope scope;
|
|
||||||
|
|
||||||
Handle<FixedArray> cache(cache_obj);
|
|
||||||
Handle<Object> key(key_obj);
|
|
||||||
Handle<JSFunction> factory(JSFunction::cast(
|
|
||||||
cache->get(JSFunctionResultCache::kFactoryIndex)));
|
|
||||||
// TODO(antonm): consider passing a receiver when constructing a cache.
|
|
||||||
Handle<Object> receiver(Top::global_context()->global());
|
|
||||||
|
|
||||||
Handle<Object> value;
|
|
||||||
{
|
|
||||||
// This handle is nor shared, nor used later, so it's safe.
|
|
||||||
Object** argv[] = { key.location() };
|
|
||||||
bool pending_exception = false;
|
|
||||||
value = Execution::Call(factory,
|
|
||||||
receiver,
|
|
||||||
1,
|
|
||||||
argv,
|
|
||||||
&pending_exception);
|
|
||||||
if (pending_exception) return Failure::Exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
cache->set(index, *key);
|
|
||||||
cache->set(index + 1, *value);
|
|
||||||
cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
|
|
||||||
|
|
||||||
return *value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static MaybeObject* Runtime_GetFromCache(Arguments args) {
|
static MaybeObject* Runtime_GetFromCache(Arguments args) {
|
||||||
// This is only called from codegen, so checks might be more lax.
|
// This is only called from codegen, so checks might be more lax.
|
||||||
CONVERT_CHECKED(FixedArray, cache, args[0]);
|
CONVERT_CHECKED(JSFunctionResultCache, cache, args[0]);
|
||||||
Object* key = args[1];
|
Object* key = args[1];
|
||||||
|
|
||||||
const int finger_index =
|
int finger_index = cache->finger_index();
|
||||||
Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
|
|
||||||
|
|
||||||
Object* o = cache->get(finger_index);
|
Object* o = cache->get(finger_index);
|
||||||
if (o == key) {
|
if (o == key) {
|
||||||
// The fastest case: hit the same place again.
|
// The fastest case: hit the same place again.
|
||||||
@ -10710,35 +10671,78 @@ static MaybeObject* Runtime_GetFromCache(Arguments args) {
|
|||||||
i -= 2) {
|
i -= 2) {
|
||||||
o = cache->get(i);
|
o = cache->get(i);
|
||||||
if (o == key) {
|
if (o == key) {
|
||||||
cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
|
cache->set_finger_index(i);
|
||||||
return cache->get(i + 1);
|
return cache->get(i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int size =
|
int size = cache->size();
|
||||||
Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
|
|
||||||
ASSERT(size <= cache->length());
|
ASSERT(size <= cache->length());
|
||||||
|
|
||||||
for (int i = size - 2; i > finger_index; i -= 2) {
|
for (int i = size - 2; i > finger_index; i -= 2) {
|
||||||
o = cache->get(i);
|
o = cache->get(i);
|
||||||
if (o == key) {
|
if (o == key) {
|
||||||
cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
|
cache->set_finger_index(i);
|
||||||
return cache->get(i + 1);
|
return cache->get(i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache miss. If we have spare room, put new data into it, otherwise
|
// There is no value in the cache. Invoke the function and cache result.
|
||||||
// evict post finger entry which must be least recently used.
|
HandleScope scope;
|
||||||
if (size < cache->length()) {
|
|
||||||
cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
|
Handle<JSFunctionResultCache> cache_handle(cache);
|
||||||
return CacheMiss(cache, size, key);
|
Handle<Object> key_handle(key);
|
||||||
|
Handle<Object> value;
|
||||||
|
{
|
||||||
|
Handle<JSFunction> factory(JSFunction::cast(
|
||||||
|
cache_handle->get(JSFunctionResultCache::kFactoryIndex)));
|
||||||
|
// TODO(antonm): consider passing a receiver when constructing a cache.
|
||||||
|
Handle<Object> receiver(Top::global_context()->global());
|
||||||
|
// This handle is nor shared, nor used later, so it's safe.
|
||||||
|
Object** argv[] = { key_handle.location() };
|
||||||
|
bool pending_exception = false;
|
||||||
|
value = Execution::Call(factory,
|
||||||
|
receiver,
|
||||||
|
1,
|
||||||
|
argv,
|
||||||
|
&pending_exception);
|
||||||
|
if (pending_exception) return Failure::Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
cache_handle->JSFunctionResultCacheVerify();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Function invocation may have cleared the cache. Reread all the data.
|
||||||
|
finger_index = cache_handle->finger_index();
|
||||||
|
size = cache_handle->size();
|
||||||
|
|
||||||
|
// If we have spare room, put new data into it, otherwise evict post finger
|
||||||
|
// entry which is likely to be the least recently used.
|
||||||
|
int index = -1;
|
||||||
|
if (size < cache_handle->length()) {
|
||||||
|
cache_handle->set_size(size + JSFunctionResultCache::kEntrySize);
|
||||||
|
index = size;
|
||||||
} else {
|
} else {
|
||||||
int target_index = finger_index + JSFunctionResultCache::kEntrySize;
|
index = finger_index + JSFunctionResultCache::kEntrySize;
|
||||||
if (target_index == cache->length()) {
|
if (index == cache_handle->length()) {
|
||||||
target_index = JSFunctionResultCache::kEntriesIndex;
|
index = JSFunctionResultCache::kEntriesIndex;
|
||||||
}
|
}
|
||||||
return CacheMiss(cache, target_index, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASSERT(index % 2 == 0);
|
||||||
|
ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
|
||||||
|
ASSERT(index < cache_handle->length());
|
||||||
|
|
||||||
|
cache_handle->set(index, *key_handle);
|
||||||
|
cache_handle->set(index + 1, *value);
|
||||||
|
cache_handle->set_finger_index(index);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
cache_handle->JSFunctionResultCacheVerify();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
Loading…
Reference in New Issue
Block a user