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