v8/test/cctest/test-lockers.cc
mikhail.naganov@gmail.com 508b22c436 "Deiceolate" Thread classes.
Thread class was receiving an isolate parameter by default.
This approact violates the assumption that only VM threads
can have an associated isolate, and can lead to troubles,
because accessing the same isolate from different threads
leads to race conditions.

This was found by investigating mysterious failures of the
CPU profiler layout test on Linux Chromium. As almost all
threads were associated with some isolate, the sampler was
trying to sample them.

As a side effect, we have also fixed the DebuggerAgent test.

Thanks to Vitaly for help in fixing isolates handling!

R=vitalyr@chromium.org
BUG=none
TEST=none

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8259 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2011-06-10 09:54:04 +00:00

630 lines
17 KiB
C++

// Copyright 2007-2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <limits.h>
#include "v8.h"
#include "api.h"
#include "isolate.h"
#include "compilation-cache.h"
#include "execution.h"
#include "snapshot.h"
#include "platform.h"
#include "utils.h"
#include "cctest.h"
#include "parser.h"
#include "unicode-inl.h"
using ::v8::AccessorInfo;
using ::v8::Context;
using ::v8::Extension;
using ::v8::Function;
using ::v8::HandleScope;
using ::v8::Local;
using ::v8::Object;
using ::v8::ObjectTemplate;
using ::v8::Persistent;
using ::v8::Script;
using ::v8::String;
using ::v8::Value;
using ::v8::V8;
namespace i = ::i;
// Migrating an isolate
class KangarooThread : public v8::internal::Thread {
public:
KangarooThread(v8::Isolate* isolate,
v8::Handle<v8::Context> context, int value)
: Thread("KangarooThread"),
isolate_(isolate), context_(context), value_(value) {
}
void Run() {
{
v8::Locker locker(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
CHECK_EQ(isolate_, v8::internal::Isolate::Current());
v8::HandleScope scope;
v8::Context::Scope context_scope(context_);
Local<Value> v = CompileRun("getValue()");
CHECK(v->IsNumber());
CHECK_EQ(30, static_cast<int>(v->NumberValue()));
}
{
v8::Locker locker(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8::Context::Scope context_scope(context_);
v8::HandleScope scope;
Local<Value> v = CompileRun("getValue()");
CHECK(v->IsNumber());
CHECK_EQ(30, static_cast<int>(v->NumberValue()));
}
isolate_->Dispose();
}
private:
v8::Isolate* isolate_;
Persistent<v8::Context> context_;
int value_;
};
// Migrates an isolate from one thread to another
TEST(KangarooIsolates) {
v8::Isolate* isolate = v8::Isolate::New();
Persistent<v8::Context> context;
{
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope;
context = v8::Context::New();
v8::Context::Scope context_scope(context);
CHECK_EQ(isolate, v8::internal::Isolate::Current());
CompileRun("function getValue() { return 30; }");
}
KangarooThread thread1(isolate, context, 1);
thread1.Start();
thread1.Join();
}
static void CalcFibAndCheck() {
Local<Value> v = CompileRun("function fib(n) {"
" if (n <= 2) return 1;"
" return fib(n-1) + fib(n-2);"
"}"
"fib(10)");
CHECK(v->IsNumber());
CHECK_EQ(55, static_cast<int>(v->NumberValue()));
}
class JoinableThread {
public:
explicit JoinableThread(const char* name)
: name_(name),
semaphore_(i::OS::CreateSemaphore(0)),
thread_(this) {
}
virtual ~JoinableThread() {
delete semaphore_;
}
void Start() {
thread_.Start();
}
void Join() {
semaphore_->Wait();
}
virtual void Run() = 0;
private:
class ThreadWithSemaphore : public i::Thread {
public:
explicit ThreadWithSemaphore(JoinableThread* joinable_thread)
: Thread(joinable_thread->name_),
joinable_thread_(joinable_thread) {
}
virtual void Run() {
joinable_thread_->Run();
joinable_thread_->semaphore_->Signal();
}
private:
JoinableThread* joinable_thread_;
};
const char* name_;
i::Semaphore* semaphore_;
ThreadWithSemaphore thread_;
friend class ThreadWithSemaphore;
DISALLOW_COPY_AND_ASSIGN(JoinableThread);
};
class IsolateLockingThreadWithLocalContext : public JoinableThread {
public:
explicit IsolateLockingThreadWithLocalContext(v8::Isolate* isolate)
: JoinableThread("IsolateLockingThread"),
isolate_(isolate) {
}
virtual void Run() {
v8::Locker locker(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope;
LocalContext local_context;
CHECK_EQ(isolate_, v8::internal::Isolate::Current());
CalcFibAndCheck();
}
private:
v8::Isolate* isolate_;
};
static void StartJoinAndDeleteThreads(const i::List<JoinableThread*>& threads) {
for (int i = 0; i < threads.length(); i++) {
threads[i]->Start();
}
for (int i = 0; i < threads.length(); i++) {
threads[i]->Join();
}
for (int i = 0; i < threads.length(); i++) {
delete threads[i];
}
}
// Run many threads all locking on the same isolate
TEST(IsolateLockingStress) {
const int kNThreads = 100;
i::List<JoinableThread*> threads(kNThreads);
v8::Isolate* isolate = v8::Isolate::New();
for (int i = 0; i < kNThreads; i++) {
threads.Add(new IsolateLockingThreadWithLocalContext(isolate));
}
StartJoinAndDeleteThreads(threads);
isolate->Dispose();
}
class IsolateNonlockingThread : public JoinableThread {
public:
explicit IsolateNonlockingThread()
: JoinableThread("IsolateNonlockingThread") {
}
virtual void Run() {
v8::Isolate* isolate = v8::Isolate::New();
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope;
v8::Handle<v8::Context> context = v8::Context::New();
v8::Context::Scope context_scope(context);
CHECK_EQ(isolate, v8::internal::Isolate::Current());
CalcFibAndCheck();
}
isolate->Dispose();
}
private:
};
// Run many threads each accessing its own isolate without locking
TEST(MultithreadedParallelIsolates) {
#ifdef V8_TARGET_ARCH_ARM
const int kNThreads = 10;
#else
const int kNThreads = 50;
#endif
i::List<JoinableThread*> threads(kNThreads);
for (int i = 0; i < kNThreads; i++) {
threads.Add(new IsolateNonlockingThread());
}
StartJoinAndDeleteThreads(threads);
}
class IsolateNestedLockingThread : public JoinableThread {
public:
explicit IsolateNestedLockingThread(v8::Isolate* isolate)
: JoinableThread("IsolateNestedLocking"), isolate_(isolate) {
}
virtual void Run() {
v8::Locker lock(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope;
LocalContext local_context;
{
v8::Locker another_lock(isolate_);
CalcFibAndCheck();
}
{
v8::Locker another_lock(isolate_);
CalcFibAndCheck();
}
}
private:
v8::Isolate* isolate_;
};
// Run many threads with nested locks
TEST(IsolateNestedLocking) {
const int kNThreads = 100;
v8::Isolate* isolate = v8::Isolate::New();
i::List<JoinableThread*> threads(kNThreads);
for (int i = 0; i < kNThreads; i++) {
threads.Add(new IsolateNestedLockingThread(isolate));
}
StartJoinAndDeleteThreads(threads);
}
class SeparateIsolatesLocksNonexclusiveThread : public JoinableThread {
public:
SeparateIsolatesLocksNonexclusiveThread(v8::Isolate* isolate1,
v8::Isolate* isolate2)
: JoinableThread("SeparateIsolatesLocksNonexclusiveThread"),
isolate1_(isolate1), isolate2_(isolate2) {
}
virtual void Run() {
v8::Locker lock(isolate1_);
v8::Isolate::Scope isolate_scope(isolate1_);
v8::HandleScope handle_scope;
LocalContext local_context;
IsolateLockingThreadWithLocalContext threadB(isolate2_);
threadB.Start();
CalcFibAndCheck();
threadB.Join();
}
private:
v8::Isolate* isolate1_;
v8::Isolate* isolate2_;
};
// Run parallel threads that lock and access different isolates in parallel
TEST(SeparateIsolatesLocksNonexclusive) {
const int kNThreads = 100;
v8::Isolate* isolate1 = v8::Isolate::New();
v8::Isolate* isolate2 = v8::Isolate::New();
i::List<JoinableThread*> threads(kNThreads);
for (int i = 0; i < kNThreads; i++) {
threads.Add(new SeparateIsolatesLocksNonexclusiveThread(isolate1,
isolate2));
}
StartJoinAndDeleteThreads(threads);
isolate2->Dispose();
isolate1->Dispose();
}
class LockIsolateAndCalculateFibSharedContextThread : public JoinableThread {
public:
explicit LockIsolateAndCalculateFibSharedContextThread(
v8::Isolate* isolate, v8::Handle<v8::Context> context)
: JoinableThread("LockIsolateAndCalculateFibThread"),
isolate_(isolate),
context_(context) {
}
virtual void Run() {
v8::Locker lock(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
HandleScope handle_scope;
v8::Context::Scope context_scope(context_);
CalcFibAndCheck();
}
private:
v8::Isolate* isolate_;
Persistent<v8::Context> context_;
};
class LockerUnlockerThread : public JoinableThread {
public:
explicit LockerUnlockerThread(v8::Isolate* isolate)
: JoinableThread("LockerUnlockerThread"),
isolate_(isolate) {
}
virtual void Run() {
v8::Locker lock(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope;
v8::Handle<v8::Context> context = v8::Context::New();
{
v8::Context::Scope context_scope(context);
CalcFibAndCheck();
}
{
isolate_->Exit();
v8::Unlocker unlocker(isolate_);
LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
thread.Start();
thread.Join();
}
isolate_->Enter();
{
v8::Context::Scope context_scope(context);
CalcFibAndCheck();
}
}
private:
v8::Isolate* isolate_;
};
// Use unlocker inside of a Locker, multiple threads.
TEST(LockerUnlocker) {
const int kNThreads = 100;
i::List<JoinableThread*> threads(kNThreads);
v8::Isolate* isolate = v8::Isolate::New();
for (int i = 0; i < kNThreads; i++) {
threads.Add(new LockerUnlockerThread(isolate));
}
StartJoinAndDeleteThreads(threads);
isolate->Dispose();
}
class LockTwiceAndUnlockThread : public JoinableThread {
public:
explicit LockTwiceAndUnlockThread(v8::Isolate* isolate)
: JoinableThread("LockTwiceAndUnlockThread"),
isolate_(isolate) {
}
virtual void Run() {
v8::Locker lock(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope;
v8::Handle<v8::Context> context = v8::Context::New();
{
v8::Context::Scope context_scope(context);
CalcFibAndCheck();
}
{
v8::Locker second_lock(isolate_);
{
isolate_->Exit();
v8::Unlocker unlocker(isolate_);
LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
thread.Start();
thread.Join();
}
}
isolate_->Enter();
{
v8::Context::Scope context_scope(context);
CalcFibAndCheck();
}
}
private:
v8::Isolate* isolate_;
};
// Use Unlocker inside two Lockers.
TEST(LockTwiceAndUnlock) {
const int kNThreads = 100;
i::List<JoinableThread*> threads(kNThreads);
v8::Isolate* isolate = v8::Isolate::New();
for (int i = 0; i < kNThreads; i++) {
threads.Add(new LockTwiceAndUnlockThread(isolate));
}
StartJoinAndDeleteThreads(threads);
isolate->Dispose();
}
class LockAndUnlockDifferentIsolatesThread : public JoinableThread {
public:
LockAndUnlockDifferentIsolatesThread(v8::Isolate* isolate1,
v8::Isolate* isolate2)
: JoinableThread("LockAndUnlockDifferentIsolatesThread"),
isolate1_(isolate1),
isolate2_(isolate2) {
}
virtual void Run() {
Persistent<v8::Context> context1;
Persistent<v8::Context> context2;
v8::Locker lock1(isolate1_);
CHECK(v8::Locker::IsLocked(isolate1_));
CHECK(!v8::Locker::IsLocked(isolate2_));
{
v8::Isolate::Scope isolate_scope(isolate1_);
v8::HandleScope handle_scope;
context1 = v8::Context::New();
{
v8::Context::Scope context_scope(context1);
CalcFibAndCheck();
}
}
v8::Locker lock2(isolate2_);
CHECK(v8::Locker::IsLocked(isolate1_));
CHECK(v8::Locker::IsLocked(isolate2_));
{
v8::Isolate::Scope isolate_scope(isolate2_);
v8::HandleScope handle_scope;
context2 = v8::Context::New();
{
v8::Context::Scope context_scope(context2);
CalcFibAndCheck();
}
}
{
v8::Unlocker unlock1(isolate1_);
CHECK(!v8::Locker::IsLocked(isolate1_));
CHECK(v8::Locker::IsLocked(isolate2_));
v8::Isolate::Scope isolate_scope(isolate2_);
v8::HandleScope handle_scope;
v8::Context::Scope context_scope(context2);
LockIsolateAndCalculateFibSharedContextThread thread(isolate1_, context1);
thread.Start();
CalcFibAndCheck();
thread.Join();
}
}
private:
v8::Isolate* isolate1_;
v8::Isolate* isolate2_;
};
// Lock two isolates and unlock one of them.
TEST(LockAndUnlockDifferentIsolates) {
v8::Isolate* isolate1 = v8::Isolate::New();
v8::Isolate* isolate2 = v8::Isolate::New();
LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2);
thread.Start();
thread.Join();
isolate2->Dispose();
isolate1->Dispose();
}
class LockUnlockLockThread : public JoinableThread {
public:
LockUnlockLockThread(v8::Isolate* isolate, v8::Handle<v8::Context> context)
: JoinableThread("LockUnlockLockThread"),
isolate_(isolate),
context_(context) {
}
virtual void Run() {
v8::Locker lock1(isolate_);
CHECK(v8::Locker::IsLocked(isolate_));
CHECK(!v8::Locker::IsLocked());
{
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope;
v8::Context::Scope context_scope(context_);
CalcFibAndCheck();
}
{
v8::Unlocker unlock1(isolate_);
CHECK(!v8::Locker::IsLocked(isolate_));
CHECK(!v8::Locker::IsLocked());
{
v8::Locker lock2(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope;
CHECK(v8::Locker::IsLocked(isolate_));
CHECK(!v8::Locker::IsLocked());
v8::Context::Scope context_scope(context_);
CalcFibAndCheck();
}
}
}
private:
v8::Isolate* isolate_;
v8::Persistent<v8::Context> context_;
};
// Locker inside an Unlocker inside a Locker.
TEST(LockUnlockLockMultithreaded) {
const int kNThreads = 100;
v8::Isolate* isolate = v8::Isolate::New();
Persistent<v8::Context> context;
{
v8::Locker locker_(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope;
context = v8::Context::New();
}
i::List<JoinableThread*> threads(kNThreads);
for (int i = 0; i < kNThreads; i++) {
threads.Add(new LockUnlockLockThread(isolate, context));
}
StartJoinAndDeleteThreads(threads);
}
class LockUnlockLockDefaultIsolateThread : public JoinableThread {
public:
explicit LockUnlockLockDefaultIsolateThread(v8::Handle<v8::Context> context)
: JoinableThread("LockUnlockLockDefaultIsolateThread"),
context_(context) {
}
virtual void Run() {
v8::Locker lock1;
{
v8::HandleScope handle_scope;
v8::Context::Scope context_scope(context_);
CalcFibAndCheck();
}
{
v8::Unlocker unlock1;
{
v8::Locker lock2;
v8::HandleScope handle_scope;
v8::Context::Scope context_scope(context_);
CalcFibAndCheck();
}
}
}
private:
v8::Persistent<v8::Context> context_;
};
// Locker inside an Unlocker inside a Locker for default isolate.
TEST(LockUnlockLockDefaultIsolateMultithreaded) {
const int kNThreads = 100;
Persistent<v8::Context> context;
{
v8::Locker locker_;
v8::HandleScope handle_scope;
context = v8::Context::New();
}
i::List<JoinableThread*> threads(kNThreads);
for (int i = 0; i < kNThreads; i++) {
threads.Add(new LockUnlockLockDefaultIsolateThread(context));
}
StartJoinAndDeleteThreads(threads);
}
TEST(Regress1433) {
for (int i = 0; i < 10; i++) {
v8::Isolate* isolate = v8::Isolate::New();
{
v8::Locker lock(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope;
v8::Persistent<Context> context = v8::Context::New();
v8::Context::Scope context_scope(context);
v8::Handle<String> source = v8::String::New("1+1");
v8::Handle<Script> script = v8::Script::Compile(source);
v8::Handle<Value> result = script->Run();
v8::String::AsciiValue ascii(result);
context.Dispose();
}
isolate->Dispose();
}
}