Merge remote-tracking branch 'upstream/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 =
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(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::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(args.GetIsolate()));
83 }
84
85
86 void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
87   v8::TryCatch try_catch;
88   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
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(args.GetIsolate()));
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(CcTest::isolate());
119   v8::Handle<v8::ObjectTemplate> global =
120       CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
121   v8::Handle<v8::Context> context =
122       v8::Context::New(CcTest::isolate(), NULL, global);
123   v8::Context::Scope context_scope(context);
124   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
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(CcTest::isolate()));
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(CcTest::isolate());
139   v8::Handle<v8::ObjectTemplate> global =
140       CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
141   v8::Handle<v8::Context> context =
142       v8::Context::New(CcTest::isolate(), NULL, global);
143   v8::Context::Scope context_scope(context);
144   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
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(CcTest::isolate()));
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 = new v8::internal::Semaphore(0);
175   TerminatorThread thread(CcTest::i_isolate());
176   thread.Start();
177
178   v8::HandleScope scope(CcTest::isolate());
179   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
180   v8::Handle<v8::Context> context =
181       v8::Context::New(CcTest::isolate(), NULL, global);
182   v8::Context::Scope context_scope(context);
183   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
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 int call_count = 0;
196
197
198 void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
199   if (++call_count == 10) {
200     CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
201     v8::V8::TerminateExecution(args.GetIsolate());
202     return;
203   }
204   v8::Local<v8::Object> result = v8::Object::New();
205   result->Set(v8::String::New("x"), v8::Integer::New(42));
206   args.GetReturnValue().Set(result);
207 }
208
209
210 void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
211   v8::TryCatch try_catch;
212   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
213   v8::Script::Compile(v8::String::New("function f() {"
214                                       "  try {"
215                                       "    while(true) {"
216                                       "      terminate_or_return_object().x;"
217                                       "    }"
218                                       "    fail();"
219                                       "  } catch(e) {"
220                                       "    fail();"
221                                       "  }"
222                                       "}"
223                                       "f()"))->Run();
224   CHECK(try_catch.HasCaught());
225   CHECK(try_catch.Exception()->IsNull());
226   CHECK(try_catch.Message().IsEmpty());
227   CHECK(!try_catch.CanContinue());
228   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
229 }
230
231
232 // Test that we correctly handle termination exceptions if they are
233 // triggered by the creation of error objects in connection with ICs.
234 TEST(TerminateLoadICException) {
235   v8::HandleScope scope(CcTest::isolate());
236   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
237   global->Set(v8::String::New("terminate_or_return_object"),
238               v8::FunctionTemplate::New(TerminateOrReturnObject));
239   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
240   global->Set(v8::String::New("loop"),
241               v8::FunctionTemplate::New(LoopGetProperty));
242
243   v8::Handle<v8::Context> context =
244       v8::Context::New(CcTest::isolate(), NULL, global);
245   v8::Context::Scope context_scope(context);
246   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
247   // Run a loop that will be infinite if thread termination does not work.
248   v8::Handle<v8::String> source =
249       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
250   call_count = 0;
251   v8::Script::Compile(source)->Run();
252   // Test that we can run the code again after thread termination.
253   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
254   call_count = 0;
255   v8::Script::Compile(source)->Run();
256 }
257
258
259 void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
260   v8::TryCatch try_catch;
261   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
262   v8::Script::Compile(v8::String::New("function f() {"
263                                       "  var term = true;"
264                                       "  try {"
265                                       "    while(true) {"
266                                       "      if (term) terminate();"
267                                       "      term = false;"
268                                       "    }"
269                                       "    fail();"
270                                       "  } catch(e) {"
271                                       "    fail();"
272                                       "  }"
273                                       "}"
274                                       "f()"))->Run();
275   CHECK(try_catch.HasCaught());
276   CHECK(try_catch.Exception()->IsNull());
277   CHECK(try_catch.Message().IsEmpty());
278   CHECK(!try_catch.CanContinue());
279   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
280   v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
281 }
282
283
284 // Test that reentry into V8 while the termination exception is still pending
285 // (has not yet unwound the 0-level JS frame) does not crash.
286 TEST(TerminateAndReenterFromThreadItself) {
287   v8::HandleScope scope(CcTest::isolate());
288   v8::Handle<v8::ObjectTemplate> global =
289       CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
290   v8::Handle<v8::Context> context =
291       v8::Context::New(CcTest::isolate(), NULL, global);
292   v8::Context::Scope context_scope(context);
293   CHECK(!v8::V8::IsExecutionTerminating());
294   v8::Handle<v8::String> source =
295       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
296   v8::Script::Compile(source)->Run();
297   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
298   // Check we can run JS again after termination.
299   CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
300                                             "f()"))->Run()->IsTrue());
301 }
302
303
304 void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
305   v8::TryCatch try_catch;
306   CHECK(!v8::V8::IsExecutionTerminating());
307   v8::Script::Compile(v8::String::New("var term = true;"
308                                       "while(true) {"
309                                       "  if (term) terminate();"
310                                       "  term = false;"
311                                       "}"
312                                       "fail();"))->Run();
313   CHECK(try_catch.HasCaught());
314   CHECK(try_catch.Exception()->IsNull());
315   CHECK(try_catch.Message().IsEmpty());
316   CHECK(!try_catch.CanContinue());
317   CHECK(v8::V8::IsExecutionTerminating());
318   CHECK(try_catch.HasTerminated());
319   v8::V8::CancelTerminateExecution(CcTest::isolate());
320   CHECK(!v8::V8::IsExecutionTerminating());
321 }
322
323
324 // Test that a single thread of JavaScript execution can terminate
325 // itself and then resume execution.
326 TEST(TerminateCancelTerminateFromThreadItself) {
327   v8::Isolate* isolate = CcTest::isolate();
328   v8::HandleScope scope(isolate);
329   v8::Handle<v8::ObjectTemplate> global =
330       CreateGlobalTemplate(TerminateCurrentThread, DoLoopCancelTerminate);
331   v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL, global);
332   v8::Context::Scope context_scope(context);
333   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
334   v8::Handle<v8::String> source =
335       v8::String::New("try { doloop(); } catch(e) { fail(); } 'completed';");
336   // Check that execution completed with correct return value.
337   CHECK(v8::Script::Compile(source)->Run()->Equals(v8_str("completed")));
338 }