Index: samples/shell.cc |
=================================================================== |
--- samples/shell.cc (revision 7267) |
+++ samples/shell.cc (working copy) |
@@ -33,7 +33,26 @@ |
#include <stdio.h> |
#include <stdlib.h> |
+#include "../src/v8.h" |
+// TODO(isolates): |
+// o Either use V8 internal platform stuff for every platform or |
+// re-implement it. |
+// o Do not assume not WIN32 implies pthreads. |
+#ifndef WIN32 |
+#include <pthread.h> // NOLINT |
+#include <unistd.h> // NOLINT |
+#endif |
+ |
+static void ExitShell(int exit_code) { |
+ // Use _exit instead of exit to avoid races between isolate |
+ // threads and static destructors. |
+ fflush(stdout); |
+ fflush(stderr); |
+ _exit(exit_code); |
+} |
+ |
+v8::Persistent<v8::Context> CreateShellContext(); |
void RunShell(v8::Handle<v8::Context> context); |
bool ExecuteString(v8::Handle<v8::String> source, |
v8::Handle<v8::Value> name, |
@@ -48,63 +67,193 @@ |
void ReportException(v8::TryCatch* handler); |
+#ifndef WIN32 |
+void* IsolateThreadEntry(void* arg); |
+#endif |
+ |
+static bool last_run = true; |
+ |
+class SourceGroup { |
+ public: |
+ SourceGroup() : argv_(NULL), |
+ begin_offset_(0), |
+ end_offset_(0), |
+ next_semaphore_(NULL), |
+ done_semaphore_(NULL) { |
+#ifndef WIN32 |
+ next_semaphore_ = v8::internal::OS::CreateSemaphore(0); |
+ done_semaphore_ = v8::internal::OS::CreateSemaphore(0); |
+ thread_ = 0; |
+#endif |
+ } |
+ |
+ void Begin(char** argv, int offset) { |
+ argv_ = const_cast<const char**>(argv); |
+ begin_offset_ = offset; |
+ } |
+ |
+ void End(int offset) { end_offset_ = offset; } |
+ |
+ void Execute() { |
+ for (int i = begin_offset_; i < end_offset_; ++i) { |
+ const char* arg = argv_[i]; |
+ if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) { |
+ // Execute argument given to -e option directly. |
+ v8::HandleScope handle_scope; |
+ v8::Handle<v8::String> file_name = v8::String::New("unnamed"); |
+ v8::Handle<v8::String> source = v8::String::New(argv_[i + 1]); |
+ if (!ExecuteString(source, file_name, false, true)) { |
+ ExitShell(1); |
+ return; |
+ } |
+ ++i; |
+ } else if (arg[0] == '-') { |
+ // Ignore other options. They have been parsed already. |
+ } else { |
+ // Use all other arguments as names of files to load and run. |
+ v8::HandleScope handle_scope; |
+ v8::Handle<v8::String> file_name = v8::String::New(arg); |
+ v8::Handle<v8::String> source = ReadFile(arg); |
+ if (source.IsEmpty()) { |
+ printf("Error reading '%s'\n", arg); |
+ } |
+ if (!ExecuteString(source, file_name, false, true)) { |
+ ExitShell(1); |
+ return; |
+ } |
+ } |
+ } |
+ } |
+ |
+#ifdef WIN32 |
+ void StartExecuteInThread() { ExecuteInThread(); } |
+ void WaitForThread() {} |
+ |
+#else |
+ void StartExecuteInThread() { |
+ if (thread_ == 0) { |
+ pthread_attr_t attr; |
+ // On some systems (OSX 10.6) the stack size default is 0.5Mb or less |
+ // which is not enough to parse the big literal expressions used in tests. |
+ // The stack size should be at least StackGuard::kLimitSize + some |
+ // OS-specific padding for thread startup code. |
+ size_t stacksize = 2 << 20; // 2 Mb seems to be enough |
+ pthread_attr_init(&attr); |
+ pthread_attr_setstacksize(&attr, stacksize); |
+ int error = pthread_create(&thread_, &attr, &IsolateThreadEntry, this); |
+ if (error != 0) { |
+ fprintf(stderr, "Error creating isolate thread.\n"); |
+ ExitShell(1); |
+ } |
+ } |
+ next_semaphore_->Signal(); |
+ } |
+ |
+ void WaitForThread() { |
+ if (thread_ == 0) return; |
+ if (last_run) { |
+ pthread_join(thread_, NULL); |
+ thread_ = 0; |
+ } else { |
+ done_semaphore_->Wait(); |
+ } |
+ } |
+#endif // WIN32 |
+ |
+ private: |
+ void ExecuteInThread() { |
+ v8::Isolate* isolate = v8::Isolate::New(); |
+ do { |
+ if (next_semaphore_ != NULL) next_semaphore_->Wait(); |
+ { |
+ v8::Isolate::Scope iscope(isolate); |
+ v8::HandleScope scope; |
+ v8::Persistent<v8::Context> context = CreateShellContext(); |
+ { |
+ v8::Context::Scope cscope(context); |
+ Execute(); |
+ } |
+ context.Dispose(); |
+ } |
+ if (done_semaphore_ != NULL) done_semaphore_->Signal(); |
+ } while (!last_run); |
+ isolate->Dispose(); |
+ } |
+ |
+ const char** argv_; |
+ int begin_offset_; |
+ int end_offset_; |
+ v8::internal::Semaphore* next_semaphore_; |
+ v8::internal::Semaphore* done_semaphore_; |
+#ifndef WIN32 |
+ pthread_t thread_; |
+#endif |
+ |
+ friend void* IsolateThreadEntry(void* arg); |
+}; |
+ |
+#ifndef WIN32 |
+void* IsolateThreadEntry(void* arg) { |
+ reinterpret_cast<SourceGroup*>(arg)->ExecuteInThread(); |
+ return NULL; |
+} |
+#endif |
+ |
+ |
+static SourceGroup* isolate_sources = NULL; |
+ |
+ |
int RunMain(int argc, char* argv[]) { |
+ v8::V8::SetFlagsFromCommandLine(&argc, argv, true); |
v8::HandleScope handle_scope; |
- // Create a template for the global object. |
- v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); |
- // Bind the global 'print' function to the C++ Print callback. |
- global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)); |
- // Bind the global 'read' function to the C++ Read callback. |
- global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read)); |
- // Bind the global 'load' function to the C++ Load callback. |
- global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load)); |
- // Bind the 'quit' function |
- global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit)); |
- // Bind the 'version' function |
- global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version)); |
- // Create a new execution environment containing the built-in |
- // functions |
- v8::Persistent<v8::Context> context = v8::Context::New(NULL, global); |
+ v8::Persistent<v8::Context> context = CreateShellContext(); |
+ // Enter the newly created execution environment. |
+ context->Enter(); |
if (context.IsEmpty()) { |
printf("Error creating context\n"); |
return 1; |
} |
bool run_shell = (argc == 1); |
+ int num_isolates = 1; |
for (int i = 1; i < argc; i++) { |
- // Enter the execution environment before evaluating any code. |
- v8::Context::Scope context_scope(context); |
- const char* str = argv[i]; |
- if (strcmp(str, "--shell") == 0) { |
- run_shell = true; |
- } else if (strcmp(str, "-f") == 0) { |
- // Ignore any -f flags for compatibility with the other stand- |
- // alone JavaScript engines. |
- continue; |
- } else if (strncmp(str, "--", 2) == 0) { |
- printf("Warning: unknown flag %s.\nTry --help for options\n", str); |
- } else if (strcmp(str, "-e") == 0 && i + 1 < argc) { |
- // Execute argument given to -e option directly |
- v8::HandleScope handle_scope; |
- v8::Handle<v8::String> file_name = v8::String::New("unnamed"); |
- v8::Handle<v8::String> source = v8::String::New(argv[i + 1]); |
- if (!ExecuteString(source, file_name, false, true)) |
- return 1; |
- i++; |
- } else { |
- // Use all other arguments as names of files to load and run. |
- v8::HandleScope handle_scope; |
- v8::Handle<v8::String> file_name = v8::String::New(str); |
- v8::Handle<v8::String> source = ReadFile(str); |
- if (source.IsEmpty()) { |
- printf("Error reading '%s'\n", str); |
- return 1; |
+ if (strcmp(argv[i], "--isolate") == 0) ++num_isolates; |
+ } |
+ if (isolate_sources == NULL) { |
+ isolate_sources = new SourceGroup[num_isolates]; |
+ SourceGroup* current = isolate_sources; |
+ current->Begin(argv, 1); |
+ for (int i = 1; i < argc; i++) { |
+ const char* str = argv[i]; |
+ if (strcmp(str, "--isolate") == 0) { |
+ current->End(i); |
+ current++; |
+ current->Begin(argv, i + 1); |
+ } else if (strcmp(str, "--shell") == 0) { |
+ run_shell = true; |
+ } else if (strcmp(str, "-f") == 0) { |
+ // Ignore any -f flags for compatibility with the other stand- |
+ // alone JavaScript engines. |
+ continue; |
+ } else if (strncmp(str, "--", 2) == 0) { |
+ printf("Warning: unknown flag %s.\nTry --help for options\n", str); |
} |
- if (!ExecuteString(source, file_name, false, true)) |
- return 1; |
} |
+ current->End(argc); |
} |
+ for (int i = 1; i < num_isolates; ++i) { |
+ isolate_sources[i].StartExecuteInThread(); |
+ } |
+ isolate_sources[0].Execute(); |
if (run_shell) RunShell(context); |
+ for (int i = 1; i < num_isolates; ++i) { |
+ isolate_sources[i].WaitForThread(); |
+ } |
+ if (last_run) { |
+ delete[] isolate_sources; |
+ isolate_sources = NULL; |
+ } |
+ context->Exit(); |
context.Dispose(); |
return 0; |
} |
@@ -142,6 +291,7 @@ |
printf("============ Stress %d/%d ============\n", |
i + 1, stress_runs); |
v8::Testing::PrepareStressRun(i); |
+ last_run = (i == stress_runs - 1); |
result = RunMain(argc, argv); |
} |
printf("======== Full Deoptimization =======\n"); |
@@ -160,6 +310,25 @@ |
} |
+// Creates a new execution environment containing the built-in |
+// functions. |
+v8::Persistent<v8::Context> CreateShellContext() { |
+ // Create a template for the global object. |
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); |
+ // Bind the global 'print' function to the C++ Print callback. |
+ global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)); |
+ // Bind the global 'read' function to the C++ Read callback. |
+ global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read)); |
+ // Bind the global 'load' function to the C++ Load callback. |
+ global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load)); |
+ // Bind the 'quit' function |
+ global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit)); |
+ // Bind the 'version' function |
+ global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version)); |
+ return v8::Context::New(NULL, global); |
+} |
+ |
+ |
// The callback that is invoked by v8 whenever the JavaScript 'print' |
// function is called. Prints its arguments on stdout separated by |
// spaces and ending with a newline. |
@@ -229,7 +398,7 @@ |
// If not arguments are given args[0] will yield undefined which |
// converts to the integer value 0. |
int exit_code = args[0]->Int32Value(); |
- exit(exit_code); |
+ ExitShell(exit_code); |
return v8::Undefined(); |
} |