1// Copyright 2007-2011 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <limits.h>
29
30#include "src/v8.h"
31
32#include "src/api.h"
33#include "src/base/platform/platform.h"
34#include "src/compilation-cache.h"
35#include "src/execution.h"
36#include "src/isolate.h"
37#include "src/parser.h"
38#include "src/smart-pointers.h"
39#include "src/snapshot.h"
40#include "src/unicode-inl.h"
41#include "src/utils.h"
42#include "test/cctest/cctest.h"
43
44using ::v8::Context;
45using ::v8::Extension;
46using ::v8::Function;
47using ::v8::HandleScope;
48using ::v8::Local;
49using ::v8::Object;
50using ::v8::ObjectTemplate;
51using ::v8::Persistent;
52using ::v8::Script;
53using ::v8::String;
54using ::v8::Value;
55using ::v8::V8;
56
57
58// Migrating an isolate
59class KangarooThread : public v8::base::Thread {
60 public:
61  KangarooThread(v8::Isolate* isolate, v8::Handle<v8::Context> context)
62      : Thread(Options("KangarooThread")),
63        isolate_(isolate),
64        context_(isolate, context) {}
65
66  void Run() {
67    {
68      v8::Locker locker(isolate_);
69      v8::Isolate::Scope isolate_scope(isolate_);
70      CHECK_EQ(isolate_, v8::internal::Isolate::Current());
71      v8::HandleScope scope(isolate_);
72      v8::Local<v8::Context> context =
73          v8::Local<v8::Context>::New(isolate_, context_);
74      v8::Context::Scope context_scope(context);
75      Local<Value> v = CompileRun("getValue()");
76      CHECK(v->IsNumber());
77      CHECK_EQ(30, static_cast<int>(v->NumberValue()));
78    }
79    {
80      v8::Locker locker(isolate_);
81      v8::Isolate::Scope isolate_scope(isolate_);
82      v8::HandleScope scope(isolate_);
83      v8::Local<v8::Context> context =
84          v8::Local<v8::Context>::New(isolate_, context_);
85      v8::Context::Scope context_scope(context);
86      Local<Value> v = CompileRun("getValue()");
87      CHECK(v->IsNumber());
88      CHECK_EQ(30, static_cast<int>(v->NumberValue()));
89    }
90    isolate_->Dispose();
91  }
92
93 private:
94  v8::Isolate* isolate_;
95  Persistent<v8::Context> context_;
96};
97
98
99// Migrates an isolate from one thread to another
100TEST(KangarooIsolates) {
101  v8::Isolate* isolate = v8::Isolate::New();
102  i::SmartPointer<KangarooThread> thread1;
103  {
104    v8::Locker locker(isolate);
105    v8::Isolate::Scope isolate_scope(isolate);
106    v8::HandleScope handle_scope(isolate);
107    v8::Local<v8::Context> context = v8::Context::New(isolate);
108    v8::Context::Scope context_scope(context);
109    CHECK_EQ(isolate, v8::internal::Isolate::Current());
110    CompileRun("function getValue() { return 30; }");
111    thread1.Reset(new KangarooThread(isolate, context));
112  }
113  thread1->Start();
114  thread1->Join();
115}
116
117
118static void CalcFibAndCheck() {
119  Local<Value> v = CompileRun("function fib(n) {"
120                              "  if (n <= 2) return 1;"
121                              "  return fib(n-1) + fib(n-2);"
122                              "}"
123                              "fib(10)");
124  CHECK(v->IsNumber());
125  CHECK_EQ(55, static_cast<int>(v->NumberValue()));
126}
127
128class JoinableThread {
129 public:
130  explicit JoinableThread(const char* name)
131    : name_(name),
132      semaphore_(0),
133      thread_(this) {
134  }
135
136  virtual ~JoinableThread() {}
137
138  void Start() {
139    thread_.Start();
140  }
141
142  void Join() {
143    semaphore_.Wait();
144  }
145
146  virtual void Run() = 0;
147
148 private:
149  class ThreadWithSemaphore : public v8::base::Thread {
150   public:
151    explicit ThreadWithSemaphore(JoinableThread* joinable_thread)
152        : Thread(Options(joinable_thread->name_)),
153          joinable_thread_(joinable_thread) {}
154
155    virtual void Run() {
156      joinable_thread_->Run();
157      joinable_thread_->semaphore_.Signal();
158    }
159
160   private:
161    JoinableThread* joinable_thread_;
162  };
163
164  const char* name_;
165  v8::base::Semaphore semaphore_;
166  ThreadWithSemaphore thread_;
167
168  friend class ThreadWithSemaphore;
169
170  DISALLOW_COPY_AND_ASSIGN(JoinableThread);
171};
172
173
174class IsolateLockingThreadWithLocalContext : public JoinableThread {
175 public:
176  explicit IsolateLockingThreadWithLocalContext(v8::Isolate* isolate)
177    : JoinableThread("IsolateLockingThread"),
178      isolate_(isolate) {
179  }
180
181  virtual void Run() {
182    v8::Locker locker(isolate_);
183    v8::Isolate::Scope isolate_scope(isolate_);
184    v8::HandleScope handle_scope(isolate_);
185    LocalContext local_context(isolate_);
186    CHECK_EQ(isolate_, v8::internal::Isolate::Current());
187    CalcFibAndCheck();
188  }
189 private:
190  v8::Isolate* isolate_;
191};
192
193
194static void StartJoinAndDeleteThreads(const i::List<JoinableThread*>& threads) {
195  for (int i = 0; i < threads.length(); i++) {
196    threads[i]->Start();
197  }
198  for (int i = 0; i < threads.length(); i++) {
199    threads[i]->Join();
200  }
201  for (int i = 0; i < threads.length(); i++) {
202    delete threads[i];
203  }
204}
205
206
207// Run many threads all locking on the same isolate
208TEST(IsolateLockingStress) {
209#if V8_TARGET_ARCH_MIPS
210  const int kNThreads = 50;
211#else
212  const int kNThreads = 100;
213#endif
214  i::List<JoinableThread*> threads(kNThreads);
215  v8::Isolate* isolate = v8::Isolate::New();
216  for (int i = 0; i < kNThreads; i++) {
217    threads.Add(new IsolateLockingThreadWithLocalContext(isolate));
218  }
219  StartJoinAndDeleteThreads(threads);
220  isolate->Dispose();
221}
222
223class IsolateNonlockingThread : public JoinableThread {
224 public:
225  IsolateNonlockingThread() : JoinableThread("IsolateNonlockingThread") {}
226
227  virtual void Run() {
228    v8::Isolate* isolate = v8::Isolate::New();
229    {
230      v8::Isolate::Scope isolate_scope(isolate);
231      v8::HandleScope handle_scope(isolate);
232      v8::Handle<v8::Context> context = v8::Context::New(isolate);
233      v8::Context::Scope context_scope(context);
234      CHECK_EQ(isolate, v8::internal::Isolate::Current());
235      CalcFibAndCheck();
236    }
237    isolate->Dispose();
238  }
239 private:
240};
241
242
243// Run many threads each accessing its own isolate without locking
244TEST(MultithreadedParallelIsolates) {
245#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
246  const int kNThreads = 10;
247#elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT
248  const int kNThreads = 4;
249#else
250  const int kNThreads = 50;
251#endif
252  i::List<JoinableThread*> threads(kNThreads);
253  for (int i = 0; i < kNThreads; i++) {
254    threads.Add(new IsolateNonlockingThread());
255  }
256  StartJoinAndDeleteThreads(threads);
257}
258
259
260class IsolateNestedLockingThread : public JoinableThread {
261 public:
262  explicit IsolateNestedLockingThread(v8::Isolate* isolate)
263    : JoinableThread("IsolateNestedLocking"), isolate_(isolate) {
264  }
265  virtual void Run() {
266    v8::Locker lock(isolate_);
267    v8::Isolate::Scope isolate_scope(isolate_);
268    v8::HandleScope handle_scope(isolate_);
269    LocalContext local_context(isolate_);
270    {
271      v8::Locker another_lock(isolate_);
272      CalcFibAndCheck();
273    }
274    {
275      v8::Locker another_lock(isolate_);
276      CalcFibAndCheck();
277    }
278  }
279 private:
280  v8::Isolate* isolate_;
281};
282
283
284// Run  many threads with nested locks
285TEST(IsolateNestedLocking) {
286#if V8_TARGET_ARCH_MIPS
287  const int kNThreads = 50;
288#else
289  const int kNThreads = 100;
290#endif
291  v8::Isolate* isolate = v8::Isolate::New();
292  i::List<JoinableThread*> threads(kNThreads);
293  for (int i = 0; i < kNThreads; i++) {
294    threads.Add(new IsolateNestedLockingThread(isolate));
295  }
296  StartJoinAndDeleteThreads(threads);
297  isolate->Dispose();
298}
299
300
301class SeparateIsolatesLocksNonexclusiveThread : public JoinableThread {
302 public:
303  SeparateIsolatesLocksNonexclusiveThread(v8::Isolate* isolate1,
304                                          v8::Isolate* isolate2)
305    : JoinableThread("SeparateIsolatesLocksNonexclusiveThread"),
306      isolate1_(isolate1), isolate2_(isolate2) {
307  }
308
309  virtual void Run() {
310    v8::Locker lock(isolate1_);
311    v8::Isolate::Scope isolate_scope(isolate1_);
312    v8::HandleScope handle_scope(isolate1_);
313    LocalContext local_context(isolate1_);
314
315    IsolateLockingThreadWithLocalContext threadB(isolate2_);
316    threadB.Start();
317    CalcFibAndCheck();
318    threadB.Join();
319  }
320 private:
321  v8::Isolate* isolate1_;
322  v8::Isolate* isolate2_;
323};
324
325
326// Run parallel threads that lock and access different isolates in parallel
327TEST(SeparateIsolatesLocksNonexclusive) {
328#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
329  const int kNThreads = 50;
330#else
331  const int kNThreads = 100;
332#endif
333  v8::Isolate* isolate1 = v8::Isolate::New();
334  v8::Isolate* isolate2 = v8::Isolate::New();
335  i::List<JoinableThread*> threads(kNThreads);
336  for (int i = 0; i < kNThreads; i++) {
337    threads.Add(new SeparateIsolatesLocksNonexclusiveThread(isolate1,
338                                                             isolate2));
339  }
340  StartJoinAndDeleteThreads(threads);
341  isolate2->Dispose();
342  isolate1->Dispose();
343}
344
345class LockIsolateAndCalculateFibSharedContextThread : public JoinableThread {
346 public:
347  explicit LockIsolateAndCalculateFibSharedContextThread(
348      v8::Isolate* isolate, v8::Handle<v8::Context> context)
349    : JoinableThread("LockIsolateAndCalculateFibThread"),
350      isolate_(isolate),
351      context_(isolate, context) {
352  }
353
354  virtual void Run() {
355    v8::Locker lock(isolate_);
356    v8::Isolate::Scope isolate_scope(isolate_);
357    HandleScope handle_scope(isolate_);
358    v8::Local<v8::Context> context =
359        v8::Local<v8::Context>::New(isolate_, context_);
360    v8::Context::Scope context_scope(context);
361    CalcFibAndCheck();
362  }
363 private:
364  v8::Isolate* isolate_;
365  Persistent<v8::Context> context_;
366};
367
368class LockerUnlockerThread : public JoinableThread {
369 public:
370  explicit LockerUnlockerThread(v8::Isolate* isolate)
371    : JoinableThread("LockerUnlockerThread"),
372      isolate_(isolate) {
373  }
374
375  virtual void Run() {
376    v8::Locker lock(isolate_);
377    v8::Isolate::Scope isolate_scope(isolate_);
378    v8::HandleScope handle_scope(isolate_);
379    v8::Local<v8::Context> context = v8::Context::New(isolate_);
380    {
381      v8::Context::Scope context_scope(context);
382      CalcFibAndCheck();
383    }
384    {
385      LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
386      isolate_->Exit();
387      v8::Unlocker unlocker(isolate_);
388      thread.Start();
389      thread.Join();
390    }
391    isolate_->Enter();
392    {
393      v8::Context::Scope context_scope(context);
394      CalcFibAndCheck();
395    }
396  }
397
398 private:
399  v8::Isolate* isolate_;
400};
401
402
403// Use unlocker inside of a Locker, multiple threads.
404TEST(LockerUnlocker) {
405#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
406  const int kNThreads = 50;
407#else
408  const int kNThreads = 100;
409#endif
410  i::List<JoinableThread*> threads(kNThreads);
411  v8::Isolate* isolate = v8::Isolate::New();
412  for (int i = 0; i < kNThreads; i++) {
413    threads.Add(new LockerUnlockerThread(isolate));
414  }
415  StartJoinAndDeleteThreads(threads);
416  isolate->Dispose();
417}
418
419class LockTwiceAndUnlockThread : public JoinableThread {
420 public:
421  explicit LockTwiceAndUnlockThread(v8::Isolate* isolate)
422    : JoinableThread("LockTwiceAndUnlockThread"),
423      isolate_(isolate) {
424  }
425
426  virtual void Run() {
427    v8::Locker lock(isolate_);
428    v8::Isolate::Scope isolate_scope(isolate_);
429    v8::HandleScope handle_scope(isolate_);
430    v8::Local<v8::Context> context = v8::Context::New(isolate_);
431    {
432      v8::Context::Scope context_scope(context);
433      CalcFibAndCheck();
434    }
435    {
436      v8::Locker second_lock(isolate_);
437      {
438        LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
439        isolate_->Exit();
440        v8::Unlocker unlocker(isolate_);
441        thread.Start();
442        thread.Join();
443      }
444    }
445    isolate_->Enter();
446    {
447      v8::Context::Scope context_scope(context);
448      CalcFibAndCheck();
449    }
450  }
451
452 private:
453  v8::Isolate* isolate_;
454};
455
456
457// Use Unlocker inside two Lockers.
458TEST(LockTwiceAndUnlock) {
459#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
460  const int kNThreads = 50;
461#else
462  const int kNThreads = 100;
463#endif
464  i::List<JoinableThread*> threads(kNThreads);
465  v8::Isolate* isolate = v8::Isolate::New();
466  for (int i = 0; i < kNThreads; i++) {
467    threads.Add(new LockTwiceAndUnlockThread(isolate));
468  }
469  StartJoinAndDeleteThreads(threads);
470  isolate->Dispose();
471}
472
473class LockAndUnlockDifferentIsolatesThread : public JoinableThread {
474 public:
475  LockAndUnlockDifferentIsolatesThread(v8::Isolate* isolate1,
476                                       v8::Isolate* isolate2)
477    : JoinableThread("LockAndUnlockDifferentIsolatesThread"),
478      isolate1_(isolate1),
479      isolate2_(isolate2) {
480  }
481
482  virtual void Run() {
483    i::SmartPointer<LockIsolateAndCalculateFibSharedContextThread> thread;
484    v8::Locker lock1(isolate1_);
485    CHECK(v8::Locker::IsLocked(isolate1_));
486    CHECK(!v8::Locker::IsLocked(isolate2_));
487    {
488      v8::Isolate::Scope isolate_scope(isolate1_);
489      v8::HandleScope handle_scope(isolate1_);
490      v8::Handle<v8::Context> context1 = v8::Context::New(isolate1_);
491      {
492        v8::Context::Scope context_scope(context1);
493        CalcFibAndCheck();
494      }
495      thread.Reset(new LockIsolateAndCalculateFibSharedContextThread(
496          isolate1_, context1));
497    }
498    v8::Locker lock2(isolate2_);
499    CHECK(v8::Locker::IsLocked(isolate1_));
500    CHECK(v8::Locker::IsLocked(isolate2_));
501    {
502      v8::Isolate::Scope isolate_scope(isolate2_);
503      v8::HandleScope handle_scope(isolate2_);
504      v8::Handle<v8::Context> context2 = v8::Context::New(isolate2_);
505      {
506        v8::Context::Scope context_scope(context2);
507        CalcFibAndCheck();
508      }
509      v8::Unlocker unlock1(isolate1_);
510      CHECK(!v8::Locker::IsLocked(isolate1_));
511      CHECK(v8::Locker::IsLocked(isolate2_));
512      v8::Context::Scope context_scope(context2);
513      thread->Start();
514      CalcFibAndCheck();
515      thread->Join();
516    }
517  }
518
519 private:
520  v8::Isolate* isolate1_;
521  v8::Isolate* isolate2_;
522};
523
524
525// Lock two isolates and unlock one of them.
526TEST(LockAndUnlockDifferentIsolates) {
527  v8::Isolate* isolate1 = v8::Isolate::New();
528  v8::Isolate* isolate2 = v8::Isolate::New();
529  LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2);
530  thread.Start();
531  thread.Join();
532  isolate2->Dispose();
533  isolate1->Dispose();
534}
535
536class LockUnlockLockThread : public JoinableThread {
537 public:
538  LockUnlockLockThread(v8::Isolate* isolate, v8::Handle<v8::Context> context)
539    : JoinableThread("LockUnlockLockThread"),
540      isolate_(isolate),
541      context_(isolate, context) {
542  }
543
544  virtual void Run() {
545    v8::Locker lock1(isolate_);
546    CHECK(v8::Locker::IsLocked(isolate_));
547    CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
548    {
549      v8::Isolate::Scope isolate_scope(isolate_);
550      v8::HandleScope handle_scope(isolate_);
551      v8::Local<v8::Context> context =
552          v8::Local<v8::Context>::New(isolate_, context_);
553      v8::Context::Scope context_scope(context);
554      CalcFibAndCheck();
555    }
556    {
557      v8::Unlocker unlock1(isolate_);
558      CHECK(!v8::Locker::IsLocked(isolate_));
559      CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
560      {
561        v8::Locker lock2(isolate_);
562        v8::Isolate::Scope isolate_scope(isolate_);
563        v8::HandleScope handle_scope(isolate_);
564        CHECK(v8::Locker::IsLocked(isolate_));
565        CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
566        v8::Local<v8::Context> context =
567            v8::Local<v8::Context>::New(isolate_, context_);
568        v8::Context::Scope context_scope(context);
569        CalcFibAndCheck();
570      }
571    }
572  }
573
574 private:
575  v8::Isolate* isolate_;
576  v8::Persistent<v8::Context> context_;
577};
578
579
580// Locker inside an Unlocker inside a Locker.
581TEST(LockUnlockLockMultithreaded) {
582#if V8_TARGET_ARCH_MIPS
583  const int kNThreads = 50;
584#else
585  const int kNThreads = 100;
586#endif
587  v8::Isolate* isolate = v8::Isolate::New();
588  i::List<JoinableThread*> threads(kNThreads);
589  {
590    v8::Locker locker_(isolate);
591    v8::Isolate::Scope isolate_scope(isolate);
592    v8::HandleScope handle_scope(isolate);
593    v8::Handle<v8::Context> context = v8::Context::New(isolate);
594    for (int i = 0; i < kNThreads; i++) {
595      threads.Add(new LockUnlockLockThread(
596          isolate, context));
597    }
598  }
599  StartJoinAndDeleteThreads(threads);
600  isolate->Dispose();
601}
602
603class LockUnlockLockDefaultIsolateThread : public JoinableThread {
604 public:
605  explicit LockUnlockLockDefaultIsolateThread(v8::Handle<v8::Context> context)
606      : JoinableThread("LockUnlockLockDefaultIsolateThread"),
607        context_(CcTest::isolate(), context) {}
608
609  virtual void Run() {
610    v8::Locker lock1(CcTest::isolate());
611    {
612      v8::Isolate::Scope isolate_scope(CcTest::isolate());
613      v8::HandleScope handle_scope(CcTest::isolate());
614      v8::Local<v8::Context> context =
615          v8::Local<v8::Context>::New(CcTest::isolate(), context_);
616      v8::Context::Scope context_scope(context);
617      CalcFibAndCheck();
618    }
619    {
620      v8::Unlocker unlock1(CcTest::isolate());
621      {
622        v8::Locker lock2(CcTest::isolate());
623        v8::Isolate::Scope isolate_scope(CcTest::isolate());
624        v8::HandleScope handle_scope(CcTest::isolate());
625        v8::Local<v8::Context> context =
626            v8::Local<v8::Context>::New(CcTest::isolate(), context_);
627        v8::Context::Scope context_scope(context);
628        CalcFibAndCheck();
629      }
630    }
631  }
632
633 private:
634  v8::Persistent<v8::Context> context_;
635};
636
637
638// Locker inside an Unlocker inside a Locker for default isolate.
639TEST(LockUnlockLockDefaultIsolateMultithreaded) {
640#if V8_TARGET_ARCH_MIPS
641  const int kNThreads = 50;
642#else
643  const int kNThreads = 100;
644#endif
645  Local<v8::Context> context;
646  i::List<JoinableThread*> threads(kNThreads);
647  {
648    v8::Locker locker_(CcTest::isolate());
649    v8::Isolate::Scope isolate_scope(CcTest::isolate());
650    v8::HandleScope handle_scope(CcTest::isolate());
651    context = v8::Context::New(CcTest::isolate());
652    for (int i = 0; i < kNThreads; i++) {
653      threads.Add(new LockUnlockLockDefaultIsolateThread(context));
654    }
655  }
656  StartJoinAndDeleteThreads(threads);
657}
658
659
660TEST(Regress1433) {
661  for (int i = 0; i < 10; i++) {
662    v8::Isolate* isolate = v8::Isolate::New();
663    {
664      v8::Locker lock(isolate);
665      v8::Isolate::Scope isolate_scope(isolate);
666      v8::HandleScope handle_scope(isolate);
667      v8::Handle<Context> context = v8::Context::New(isolate);
668      v8::Context::Scope context_scope(context);
669      v8::Handle<String> source = v8::String::NewFromUtf8(isolate, "1+1");
670      v8::Handle<Script> script = v8::Script::Compile(source);
671      v8::Handle<Value> result = script->Run();
672      v8::String::Utf8Value utf8(result);
673    }
674    isolate->Dispose();
675  }
676}
677
678
679static const char* kSimpleExtensionSource =
680  "(function Foo() {"
681  "  return 4;"
682  "})() ";
683
684class IsolateGenesisThread : public JoinableThread {
685 public:
686  IsolateGenesisThread(int count, const char* extension_names[])
687    : JoinableThread("IsolateGenesisThread"),
688      count_(count),
689      extension_names_(extension_names)
690  {}
691
692  virtual void Run() {
693    v8::Isolate* isolate = v8::Isolate::New();
694    {
695      v8::Isolate::Scope isolate_scope(isolate);
696      CHECK(!i::Isolate::Current()->has_installed_extensions());
697      v8::ExtensionConfiguration extensions(count_, extension_names_);
698      v8::HandleScope handle_scope(isolate);
699      v8::Context::New(isolate, &extensions);
700      CHECK(i::Isolate::Current()->has_installed_extensions());
701    }
702    isolate->Dispose();
703  }
704 private:
705  int count_;
706  const char** extension_names_;
707};
708
709
710// Test installing extensions in separate isolates concurrently.
711// http://code.google.com/p/v8/issues/detail?id=1821
712TEST(ExtensionsRegistration) {
713#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
714  const int kNThreads = 10;
715#elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT
716  const int kNThreads = 4;
717#else
718  const int kNThreads = 40;
719#endif
720  v8::RegisterExtension(new v8::Extension("test0",
721                                          kSimpleExtensionSource));
722  v8::RegisterExtension(new v8::Extension("test1",
723                                          kSimpleExtensionSource));
724  v8::RegisterExtension(new v8::Extension("test2",
725                                          kSimpleExtensionSource));
726  v8::RegisterExtension(new v8::Extension("test3",
727                                          kSimpleExtensionSource));
728  v8::RegisterExtension(new v8::Extension("test4",
729                                          kSimpleExtensionSource));
730  v8::RegisterExtension(new v8::Extension("test5",
731                                          kSimpleExtensionSource));
732  v8::RegisterExtension(new v8::Extension("test6",
733                                          kSimpleExtensionSource));
734  v8::RegisterExtension(new v8::Extension("test7",
735                                          kSimpleExtensionSource));
736  const char* extension_names[] = { "test0", "test1",
737                                    "test2", "test3", "test4",
738                                    "test5", "test6", "test7" };
739  i::List<JoinableThread*> threads(kNThreads);
740  for (int i = 0; i < kNThreads; i++) {
741    threads.Add(new IsolateGenesisThread(8, extension_names));
742  }
743  StartJoinAndDeleteThreads(threads);
744}
745