* Fix memory leaks caused by thread local data being lost.

* Rename some instance variables and accessors to fit code style.
* Don't overwrite existing thread ID.
Review URL: http://codereview.chromium.org/251014

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2977 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
erik.corry@gmail.com 2009-09-28 12:25:21 +00:00
parent f0ba671344
commit ae882b44e7
16 changed files with 115 additions and 57 deletions

View File

@ -71,7 +71,7 @@ namespace v8 {
thread_local.DecrementCallDepth(); \
if (has_pending_exception) { \
if (thread_local.CallDepthIsZero() && i::Top::is_out_of_memory()) { \
if (!thread_local.IgnoreOutOfMemory()) \
if (!thread_local.ignore_out_of_memory()) \
i::V8::FatalProcessOutOfMemory(NULL); \
} \
bool call_depth_is_zero = thread_local.CallDepthIsZero(); \
@ -3208,7 +3208,7 @@ Local<Integer> v8::Integer::New(int32_t value) {
void V8::IgnoreOutOfMemoryException() {
thread_local.SetIgnoreOutOfMemory(true);
thread_local.set_ignore_out_of_memory(true);
}
@ -3690,6 +3690,11 @@ HandleScopeImplementer* HandleScopeImplementer::instance() {
}
void HandleScopeImplementer::FreeThreadResources() {
thread_local.Free();
}
char* HandleScopeImplementer::ArchiveThread(char* storage) {
return thread_local.ArchiveThreadHelper(storage);
}
@ -3701,7 +3706,7 @@ char* HandleScopeImplementer::ArchiveThreadHelper(char* storage) {
handle_scope_data_ = *current;
memcpy(storage, this, sizeof(*this));
Initialize();
ResetAfterArchive();
current->Initialize();
return storage + ArchiveSpacePerThread();
@ -3727,14 +3732,14 @@ char* HandleScopeImplementer::RestoreThreadHelper(char* storage) {
void HandleScopeImplementer::IterateThis(ObjectVisitor* v) {
// Iterate over all handles in the blocks except for the last.
for (int i = Blocks()->length() - 2; i >= 0; --i) {
Object** block = Blocks()->at(i);
for (int i = blocks()->length() - 2; i >= 0; --i) {
Object** block = blocks()->at(i);
v->VisitPointers(block, &block[kHandleBlockSize]);
}
// Iterate over live handles in the last block (if any).
if (!Blocks()->is_empty()) {
v->VisitPointers(Blocks()->last(), handle_scope_data_.next);
if (!blocks()->is_empty()) {
v->VisitPointers(blocks()->last(), handle_scope_data_.next);
}
if (!saved_contexts_.is_empty()) {

View File

@ -311,20 +311,12 @@ class HandleScopeImplementer {
public:
HandleScopeImplementer()
: blocks(0),
: blocks_(0),
entered_contexts_(0),
saved_contexts_(0) {
Initialize();
}
void Initialize() {
blocks.Initialize(0);
entered_contexts_.Initialize(0);
saved_contexts_.Initialize(0);
spare = NULL;
ignore_out_of_memory = false;
call_depth = 0;
}
saved_contexts_(0),
spare_(NULL),
ignore_out_of_memory_(false),
call_depth_(0) { }
static HandleScopeImplementer* instance();
@ -332,6 +324,7 @@ class HandleScopeImplementer {
static int ArchiveSpacePerThread();
static char* RestoreThread(char* from);
static char* ArchiveThread(char* to);
static void FreeThreadResources();
// Garbage collection support.
static void Iterate(v8::internal::ObjectVisitor* v);
@ -341,9 +334,9 @@ class HandleScopeImplementer {
inline internal::Object** GetSpareOrNewBlock();
inline void DeleteExtensions(int extensions);
inline void IncrementCallDepth() {call_depth++;}
inline void DecrementCallDepth() {call_depth--;}
inline bool CallDepthIsZero() { return call_depth == 0; }
inline void IncrementCallDepth() {call_depth_++;}
inline void DecrementCallDepth() {call_depth_--;}
inline bool CallDepthIsZero() { return call_depth_ == 0; }
inline void EnterContext(Handle<Object> context);
inline bool LeaveLastContext();
@ -356,20 +349,41 @@ class HandleScopeImplementer {
inline Context* RestoreContext();
inline bool HasSavedContexts();
inline List<internal::Object**>* Blocks() { return &blocks; }
inline bool IgnoreOutOfMemory() { return ignore_out_of_memory; }
inline void SetIgnoreOutOfMemory(bool value) { ignore_out_of_memory = value; }
inline List<internal::Object**>* blocks() { return &blocks_; }
inline bool ignore_out_of_memory() { return ignore_out_of_memory_; }
inline void set_ignore_out_of_memory(bool value) {
ignore_out_of_memory_ = value;
}
private:
List<internal::Object**> blocks;
Object** spare;
int call_depth;
void ResetAfterArchive() {
blocks_.Initialize(0);
entered_contexts_.Initialize(0);
saved_contexts_.Initialize(0);
spare_ = NULL;
ignore_out_of_memory_ = false;
call_depth_ = 0;
}
void Free() {
ASSERT(blocks_.length() == 0);
ASSERT(entered_contexts_.length() == 0);
ASSERT(saved_contexts_.length() == 0);
if (spare_ != NULL) {
DeleteArray(spare_);
spare_ = NULL;
}
ASSERT(call_depth_ == 0);
}
List<internal::Object**> blocks_;
// Used as a stack to keep track of entered contexts.
List<Handle<Object> > entered_contexts_;
// Used as a stack to keep track of saved contexts.
List<Context*> saved_contexts_;
bool ignore_out_of_memory;
Object** spare_;
bool ignore_out_of_memory_;
int call_depth_;
// This is only used for threading support.
v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
@ -419,32 +433,32 @@ Handle<Object> HandleScopeImplementer::LastEnteredContext() {
// If there's a spare block, use it for growing the current scope.
internal::Object** HandleScopeImplementer::GetSpareOrNewBlock() {
internal::Object** block = (spare != NULL) ?
spare :
internal::Object** block = (spare_ != NULL) ?
spare_ :
NewArray<internal::Object*>(kHandleBlockSize);
spare = NULL;
spare_ = NULL;
return block;
}
void HandleScopeImplementer::DeleteExtensions(int extensions) {
if (spare != NULL) {
DeleteArray(spare);
spare = NULL;
if (spare_ != NULL) {
DeleteArray(spare_);
spare_ = NULL;
}
for (int i = extensions; i > 1; --i) {
internal::Object** block = blocks.RemoveLast();
internal::Object** block = blocks_.RemoveLast();
#ifdef DEBUG
v8::ImplementationUtilities::ZapHandleRange(block,
&block[kHandleBlockSize]);
#endif
DeleteArray(block);
}
spare = blocks.RemoveLast();
spare_ = blocks_.RemoveLast();
#ifdef DEBUG
v8::ImplementationUtilities::ZapHandleRange(
spare,
&spare[kHandleBlockSize]);
spare_,
&spare_[kHandleBlockSize]);
#endif
}

View File

@ -1586,6 +1586,12 @@ char* Bootstrapper::RestoreState(char* from) {
}
// Called when the top-level V8 mutex is destroyed.
void Bootstrapper::FreeThreadResources() {
ASSERT(Genesis::current() == NULL);
}
// Reserve space for statics needing saving and restoring.
int Genesis::ArchiveSpacePerThread() {
return sizeof(current_);

View File

@ -74,6 +74,7 @@ class Bootstrapper : public AllStatic {
static int ArchiveSpacePerThread();
static char* ArchiveState(char* to);
static char* RestoreState(char* from);
static void FreeThreadResources();
};
}} // namespace v8::internal

View File

@ -354,6 +354,7 @@ class Debug {
static char* ArchiveDebug(char* to);
static char* RestoreDebug(char* from);
static int ArchiveSpacePerThread();
static void FreeThreadResources() { }
// Mirror cache handling.
static void ClearMirrorCache();

View File

@ -407,6 +407,10 @@ char* StackGuard::RestoreStackGuard(char* from) {
}
void StackGuard::FreeThreadResources() {
}
// --- C a l l s t o n a t i v e s ---
#define RETURN_NATIVE_CALL(name, argc, argv, has_pending_exception) \

View File

@ -159,6 +159,7 @@ class StackGuard BASE_EMBEDDED {
static char* ArchiveStackGuard(char* to);
static char* RestoreStackGuard(char* from);
static int ArchiveSpacePerThread();
static void FreeThreadResources();
static bool IsStackOverflow();
static bool IsPreempted();

View File

@ -46,10 +46,10 @@ v8::ImplementationUtilities::HandleScopeData HandleScope::current_ =
int HandleScope::NumberOfHandles() {
int n = HandleScopeImplementer::instance()->Blocks()->length();
int n = HandleScopeImplementer::instance()->blocks()->length();
if (n == 0) return 0;
return ((n - 1) * kHandleBlockSize) +
(current_.next - HandleScopeImplementer::instance()->Blocks()->last());
(current_.next - HandleScopeImplementer::instance()->blocks()->last());
}
@ -67,8 +67,8 @@ Object** HandleScope::Extend() {
HandleScopeImplementer* impl = HandleScopeImplementer::instance();
// If there's more room in the last block, we use that. This is used
// for fast creation of scopes after scope barriers.
if (!impl->Blocks()->is_empty()) {
Object** limit = &impl->Blocks()->last()[kHandleBlockSize];
if (!impl->blocks()->is_empty()) {
Object** limit = &impl->blocks()->last()[kHandleBlockSize];
if (current_.limit != limit) {
current_.limit = limit;
}
@ -81,7 +81,7 @@ Object** HandleScope::Extend() {
result = impl->GetSpareOrNewBlock();
// Add the extension to the global list of blocks, but count the
// extension as part of the current scope.
impl->Blocks()->Add(result);
impl->blocks()->Add(result);
current_.extensions++;
current_.limit = &result[kHandleBlockSize];
}

View File

@ -69,6 +69,14 @@ void RegExpStack::Reset() {
}
void RegExpStack::ThreadLocal::Free() {
if (thread_local_.memory_size_ > 0) {
DeleteArray(thread_local_.memory_);
thread_local_ = ThreadLocal();
}
}
Address RegExpStack::EnsureCapacity(size_t size) {
if (size > kMaximumStackSize) return NULL;
if (size < kMinimumStackSize) size = kMinimumStackSize;

View File

@ -71,6 +71,7 @@ class RegExpStack {
static size_t ArchiveSpacePerThread() { return sizeof(thread_local_); }
static char* ArchiveStack(char* to);
static char* RestoreStack(char* from);
static void FreeThreadResources() { thread_local_.Free(); }
private:
// Artificial limit used when no memory has been allocated.
@ -92,6 +93,7 @@ class RegExpStack {
Address memory_;
size_t memory_size_;
Address limit_;
void Free();
};
// Resets the buffer if it has grown beyond the default/minimum size.

View File

@ -1062,7 +1062,7 @@ void Serializer::Serialize() {
// No active threads.
CHECK_EQ(NULL, ThreadState::FirstInUse());
// No active or weak handles.
CHECK(HandleScopeImplementer::instance()->Blocks()->is_empty());
CHECK(HandleScopeImplementer::instance()->blocks()->is_empty());
CHECK_EQ(0, GlobalHandles::NumberOfWeakHandles());
// We need a counter function during serialization to resolve the
// references to counters in the code on the heap.
@ -1395,7 +1395,7 @@ void Deserializer::Deserialize() {
// No active threads.
ASSERT_EQ(NULL, ThreadState::FirstInUse());
// No active handles.
ASSERT(HandleScopeImplementer::instance()->Blocks()->is_empty());
ASSERT(HandleScopeImplementer::instance()->blocks()->is_empty());
reference_decoder_ = new ExternalReferenceDecoder();
// By setting linear allocation only, we forbid the use of free list
// allocation which is not predicted by SimulatedAddress.

View File

@ -98,7 +98,8 @@ void Top::InitializeThreadLocal() {
thread_local_.stack_is_cooked_ = false;
thread_local_.try_catch_handler_ = NULL;
thread_local_.context_ = NULL;
thread_local_.thread_id_ = ThreadManager::kInvalidId;
int id = ThreadManager::CurrentId();
thread_local_.thread_id_ = (id == 0) ? ThreadManager::kInvalidId : id;
thread_local_.external_caught_exception_ = false;
thread_local_.failed_access_check_callback_ = NULL;
clear_pending_exception();

View File

@ -78,6 +78,12 @@ class ThreadLocalTop BASE_EMBEDDED {
// Call back function to report unsafe JS accesses.
v8::FailedAccessCheckCallback failed_access_check_callback_;
void Free() {
ASSERT(!has_pending_message_);
ASSERT(!external_caught_exception_);
ASSERT(try_catch_handler_ == NULL);
}
};
#define TOP_ADDRESS_LIST(C) \
@ -316,6 +322,7 @@ class Top {
static int ArchiveSpacePerThread() { return sizeof(ThreadLocalTop); }
static char* ArchiveThread(char* to);
static char* RestoreThread(char* from);
static void FreeThreadResources() { thread_local_.Free(); }
static const char* kStackOverflowMessage;

View File

@ -77,7 +77,9 @@ bool Locker::IsLocked() {
Locker::~Locker() {
ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
if (has_lock_) {
if (!top_level_) {
if (top_level_) {
internal::ThreadManager::FreeThreadResources();
} else {
internal::ThreadManager::ArchiveThread();
}
internal::ThreadManager::Unlock();
@ -284,6 +286,18 @@ void ThreadManager::EagerlyArchiveThread() {
}
void ThreadManager::FreeThreadResources() {
HandleScopeImplementer::FreeThreadResources();
Top::FreeThreadResources();
#ifdef ENABLE_DEBUGGER_SUPPORT
Debug::FreeThreadResources();
#endif
StackGuard::FreeThreadResources();
RegExpStack::FreeThreadResources();
Bootstrapper::FreeThreadResources();
}
bool ThreadManager::IsArchived() {
return Thread::HasThreadLocal(thread_state_key);
}

View File

@ -86,6 +86,7 @@ class ThreadManager : public AllStatic {
static void ArchiveThread();
static bool RestoreThread();
static void FreeThreadResources();
static bool IsArchived();
static void Iterate(ObjectVisitor* v);

View File

@ -401,13 +401,6 @@ class TestSampler : public v8::internal::Sampler {
} // namespace
TEST(ProfMultipleThreads) {
// V8 needs to be initialized before the first Locker
// instantiation. Otherwise, Top::Initialize will reset
// thread_id_ in ThreadTopLocal.
v8::HandleScope scope;
v8::Handle<v8::Context> env = v8::Context::New();
env->Enter();
LoopingJsThread jsThread;
jsThread.Start();
LoopingNonJsThread nonJsThread;