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 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(v8::Isolate::GetCurrent());
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(context->GetIsolate());
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(v8::Isolate::GetCurrent());
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(context->GetIsolate());
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(v8::Isolate::GetCurrent());
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(context->GetIsolate());
198 }
199
200
201 class LoopingThread : public v8::internal::Thread {
202  public:
203   LoopingThread() : Thread("LoopingThread") { }
204   void Run() {
205     v8::Locker locker(CcTest::default_isolate());
206     v8::HandleScope scope(CcTest::default_isolate());
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(context->GetIsolate());
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(CcTest::default_isolate());
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(CcTest::default_isolate());
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     v8::Locker locker(CcTest::default_isolate());
260     v8::Locker::StopPreemption();
261   }
262
263   delete semaphore;
264   semaphore = NULL;
265 }
266
267
268 int call_count = 0;
269
270
271 v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
272   if (++call_count == 10) {
273     CHECK(!v8::V8::IsExecutionTerminating());
274     v8::V8::TerminateExecution();
275     return v8::Undefined();
276   }
277   v8::Local<v8::Object> result = v8::Object::New();
278   result->Set(v8::String::New("x"), v8::Integer::New(42));
279   return result;
280 }
281
282
283 v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
284   v8::TryCatch try_catch;
285   CHECK(!v8::V8::IsExecutionTerminating());
286   v8::Script::Compile(v8::String::New("function f() {"
287                                       "  try {"
288                                       "    while(true) {"
289                                       "      terminate_or_return_object().x;"
290                                       "    }"
291                                       "    fail();"
292                                       "  } catch(e) {"
293                                       "    fail();"
294                                       "  }"
295                                       "}"
296                                       "f()"))->Run();
297   CHECK(try_catch.HasCaught());
298   CHECK(try_catch.Exception()->IsNull());
299   CHECK(try_catch.Message().IsEmpty());
300   CHECK(!try_catch.CanContinue());
301   CHECK(v8::V8::IsExecutionTerminating());
302   return v8::Undefined();
303 }
304
305
306 // Test that we correctly handle termination exceptions if they are
307 // triggered by the creation of error objects in connection with ICs.
308 TEST(TerminateLoadICException) {
309   v8::HandleScope scope(v8::Isolate::GetCurrent());
310   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
311   global->Set(v8::String::New("terminate_or_return_object"),
312               v8::FunctionTemplate::New(TerminateOrReturnObject));
313   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
314   global->Set(v8::String::New("loop"),
315               v8::FunctionTemplate::New(LoopGetProperty));
316
317   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
318   v8::Context::Scope context_scope(context);
319   CHECK(!v8::V8::IsExecutionTerminating());
320   // Run a loop that will be infinite if thread termination does not work.
321   v8::Handle<v8::String> source =
322       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
323   call_count = 0;
324   v8::Script::Compile(source)->Run();
325   // Test that we can run the code again after thread termination.
326   CHECK(!v8::V8::IsExecutionTerminating());
327   call_count = 0;
328   v8::Script::Compile(source)->Run();
329   context.Dispose(context->GetIsolate());
330 }
331
332 v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) {
333   v8::TryCatch try_catch;
334   CHECK(!v8::V8::IsExecutionTerminating());
335   v8::Script::Compile(v8::String::New("function f() {"
336                                       "  var term = true;"
337                                       "  try {"
338                                       "    while(true) {"
339                                       "      if (term) terminate();"
340                                       "      term = false;"
341                                       "    }"
342                                       "    fail();"
343                                       "  } catch(e) {"
344                                       "    fail();"
345                                       "  }"
346                                       "}"
347                                       "f()"))->Run();
348   CHECK(try_catch.HasCaught());
349   CHECK(try_catch.Exception()->IsNull());
350   CHECK(try_catch.Message().IsEmpty());
351   CHECK(!try_catch.CanContinue());
352   CHECK(v8::V8::IsExecutionTerminating());
353   v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
354   return v8::Undefined();
355 }
356
357 // Test that reentry into V8 while the termination exception is still pending
358 // (has not yet unwound the 0-level JS frame) does not crash.
359 TEST(TerminateAndReenterFromThreadItself) {
360   v8::HandleScope scope(v8::Isolate::GetCurrent());
361   v8::Handle<v8::ObjectTemplate> global =
362       CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
363   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
364   v8::Context::Scope context_scope(context);
365   CHECK(!v8::V8::IsExecutionTerminating());
366   v8::Handle<v8::String> source =
367       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
368   v8::Script::Compile(source)->Run();
369   CHECK(!v8::V8::IsExecutionTerminating());
370   // Check we can run JS again after termination.
371   CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
372                                             "f()"))->Run()->IsTrue());
373   context.Dispose(context->GetIsolate());
374 }