Commit ff3433d0 authored by iposva@chromium.org's avatar iposva@chromium.org

- Preserve bootstrapper state across thread switches (fixes issue 143).

- Make sure stack guards are properly setup even when preemption is active.
- Fix missing v8::Locker and v8::Unlocker use in d8.cc.
- Threads forked in d8 do get their own context setup.

Review URL: http://codereview.chromium.org/18581

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1161 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 01198fed
...@@ -265,6 +265,11 @@ class Genesis BASE_EMBEDDED { ...@@ -265,6 +265,11 @@ class Genesis BASE_EMBEDDED {
Genesis* previous() { return previous_; } Genesis* previous() { return previous_; }
static Genesis* current() { return current_; } static Genesis* current() { return current_; }
// Support for thread preemption.
static int ArchiveSpacePerThread();
static char* ArchiveState(char* to);
static char* RestoreState(char* from);
private: private:
Handle<Context> global_context_; Handle<Context> global_context_;
...@@ -1466,4 +1471,45 @@ Genesis::Genesis(Handle<Object> global_object, ...@@ -1466,4 +1471,45 @@ Genesis::Genesis(Handle<Object> global_object,
result_ = global_context_; result_ = global_context_;
} }
// Support for thread preemption.
// Reserve space for statics needing saving and restoring.
int Bootstrapper::ArchiveSpacePerThread() {
return Genesis::ArchiveSpacePerThread();
}
// Archive statics that are thread local.
char* Bootstrapper::ArchiveState(char* to) {
return Genesis::ArchiveState(to);
}
// Restore statics that are thread local.
char* Bootstrapper::RestoreState(char* from) {
return Genesis::RestoreState(from);
}
// Reserve space for statics needing saving and restoring.
int Genesis::ArchiveSpacePerThread() {
return sizeof(current_);
}
// Archive statics that are thread local.
char* Genesis::ArchiveState(char* to) {
*reinterpret_cast<Genesis**>(to) = current_;
current_ = NULL;
return to + sizeof(current_);
}
// Restore statics that are thread local.
char* Genesis::RestoreState(char* from) {
current_ = *reinterpret_cast<Genesis**>(from);
return from + sizeof(current_);
}
} } // namespace v8::internal } } // namespace v8::internal
...@@ -68,6 +68,11 @@ class Bootstrapper : public AllStatic { ...@@ -68,6 +68,11 @@ class Bootstrapper : public AllStatic {
class FixupFlagsIsPCRelative: public BitField<bool, 0, 1> {}; class FixupFlagsIsPCRelative: public BitField<bool, 0, 1> {};
class FixupFlagsUseCodeObject: public BitField<bool, 1, 1> {}; class FixupFlagsUseCodeObject: public BitField<bool, 1, 1> {};
class FixupFlagsArgumentsCount: public BitField<uint32_t, 2, 32-2> {}; class FixupFlagsArgumentsCount: public BitField<uint32_t, 2, 32-2> {};
// Support for thread preemption.
static int ArchiveSpacePerThread();
static char* ArchiveState(char* to);
static char* RestoreState(char* from);
}; };
}} // namespace v8::internal }} // namespace v8::internal
......
...@@ -385,6 +385,7 @@ void Shell::OnExit() { ...@@ -385,6 +385,7 @@ void Shell::OnExit() {
static char* ReadChars(const char *name, int* size_out) { static char* ReadChars(const char *name, int* size_out) {
v8::Unlocker unlocker; // Release the V8 lock while reading files.
FILE* file = i::OS::FOpen(name, "rb"); FILE* file = i::OS::FOpen(name, "rb");
if (file == NULL) return NULL; if (file == NULL) return NULL;
...@@ -422,6 +423,7 @@ void Shell::RunShell() { ...@@ -422,6 +423,7 @@ void Shell::RunShell() {
while (true) { while (true) {
Locker locker; Locker locker;
HandleScope handle_scope; HandleScope handle_scope;
Context::Scope context_scope(evaluation_context_);
i::SmartPointer<char> input = editor->Prompt(Shell::kPrompt); i::SmartPointer<char> input = editor->Prompt(Shell::kPrompt);
if (input.is_empty()) if (input.is_empty())
break; break;
...@@ -446,8 +448,24 @@ class ShellThread : public i::Thread { ...@@ -446,8 +448,24 @@ class ShellThread : public i::Thread {
void ShellThread::Run() { void ShellThread::Run() {
// Prepare the context for this thread.
Locker locker;
HandleScope scope;
Handle<ObjectTemplate> global_template = ObjectTemplate::New();
global_template->Set(String::New("print"),
FunctionTemplate::New(Shell::Print));
global_template->Set(String::New("load"),
FunctionTemplate::New(Shell::Load));
global_template->Set(String::New("version"),
FunctionTemplate::New(Shell::Version));
Persistent<Context> thread_context = Context::New(NULL, global_template);
thread_context->SetSecurityToken(Undefined());
Context::Scope context_scope(thread_context);
char* ptr = const_cast<char*>(files_.start()); char* ptr = const_cast<char*>(files_.start());
while (ptr != NULL) { while ((ptr != NULL) && (*ptr != '\0')) {
// For each newline-separated line. // For each newline-separated line.
char *filename = ptr; char *filename = ptr;
char* next = ::strchr(ptr, '\n'); char* next = ::strchr(ptr, '\n');
...@@ -457,11 +475,11 @@ void ShellThread::Run() { ...@@ -457,11 +475,11 @@ void ShellThread::Run() {
} else { } else {
ptr = NULL; ptr = NULL;
} }
Locker locker;
HandleScope scope;
Handle<String> str = Shell::ReadFile(filename); Handle<String> str = Shell::ReadFile(filename);
Shell::ExecuteString(str, String::New(filename), false, false); Shell::ExecuteString(str, String::New(filename), false, false);
} }
thread_context.Dispose();
} }
...@@ -473,52 +491,59 @@ int Shell::Main(int argc, char* argv[]) { ...@@ -473,52 +491,59 @@ int Shell::Main(int argc, char* argv[]) {
Initialize(); Initialize();
bool run_shell = (argc == 1); bool run_shell = (argc == 1);
i::List<i::Thread*> threads(1); i::List<i::Thread*> threads(1);
Context::Scope context_scope(evaluation_context_);
for (int i = 1; i < argc; i++) { {
char* str = argv[i]; // Acquire the V8 lock once initialization has finished. Since the thread
if (strcmp(str, "--shell") == 0) { // below may spawn new threads accessing V8 holding the V8 lock here is
run_shell = true; // mandatory.
} else if (strcmp(str, "-f") == 0) { Locker locker;
// Ignore any -f flags for compatibility with other stand-alone Context::Scope context_scope(evaluation_context_);
// JavaScript engines. for (int i = 1; i < argc; i++) {
continue; char* str = argv[i];
} else if (strncmp(str, "--", 2) == 0) { if (strcmp(str, "--shell") == 0) {
printf("Warning: unknown flag %s.\nTry --help for options\n", str); run_shell = true;
} else if (strcmp(str, "-e") == 0 && i + 1 < argc) { } else if (strcmp(str, "-f") == 0) {
// Execute argument given to -e option directly. // Ignore any -f flags for compatibility with other stand-alone
Locker locker; // JavaScript engines.
v8::HandleScope handle_scope; continue;
v8::Handle<v8::String> file_name = v8::String::New("unnamed"); } else if (strncmp(str, "--", 2) == 0) {
v8::Handle<v8::String> source = v8::String::New(argv[i + 1]); printf("Warning: unknown flag %s.\nTry --help for options\n", str);
if (!ExecuteString(source, file_name, false, true)) } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
return 1; // Execute argument given to -e option directly.
i++; v8::HandleScope handle_scope;
} else if (strcmp(str, "-p") == 0 && i + 1 < argc) { v8::Handle<v8::String> file_name = v8::String::New("unnamed");
Locker locker; v8::Handle<v8::String> source = v8::String::New(argv[i + 1]);
Locker::StartPreemption(10); if (!ExecuteString(source, file_name, false, true))
int size = 0; return 1;
const char *files = ReadChars(argv[++i], &size); i++;
if (files == NULL) return 1; } else if (strcmp(str, "-p") == 0 && i + 1 < argc) {
ShellThread *thread = // Use the lowest possible thread preemption interval to test as many
new ShellThread(threads.length(), i::Vector<const char>(files, size)); // edgecases as possible.
thread->Start(); Locker::StartPreemption(1);
threads.Add(thread); int size = 0;
} else { const char *files = ReadChars(argv[++i], &size);
// Use all other arguments as names of files to load and run. if (files == NULL) return 1;
Locker locker; ShellThread *thread =
HandleScope handle_scope; new ShellThread(threads.length(),
Handle<String> file_name = v8::String::New(str); i::Vector<const char>(files, size));
Handle<String> source = ReadFile(str); thread->Start();
if (source.IsEmpty()) { threads.Add(thread);
printf("Error reading '%s'\n", str); } else {
return 1; // Use all other arguments as names of files to load and run.
HandleScope handle_scope;
Handle<String> file_name = v8::String::New(str);
Handle<String> source = ReadFile(str);
if (source.IsEmpty()) {
printf("Error reading '%s'\n", str);
return 1;
}
if (!ExecuteString(source, file_name, false, true))
return 1;
} }
if (!ExecuteString(source, file_name, false, true))
return 1;
} }
if (i::FLAG_debugger)
v8::Debug::AddDebugEventListener(HandleDebugEvent);
} }
if (i::FLAG_debugger)
v8::Debug::AddDebugEventListener(HandleDebugEvent);
if (run_shell) if (run_shell)
RunShell(); RunShell();
for (int i = 0; i < threads.length(); i++) { for (int i = 0; i < threads.length(); i++) {
......
...@@ -191,12 +191,24 @@ StackGuard::ThreadLocal StackGuard::thread_local_; ...@@ -191,12 +191,24 @@ StackGuard::ThreadLocal StackGuard::thread_local_;
StackGuard::StackGuard() { StackGuard::StackGuard() {
// NOTE: Overall the StackGuard code assumes that the stack grows towards
// lower addresses.
ExecutionAccess access; ExecutionAccess access;
if (thread_local_.nesting_++ == 0 && if (thread_local_.nesting_++ == 0) {
thread_local_.jslimit_ != kInterruptLimit) { // Initial StackGuard is being set. We will set the stack limits based on
// NOTE: We assume that the stack grows towards lower addresses. // the current stack pointer allowing the stack to grow kLimitSize from
ASSERT(thread_local_.jslimit_ == kIllegalLimit); // here.
ASSERT(thread_local_.climit_ == kIllegalLimit);
// Ensure that either the stack limits are unset (kIllegalLimit) or that
// they indicate a pending interruption. The interrupt limit will be
// temporarily reset through the code below and reestablished if the
// interrupt flags indicate that an interrupt is pending.
ASSERT(thread_local_.jslimit_ == kIllegalLimit ||
(thread_local_.jslimit_ == kInterruptLimit &&
thread_local_.interrupt_flags_ != 0));
ASSERT(thread_local_.climit_ == kIllegalLimit ||
(thread_local_.climit_ == kInterruptLimit &&
thread_local_.interrupt_flags_ != 0));
thread_local_.initial_jslimit_ = thread_local_.jslimit_ = thread_local_.initial_jslimit_ = thread_local_.jslimit_ =
GENERATED_CODE_STACK_LIMIT(kLimitSize); GENERATED_CODE_STACK_LIMIT(kLimitSize);
...@@ -210,9 +222,11 @@ StackGuard::StackGuard() { ...@@ -210,9 +222,11 @@ StackGuard::StackGuard() {
set_limits(kInterruptLimit, access); set_limits(kInterruptLimit, access);
} }
} }
// make sure we have proper limits setup // Ensure that proper limits have been set.
ASSERT(thread_local_.jslimit_ != kIllegalLimit && ASSERT(thread_local_.jslimit_ != kIllegalLimit &&
thread_local_.climit_ != kIllegalLimit); thread_local_.climit_ != kIllegalLimit);
ASSERT(thread_local_.initial_jslimit_ != kIllegalLimit &&
thread_local_.initial_climit_ != kIllegalLimit);
} }
......
...@@ -228,7 +228,12 @@ class StackGuard BASE_EMBEDDED { ...@@ -228,7 +228,12 @@ class StackGuard BASE_EMBEDDED {
class StackLimitCheck BASE_EMBEDDED { class StackLimitCheck BASE_EMBEDDED {
public: public:
bool HasOverflowed() const { bool HasOverflowed() const {
return reinterpret_cast<uintptr_t>(this) < StackGuard::climit(); // Stack has overflowed in C++ code only if stack pointer exceeds the C++
// stack guard and the limits are not set to interrupt values.
// TODO(214): Stack overflows are ignored if a interrupt is pending. This
// code should probably always use the initial C++ limit.
return (reinterpret_cast<uintptr_t>(this) < StackGuard::climit()) &&
StackGuard::IsStackOverflow();
} }
}; };
......
...@@ -3778,7 +3778,9 @@ static Object* Runtime_StackGuard(Arguments args) { ...@@ -3778,7 +3778,9 @@ static Object* Runtime_StackGuard(Arguments args) {
ASSERT(args.length() == 1); ASSERT(args.length() == 1);
// First check if this is a real stack overflow. // First check if this is a real stack overflow.
if (StackGuard::IsStackOverflow()) return Runtime_StackOverflow(args); if (StackGuard::IsStackOverflow()) {
return Runtime_StackOverflow(args);
}
return Execution::HandleStackGuardInterrupt(); return Execution::HandleStackGuardInterrupt();
} }
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
namespace v8 { namespace internal { namespace v8 { namespace internal {
ThreadLocalTop Top::thread_local_; ThreadLocalTop Top::thread_local_;
Mutex* Top::break_access_; Mutex* Top::break_access_ = OS::CreateMutex();
StackFrame::Id Top::break_frame_id_; StackFrame::Id Top::break_frame_id_;
int Top::break_count_; int Top::break_count_;
int Top::break_id_; int Top::break_id_;
...@@ -225,7 +225,6 @@ void Top::Initialize() { ...@@ -225,7 +225,6 @@ void Top::Initialize() {
InitializeThreadLocal(); InitializeThreadLocal();
break_access_ = OS::CreateMutex();
break_frame_id_ = StackFrame::NO_ID; break_frame_id_ = StackFrame::NO_ID;
break_count_ = 0; break_count_ = 0;
break_id_ = 0; break_id_ = 0;
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "v8.h" #include "v8.h"
#include "api.h" #include "api.h"
#include "bootstrapper.h"
#include "debug.h" #include "debug.h"
#include "execution.h" #include "execution.h"
#include "v8threads.h" #include "v8threads.h"
...@@ -119,6 +120,11 @@ bool ThreadManager::RestoreThread() { ...@@ -119,6 +120,11 @@ bool ThreadManager::RestoreThread() {
Thread::SetThreadLocal(thread_state_key, NULL); Thread::SetThreadLocal(thread_state_key, NULL);
return true; return true;
} }
// Make sure that the preemption thread cannot modify the thread state while
// it is being archived or restored.
ExecutionAccess access;
// If there is another thread that was lazily archived then we have to really // If there is another thread that was lazily archived then we have to really
// archive it now. // archive it now.
if (lazily_archived_thread_.IsValid()) { if (lazily_archived_thread_.IsValid()) {
...@@ -135,6 +141,7 @@ bool ThreadManager::RestoreThread() { ...@@ -135,6 +141,7 @@ bool ThreadManager::RestoreThread() {
from = Debug::RestoreDebug(from); from = Debug::RestoreDebug(from);
from = StackGuard::RestoreStackGuard(from); from = StackGuard::RestoreStackGuard(from);
from = RegExpStack::RestoreStack(from); from = RegExpStack::RestoreStack(from);
from = Bootstrapper::RestoreState(from);
Thread::SetThreadLocal(thread_state_key, NULL); Thread::SetThreadLocal(thread_state_key, NULL);
state->Unlink(); state->Unlink();
state->LinkInto(ThreadState::FREE_LIST); state->LinkInto(ThreadState::FREE_LIST);
...@@ -160,7 +167,8 @@ static int ArchiveSpacePerThread() { ...@@ -160,7 +167,8 @@ static int ArchiveSpacePerThread() {
Top::ArchiveSpacePerThread() + Top::ArchiveSpacePerThread() +
Debug::ArchiveSpacePerThread() + Debug::ArchiveSpacePerThread() +
StackGuard::ArchiveSpacePerThread() + StackGuard::ArchiveSpacePerThread() +
RegExpStack::ArchiveSpacePerThread(); RegExpStack::ArchiveSpacePerThread() +
Bootstrapper::ArchiveSpacePerThread();
} }
...@@ -242,6 +250,7 @@ void ThreadManager::EagerlyArchiveThread() { ...@@ -242,6 +250,7 @@ void ThreadManager::EagerlyArchiveThread() {
to = Debug::ArchiveDebug(to); to = Debug::ArchiveDebug(to);
to = StackGuard::ArchiveStackGuard(to); to = StackGuard::ArchiveStackGuard(to);
to = RegExpStack::ArchiveStack(to); to = RegExpStack::ArchiveStack(to);
to = Bootstrapper::ArchiveState(to);
lazily_archived_thread_.Initialize(ThreadHandle::INVALID); lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
lazily_archived_thread_state_ = NULL; lazily_archived_thread_state_ = NULL;
} }
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment