Line data Source code
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 <memory>
31 :
32 : #include "src/v8.h"
33 :
34 : #include "src/api.h"
35 : #include "src/base/platform/platform.h"
36 : #include "src/compilation-cache.h"
37 : #include "src/execution.h"
38 : #include "src/isolate.h"
39 : #include "src/objects-inl.h"
40 : #include "src/unicode-inl.h"
41 : #include "src/utils.h"
42 : #include "test/cctest/cctest.h"
43 :
44 : namespace {
45 :
46 15 : class DeoptimizeCodeThread : public v8::base::Thread {
47 : public:
48 15 : DeoptimizeCodeThread(v8::Isolate* isolate, v8::Local<v8::Context> context,
49 : const char* trigger)
50 : : Thread(Options("DeoptimizeCodeThread")),
51 : isolate_(isolate),
52 : context_(isolate, context),
53 30 : source_(trigger) {}
54 :
55 15 : void Run() {
56 15 : v8::Locker locker(isolate_);
57 15 : isolate_->Enter();
58 30 : v8::HandleScope handle_scope(isolate_);
59 : v8::Local<v8::Context> context =
60 15 : v8::Local<v8::Context>::New(isolate_, context_);
61 : v8::Context::Scope context_scope(context);
62 15 : CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
63 : // This code triggers deoptimization of some function that will be
64 : // used in a different thread.
65 15 : CompileRun(source_);
66 30 : isolate_->Exit();
67 15 : }
68 :
69 : private:
70 : v8::Isolate* isolate_;
71 : v8::Persistent<v8::Context> context_;
72 : // The code that triggers the deoptimization.
73 : const char* source_;
74 : };
75 :
76 5 : void UnlockForDeoptimization(const v8::FunctionCallbackInfo<v8::Value>& args) {
77 5 : v8::Isolate* isolate = v8::Isolate::GetCurrent();
78 : // Gets the pointer to the thread that will trigger the deoptimization of the
79 : // code.
80 : DeoptimizeCodeThread* deoptimizer =
81 : reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
82 : {
83 : // Exits and unlocks the isolate.
84 5 : isolate->Exit();
85 : v8::Unlocker unlocker(isolate);
86 : // Starts the deoptimizing thread.
87 5 : deoptimizer->Start();
88 : // Waits for deoptimization to finish.
89 5 : deoptimizer->Join();
90 : }
91 : // The deoptimizing thread has finished its work, and the isolate
92 : // will now be used by the current thread.
93 5 : isolate->Enter();
94 5 : }
95 :
96 40 : void UnlockForDeoptimizationIfReady(
97 : const v8::FunctionCallbackInfo<v8::Value>& args) {
98 40 : v8::Isolate* isolate = v8::Isolate::GetCurrent();
99 : bool* ready_to_deoptimize = reinterpret_cast<bool*>(isolate->GetData(1));
100 40 : if (*ready_to_deoptimize) {
101 : // The test should enter here only once, so put the flag back to false.
102 10 : *ready_to_deoptimize = false;
103 : // Gets the pointer to the thread that will trigger the deoptimization of
104 : // the code.
105 : DeoptimizeCodeThread* deoptimizer =
106 : reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
107 : {
108 : // Exits and unlocks the thread.
109 10 : isolate->Exit();
110 : v8::Unlocker unlocker(isolate);
111 : // Starts the thread that deoptimizes the function.
112 10 : deoptimizer->Start();
113 : // Waits for the deoptimizing thread to finish.
114 10 : deoptimizer->Join();
115 : }
116 : // The deoptimizing thread has finished its work, and the isolate
117 : // will now be used by the current thread.
118 10 : isolate->Enter();
119 : }
120 40 : }
121 : } // namespace
122 :
123 : namespace v8 {
124 : namespace internal {
125 : namespace test_lockers {
126 :
127 23723 : TEST(LazyDeoptimizationMultithread) {
128 5 : i::FLAG_allow_natives_syntax = true;
129 : v8::Isolate::CreateParams create_params;
130 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
131 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
132 : {
133 : v8::Locker locker(isolate);
134 : v8::Isolate::Scope isolate_scope(isolate);
135 10 : v8::HandleScope scope(isolate);
136 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
137 : const char* trigger_deopt = "obj = { y: 0, x: 1 };";
138 :
139 : // We use the isolate to pass arguments to the UnlockForDeoptimization
140 : // function. Namely, we pass a pointer to the deoptimizing thread.
141 5 : DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
142 : isolate->SetData(0, &deoptimize_thread);
143 : v8::Context::Scope context_scope(context);
144 :
145 : // Create the function templace for C++ code that is invoked from
146 : // JavaScript code.
147 : Local<v8::FunctionTemplate> fun_templ =
148 5 : v8::FunctionTemplate::New(isolate, UnlockForDeoptimization);
149 5 : Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
150 20 : CHECK(context->Global()
151 : ->Set(context, v8_str("unlock_for_deoptimization"), fun)
152 : .FromJust());
153 :
154 : // Optimizes a function f, which will be deoptimized in another
155 : // thread.
156 : CompileRun(
157 : "var b = false; var obj = { x: 1 };"
158 : "function f() { g(); return obj.x; }"
159 : "function g() { if (b) { unlock_for_deoptimization(); } }"
160 : "%NeverOptimizeFunction(g);"
161 : "f(); f(); %OptimizeFunctionOnNextCall(f);"
162 : "f();");
163 :
164 : // Trigger the unlocking.
165 : Local<Value> v = CompileRun("b = true; f();");
166 :
167 : // Once the isolate has been unlocked, the thread will wait for the
168 : // other thread to finish its task. Once this happens, this thread
169 : // continues with its execution, that is, with the execution of the
170 : // function g, which then returns to f. The function f should have
171 : // also been deoptimized. If the replacement did not happen on this
172 : // thread's stack, then the test will fail here.
173 5 : CHECK(v->IsNumber());
174 15 : CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
175 : }
176 5 : isolate->Dispose();
177 5 : }
178 :
179 23723 : TEST(LazyDeoptimizationMultithreadWithNatives) {
180 5 : i::FLAG_allow_natives_syntax = true;
181 : v8::Isolate::CreateParams create_params;
182 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
183 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
184 : {
185 : v8::Locker locker(isolate);
186 : v8::Isolate::Scope isolate_scope(isolate);
187 10 : v8::HandleScope scope(isolate);
188 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
189 : const char* trigger_deopt = "%DeoptimizeFunction(f);";
190 :
191 : // We use the isolate to pass arguments to the UnlockForDeoptimization
192 : // function. Namely, we pass a pointer to the deoptimizing thread.
193 5 : DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
194 : isolate->SetData(0, &deoptimize_thread);
195 5 : bool ready_to_deopt = false;
196 : isolate->SetData(1, &ready_to_deopt);
197 : v8::Context::Scope context_scope(context);
198 :
199 : // Create the function templace for C++ code that is invoked from
200 : // JavaScript code.
201 : Local<v8::FunctionTemplate> fun_templ =
202 5 : v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
203 5 : Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
204 20 : CHECK(context->Global()
205 : ->Set(context, v8_str("unlock_for_deoptimization"), fun)
206 : .FromJust());
207 :
208 : // Optimizes a function f, which will be deoptimized in another
209 : // thread.
210 : CompileRun(
211 : "var obj = { x: 1 };"
212 : "function f() { g(); return obj.x;}"
213 : "function g() { "
214 : " unlock_for_deoptimization(); }"
215 : "%NeverOptimizeFunction(g);"
216 : "f(); f(); %OptimizeFunctionOnNextCall(f);");
217 :
218 : // Trigger the unlocking.
219 5 : ready_to_deopt = true;
220 : isolate->SetData(1, &ready_to_deopt);
221 : Local<Value> v = CompileRun("f();");
222 :
223 : // Once the isolate has been unlocked, the thread will wait for the
224 : // other thread to finish its task. Once this happens, this thread
225 : // continues with its execution, that is, with the execution of the
226 : // function g, which then returns to f. The function f should have
227 : // also been deoptimized. Otherwise, the test will fail here.
228 5 : CHECK(v->IsNumber());
229 15 : CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
230 : }
231 5 : isolate->Dispose();
232 5 : }
233 :
234 23723 : TEST(EagerDeoptimizationMultithread) {
235 5 : i::FLAG_allow_natives_syntax = true;
236 : v8::Isolate::CreateParams create_params;
237 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
238 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
239 : {
240 : v8::Locker locker(isolate);
241 : v8::Isolate::Scope isolate_scope(isolate);
242 10 : v8::HandleScope scope(isolate);
243 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
244 : const char* trigger_deopt = "f({y: 0, x: 1});";
245 :
246 : // We use the isolate to pass arguments to the UnlockForDeoptimization
247 : // function. Namely, we pass a pointer to the deoptimizing thread.
248 5 : DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
249 : isolate->SetData(0, &deoptimize_thread);
250 5 : bool ready_to_deopt = false;
251 : isolate->SetData(1, &ready_to_deopt);
252 : v8::Context::Scope context_scope(context);
253 :
254 : // Create the function templace for C++ code that is invoked from
255 : // JavaScript code.
256 : Local<v8::FunctionTemplate> fun_templ =
257 5 : v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
258 5 : Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
259 20 : CHECK(context->Global()
260 : ->Set(context, v8_str("unlock_for_deoptimization"), fun)
261 : .FromJust());
262 :
263 : // Optimizes a function f, which will be deoptimized by another thread.
264 : CompileRun(
265 : "function f(obj) { unlock_for_deoptimization(); return obj.x; }"
266 : "f({x: 1}); f({x: 1});"
267 : "%OptimizeFunctionOnNextCall(f);"
268 : "f({x: 1});");
269 :
270 : // Trigger the unlocking.
271 5 : ready_to_deopt = true;
272 : isolate->SetData(1, &ready_to_deopt);
273 : Local<Value> v = CompileRun("f({x: 1});");
274 :
275 : // Once the isolate has been unlocked, the thread will wait for the
276 : // other thread to finish its task. Once this happens, this thread
277 : // continues with its execution, that is, with the execution of the
278 : // function g, which then returns to f. The function f should have
279 : // also been deoptimized. Otherwise, the test will fail here.
280 5 : CHECK(v->IsNumber());
281 15 : CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
282 : }
283 5 : isolate->Dispose();
284 5 : }
285 :
286 : // Migrating an isolate
287 5 : class KangarooThread : public v8::base::Thread {
288 : public:
289 5 : KangarooThread(v8::Isolate* isolate, v8::Local<v8::Context> context)
290 : : Thread(Options("KangarooThread")),
291 : isolate_(isolate),
292 10 : context_(isolate, context) {}
293 :
294 5 : void Run() {
295 : {
296 5 : v8::Locker locker(isolate_);
297 5 : v8::Isolate::Scope isolate_scope(isolate_);
298 5 : CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
299 10 : v8::HandleScope scope(isolate_);
300 : v8::Local<v8::Context> context =
301 5 : v8::Local<v8::Context>::New(isolate_, context_);
302 : v8::Context::Scope context_scope(context);
303 : Local<Value> v = CompileRun("getValue()");
304 5 : CHECK(v->IsNumber());
305 15 : CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust()));
306 : }
307 : {
308 5 : v8::Locker locker(isolate_);
309 5 : v8::Isolate::Scope isolate_scope(isolate_);
310 10 : v8::HandleScope scope(isolate_);
311 : v8::Local<v8::Context> context =
312 5 : v8::Local<v8::Context>::New(isolate_, context_);
313 : v8::Context::Scope context_scope(context);
314 : Local<Value> v = CompileRun("getValue()");
315 5 : CHECK(v->IsNumber());
316 15 : CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust()));
317 : }
318 5 : isolate_->Dispose();
319 5 : }
320 :
321 : private:
322 : v8::Isolate* isolate_;
323 : v8::Persistent<v8::Context> context_;
324 : };
325 :
326 :
327 : // Migrates an isolate from one thread to another
328 23723 : TEST(KangarooIsolates) {
329 : v8::Isolate::CreateParams create_params;
330 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
331 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
332 : std::unique_ptr<KangarooThread> thread1;
333 : {
334 : v8::Locker locker(isolate);
335 : v8::Isolate::Scope isolate_scope(isolate);
336 10 : v8::HandleScope handle_scope(isolate);
337 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
338 : v8::Context::Scope context_scope(context);
339 5 : CHECK_EQ(isolate, v8::Isolate::GetCurrent());
340 : CompileRun("function getValue() { return 30; }");
341 10 : thread1.reset(new KangarooThread(isolate, context));
342 : }
343 5 : thread1->Start();
344 5 : thread1->Join();
345 5 : }
346 :
347 :
348 7520 : static void CalcFibAndCheck(v8::Local<v8::Context> context) {
349 : Local<Value> v = CompileRun("function fib(n) {"
350 : " if (n <= 2) return 1;"
351 : " return fib(n-1) + fib(n-2);"
352 : "}"
353 : "fib(10)");
354 7520 : CHECK(v->IsNumber());
355 15040 : CHECK_EQ(55, static_cast<int>(v->NumberValue(context).FromJust()));
356 7520 : }
357 :
358 : class JoinableThread {
359 : public:
360 5210 : explicit JoinableThread(const char* name)
361 : : name_(name),
362 : semaphore_(0),
363 5210 : thread_(this) {
364 5210 : }
365 :
366 10420 : virtual ~JoinableThread() {}
367 :
368 : void Start() {
369 5210 : thread_.Start();
370 : }
371 :
372 : void Join() {
373 5210 : semaphore_.Wait();
374 5210 : thread_.Join();
375 : }
376 :
377 : virtual void Run() = 0;
378 :
379 : private:
380 5210 : class ThreadWithSemaphore : public v8::base::Thread {
381 : public:
382 : explicit ThreadWithSemaphore(JoinableThread* joinable_thread)
383 : : Thread(Options(joinable_thread->name_)),
384 10420 : joinable_thread_(joinable_thread) {}
385 :
386 5200 : virtual void Run() {
387 5200 : joinable_thread_->Run();
388 5210 : joinable_thread_->semaphore_.Signal();
389 5210 : }
390 :
391 : private:
392 : JoinableThread* joinable_thread_;
393 : };
394 :
395 : const char* name_;
396 : v8::base::Semaphore semaphore_;
397 : ThreadWithSemaphore thread_;
398 :
399 : friend class ThreadWithSemaphore;
400 :
401 : DISALLOW_COPY_AND_ASSIGN(JoinableThread);
402 : };
403 :
404 :
405 2500 : class IsolateLockingThreadWithLocalContext : public JoinableThread {
406 : public:
407 : explicit IsolateLockingThreadWithLocalContext(v8::Isolate* isolate)
408 : : JoinableThread("IsolateLockingThread"),
409 1000 : isolate_(isolate) {
410 : }
411 :
412 999 : virtual void Run() {
413 999 : v8::Locker locker(isolate_);
414 1000 : v8::Isolate::Scope isolate_scope(isolate_);
415 2000 : v8::HandleScope handle_scope(isolate_);
416 2000 : LocalContext local_context(isolate_);
417 1000 : CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
418 2000 : CalcFibAndCheck(local_context.local());
419 1000 : }
420 : private:
421 : v8::Isolate* isolate_;
422 : };
423 :
424 40 : static void StartJoinAndDeleteThreads(
425 : const std::vector<JoinableThread*>& threads) {
426 3780 : for (const auto& thread : threads) {
427 3700 : thread->Start();
428 : }
429 3780 : for (const auto& thread : threads) {
430 3700 : thread->Join();
431 : }
432 3780 : for (const auto& thread : threads) {
433 3700 : delete thread;
434 : }
435 40 : }
436 :
437 :
438 : // Run many threads all locking on the same isolate
439 23723 : TEST(IsolateLockingStress) {
440 5 : i::FLAG_always_opt = false;
441 : #if V8_TARGET_ARCH_MIPS
442 : const int kNThreads = 50;
443 : #else
444 : const int kNThreads = 100;
445 : #endif
446 : std::vector<JoinableThread*> threads;
447 5 : threads.reserve(kNThreads);
448 : v8::Isolate::CreateParams create_params;
449 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
450 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
451 505 : for (int i = 0; i < kNThreads; i++) {
452 1500 : threads.push_back(new IsolateLockingThreadWithLocalContext(isolate));
453 : }
454 5 : StartJoinAndDeleteThreads(threads);
455 5 : isolate->Dispose();
456 5 : }
457 :
458 :
459 1500 : class IsolateNestedLockingThread : public JoinableThread {
460 : public:
461 : explicit IsolateNestedLockingThread(v8::Isolate* isolate)
462 500 : : JoinableThread("IsolateNestedLocking"), isolate_(isolate) {
463 : }
464 489 : virtual void Run() {
465 489 : v8::Locker lock(isolate_);
466 500 : v8::Isolate::Scope isolate_scope(isolate_);
467 1000 : v8::HandleScope handle_scope(isolate_);
468 1000 : LocalContext local_context(isolate_);
469 : {
470 500 : v8::Locker another_lock(isolate_);
471 500 : CalcFibAndCheck(local_context.local());
472 : }
473 : {
474 500 : v8::Locker another_lock(isolate_);
475 500 : CalcFibAndCheck(local_context.local());
476 500 : }
477 500 : }
478 : private:
479 : v8::Isolate* isolate_;
480 : };
481 :
482 :
483 : // Run many threads with nested locks
484 23723 : TEST(IsolateNestedLocking) {
485 5 : i::FLAG_always_opt = false;
486 : #if V8_TARGET_ARCH_MIPS
487 : const int kNThreads = 50;
488 : #else
489 : const int kNThreads = 100;
490 : #endif
491 : v8::Isolate::CreateParams create_params;
492 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
493 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
494 : std::vector<JoinableThread*> threads;
495 5 : threads.reserve(kNThreads);
496 505 : for (int i = 0; i < kNThreads; i++) {
497 1500 : threads.push_back(new IsolateNestedLockingThread(isolate));
498 : }
499 5 : StartJoinAndDeleteThreads(threads);
500 5 : isolate->Dispose();
501 5 : }
502 :
503 :
504 1500 : class SeparateIsolatesLocksNonexclusiveThread : public JoinableThread {
505 : public:
506 : SeparateIsolatesLocksNonexclusiveThread(v8::Isolate* isolate1,
507 : v8::Isolate* isolate2)
508 : : JoinableThread("SeparateIsolatesLocksNonexclusiveThread"),
509 500 : isolate1_(isolate1), isolate2_(isolate2) {
510 : }
511 :
512 500 : virtual void Run() {
513 500 : v8::Locker lock(isolate1_);
514 500 : v8::Isolate::Scope isolate_scope(isolate1_);
515 1000 : v8::HandleScope handle_scope(isolate1_);
516 1000 : LocalContext local_context(isolate1_);
517 :
518 1000 : IsolateLockingThreadWithLocalContext threadB(isolate2_);
519 : threadB.Start();
520 500 : CalcFibAndCheck(local_context.local());
521 500 : threadB.Join();
522 500 : }
523 : private:
524 : v8::Isolate* isolate1_;
525 : v8::Isolate* isolate2_;
526 : };
527 :
528 :
529 : // Run parallel threads that lock and access different isolates in parallel
530 23723 : TEST(SeparateIsolatesLocksNonexclusive) {
531 5 : i::FLAG_always_opt = false;
532 : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
533 : const int kNThreads = 50;
534 : #else
535 : const int kNThreads = 100;
536 : #endif
537 : v8::Isolate::CreateParams create_params;
538 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
539 5 : v8::Isolate* isolate1 = v8::Isolate::New(create_params);
540 5 : v8::Isolate* isolate2 = v8::Isolate::New(create_params);
541 : std::vector<JoinableThread*> threads;
542 5 : threads.reserve(kNThreads);
543 505 : for (int i = 0; i < kNThreads; i++) {
544 : threads.push_back(
545 1500 : new SeparateIsolatesLocksNonexclusiveThread(isolate1, isolate2));
546 : }
547 5 : StartJoinAndDeleteThreads(threads);
548 5 : isolate2->Dispose();
549 5 : isolate1->Dispose();
550 5 : }
551 :
552 2015 : class LockIsolateAndCalculateFibSharedContextThread : public JoinableThread {
553 : public:
554 1005 : explicit LockIsolateAndCalculateFibSharedContextThread(
555 : v8::Isolate* isolate, v8::Local<v8::Context> context)
556 : : JoinableThread("LockIsolateAndCalculateFibThread"),
557 : isolate_(isolate),
558 2010 : context_(isolate, context) {}
559 :
560 1000 : virtual void Run() {
561 1000 : v8::Locker lock(isolate_);
562 1005 : v8::Isolate::Scope isolate_scope(isolate_);
563 2010 : v8::HandleScope handle_scope(isolate_);
564 : v8::Local<v8::Context> context =
565 1005 : v8::Local<v8::Context>::New(isolate_, context_);
566 : v8::Context::Scope context_scope(context);
567 2010 : CalcFibAndCheck(context);
568 1005 : }
569 : private:
570 : v8::Isolate* isolate_;
571 : v8::Persistent<v8::Context> context_;
572 : };
573 :
574 1500 : class LockerUnlockerThread : public JoinableThread {
575 : public:
576 : explicit LockerUnlockerThread(v8::Isolate* isolate)
577 : : JoinableThread("LockerUnlockerThread"),
578 500 : isolate_(isolate) {
579 : }
580 :
581 500 : virtual void Run() {
582 500 : isolate_->DiscardThreadSpecificMetadata(); // No-op
583 : {
584 499 : v8::Locker lock(isolate_);
585 500 : v8::Isolate::Scope isolate_scope(isolate_);
586 1000 : v8::HandleScope handle_scope(isolate_);
587 500 : v8::Local<v8::Context> context = v8::Context::New(isolate_);
588 : {
589 : v8::Context::Scope context_scope(context);
590 500 : CalcFibAndCheck(context);
591 : }
592 : {
593 500 : LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
594 500 : isolate_->Exit();
595 1000 : v8::Unlocker unlocker(isolate_);
596 : thread.Start();
597 500 : thread.Join();
598 : }
599 500 : isolate_->Enter();
600 : {
601 : v8::Context::Scope context_scope(context);
602 500 : CalcFibAndCheck(context);
603 500 : }
604 : }
605 500 : isolate_->DiscardThreadSpecificMetadata();
606 500 : isolate_->DiscardThreadSpecificMetadata(); // No-op
607 500 : }
608 :
609 : private:
610 : v8::Isolate* isolate_;
611 : };
612 :
613 :
614 : // Use unlocker inside of a Locker, multiple threads.
615 23723 : TEST(LockerUnlocker) {
616 5 : i::FLAG_always_opt = false;
617 : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
618 : const int kNThreads = 50;
619 : #else
620 : const int kNThreads = 100;
621 : #endif
622 : std::vector<JoinableThread*> threads;
623 5 : threads.reserve(kNThreads);
624 : v8::Isolate::CreateParams create_params;
625 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
626 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
627 505 : for (int i = 0; i < kNThreads; i++) {
628 1500 : threads.push_back(new LockerUnlockerThread(isolate));
629 : }
630 5 : StartJoinAndDeleteThreads(threads);
631 5 : isolate->Dispose();
632 5 : }
633 :
634 1500 : class LockTwiceAndUnlockThread : public JoinableThread {
635 : public:
636 : explicit LockTwiceAndUnlockThread(v8::Isolate* isolate)
637 : : JoinableThread("LockTwiceAndUnlockThread"),
638 500 : isolate_(isolate) {
639 : }
640 :
641 500 : virtual void Run() {
642 500 : v8::Locker lock(isolate_);
643 500 : v8::Isolate::Scope isolate_scope(isolate_);
644 1000 : v8::HandleScope handle_scope(isolate_);
645 500 : v8::Local<v8::Context> context = v8::Context::New(isolate_);
646 : {
647 : v8::Context::Scope context_scope(context);
648 500 : CalcFibAndCheck(context);
649 : }
650 : {
651 500 : v8::Locker second_lock(isolate_);
652 : {
653 500 : LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
654 500 : isolate_->Exit();
655 1000 : v8::Unlocker unlocker(isolate_);
656 : thread.Start();
657 500 : thread.Join();
658 500 : }
659 : }
660 500 : isolate_->Enter();
661 : {
662 : v8::Context::Scope context_scope(context);
663 500 : CalcFibAndCheck(context);
664 500 : }
665 500 : }
666 :
667 : private:
668 : v8::Isolate* isolate_;
669 : };
670 :
671 :
672 : // Use Unlocker inside two Lockers.
673 23723 : TEST(LockTwiceAndUnlock) {
674 5 : i::FLAG_always_opt = false;
675 : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
676 : const int kNThreads = 50;
677 : #else
678 : const int kNThreads = 100;
679 : #endif
680 : std::vector<JoinableThread*> threads;
681 5 : threads.reserve(kNThreads);
682 : v8::Isolate::CreateParams create_params;
683 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
684 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
685 505 : for (int i = 0; i < kNThreads; i++) {
686 1500 : threads.push_back(new LockTwiceAndUnlockThread(isolate));
687 : }
688 5 : StartJoinAndDeleteThreads(threads);
689 5 : isolate->Dispose();
690 5 : }
691 :
692 10 : class LockAndUnlockDifferentIsolatesThread : public JoinableThread {
693 : public:
694 : LockAndUnlockDifferentIsolatesThread(v8::Isolate* isolate1,
695 : v8::Isolate* isolate2)
696 : : JoinableThread("LockAndUnlockDifferentIsolatesThread"),
697 : isolate1_(isolate1),
698 5 : isolate2_(isolate2) {
699 : }
700 :
701 5 : virtual void Run() {
702 : std::unique_ptr<LockIsolateAndCalculateFibSharedContextThread> thread;
703 10 : v8::Locker lock1(isolate1_);
704 5 : CHECK(v8::Locker::IsLocked(isolate1_));
705 5 : CHECK(!v8::Locker::IsLocked(isolate2_));
706 : {
707 5 : v8::Isolate::Scope isolate_scope(isolate1_);
708 10 : v8::HandleScope handle_scope(isolate1_);
709 5 : v8::Local<v8::Context> context1 = v8::Context::New(isolate1_);
710 : {
711 : v8::Context::Scope context_scope(context1);
712 5 : CalcFibAndCheck(context1);
713 : }
714 : thread.reset(new LockIsolateAndCalculateFibSharedContextThread(isolate1_,
715 5 : context1));
716 : }
717 10 : v8::Locker lock2(isolate2_);
718 5 : CHECK(v8::Locker::IsLocked(isolate1_));
719 5 : CHECK(v8::Locker::IsLocked(isolate2_));
720 : {
721 5 : v8::Isolate::Scope isolate_scope(isolate2_);
722 10 : v8::HandleScope handle_scope(isolate2_);
723 5 : v8::Local<v8::Context> context2 = v8::Context::New(isolate2_);
724 : {
725 : v8::Context::Scope context_scope(context2);
726 5 : CalcFibAndCheck(context2);
727 : }
728 10 : v8::Unlocker unlock1(isolate1_);
729 5 : CHECK(!v8::Locker::IsLocked(isolate1_));
730 5 : CHECK(v8::Locker::IsLocked(isolate2_));
731 : v8::Context::Scope context_scope(context2);
732 : thread->Start();
733 5 : CalcFibAndCheck(context2);
734 : thread->Join();
735 : }
736 5 : }
737 :
738 : private:
739 : v8::Isolate* isolate1_;
740 : v8::Isolate* isolate2_;
741 : };
742 :
743 :
744 : // Lock two isolates and unlock one of them.
745 23723 : TEST(LockAndUnlockDifferentIsolates) {
746 : v8::Isolate::CreateParams create_params;
747 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
748 5 : v8::Isolate* isolate1 = v8::Isolate::New(create_params);
749 5 : v8::Isolate* isolate2 = v8::Isolate::New(create_params);
750 : LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2);
751 : thread.Start();
752 : thread.Join();
753 5 : isolate2->Dispose();
754 5 : isolate1->Dispose();
755 5 : }
756 :
757 1500 : class LockUnlockLockThread : public JoinableThread {
758 : public:
759 500 : LockUnlockLockThread(v8::Isolate* isolate, v8::Local<v8::Context> context)
760 : : JoinableThread("LockUnlockLockThread"),
761 : isolate_(isolate),
762 1000 : context_(isolate, context) {}
763 :
764 500 : virtual void Run() {
765 500 : v8::Locker lock1(isolate_);
766 500 : CHECK(v8::Locker::IsLocked(isolate_));
767 500 : CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
768 : {
769 500 : v8::Isolate::Scope isolate_scope(isolate_);
770 1000 : v8::HandleScope handle_scope(isolate_);
771 : v8::Local<v8::Context> context =
772 500 : v8::Local<v8::Context>::New(isolate_, context_);
773 : v8::Context::Scope context_scope(context);
774 500 : CalcFibAndCheck(context);
775 : }
776 : {
777 500 : v8::Unlocker unlock1(isolate_);
778 500 : CHECK(!v8::Locker::IsLocked(isolate_));
779 500 : CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
780 : {
781 500 : v8::Locker lock2(isolate_);
782 500 : v8::Isolate::Scope isolate_scope(isolate_);
783 1000 : v8::HandleScope handle_scope(isolate_);
784 500 : CHECK(v8::Locker::IsLocked(isolate_));
785 500 : CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
786 : v8::Local<v8::Context> context =
787 500 : v8::Local<v8::Context>::New(isolate_, context_);
788 : v8::Context::Scope context_scope(context);
789 1000 : CalcFibAndCheck(context);
790 500 : }
791 500 : }
792 500 : }
793 :
794 : private:
795 : v8::Isolate* isolate_;
796 : v8::Persistent<v8::Context> context_;
797 : };
798 :
799 :
800 : // Locker inside an Unlocker inside a Locker.
801 23723 : TEST(LockUnlockLockMultithreaded) {
802 : #if V8_TARGET_ARCH_MIPS
803 : const int kNThreads = 50;
804 : #else
805 : const int kNThreads = 100;
806 : #endif
807 : v8::Isolate::CreateParams create_params;
808 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
809 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
810 : std::vector<JoinableThread*> threads;
811 5 : threads.reserve(kNThreads);
812 : {
813 : v8::Locker locker_(isolate);
814 : v8::Isolate::Scope isolate_scope(isolate);
815 10 : v8::HandleScope handle_scope(isolate);
816 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
817 505 : for (int i = 0; i < kNThreads; i++) {
818 1000 : threads.push_back(new LockUnlockLockThread(isolate, context));
819 5 : }
820 : }
821 5 : StartJoinAndDeleteThreads(threads);
822 5 : isolate->Dispose();
823 5 : }
824 :
825 1500 : class LockUnlockLockDefaultIsolateThread : public JoinableThread {
826 : public:
827 500 : explicit LockUnlockLockDefaultIsolateThread(v8::Local<v8::Context> context)
828 : : JoinableThread("LockUnlockLockDefaultIsolateThread"),
829 1000 : context_(CcTest::isolate(), context) {}
830 :
831 499 : virtual void Run() {
832 499 : v8::Locker lock1(CcTest::isolate());
833 : {
834 500 : v8::Isolate::Scope isolate_scope(CcTest::isolate());
835 1000 : v8::HandleScope handle_scope(CcTest::isolate());
836 : v8::Local<v8::Context> context =
837 500 : v8::Local<v8::Context>::New(CcTest::isolate(), context_);
838 : v8::Context::Scope context_scope(context);
839 500 : CalcFibAndCheck(context);
840 : }
841 : {
842 500 : v8::Unlocker unlock1(CcTest::isolate());
843 : {
844 500 : v8::Locker lock2(CcTest::isolate());
845 500 : v8::Isolate::Scope isolate_scope(CcTest::isolate());
846 1000 : v8::HandleScope handle_scope(CcTest::isolate());
847 : v8::Local<v8::Context> context =
848 500 : v8::Local<v8::Context>::New(CcTest::isolate(), context_);
849 : v8::Context::Scope context_scope(context);
850 1000 : CalcFibAndCheck(context);
851 500 : }
852 500 : }
853 500 : }
854 :
855 : private:
856 : v8::Persistent<v8::Context> context_;
857 : };
858 :
859 :
860 : // Locker inside an Unlocker inside a Locker for default isolate.
861 23723 : TEST(LockUnlockLockDefaultIsolateMultithreaded) {
862 : #if V8_TARGET_ARCH_MIPS
863 : const int kNThreads = 50;
864 : #else
865 : const int kNThreads = 100;
866 : #endif
867 : Local<v8::Context> context;
868 : std::vector<JoinableThread*> threads;
869 5 : threads.reserve(kNThreads);
870 : {
871 5 : v8::Locker locker_(CcTest::isolate());
872 5 : v8::Isolate::Scope isolate_scope(CcTest::isolate());
873 10 : v8::HandleScope handle_scope(CcTest::isolate());
874 5 : context = v8::Context::New(CcTest::isolate());
875 505 : for (int i = 0; i < kNThreads; i++) {
876 1000 : threads.push_back(new LockUnlockLockDefaultIsolateThread(context));
877 5 : }
878 : }
879 5 : StartJoinAndDeleteThreads(threads);
880 5 : }
881 :
882 :
883 23723 : TEST(Regress1433) {
884 55 : for (int i = 0; i < 10; i++) {
885 : v8::Isolate::CreateParams create_params;
886 50 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
887 50 : v8::Isolate* isolate = v8::Isolate::New(create_params);
888 : {
889 : v8::Locker lock(isolate);
890 : v8::Isolate::Scope isolate_scope(isolate);
891 100 : v8::HandleScope handle_scope(isolate);
892 50 : v8::Local<v8::Context> context = v8::Context::New(isolate);
893 : v8::Context::Scope context_scope(context);
894 50 : v8::Local<v8::String> source = v8_str("1+1");
895 : v8::Local<v8::Script> script =
896 50 : v8::Script::Compile(context, source).ToLocalChecked();
897 100 : v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
898 100 : v8::String::Utf8Value utf8(isolate, result);
899 : }
900 50 : isolate->Dispose();
901 : }
902 5 : }
903 :
904 :
905 : static const char* kSimpleExtensionSource =
906 : "(function Foo() {"
907 : " return 4;"
908 : "})() ";
909 :
910 600 : class IsolateGenesisThread : public JoinableThread {
911 : public:
912 : IsolateGenesisThread(int count, const char* extension_names[])
913 : : JoinableThread("IsolateGenesisThread"),
914 : count_(count),
915 200 : extension_names_(extension_names)
916 : {}
917 :
918 200 : virtual void Run() {
919 : v8::Isolate::CreateParams create_params;
920 200 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
921 200 : v8::Isolate* isolate = v8::Isolate::New(create_params);
922 : {
923 : v8::Isolate::Scope isolate_scope(isolate);
924 200 : v8::ExtensionConfiguration extensions(count_, extension_names_);
925 400 : v8::HandleScope handle_scope(isolate);
926 200 : v8::Context::New(isolate, &extensions);
927 : }
928 200 : isolate->Dispose();
929 200 : }
930 :
931 : private:
932 : int count_;
933 : const char** extension_names_;
934 : };
935 :
936 :
937 : // Test installing extensions in separate isolates concurrently.
938 : // http://code.google.com/p/v8/issues/detail?id=1821
939 23723 : TEST(ExtensionsRegistration) {
940 : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
941 : const int kNThreads = 10;
942 : #elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT
943 : const int kNThreads = 4;
944 : #elif V8_TARGET_ARCH_S390 && V8_TARGET_ARCH_32_BIT
945 : const int kNThreads = 10;
946 : #else
947 : const int kNThreads = 40;
948 : #endif
949 : v8::RegisterExtension(new v8::Extension("test0",
950 5 : kSimpleExtensionSource));
951 : v8::RegisterExtension(new v8::Extension("test1",
952 5 : kSimpleExtensionSource));
953 : v8::RegisterExtension(new v8::Extension("test2",
954 5 : kSimpleExtensionSource));
955 : v8::RegisterExtension(new v8::Extension("test3",
956 5 : kSimpleExtensionSource));
957 : v8::RegisterExtension(new v8::Extension("test4",
958 5 : kSimpleExtensionSource));
959 : v8::RegisterExtension(new v8::Extension("test5",
960 5 : kSimpleExtensionSource));
961 : v8::RegisterExtension(new v8::Extension("test6",
962 5 : kSimpleExtensionSource));
963 : v8::RegisterExtension(new v8::Extension("test7",
964 5 : kSimpleExtensionSource));
965 : const char* extension_names[] = { "test0", "test1",
966 : "test2", "test3", "test4",
967 5 : "test5", "test6", "test7" };
968 : std::vector<JoinableThread*> threads;
969 5 : threads.reserve(kNThreads);
970 205 : for (int i = 0; i < kNThreads; i++) {
971 600 : threads.push_back(new IsolateGenesisThread(8, extension_names));
972 : }
973 5 : StartJoinAndDeleteThreads(threads);
974 5 : }
975 :
976 : } // namespace test_lockers
977 : } // namespace internal
978 71154 : } // namespace v8
|