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.
28 // TODO(dcarney): remove
29 #define V8_ALLOW_ACCESS_TO_PERSISTENT_IMPLICIT
30 #define V8_ALLOW_ACCESS_TO_PERSISTENT_ARROW
37 v8::internal::Semaphore* semaphore = NULL;
40 v8::Handle<v8::Value> Signal(const v8::Arguments& args) {
42 return v8::Undefined();
46 v8::Handle<v8::Value> TerminateCurrentThread(const v8::Arguments& args) {
47 CHECK(!v8::V8::IsExecutionTerminating());
48 v8::V8::TerminateExecution();
49 return v8::Undefined();
53 v8::Handle<v8::Value> Fail(const v8::Arguments& args) {
55 return v8::Undefined();
59 v8::Handle<v8::Value> Loop(const v8::Arguments& args) {
60 CHECK(!v8::V8::IsExecutionTerminating());
61 v8::Handle<v8::String> source =
62 v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
63 v8::Handle<v8::Value> result = v8::Script::Compile(source)->Run();
64 CHECK(result.IsEmpty());
65 CHECK(v8::V8::IsExecutionTerminating());
66 return v8::Undefined();
70 v8::Handle<v8::Value> DoLoop(const v8::Arguments& args) {
71 v8::TryCatch try_catch;
72 CHECK(!v8::V8::IsExecutionTerminating());
73 v8::Script::Compile(v8::String::New("function f() {"
77 " if (term) terminate();"
86 CHECK(try_catch.HasCaught());
87 CHECK(try_catch.Exception()->IsNull());
88 CHECK(try_catch.Message().IsEmpty());
89 CHECK(!try_catch.CanContinue());
90 CHECK(v8::V8::IsExecutionTerminating());
91 return v8::Undefined();
95 v8::Handle<v8::Value> DoLoopNoCall(const v8::Arguments& args) {
96 v8::TryCatch try_catch;
97 CHECK(!v8::V8::IsExecutionTerminating());
98 v8::Script::Compile(v8::String::New("var term = true;"
100 " if (term) terminate();"
103 CHECK(try_catch.HasCaught());
104 CHECK(try_catch.Exception()->IsNull());
105 CHECK(try_catch.Message().IsEmpty());
106 CHECK(!try_catch.CanContinue());
107 CHECK(v8::V8::IsExecutionTerminating());
108 return v8::Undefined();
112 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
113 v8::InvocationCallback terminate,
114 v8::InvocationCallback doloop) {
115 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
116 global->Set(v8::String::New("terminate"),
117 v8::FunctionTemplate::New(terminate));
118 global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
119 global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop));
120 global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
125 // Test that a single thread of JavaScript execution can terminate
127 TEST(TerminateOnlyV8ThreadFromThreadItself) {
128 v8::HandleScope scope(v8::Isolate::GetCurrent());
129 v8::Handle<v8::ObjectTemplate> global =
130 CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
131 v8::Handle<v8::Context> context =
132 v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
133 v8::Context::Scope context_scope(context);
134 CHECK(!v8::V8::IsExecutionTerminating());
135 // Run a loop that will be infinite if thread termination does not work.
136 v8::Handle<v8::String> source =
137 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
138 v8::Script::Compile(source)->Run();
139 // Test that we can run the code again after thread termination.
140 CHECK(!v8::V8::IsExecutionTerminating());
141 v8::Script::Compile(source)->Run();
145 // Test that a single thread of JavaScript execution can terminate
146 // itself in a loop that performs no calls.
147 TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
148 v8::HandleScope scope(v8::Isolate::GetCurrent());
149 v8::Handle<v8::ObjectTemplate> global =
150 CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
151 v8::Handle<v8::Context> context =
152 v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
153 v8::Context::Scope context_scope(context);
154 CHECK(!v8::V8::IsExecutionTerminating());
155 // Run a loop that will be infinite if thread termination does not work.
156 v8::Handle<v8::String> source =
157 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
158 v8::Script::Compile(source)->Run();
159 CHECK(!v8::V8::IsExecutionTerminating());
160 // Test that we can run the code again after thread termination.
161 v8::Script::Compile(source)->Run();
165 class TerminatorThread : public v8::internal::Thread {
167 explicit TerminatorThread(i::Isolate* isolate)
168 : Thread("TerminatorThread"),
169 isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { }
172 CHECK(!v8::V8::IsExecutionTerminating(isolate_));
173 v8::V8::TerminateExecution(isolate_);
177 v8::Isolate* isolate_;
181 // Test that a single thread of JavaScript execution can be terminated
182 // from the side by another thread.
183 TEST(TerminateOnlyV8ThreadFromOtherThread) {
184 semaphore = v8::internal::OS::CreateSemaphore(0);
185 TerminatorThread thread(i::Isolate::Current());
188 v8::HandleScope scope(v8::Isolate::GetCurrent());
189 v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
190 v8::Handle<v8::Context> context =
191 v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
192 v8::Context::Scope context_scope(context);
193 CHECK(!v8::V8::IsExecutionTerminating());
194 // Run a loop that will be infinite if thread termination does not work.
195 v8::Handle<v8::String> source =
196 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
197 v8::Script::Compile(source)->Run();
205 class LoopingThread : public v8::internal::Thread {
207 LoopingThread() : Thread("LoopingThread") { }
209 v8::Locker locker(CcTest::default_isolate());
210 v8::HandleScope scope(CcTest::default_isolate());
211 v8_thread_id_ = v8::V8::GetCurrentThreadId();
212 v8::Handle<v8::ObjectTemplate> global =
213 CreateGlobalTemplate(Signal, DoLoop);
214 v8::Handle<v8::Context> context =
215 v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
216 v8::Context::Scope context_scope(context);
217 CHECK(!v8::V8::IsExecutionTerminating());
218 // Run a loop that will be infinite if thread termination does not work.
219 v8::Handle<v8::String> source =
220 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
221 v8::Script::Compile(source)->Run();
224 int GetV8ThreadId() { return v8_thread_id_; }
231 // Test that multiple threads using default isolate can be terminated
232 // from another thread when using Lockers and preemption.
233 TEST(TerminateMultipleV8ThreadsDefaultIsolate) {
235 v8::Locker locker(CcTest::default_isolate());
236 v8::V8::Initialize();
237 v8::Locker::StartPreemption(1);
238 semaphore = v8::internal::OS::CreateSemaphore(0);
240 const int kThreads = 2;
241 i::List<LoopingThread*> threads(kThreads);
242 for (int i = 0; i < kThreads; i++) {
243 threads.Add(new LoopingThread());
245 for (int i = 0; i < kThreads; i++) {
248 // Wait until all threads have signaled the semaphore.
249 for (int i = 0; i < kThreads; i++) {
253 v8::Locker locker(CcTest::default_isolate());
254 for (int i = 0; i < kThreads; i++) {
255 v8::V8::TerminateExecution(threads[i]->GetV8ThreadId());
258 for (int i = 0; i < kThreads; i++) {
263 v8::Locker locker(CcTest::default_isolate());
264 v8::Locker::StopPreemption();
275 v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
276 if (++call_count == 10) {
277 CHECK(!v8::V8::IsExecutionTerminating());
278 v8::V8::TerminateExecution();
279 return v8::Undefined();
281 v8::Local<v8::Object> result = v8::Object::New();
282 result->Set(v8::String::New("x"), v8::Integer::New(42));
287 v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
288 v8::TryCatch try_catch;
289 CHECK(!v8::V8::IsExecutionTerminating());
290 v8::Script::Compile(v8::String::New("function f() {"
293 " terminate_or_return_object().x;"
301 CHECK(try_catch.HasCaught());
302 CHECK(try_catch.Exception()->IsNull());
303 CHECK(try_catch.Message().IsEmpty());
304 CHECK(!try_catch.CanContinue());
305 CHECK(v8::V8::IsExecutionTerminating());
306 return v8::Undefined();
310 // Test that we correctly handle termination exceptions if they are
311 // triggered by the creation of error objects in connection with ICs.
312 TEST(TerminateLoadICException) {
313 v8::HandleScope scope(v8::Isolate::GetCurrent());
314 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
315 global->Set(v8::String::New("terminate_or_return_object"),
316 v8::FunctionTemplate::New(TerminateOrReturnObject));
317 global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
318 global->Set(v8::String::New("loop"),
319 v8::FunctionTemplate::New(LoopGetProperty));
321 v8::Handle<v8::Context> context =
322 v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
323 v8::Context::Scope context_scope(context);
324 CHECK(!v8::V8::IsExecutionTerminating());
325 // Run a loop that will be infinite if thread termination does not work.
326 v8::Handle<v8::String> source =
327 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
329 v8::Script::Compile(source)->Run();
330 // Test that we can run the code again after thread termination.
331 CHECK(!v8::V8::IsExecutionTerminating());
333 v8::Script::Compile(source)->Run();
336 v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) {
337 v8::TryCatch try_catch;
338 CHECK(!v8::V8::IsExecutionTerminating());
339 v8::Script::Compile(v8::String::New("function f() {"
343 " if (term) terminate();"
352 CHECK(try_catch.HasCaught());
353 CHECK(try_catch.Exception()->IsNull());
354 CHECK(try_catch.Message().IsEmpty());
355 CHECK(!try_catch.CanContinue());
356 CHECK(v8::V8::IsExecutionTerminating());
357 v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
358 return v8::Undefined();
361 // Test that reentry into V8 while the termination exception is still pending
362 // (has not yet unwound the 0-level JS frame) does not crash.
363 TEST(TerminateAndReenterFromThreadItself) {
364 v8::HandleScope scope(v8::Isolate::GetCurrent());
365 v8::Handle<v8::ObjectTemplate> global =
366 CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
367 v8::Handle<v8::Context> context =
368 v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
369 v8::Context::Scope context_scope(context);
370 CHECK(!v8::V8::IsExecutionTerminating());
371 v8::Handle<v8::String> source =
372 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
373 v8::Script::Compile(source)->Run();
374 CHECK(!v8::V8::IsExecutionTerminating());
375 // Check we can run JS again after termination.
376 CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
377 "f()"))->Run()->IsTrue());
380 v8::Handle<v8::Value> DoLoopCancelTerminate(const v8::Arguments& args) {
381 v8::TryCatch try_catch;
382 CHECK(!v8::V8::IsExecutionTerminating());
383 v8::Script::Compile(v8::String::New("var term = true;"
385 " if (term) terminate();"
389 CHECK(try_catch.HasCaught());
390 CHECK(try_catch.Exception()->IsNull());
391 CHECK(try_catch.Message().IsEmpty());
392 CHECK(!try_catch.CanContinue());
393 CHECK(v8::V8::IsExecutionTerminating());
394 CHECK(try_catch.HasTerminated());
395 v8::V8::CancelTerminateExecution(v8::Isolate::GetCurrent());
396 CHECK(!v8::V8::IsExecutionTerminating());
397 return v8::Undefined();
400 // Test that a single thread of JavaScript execution can terminate
401 // itself and then resume execution.
402 TEST(TerminateCancelTerminateFromThreadItself) {
403 v8::HandleScope scope;
404 v8::Handle<v8::ObjectTemplate> global =
405 CreateGlobalTemplate(TerminateCurrentThread, DoLoopCancelTerminate);
406 v8::Handle<v8::Context> context =
407 v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
408 v8::Context::Scope context_scope(context);
409 CHECK(!v8::V8::IsExecutionTerminating());
410 v8::Handle<v8::String> source =
411 v8::String::New("try { doloop(); } catch(e) { fail(); } 'completed';");
412 // Check that execution completed with correct return value.
413 CHECK(v8::Script::Compile(source)->Run()->Equals(v8_str("completed")));