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 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 33 v8::internal::Semaphore* semaphore = NULL; 34 35 36 v8::Handle<v8::Value> Signal(const v8::Arguments& args) { 37 semaphore->Signal(); 38 return v8::Undefined(); 39 } 40 41 42 v8::Handle<v8::Value> TerminateCurrentThread(const v8::Arguments& args) { 43 CHECK(!v8::V8::IsExecutionTerminating()); 44 v8::V8::TerminateExecution(); 45 return v8::Undefined(); 46 } 47 48 49 v8::Handle<v8::Value> Fail(const v8::Arguments& args) { 50 CHECK(false); 51 return v8::Undefined(); 52 } 53 54 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(); 63 } 64 65 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() {" 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()); 86 CHECK(v8::V8::IsExecutionTerminating()); 87 return v8::Undefined(); 88 } 89 90 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;" 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()); 103 CHECK(v8::V8::IsExecutionTerminating()); 104 return v8::Undefined(); 105 } 106 107 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)); 117 return global; 118 } 119 120 121 // Test that a single thread of JavaScript execution can terminate 122 // itself. 123 TEST(TerminateOnlyV8ThreadFromThreadItself) { 124 v8::HandleScope scope; 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(); 138 } 139 140 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; 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(); 158 } 159 160 161 class TerminatorThread : public v8::internal::Thread { 162 public: 163 explicit TerminatorThread(i::Isolate* isolate) 164 : Thread("TerminatorThread"), 165 isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { } 166 void Run() { 167 semaphore->Wait(); 168 CHECK(!v8::V8::IsExecutionTerminating(isolate_)); 169 v8::V8::TerminateExecution(isolate_); 170 } 171 172 private: 173 v8::Isolate* isolate_; 174 }; 175 176 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()); 182 thread.Start(); 183 184 v8::HandleScope scope; 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(); 193 194 thread.Join(); 195 delete semaphore; 196 semaphore = NULL; 197 context.Dispose(); 198 } 199 200 201 class LoopingThread : public v8::internal::Thread { 202 public: 203 LoopingThread() : Thread("LoopingThread") { } 204 void Run() { 205 v8::Locker locker; 206 v8::HandleScope scope; 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(); 218 } 219 220 int GetV8ThreadId() { return v8_thread_id_; } 221 222 private: 223 int v8_thread_id_; 224 }; 225 226 227 // Test that multiple threads using default isolate can be terminated 228 // from another thread when using Lockers and preemption. 229 TEST(TerminateMultipleV8ThreadsDefaultIsolate) { 230 { 231 v8::Locker locker; 232 v8::V8::Initialize(); 233 v8::Locker::StartPreemption(1); 234 semaphore = v8::internal::OS::CreateSemaphore(0); 235 } 236 const int kThreads = 2; 237 i::List<LoopingThread*> threads(kThreads); 238 for (int i = 0; i < kThreads; i++) { 239 threads.Add(new LoopingThread()); 240 } 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 } 248 { 249 v8::Locker locker; 250 for (int i = 0; i < kThreads; i++) { 251 v8::V8::TerminateExecution(threads[i]->GetV8ThreadId()); 252 } 253 } 254 for (int i = 0; i < kThreads; i++) { 255 threads[i]->Join(); 256 delete threads[i]; 257 } 258 259 delete semaphore; 260 semaphore = NULL; 261 } 262 263 264 int call_count = 0; 265 266 267 v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) { 268 if (++call_count == 10) { 269 CHECK(!v8::V8::IsExecutionTerminating()); 270 v8::V8::TerminateExecution(); 271 return v8::Undefined(); 272 } 273 v8::Local<v8::Object> result = v8::Object::New(); 274 result->Set(v8::String::New("x"), v8::Integer::New(42)); 275 return result; 276 } 277 278 279 v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) { 280 v8::TryCatch try_catch; 281 CHECK(!v8::V8::IsExecutionTerminating()); 282 v8::Script::Compile(v8::String::New("function f() {" 283 " try {" 284 " while(true) {" 285 " terminate_or_return_object().x;" 286 " }" 287 " fail();" 288 " } catch(e) {" 289 " fail();" 290 " }" 291 "}" 292 "f()"))->Run(); 293 CHECK(try_catch.HasCaught()); 294 CHECK(try_catch.Exception()->IsNull()); 295 CHECK(try_catch.Message().IsEmpty()); 296 CHECK(!try_catch.CanContinue()); 297 CHECK(v8::V8::IsExecutionTerminating()); 298 return v8::Undefined(); 299 } 300 301 302 // Test that we correctly handle termination exceptions if they are 303 // triggered by the creation of error objects in connection with ICs. 304 TEST(TerminateLoadICException) { 305 v8::HandleScope scope; 306 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); 307 global->Set(v8::String::New("terminate_or_return_object"), 308 v8::FunctionTemplate::New(TerminateOrReturnObject)); 309 global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail)); 310 global->Set(v8::String::New("loop"), 311 v8::FunctionTemplate::New(LoopGetProperty)); 312 313 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global); 314 v8::Context::Scope context_scope(context); 315 CHECK(!v8::V8::IsExecutionTerminating()); 316 // Run a loop that will be infinite if thread termination does not work. 317 v8::Handle<v8::String> source = 318 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }"); 319 call_count = 0; 320 v8::Script::Compile(source)->Run(); 321 // Test that we can run the code again after thread termination. 322 CHECK(!v8::V8::IsExecutionTerminating()); 323 call_count = 0; 324 v8::Script::Compile(source)->Run(); 325 context.Dispose(); 326 } 327 328 v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) { 329 v8::TryCatch try_catch; 330 CHECK(!v8::V8::IsExecutionTerminating()); 331 v8::Script::Compile(v8::String::New("function f() {" 332 " var term = true;" 333 " try {" 334 " while(true) {" 335 " if (term) terminate();" 336 " term = false;" 337 " }" 338 " fail();" 339 " } catch(e) {" 340 " fail();" 341 " }" 342 "}" 343 "f()"))->Run(); 344 CHECK(try_catch.HasCaught()); 345 CHECK(try_catch.Exception()->IsNull()); 346 CHECK(try_catch.Message().IsEmpty()); 347 CHECK(!try_catch.CanContinue()); 348 CHECK(v8::V8::IsExecutionTerminating()); 349 v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run(); 350 return v8::Undefined(); 351 } 352 353 // Test that reentry into V8 while the termination exception is still pending 354 // (has not yet unwound the 0-level JS frame) does not crash. 355 TEST(TerminateAndReenterFromThreadItself) { 356 v8::HandleScope scope; 357 v8::Handle<v8::ObjectTemplate> global = 358 CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination); 359 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global); 360 v8::Context::Scope context_scope(context); 361 CHECK(!v8::V8::IsExecutionTerminating()); 362 v8::Handle<v8::String> source = 363 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }"); 364 v8::Script::Compile(source)->Run(); 365 CHECK(!v8::V8::IsExecutionTerminating()); 366 // Check we can run JS again after termination. 367 CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }" 368 "f()"))->Run()->IsTrue()); 369 context.Dispose(); 370 } 371 372