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(isolate, "TerminatorThread") { } 165 void Run() { 166 semaphore->Wait(); 167 CHECK(!v8::V8::IsExecutionTerminating()); 168 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. 175 TEST(TerminateOnlyV8ThreadFromOtherThread) { 176 semaphore = v8::internal::OS::CreateSemaphore(0); 177 TerminatorThread thread(i::Isolate::Current()); 178 thread.Start(); 179 180 v8::HandleScope scope; 181 v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop); 182 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global); 183 v8::Context::Scope context_scope(context); 184 CHECK(!v8::V8::IsExecutionTerminating()); 185 // 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 197 class LoopingThread : public v8::internal::Thread { 198 public: 199 explicit LoopingThread(i::Isolate* isolate) 200 : Thread(isolate, "LoopingThread") { } 201 void Run() { 202 v8::Locker locker; 203 v8::HandleScope scope; 204 v8_thread_id_ = v8::V8::GetCurrentThreadId(); 205 v8::Handle<v8::ObjectTemplate> global = 206 CreateGlobalTemplate(Signal, DoLoop); 207 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global); 208 v8::Context::Scope context_scope(context); 209 CHECK(!v8::V8::IsExecutionTerminating()); 210 // 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. 226 TEST(TerminateMultipleV8Threads) { 227 { 228 v8::Locker locker; 229 v8::V8::Initialize(); 230 v8::Locker::StartPreemption(1); 231 semaphore = v8::internal::OS::CreateSemaphore(0); 232 } 233 LoopingThread thread1(i::Isolate::Current()); 234 thread1.Start(); 235 LoopingThread thread2(i::Isolate::Current()); 236 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 253 int call_count = 0; 254 255 256 v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) { 257 if (++call_count == 10) { 258 CHECK(!v8::V8::IsExecutionTerminating()); 259 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 268 v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) { 269 v8::TryCatch try_catch; 270 CHECK(!v8::V8::IsExecutionTerminating()); 271 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()); 286 CHECK(v8::V8::IsExecutionTerminating()); 287 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. 293 TEST(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); 304 CHECK(!v8::V8::IsExecutionTerminating()); 305 // 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. 311 CHECK(!v8::V8::IsExecutionTerminating()); 312 call_count = 0; 313 v8::Script::Compile(source)->Run(); 314 context.Dispose(); 315 } 316 317 v8::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. 344 TEST(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 361