Chromium Code Reviews| Index: src/heap-inl.h | 
| =================================================================== | 
| --- src/heap-inl.h (revision 645) | 
| +++ src/heap-inl.h (working copy) | 
| @@ -39,8 +39,12 @@ | 
| Object* Heap::AllocateRaw(int size_in_bytes, | 
| - AllocationSpace space) { | 
| + AllocationSpace space, | 
| + AllocationSpace retry_space) { | 
| 
 
Erik Corry
2008/10/30 09:08:43
This is fine for now.  In the long run I think we
 
 | 
| ASSERT(allocation_allowed_ && gc_state_ == NOT_IN_GC); | 
| + ASSERT(space != NEW_SPACE || | 
| + retry_space == OLD_POINTER_SPACE || | 
| + retry_space == OLD_DATA_SPACE); | 
| #ifdef DEBUG | 
| if (FLAG_gc_interval >= 0 && | 
| !disallow_allocation_failure_ && | 
| @@ -50,11 +54,13 @@ | 
| Counters::objs_since_last_full.Increment(); | 
| Counters::objs_since_last_young.Increment(); | 
| #endif | 
| + Object* result; | 
| if (NEW_SPACE == space) { | 
| - return new_space_.AllocateRaw(size_in_bytes); | 
| + result = new_space_.AllocateRaw(size_in_bytes); | 
| + if (!always_allocate() || !result->IsFailure()) return result; | 
| 
 
Mads Ager (chromium)
2008/10/30 08:48:14
Normally, I like these bail-outs instead of if-els
 
 | 
| + space = retry_space; | 
| } | 
| - Object* result; | 
| if (OLD_POINTER_SPACE == space) { | 
| result = old_pointer_space_->AllocateRaw(size_in_bytes); | 
| } else if (OLD_DATA_SPACE == space) { | 
| @@ -132,17 +138,25 @@ | 
| OldSpace* Heap::TargetSpace(HeapObject* object) { | 
| + InstanceType type = object->map()->instance_type(); | 
| + AllocationSpace space = TargetSpaceId(type); | 
| + return (space == OLD_POINTER_SPACE) | 
| + ? old_pointer_space_ | 
| + : old_data_space_; | 
| +} | 
| + | 
| + | 
| +AllocationSpace Heap::TargetSpaceId(InstanceType type) { | 
| // Heap numbers and sequential strings are promoted to old data space, all | 
| // other object types are promoted to old pointer space. We do not use | 
| // object->IsHeapNumber() and object->IsSeqString() because we already | 
| // know that object has the heap object tag. | 
| - InstanceType type = object->map()->instance_type(); | 
| ASSERT((type != CODE_TYPE) && (type != MAP_TYPE)); | 
| bool has_pointers = | 
| type != HEAP_NUMBER_TYPE && | 
| (type >= FIRST_NONSTRING_TYPE || | 
| - String::cast(object)->representation_tag() != kSeqStringTag); | 
| - return has_pointers ? old_pointer_space_ : old_data_space_; | 
| + (type & kStringRepresentationMask) != kSeqStringTag); | 
| + return has_pointers ? OLD_POINTER_SPACE : OLD_DATA_SPACE; | 
| } | 
| @@ -188,76 +202,60 @@ | 
| #define GC_GREEDY_CHECK() \ | 
| ASSERT(!FLAG_gc_greedy || v8::internal::Heap::GarbageCollectionGreedyCheck()) | 
| -// Do not use the identifier __object__ in a call to this macro. | 
| -// | 
| -// Call the function FUNCTION_CALL. If it fails with a RetryAfterGC | 
| -// failure, call the garbage collector and retry the function. If the | 
| -// garbage collector cannot reclaim the required space or the second | 
| -// call fails with a RetryAfterGC failure, fail with out of memory. | 
| -// If there is any other failure, return a null handle. If either | 
| -// call succeeds, return a handle to the functions return value. | 
| -// | 
| -// Note that this macro always returns or raises a fatal error. | 
| -#define CALL_HEAP_FUNCTION(FUNCTION_CALL, TYPE) \ | 
| - do { \ | 
| - GC_GREEDY_CHECK(); \ | 
| - Object* __object__ = FUNCTION_CALL; \ | 
| - if (__object__->IsFailure()) { \ | 
| - if (__object__->IsRetryAfterGC()) { \ | 
| - if (!Heap::CollectGarbage( \ | 
| - Failure::cast(__object__)->requested(), \ | 
| - Failure::cast(__object__)->allocation_space())) { \ | 
| - /* TODO(1181417): Fix this. */ \ | 
| - v8::internal::V8::FatalProcessOutOfMemory("CALL_HEAP_FUNCTION"); \ | 
| - } \ | 
| - __object__ = FUNCTION_CALL; \ | 
| - if (__object__->IsFailure()) { \ | 
| - if (__object__->IsRetryAfterGC()) { \ | 
| - /* TODO(1181417): Fix this. */ \ | 
| - v8::internal::V8::FatalProcessOutOfMemory("CALL_HEAP_FUNCTION"); \ | 
| - } \ | 
| - return Handle<TYPE>(); \ | 
| - } \ | 
| - } else { \ | 
| - if (__object__->IsOutOfMemoryFailure()) { \ | 
| - v8::internal::V8::FatalProcessOutOfMemory("CALL_HEAP_FUNCTION"); \ | 
| - } \ | 
| - return Handle<TYPE>(); \ | 
| - } \ | 
| - } \ | 
| - return Handle<TYPE>(TYPE::cast(__object__)); \ | 
| + | 
| +// Calls the FUNCTION_CALL function and retries it up to three times | 
| +// to guarantee that any allocations performed during the call will | 
| +// succeed if there's enough memory. | 
| + | 
| +// Warning: Do not use the identifiers __object__ or __scope__ in a | 
| 
 
Erik Corry
2008/10/30 09:08:43
I don't really feel we need this warning, but if w
 
 | 
| +// call to this macro. | 
| + | 
| +#define CALL_AND_RETRY(FUNCTION_CALL, RETURN_VALUE, RETURN_EMPTY) \ | 
| + do { \ | 
| + GC_GREEDY_CHECK(); \ | 
| + Object* __object__ = FUNCTION_CALL; \ | 
| + if (!__object__->IsFailure()) return RETURN_VALUE; \ | 
| + if (__object__->IsOutOfMemoryFailure()) { \ | 
| + v8::internal::V8::FatalProcessOutOfMemory("CALL_AND_RETRY_0"); \ | 
| 
 
Erik Corry
2008/10/30 09:08:43
Is there a subtle fall-through here?  Comment need
 
 | 
| + } \ | 
| + if (!__object__->IsRetryAfterGC()) return RETURN_EMPTY; \ | 
| + if (!Heap::CollectGarbage( \ | 
| + Failure::cast(__object__)->requested(), \ | 
| + Failure::cast(__object__)->allocation_space())) { \ | 
| + v8::internal::V8::FatalProcessOutOfMemory("CALL_AND_RETRY_1"); \ | 
| + } \ | 
| + __object__ = FUNCTION_CALL; \ | 
| + if (!__object__->IsFailure()) return RETURN_VALUE; \ | 
| + if (__object__->IsOutOfMemoryFailure()) { \ | 
| + v8::internal::V8::FatalProcessOutOfMemory("CALL_AND_RETRY_2"); \ | 
| + } \ | 
| + if (!__object__->IsRetryAfterGC()) return RETURN_EMPTY; \ | 
| + Counters::gc_last_resort_from_handles.Increment(); \ | 
| + Heap::CollectAllGarbage(); \ | 
| + { \ | 
| + AlwaysAllocateScope __scope__; \ | 
| + __object__ = FUNCTION_CALL; \ | 
| + } \ | 
| + if (!__object__->IsFailure()) return RETURN_VALUE; \ | 
| + if (__object__->IsOutOfMemoryFailure()) { \ | 
| + /* TODO(1181417): Fix this. */ \ | 
| + v8::internal::V8::FatalProcessOutOfMemory("CALL_AND_RETRY_3"); \ | 
| + } \ | 
| + ASSERT(!__object__->IsRetryAfterGC()); \ | 
| + return RETURN_EMPTY; \ | 
| } while (false) | 
| -// Don't use the following names: __object__, __failure__. | 
| -#define CALL_HEAP_FUNCTION_VOID(FUNCTION_CALL) \ | 
| - GC_GREEDY_CHECK(); \ | 
| - Object* __object__ = FUNCTION_CALL; \ | 
| - if (__object__->IsFailure()) { \ | 
| - if (__object__->IsRetryAfterGC()) { \ | 
| - Failure* __failure__ = Failure::cast(__object__); \ | 
| - if (!Heap::CollectGarbage(__failure__->requested(), \ | 
| - __failure__->allocation_space())) { \ | 
| - /* TODO(1181417): Fix this. */ \ | 
| - V8::FatalProcessOutOfMemory("Handles"); \ | 
| - } \ | 
| - __object__ = FUNCTION_CALL; \ | 
| - if (__object__->IsFailure()) { \ | 
| - if (__object__->IsRetryAfterGC()) { \ | 
| - /* TODO(1181417): Fix this. */ \ | 
| - V8::FatalProcessOutOfMemory("Handles"); \ | 
| - } \ | 
| - return; \ | 
| - } \ | 
| - } else { \ | 
| - if (__object__->IsOutOfMemoryFailure()) { \ | 
| - V8::FatalProcessOutOfMemory("Handles"); \ | 
| - } \ | 
| - UNREACHABLE(); \ | 
| - } \ | 
| - } | 
| +#define CALL_HEAP_FUNCTION(FUNCTION_CALL, TYPE) \ | 
| + CALL_AND_RETRY(FUNCTION_CALL, \ | 
| + Handle<TYPE>(TYPE::cast(__object__)), \ | 
| + Handle<TYPE>()) | 
| +#define CALL_HEAP_FUNCTION_VOID(FUNCTION_CALL) \ | 
| + CALL_AND_RETRY(FUNCTION_CALL,,) | 
| + | 
| + | 
| #ifdef DEBUG | 
| inline bool Heap::allow_allocation(bool new_state) { |