* 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:
parent
f0ba671344
commit
ae882b44e7
19
src/api.cc
19
src/api.cc
@ -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()) {
|
||||
|
82
src/api.h
82
src/api.h
@ -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
|
||||
}
|
||||
|
||||
|
@ -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_);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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) \
|
||||
|
@ -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();
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user