1 // 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
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.
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.
33 v8::internal::Semaphore* semaphore = NULL;
36 v8::Handle<v8::Value> Signal(const v8::Arguments& args) {
38 return v8::Undefined();
42 v8::Handle<v8::Value> TerminateCurrentThread(const v8::Arguments& args) {
43 CHECK(!v8::V8::IsExecutionTerminating());
44 v8::V8::TerminateExecution();
45 return v8::Undefined();
49 v8::Handle<v8::Value> Fail(const v8::Arguments& args) {
51 return v8::Undefined();
55 v8::Handle<v8::Value> Loop(const v8::Arguments& args) {
56 CHECK(!v8::V8::IsExecutionTerminating());
57 v8::Handle<v8::String> source =
58 v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
59 v8::Handle<v8::Value> result = v8::Script::Compile(source)->Run();
60 CHECK(result.IsEmpty());
61 CHECK(v8::V8::IsExecutionTerminating());
62 return v8::Undefined();
66 v8::Handle<v8::Value> DoLoop(const v8::Arguments& args) {
67 v8::TryCatch try_catch;
68 CHECK(!v8::V8::IsExecutionTerminating());
69 v8::Script::Compile(v8::String::New("function f() {"
73 " if (term) terminate();"
82 CHECK(try_catch.HasCaught());
83 CHECK(try_catch.Exception()->IsNull());
84 CHECK(try_catch.Message().IsEmpty());
85 CHECK(!try_catch.CanContinue());
86 CHECK(v8::V8::IsExecutionTerminating());
87 return v8::Undefined();
91 v8::Handle<v8::Value> DoLoopNoCall(const v8::Arguments& args) {
92 v8::TryCatch try_catch;
93 CHECK(!v8::V8::IsExecutionTerminating());
94 v8::Script::Compile(v8::String::New("var term = true;"
96 " if (term) terminate();"
99 CHECK(try_catch.HasCaught());
100 CHECK(try_catch.Exception()->IsNull());
101 CHECK(try_catch.Message().IsEmpty());
102 CHECK(!try_catch.CanContinue());
103 CHECK(v8::V8::IsExecutionTerminating());
104 return v8::Undefined();
108 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
109 v8::InvocationCallback terminate,
110 v8::InvocationCallback doloop) {
111 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));
116 global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
121 // Test that a single thread of JavaScript execution can terminate
123 TEST(TerminateOnlyV8ThreadFromThreadItself) {
124 v8::HandleScope scope(v8::Isolate::GetCurrent());
125 v8::Handle<v8::ObjectTemplate> global =
126 CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
127 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
128 v8::Context::Scope context_scope(context);
129 CHECK(!v8::V8::IsExecutionTerminating());
130 // 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.
135 CHECK(!v8::V8::IsExecutionTerminating());
136 v8::Script::Compile(source)->Run();
137 context.Dispose(context->GetIsolate());
141 // Test that a single thread of JavaScript execution can terminate
142 // itself in a loop that performs no calls.
143 TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
144 v8::HandleScope scope(v8::Isolate::GetCurrent());
145 v8::Handle<v8::ObjectTemplate> global =
146 CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
147 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
148 v8::Context::Scope context_scope(context);
149 CHECK(!v8::V8::IsExecutionTerminating());
150 // 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();
154 CHECK(!v8::V8::IsExecutionTerminating());
155 // Test that we can run the code again after thread termination.
156 v8::Script::Compile(source)->Run();
157 context.Dispose(context->GetIsolate());
161 class TerminatorThread : public v8::internal::Thread {
163 explicit TerminatorThread(i::Isolate* isolate)
164 : Thread("TerminatorThread"),
165 isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { }
168 CHECK(!v8::V8::IsExecutionTerminating(isolate_));
169 v8::V8::TerminateExecution(isolate_);
173 v8::Isolate* isolate_;
177 // Test that a single thread of JavaScript execution can be terminated
178 // from the side by another thread.
179 TEST(TerminateOnlyV8ThreadFromOtherThread) {
180 semaphore = v8::internal::OS::CreateSemaphore(0);
181 TerminatorThread thread(i::Isolate::Current());
184 v8::HandleScope scope(v8::Isolate::GetCurrent());
185 v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
186 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
187 v8::Context::Scope context_scope(context);
188 CHECK(!v8::V8::IsExecutionTerminating());
189 // 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();
197 context.Dispose(context->GetIsolate());
201 class LoopingThread : public v8::internal::Thread {
203 LoopingThread() : Thread("LoopingThread") { }
205 v8::Locker locker(CcTest::default_isolate());
206 v8::HandleScope scope(CcTest::default_isolate());
207 v8_thread_id_ = v8::V8::GetCurrentThreadId();
208 v8::Handle<v8::ObjectTemplate> global =
209 CreateGlobalTemplate(Signal, DoLoop);
210 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
211 v8::Context::Scope context_scope(context);
212 CHECK(!v8::V8::IsExecutionTerminating());
213 // 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();
217 context.Dispose(context->GetIsolate());
220 int GetV8ThreadId() { return v8_thread_id_; }
227 // Test that multiple threads using default isolate can be terminated
228 // from another thread when using Lockers and preemption.
229 TEST(TerminateMultipleV8ThreadsDefaultIsolate) {
231 v8::Locker locker(CcTest::default_isolate());
232 v8::V8::Initialize();
233 v8::Locker::StartPreemption(1);
234 semaphore = v8::internal::OS::CreateSemaphore(0);
236 const int kThreads = 2;
237 i::List<LoopingThread*> threads(kThreads);
238 for (int i = 0; i < kThreads; i++) {
239 threads.Add(new LoopingThread());
241 for (int i = 0; i < kThreads; i++) {
244 // Wait until all threads have signaled the semaphore.
245 for (int i = 0; i < kThreads; i++) {
249 v8::Locker locker(CcTest::default_isolate());
250 for (int i = 0; i < kThreads; i++) {
251 v8::V8::TerminateExecution(threads[i]->GetV8ThreadId());
254 for (int i = 0; i < kThreads; i++) {
259 v8::Locker locker(CcTest::default_isolate());
260 v8::Locker::StopPreemption();
271 v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
272 if (++call_count == 10) {
273 CHECK(!v8::V8::IsExecutionTerminating());
274 v8::V8::TerminateExecution();
275 return v8::Undefined();
277 v8::Local<v8::Object> result = v8::Object::New();
278 result->Set(v8::String::New("x"), v8::Integer::New(42));
283 v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
284 v8::TryCatch try_catch;
285 CHECK(!v8::V8::IsExecutionTerminating());
286 v8::Script::Compile(v8::String::New("function f() {"
289 " terminate_or_return_object().x;"
297 CHECK(try_catch.HasCaught());
298 CHECK(try_catch.Exception()->IsNull());
299 CHECK(try_catch.Message().IsEmpty());
300 CHECK(!try_catch.CanContinue());
301 CHECK(v8::V8::IsExecutionTerminating());
302 return v8::Undefined();
306 // Test that we correctly handle termination exceptions if they are
307 // triggered by the creation of error objects in connection with ICs.
308 TEST(TerminateLoadICException) {
309 v8::HandleScope scope(v8::Isolate::GetCurrent());
310 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));
317 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
318 v8::Context::Scope context_scope(context);
319 CHECK(!v8::V8::IsExecutionTerminating());
320 // 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(); }");
324 v8::Script::Compile(source)->Run();
325 // Test that we can run the code again after thread termination.
326 CHECK(!v8::V8::IsExecutionTerminating());
328 v8::Script::Compile(source)->Run();
329 context.Dispose(context->GetIsolate());
332 v8::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() {"
339 " if (term) terminate();"
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();
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.
359 TEST(TerminateAndReenterFromThreadItself) {
360 v8::HandleScope scope(v8::Isolate::GetCurrent());
361 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());
373 context.Dispose(context->GetIsolate());