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