Merge remote-tracking branch 'origin/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(args.GetIsolate()));
43   v8::V8::TerminateExecution(args.GetIsolate());
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(args.GetIsolate()));
54   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
55       args.GetIsolate(), "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(args.GetIsolate()));
59 }
60
61
62 void DoLoop(const v8::FunctionCallbackInfo<v8::Value>& args) {
63   v8::TryCatch try_catch;
64   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
65   v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
66                                               "function f() {"
67                                               "  var term = true;"
68                                               "  try {"
69                                               "    while(true) {"
70                                               "      if (term) terminate();"
71                                               "      term = false;"
72                                               "    }"
73                                               "    fail();"
74                                               "  } catch(e) {"
75                                               "    fail();"
76                                               "  }"
77                                               "}"
78                                               "f()"))->Run();
79   CHECK(try_catch.HasCaught());
80   CHECK(try_catch.Exception()->IsNull());
81   CHECK(try_catch.Message().IsEmpty());
82   CHECK(!try_catch.CanContinue());
83   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
84 }
85
86
87 void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
88   v8::TryCatch try_catch;
89   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
90   v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
91                                               "var term = true;"
92                                               "while(true) {"
93                                               "  if (term) terminate();"
94                                               "  term = false;"
95                                               "}"))->Run();
96   CHECK(try_catch.HasCaught());
97   CHECK(try_catch.Exception()->IsNull());
98   CHECK(try_catch.Message().IsEmpty());
99   CHECK(!try_catch.CanContinue());
100   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
101 }
102
103
104 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
105     v8::Isolate* isolate,
106     v8::FunctionCallback terminate,
107     v8::FunctionCallback doloop) {
108   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
109   global->Set(v8::String::NewFromUtf8(isolate, "terminate"),
110               v8::FunctionTemplate::New(isolate, terminate));
111   global->Set(v8::String::NewFromUtf8(isolate, "fail"),
112               v8::FunctionTemplate::New(isolate, Fail));
113   global->Set(v8::String::NewFromUtf8(isolate, "loop"),
114               v8::FunctionTemplate::New(isolate, Loop));
115   global->Set(v8::String::NewFromUtf8(isolate, "doloop"),
116               v8::FunctionTemplate::New(isolate, 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(CcTest::isolate());
125   v8::Handle<v8::ObjectTemplate> global =
126       CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
127   v8::Handle<v8::Context> context =
128       v8::Context::New(CcTest::isolate(), NULL, global);
129   v8::Context::Scope context_scope(context);
130   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
131   // Run a loop that will be infinite if thread termination does not work.
132   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
133       CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
134   v8::Script::Compile(source)->Run();
135   // Test that we can run the code again after thread termination.
136   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
137   v8::Script::Compile(source)->Run();
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(CcTest::isolate());
145   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
146       CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
147   v8::Handle<v8::Context> context =
148       v8::Context::New(CcTest::isolate(), NULL, global);
149   v8::Context::Scope context_scope(context);
150   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
151   // Run a loop that will be infinite if thread termination does not work.
152   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
153       CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
154   v8::Script::Compile(source)->Run();
155   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
156   // Test that we can run the code again after thread termination.
157   v8::Script::Compile(source)->Run();
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 = new v8::internal::Semaphore(0);
181   TerminatorThread thread(CcTest::i_isolate());
182   thread.Start();
183
184   v8::HandleScope scope(CcTest::isolate());
185   v8::Handle<v8::ObjectTemplate> global =
186       CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
187   v8::Handle<v8::Context> context =
188       v8::Context::New(CcTest::isolate(), NULL, global);
189   v8::Context::Scope context_scope(context);
190   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
191   // Run a loop that will be infinite if thread termination does not work.
192   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
193       CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
194   v8::Script::Compile(source)->Run();
195
196   thread.Join();
197   delete semaphore;
198   semaphore = NULL;
199 }
200
201
202 int call_count = 0;
203
204
205 void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
206   if (++call_count == 10) {
207     CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
208     v8::V8::TerminateExecution(args.GetIsolate());
209     return;
210   }
211   v8::Local<v8::Object> result = v8::Object::New(args.GetIsolate());
212   result->Set(v8::String::NewFromUtf8(args.GetIsolate(), "x"),
213               v8::Integer::New(args.GetIsolate(), 42));
214   args.GetReturnValue().Set(result);
215 }
216
217
218 void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
219   v8::TryCatch try_catch;
220   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
221   v8::Script::Compile(
222       v8::String::NewFromUtf8(args.GetIsolate(),
223                               "function f() {"
224                               "  try {"
225                               "    while(true) {"
226                               "      terminate_or_return_object().x;"
227                               "    }"
228                               "    fail();"
229                               "  } catch(e) {"
230                               "    fail();"
231                               "  }"
232                               "}"
233                               "f()"))->Run();
234   CHECK(try_catch.HasCaught());
235   CHECK(try_catch.Exception()->IsNull());
236   CHECK(try_catch.Message().IsEmpty());
237   CHECK(!try_catch.CanContinue());
238   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
239 }
240
241
242 // Test that we correctly handle termination exceptions if they are
243 // triggered by the creation of error objects in connection with ICs.
244 TEST(TerminateLoadICException) {
245   v8::Isolate* isolate = CcTest::isolate();
246   v8::HandleScope scope(isolate);
247   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
248   global->Set(
249       v8::String::NewFromUtf8(isolate, "terminate_or_return_object"),
250       v8::FunctionTemplate::New(isolate, TerminateOrReturnObject));
251   global->Set(v8::String::NewFromUtf8(isolate, "fail"),
252               v8::FunctionTemplate::New(isolate, Fail));
253   global->Set(v8::String::NewFromUtf8(isolate, "loop"),
254               v8::FunctionTemplate::New(isolate, LoopGetProperty));
255
256   v8::Handle<v8::Context> context =
257       v8::Context::New(isolate, NULL, global);
258   v8::Context::Scope context_scope(context);
259   CHECK(!v8::V8::IsExecutionTerminating(isolate));
260   // Run a loop that will be infinite if thread termination does not work.
261   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
262       isolate, "try { loop(); fail(); } catch(e) { fail(); }");
263   call_count = 0;
264   v8::Script::Compile(source)->Run();
265   // Test that we can run the code again after thread termination.
266   CHECK(!v8::V8::IsExecutionTerminating(isolate));
267   call_count = 0;
268   v8::Script::Compile(source)->Run();
269 }
270
271
272 void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
273   v8::TryCatch try_catch;
274   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
275   v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
276                                               "function f() {"
277                                               "  var term = true;"
278                                               "  try {"
279                                               "    while(true) {"
280                                               "      if (term) terminate();"
281                                               "      term = false;"
282                                               "    }"
283                                               "    fail();"
284                                               "  } catch(e) {"
285                                               "    fail();"
286                                               "  }"
287                                               "}"
288                                               "f()"))->Run();
289   CHECK(try_catch.HasCaught());
290   CHECK(try_catch.Exception()->IsNull());
291   CHECK(try_catch.Message().IsEmpty());
292   CHECK(!try_catch.CanContinue());
293   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
294   v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
295                                               "function f() { fail(); } f()"))
296       ->Run();
297 }
298
299
300 // Test that reentry into V8 while the termination exception is still pending
301 // (has not yet unwound the 0-level JS frame) does not crash.
302 TEST(TerminateAndReenterFromThreadItself) {
303   v8::Isolate* isolate = CcTest::isolate();
304   v8::HandleScope scope(isolate);
305   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
306       isolate, TerminateCurrentThread, ReenterAfterTermination);
307   v8::Handle<v8::Context> context =
308       v8::Context::New(isolate, NULL, global);
309   v8::Context::Scope context_scope(context);
310   CHECK(!v8::V8::IsExecutionTerminating());
311   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
312       isolate, "try { loop(); fail(); } catch(e) { fail(); }");
313   v8::Script::Compile(source)->Run();
314   CHECK(!v8::V8::IsExecutionTerminating(isolate));
315   // Check we can run JS again after termination.
316   CHECK(v8::Script::Compile(
317       v8::String::NewFromUtf8(isolate,
318                               "function f() { return true; }"
319                               "f()"))
320             ->Run()
321             ->IsTrue());
322 }
323
324
325 void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
326   v8::TryCatch try_catch;
327   CHECK(!v8::V8::IsExecutionTerminating());
328   v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
329                                               "var term = true;"
330                                               "while(true) {"
331                                               "  if (term) terminate();"
332                                               "  term = false;"
333                                               "}"
334                                               "fail();"))->Run();
335   CHECK(try_catch.HasCaught());
336   CHECK(try_catch.Exception()->IsNull());
337   CHECK(try_catch.Message().IsEmpty());
338   CHECK(!try_catch.CanContinue());
339   CHECK(v8::V8::IsExecutionTerminating());
340   CHECK(try_catch.HasTerminated());
341   v8::V8::CancelTerminateExecution(CcTest::isolate());
342   CHECK(!v8::V8::IsExecutionTerminating());
343 }
344
345
346 // Test that a single thread of JavaScript execution can terminate
347 // itself and then resume execution.
348 TEST(TerminateCancelTerminateFromThreadItself) {
349   v8::Isolate* isolate = CcTest::isolate();
350   v8::HandleScope scope(isolate);
351   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
352       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
353   v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL, global);
354   v8::Context::Scope context_scope(context);
355   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
356   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
357       isolate, "try { doloop(); } catch(e) { fail(); } 'completed';");
358   // Check that execution completed with correct return value.
359   CHECK(v8::Script::Compile(source)->Run()->Equals(v8_str("completed")));
360 }