Revision: 5599
Author: [email protected]
Date: Thu Oct  7 01:42:25 2010
Log: [Isolates] Allow running multiple isolates in shell and use this in tests.

Sample usage (prints a sequence of mixed 1's and 2's):
  $ ./shell_g -e 'while (1) print(1)' --isolate -e 'while (1) print(2)'

The first group of source files (before the first --isolate) is run in
the default isolate. All the other groups are run in separate isolate
threads.

It would be nice to use our platform.h but it drags too many includes,
so I'm using pthreads on anything non-WIN32 for now.

Review URL: http://codereview.chromium.org/3601010
http://code.google.com/p/v8/source/detail?r=5599

Modified:
 /branches/experimental/isolates/samples/shell.cc
 /branches/experimental/isolates/src/allocation-inl.h
 /branches/experimental/isolates/src/isolate.cc
 /branches/experimental/isolates/src/isolate.h
 /branches/experimental/isolates/src/top.cc
 /branches/experimental/isolates/test/mjsunit/testcfg.py
 /branches/experimental/isolates/tools/test.py

=======================================
--- /branches/experimental/isolates/samples/shell.cc Fri May 14 03:00:24 2010 +++ /branches/experimental/isolates/samples/shell.cc Thu Oct 7 01:42:25 2010
@@ -31,7 +31,14 @@
 #include <stdio.h>
 #include <stdlib.h>

-
+// TODO(isolates):
+//   o Add thread implementation for more platforms.
+//   o Do not assume not WIN32 implies pthreads.
+#ifndef WIN32
+#include <pthread.h>
+#endif
+
+v8::Handle<v8::Context> CreateShellContext();
 void RunShell(v8::Handle<v8::Context> context);
 bool ExecuteString(v8::Handle<v8::String> source,
                    v8::Handle<v8::Value> name,
@@ -46,30 +53,123 @@
 void ReportException(v8::TryCatch* handler);


+#ifndef WIN32
+void* IsolateThreadEntry(void* arg);
+#endif
+
+class SourceGroup {
+ public:
+  SourceGroup() : argv_(NULL), begin_offset_(0), end_offset_(0) {
+#ifndef WIN32
+    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)) {
+          exit(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)) {
+          exit(1);
+          return;
+        }
+      }
+    }
+  }
+
+#ifdef WIN32
+  void StartExecuteInThread() { ExecuteInThread(); }
+  void WaitForThread() {}
+
+#else
+  void StartExecuteInThread() {
+    pthread_create(&thread_, NULL, &IsolateThreadEntry, this);
+  }
+
+  void WaitForThread() {
+    if (thread_ == 0) return;
+    pthread_join(thread_, NULL);
+    thread_ = 0;
+  }
+#endif  // WIN32
+
+ private:
+  void ExecuteInThread() {
+    v8::Isolate* isolate = v8::Isolate::New();
+    {
+      v8::Isolate::Scope iscope(isolate);
+      v8::HandleScope scope;
+      v8::Context::Scope cscope(CreateShellContext());
+      Execute();
+    }
+    isolate->Dispose();
+  }
+
+  const char** argv_;
+  int begin_offset_;
+  int end_offset_;
+#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
+
+
 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::Handle<v8::Context> context = v8::Context::New(NULL, global);
+  v8::Handle<v8::Context> context = CreateShellContext();
   // Enter the newly created execution environment.
   v8::Context::Scope context_scope(context);
   bool run_shell = (argc == 1);
+  int num_isolates = 1;
+  for (int i = 1; i < argc; i++) {
+    if (strcmp(argv[i], "--isolate") == 0) ++num_isolates;
+  }
+  SourceGroup* 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, "--shell") == 0) {
+    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-
@@ -77,28 +177,18 @@
       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 (!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();
+  }
+  delete[] isolate_sources;
   return 0;
 }

@@ -114,6 +204,25 @@
 const char* ToCString(const v8::String::Utf8Value& value) {
   return *value ? *value : "<string conversion failed>";
 }
+
+
+// Creates a new execution environment containing the built-in
+// functions.
+v8::Handle<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'
=======================================
--- /branches/experimental/isolates/src/allocation-inl.h Thu Jun 24 09:41:05 2010 +++ /branches/experimental/isolates/src/allocation-inl.h Thu Oct 7 01:42:25 2010
@@ -58,7 +58,11 @@


 bool NativeAllocationChecker::allocation_allowed() {
+#ifdef DEBUG
   return Isolate::Current()->allocation_disallowed() == 0;
+#else
+  return true;
+#endif  // DEBUG
 }


=======================================
--- /branches/experimental/isolates/src/isolate.cc      Fri Oct  1 11:01:30 2010
+++ /branches/experimental/isolates/src/isolate.cc      Thu Oct  7 01:42:25 2010
@@ -247,6 +247,8 @@
     thread_data_table_ = new Isolate::ThreadDataTable();
     default_isolate_ = new Isolate();
   }
+  // Can't use SetIsolateThreadLocals(default_isolate_, NULL) here
+  // becase a non-null thread data may be already set.
   Thread::SetThreadLocal(isolate_key_, default_isolate_);
   CHECK(default_isolate_->PreInit());
 }
@@ -389,11 +391,22 @@
 }

 void Isolate::TearDown() {
+  // Temporarily set this isolate as current so that various parts of
+  // the isolate can access it in their destructors without having a
+  // direct pointer. We don't use Enter/Exit here to avoid
+  // initializing the thread data.
+  PerIsolateThreadData* saved_data = CurrentPerIsolateThreadData();
+  Isolate* saved_isolate = UncheckedCurrent();
+  SetIsolateThreadLocals(this, NULL);
+
   Deinit();

   if (!IsDefaultIsolate()) {
     delete this;
   }
+
+  // Restore the previous current isolate.
+  SetIsolateThreadLocals(saved_isolate, saved_data);
 }


@@ -421,6 +434,14 @@
     state_ = PREINITIALIZED;
   }
 }
+
+
+void Isolate::SetIsolateThreadLocals(Isolate* isolate,
+                                     PerIsolateThreadData* data) {
+  Thread::SetThreadLocal(isolate_key_, isolate);
+  Thread::SetThreadLocal(per_isolate_thread_data_key_, data);
+}
+

 Isolate::~Isolate() {
 #ifdef ENABLE_LOGGING_AND_PROFILING
@@ -701,8 +722,7 @@
                                             entry_stack_);
   entry_stack_ = item;

-  Thread::SetThreadLocal(per_isolate_thread_data_key_, data);
-  Thread::SetThreadLocal(isolate_key_, this);
+  SetIsolateThreadLocals(this, data);

   CHECK(PreInit());

@@ -732,8 +752,7 @@
   delete item;

// Reinit the current thread for the isolate it was running before this one. - Thread::SetThreadLocal(per_isolate_thread_data_key_, previous_thread_data);
-  Thread::SetThreadLocal(isolate_key_, previous_isolate);
+  SetIsolateThreadLocals(previous_isolate, previous_thread_data);
 }

 } }  // namespace v8::internal
=======================================
--- /branches/experimental/isolates/src/isolate.h       Fri Sep 24 17:27:22 2010
+++ /branches/experimental/isolates/src/isolate.h       Thu Oct  7 01:42:25 2010
@@ -633,7 +633,7 @@
   char* ArchiveThread(char* to);
   char* RestoreThread(char* from);

-  static const char* kStackOverflowMessage;
+  static const char* const kStackOverflowMessage;

   // Accessors.
#define GLOBAL_ACCESSOR(type, name, initialvalue) \
@@ -886,6 +886,9 @@

   void Deinit();

+  static void SetIsolateThreadLocals(Isolate* isolate,
+                                     PerIsolateThreadData* data);
+
   enum State {
     UNINITIALIZED,    // Some components may not have been allocated.
PREINITIALIZED, // Components have been allocated but not initialized.
=======================================
--- /branches/experimental/isolates/src/top.cc  Thu Sep  9 17:53:48 2010
+++ /branches/experimental/isolates/src/top.cc  Thu Oct  7 01:42:25 2010
@@ -461,7 +461,7 @@
 }


-const char* Isolate::kStackOverflowMessage =
+const char* const Isolate::kStackOverflowMessage =
   "Uncaught RangeError: Maximum call stack size exceeded";


=======================================
--- /branches/experimental/isolates/test/mjsunit/testcfg.py Wed Sep 1 10:01:38 2010 +++ /branches/experimental/isolates/test/mjsunit/testcfg.py Thu Oct 7 01:42:25 2010
@@ -38,24 +38,31 @@

 class MjsunitTestCase(test.TestCase):

-  def __init__(self, path, file, mode, context, config):
+  def __init__(self, path, file, mode, context, config, isolates):
     super(MjsunitTestCase, self).__init__(context, path, mode)
     self.file = file
     self.config = config
     self.self_script = False
+    self.isolates = isolates

   def GetLabel(self):
     return "%s %s" % (self.mode, self.GetName())

   def GetName(self):
-    return self.path[-1]
-
-  def GetCommand(self):
+    return self.path[-1] + ["", "-isolates"][self.isolates]
+
+  def TestsIsolates(self):
+    return self.isolates
+
+  def GetVmCommand(self, source):
     result = self.config.context.GetVmCommand(self, self.mode)
-    source = open(self.file).read()
     flags_match = FLAGS_PATTERN.search(source)
     if flags_match:
       result += flags_match.group(1).strip().split()
+    return result
+
+  def GetVmArguments(self, source):
+    result = []
     additional_files = []
     files_match = FILES_PATTERN.search(source);
     # Accept several lines of 'Files:'
@@ -73,6 +80,15 @@
     result += [framework, self.file]
     return result

+  def GetCommand(self):
+    source = open(self.file).read()
+    result = self.GetVmCommand(source)
+    result += self.GetVmArguments(source)
+    if self.isolates:
+      result.append("--isolate")
+      result += self.GetVmArguments(source)
+    return result
+
   def GetSource(self):
     return open(self.file).read()

@@ -116,7 +132,8 @@
     for test in all_tests:
       if self.Contains(path, test):
         file_path = join(self.root, reduce(join, test[1:], "") + ".js")
- result.append(MjsunitTestCase(test, file_path, mode, self.context, self)) + result.append(MjsunitTestCase(test, file_path, mode, self.context, self, False)) + result.append(MjsunitTestCase(test, file_path, mode, self.context, self, True))
     return result

   def GetBuildRequirements(self):
=======================================
--- /branches/experimental/isolates/tools/test.py       Wed Sep  1 10:01:38 2010
+++ /branches/experimental/isolates/tools/test.py       Thu Oct  7 01:42:25 2010
@@ -340,6 +340,9 @@
   def IsNegative(self):
     return False

+  def TestsIsolates(self):
+    return False
+
   def CompareTime(self, other):
     return cmp(other.duration, self.duration)

@@ -1000,6 +1003,9 @@
     self.case = case
     self.outcomes = outcomes

+  def TestsIsolates(self):
+    return self.case.TestsIsolates()
+

 class Configuration(object):
   """The parsed contents of a configuration file"""
@@ -1159,6 +1165,7 @@
result.add_option("--no-suppress-dialogs", help="Display Windows dialogs for crashing tests",
         dest="suppress_dialogs", action="store_false")
   result.add_option("--shell", help="Path to V8 shell", default="shell")
+ result.add_option("--isolates", help="Whether to test isolates", default=False, action="store_true")
   result.add_option("--store-unexpected-output",
       help="Store the temporary JS files from tests that fails",
       dest="store_unexpected_output", default=True, action="store_true")
@@ -1380,6 +1387,8 @@
   def DoSkip(case):
     return SKIP in case.outcomes or SLOW in case.outcomes
   cases_to_run = [ c for c in all_cases if not DoSkip(c) ]
+  if not options.isolates:
+    cases_to_run = [c for c in cases_to_run if not c.TestsIsolates()]
   if len(cases_to_run) == 0:
     print "No tests to run."
     return 0

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to