OLD | NEW |
| (Empty) |
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 "v8.h" | |
31 | |
32 #include "api.h" | |
33 #include "isolate.h" | |
34 #include "compilation-cache.h" | |
35 #include "execution.h" | |
36 #include "snapshot.h" | |
37 #include "platform.h" | |
38 #include "utils.h" | |
39 #include "cctest.h" | |
40 #include "parser.h" | |
41 #include "unicode-inl.h" | |
42 | |
43 using ::v8::AccessorInfo; | |
44 using ::v8::Context; | |
45 using ::v8::Extension; | |
46 using ::v8::Function; | |
47 using ::v8::HandleScope; | |
48 using ::v8::Local; | |
49 using ::v8::Object; | |
50 using ::v8::ObjectTemplate; | |
51 using ::v8::Persistent; | |
52 using ::v8::Script; | |
53 using ::v8::String; | |
54 using ::v8::Value; | |
55 using ::v8::V8; | |
56 | |
57 namespace i = ::i; | |
58 | |
59 | |
60 | |
61 | |
62 // Migrating an isolate | |
63 class KangarooThread : public v8::internal::Thread { | |
64 public: | |
65 KangarooThread(v8::Isolate* isolate, | |
66 v8::Handle<v8::Context> context, int value) | |
67 : Thread(NULL, "KangarooThread"), | |
68 isolate_(isolate), context_(context), value_(value) { | |
69 } | |
70 | |
71 void Run() { | |
72 { | |
73 v8::Locker locker(isolate_); | |
74 v8::Isolate::Scope isolate_scope(isolate_); | |
75 CHECK_EQ(isolate_, v8::internal::Isolate::Current()); | |
76 v8::HandleScope scope; | |
77 v8::Context::Scope context_scope(context_); | |
78 Local<Value> v = CompileRun("getValue()"); | |
79 CHECK(v->IsNumber()); | |
80 CHECK_EQ(30, static_cast<int>(v->NumberValue())); | |
81 } | |
82 { | |
83 v8::Locker locker(isolate_); | |
84 v8::Isolate::Scope isolate_scope(isolate_); | |
85 v8::Context::Scope context_scope(context_); | |
86 v8::HandleScope scope; | |
87 Local<Value> v = CompileRun("getValue()"); | |
88 CHECK(v->IsNumber()); | |
89 CHECK_EQ(30, static_cast<int>(v->NumberValue())); | |
90 } | |
91 isolate_->Dispose(); | |
92 } | |
93 | |
94 private: | |
95 v8::Isolate* isolate_; | |
96 Persistent<v8::Context> context_; | |
97 int value_; | |
98 }; | |
99 | |
100 // Migrates an isolate from one thread to another | |
101 TEST(KangarooIsolates) { | |
102 v8::Isolate* isolate = v8::Isolate::New(); | |
103 Persistent<v8::Context> context; | |
104 { | |
105 v8::Locker locker(isolate); | |
106 v8::Isolate::Scope isolate_scope(isolate); | |
107 v8::HandleScope handle_scope; | |
108 context = v8::Context::New(); | |
109 v8::Context::Scope context_scope(context); | |
110 CHECK_EQ(isolate, v8::internal::Isolate::Current()); | |
111 CompileRun("function getValue() { return 30; }"); | |
112 } | |
113 KangarooThread thread1(isolate, context, 1); | |
114 thread1.Start(); | |
115 thread1.Join(); | |
116 } | |
117 | |
118 static 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 | |
128 class JoinableThread { | |
129 public: | |
130 explicit JoinableThread(const char* name) | |
131 : name_(name), | |
132 semaphore_(i::OS::CreateSemaphore(0)), | |
133 thread_(this) { | |
134 } | |
135 | |
136 virtual ~JoinableThread() { | |
137 delete semaphore_; | |
138 } | |
139 | |
140 void Start() { | |
141 thread_.Start(); | |
142 } | |
143 | |
144 void Join() { | |
145 semaphore_->Wait(); | |
146 } | |
147 | |
148 virtual void Run() = 0; | |
149 private: | |
150 class ThreadWithSemaphore : public i::Thread { | |
151 public: | |
152 explicit ThreadWithSemaphore(JoinableThread* joinable_thread) | |
153 : Thread(NULL, joinable_thread->name_), | |
154 joinable_thread_(joinable_thread) { | |
155 } | |
156 | |
157 virtual void Run() { | |
158 joinable_thread_->Run(); | |
159 joinable_thread_->semaphore_->Signal(); | |
160 } | |
161 | |
162 private: | |
163 JoinableThread* joinable_thread_; | |
164 }; | |
165 | |
166 const char* name_; | |
167 i::Semaphore* semaphore_; | |
168 ThreadWithSemaphore thread_; | |
169 | |
170 friend class ThreadWithSemaphore; | |
171 | |
172 DISALLOW_COPY_AND_ASSIGN(JoinableThread); | |
173 }; | |
174 | |
175 | |
176 class IsolateLockingThreadWithLocalContext : public JoinableThread { | |
177 public: | |
178 explicit IsolateLockingThreadWithLocalContext(v8::Isolate* isolate) | |
179 : JoinableThread("IsolateLockingThread"), | |
180 isolate_(isolate) { | |
181 } | |
182 | |
183 virtual void Run() { | |
184 v8::Locker locker(isolate_); | |
185 v8::Isolate::Scope isolate_scope(isolate_); | |
186 v8::HandleScope handle_scope; | |
187 LocalContext local_context; | |
188 CHECK_EQ(isolate_, v8::internal::Isolate::Current()); | |
189 CalcFibAndCheck(); | |
190 } | |
191 private: | |
192 v8::Isolate* isolate_; | |
193 }; | |
194 | |
195 static void StartJoinAndDeleteThreads(const i::List<JoinableThread*>& threads) { | |
196 for (int i = 0; i < threads.length(); i++) { | |
197 threads[i]->Start(); | |
198 } | |
199 for (int i = 0; i < threads.length(); i++) { | |
200 threads[i]->Join(); | |
201 } | |
202 for (int i = 0; i < threads.length(); i++) { | |
203 delete threads[i]; | |
204 } | |
205 } | |
206 | |
207 | |
208 // Run many threads all locking on the same isolate | |
209 TEST(IsolateLockingStress) { | |
210 const int kNThreads = 100; | |
211 i::List<JoinableThread*> threads(kNThreads); | |
212 v8::Isolate* isolate = v8::Isolate::New(); | |
213 for (int i = 0; i < kNThreads; i++) { | |
214 threads.Add(new IsolateLockingThreadWithLocalContext(isolate)); | |
215 } | |
216 StartJoinAndDeleteThreads(threads); | |
217 isolate->Dispose(); | |
218 } | |
219 | |
220 class IsolateNonlockingThread : public JoinableThread { | |
221 public: | |
222 explicit IsolateNonlockingThread() | |
223 : JoinableThread("IsolateNonlockingThread") { | |
224 } | |
225 | |
226 virtual void Run() { | |
227 v8::Isolate* isolate = v8::Isolate::New(); | |
228 { | |
229 v8::Isolate::Scope isolate_scope(isolate); | |
230 v8::HandleScope handle_scope; | |
231 v8::Handle<v8::Context> context = v8::Context::New(); | |
232 v8::Context::Scope context_scope(context); | |
233 CHECK_EQ(isolate, v8::internal::Isolate::Current()); | |
234 CalcFibAndCheck(); | |
235 } | |
236 isolate->Dispose(); | |
237 } | |
238 private: | |
239 }; | |
240 | |
241 // Run many threads each accessing its own isolate without locking | |
242 TEST(MultithreadedParallelIsolates) { | |
243 const int kNThreads = 50; | |
244 i::List<JoinableThread*> threads(kNThreads); | |
245 for (int i = 0; i < kNThreads; i++) { | |
246 threads.Add(new IsolateNonlockingThread()); | |
247 } | |
248 StartJoinAndDeleteThreads(threads); | |
249 } | |
250 | |
251 | |
252 class IsolateNestedLockingThread : public JoinableThread { | |
253 public: | |
254 explicit IsolateNestedLockingThread(v8::Isolate* isolate) | |
255 : JoinableThread("IsolateNestedLocking"), isolate_(isolate) { | |
256 } | |
257 virtual void Run() { | |
258 v8::Locker lock(isolate_); | |
259 v8::Isolate::Scope isolate_scope(isolate_); | |
260 v8::HandleScope handle_scope; | |
261 LocalContext local_context; | |
262 { | |
263 v8::Locker another_lock(isolate_); | |
264 CalcFibAndCheck(); | |
265 } | |
266 { | |
267 v8::Locker another_lock(isolate_); | |
268 CalcFibAndCheck(); | |
269 } | |
270 } | |
271 private: | |
272 v8::Isolate* isolate_; | |
273 }; | |
274 | |
275 // Run many threads with nested locks | |
276 TEST(IsolateNestedLocking) { | |
277 const int kNThreads = 100; | |
278 v8::Isolate* isolate = v8::Isolate::New(); | |
279 i::List<JoinableThread*> threads(kNThreads); | |
280 for (int i = 0; i < kNThreads; i++) { | |
281 threads.Add(new IsolateNestedLockingThread(isolate)); | |
282 } | |
283 StartJoinAndDeleteThreads(threads); | |
284 } | |
285 | |
286 | |
287 class SeparateIsolatesLocksNonexclusiveThread : public JoinableThread { | |
288 public: | |
289 SeparateIsolatesLocksNonexclusiveThread(v8::Isolate* isolate1, | |
290 v8::Isolate* isolate2) | |
291 : JoinableThread("SeparateIsolatesLocksNonexclusiveThread"), | |
292 isolate1_(isolate1), isolate2_(isolate2) { | |
293 } | |
294 | |
295 virtual void Run() { | |
296 v8::Locker lock(isolate1_); | |
297 v8::Isolate::Scope isolate_scope(isolate1_); | |
298 v8::HandleScope handle_scope; | |
299 LocalContext local_context; | |
300 | |
301 IsolateLockingThreadWithLocalContext threadB(isolate2_); | |
302 threadB.Start(); | |
303 CalcFibAndCheck(); | |
304 threadB.Join(); | |
305 } | |
306 private: | |
307 v8::Isolate* isolate1_; | |
308 v8::Isolate* isolate2_; | |
309 }; | |
310 | |
311 // Run parallel threads that lock and access different isolates in parallel | |
312 TEST(SeparateIsolatesLocksNonexclusive) { | |
313 const int kNThreads = 100; | |
314 v8::Isolate* isolate1 = v8::Isolate::New(); | |
315 v8::Isolate* isolate2 = v8::Isolate::New(); | |
316 i::List<JoinableThread*> threads(kNThreads); | |
317 for (int i = 0; i < kNThreads; i++) { | |
318 threads.Add(new SeparateIsolatesLocksNonexclusiveThread(isolate1, | |
319 isolate2)); | |
320 } | |
321 StartJoinAndDeleteThreads(threads); | |
322 isolate2->Dispose(); | |
323 isolate1->Dispose(); | |
324 } | |
325 | |
326 class LockIsolateAndCalculateFibSharedContextThread : public JoinableThread { | |
327 public: | |
328 explicit LockIsolateAndCalculateFibSharedContextThread( | |
329 v8::Isolate* isolate, v8::Handle<v8::Context> context) | |
330 : JoinableThread("LockIsolateAndCalculateFibThread"), | |
331 isolate_(isolate), | |
332 context_(context) { | |
333 } | |
334 | |
335 virtual void Run() { | |
336 v8::Locker lock(isolate_); | |
337 v8::Isolate::Scope isolate_scope(isolate_); | |
338 HandleScope handle_scope; | |
339 v8::Context::Scope context_scope(context_); | |
340 CalcFibAndCheck(); | |
341 } | |
342 private: | |
343 v8::Isolate* isolate_; | |
344 Persistent<v8::Context> context_; | |
345 }; | |
346 | |
347 class LockerUnlockerThread : public JoinableThread { | |
348 public: | |
349 explicit LockerUnlockerThread(v8::Isolate* isolate) | |
350 : JoinableThread("LockerUnlockerThread"), | |
351 isolate_(isolate) { | |
352 } | |
353 | |
354 virtual void Run() { | |
355 v8::Locker lock(isolate_); | |
356 v8::Isolate::Scope isolate_scope(isolate_); | |
357 v8::HandleScope handle_scope; | |
358 v8::Handle<v8::Context> context = v8::Context::New(); | |
359 { | |
360 v8::Context::Scope context_scope(context); | |
361 CalcFibAndCheck(); | |
362 } | |
363 { | |
364 isolate_->Exit(); | |
365 v8::Unlocker unlocker(isolate_); | |
366 LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context); | |
367 thread.Start(); | |
368 thread.Join(); | |
369 } | |
370 isolate_->Enter(); | |
371 { | |
372 v8::Context::Scope context_scope(context); | |
373 CalcFibAndCheck(); | |
374 } | |
375 } | |
376 private: | |
377 v8::Isolate* isolate_; | |
378 }; | |
379 | |
380 // Use unlocker inside of a Locker, multiple threads. | |
381 TEST(LockerUnlocker) { | |
382 const int kNThreads = 100; | |
383 i::List<JoinableThread*> threads(kNThreads); | |
384 v8::Isolate* isolate = v8::Isolate::New(); | |
385 for (int i = 0; i < kNThreads; i++) { | |
386 threads.Add(new LockerUnlockerThread(isolate)); | |
387 } | |
388 StartJoinAndDeleteThreads(threads); | |
389 isolate->Dispose(); | |
390 } | |
391 | |
392 class LockTwiceAndUnlockThread : public JoinableThread { | |
393 public: | |
394 explicit LockTwiceAndUnlockThread(v8::Isolate* isolate) | |
395 : JoinableThread("LockTwiceAndUnlockThread"), | |
396 isolate_(isolate) { | |
397 } | |
398 | |
399 virtual void Run() { | |
400 v8::Locker lock(isolate_); | |
401 v8::Isolate::Scope isolate_scope(isolate_); | |
402 v8::HandleScope handle_scope; | |
403 v8::Handle<v8::Context> context = v8::Context::New(); | |
404 { | |
405 v8::Context::Scope context_scope(context); | |
406 CalcFibAndCheck(); | |
407 } | |
408 { | |
409 v8::Locker second_lock(isolate_); | |
410 { | |
411 isolate_->Exit(); | |
412 v8::Unlocker unlocker(isolate_); | |
413 LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context); | |
414 thread.Start(); | |
415 thread.Join(); | |
416 } | |
417 } | |
418 isolate_->Enter(); | |
419 { | |
420 v8::Context::Scope context_scope(context); | |
421 CalcFibAndCheck(); | |
422 } | |
423 } | |
424 private: | |
425 v8::Isolate* isolate_; | |
426 }; | |
427 | |
428 // Use Unlocker inside two Lockers. | |
429 TEST(LockTwiceAndUnlock) { | |
430 const int kNThreads = 100; | |
431 i::List<JoinableThread*> threads(kNThreads); | |
432 v8::Isolate* isolate = v8::Isolate::New(); | |
433 for (int i = 0; i < kNThreads; i++) { | |
434 threads.Add(new LockTwiceAndUnlockThread(isolate)); | |
435 } | |
436 StartJoinAndDeleteThreads(threads); | |
437 isolate->Dispose(); | |
438 } | |
439 | |
440 class LockAndUnlockDifferentIsolatesThread : public JoinableThread { | |
441 public: | |
442 LockAndUnlockDifferentIsolatesThread(v8::Isolate* isolate1, | |
443 v8::Isolate* isolate2) | |
444 : JoinableThread("LockAndUnlockDifferentIsolatesThread"), | |
445 isolate1_(isolate1), | |
446 isolate2_(isolate2) { | |
447 } | |
448 | |
449 virtual void Run() { | |
450 Persistent<v8::Context> context1; | |
451 Persistent<v8::Context> context2; | |
452 v8::Locker lock1(isolate1_); | |
453 CHECK(v8::Locker::IsLocked(isolate1_)); | |
454 CHECK(!v8::Locker::IsLocked(isolate2_)); | |
455 { | |
456 v8::Isolate::Scope isolate_scope(isolate1_); | |
457 v8::HandleScope handle_scope; | |
458 context1 = v8::Context::New(); | |
459 { | |
460 v8::Context::Scope context_scope(context1); | |
461 CalcFibAndCheck(); | |
462 } | |
463 } | |
464 v8::Locker lock2(isolate2_); | |
465 CHECK(v8::Locker::IsLocked(isolate1_)); | |
466 CHECK(v8::Locker::IsLocked(isolate2_)); | |
467 { | |
468 v8::Isolate::Scope isolate_scope(isolate2_); | |
469 v8::HandleScope handle_scope; | |
470 context2 = v8::Context::New(); | |
471 { | |
472 v8::Context::Scope context_scope(context2); | |
473 CalcFibAndCheck(); | |
474 } | |
475 } | |
476 { | |
477 v8::Unlocker unlock1(isolate1_); | |
478 CHECK(!v8::Locker::IsLocked(isolate1_)); | |
479 CHECK(v8::Locker::IsLocked(isolate2_)); | |
480 v8::Isolate::Scope isolate_scope(isolate2_); | |
481 v8::HandleScope handle_scope; | |
482 v8::Context::Scope context_scope(context2); | |
483 LockIsolateAndCalculateFibSharedContextThread thread(isolate1_, context1); | |
484 thread.Start(); | |
485 CalcFibAndCheck(); | |
486 thread.Join(); | |
487 } | |
488 } | |
489 private: | |
490 v8::Isolate* isolate1_; | |
491 v8::Isolate* isolate2_; | |
492 }; | |
493 | |
494 // Lock two isolates and unlock one of them. | |
495 TEST(LockAndUnlockDifferentIsolates) { | |
496 v8::Isolate* isolate1 = v8::Isolate::New(); | |
497 v8::Isolate* isolate2 = v8::Isolate::New(); | |
498 LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2); | |
499 thread.Start(); | |
500 thread.Join(); | |
501 isolate2->Dispose(); | |
502 isolate1->Dispose(); | |
503 } | |
504 | |
505 class LockUnlockLockThread : public JoinableThread { | |
506 public: | |
507 LockUnlockLockThread(v8::Isolate* isolate, v8::Handle<v8::Context> context) | |
508 : JoinableThread("LockUnlockLockThread"), | |
509 isolate_(isolate), | |
510 context_(context) { | |
511 } | |
512 | |
513 virtual void Run() { | |
514 v8::Locker lock1(isolate_); | |
515 CHECK(v8::Locker::IsLocked(isolate_)); | |
516 CHECK(!v8::Locker::IsLocked()); | |
517 { | |
518 v8::Isolate::Scope isolate_scope(isolate_); | |
519 v8::HandleScope handle_scope; | |
520 v8::Context::Scope context_scope(context_); | |
521 CalcFibAndCheck(); | |
522 } | |
523 { | |
524 v8::Unlocker unlock1(isolate_); | |
525 CHECK(!v8::Locker::IsLocked(isolate_)); | |
526 CHECK(!v8::Locker::IsLocked()); | |
527 { | |
528 v8::Locker lock2(isolate_); | |
529 v8::Isolate::Scope isolate_scope(isolate_); | |
530 v8::HandleScope handle_scope; | |
531 CHECK(v8::Locker::IsLocked(isolate_)); | |
532 CHECK(!v8::Locker::IsLocked()); | |
533 v8::Context::Scope context_scope(context_); | |
534 CalcFibAndCheck(); | |
535 } | |
536 } | |
537 } | |
538 | |
539 private: | |
540 v8::Isolate* isolate_; | |
541 v8::Persistent<v8::Context> context_; | |
542 }; | |
543 | |
544 // Locker inside an Unlocker inside a Locker. | |
545 TEST(LockUnlockLockMultithreaded) { | |
546 const int kNThreads = 100; | |
547 v8::Isolate* isolate = v8::Isolate::New(); | |
548 Persistent<v8::Context> context; | |
549 { | |
550 v8::Locker locker_(isolate); | |
551 v8::Isolate::Scope isolate_scope(isolate); | |
552 v8::HandleScope handle_scope; | |
553 context = v8::Context::New(); | |
554 } | |
555 i::List<JoinableThread*> threads(kNThreads); | |
556 for (int i = 0; i < kNThreads; i++) { | |
557 threads.Add(new LockUnlockLockThread(isolate, context)); | |
558 } | |
559 StartJoinAndDeleteThreads(threads); | |
560 } | |
561 | |
562 class LockUnlockLockDefaultIsolateThread : public JoinableThread { | |
563 public: | |
564 LockUnlockLockDefaultIsolateThread(v8::Handle<v8::Context> context) | |
565 : JoinableThread("LockUnlockLockDefaultIsolateThread"), | |
566 context_(context) { | |
567 } | |
568 | |
569 virtual void Run() { | |
570 v8::Locker lock1; | |
571 { | |
572 v8::HandleScope handle_scope; | |
573 v8::Context::Scope context_scope(context_); | |
574 CalcFibAndCheck(); | |
575 } | |
576 { | |
577 v8::Unlocker unlock1; | |
578 { | |
579 v8::Locker lock2; | |
580 v8::HandleScope handle_scope; | |
581 v8::Context::Scope context_scope(context_); | |
582 CalcFibAndCheck(); | |
583 } | |
584 } | |
585 } | |
586 | |
587 private: | |
588 v8::Persistent<v8::Context> context_; | |
589 }; | |
590 | |
591 // Locker inside an Unlocker inside a Locker for default isolate. | |
592 TEST(LockUnlockLockDefaultIsolateMultithreaded) { | |
593 const int kNThreads = 100; | |
594 Persistent<v8::Context> context; | |
595 { | |
596 v8::Locker locker_; | |
597 v8::HandleScope handle_scope; | |
598 context = v8::Context::New(); | |
599 } | |
600 i::List<JoinableThread*> threads(kNThreads); | |
601 for (int i = 0; i < kNThreads; i++) { | |
602 threads.Add(new LockUnlockLockDefaultIsolateThread(context)); | |
603 } | |
604 StartJoinAndDeleteThreads(threads); | |
605 } | |
OLD | NEW |