Merge remote-tracking branch 'ry/v0.10'
[platform/upstream/nodejs.git] / deps / v8 / test / cctest / test-thread-termination.cc
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 void Signal(const v8::FunctionCallbackInfo<v8::Value>& args) {
37   semaphore->Signal();
38 }
39
40
41 void TerminateCurrentThread(const v8::FunctionCallbackInfo<v8::Value>& args) {
42   CHECK(!v8::V8::IsExecutionTerminating());
43   v8::V8::TerminateExecution();
44 }
45
46
47 void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) {
48   CHECK(false);
49 }
50
51
52 void Loop(const v8::FunctionCallbackInfo<v8::Value>& args) {
53   CHECK(!v8::V8::IsExecutionTerminating());
54   v8::Handle<v8::String> source =
55       v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
56   v8::Handle<v8::Value> result = v8::Script::Compile(source)->Run();
57   CHECK(result.IsEmpty());
58   CHECK(v8::V8::IsExecutionTerminating());
59 }
60
61
62 void DoLoop(const v8::FunctionCallbackInfo<v8::Value>& args) {
63   v8::TryCatch try_catch;
64   CHECK(!v8::V8::IsExecutionTerminating());
65   v8::Script::Compile(v8::String::New("function f() {"
66                                       "  var term = true;"
67                                       "  try {"
68                                       "    while(true) {"
69                                       "      if (term) terminate();"
70                                       "      term = false;"
71                                       "    }"
72                                       "    fail();"
73                                       "  } catch(e) {"
74                                       "    fail();"
75                                       "  }"
76                                       "}"
77                                       "f()"))->Run();
78   CHECK(try_catch.HasCaught());
79   CHECK(try_catch.Exception()->IsNull());
80   CHECK(try_catch.Message().IsEmpty());
81   CHECK(!try_catch.CanContinue());
82   CHECK(v8::V8::IsExecutionTerminating());
83 }
84
85
86 void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
87   v8::TryCatch try_catch;
88   CHECK(!v8::V8::IsExecutionTerminating());
89   v8::Script::Compile(v8::String::New("var term = true;"
90                                       "while(true) {"
91                                       "  if (term) terminate();"
92                                       "  term = false;"
93                                       "}"))->Run();
94   CHECK(try_catch.HasCaught());
95   CHECK(try_catch.Exception()->IsNull());
96   CHECK(try_catch.Message().IsEmpty());
97   CHECK(!try_catch.CanContinue());
98   CHECK(v8::V8::IsExecutionTerminating());
99 }
100
101
102 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
103     v8::FunctionCallback terminate,
104     v8::FunctionCallback doloop) {
105   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
106   global->Set(v8::String::New("terminate"),
107               v8::FunctionTemplate::New(terminate));
108   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
109   global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop));
110   global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
111   return global;
112 }
113
114
115 // Test that a single thread of JavaScript execution can terminate
116 // itself.
117 TEST(TerminateOnlyV8ThreadFromThreadItself) {
118   v8::HandleScope scope(v8::Isolate::GetCurrent());
119   v8::Handle<v8::ObjectTemplate> global =
120       CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
121   v8::Handle<v8::Context> context =
122       v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
123   v8::Context::Scope context_scope(context);
124   CHECK(!v8::V8::IsExecutionTerminating());
125   // Run a loop that will be infinite if thread termination does not work.
126   v8::Handle<v8::String> source =
127       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
128   v8::Script::Compile(source)->Run();
129   // Test that we can run the code again after thread termination.
130   CHECK(!v8::V8::IsExecutionTerminating());
131   v8::Script::Compile(source)->Run();
132 }
133
134
135 // Test that a single thread of JavaScript execution can terminate
136 // itself in a loop that performs no calls.
137 TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
138   v8::HandleScope scope(v8::Isolate::GetCurrent());
139   v8::Handle<v8::ObjectTemplate> global =
140       CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
141   v8::Handle<v8::Context> context =
142       v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
143   v8::Context::Scope context_scope(context);
144   CHECK(!v8::V8::IsExecutionTerminating());
145   // Run a loop that will be infinite if thread termination does not work.
146   v8::Handle<v8::String> source =
147       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
148   v8::Script::Compile(source)->Run();
149   CHECK(!v8::V8::IsExecutionTerminating());
150   // Test that we can run the code again after thread termination.
151   v8::Script::Compile(source)->Run();
152 }
153
154
155 class TerminatorThread : public v8::internal::Thread {
156  public:
157   explicit TerminatorThread(i::Isolate* isolate)
158       : Thread("TerminatorThread"),
159         isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { }
160   void Run() {
161     semaphore->Wait();
162     CHECK(!v8::V8::IsExecutionTerminating(isolate_));
163     v8::V8::TerminateExecution(isolate_);
164   }
165
166  private:
167   v8::Isolate* isolate_;
168 };
169
170
171 // Test that a single thread of JavaScript execution can be terminated
172 // from the side by another thread.
173 TEST(TerminateOnlyV8ThreadFromOtherThread) {
174   semaphore = v8::internal::OS::CreateSemaphore(0);
175   TerminatorThread thread(i::Isolate::Current());
176   thread.Start();
177
178   v8::HandleScope scope(v8::Isolate::GetCurrent());
179   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
180   v8::Handle<v8::Context> context =
181       v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
182   v8::Context::Scope context_scope(context);
183   CHECK(!v8::V8::IsExecutionTerminating());
184   // Run a loop that will be infinite if thread termination does not work.
185   v8::Handle<v8::String> source =
186       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
187   v8::Script::Compile(source)->Run();
188
189   thread.Join();
190   delete semaphore;
191   semaphore = NULL;
192 }
193
194
195 class LoopingThread : public v8::internal::Thread {
196  public:
197   LoopingThread() : Thread("LoopingThread") { }
198   void Run() {
199     v8::Locker locker(CcTest::default_isolate());
200     v8::HandleScope scope(CcTest::default_isolate());
201     v8_thread_id_ = v8::V8::GetCurrentThreadId();
202     v8::Handle<v8::ObjectTemplate> global =
203         CreateGlobalTemplate(Signal, DoLoop);
204     v8::Handle<v8::Context> context =
205         v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
206     v8::Context::Scope context_scope(context);
207     CHECK(!v8::V8::IsExecutionTerminating());
208     // Run a loop that will be infinite if thread termination does not work.
209     v8::Handle<v8::String> source =
210         v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
211     v8::Script::Compile(source)->Run();
212   }
213
214   int GetV8ThreadId() { return v8_thread_id_; }
215
216  private:
217   int v8_thread_id_;
218 };
219
220
221 // Test that multiple threads using default isolate can be terminated
222 // from another thread when using Lockers and preemption.
223 TEST(TerminateMultipleV8ThreadsDefaultIsolate) {
224   {
225     v8::Locker locker(CcTest::default_isolate());
226     v8::V8::Initialize();
227     v8::Locker::StartPreemption(1);
228     semaphore = v8::internal::OS::CreateSemaphore(0);
229   }
230   const int kThreads = 2;
231   i::List<LoopingThread*> threads(kThreads);
232   for (int i = 0; i < kThreads; i++) {
233     threads.Add(new LoopingThread());
234   }
235   for (int i = 0; i < kThreads; i++) {
236     threads[i]->Start();
237   }
238   // Wait until all threads have signaled the semaphore.
239   for (int i = 0; i < kThreads; i++) {
240     semaphore->Wait();
241   }
242   {
243     v8::Locker locker(CcTest::default_isolate());
244     for (int i = 0; i < kThreads; i++) {
245       v8::V8::TerminateExecution(threads[i]->GetV8ThreadId());
246     }
247   }
248   for (int i = 0; i < kThreads; i++) {
249     threads[i]->Join();
250     delete threads[i];
251   }
252   {
253     v8::Locker locker(CcTest::default_isolate());
254     v8::Locker::StopPreemption();
255   }
256
257   delete semaphore;
258   semaphore = NULL;
259 }
260
261
262 int call_count = 0;
263
264
265 void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
266   if (++call_count == 10) {
267     CHECK(!v8::V8::IsExecutionTerminating());
268     v8::V8::TerminateExecution();
269     return;
270   }
271   v8::Local<v8::Object> result = v8::Object::New();
272   result->Set(v8::String::New("x"), v8::Integer::New(42));
273   args.GetReturnValue().Set(result);
274 }
275
276
277 void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
278   v8::TryCatch try_catch;
279   CHECK(!v8::V8::IsExecutionTerminating());
280   v8::Script::Compile(v8::String::New("function f() {"
281                                       "  try {"
282                                       "    while(true) {"
283                                       "      terminate_or_return_object().x;"
284                                       "    }"
285                                       "    fail();"
286                                       "  } catch(e) {"
287                                       "    fail();"
288                                       "  }"
289                                       "}"
290                                       "f()"))->Run();
291   CHECK(try_catch.HasCaught());
292   CHECK(try_catch.Exception()->IsNull());
293   CHECK(try_catch.Message().IsEmpty());
294   CHECK(!try_catch.CanContinue());
295   CHECK(v8::V8::IsExecutionTerminating());
296 }
297
298
299 // Test that we correctly handle termination exceptions if they are
300 // triggered by the creation of error objects in connection with ICs.
301 TEST(TerminateLoadICException) {
302   v8::HandleScope scope(v8::Isolate::GetCurrent());
303   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
304   global->Set(v8::String::New("terminate_or_return_object"),
305               v8::FunctionTemplate::New(TerminateOrReturnObject));
306   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
307   global->Set(v8::String::New("loop"),
308               v8::FunctionTemplate::New(LoopGetProperty));
309
310   v8::Handle<v8::Context> context =
311       v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
312   v8::Context::Scope context_scope(context);
313   CHECK(!v8::V8::IsExecutionTerminating());
314   // Run a loop that will be infinite if thread termination does not work.
315   v8::Handle<v8::String> source =
316       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
317   call_count = 0;
318   v8::Script::Compile(source)->Run();
319   // Test that we can run the code again after thread termination.
320   CHECK(!v8::V8::IsExecutionTerminating());
321   call_count = 0;
322   v8::Script::Compile(source)->Run();
323 }
324
325
326 void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
327   v8::TryCatch try_catch;
328   CHECK(!v8::V8::IsExecutionTerminating());
329   v8::Script::Compile(v8::String::New("function f() {"
330                                       "  var term = true;"
331                                       "  try {"
332                                       "    while(true) {"
333                                       "      if (term) terminate();"
334                                       "      term = false;"
335                                       "    }"
336                                       "    fail();"
337                                       "  } catch(e) {"
338                                       "    fail();"
339                                       "  }"
340                                       "}"
341                                       "f()"))->Run();
342   CHECK(try_catch.HasCaught());
343   CHECK(try_catch.Exception()->IsNull());
344   CHECK(try_catch.Message().IsEmpty());
345   CHECK(!try_catch.CanContinue());
346   CHECK(v8::V8::IsExecutionTerminating());
347   v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
348 }
349
350
351 // Test that reentry into V8 while the termination exception is still pending
352 // (has not yet unwound the 0-level JS frame) does not crash.
353 TEST(TerminateAndReenterFromThreadItself) {
354   v8::HandleScope scope(v8::Isolate::GetCurrent());
355   v8::Handle<v8::ObjectTemplate> global =
356       CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
357   v8::Handle<v8::Context> context =
358       v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
359   v8::Context::Scope context_scope(context);
360   CHECK(!v8::V8::IsExecutionTerminating());
361   v8::Handle<v8::String> source =
362       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
363   v8::Script::Compile(source)->Run();
364   CHECK(!v8::V8::IsExecutionTerminating());
365   // Check we can run JS again after termination.
366   CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
367                                             "f()"))->Run()->IsTrue());
368 }
369
370
371 void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
372   v8::TryCatch try_catch;
373   CHECK(!v8::V8::IsExecutionTerminating());
374   v8::Script::Compile(v8::String::New("var term = true;"
375                                       "while(true) {"
376                                       "  if (term) terminate();"
377                                       "  term = false;"
378                                       "}"
379                                       "fail();"))->Run();
380   CHECK(try_catch.HasCaught());
381   CHECK(try_catch.Exception()->IsNull());
382   CHECK(try_catch.Message().IsEmpty());
383   CHECK(!try_catch.CanContinue());
384   CHECK(v8::V8::IsExecutionTerminating());
385   CHECK(try_catch.HasTerminated());
386   v8::V8::CancelTerminateExecution(v8::Isolate::GetCurrent());
387   CHECK(!v8::V8::IsExecutionTerminating());
388 }
389
390
391 // Test that a single thread of JavaScript execution can terminate
392 // itself and then resume execution.
393 TEST(TerminateCancelTerminateFromThreadItself) {
394   v8::HandleScope scope;
395   v8::Handle<v8::ObjectTemplate> global =
396       CreateGlobalTemplate(TerminateCurrentThread, DoLoopCancelTerminate);
397   v8::Handle<v8::Context> context =
398       v8::Context::New(v8::Isolate::GetCurrent(), NULL, global);
399   v8::Context::Scope context_scope(context);
400   CHECK(!v8::V8::IsExecutionTerminating());
401   v8::Handle<v8::String> source =
402       v8::String::New("try { doloop(); } catch(e) { fail(); } 'completed';");
403   // Check that execution completed with correct return value.
404   CHECK(v8::Script::Compile(source)->Run()->Equals(v8_str("completed")));
405 }
406