blob: 5635b1761c639829b4bce3ff555e6689fb899a87 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +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) {
Steve Block6ded16b2010-05-10 14:33:55 +010043 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +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) {
Steve Block6ded16b2010-05-10 14:33:55 +010056 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +000057 v8::Handle<v8::String> source =
58 v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
Steve Block6ded16b2010-05-10 14:33:55 +010059 v8::Handle<v8::Value> result = v8::Script::Compile(source)->Run();
60 CHECK(result.IsEmpty());
61 CHECK(v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +000062 return v8::Undefined();
63}
64
65
66v8::Handle<v8::Value> DoLoop(const v8::Arguments& args) {
67 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +010068 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +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());
Steve Block6ded16b2010-05-10 14:33:55 +010086 CHECK(v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +000087 return v8::Undefined();
88}
89
90
Steve Blockd0582a62009-12-15 09:54:21 +000091v8::Handle<v8::Value> DoLoopNoCall(const v8::Arguments& args) {
92 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +010093 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blockd0582a62009-12-15 09:54:21 +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());
Steve Block6ded16b2010-05-10 14:33:55 +0100103 CHECK(v8::V8::IsExecutionTerminating());
Steve Blockd0582a62009-12-15 09:54:21 +0000104 return v8::Undefined();
105}
106
107
Steve Blocka7e24c12009-10-30 11:49:00 +0000108v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
Steve Blockd0582a62009-12-15 09:54:21 +0000109 v8::InvocationCallback terminate,
110 v8::InvocationCallback doloop) {
Steve Blocka7e24c12009-10-30 11:49:00 +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));
Steve Blockd0582a62009-12-15 09:54:21 +0000116 global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
Steve Blocka7e24c12009-10-30 11:49:00 +0000117 return global;
118}
119
120
121// Test that a single thread of JavaScript execution can terminate
122// itself.
123TEST(TerminateOnlyV8ThreadFromThreadItself) {
124 v8::HandleScope scope;
125 v8::Handle<v8::ObjectTemplate> global =
Steve Blockd0582a62009-12-15 09:54:21 +0000126 CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
127 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
128 v8::Context::Scope context_scope(context);
Steve Block6ded16b2010-05-10 14:33:55 +0100129 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blockd0582a62009-12-15 09:54:21 +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.
Steve Block6ded16b2010-05-10 14:33:55 +0100135 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blockd0582a62009-12-15 09:54:21 +0000136 v8::Script::Compile(source)->Run();
137 context.Dispose();
138}
139
140
141// Test that a single thread of JavaScript execution can terminate
142// itself in a loop that performs no calls.
143TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
144 v8::HandleScope scope;
145 v8::Handle<v8::ObjectTemplate> global =
146 CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
Steve Blocka7e24c12009-10-30 11:49:00 +0000147 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
148 v8::Context::Scope context_scope(context);
Steve Block6ded16b2010-05-10 14:33:55 +0100149 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +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();
Steve Block6ded16b2010-05-10 14:33:55 +0100154 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000155 // Test that we can run the code again after thread termination.
156 v8::Script::Compile(source)->Run();
157 context.Dispose();
158}
159
160
161class TerminatorThread : public v8::internal::Thread {
Steve Block44f0eee2011-05-26 01:26:41 +0100162 public:
163 explicit TerminatorThread(i::Isolate* isolate)
164 : Thread(isolate, "TerminatorThread") { }
Steve Blocka7e24c12009-10-30 11:49:00 +0000165 void Run() {
166 semaphore->Wait();
Steve Block6ded16b2010-05-10 14:33:55 +0100167 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000168 v8::V8::TerminateExecution();
169 }
170};
171
172
173// Test that a single thread of JavaScript execution can be terminated
174// from the side by another thread.
175TEST(TerminateOnlyV8ThreadFromOtherThread) {
176 semaphore = v8::internal::OS::CreateSemaphore(0);
Steve Block44f0eee2011-05-26 01:26:41 +0100177 TerminatorThread thread(i::Isolate::Current());
Steve Blocka7e24c12009-10-30 11:49:00 +0000178 thread.Start();
179
180 v8::HandleScope scope;
Steve Blockd0582a62009-12-15 09:54:21 +0000181 v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
Steve Blocka7e24c12009-10-30 11:49:00 +0000182 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
183 v8::Context::Scope context_scope(context);
Steve Block6ded16b2010-05-10 14:33:55 +0100184 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000185 // Run a loop that will be infinite if thread termination does not work.
186 v8::Handle<v8::String> source =
187 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
188 v8::Script::Compile(source)->Run();
189
190 thread.Join();
191 delete semaphore;
192 semaphore = NULL;
193 context.Dispose();
194}
195
196
197class LoopingThread : public v8::internal::Thread {
198 public:
Steve Block44f0eee2011-05-26 01:26:41 +0100199 explicit LoopingThread(i::Isolate* isolate)
200 : Thread(isolate, "LoopingThread") { }
Steve Blocka7e24c12009-10-30 11:49:00 +0000201 void Run() {
202 v8::Locker locker;
203 v8::HandleScope scope;
204 v8_thread_id_ = v8::V8::GetCurrentThreadId();
Steve Blockd0582a62009-12-15 09:54:21 +0000205 v8::Handle<v8::ObjectTemplate> global =
206 CreateGlobalTemplate(Signal, DoLoop);
Steve Blocka7e24c12009-10-30 11:49:00 +0000207 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
208 v8::Context::Scope context_scope(context);
Steve Block6ded16b2010-05-10 14:33:55 +0100209 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000210 // Run a loop that will be infinite if thread termination does not work.
211 v8::Handle<v8::String> source =
212 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
213 v8::Script::Compile(source)->Run();
214 context.Dispose();
215 }
216
217 int GetV8ThreadId() { return v8_thread_id_; }
218
219 private:
220 int v8_thread_id_;
221};
222
223
224// Test that multiple threads using V8 can be terminated from another
225// thread when using Lockers and preemption.
226TEST(TerminateMultipleV8Threads) {
227 {
228 v8::Locker locker;
229 v8::V8::Initialize();
230 v8::Locker::StartPreemption(1);
231 semaphore = v8::internal::OS::CreateSemaphore(0);
232 }
Steve Block44f0eee2011-05-26 01:26:41 +0100233 LoopingThread thread1(i::Isolate::Current());
Steve Blocka7e24c12009-10-30 11:49:00 +0000234 thread1.Start();
Steve Block44f0eee2011-05-26 01:26:41 +0100235 LoopingThread thread2(i::Isolate::Current());
Steve Blocka7e24c12009-10-30 11:49:00 +0000236 thread2.Start();
237 // Wait until both threads have signaled the semaphore.
238 semaphore->Wait();
239 semaphore->Wait();
240 {
241 v8::Locker locker;
242 v8::V8::TerminateExecution(thread1.GetV8ThreadId());
243 v8::V8::TerminateExecution(thread2.GetV8ThreadId());
244 }
245 thread1.Join();
246 thread2.Join();
247
248 delete semaphore;
249 semaphore = NULL;
250}
251
252
253int call_count = 0;
254
255
256v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
257 if (++call_count == 10) {
Steve Block6ded16b2010-05-10 14:33:55 +0100258 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000259 v8::V8::TerminateExecution();
260 return v8::Undefined();
261 }
262 v8::Local<v8::Object> result = v8::Object::New();
263 result->Set(v8::String::New("x"), v8::Integer::New(42));
264 return result;
265}
266
267
268v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
269 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +0100270 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000271 v8::Script::Compile(v8::String::New("function f() {"
272 " try {"
273 " while(true) {"
274 " terminate_or_return_object().x;"
275 " }"
276 " fail();"
277 " } catch(e) {"
278 " fail();"
279 " }"
280 "}"
281 "f()"))->Run();
282 CHECK(try_catch.HasCaught());
283 CHECK(try_catch.Exception()->IsNull());
284 CHECK(try_catch.Message().IsEmpty());
285 CHECK(!try_catch.CanContinue());
Steve Block6ded16b2010-05-10 14:33:55 +0100286 CHECK(v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000287 return v8::Undefined();
288}
289
290
291// Test that we correctly handle termination exceptions if they are
292// triggered by the creation of error objects in connection with ICs.
293TEST(TerminateLoadICException) {
294 v8::HandleScope scope;
295 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
296 global->Set(v8::String::New("terminate_or_return_object"),
297 v8::FunctionTemplate::New(TerminateOrReturnObject));
298 global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
299 global->Set(v8::String::New("loop"),
300 v8::FunctionTemplate::New(LoopGetProperty));
301
302 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
303 v8::Context::Scope context_scope(context);
Steve Block6ded16b2010-05-10 14:33:55 +0100304 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000305 // Run a loop that will be infinite if thread termination does not work.
306 v8::Handle<v8::String> source =
307 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
308 call_count = 0;
309 v8::Script::Compile(source)->Run();
310 // Test that we can run the code again after thread termination.
Steve Block6ded16b2010-05-10 14:33:55 +0100311 CHECK(!v8::V8::IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000312 call_count = 0;
313 v8::Script::Compile(source)->Run();
314 context.Dispose();
315}
Leon Clarkef7060e22010-06-03 12:02:55 +0100316
317v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) {
318 v8::TryCatch try_catch;
319 CHECK(!v8::V8::IsExecutionTerminating());
320 v8::Script::Compile(v8::String::New("function f() {"
321 " var term = true;"
322 " try {"
323 " while(true) {"
324 " if (term) terminate();"
325 " term = false;"
326 " }"
327 " fail();"
328 " } catch(e) {"
329 " fail();"
330 " }"
331 "}"
332 "f()"))->Run();
333 CHECK(try_catch.HasCaught());
334 CHECK(try_catch.Exception()->IsNull());
335 CHECK(try_catch.Message().IsEmpty());
336 CHECK(!try_catch.CanContinue());
337 CHECK(v8::V8::IsExecutionTerminating());
338 v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
339 return v8::Undefined();
340}
341
342// Test that reentry into V8 while the termination exception is still pending
343// (has not yet unwound the 0-level JS frame) does not crash.
344TEST(TerminateAndReenterFromThreadItself) {
345 v8::HandleScope scope;
346 v8::Handle<v8::ObjectTemplate> global =
347 CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
348 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
349 v8::Context::Scope context_scope(context);
350 CHECK(!v8::V8::IsExecutionTerminating());
351 v8::Handle<v8::String> source =
352 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
353 v8::Script::Compile(source)->Run();
354 CHECK(!v8::V8::IsExecutionTerminating());
355 // Check we can run JS again after termination.
356 CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
357 "f()"))->Run()->IsTrue());
358 context.Dispose();
359}
360