blob: 190fc7bbd44ac64987b4124d9780884c19872174 [file] [log] [blame]
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00001// Copyright 2009 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 "v8.h"
29#include "platform.h"
30#include "cctest.h"
31
32
33v8::internal::Semaphore* semaphore = NULL;
34
35
36v8::Handle<v8::Value> Signal(const v8::Arguments& args) {
37 semaphore->Signal();
38 return v8::Undefined();
39}
40
41
42v8::Handle<v8::Value> TerminateCurrentThread(const v8::Arguments& args) {
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +000043 CHECK(!v8::V8::IsExecutionTerminating());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000044 v8::V8::TerminateExecution();
45 return v8::Undefined();
46}
47
48
49v8::Handle<v8::Value> Fail(const v8::Arguments& args) {
50 CHECK(false);
51 return v8::Undefined();
52}
53
54
55v8::Handle<v8::Value> Loop(const v8::Arguments& args) {
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +000056 CHECK(!v8::V8::IsExecutionTerminating());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000057 v8::Handle<v8::String> source =
58 v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +000059 v8::Handle<v8::Value> result = v8::Script::Compile(source)->Run();
60 CHECK(result.IsEmpty());
61 CHECK(v8::V8::IsExecutionTerminating());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000062 return v8::Undefined();
63}
64
65
66v8::Handle<v8::Value> DoLoop(const v8::Arguments& args) {
67 v8::TryCatch try_catch;
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +000068 CHECK(!v8::V8::IsExecutionTerminating());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000069 v8::Script::Compile(v8::String::New("function f() {"
70 " var term = true;"
71 " try {"
72 " while(true) {"
73 " if (term) terminate();"
74 " term = false;"
75 " }"
76 " fail();"
77 " } catch(e) {"
78 " fail();"
79 " }"
80 "}"
81 "f()"))->Run();
82 CHECK(try_catch.HasCaught());
83 CHECK(try_catch.Exception()->IsNull());
84 CHECK(try_catch.Message().IsEmpty());
85 CHECK(!try_catch.CanContinue());
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +000086 CHECK(v8::V8::IsExecutionTerminating());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +000087 return v8::Undefined();
88}
89
90
ager@chromium.org01beca72009-11-24 14:29:16 +000091v8::Handle<v8::Value> DoLoopNoCall(const v8::Arguments& args) {
92 v8::TryCatch try_catch;
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +000093 CHECK(!v8::V8::IsExecutionTerminating());
ager@chromium.org01beca72009-11-24 14:29:16 +000094 v8::Script::Compile(v8::String::New("var term = true;"
95 "while(true) {"
96 " if (term) terminate();"
97 " term = false;"
98 "}"))->Run();
99 CHECK(try_catch.HasCaught());
100 CHECK(try_catch.Exception()->IsNull());
101 CHECK(try_catch.Message().IsEmpty());
102 CHECK(!try_catch.CanContinue());
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000103 CHECK(v8::V8::IsExecutionTerminating());
ager@chromium.org01beca72009-11-24 14:29:16 +0000104 return v8::Undefined();
105}
106
107
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000108v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
ager@chromium.org01beca72009-11-24 14:29:16 +0000109 v8::InvocationCallback terminate,
110 v8::InvocationCallback doloop) {
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000111 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
112 global->Set(v8::String::New("terminate"),
113 v8::FunctionTemplate::New(terminate));
114 global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
115 global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop));
ager@chromium.org01beca72009-11-24 14:29:16 +0000116 global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000117 return global;
118}
119
120
121// Test that a single thread of JavaScript execution can terminate
122// itself.
123TEST(TerminateOnlyV8ThreadFromThreadItself) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000124 v8::HandleScope scope(v8::Isolate::GetCurrent());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000125 v8::Handle<v8::ObjectTemplate> global =
ager@chromium.org01beca72009-11-24 14:29:16 +0000126 CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
127 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
128 v8::Context::Scope context_scope(context);
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000129 CHECK(!v8::V8::IsExecutionTerminating());
ager@chromium.org01beca72009-11-24 14:29:16 +0000130 // Run a loop that will be infinite if thread termination does not work.
131 v8::Handle<v8::String> source =
132 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
133 v8::Script::Compile(source)->Run();
134 // Test that we can run the code again after thread termination.
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000135 CHECK(!v8::V8::IsExecutionTerminating());
ager@chromium.org01beca72009-11-24 14:29:16 +0000136 v8::Script::Compile(source)->Run();
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +0000137 context.Dispose(context->GetIsolate());
ager@chromium.org01beca72009-11-24 14:29:16 +0000138}
139
140
141// Test that a single thread of JavaScript execution can terminate
142// itself in a loop that performs no calls.
143TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000144 v8::HandleScope scope(v8::Isolate::GetCurrent());
ager@chromium.org01beca72009-11-24 14:29:16 +0000145 v8::Handle<v8::ObjectTemplate> global =
146 CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000147 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
148 v8::Context::Scope context_scope(context);
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000149 CHECK(!v8::V8::IsExecutionTerminating());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000150 // Run a loop that will be infinite if thread termination does not work.
151 v8::Handle<v8::String> source =
152 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
153 v8::Script::Compile(source)->Run();
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000154 CHECK(!v8::V8::IsExecutionTerminating());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000155 // Test that we can run the code again after thread termination.
156 v8::Script::Compile(source)->Run();
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +0000157 context.Dispose(context->GetIsolate());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000158}
159
160
161class TerminatorThread : public v8::internal::Thread {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000162 public:
163 explicit TerminatorThread(i::Isolate* isolate)
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000164 : Thread("TerminatorThread"),
165 isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { }
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000166 void Run() {
167 semaphore->Wait();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000168 CHECK(!v8::V8::IsExecutionTerminating(isolate_));
169 v8::V8::TerminateExecution(isolate_);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000170 }
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000171
172 private:
173 v8::Isolate* isolate_;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000174};
175
176
177// Test that a single thread of JavaScript execution can be terminated
178// from the side by another thread.
179TEST(TerminateOnlyV8ThreadFromOtherThread) {
180 semaphore = v8::internal::OS::CreateSemaphore(0);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000181 TerminatorThread thread(i::Isolate::Current());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000182 thread.Start();
183
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000184 v8::HandleScope scope(v8::Isolate::GetCurrent());
ager@chromium.org01beca72009-11-24 14:29:16 +0000185 v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000186 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
187 v8::Context::Scope context_scope(context);
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000188 CHECK(!v8::V8::IsExecutionTerminating());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000189 // Run a loop that will be infinite if thread termination does not work.
190 v8::Handle<v8::String> source =
191 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
192 v8::Script::Compile(source)->Run();
193
194 thread.Join();
195 delete semaphore;
196 semaphore = NULL;
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +0000197 context.Dispose(context->GetIsolate());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000198}
199
200
201class LoopingThread : public v8::internal::Thread {
202 public:
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000203 LoopingThread() : Thread("LoopingThread") { }
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000204 void Run() {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +0000205 v8::Locker locker(CcTest::default_isolate());
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000206 v8::HandleScope scope(CcTest::default_isolate());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000207 v8_thread_id_ = v8::V8::GetCurrentThreadId();
ager@chromium.org01beca72009-11-24 14:29:16 +0000208 v8::Handle<v8::ObjectTemplate> global =
209 CreateGlobalTemplate(Signal, DoLoop);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000210 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
211 v8::Context::Scope context_scope(context);
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000212 CHECK(!v8::V8::IsExecutionTerminating());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000213 // Run a loop that will be infinite if thread termination does not work.
214 v8::Handle<v8::String> source =
215 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
216 v8::Script::Compile(source)->Run();
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +0000217 context.Dispose(context->GetIsolate());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000218 }
219
220 int GetV8ThreadId() { return v8_thread_id_; }
221
222 private:
223 int v8_thread_id_;
224};
225
226
lrn@chromium.org1c092762011-05-09 09:42:16 +0000227// Test that multiple threads using default isolate can be terminated
228// from another thread when using Lockers and preemption.
229TEST(TerminateMultipleV8ThreadsDefaultIsolate) {
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000230 {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +0000231 v8::Locker locker(CcTest::default_isolate());
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000232 v8::V8::Initialize();
233 v8::Locker::StartPreemption(1);
234 semaphore = v8::internal::OS::CreateSemaphore(0);
235 }
lrn@chromium.org1c092762011-05-09 09:42:16 +0000236 const int kThreads = 2;
237 i::List<LoopingThread*> threads(kThreads);
238 for (int i = 0; i < kThreads; i++) {
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000239 threads.Add(new LoopingThread());
lrn@chromium.org1c092762011-05-09 09:42:16 +0000240 }
241 for (int i = 0; i < kThreads; i++) {
242 threads[i]->Start();
243 }
244 // Wait until all threads have signaled the semaphore.
245 for (int i = 0; i < kThreads; i++) {
246 semaphore->Wait();
247 }
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000248 {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +0000249 v8::Locker locker(CcTest::default_isolate());
lrn@chromium.org1c092762011-05-09 09:42:16 +0000250 for (int i = 0; i < kThreads; i++) {
251 v8::V8::TerminateExecution(threads[i]->GetV8ThreadId());
252 }
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000253 }
lrn@chromium.org1c092762011-05-09 09:42:16 +0000254 for (int i = 0; i < kThreads; i++) {
255 threads[i]->Join();
256 delete threads[i];
257 }
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000258 {
yangguo@chromium.org46a2a512013-01-18 16:29:40 +0000259 v8::Locker locker(CcTest::default_isolate());
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000260 v8::Locker::StopPreemption();
261 }
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +0000262
263 delete semaphore;
264 semaphore = NULL;
265}
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000266
267
268int call_count = 0;
269
270
271v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
272 if (++call_count == 10) {
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000273 CHECK(!v8::V8::IsExecutionTerminating());
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000274 v8::V8::TerminateExecution();
275 return v8::Undefined();
276 }
277 v8::Local<v8::Object> result = v8::Object::New();
278 result->Set(v8::String::New("x"), v8::Integer::New(42));
279 return result;
280}
281
282
283v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
284 v8::TryCatch try_catch;
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000285 CHECK(!v8::V8::IsExecutionTerminating());
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000286 v8::Script::Compile(v8::String::New("function f() {"
287 " try {"
288 " while(true) {"
289 " terminate_or_return_object().x;"
290 " }"
291 " fail();"
292 " } catch(e) {"
293 " fail();"
294 " }"
295 "}"
296 "f()"))->Run();
297 CHECK(try_catch.HasCaught());
298 CHECK(try_catch.Exception()->IsNull());
299 CHECK(try_catch.Message().IsEmpty());
300 CHECK(!try_catch.CanContinue());
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000301 CHECK(v8::V8::IsExecutionTerminating());
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000302 return v8::Undefined();
303}
304
305
306// Test that we correctly handle termination exceptions if they are
307// triggered by the creation of error objects in connection with ICs.
308TEST(TerminateLoadICException) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000309 v8::HandleScope scope(v8::Isolate::GetCurrent());
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000310 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
311 global->Set(v8::String::New("terminate_or_return_object"),
312 v8::FunctionTemplate::New(TerminateOrReturnObject));
313 global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
314 global->Set(v8::String::New("loop"),
315 v8::FunctionTemplate::New(LoopGetProperty));
316
317 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
318 v8::Context::Scope context_scope(context);
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000319 CHECK(!v8::V8::IsExecutionTerminating());
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000320 // Run a loop that will be infinite if thread termination does not work.
321 v8::Handle<v8::String> source =
322 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
323 call_count = 0;
324 v8::Script::Compile(source)->Run();
325 // Test that we can run the code again after thread termination.
sgjesse@chromium.org2ab99522010-03-10 09:03:43 +0000326 CHECK(!v8::V8::IsExecutionTerminating());
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000327 call_count = 0;
328 v8::Script::Compile(source)->Run();
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +0000329 context.Dispose(context->GetIsolate());
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000330}
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000331
332v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) {
333 v8::TryCatch try_catch;
334 CHECK(!v8::V8::IsExecutionTerminating());
335 v8::Script::Compile(v8::String::New("function f() {"
336 " var term = true;"
337 " try {"
338 " while(true) {"
339 " if (term) terminate();"
340 " term = false;"
341 " }"
342 " fail();"
343 " } catch(e) {"
344 " fail();"
345 " }"
346 "}"
347 "f()"))->Run();
348 CHECK(try_catch.HasCaught());
349 CHECK(try_catch.Exception()->IsNull());
350 CHECK(try_catch.Message().IsEmpty());
351 CHECK(!try_catch.CanContinue());
352 CHECK(v8::V8::IsExecutionTerminating());
353 v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
354 return v8::Undefined();
355}
356
357// Test that reentry into V8 while the termination exception is still pending
358// (has not yet unwound the 0-level JS frame) does not crash.
359TEST(TerminateAndReenterFromThreadItself) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000360 v8::HandleScope scope(v8::Isolate::GetCurrent());
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000361 v8::Handle<v8::ObjectTemplate> global =
362 CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
363 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
364 v8::Context::Scope context_scope(context);
365 CHECK(!v8::V8::IsExecutionTerminating());
366 v8::Handle<v8::String> source =
367 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
368 v8::Script::Compile(source)->Run();
369 CHECK(!v8::V8::IsExecutionTerminating());
370 // Check we can run JS again after termination.
371 CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
372 "f()"))->Run()->IsTrue());
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +0000373 context.Dispose(context->GetIsolate());
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000374}